From: David van Moolenbroek Date: Wed, 1 Oct 2014 15:59:46 +0000 (+0000) Subject: Import tmux X-Git-Url: http://zhaoyanbai.com/repos/%22http:/www.isc.org/icons/zpipe.c?a=commitdiff_plain;h=eda6f5931d42c77e1480347b1fc3eef2f8d33806;p=minix.git Import tmux We have to use SOCK_SEQPACKET instead of SOCK_STREAM for client/server communication, because UDS does things with control messages that tmux does not expect. Change-Id: I3edb1875d61fb976cf6485c650f4fd4b82fa354c --- diff --git a/distrib/sets/lists/minix/mi b/distrib/sets/lists/minix/mi index 1c10e396a..792effd3f 100644 --- a/distrib/sets/lists/minix/mi +++ b/distrib/sets/lists/minix/mi @@ -516,6 +516,7 @@ ./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 @@ -2555,6 +2556,7 @@ ./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 @@ -5344,6 +5346,8 @@ ./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 diff --git a/etc/mtree/NetBSD.dist.base b/etc/mtree/NetBSD.dist.base index 25b39e563..be2d977ff 100644 --- a/etc/mtree/NetBSD.dist.base +++ b/etc/mtree/NetBSD.dist.base @@ -117,6 +117,8 @@ ./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 diff --git a/etc/mtree/NetBSD.dist.tests b/etc/mtree/NetBSD.dist.tests index c0270cbb1..76ce7b773 100644 --- a/etc/mtree/NetBSD.dist.tests +++ b/etc/mtree/NetBSD.dist.tests @@ -162,7 +162,6 @@ ./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 diff --git a/external/bsd/Makefile b/external/bsd/Makefile index 34c353c27..1b4650723 100644 --- a/external/bsd/Makefile +++ b/external/bsd/Makefile @@ -4,7 +4,7 @@ SUBDIR= byacc file flex less \ libarchive libevent mdocml \ - + tmux .if (${MKATF} != "no") SUBDIR+= atf diff --git a/external/bsd/tmux/Makefile b/external/bsd/tmux/Makefile new file mode 100644 index 000000000..fb58a82a3 --- /dev/null +++ b/external/bsd/tmux/Makefile @@ -0,0 +1,5 @@ +# $NetBSD: Makefile,v 1.1 2011/03/10 09:18:00 jmmv Exp $ + +SUBDIR= share usr.bin + +.include diff --git a/external/bsd/tmux/README b/external/bsd/tmux/README new file mode 100644 index 000000000..a14edfe05 --- /dev/null +++ b/external/bsd/tmux/README @@ -0,0 +1,15 @@ +To update tmux to a new version: + +- Build the package from pkgsrc and write down all -D flags passed to the + compiler. Autoconf is not generating a config.h file, so this is the + best we can do to get the build-time settings in place. +- Use prepare-import.sh to regenerate the dist directory. +- Update usr.bin/tmux/Makefile to sync the CPPFLAGS to the list of -D flags + gathered earlier on. +- Update the list of source files in usr.bin/tmux/Makefile with the new + dist/*.c listing. +- cvs import the contents of the new dist directory. +- Fix merge conflicts, if any. +- Commit the changes to the reachover Makefiles. +- Update doc/3RDPARTY with the new tmux version. +- Add a note to doc/CHANGES about the new version. diff --git a/external/bsd/tmux/dist/arguments.c b/external/bsd/tmux/dist/arguments.c new file mode 100644 index 000000000..cf3274945 --- /dev/null +++ b/external/bsd/tmux/dist/arguments.c @@ -0,0 +1,222 @@ +/* $Id: arguments.c,v 1.1.1.1 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2010 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +/* Create an arguments set with no flags. */ +struct args * +args_create(int argc, ...) +{ + struct args *args; + va_list ap; + int i; + + args = xcalloc(1, sizeof *args); + if ((args->flags = bit_alloc(SCHAR_MAX)) == NULL) + fatal("bit_alloc failed"); + + args->argc = argc; + if (argc == 0) + args->argv = NULL; + else + args->argv = xcalloc(argc, sizeof *args->argv); + + va_start(ap, argc); + for (i = 0; i < argc; i++) + args->argv[i] = xstrdup(va_arg(ap, char *)); + va_end(ap); + + return (args); +} + +/* Parse an argv and argc into a new argument set. */ +struct args * +args_parse(const char *template, int argc, char **argv) +{ + struct args *args; + char *ptr; + int opt; + + args = xcalloc(1, sizeof *args); + if ((args->flags = bit_alloc(SCHAR_MAX)) == NULL) + fatal("bit_alloc failed"); + + optreset = 1; + optind = 1; + + while ((opt = getopt(argc, argv, template)) != -1) { + if (opt < 0 || opt >= SCHAR_MAX) + continue; + if (opt == '?' || (ptr = strchr(template, opt)) == NULL) { + xfree(args->flags); + xfree(args); + return (NULL); + } + + bit_set(args->flags, opt); + if (ptr[1] == ':') { + if (args->values[opt] != NULL) + xfree(args->values[opt]); + args->values[opt] = xstrdup(optarg); + } + } + argc -= optind; + argv += optind; + + args->argc = argc; + args->argv = cmd_copy_argv(argc, argv); + + return (args); +} + +/* Free an arguments set. */ +void +args_free(struct args *args) +{ + u_int i; + + cmd_free_argv(args->argc, args->argv); + + for (i = 0; i < SCHAR_MAX; i++) { + if (args->values[i] != NULL) + xfree(args->values[i]); + } + + xfree(args->flags); + xfree(args); +} + +/* Print a set of arguments. */ +size_t +args_print(struct args *args, char *buf, size_t len) +{ + size_t off; + int i; + const char *quotes; + + /* There must be at least one byte at the start. */ + if (len == 0) + return (0); + off = 0; + + /* Process the flags first. */ + buf[off++] = '-'; + for (i = 0; i < SCHAR_MAX; i++) { + if (!bit_test(args->flags, i) || args->values[i] != NULL) + continue; + + if (off == len - 1) { + buf[off] = '\0'; + return (len); + } + buf[off++] = i; + buf[off] = '\0'; + } + if (off == 1) + buf[--off] = '\0'; + + /* Then the flags with arguments. */ + for (i = 0; i < SCHAR_MAX; i++) { + if (!bit_test(args->flags, i) || args->values[i] == NULL) + continue; + + if (off >= len) { + /* snprintf will have zero terminated. */ + return (len); + } + + if (strchr(args->values[i], ' ') != NULL) + quotes = "\""; + else + quotes = ""; + off += xsnprintf(buf + off, len - off, "%s-%c %s%s%s", + off != 0 ? " " : "", i, quotes, args->values[i], quotes); + } + + /* And finally the argument vector. */ + for (i = 0; i < args->argc; i++) { + if (off >= len) { + /* snprintf will have zero terminated. */ + return (len); + } + + if (strchr(args->argv[i], ' ') != NULL) + quotes = "\""; + else + quotes = ""; + off += xsnprintf(buf + off, len - off, "%s%s%s%s", + off != 0 ? " " : "", quotes, args->argv[i], quotes); + } + + return (off); +} + +/* Return if an argument is present. */ +int +args_has(struct args *args, u_char ch) +{ + return (bit_test(args->flags, ch)); +} + +/* Set argument value. */ +void +args_set(struct args *args, u_char ch, const char *value) +{ + if (args->values[ch] != NULL) + xfree(args->values[ch]); + if (value != NULL) + args->values[ch] = xstrdup(value); + else + args->values[ch] = NULL; + bit_set(args->flags, ch); +} + +/* Get argument value. Will be NULL if it isn't present. */ +const char * +args_get(struct args *args, u_char ch) +{ + return (args->values[ch]); +} + +/* Convert an argument value to a number. */ +long long +args_strtonum(struct args *args, + u_char ch, long long minval, long long maxval, char **cause) +{ + const char *errstr; + long long ll; + + if (!args_has(args, ch)) { + *cause = xstrdup("missing"); + return (0); + } + + ll = strtonum(args->values[ch], minval, maxval, &errstr); + if (errstr != NULL) { + *cause = xstrdup(errstr); + return (0); + } + + *cause = NULL; + return (ll); +} diff --git a/external/bsd/tmux/dist/array.h b/external/bsd/tmux/dist/array.h new file mode 100644 index 000000000..dc1a4e3da --- /dev/null +++ b/external/bsd/tmux/dist/array.h @@ -0,0 +1,121 @@ +/* $Id: array.h,v 1.1.1.2 2011/08/17 18:40:05 jmmv Exp $ */ + +/* + * Copyright (c) 2006 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef ARRAY_H +#define ARRAY_H + +#define ARRAY_INITIALIZER { NULL, 0, 0 } + +#define ARRAY_DECL(n, c) \ + struct n { \ + c *list; \ + u_int num; \ + size_t space; \ + } + +#define ARRAY_ITEM(a, i) ((a)->list[i]) +#define ARRAY_ITEMSIZE(a) (sizeof *(a)->list) +#define ARRAY_INITIALSPACE(a) (10 * ARRAY_ITEMSIZE(a)) + +#define ARRAY_ENSURE(a, n) do { \ + if (UINT_MAX - (n) < (a)->num) \ + fatalx("number too big"); \ + if (SIZE_MAX / ((a)->num + (n)) < ARRAY_ITEMSIZE(a)) \ + fatalx("size too big"); \ + if ((a)->space == 0) { \ + (a)->space = ARRAY_INITIALSPACE(a); \ + (a)->list = xrealloc((a)->list, 1, (a)->space); \ + } \ + while ((a)->space <= ((a)->num + (n)) * ARRAY_ITEMSIZE(a)) { \ + (a)->list = xrealloc((a)->list, 2, (a)->space); \ + (a)->space *= 2; \ + } \ +} while (0) + +#define ARRAY_EMPTY(a) (((void *) (a)) == NULL || (a)->num == 0) +#define ARRAY_LENGTH(a) ((a)->num) +#define ARRAY_DATA(a) ((a)->list) + +#define ARRAY_FIRST(a) ARRAY_ITEM(a, 0) +#define ARRAY_LAST(a) ARRAY_ITEM(a, (a)->num - 1) + +#define ARRAY_INIT(a) do { \ + (a)->num = 0; \ + (a)->list = NULL; \ + (a)->space = 0; \ +} while (0) +#define ARRAY_CLEAR(a) do { \ + (a)->num = 0; \ +} while (0) + +#define ARRAY_SET(a, i, s) do { \ + (a)->list[i] = s; \ +} while (0) + +#define ARRAY_ADD(a, s) do { \ + ARRAY_ENSURE(a, 1); \ + (a)->list[(a)->num] = s; \ + (a)->num++; \ +} while (0) +#define ARRAY_INSERT(a, i, s) do { \ + ARRAY_ENSURE(a, 1); \ + if ((i) < (a)->num) { \ + memmove((a)->list + (i) + 1, (a)->list + (i), \ + ARRAY_ITEMSIZE(a) * ((a)->num - (i))); \ + } \ + (a)->list[i] = s; \ + (a)->num++; \ +} while (0) +#define ARRAY_REMOVE(a, i) do { \ + if ((i) < (a)->num - 1) { \ + memmove((a)->list + (i), (a)->list + (i) + 1, \ + ARRAY_ITEMSIZE(a) * ((a)->num - (i) - 1)); \ + } \ + (a)->num--; \ + if ((a)->num == 0) \ + ARRAY_FREE(a); \ +} while (0) + +#define ARRAY_EXPAND(a, n) do { \ + ARRAY_ENSURE(a, n); \ + (a)->num += n; \ +} while (0) +#define ARRAY_TRUNC(a, n) do { \ + if ((a)->num > n) \ + (a)->num -= n; \ + else \ + ARRAY_FREE(a); \ +} while (0) + +#define ARRAY_CONCAT(a, b) do { \ + ARRAY_ENSURE(a, (b)->num); \ + memcpy((a)->list + (a)->num, (b)->list, (b)->num * ARRAY_ITEMSIZE(a)); \ + (a)->num += (b)->num; \ +} while (0) + +#define ARRAY_FREE(a) do { \ + if ((a)->list != NULL) \ + xfree((a)->list); \ + ARRAY_INIT(a); \ +} while (0) +#define ARRAY_FREEALL(a) do { \ + ARRAY_FREE(a); \ + xfree(a); \ +} while (0) + +#endif diff --git a/external/bsd/tmux/dist/attributes.c b/external/bsd/tmux/dist/attributes.c new file mode 100644 index 000000000..8fbd4fa30 --- /dev/null +++ b/external/bsd/tmux/dist/attributes.c @@ -0,0 +1,93 @@ +/* $Id: attributes.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Joshua Elsasser + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +const char * +attributes_tostring(u_char attr) +{ + static char buf[128]; + + if (attr == 0) + return ("none"); + + buf[0] = '\0'; + if (attr & GRID_ATTR_BRIGHT) + strlcat(buf, "bright,", sizeof (buf)); + if (attr & GRID_ATTR_DIM) + strlcat(buf, "dim,", sizeof (buf)); + if (attr & GRID_ATTR_UNDERSCORE) + strlcat(buf, "underscore,", sizeof (buf)); + if (attr & GRID_ATTR_BLINK) + strlcat(buf, "blink,", sizeof (buf)); + if (attr & GRID_ATTR_REVERSE) + strlcat(buf, "reverse,", sizeof (buf)); + if (attr & GRID_ATTR_HIDDEN) + strlcat(buf, "hidden,", sizeof (buf)); + if (attr & GRID_ATTR_ITALICS) + strlcat(buf, "italics,", sizeof (buf)); + if (*buf != '\0') + *(strrchr(buf, ',')) = '\0'; + + return (buf); +} + +int +attributes_fromstring(const char *str) +{ + const char delimiters[] = " ,|"; + u_char attr; + size_t end; + + if (*str == '\0' || strcspn(str, delimiters) == 0) + return (-1); + if (strchr(delimiters, str[strlen(str) - 1]) != NULL) + return (-1); + + if (strcasecmp(str, "default") == 0 || strcasecmp(str, "none") == 0) + return (0); + + attr = 0; + do { + end = strcspn(str, delimiters); + if ((end == 6 && strncasecmp(str, "bright", end) == 0) || + (end == 4 && strncasecmp(str, "bold", end) == 0)) + attr |= GRID_ATTR_BRIGHT; + else if (end == 3 && strncasecmp(str, "dim", end) == 0) + attr |= GRID_ATTR_DIM; + else if (end == 10 && strncasecmp(str, "underscore", end) == 0) + attr |= GRID_ATTR_UNDERSCORE; + else if (end == 5 && strncasecmp(str, "blink", end) == 0) + attr |= GRID_ATTR_BLINK; + else if (end == 7 && strncasecmp(str, "reverse", end) == 0) + attr |= GRID_ATTR_REVERSE; + else if (end == 6 && strncasecmp(str, "hidden", end) == 0) + attr |= GRID_ATTR_HIDDEN; + else if (end == 7 && strncasecmp(str, "italics", end) == 0) + attr |= GRID_ATTR_ITALICS; + else + return (-1); + str += end + strspn(str + end, delimiters); + } while (*str != '\0'); + + return (attr); +} diff --git a/external/bsd/tmux/dist/cfg.c b/external/bsd/tmux/dist/cfg.c new file mode 100644 index 000000000..c14fd5393 --- /dev/null +++ b/external/bsd/tmux/dist/cfg.c @@ -0,0 +1,143 @@ +/* $Id: cfg.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2008 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include +#include +#include + +#include "tmux.h" + +/* + * Config file parser. Pretty quick and simple, each line is parsed into a + * argv array and executed as a command. + */ + +void printflike2 cfg_print(struct cmd_ctx *, const char *, ...); +void printflike2 cfg_error(struct cmd_ctx *, const char *, ...); + +char *cfg_cause; +int cfg_finished; +struct causelist cfg_causes = ARRAY_INITIALIZER; + +/* ARGSUSED */ +void printflike2 +cfg_print(unused struct cmd_ctx *ctx, unused const char *fmt, ...) +{ +} + +/* ARGSUSED */ +void printflike2 +cfg_error(unused struct cmd_ctx *ctx, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + xvasprintf(&cfg_cause, fmt, ap); + va_end(ap); +} + +void printflike2 +cfg_add_cause(struct causelist *causes, const char *fmt, ...) +{ + char *cause; + va_list ap; + + va_start(ap, fmt); + xvasprintf(&cause, fmt, ap); + va_end(ap); + + ARRAY_ADD(causes, cause); +} + +/* + * Load configuration file. Returns -1 for an error with a list of messages in + * causes. Note that causes must be initialised by the caller! + */ +int +load_cfg(const char *path, struct cmd_ctx *ctxin, struct causelist *causes) +{ + FILE *f; + u_int n; + char *buf, *line, *cause; + size_t len; + struct cmd_list *cmdlist; + struct cmd_ctx ctx; + int retval; + + if ((f = fopen(path, "rb")) == NULL) { + cfg_add_cause(causes, "%s: %s", path, strerror(errno)); + return (-1); + } + n = 0; + + line = NULL; + retval = 0; + while ((buf = fgetln(f, &len))) { + if (buf[len - 1] == '\n') + buf[len - 1] = '\0'; + else { + line = xrealloc(line, 1, len + 1); + memcpy(line, buf, len); + line[len] = '\0'; + buf = line; + } + n++; + + if (cmd_string_parse(buf, &cmdlist, &cause) != 0) { + if (cause == NULL) + continue; + cfg_add_cause(causes, "%s: %u: %s", path, n, cause); + xfree(cause); + continue; + } + if (cmdlist == NULL) + continue; + cfg_cause = NULL; + + if (ctxin == NULL) { + ctx.msgdata = NULL; + ctx.curclient = NULL; + ctx.cmdclient = NULL; + } else { + ctx.msgdata = ctxin->msgdata; + ctx.curclient = ctxin->curclient; + ctx.cmdclient = ctxin->cmdclient; + } + + ctx.error = cfg_error; + ctx.print = cfg_print; + ctx.info = cfg_print; + + cfg_cause = NULL; + if (cmd_list_exec(cmdlist, &ctx) == 1) + retval = 1; + cmd_list_free(cmdlist); + if (cfg_cause != NULL) { + cfg_add_cause(causes, "%s: %d: %s", path, n, cfg_cause); + xfree(cfg_cause); + } + } + if (line != NULL) + xfree(line); + fclose(f); + + return (retval); +} diff --git a/external/bsd/tmux/dist/client.c b/external/bsd/tmux/dist/client.c new file mode 100644 index 000000000..a63b811b9 --- /dev/null +++ b/external/bsd/tmux/dist/client.c @@ -0,0 +1,512 @@ +/* $Id: client.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "tmux.h" + +struct imsgbuf client_ibuf; +struct event client_event; +const char *client_exitmsg; +int client_exitval; +enum msgtype client_exittype; +int client_attached; + +int client_connect(char *, int); +void client_send_identify(int); +void client_send_environ(void); +void client_write_server(enum msgtype, void *, size_t); +void client_update_event(void); +void client_signal(int, short, void *); +void client_callback(int, short, void *); +int client_dispatch_attached(void); +int client_dispatch_wait(void *); + +/* Connect client to server. */ +int +client_connect(char *path, int start_server) +{ + struct sockaddr_un sa; + size_t size; + int fd; + + memset(&sa, 0, sizeof sa); + sa.sun_family = AF_UNIX; + size = strlcpy(sa.sun_path, path, sizeof sa.sun_path); + if (size >= sizeof sa.sun_path) { + errno = ENAMETOOLONG; + return (-1); + } + +#ifndef __minix + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) +#else + if ((fd = socket(AF_UNIX, SOCK_SEQPACKET, 0)) == -1) +#endif + fatal("socket failed"); + + if (connect(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) { + if (!start_server) + goto failed; + switch (errno) { + case ECONNREFUSED: + if (unlink(path) != 0) + goto failed; + /* FALLTHROUGH */ + case ENOENT: + if ((fd = server_start()) == -1) + goto failed; + break; + default: + goto failed; + } + } + + setblocking(fd, 0); + return (fd); + +failed: + close(fd); + return (-1); +} + +/* Client main loop. */ +int +client_main(int argc, char **argv, int flags) +{ + struct cmd *cmd; + struct cmd_list *cmdlist; + struct msg_command_data cmddata; + int cmdflags, fd; + pid_t ppid; + enum msgtype msg; + char *cause; + + /* Set up the initial command. */ + cmdflags = 0; + if (shell_cmd != NULL) { + msg = MSG_SHELL; + cmdflags = CMD_STARTSERVER; + } else if (argc == 0) { + msg = MSG_COMMAND; + cmdflags = CMD_STARTSERVER|CMD_SENDENVIRON|CMD_CANTNEST; + } else { + msg = MSG_COMMAND; + + /* + * It sucks parsing the command string twice (in client and + * later in server) but it is necessary to get the start server + * flag. + */ + if ((cmdlist = cmd_list_parse(argc, argv, &cause)) == NULL) { + log_warnx("%s", cause); + return (1); + } + cmdflags &= ~CMD_STARTSERVER; + TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { + if (cmd->entry->flags & CMD_STARTSERVER) + cmdflags |= CMD_STARTSERVER; + if (cmd->entry->flags & CMD_SENDENVIRON) + cmdflags |= CMD_SENDENVIRON; + if (cmd->entry->flags & CMD_CANTNEST) + cmdflags |= CMD_CANTNEST; + } + cmd_list_free(cmdlist); + } + + /* + * Check if this could be a nested session, if the command can't nest: + * if the socket path matches $TMUX, this is probably the same server. + */ + if (shell_cmd == NULL && environ_path != NULL && + cmdflags & CMD_CANTNEST && strcmp(socket_path, environ_path) == 0) { + log_warnx("sessions should be nested with care. " + "unset $TMUX to force."); + return (1); + } + + /* Initialise the client socket and start the server. */ + fd = client_connect(socket_path, cmdflags & CMD_STARTSERVER); + if (fd == -1) { + log_warn("failed to connect to server"); + return (1); + } + + /* Set process title, log and signals now this is the client. */ +#ifdef HAVE_SETPROCTITLE + setproctitle("client (%s)", socket_path); +#endif + logfile("client"); + + /* Create imsg. */ + imsg_init(&client_ibuf, fd); + event_set(&client_event, fd, EV_READ, client_callback, shell_cmd); + + /* Establish signal handlers. */ + set_signals(client_signal); + + /* Send initial environment. */ + if (cmdflags & CMD_SENDENVIRON) + client_send_environ(); + client_send_identify(flags); + + /* Send first command. */ + if (msg == MSG_COMMAND) { + /* Fill in command line arguments. */ + cmddata.pid = environ_pid; + cmddata.idx = environ_idx; + + /* Prepare command for server. */ + cmddata.argc = argc; + if (cmd_pack_argv( + argc, argv, cmddata.argv, sizeof cmddata.argv) != 0) { + log_warnx("command too long"); + return (1); + } + + client_write_server(msg, &cmddata, sizeof cmddata); + } else if (msg == MSG_SHELL) + client_write_server(msg, NULL, 0); + + /* Set the event and dispatch. */ + client_update_event(); + event_dispatch(); + + /* Print the exit message, if any, and exit. */ + if (client_attached) { + if (client_exitmsg != NULL && !login_shell) + printf("[%s]\n", client_exitmsg); + + ppid = getppid(); + if (client_exittype == MSG_DETACHKILL && ppid > 1) + kill(ppid, SIGHUP); + } + return (client_exitval); +} + +/* Send identify message to server with the file descriptors. */ +void +client_send_identify(int flags) +{ + struct msg_identify_data data; + char *term; + int fd; + + data.flags = flags; + + if (getcwd(data.cwd, sizeof data.cwd) == NULL) + *data.cwd = '\0'; + + term = getenv("TERM"); + if (term == NULL || + strlcpy(data.term, term, sizeof data.term) >= sizeof data.term) + *data.term = '\0'; + + if ((fd = dup(STDIN_FILENO)) == -1) + fatal("dup failed"); + imsg_compose(&client_ibuf, + MSG_IDENTIFY, PROTOCOL_VERSION, -1, fd, &data, sizeof data); + + if ((fd = dup(STDOUT_FILENO)) == -1) + fatal("dup failed"); + imsg_compose(&client_ibuf, + MSG_STDOUT, PROTOCOL_VERSION, -1, fd, NULL, 0); + + if ((fd = dup(STDERR_FILENO)) == -1) + fatal("dup failed"); + imsg_compose(&client_ibuf, + MSG_STDERR, PROTOCOL_VERSION, -1, fd, NULL, 0); +} + +/* Forward entire environment to server. */ +void +client_send_environ(void) +{ + struct msg_environ_data data; + char **var; + + for (var = environ; *var != NULL; var++) { + if (strlcpy(data.var, *var, sizeof data.var) >= sizeof data.var) + continue; + client_write_server(MSG_ENVIRON, &data, sizeof data); + } +} + +/* Write a message to the server without a file descriptor. */ +void +client_write_server(enum msgtype type, void *buf, size_t len) +{ + imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, -1, buf, len); +} + +/* Update client event based on whether it needs to read or read and write. */ +void +client_update_event(void) +{ + short events; + + event_del(&client_event); + events = EV_READ; + if (client_ibuf.w.queued > 0) + events |= EV_WRITE; + event_set( + &client_event, client_ibuf.fd, events, client_callback, shell_cmd); + event_add(&client_event, NULL); +} + +/* Callback to handle signals in the client. */ +/* ARGSUSED */ +void +client_signal(int sig, unused short events, unused void *data) +{ + struct sigaction sigact; + int status; + + if (!client_attached) { + switch (sig) { + case SIGCHLD: + waitpid(WAIT_ANY, &status, WNOHANG); + break; + case SIGTERM: + event_loopexit(NULL); + break; + } + } else { + switch (sig) { + case SIGHUP: + client_exitmsg = "lost tty"; + client_exitval = 1; + client_write_server(MSG_EXITING, NULL, 0); + break; + case SIGTERM: + client_exitmsg = "terminated"; + client_exitval = 1; + client_write_server(MSG_EXITING, NULL, 0); + break; + case SIGWINCH: + client_write_server(MSG_RESIZE, NULL, 0); + break; + case SIGCONT: + memset(&sigact, 0, sizeof sigact); + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = SA_RESTART; + sigact.sa_handler = SIG_IGN; + if (sigaction(SIGTSTP, &sigact, NULL) != 0) + fatal("sigaction failed"); + client_write_server(MSG_WAKEUP, NULL, 0); + break; + } + } + + client_update_event(); +} + +/* Callback for client imsg read events. */ +/* ARGSUSED */ +void +client_callback(unused int fd, short events, void *data) +{ + ssize_t n; + int retval; + + if (events & EV_READ) { + if ((n = imsg_read(&client_ibuf)) == -1 || n == 0) + goto lost_server; + if (client_attached) + retval = client_dispatch_attached(); + else + retval = client_dispatch_wait(data); + if (retval != 0) { + event_loopexit(NULL); + return; + } + } + + if (events & EV_WRITE) { + if (msgbuf_write(&client_ibuf.w) < 0) + goto lost_server; + } + + client_update_event(); + return; + +lost_server: + client_exitmsg = "lost server"; + client_exitval = 1; + event_loopexit(NULL); +} + +/* Dispatch imsgs when in wait state (before MSG_READY). */ +int +client_dispatch_wait(void *data) +{ + struct imsg imsg; + ssize_t n, datalen; + struct msg_shell_data shelldata; + struct msg_exit_data exitdata; + const char *shellcmd = data; + + if ((n = imsg_read(&client_ibuf)) == -1 || n == 0) + fatalx("imsg_read failed"); + + for (;;) { + if ((n = imsg_get(&client_ibuf, &imsg)) == -1) + fatalx("imsg_get failed"); + if (n == 0) + return (0); + datalen = imsg.hdr.len - IMSG_HEADER_SIZE; + + switch (imsg.hdr.type) { + case MSG_EXIT: + case MSG_SHUTDOWN: + if (datalen != sizeof exitdata) { + if (datalen != 0) + fatalx("bad MSG_EXIT size"); + } else { + memcpy(&exitdata, imsg.data, sizeof exitdata); + client_exitval = exitdata.retcode; + } + imsg_free(&imsg); + return (-1); + case MSG_READY: + if (datalen != 0) + fatalx("bad MSG_READY size"); + + client_attached = 1; + break; + case MSG_VERSION: + if (datalen != 0) + fatalx("bad MSG_VERSION size"); + + log_warnx("protocol version mismatch (client %u, " + "server %u)", PROTOCOL_VERSION, imsg.hdr.peerid); + client_exitval = 1; + + imsg_free(&imsg); + return (-1); + case MSG_SHELL: + if (datalen != sizeof shelldata) + fatalx("bad MSG_SHELL size"); + memcpy(&shelldata, imsg.data, sizeof shelldata); + shelldata.shell[(sizeof shelldata.shell) - 1] = '\0'; + + clear_signals(0); + + shell_exec(shelldata.shell, shellcmd); + /* NOTREACHED */ + default: + fatalx("unexpected message"); + } + + imsg_free(&imsg); + } +} + +/* Dispatch imsgs in attached state (after MSG_READY). */ +/* ARGSUSED */ +int +client_dispatch_attached(void) +{ + struct imsg imsg; + struct msg_lock_data lockdata; + struct sigaction sigact; + ssize_t n, datalen; + + for (;;) { + if ((n = imsg_get(&client_ibuf, &imsg)) == -1) + fatalx("imsg_get failed"); + if (n == 0) + return (0); + datalen = imsg.hdr.len - IMSG_HEADER_SIZE; + + log_debug("client got %d", imsg.hdr.type); + switch (imsg.hdr.type) { + case MSG_DETACHKILL: + case MSG_DETACH: + if (datalen != 0) + fatalx("bad MSG_DETACH size"); + + client_exittype = imsg.hdr.type; + if (imsg.hdr.type == MSG_DETACHKILL) + client_exitmsg = "detached and SIGHUP"; + else + client_exitmsg = "detached"; + client_write_server(MSG_EXITING, NULL, 0); + break; + case MSG_EXIT: + if (datalen != 0 && + datalen != sizeof (struct msg_exit_data)) + fatalx("bad MSG_EXIT size"); + + client_write_server(MSG_EXITING, NULL, 0); + client_exitmsg = "exited"; + break; + case MSG_EXITED: + if (datalen != 0) + fatalx("bad MSG_EXITED size"); + + imsg_free(&imsg); + return (-1); + case MSG_SHUTDOWN: + if (datalen != 0) + fatalx("bad MSG_SHUTDOWN size"); + + client_write_server(MSG_EXITING, NULL, 0); + client_exitmsg = "server exited"; + client_exitval = 1; + break; + case MSG_SUSPEND: + if (datalen != 0) + fatalx("bad MSG_SUSPEND size"); + + memset(&sigact, 0, sizeof sigact); + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = SA_RESTART; + sigact.sa_handler = SIG_DFL; + if (sigaction(SIGTSTP, &sigact, NULL) != 0) + fatal("sigaction failed"); + kill(getpid(), SIGTSTP); + break; + case MSG_LOCK: + if (datalen != sizeof lockdata) + fatalx("bad MSG_LOCK size"); + memcpy(&lockdata, imsg.data, sizeof lockdata); + + lockdata.cmd[(sizeof lockdata.cmd) - 1] = '\0'; + system(lockdata.cmd); + client_write_server(MSG_UNLOCK, NULL, 0); + break; + default: + fatalx("unexpected message"); + } + + imsg_free(&imsg); + } +} diff --git a/external/bsd/tmux/dist/clock.c b/external/bsd/tmux/dist/clock.c new file mode 100644 index 000000000..64d22f60a --- /dev/null +++ b/external/bsd/tmux/dist/clock.c @@ -0,0 +1,159 @@ +/* $Id: clock.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +const char clock_table[14][5][5] = { + { { 1,1,1,1,1 }, /* 0 */ + { 1,0,0,0,1 }, + { 1,0,0,0,1 }, + { 1,0,0,0,1 }, + { 1,1,1,1,1 } }, + { { 0,0,0,0,1 }, /* 1 */ + { 0,0,0,0,1 }, + { 0,0,0,0,1 }, + { 0,0,0,0,1 }, + { 0,0,0,0,1 } }, + { { 1,1,1,1,1 }, /* 2 */ + { 0,0,0,0,1 }, + { 1,1,1,1,1 }, + { 1,0,0,0,0 }, + { 1,1,1,1,1 } }, + { { 1,1,1,1,1 }, /* 3 */ + { 0,0,0,0,1 }, + { 1,1,1,1,1 }, + { 0,0,0,0,1 }, + { 1,1,1,1,1 } }, + { { 1,0,0,0,1 }, /* 4 */ + { 1,0,0,0,1 }, + { 1,1,1,1,1 }, + { 0,0,0,0,1 }, + { 0,0,0,0,1 } }, + { { 1,1,1,1,1 }, /* 5 */ + { 1,0,0,0,0 }, + { 1,1,1,1,1 }, + { 0,0,0,0,1 }, + { 1,1,1,1,1 } }, + { { 1,1,1,1,1 }, /* 6 */ + { 1,0,0,0,0 }, + { 1,1,1,1,1 }, + { 1,0,0,0,1 }, + { 1,1,1,1,1 } }, + { { 1,1,1,1,1 }, /* 7 */ + { 0,0,0,0,1 }, + { 0,0,0,0,1 }, + { 0,0,0,0,1 }, + { 0,0,0,0,1 } }, + { { 1,1,1,1,1 }, /* 8 */ + { 1,0,0,0,1 }, + { 1,1,1,1,1 }, + { 1,0,0,0,1 }, + { 1,1,1,1,1 } }, + { { 1,1,1,1,1 }, /* 9 */ + { 1,0,0,0,1 }, + { 1,1,1,1,1 }, + { 0,0,0,0,1 }, + { 1,1,1,1,1 } }, + { { 0,0,0,0,0 }, /* : */ + { 0,0,1,0,0 }, + { 0,0,0,0,0 }, + { 0,0,1,0,0 }, + { 0,0,0,0,0 } }, + { { 1,1,1,1,1 }, /* A */ + { 1,0,0,0,1 }, + { 1,1,1,1,1 }, + { 1,0,0,0,1 }, + { 1,0,0,0,1 } }, + { { 1,1,1,1,1 }, /* P */ + { 1,0,0,0,1 }, + { 1,1,1,1,1 }, + { 1,0,0,0,0 }, + { 1,0,0,0,0 } }, + { { 1,0,0,0,1 }, /* M */ + { 1,1,0,1,1 }, + { 1,0,1,0,1 }, + { 1,0,0,0,1 }, + { 1,0,0,0,1 } }, +}; + +void +clock_draw(struct screen_write_ctx *ctx, int colour, int style) +{ + struct screen *s = ctx->s; + struct grid_cell gc; + char tim[64], *ptr; + time_t t; + u_int i, j, x, y, idx; + + t = time(NULL); + if (style == 0) + strftime(tim, sizeof tim, "%l:%M %p", localtime(&t)); + else + strftime(tim, sizeof tim, "%H:%M", localtime(&t)); + + screen_write_clearscreen(ctx); + + if (screen_size_x(s) < 6 * strlen(tim) || screen_size_y(s) < 6) { + if (screen_size_x(s) >= strlen(tim) && screen_size_y(s) != 0) { + x = (screen_size_x(s) / 2) - (strlen(tim) / 2); + y = screen_size_y(s) / 2; + screen_write_cursormove(ctx, x, y); + + memcpy(&gc, &grid_default_cell, sizeof gc); + colour_set_fg(&gc, colour); + screen_write_puts(ctx, &gc, "%s", tim); + } + return; + } + + x = (screen_size_x(s) / 2) - 3 * strlen(tim); + y = (screen_size_y(s) / 2) - 3; + + memcpy(&gc, &grid_default_cell, sizeof gc); + colour_set_bg(&gc, colour); + for (ptr = tim; *ptr != '\0'; ptr++) { + if (*ptr >= '0' && *ptr <= '9') + idx = *ptr - '0'; + else if (*ptr == ':') + idx = 10; + else if (*ptr == 'A') + idx = 11; + else if (*ptr == 'P') + idx = 12; + else if (*ptr == 'M') + idx = 13; + else { + x += 6; + continue; + } + + for (j = 0; j < 5; j++) { + for (i = 0; i < 5; i++) { + screen_write_cursormove(ctx, x + i, y + j); + if (clock_table[idx][j][i]) + screen_write_putc(ctx, &gc, ' '); + } + } + x += 6; + } +} diff --git a/external/bsd/tmux/dist/cmd-attach-session.c b/external/bsd/tmux/dist/cmd-attach-session.c new file mode 100644 index 000000000..1702bbe45 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-attach-session.c @@ -0,0 +1,112 @@ +/* $Id: cmd-attach-session.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "tmux.h" + +/* + * Attach existing session to the current terminal. + */ + +int cmd_attach_session_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_attach_session_entry = { + "attach-session", "attach", + "drt:", 0, 0, + "[-dr] " CMD_TARGET_SESSION_USAGE, + CMD_CANTNEST|CMD_STARTSERVER|CMD_SENDENVIRON, + NULL, + NULL, + cmd_attach_session_exec +}; + +int +cmd_attach_session_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct session *s; + struct client *c; + const char *update; + char *overrides, *cause; + u_int i; + + if (RB_EMPTY(&sessions)) { + ctx->error(ctx, "no sessions"); + return (-1); + } + + if ((s = cmd_find_session(ctx, args_get(args, 't'), 1)) == NULL) + return (-1); + + if (ctx->cmdclient == NULL && ctx->curclient == NULL) + return (0); + + if (ctx->cmdclient == NULL) { + if (args_has(self->args, 'd')) { + /* + * Can't use server_write_session in case attaching to + * the same session as currently attached to. + */ + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session != s) + continue; + if (c == ctx->curclient) + continue; + server_write_client(c, MSG_DETACH, NULL, 0); + } + } + + ctx->curclient->session = s; + session_update_activity(s); + server_redraw_client(ctx->curclient); + } else { + if (!(ctx->cmdclient->flags & CLIENT_TERMINAL)) { + ctx->error(ctx, "not a terminal"); + return (-1); + } + + overrides = + options_get_string(&s->options, "terminal-overrides"); + if (tty_open(&ctx->cmdclient->tty, overrides, &cause) != 0) { + ctx->error(ctx, "terminal open failed: %s", cause); + xfree(cause); + return (-1); + } + + if (args_has(self->args, 'r')) + ctx->cmdclient->flags |= CLIENT_READONLY; + + if (args_has(self->args, 'd')) + server_write_session(s, MSG_DETACH, NULL, 0); + + ctx->cmdclient->session = s; + session_update_activity(s); + server_write_client(ctx->cmdclient, MSG_READY, NULL, 0); + + update = options_get_string(&s->options, "update-environment"); + environ_update(update, &ctx->cmdclient->environ, &s->environ); + + server_redraw_client(ctx->cmdclient); + } + recalculate_sizes(); + server_update_socket(); + + return (1); /* 1 means don't tell command client to exit */ +} diff --git a/external/bsd/tmux/dist/cmd-bind-key.c b/external/bsd/tmux/dist/cmd-bind-key.c new file mode 100644 index 000000000..b5fc7590f --- /dev/null +++ b/external/bsd/tmux/dist/cmd-bind-key.c @@ -0,0 +1,120 @@ +/* $Id: cmd-bind-key.c,v 1.1.1.2 2011/08/17 18:40:03 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * Bind a key to a command, this recurses through cmd_*. + */ + +int cmd_bind_key_check(struct args *); +int cmd_bind_key_exec(struct cmd *, struct cmd_ctx *); + +int cmd_bind_key_table(struct cmd *, struct cmd_ctx *, int); + +const struct cmd_entry cmd_bind_key_entry = { + "bind-key", "bind", + "cnrt:", 1, -1, + "[-cnr] [-t key-table] key command [arguments]", + 0, + NULL, + cmd_bind_key_check, + cmd_bind_key_exec +}; + +int +cmd_bind_key_check(struct args *args) +{ + if (args_has(args, 't')) { + if (args->argc != 2) + return (-1); + } else { + if (args->argc < 2) + return (-1); + } + return (0); +} + +int +cmd_bind_key_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + char *cause; + struct cmd_list *cmdlist; + int key; + + key = key_string_lookup_string(args->argv[0]); + if (key == KEYC_NONE) { + ctx->error(ctx, "unknown key: %s", args->argv[0]); + return (-1); + } + + if (args_has(args, 't')) + return (cmd_bind_key_table(self, ctx, key)); + + cmdlist = cmd_list_parse(args->argc - 1, args->argv + 1, &cause); + if (cmdlist == NULL) { + ctx->error(ctx, "%s", cause); + xfree(cause); + return (-1); + } + + if (!args_has(args, 'n')) + key |= KEYC_PREFIX; + key_bindings_add(key, args_has(args, 'r'), cmdlist); + return (0); +} + +int +cmd_bind_key_table(struct cmd *self, struct cmd_ctx *ctx, int key) +{ + struct args *args = self->args; + const char *tablename; + const struct mode_key_table *mtab; + struct mode_key_binding *mbind, mtmp; + enum mode_key_cmd cmd; + + tablename = args_get(args, 't'); + if ((mtab = mode_key_findtable(tablename)) == NULL) { + ctx->error(ctx, "unknown key table: %s", tablename); + return (-1); + } + + cmd = mode_key_fromstring(mtab->cmdstr, args->argv[1]); + if (cmd == MODEKEY_NONE) { + ctx->error(ctx, "unknown command: %s", args->argv[1]); + return (-1); + } + + mtmp.key = key; + mtmp.mode = !!args_has(args, 'c'); + if ((mbind = SPLAY_FIND(mode_key_tree, mtab->tree, &mtmp)) != NULL) { + mbind->cmd = cmd; + return (0); + } + mbind = xmalloc(sizeof *mbind); + mbind->key = mtmp.key; + mbind->mode = mtmp.mode; + mbind->cmd = cmd; + SPLAY_INSERT(mode_key_tree, mtab->tree, mbind); + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-break-pane.c b/external/bsd/tmux/dist/cmd-break-pane.c new file mode 100644 index 000000000..ba27d7394 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-break-pane.c @@ -0,0 +1,89 @@ +/* $Id: cmd-break-pane.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * Break pane off into a window. + */ + +int cmd_break_pane_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_break_pane_entry = { + "break-pane", "breakp", + "dt:", 0, 0, + "[-d] " CMD_TARGET_PANE_USAGE, + 0, + NULL, + NULL, + cmd_break_pane_exec +}; + +int +cmd_break_pane_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct winlink *wl; + struct session *s; + struct window_pane *wp; + struct window *w; + char *cause; + int base_idx; + + if ((wl = cmd_find_pane(ctx, args_get(args, 't'), &s, &wp)) == NULL) + return (-1); + + if (window_count_panes(wl->window) == 1) { + ctx->error(ctx, "can't break with only one pane"); + return (-1); + } + + w = wl->window; + TAILQ_REMOVE(&w->panes, wp, entry); + if (wp == w->active) { + w->active = w->last; + w->last = NULL; + if (w->active == NULL) { + w->active = TAILQ_PREV(wp, window_panes, entry); + if (w->active == NULL) + w->active = TAILQ_NEXT(wp, entry); + } + } else if (wp == w->last) + w->last = NULL; + layout_close_pane(wp); + + w = wp->window = window_create1(s->sx, s->sy); + TAILQ_INSERT_HEAD(&w->panes, wp, entry); + w->active = wp; + w->name = default_window_name(w); + layout_init(w); + + base_idx = options_get_number(&s->options, "base-index"); + wl = session_attach(s, w, -1 - base_idx, &cause); /* can't fail */ + if (!args_has(self->args, 'd')) + session_select(s, wl->idx); + + server_redraw_session(s); + server_status_session_group(s); + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-capture-pane.c b/external/bsd/tmux/dist/cmd-capture-pane.c new file mode 100644 index 000000000..c680842c5 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-capture-pane.c @@ -0,0 +1,123 @@ +/* $Id: cmd-capture-pane.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Jonathan Alvarado + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +/* + * Write the entire contents of a pane to a buffer. + */ + +int cmd_capture_pane_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_capture_pane_entry = { + "capture-pane", "capturep", + "b:E:S:t:", 0, 0, + "[-b buffer-index] [-E end-line] [-S start-line] [-t target-pane]", + 0, + NULL, + NULL, + cmd_capture_pane_exec +}; + +int +cmd_capture_pane_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct window_pane *wp; + char *buf, *line, *cause; + struct screen *s; + struct grid *gd; + int buffer, n; + u_int i, limit, top, bottom, tmp; + size_t len, linelen; + + if (cmd_find_pane(ctx, args_get(args, 't'), NULL, &wp) == NULL) + return (-1); + s = &wp->base; + gd = s->grid; + + buf = NULL; + len = 0; + + n = args_strtonum(args, 'S', SHRT_MIN, SHRT_MAX, &cause); + if (cause != NULL) { + top = gd->hsize; + xfree(cause); + } else if (n < 0 && (u_int) -n > gd->hsize) + top = 0; + else + top = gd->hsize + n; + if (top > gd->hsize + gd->sy - 1) + top = gd->hsize + gd->sy - 1; + + n = args_strtonum(args, 'E', SHRT_MIN, SHRT_MAX, &cause); + if (cause != NULL) { + bottom = gd->hsize + gd->sy - 1; + xfree(cause); + } else if (n < 0 && (u_int) -n > gd->hsize) + bottom = 0; + else + bottom = gd->hsize + n; + if (bottom > gd->hsize + gd->sy - 1) + bottom = gd->hsize + gd->sy - 1; + + if (bottom < top) { + tmp = bottom; + bottom = top; + top = tmp; + } + + for (i = top; i <= bottom; i++) { + line = grid_string_cells(s->grid, 0, i, screen_size_x(s)); + linelen = strlen(line); + + buf = xrealloc(buf, 1, len + linelen + 1); + memcpy(buf + len, line, linelen); + len += linelen; + buf[len++] = '\n'; + + xfree(line); + } + + limit = options_get_number(&global_options, "buffer-limit"); + + if (!args_has(args, 'b')) { + paste_add(&global_buffers, buf, len, limit); + return (0); + } + + buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); + if (cause != NULL) { + ctx->error(ctx, "buffer %s", cause); + xfree(cause); + return (-1); + } + + if (paste_replace(&global_buffers, buffer, buf, len) != 0) { + ctx->error(ctx, "no buffer %d", buffer); + xfree(buf); + return (-1); + } + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-choose-buffer.c b/external/bsd/tmux/dist/cmd-choose-buffer.c new file mode 100644 index 000000000..fd7dbc45c --- /dev/null +++ b/external/bsd/tmux/dist/cmd-choose-buffer.c @@ -0,0 +1,143 @@ +/* $Id: cmd-choose-buffer.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2010 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * Enter choice mode to choose a buffer. + */ + +int cmd_choose_buffer_exec(struct cmd *, struct cmd_ctx *); + +void cmd_choose_buffer_callback(void *, int); +void cmd_choose_buffer_free(void *); + +const struct cmd_entry cmd_choose_buffer_entry = { + "choose-buffer", NULL, + "t:", 0, 1, + CMD_TARGET_WINDOW_USAGE " [template]", + 0, + NULL, + NULL, + cmd_choose_buffer_exec +}; + +struct cmd_choose_buffer_data { + struct client *client; + char *template; +}; + +int +cmd_choose_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct cmd_choose_buffer_data *cdata; + struct winlink *wl; + struct paste_buffer *pb; + u_int idx; + char *tmp; + + if (ctx->curclient == NULL) { + ctx->error(ctx, "must be run interactively"); + return (-1); + } + + if ((wl = cmd_find_window(ctx, args_get(args, 't'), NULL)) == NULL) + return (-1); + + if (paste_get_top(&global_buffers) == NULL) + return (0); + + if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) + return (0); + + idx = 0; + while ((pb = paste_walk_stack(&global_buffers, &idx)) != NULL) { + tmp = paste_print(pb, 50); + window_choose_add(wl->window->active, idx - 1, + "%u: %zu bytes: \"%s\"", idx - 1, pb->size, tmp); + xfree(tmp); + } + + cdata = xmalloc(sizeof *cdata); + if (args->argc != 0) + cdata->template = xstrdup(args->argv[0]); + else + cdata->template = xstrdup("paste-buffer -b '%%'"); + cdata->client = ctx->curclient; + cdata->client->references++; + + window_choose_ready(wl->window->active, + 0, cmd_choose_buffer_callback, cmd_choose_buffer_free, cdata); + + return (0); +} + +void +cmd_choose_buffer_callback(void *data, int idx) +{ + struct cmd_choose_buffer_data *cdata = data; + struct cmd_list *cmdlist; + struct cmd_ctx ctx; + char *template, *cause, tmp[16]; + + if (idx == -1) + return; + if (cdata->client->flags & CLIENT_DEAD) + return; + + xsnprintf(tmp, sizeof tmp, "%u", idx); + template = cmd_template_replace(cdata->template, tmp, 1); + + if (cmd_string_parse(template, &cmdlist, &cause) != 0) { + if (cause != NULL) { + *cause = toupper((u_char) *cause); + status_message_set(cdata->client, "%s", cause); + xfree(cause); + } + xfree(template); + return; + } + xfree(template); + + ctx.msgdata = NULL; + ctx.curclient = cdata->client; + + ctx.error = key_bindings_error; + ctx.print = key_bindings_print; + ctx.info = key_bindings_info; + + ctx.cmdclient = NULL; + + cmd_list_exec(cmdlist, &ctx); + cmd_list_free(cmdlist); +} + +void +cmd_choose_buffer_free(void *data) +{ + struct cmd_choose_buffer_data *cdata = data; + + cdata->client->references--; + xfree(cdata->template); + xfree(cdata); +} diff --git a/external/bsd/tmux/dist/cmd-choose-client.c b/external/bsd/tmux/dist/cmd-choose-client.c new file mode 100644 index 000000000..2a67fe91a --- /dev/null +++ b/external/bsd/tmux/dist/cmd-choose-client.c @@ -0,0 +1,151 @@ +/* $Id: cmd-choose-client.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * Enter choice mode to choose a client. + */ + +int cmd_choose_client_exec(struct cmd *, struct cmd_ctx *); + +void cmd_choose_client_callback(void *, int); +void cmd_choose_client_free(void *); + +const struct cmd_entry cmd_choose_client_entry = { + "choose-client", NULL, + "t:", 0, 1, + CMD_TARGET_WINDOW_USAGE " [template]", + 0, + NULL, + NULL, + cmd_choose_client_exec +}; + +struct cmd_choose_client_data { + struct client *client; + char *template; +}; + +int +cmd_choose_client_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct cmd_choose_client_data *cdata; + struct winlink *wl; + struct client *c; + u_int i, idx, cur; + + if (ctx->curclient == NULL) { + ctx->error(ctx, "must be run interactively"); + return (-1); + } + + if ((wl = cmd_find_window(ctx, args_get(args, 't'), NULL)) == NULL) + return (-1); + + if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) + return (0); + + cur = idx = 0; + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session == NULL) + continue; + if (c == ctx->curclient) + cur = idx; + idx++; + + window_choose_add(wl->window->active, i, + "%s: %s [%ux%u %s]%s", c->tty.path, + c->session->name, c->tty.sx, c->tty.sy, + c->tty.termname, c->tty.flags & TTY_UTF8 ? " (utf8)" : ""); + } + + cdata = xmalloc(sizeof *cdata); + if (args->argc != 0) + cdata->template = xstrdup(args->argv[0]); + else + cdata->template = xstrdup("detach-client -t '%%'"); + cdata->client = ctx->curclient; + cdata->client->references++; + + window_choose_ready(wl->window->active, + cur, cmd_choose_client_callback, cmd_choose_client_free, cdata); + + return (0); +} + +void +cmd_choose_client_callback(void *data, int idx) +{ + struct cmd_choose_client_data *cdata = data; + struct client *c; + struct cmd_list *cmdlist; + struct cmd_ctx ctx; + char *template, *cause; + + if (idx == -1) + return; + if (cdata->client->flags & CLIENT_DEAD) + return; + + if ((u_int) idx > ARRAY_LENGTH(&clients) - 1) + return; + c = ARRAY_ITEM(&clients, idx); + if (c == NULL || c->session == NULL) + return; + template = cmd_template_replace(cdata->template, c->tty.path, 1); + + if (cmd_string_parse(template, &cmdlist, &cause) != 0) { + if (cause != NULL) { + *cause = toupper((u_char) *cause); + status_message_set(c, "%s", cause); + xfree(cause); + } + xfree(template); + return; + } + xfree(template); + + ctx.msgdata = NULL; + ctx.curclient = cdata->client; + + ctx.error = key_bindings_error; + ctx.print = key_bindings_print; + ctx.info = key_bindings_info; + + ctx.cmdclient = NULL; + + cmd_list_exec(cmdlist, &ctx); + cmd_list_free(cmdlist); +} + +void +cmd_choose_client_free(void *data) +{ + struct cmd_choose_client_data *cdata = data; + + cdata->client->references--; + xfree(cdata->template); + xfree(cdata); +} diff --git a/external/bsd/tmux/dist/cmd-choose-session.c b/external/bsd/tmux/dist/cmd-choose-session.c new file mode 100644 index 000000000..6bd1c34ea --- /dev/null +++ b/external/bsd/tmux/dist/cmd-choose-session.c @@ -0,0 +1,156 @@ +/* $Id: cmd-choose-session.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * Enter choice mode to choose a session. + */ + +int cmd_choose_session_exec(struct cmd *, struct cmd_ctx *); + +void cmd_choose_session_callback(void *, int); +void cmd_choose_session_free(void *); + +const struct cmd_entry cmd_choose_session_entry = { + "choose-session", NULL, + "t:", 0, 1, + CMD_TARGET_WINDOW_USAGE " [template]", + 0, + NULL, + NULL, + cmd_choose_session_exec +}; + +struct cmd_choose_session_data { + struct client *client; + char *template; +}; + +int +cmd_choose_session_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct cmd_choose_session_data *cdata; + struct winlink *wl; + struct session *s; + struct session_group *sg; + u_int idx, sgidx, cur; + char tmp[64]; + + if (ctx->curclient == NULL) { + ctx->error(ctx, "must be run interactively"); + return (-1); + } + + if ((wl = cmd_find_window(ctx, args_get(args, 't'), NULL)) == NULL) + return (-1); + + if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) + return (0); + + cur = idx = 0; + RB_FOREACH(s, sessions, &sessions) { + if (s == ctx->curclient->session) + cur = idx; + idx++; + + sg = session_group_find(s); + if (sg == NULL) + *tmp = '\0'; + else { + sgidx = session_group_index(sg); + xsnprintf(tmp, sizeof tmp, " (group %u)", sgidx); + } + + window_choose_add(wl->window->active, s->idx, + "%s: %u windows [%ux%u]%s%s", s->name, + winlink_count(&s->windows), s->sx, s->sy, + tmp, s->flags & SESSION_UNATTACHED ? "" : " (attached)"); + } + + cdata = xmalloc(sizeof *cdata); + if (args->argc != 0) + cdata->template = xstrdup(args->argv[0]); + else + cdata->template = xstrdup("switch-client -t '%%'"); + cdata->client = ctx->curclient; + cdata->client->references++; + + window_choose_ready(wl->window->active, + cur, cmd_choose_session_callback, cmd_choose_session_free, cdata); + + return (0); +} + +void +cmd_choose_session_callback(void *data, int idx) +{ + struct cmd_choose_session_data *cdata = data; + struct session *s; + struct cmd_list *cmdlist; + struct cmd_ctx ctx; + char *template, *cause; + + if (idx == -1) + return; + if (cdata->client->flags & CLIENT_DEAD) + return; + + s = session_find_by_index(idx); + if (s == NULL) + return; + template = cmd_template_replace(cdata->template, s->name, 1); + + if (cmd_string_parse(template, &cmdlist, &cause) != 0) { + if (cause != NULL) { + *cause = toupper((u_char) *cause); + status_message_set(cdata->client, "%s", cause); + xfree(cause); + } + xfree(template); + return; + } + xfree(template); + + ctx.msgdata = NULL; + ctx.curclient = cdata->client; + + ctx.error = key_bindings_error; + ctx.print = key_bindings_print; + ctx.info = key_bindings_info; + + ctx.cmdclient = NULL; + + cmd_list_exec(cmdlist, &ctx); + cmd_list_free(cmdlist); +} + +void +cmd_choose_session_free(void *data) +{ + struct cmd_choose_session_data *cdata = data; + + cdata->client->references--; + xfree(cdata->template); + xfree(cdata); +} diff --git a/external/bsd/tmux/dist/cmd-choose-window.c b/external/bsd/tmux/dist/cmd-choose-window.c new file mode 100644 index 000000000..1061446ec --- /dev/null +++ b/external/bsd/tmux/dist/cmd-choose-window.c @@ -0,0 +1,169 @@ +/* $Id: cmd-choose-window.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * Enter choice mode to choose a window. + */ + +int cmd_choose_window_exec(struct cmd *, struct cmd_ctx *); + +void cmd_choose_window_callback(void *, int); +void cmd_choose_window_free(void *); + +const struct cmd_entry cmd_choose_window_entry = { + "choose-window", NULL, + "t:", 0, 1, + CMD_TARGET_WINDOW_USAGE " [template]", + 0, + NULL, + NULL, + cmd_choose_window_exec +}; + +struct cmd_choose_window_data { + struct client *client; + struct session *session; + char *template; +}; + +int +cmd_choose_window_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct cmd_choose_window_data *cdata; + struct session *s; + struct winlink *wl, *wm; + struct window *w; + u_int idx, cur; + char *flags, *title; + const char *left, *right; + + if (ctx->curclient == NULL) { + ctx->error(ctx, "must be run interactively"); + return (-1); + } + s = ctx->curclient->session; + + if ((wl = cmd_find_window(ctx, args_get(args, 't'), NULL)) == NULL) + return (-1); + + if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) + return (0); + + cur = idx = 0; + RB_FOREACH(wm, winlinks, &s->windows) { + w = wm->window; + + if (wm == s->curw) + cur = idx; + idx++; + + flags = window_printable_flags(s, wm); + title = w->active->screen->title; + if (wm == wl) + title = w->active->base.title; + left = " \""; + right = "\""; + if (*title == '\0') + left = right = ""; + + window_choose_add(wl->window->active, + wm->idx, "%3d: %s%s [%ux%u] (%u panes%s)%s%s%s", + wm->idx, w->name, flags, w->sx, w->sy, window_count_panes(w), + w->active->fd == -1 ? ", dead" : "", + left, title, right); + + xfree(flags); + } + + cdata = xmalloc(sizeof *cdata); + if (args->argc != 0) + cdata->template = xstrdup(args->argv[0]); + else + cdata->template = xstrdup("select-window -t '%%'"); + cdata->session = s; + cdata->session->references++; + cdata->client = ctx->curclient; + cdata->client->references++; + + window_choose_ready(wl->window->active, + cur, cmd_choose_window_callback, cmd_choose_window_free, cdata); + + return (0); +} + +void +cmd_choose_window_callback(void *data, int idx) +{ + struct cmd_choose_window_data *cdata = data; + struct session *s = cdata->session; + struct cmd_list *cmdlist; + struct cmd_ctx ctx; + char *target, *template, *cause; + + if (idx == -1) + return; + if (!session_alive(s)) + return; + if (cdata->client->flags & CLIENT_DEAD) + return; + + xasprintf(&target, "%s:%d", s->name, idx); + template = cmd_template_replace(cdata->template, target, 1); + xfree(target); + + if (cmd_string_parse(template, &cmdlist, &cause) != 0) { + if (cause != NULL) { + *cause = toupper((u_char) *cause); + status_message_set(cdata->client, "%s", cause); + xfree(cause); + } + xfree(template); + return; + } + xfree(template); + + ctx.msgdata = NULL; + ctx.curclient = cdata->client; + + ctx.error = key_bindings_error; + ctx.print = key_bindings_print; + ctx.info = key_bindings_info; + + ctx.cmdclient = NULL; + + cmd_list_exec(cmdlist, &ctx); + cmd_list_free(cmdlist); +} + +void +cmd_choose_window_free(void *data) +{ + struct cmd_choose_window_data *cdata = data; + + cdata->session->references--; + cdata->client->references--; + xfree(cdata->template); + xfree(cdata); +} diff --git a/external/bsd/tmux/dist/cmd-clear-history.c b/external/bsd/tmux/dist/cmd-clear-history.c new file mode 100644 index 000000000..533b703df --- /dev/null +++ b/external/bsd/tmux/dist/cmd-clear-history.c @@ -0,0 +1,54 @@ +/* $Id: cmd-clear-history.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "tmux.h" + +/* + * Clear pane history. + */ + +int cmd_clear_history_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_clear_history_entry = { + "clear-history", "clearhist", + "t:", 0, 0, + CMD_TARGET_PANE_USAGE, + 0, + NULL, + NULL, + cmd_clear_history_exec +}; + +int +cmd_clear_history_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct window_pane *wp; + struct grid *gd; + + if (cmd_find_pane(ctx, args_get(args, 't'), NULL, &wp) == NULL) + return (-1); + gd = wp->base.grid; + + grid_move_lines(gd, 0, gd->hsize, gd->sy); + gd->hsize = 0; + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-clock-mode.c b/external/bsd/tmux/dist/cmd-clock-mode.c new file mode 100644 index 000000000..a446c622b --- /dev/null +++ b/external/bsd/tmux/dist/cmd-clock-mode.c @@ -0,0 +1,51 @@ +/* $Id: cmd-clock-mode.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "tmux.h" + +/* + * Enter clock mode. + */ + +int cmd_clock_mode_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_clock_mode_entry = { + "clock-mode", NULL, + "t:", 0, 0, + CMD_TARGET_PANE_USAGE, + 0, + NULL, + NULL, + cmd_clock_mode_exec +}; + +int +cmd_clock_mode_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct window_pane *wp; + + if (cmd_find_pane(ctx, args_get(args, 't'), NULL, &wp) == NULL) + return (-1); + + window_pane_set_mode(wp, &window_clock_mode); + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-command-prompt.c b/external/bsd/tmux/dist/cmd-command-prompt.c new file mode 100644 index 000000000..54e957ef4 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-command-prompt.c @@ -0,0 +1,215 @@ +/* $Id: cmd-command-prompt.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2008 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include + +#include "tmux.h" + +/* + * Prompt for command in client. + */ + +void cmd_command_prompt_key_binding(struct cmd *, int); +int cmd_command_prompt_check(struct args *); +int cmd_command_prompt_exec(struct cmd *, struct cmd_ctx *); + +int cmd_command_prompt_callback(void *, const char *); +void cmd_command_prompt_free(void *); + +const struct cmd_entry cmd_command_prompt_entry = { + "command-prompt", NULL, + "I:p:t:", 0, 1, + "[-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " [template]", + 0, + cmd_command_prompt_key_binding, + NULL, + cmd_command_prompt_exec +}; + +struct cmd_command_prompt_cdata { + struct client *c; + char *inputs; + char *next_input; + char *next_prompt; + char *prompts; + char *template; + int idx; +}; + +void +cmd_command_prompt_key_binding(struct cmd *self, int key) +{ + switch (key) { + case '$': + self->args = args_create(1, "rename-session '%%'"); + args_set(self->args, 'I', "#S"); + break; + case ',': + self->args = args_create(1, "rename-window '%%'"); + args_set(self->args, 'I', "#W"); + break; + case '.': + self->args = args_create(1, "move-window -t '%%'"); + break; + case 'f': + self->args = args_create(1, "find-window '%%'"); + break; + case '\'': + self->args = args_create(1, "select-window -t ':%%'"); + args_set(self->args, 'p', "index"); + break; + default: + self->args = args_create(0); + break; + } +} + +int +cmd_command_prompt_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + const char *inputs, *prompts; + struct cmd_command_prompt_cdata *cdata; + struct client *c; + char *prompt, *ptr, *input = NULL; + size_t n; + + if ((c = cmd_find_client(ctx, args_get(args, 't'))) == NULL) + return (-1); + + if (c->prompt_string != NULL) + return (0); + + cdata = xmalloc(sizeof *cdata); + cdata->c = c; + cdata->idx = 1; + cdata->inputs = NULL; + cdata->next_input = NULL; + cdata->next_prompt = NULL; + cdata->prompts = NULL; + cdata->template = NULL; + + if (args->argc != 0) + cdata->template = xstrdup(args->argv[0]); + else + cdata->template = xstrdup("%1"); + + if ((prompts = args_get(args, 'p')) != NULL) + cdata->prompts = xstrdup(prompts); + else if (args->argc != 0) { + n = strcspn(cdata->template, " ,"); + xasprintf(&cdata->prompts, "(%.*s) ", (int) n, cdata->template); + } else + cdata->prompts = xstrdup(":"); + + /* Get first prompt. */ + cdata->next_prompt = cdata->prompts; + ptr = strsep(&cdata->next_prompt, ","); + if (prompts == NULL) + prompt = xstrdup(ptr); + else + xasprintf(&prompt, "%s ", ptr); + + /* Get initial prompt input. */ + if ((inputs = args_get(args, 'I')) != NULL) { + cdata->inputs = xstrdup(inputs); + cdata->next_input = cdata->inputs; + input = strsep(&cdata->next_input, ","); + } + + status_prompt_set(c, prompt, input, cmd_command_prompt_callback, + cmd_command_prompt_free, cdata, 0); + xfree(prompt); + + return (0); +} + +int +cmd_command_prompt_callback(void *data, const char *s) +{ + struct cmd_command_prompt_cdata *cdata = data; + struct client *c = cdata->c; + struct cmd_list *cmdlist; + struct cmd_ctx ctx; + char *cause, *new_template, *prompt, *ptr; + char *input = NULL; + + if (s == NULL) + return (0); + + new_template = cmd_template_replace(cdata->template, s, cdata->idx); + xfree(cdata->template); + cdata->template = new_template; + + /* + * Check if there are more prompts; if so, get its respective input + * and update the prompt data. + */ + if ((ptr = strsep(&cdata->next_prompt, ",")) != NULL) { + xasprintf(&prompt, "%s ", ptr); + input = strsep(&cdata->next_input, ","); + status_prompt_update(c, prompt, input); + + xfree(prompt); + cdata->idx++; + return (1); + } + + if (cmd_string_parse(new_template, &cmdlist, &cause) != 0) { + if (cause != NULL) { + *cause = toupper((u_char) *cause); + status_message_set(c, "%s", cause); + xfree(cause); + } + return (0); + } + + ctx.msgdata = NULL; + ctx.curclient = c; + + ctx.error = key_bindings_error; + ctx.print = key_bindings_print; + ctx.info = key_bindings_info; + + ctx.cmdclient = NULL; + + cmd_list_exec(cmdlist, &ctx); + cmd_list_free(cmdlist); + + if (c->prompt_callbackfn != (void *) &cmd_command_prompt_callback) + return (1); + return (0); +} + +void +cmd_command_prompt_free(void *data) +{ + struct cmd_command_prompt_cdata *cdata = data; + + if (cdata->inputs != NULL) + xfree(cdata->inputs); + if (cdata->prompts != NULL) + xfree(cdata->prompts); + if (cdata->template != NULL) + xfree(cdata->template); + xfree(cdata); +} diff --git a/external/bsd/tmux/dist/cmd-confirm-before.c b/external/bsd/tmux/dist/cmd-confirm-before.c new file mode 100644 index 000000000..26fa0ef1f --- /dev/null +++ b/external/bsd/tmux/dist/cmd-confirm-before.c @@ -0,0 +1,150 @@ +/* $Id: cmd-confirm-before.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Tiago Cunha + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include "tmux.h" + +/* + * Asks for confirmation before executing a command. + */ + +void cmd_confirm_before_key_binding(struct cmd *, int); +int cmd_confirm_before_exec(struct cmd *, struct cmd_ctx *); + +int cmd_confirm_before_callback(void *, const char *); +void cmd_confirm_before_free(void *); + +const struct cmd_entry cmd_confirm_before_entry = { + "confirm-before", "confirm", + "p:t:", 1, 1, + "[-p prompt] " CMD_TARGET_CLIENT_USAGE " command", + 0, + cmd_confirm_before_key_binding, + NULL, + cmd_confirm_before_exec +}; + +struct cmd_confirm_before_data { + struct client *c; + char *cmd; +}; + +void +cmd_confirm_before_key_binding(struct cmd *self, int key) +{ + switch (key) { + case '&': + self->args = args_create(1, "kill-window"); + args_set(self->args, 'p', "kill-window #W? (y/n)"); + break; + case 'x': + self->args = args_create(1, "kill-pane"); + args_set(self->args, 'p', "kill-pane #P? (y/n)"); + break; + default: + self->args = args_create(0); + break; + } +} + +int +cmd_confirm_before_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct cmd_confirm_before_data *cdata; + struct client *c; + char *cmd, *copy, *new_prompt, *ptr; + const char *prompt; + + if (ctx->curclient == NULL) { + ctx->error(ctx, "must be run interactively"); + return (-1); + } + + if ((c = cmd_find_client(ctx, args_get(args, 't'))) == NULL) + return (-1); + + if ((prompt = args_get(args, 'p')) != NULL) + xasprintf(&new_prompt, "%s ", prompt); + else { + ptr = copy = xstrdup(args->argv[0]); + cmd = strsep(&ptr, " \t"); + xasprintf(&new_prompt, "Confirm '%s'? (y/n) ", cmd); + xfree(copy); + } + + cdata = xmalloc(sizeof *cdata); + cdata->cmd = xstrdup(args->argv[0]); + cdata->c = c; + status_prompt_set(cdata->c, new_prompt, NULL, + cmd_confirm_before_callback, cmd_confirm_before_free, cdata, + PROMPT_SINGLE); + + xfree(new_prompt); + return (1); +} + +int +cmd_confirm_before_callback(void *data, const char *s) +{ + struct cmd_confirm_before_data *cdata = data; + struct client *c = cdata->c; + struct cmd_list *cmdlist; + struct cmd_ctx ctx; + char *cause; + + if (s == NULL || *s == '\0') + return (0); + if (tolower((u_char) s[0]) != 'y' || s[1] != '\0') + return (0); + + if (cmd_string_parse(cdata->cmd, &cmdlist, &cause) != 0) { + if (cause != NULL) { + *cause = toupper((u_char) *cause); + status_message_set(c, "%s", cause); + xfree(cause); + } + return (0); + } + + ctx.msgdata = NULL; + ctx.curclient = c; + + ctx.error = key_bindings_error; + ctx.print = key_bindings_print; + ctx.info = key_bindings_info; + + ctx.cmdclient = NULL; + + cmd_list_exec(cmdlist, &ctx); + cmd_list_free(cmdlist); + + return (0); +} + +void +cmd_confirm_before_free(void *data) +{ + struct cmd_confirm_before_data *cdata = data; + + if (cdata->cmd != NULL) + xfree(cdata->cmd); + xfree(cdata); +} diff --git a/external/bsd/tmux/dist/cmd-copy-mode.c b/external/bsd/tmux/dist/cmd-copy-mode.c new file mode 100644 index 000000000..8205f8b35 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-copy-mode.c @@ -0,0 +1,64 @@ +/* $Id: cmd-copy-mode.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "tmux.h" + +/* + * Enter copy mode. + */ + +void cmd_copy_mode_key_binding(struct cmd *, int); +int cmd_copy_mode_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_copy_mode_entry = { + "copy-mode", NULL, + "t:u", 0, 0, + "[-u] " CMD_TARGET_PANE_USAGE, + 0, + cmd_copy_mode_key_binding, + NULL, + cmd_copy_mode_exec +}; + +void +cmd_copy_mode_key_binding(struct cmd *self, int key) +{ + self->args = args_create(0); + if (key == KEYC_PPAGE) + args_set(self->args, 'u', NULL); +} + +int +cmd_copy_mode_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct window_pane *wp; + + if (cmd_find_pane(ctx, args_get(args, 't'), NULL, &wp) == NULL) + return (-1); + + if (window_pane_set_mode(wp, &window_copy_mode) != 0) + return (0); + window_copy_init_from_pane(wp); + if (wp->mode == &window_copy_mode && args_has(self->args, 'u')) + window_copy_pageup(wp); + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-delete-buffer.c b/external/bsd/tmux/dist/cmd-delete-buffer.c new file mode 100644 index 000000000..6ffa36f55 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-delete-buffer.c @@ -0,0 +1,66 @@ +/* $Id: cmd-delete-buffer.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * Delete a paste buffer. + */ + +int cmd_delete_buffer_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_delete_buffer_entry = { + "delete-buffer", "deleteb", + "b:", 0, 0, + CMD_BUFFER_USAGE, + 0, + NULL, + NULL, + cmd_delete_buffer_exec +}; + +int +cmd_delete_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + char *cause; + int buffer; + + if (!args_has(args, 'b')) { + paste_free_top(&global_buffers); + return (0); + } + + buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); + if (cause != NULL) { + ctx->error(ctx, "buffer %s", cause); + xfree(cause); + return (-1); + } + + if (paste_free_index(&global_buffers, buffer) != 0) { + ctx->error(ctx, "no buffer %d", buffer); + return (-1); + } + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-detach-client.c b/external/bsd/tmux/dist/cmd-detach-client.c new file mode 100644 index 000000000..d7ad14183 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-detach-client.c @@ -0,0 +1,72 @@ +/* $Id: cmd-detach-client.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "tmux.h" + +/* + * Detach a client. + */ + +int cmd_detach_client_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_detach_client_entry = { + "detach-client", "detach", + "s:t:P", 0, 0, + "[-P] [-s target-session] " CMD_TARGET_CLIENT_USAGE, + CMD_READONLY, + NULL, + NULL, + cmd_detach_client_exec +}; + +int +cmd_detach_client_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct client *c; + struct session *s; + enum msgtype msgtype; + u_int i; + + if (args_has(args, 'P')) + msgtype = MSG_DETACHKILL; + else + msgtype = MSG_DETACH; + + if (args_has(args, 's')) { + s = cmd_find_session(ctx, args_get(args, 's'), 0); + if (s == NULL) + return (-1); + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c != NULL && c->session == s) + server_write_client(c, msgtype, NULL, 0); + } + } else { + c = cmd_find_client(ctx, args_get(args, 't')); + if (c == NULL) + return (-1); + + server_write_client(c, msgtype, NULL, 0); + } + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-display-message.c b/external/bsd/tmux/dist/cmd-display-message.c new file mode 100644 index 000000000..4240a7d9c --- /dev/null +++ b/external/bsd/tmux/dist/cmd-display-message.c @@ -0,0 +1,78 @@ +/* $Id: cmd-display-message.c,v 1.2 2011/08/19 09:06:05 christos Exp $ */ + +/* + * Copyright (c) 2009 Tiago Cunha + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * Displays a message in the status line. + */ + +int cmd_display_message_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_display_message_entry = { + "display-message", "display", + "c:pt:", 0, 1, + "[-p] [-c target-client] [-t target-pane] [message]", + 0, + NULL, + NULL, + cmd_display_message_exec +}; + +int +cmd_display_message_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct client *c; + struct session *s; + struct winlink *wl; + struct window_pane *wp; + const char *template; + char *msg; + + if ((c = cmd_find_client(ctx, args_get(args, 'c'))) == NULL) + return (-1); + + if (args_has(args, 't') != 0) { + wl = cmd_find_pane(ctx, args_get(args, 't'), &s, &wp); + if (wl == NULL) + return (-1); + } else { + s = NULL; + wl = NULL; + wp = NULL; + } + + if (args->argc == 0) + template = "[#S] #I:#W, current pane #P - (%H:%M %d-%b-%Y)"; + else + template = args->argv[0]; + + msg = status_replace(c, s, wl, wp, template, time(NULL), 0); + if (args_has(self->args, 'p')) + ctx->print(ctx, "%s", msg); + else + status_message_set(c, "%s", msg); + xfree(msg); + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-display-panes.c b/external/bsd/tmux/dist/cmd-display-panes.c new file mode 100644 index 000000000..c476aa302 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-display-panes.c @@ -0,0 +1,51 @@ +/* $Id: cmd-display-panes.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "tmux.h" + +/* + * Display panes on a client. + */ + +int cmd_display_panes_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_display_panes_entry = { + "display-panes", "displayp", + "t:", 0, 0, + CMD_TARGET_CLIENT_USAGE, + 0, + NULL, + NULL, + cmd_display_panes_exec +}; + +int +cmd_display_panes_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct client *c; + + if ((c = cmd_find_client(ctx, args_get(args, 't'))) == NULL) + return (-1); + + server_set_identify(c); + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-find-window.c b/external/bsd/tmux/dist/cmd-find-window.c new file mode 100644 index 000000000..b665c1a1c --- /dev/null +++ b/external/bsd/tmux/dist/cmd-find-window.c @@ -0,0 +1,176 @@ +/* $Id: cmd-find-window.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +/* + * Find window containing text. + */ + +int cmd_find_window_exec(struct cmd *, struct cmd_ctx *); + +void cmd_find_window_callback(void *, int); +void cmd_find_window_free(void *); + +const struct cmd_entry cmd_find_window_entry = { + "find-window", "findw", + "t:", 1, 1, + CMD_TARGET_WINDOW_USAGE " match-string", + 0, + NULL, + NULL, + cmd_find_window_exec +}; + +struct cmd_find_window_data { + struct session *session; +}; + +int +cmd_find_window_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct cmd_find_window_data *cdata; + struct session *s; + struct winlink *wl, *wm; + struct window *w; + struct window_pane *wp; + ARRAY_DECL(, u_int) list_idx; + ARRAY_DECL(, char *) list_ctx; + char *str, *sres, *sctx, *searchstr; + u_int i, line; + + if (ctx->curclient == NULL) { + ctx->error(ctx, "must be run interactively"); + return (-1); + } + s = ctx->curclient->session; + + if ((wl = cmd_find_window(ctx, args_get(args, 't'), NULL)) == NULL) + return (-1); + + str = args->argv[0]; + + ARRAY_INIT(&list_idx); + ARRAY_INIT(&list_ctx); + + xasprintf(&searchstr, "*%s*", str); + RB_FOREACH(wm, winlinks, &s->windows) { + i = 0; + TAILQ_FOREACH(wp, &wm->window->panes, entry) { + i++; + + if (fnmatch(searchstr, wm->window->name, 0) == 0) + sctx = xstrdup(""); + else { + sres = window_pane_search(wp, str, &line); + if (sres == NULL && + fnmatch(searchstr, wp->base.title, 0) != 0) + continue; + + if (sres == NULL) { + xasprintf(&sctx, + "pane %u title: \"%s\"", i - 1, + wp->base.title); + } else { + xasprintf(&sctx, + "pane %u line %u: \"%s\"", i - 1, + line + 1, sres); + xfree(sres); + } + } + + ARRAY_ADD(&list_idx, wm->idx); + ARRAY_ADD(&list_ctx, sctx); + } + } + xfree(searchstr); + + if (ARRAY_LENGTH(&list_idx) == 0) { + ctx->error(ctx, "no windows matching: %s", str); + ARRAY_FREE(&list_idx); + ARRAY_FREE(&list_ctx); + return (-1); + } + + if (ARRAY_LENGTH(&list_idx) == 1) { + if (session_select(s, ARRAY_FIRST(&list_idx)) == 0) + server_redraw_session(s); + recalculate_sizes(); + goto out; + } + + if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) + goto out; + + for (i = 0; i < ARRAY_LENGTH(&list_idx); i++) { + wm = winlink_find_by_index( + &s->windows, ARRAY_ITEM(&list_idx, i)); + w = wm->window; + + sctx = ARRAY_ITEM(&list_ctx, i); + window_choose_add(wl->window->active, + wm->idx, "%3d: %s [%ux%u] (%u panes) %s", wm->idx, w->name, + w->sx, w->sy, window_count_panes(w), sctx); + xfree(sctx); + } + + cdata = xmalloc(sizeof *cdata); + cdata->session = s; + cdata->session->references++; + + window_choose_ready(wl->window->active, + 0, cmd_find_window_callback, cmd_find_window_free, cdata); + +out: + ARRAY_FREE(&list_idx); + ARRAY_FREE(&list_ctx); + + return (0); +} + +void +cmd_find_window_callback(void *data, int idx) +{ + struct cmd_find_window_data *cdata = data; + struct session *s = cdata->session; + + if (idx == -1) + return; + if (!session_alive(s)) + return; + + if (session_select(s, idx) == 0) { + server_redraw_session(s); + recalculate_sizes(); + } +} + +void +cmd_find_window_free(void *data) +{ + struct cmd_find_window_data *cdata = data; + + cdata->session->references--; + xfree(cdata); +} diff --git a/external/bsd/tmux/dist/cmd-has-session.c b/external/bsd/tmux/dist/cmd-has-session.c new file mode 100644 index 000000000..848f60909 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-has-session.c @@ -0,0 +1,48 @@ +/* $Id: cmd-has-session.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "tmux.h" + +/* + * Cause client to report an error and exit with 1 if session doesn't exist. + */ + +int cmd_has_session_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_has_session_entry = { + "has-session", "has", + "t:", 0, 0, + CMD_TARGET_SESSION_USAGE, + 0, + NULL, + NULL, + cmd_has_session_exec +}; + +int +cmd_has_session_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + + if (cmd_find_session(ctx, args_get(args, 't'), 0) == NULL) + return (-1); + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-if-shell.c b/external/bsd/tmux/dist/cmd-if-shell.c new file mode 100644 index 000000000..be6532727 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-if-shell.c @@ -0,0 +1,110 @@ +/* $Id: cmd-if-shell.c,v 1.2 2013/10/20 03:00:02 christos Exp $ */ + +/* + * Copyright (c) 2009 Tiago Cunha + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include + +#include "tmux.h" + +/* + * Executes a tmux command if a shell command returns true. + */ + +int cmd_if_shell_exec(struct cmd *, struct cmd_ctx *); + +void cmd_if_shell_callback(struct job *); +void cmd_if_shell_free(void *); + +const struct cmd_entry cmd_if_shell_entry = { + "if-shell", "if", + "", 2, 2, + "shell-command command", + 0, + NULL, + NULL, + cmd_if_shell_exec +}; + +struct cmd_if_shell_data { + char *cmd; + struct cmd_ctx ctx; +}; + +int +cmd_if_shell_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct cmd_if_shell_data *cdata; + const char *shellcmd = args->argv[0]; + + cdata = xmalloc(sizeof *cdata); + cdata->cmd = xstrdup(args->argv[1]); + memcpy(&cdata->ctx, ctx, sizeof cdata->ctx); + + if (ctx->cmdclient != NULL) + ctx->cmdclient->references++; + if (ctx->curclient != NULL) + ctx->curclient->references++; + + job_run(shellcmd, cmd_if_shell_callback, cmd_if_shell_free, cdata); + + return (1); /* don't let client exit */ +} + +void +cmd_if_shell_callback(struct job *job) +{ + struct cmd_if_shell_data *cdata = job->data; + struct cmd_ctx *ctx = &cdata->ctx; + struct cmd_list *cmdlist; + char *cause; + + if (!WIFEXITED(job->status) || WEXITSTATUS(job->status) != 0) + return; + + if (cmd_string_parse(cdata->cmd, &cmdlist, &cause) != 0) { + if (cause != NULL) { + ctx->error(ctx, "%s", cause); + xfree(cause); + } + return; + } + + cmd_list_exec(cmdlist, ctx); + cmd_list_free(cmdlist); +} + +void +cmd_if_shell_free(void *data) +{ + struct cmd_if_shell_data *cdata = data; + struct cmd_ctx *ctx = &cdata->ctx; + + if (ctx->cmdclient != NULL) { + ctx->cmdclient->references--; + ctx->cmdclient->flags |= CLIENT_EXIT; + } + if (ctx->curclient != NULL) + ctx->curclient->references--; + + xfree(cdata->cmd); + xfree(cdata); +} diff --git a/external/bsd/tmux/dist/cmd-join-pane.c b/external/bsd/tmux/dist/cmd-join-pane.c new file mode 100644 index 000000000..73a2b9b96 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-join-pane.c @@ -0,0 +1,145 @@ +/* $Id: cmd-join-pane.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +/* + * Join a pane into another (like split/swap/kill). + */ + +void cmd_join_pane_key_binding(struct cmd *, int); +int cmd_join_pane_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_join_pane_entry = { + "join-pane", "joinp", + "dhvp:l:s:t:", 0, 0, + "[-dhv] [-p percentage|-l size] [-s src-pane] [-t dst-pane]", + 0, + cmd_join_pane_key_binding, + NULL, + cmd_join_pane_exec +}; + +void +cmd_join_pane_key_binding(struct cmd *self, int key) +{ + switch (key) { + case '%': + self->args = args_create(0); + args_set(self->args, 'h', NULL); + break; + default: + self->args = args_create(0); + break; + } +} + +int +cmd_join_pane_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct session *dst_s; + struct winlink *src_wl, *dst_wl; + struct window *src_w, *dst_w; + struct window_pane *src_wp, *dst_wp; + char *cause; + int size, percentage, dst_idx; + enum layout_type type; + struct layout_cell *lc; + + dst_wl = cmd_find_pane(ctx, args_get(args, 't'), &dst_s, &dst_wp); + if (dst_wl == NULL) + return (-1); + dst_w = dst_wl->window; + dst_idx = dst_wl->idx; + + src_wl = cmd_find_pane(ctx, args_get(args, 's'), NULL, &src_wp); + if (src_wl == NULL) + return (-1); + src_w = src_wl->window; + + if (src_w == dst_w) { + ctx->error(ctx, "can't join a pane to its own window"); + return (-1); + } + + type = LAYOUT_TOPBOTTOM; + if (args_has(args, 'h')) + type = LAYOUT_LEFTRIGHT; + + size = -1; + if (args_has(args, 'l')) { + size = args_strtonum(args, 'l', 0, INT_MAX, &cause); + if (cause != NULL) { + ctx->error(ctx, "size %s", cause); + xfree(cause); + return (-1); + } + } else if (args_has(args, 'p')) { + percentage = args_strtonum(args, 'p', 0, 100, &cause); + if (cause != NULL) { + ctx->error(ctx, "percentage %s", cause); + xfree(cause); + return (-1); + } + if (type == LAYOUT_TOPBOTTOM) + size = (dst_wp->sy * percentage) / 100; + else + size = (dst_wp->sx * percentage) / 100; + } + + if ((lc = layout_split_pane(dst_wp, type, size)) == NULL) { + ctx->error(ctx, "create pane failed: pane too small"); + return (-1); + } + + layout_close_pane(src_wp); + + if (src_w->active == src_wp) { + src_w->active = TAILQ_PREV(src_wp, window_panes, entry); + if (src_w->active == NULL) + src_w->active = TAILQ_NEXT(src_wp, entry); + } + TAILQ_REMOVE(&src_w->panes, src_wp, entry); + + if (window_count_panes(src_w) == 0) + server_kill_window(src_w); + + src_wp->window = dst_w; + TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry); + layout_assign_pane(lc, src_wp); + + recalculate_sizes(); + + server_redraw_window(src_w); + server_redraw_window(dst_w); + + if (!args_has(args, 'd')) { + window_set_active_pane(dst_w, src_wp); + session_select(dst_s, dst_idx); + server_redraw_session(dst_s); + } else + server_status_session(dst_s); + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-kill-pane.c b/external/bsd/tmux/dist/cmd-kill-pane.c new file mode 100644 index 000000000..eea937712 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-kill-pane.c @@ -0,0 +1,75 @@ +/* $Id: cmd-kill-pane.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * Kill pane. + */ + +int cmd_kill_pane_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_kill_pane_entry = { + "kill-pane", "killp", + "at:", 0, 0, + "[-a] " CMD_TARGET_PANE_USAGE, + 0, + NULL, + NULL, + cmd_kill_pane_exec +}; + +int +cmd_kill_pane_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct winlink *wl; + struct window_pane *loopwp, *nextwp, *wp; + + if ((wl = cmd_find_pane(ctx, args_get(args, 't'), NULL, &wp)) == NULL) + return (-1); + + if (window_count_panes(wl->window) == 1) { + /* Only one pane, kill the window. */ + server_kill_window(wl->window); + recalculate_sizes(); + return (0); + } + + if (args_has(self->args, 'a')) { + loopwp = TAILQ_FIRST(&wl->window->panes); + while (loopwp != NULL) { + nextwp = TAILQ_NEXT(loopwp, entry); + if (loopwp != wp) { + layout_close_pane(loopwp); + window_remove_pane(wl->window, loopwp); + } + loopwp = nextwp; + } + } else { + layout_close_pane(wp); + window_remove_pane(wl->window, wp); + } + server_redraw_window(wl->window); + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-kill-server.c b/external/bsd/tmux/dist/cmd-kill-server.c new file mode 100644 index 000000000..e323381bd --- /dev/null +++ b/external/bsd/tmux/dist/cmd-kill-server.c @@ -0,0 +1,49 @@ +/* $Id: cmd-kill-server.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +/* + * Kill the server and do nothing else. + */ + +int cmd_kill_server_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_kill_server_entry = { + "kill-server", NULL, + "", 0, 0, + "", + 0, + NULL, + NULL, + cmd_kill_server_exec +}; + +/* ARGSUSED */ +int +cmd_kill_server_exec(unused struct cmd *self, unused struct cmd_ctx *ctx) +{ + kill(getpid(), SIGTERM); + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-kill-session.c b/external/bsd/tmux/dist/cmd-kill-session.c new file mode 100644 index 000000000..6406e4072 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-kill-session.c @@ -0,0 +1,55 @@ +/* $Id: cmd-kill-session.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "tmux.h" + +/* + * Destroy session, detaching all clients attached to it and destroying any + * windows linked only to this session. + * + * Note this deliberately has no alias to make it hard to hit by accident. + */ + +int cmd_kill_session_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_kill_session_entry = { + "kill-session", NULL, + "t:", 0, 0, + CMD_TARGET_SESSION_USAGE, + 0, + NULL, + NULL, + cmd_kill_session_exec +}; + +int +cmd_kill_session_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct session *s; + + if ((s = cmd_find_session(ctx, args_get(args, 't'), 0)) == NULL) + return (-1); + + server_destroy_session(s); + session_destroy(s); + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-kill-window.c b/external/bsd/tmux/dist/cmd-kill-window.c new file mode 100644 index 000000000..5f35cd963 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-kill-window.c @@ -0,0 +1,52 @@ +/* $Id: cmd-kill-window.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "tmux.h" + +/* + * Destroy window. + */ + +int cmd_kill_window_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_kill_window_entry = { + "kill-window", "killw", + "t:", 0, 0, + CMD_TARGET_WINDOW_USAGE, + 0, + NULL, + NULL, + cmd_kill_window_exec +}; + +int +cmd_kill_window_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct winlink *wl; + + if ((wl = cmd_find_window(ctx, args_get(args, 't'), NULL)) == NULL) + return (-1); + + server_kill_window(wl->window); + recalculate_sizes(); + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-link-window.c b/external/bsd/tmux/dist/cmd-link-window.c new file mode 100644 index 000000000..648746cec --- /dev/null +++ b/external/bsd/tmux/dist/cmd-link-window.c @@ -0,0 +1,65 @@ +/* $Id: cmd-link-window.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * Link a window into another session. + */ + +int cmd_link_window_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_link_window_entry = { + "link-window", "linkw", + "dks:t:", 0, 0, + "[-dk] " CMD_SRCDST_WINDOW_USAGE, + 0, + NULL, + NULL, + cmd_link_window_exec +}; + +int +cmd_link_window_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct session *src, *dst; + struct winlink *wl; + char *cause; + int idx, kflag, dflag; + + if ((wl = cmd_find_window(ctx, args_get(args, 's'), &src)) == NULL) + return (-1); + if ((idx = cmd_find_index(ctx, args_get(args, 't'), &dst)) == -2) + return (-1); + + kflag = args_has(self->args, 'k'); + dflag = args_has(self->args, 'd'); + if (server_link_window(src, wl, dst, idx, kflag, !dflag, &cause) != 0) { + ctx->error(ctx, "can't link window: %s", cause); + xfree(cause); + return (-1); + } + recalculate_sizes(); + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-list-buffers.c b/external/bsd/tmux/dist/cmd-list-buffers.c new file mode 100644 index 000000000..b942ccd40 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-list-buffers.c @@ -0,0 +1,58 @@ +/* $Id: cmd-list-buffers.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * List paste buffers. + */ + +int cmd_list_buffers_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_list_buffers_entry = { + "list-buffers", "lsb", + "", 0, 0, + "", + 0, + NULL, + NULL, + cmd_list_buffers_exec +}; + +/* ARGSUSED */ +int +cmd_list_buffers_exec(unused struct cmd *self, struct cmd_ctx *ctx) +{ + struct paste_buffer *pb; + u_int idx; + char *tmp; + + idx = 0; + while ((pb = paste_walk_stack(&global_buffers, &idx)) != NULL) { + tmp = paste_print(pb, 50); + ctx->print(ctx, + "%u: %zu bytes: \"%s\"", idx - 1, pb->size, tmp); + xfree(tmp); + } + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-list-clients.c b/external/bsd/tmux/dist/cmd-list-clients.c new file mode 100644 index 000000000..1e88fe0bb --- /dev/null +++ b/external/bsd/tmux/dist/cmd-list-clients.c @@ -0,0 +1,77 @@ +/* $Id: cmd-list-clients.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +/* + * List all clients. + */ + +int cmd_list_clients_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_list_clients_entry = { + "list-clients", "lsc", + "t:", 0, 0, + CMD_TARGET_SESSION_USAGE, + 0, + NULL, + NULL, + cmd_list_clients_exec +}; + +/* ARGSUSED */ +int +cmd_list_clients_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct client *c; + struct session *s; + u_int i; + const char *s_utf8; + + if (args_has(args, 't')) { + s = cmd_find_session(ctx, args_get(args, 't'), 0); + if (s == NULL) + return (-1); + } else + s = NULL; + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session == NULL) + continue; + + if (c->tty.flags & TTY_UTF8) + s_utf8 = " (utf8)"; + else + s_utf8 = ""; + + if (s != NULL && s != c->session) + continue; + ctx->print(ctx, "%s: %s [%ux%u %s]%s", c->tty.path, + c->session->name, c->tty.sx, c->tty.sy, + c->tty.termname, s_utf8); + } + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-list-commands.c b/external/bsd/tmux/dist/cmd-list-commands.c new file mode 100644 index 000000000..90edd70af --- /dev/null +++ b/external/bsd/tmux/dist/cmd-list-commands.c @@ -0,0 +1,49 @@ +/* $Id: cmd-list-commands.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "tmux.h" + +/* + * List all commands with usages. + */ + +int cmd_list_commands_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_list_commands_entry = { + "list-commands", "lscm", + "", 0, 0, + "", + 0, + NULL, + NULL, + cmd_list_commands_exec +}; + +/* ARGSUSED */ +int +cmd_list_commands_exec(unused struct cmd *self, struct cmd_ctx *ctx) +{ + const struct cmd_entry **entryp; + + for (entryp = cmd_table; *entryp != NULL; entryp++) + ctx->print(ctx, "%s %s", (*entryp)->name, (*entryp)->usage); + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-list-keys.c b/external/bsd/tmux/dist/cmd-list-keys.c new file mode 100644 index 000000000..cee6221f1 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-list-keys.c @@ -0,0 +1,149 @@ +/* $Id: cmd-list-keys.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * List key bindings. + */ + +int cmd_list_keys_exec(struct cmd *, struct cmd_ctx *); + +int cmd_list_keys_table(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_list_keys_entry = { + "list-keys", "lsk", + "t:", 0, 0, + "[-t key-table]", + 0, + NULL, + NULL, + cmd_list_keys_exec +}; + +int +cmd_list_keys_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct key_binding *bd; + const char *key; + char tmp[BUFSIZ], flags[8]; + size_t used; + int width, keywidth; + + if (args_has(args, 't')) + return (cmd_list_keys_table(self, ctx)); + + width = 0; + *flags = '\0'; + + SPLAY_FOREACH(bd, key_bindings, &key_bindings) { + key = key_string_lookup_key(bd->key & ~KEYC_PREFIX); + if (key == NULL) + continue; + + keywidth = strlen(key); + if (!(bd->key & KEYC_PREFIX)) { + if (bd->can_repeat) + keywidth += 4; + else + keywidth += 3; + } else if (bd->can_repeat) + keywidth += 3; + if (keywidth > width) + width = keywidth; + } + + SPLAY_FOREACH(bd, key_bindings, &key_bindings) { + key = key_string_lookup_key(bd->key & ~KEYC_PREFIX); + if (key == NULL) + continue; + + if (!(bd->key & KEYC_PREFIX)) { + if (bd->can_repeat) + xsnprintf(flags, sizeof flags, "-rn "); + else + xsnprintf(flags, sizeof flags, "-n "); + } else if (bd->can_repeat) + xsnprintf(flags, sizeof flags, "-r "); + + used = xsnprintf(tmp, sizeof tmp, "%s%*s ", + flags, (int) (width - strlen(flags)), key); + if (used >= sizeof tmp) + continue; + + cmd_list_print(bd->cmdlist, tmp + used, (sizeof tmp) - used); + ctx->print(ctx, "bind-key %s", tmp); + } + + return (0); +} + +int +cmd_list_keys_table(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + const char *tablename; + const struct mode_key_table *mtab; + struct mode_key_binding *mbind; + const char *key, *cmdstr, *mode; + int width, keywidth, any_mode; + + tablename = args_get(args, 't'); + if ((mtab = mode_key_findtable(tablename)) == NULL) { + ctx->error(ctx, "unknown key table: %s", tablename); + return (-1); + } + + width = 0; + any_mode = 0; + SPLAY_FOREACH(mbind, mode_key_tree, mtab->tree) { + key = key_string_lookup_key(mbind->key); + if (key == NULL) + continue; + + if (mbind->mode != 0) + any_mode = 1; + + keywidth = strlen(key); + if (keywidth > width) + width = keywidth; + } + + SPLAY_FOREACH(mbind, mode_key_tree, mtab->tree) { + key = key_string_lookup_key(mbind->key); + if (key == NULL) + continue; + + mode = ""; + if (mbind->mode != 0) + mode = "c"; + cmdstr = mode_key_tostring(mtab->cmdstr, mbind->cmd); + if (cmdstr != NULL) { + ctx->print(ctx, "bind-key -%st %s%s %*s %s", + mode, any_mode && *mode == '\0' ? " " : "", + mtab->name, (int) width, key, cmdstr); + } + } + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-list-panes.c b/external/bsd/tmux/dist/cmd-list-panes.c new file mode 100644 index 000000000..55e4cb89f --- /dev/null +++ b/external/bsd/tmux/dist/cmd-list-panes.c @@ -0,0 +1,137 @@ +/* $Id: cmd-list-panes.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * List panes on given window. + */ + +int cmd_list_panes_exec(struct cmd *, struct cmd_ctx *); + +void cmd_list_panes_server(struct cmd_ctx *); +void cmd_list_panes_session(struct session *, struct cmd_ctx *, int); +void cmd_list_panes_window( + struct session *, struct winlink *, struct cmd_ctx *, int); + +const struct cmd_entry cmd_list_panes_entry = { + "list-panes", "lsp", + "ast:", 0, 0, + "[-as] [-t target]", + 0, + NULL, + NULL, + cmd_list_panes_exec +}; + +int +cmd_list_panes_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct session *s; + struct winlink *wl; + + if (args_has(args, 'a')) + cmd_list_panes_server(ctx); + else if (args_has(args, 's')) { + s = cmd_find_session(ctx, args_get(args, 't'), 0); + if (s == NULL) + return (-1); + cmd_list_panes_session(s, ctx, 1); + } else { + wl = cmd_find_window(ctx, args_get(args, 't'), &s); + if (wl == NULL) + return (-1); + cmd_list_panes_window(s, wl, ctx, 0); + } + + return (0); +} + +void +cmd_list_panes_server(struct cmd_ctx *ctx) +{ + struct session *s; + + RB_FOREACH(s, sessions, &sessions) + cmd_list_panes_session(s, ctx, 2); +} + +void +cmd_list_panes_session(struct session *s, struct cmd_ctx *ctx, int type) +{ + struct winlink *wl; + + RB_FOREACH(wl, winlinks, &s->windows) + cmd_list_panes_window(s, wl, ctx, type); +} + +void +cmd_list_panes_window( + struct session *s, struct winlink *wl, struct cmd_ctx *ctx, int type) +{ + struct window_pane *wp; + struct grid *gd; + struct grid_line *gl; + u_int i, n; + unsigned long long size; + + n = 0; + TAILQ_FOREACH(wp, &wl->window->panes, entry) { + gd = wp->base.grid; + + size = 0; + for (i = 0; i < gd->hsize; i++) { + gl = &gd->linedata[i]; + size += gl->cellsize * sizeof *gl->celldata; + size += gl->utf8size * sizeof *gl->utf8data; + } + size += gd->hsize * sizeof *gd->linedata; + + switch (type) { + case 0: + ctx->print(ctx, + "%u: [%ux%u] [history %u/%u, %llu bytes] %%%u%s%s", + n, wp->sx, wp->sy, gd->hsize, gd->hlimit, size, + wp->id, wp == wp->window->active ? " (active)" : "", + wp->fd == -1 ? " (dead)" : ""); + break; + case 1: + ctx->print(ctx, + "%d.%u: [%ux%u] [history %u/%u, %llu bytes] " + "%%%u%s%s", wl->idx, + n, wp->sx, wp->sy, gd->hsize, gd->hlimit, size, + wp->id, wp == wp->window->active ? " (active)" : "", + wp->fd == -1 ? " (dead)" : ""); + break; + case 2: + ctx->print(ctx, + "%s:%d.%u: [%ux%u] [history %u/%u, %llu bytes] " + "%%%u%s%s", s->name, wl->idx, + n, wp->sx, wp->sy, gd->hsize, gd->hlimit, size, + wp->id, wp == wp->window->active ? " (active)" : "", + wp->fd == -1 ? " (dead)" : ""); + break; + } + n++; + } +} diff --git a/external/bsd/tmux/dist/cmd-list-sessions.c b/external/bsd/tmux/dist/cmd-list-sessions.c new file mode 100644 index 000000000..7ae7412e8 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-list-sessions.c @@ -0,0 +1,71 @@ +/* $Id: cmd-list-sessions.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +/* + * List all sessions. + */ + +int cmd_list_sessions_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_list_sessions_entry = { + "list-sessions", "ls", + "", 0, 0, + "", + 0, + NULL, + NULL, + cmd_list_sessions_exec +}; + +/* ARGSUSED */ +int +cmd_list_sessions_exec(unused struct cmd *self, struct cmd_ctx *ctx) +{ + struct session *s; + struct session_group *sg; + char *tim, tmp[64]; + u_int idx; + time_t t; + + RB_FOREACH(s, sessions, &sessions) { + sg = session_group_find(s); + if (sg == NULL) + *tmp = '\0'; + else { + idx = session_group_index(sg); + xsnprintf(tmp, sizeof tmp, " (group %u)", idx); + } + + t = s->creation_time.tv_sec; + tim = ctime(&t); + *strchr(tim, '\n') = '\0'; + + ctx->print(ctx, "%s: %u windows (created %s) [%ux%u]%s%s", + s->name, winlink_count(&s->windows), tim, s->sx, s->sy, + tmp, s->flags & SESSION_UNATTACHED ? "" : " (attached)"); + } + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-list-windows.c b/external/bsd/tmux/dist/cmd-list-windows.c new file mode 100644 index 000000000..d2aa4c2dc --- /dev/null +++ b/external/bsd/tmux/dist/cmd-list-windows.c @@ -0,0 +1,92 @@ +/* $Id: cmd-list-windows.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * List windows on given session. + */ + +int cmd_list_windows_exec(struct cmd *, struct cmd_ctx *); + +void cmd_list_windows_server(struct cmd_ctx *); +void cmd_list_windows_session(struct session *, struct cmd_ctx *, int); + +const struct cmd_entry cmd_list_windows_entry = { + "list-windows", "lsw", + "at:", 0, 0, + "[-a] " CMD_TARGET_SESSION_USAGE, + 0, + NULL, + NULL, + cmd_list_windows_exec +}; + +int +cmd_list_windows_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct session *s; + + if (args_has(args, 'a')) + cmd_list_windows_server(ctx); + else { + s = cmd_find_session(ctx, args_get(args, 't'), 0); + if (s == NULL) + return (-1); + cmd_list_windows_session(s, ctx, 0); + } + + return (0); +} + +void +cmd_list_windows_server(struct cmd_ctx *ctx) +{ + struct session *s; + + RB_FOREACH(s, sessions, &sessions) + cmd_list_windows_session(s, ctx, 1); +} + +void +cmd_list_windows_session(struct session *s, struct cmd_ctx *ctx, int type) +{ + struct winlink *wl; + char *layout; + + RB_FOREACH(wl, winlinks, &s->windows) { + layout = layout_dump(wl->window); + if (type) { + ctx->print(ctx, "%s:%d: %s [%ux%u] [layout %s]%s", + s->name, wl->idx, wl->window->name, wl->window->sx, + wl->window->sy, layout, + wl == s->curw ? " (active)" : ""); + } else { + ctx->print(ctx, "%d: %s [%ux%u] [layout %s]%s", + wl->idx, wl->window->name, wl->window->sx, + wl->window->sy, layout, + wl == s->curw ? " (active)" : ""); + } + xfree(layout); + } +} diff --git a/external/bsd/tmux/dist/cmd-list.c b/external/bsd/tmux/dist/cmd-list.c new file mode 100644 index 000000000..06baceed5 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-list.c @@ -0,0 +1,150 @@ +/* $Id: cmd-list.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +struct cmd_list * +cmd_list_parse(int argc, char **argv, char **cause) +{ + struct cmd_list *cmdlist; + struct cmd *cmd; + int i, lastsplit; + size_t arglen, new_argc; + char **copy_argv, **new_argv; + + copy_argv = cmd_copy_argv(argc, argv); + + cmdlist = xmalloc(sizeof *cmdlist); + cmdlist->references = 1; + TAILQ_INIT(&cmdlist->list); + + lastsplit = 0; + for (i = 0; i < argc; i++) { + arglen = strlen(copy_argv[i]); + if (arglen == 0 || copy_argv[i][arglen - 1] != ';') + continue; + copy_argv[i][arglen - 1] = '\0'; + + if (arglen > 1 && copy_argv[i][arglen - 2] == '\\') { + copy_argv[i][arglen - 2] = ';'; + continue; + } + + new_argc = i - lastsplit; + new_argv = copy_argv + lastsplit; + if (arglen != 1) + new_argc++; + + cmd = cmd_parse(new_argc, new_argv, cause); + if (cmd == NULL) + goto bad; + TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry); + + lastsplit = i + 1; + } + + if (lastsplit != argc) { + cmd = cmd_parse(argc - lastsplit, copy_argv + lastsplit, cause); + if (cmd == NULL) + goto bad; + TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry); + } + + cmd_free_argv(argc, copy_argv); + return (cmdlist); + +bad: + cmd_list_free(cmdlist); + cmd_free_argv(argc, copy_argv); + return (NULL); +} + +int +cmd_list_exec(struct cmd_list *cmdlist, struct cmd_ctx *ctx) +{ + struct cmd *cmd; + int n, retval; + + retval = 0; + TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { + if ((n = cmd_exec(cmd, ctx)) == -1) + return (-1); + + /* + * A 1 return value means the command client is being attached + * (sent MSG_READY). + */ + if (n == 1) { + retval = 1; + + /* + * The command client has been attached, so mangle the + * context to treat any following commands as if they + * were called from inside. + */ + if (ctx->curclient == NULL) { + ctx->curclient = ctx->cmdclient; + ctx->cmdclient = NULL; + + ctx->error = key_bindings_error; + ctx->print = key_bindings_print; + ctx->info = key_bindings_info; + } + } + } + return (retval); +} + +void +cmd_list_free(struct cmd_list *cmdlist) +{ + struct cmd *cmd; + + if (--cmdlist->references != 0) + return; + + while (!TAILQ_EMPTY(&cmdlist->list)) { + cmd = TAILQ_FIRST(&cmdlist->list); + TAILQ_REMOVE(&cmdlist->list, cmd, qentry); + cmd_free(cmd); + } + xfree(cmdlist); +} + +size_t +cmd_list_print(struct cmd_list *cmdlist, char *buf, size_t len) +{ + struct cmd *cmd; + size_t off; + + off = 0; + TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { + if (off >= len) + break; + off += cmd_print(cmd, buf + off, len - off); + if (off >= len) + break; + if (TAILQ_NEXT(cmd, qentry) != NULL) + off += xsnprintf(buf + off, len - off, " ; "); + } + return (off); +} diff --git a/external/bsd/tmux/dist/cmd-load-buffer.c b/external/bsd/tmux/dist/cmd-load-buffer.c new file mode 100644 index 000000000..26f032cff --- /dev/null +++ b/external/bsd/tmux/dist/cmd-load-buffer.c @@ -0,0 +1,174 @@ +/* $Id: cmd-load-buffer.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Tiago Cunha + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include +#include +#include + +#include "tmux.h" + +/* + * Loads a session paste buffer from a file. + */ + +int cmd_load_buffer_exec(struct cmd *, struct cmd_ctx *); +void cmd_load_buffer_callback(struct client *, void *); + +const struct cmd_entry cmd_load_buffer_entry = { + "load-buffer", "loadb", + "b:", 1, 1, + CMD_BUFFER_USAGE " path", + 0, + NULL, + NULL, + cmd_load_buffer_exec +}; + +int +cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct client *c = ctx->cmdclient; + FILE *f; + const char *path; + char *pdata, *new_pdata, *cause; + size_t psize; + u_int limit; + int ch, buffer; + int *buffer_ptr; + + if (!args_has(args, 'b')) + buffer = -1; + else { + buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); + if (cause != NULL) { + ctx->error(ctx, "buffer %s", cause); + xfree(cause); + return (-1); + } + } + + path = args->argv[0]; + if (strcmp(path, "-") == 0) { + if (c == NULL) { + ctx->error(ctx, "%s: can't read from stdin", path); + return (-1); + } + if (c->flags & CLIENT_TERMINAL) { + ctx->error(ctx, "%s: stdin is a tty", path); + return (-1); + } + if (c->stdin_fd == -1) { + ctx->error(ctx, "%s: can't read from stdin", path); + return (-1); + } + + buffer_ptr = xmalloc(sizeof *buffer_ptr); + *buffer_ptr = buffer; + + c->stdin_data = buffer_ptr; + c->stdin_callback = cmd_load_buffer_callback; + + c->references++; + bufferevent_enable(c->stdin_event, EV_READ); + return (1); + } + + if ((f = fopen(path, "rb")) == NULL) { + ctx->error(ctx, "%s: %s", path, strerror(errno)); + return (-1); + } + + pdata = NULL; + psize = 0; + while ((ch = getc(f)) != EOF) { + /* Do not let the server die due to memory exhaustion. */ + if ((new_pdata = realloc(pdata, psize + 2)) == NULL) { + ctx->error(ctx, "realloc error: %s", strerror(errno)); + goto error; + } + pdata = new_pdata; + pdata[psize++] = ch; + } + if (ferror(f)) { + ctx->error(ctx, "%s: read error", path); + goto error; + } + if (pdata != NULL) + pdata[psize] = '\0'; + + fclose(f); + + limit = options_get_number(&global_options, "buffer-limit"); + if (buffer == -1) { + paste_add(&global_buffers, pdata, psize, limit); + return (0); + } + if (paste_replace(&global_buffers, buffer, pdata, psize) != 0) { + ctx->error(ctx, "no buffer %d", buffer); + return (-1); + } + + return (0); + +error: + if (pdata != NULL) + xfree(pdata); + if (f != NULL) + fclose(f); + return (-1); +} + +void +cmd_load_buffer_callback(struct client *c, void *data) +{ + int *buffer = data; + char *pdata; + size_t psize; + u_int limit; + + /* + * Event callback has already checked client is not dead and reduced + * its reference count. But tell it to exit. + */ + c->flags |= CLIENT_EXIT; + + psize = EVBUFFER_LENGTH(c->stdin_event->input); + if (psize == 0 || (pdata = malloc(psize + 1)) == NULL) { + xfree(data); + return; + } + bufferevent_read(c->stdin_event, pdata, psize); + pdata[psize] = '\0'; + + limit = options_get_number(&global_options, "buffer-limit"); + if (*buffer == -1) + paste_add(&global_buffers, pdata, psize, limit); + else if (paste_replace(&global_buffers, *buffer, pdata, psize) != 0) { + /* No context so can't use server_client_msg_error. */ + evbuffer_add_printf( + c->stderr_event->output, "no buffer %d\n", *buffer); + bufferevent_enable(c->stderr_event, EV_WRITE); + } + + xfree(data); +} diff --git a/external/bsd/tmux/dist/cmd-lock-server.c b/external/bsd/tmux/dist/cmd-lock-server.c new file mode 100644 index 000000000..3a1275b21 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-lock-server.c @@ -0,0 +1,85 @@ +/* $Id: cmd-lock-server.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2008 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include + +#include "tmux.h" + +/* + * Lock commands. + */ + +int cmd_lock_server_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_lock_server_entry = { + "lock-server", "lock", + "", 0, 0, + "", + 0, + NULL, + NULL, + cmd_lock_server_exec +}; + +const struct cmd_entry cmd_lock_session_entry = { + "lock-session", "locks", + "t:", 0, 0, + CMD_TARGET_SESSION_USAGE, + 0, + NULL, + NULL, + cmd_lock_server_exec +}; + +const struct cmd_entry cmd_lock_client_entry = { + "lock-client", "lockc", + "t:", 0, 0, + CMD_TARGET_CLIENT_USAGE, + 0, + NULL, + NULL, + cmd_lock_server_exec +}; + +/* ARGSUSED */ +int +cmd_lock_server_exec(struct cmd *self, unused struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct client *c; + struct session *s; + + if (self->entry == &cmd_lock_server_entry) + server_lock(); + else if (self->entry == &cmd_lock_session_entry) { + if ((s = cmd_find_session(ctx, args_get(args, 't'), 0)) == NULL) + return (-1); + server_lock_session(s); + } else { + if ((c = cmd_find_client(ctx, args_get(args, 't'))) == NULL) + return (-1); + server_lock_client(c); + } + recalculate_sizes(); + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-move-window.c b/external/bsd/tmux/dist/cmd-move-window.c new file mode 100644 index 000000000..e36ddd747 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-move-window.c @@ -0,0 +1,66 @@ +/* $Id: cmd-move-window.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2008 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * Move a window. + */ + +int cmd_move_window_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_move_window_entry = { + "move-window", "movew", + "dks:t:", 0, 0, + "[-dk] " CMD_SRCDST_WINDOW_USAGE, + 0, + NULL, + NULL, + cmd_move_window_exec +}; + +int +cmd_move_window_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct session *src, *dst; + struct winlink *wl; + char *cause; + int idx, kflag, dflag; + + if ((wl = cmd_find_window(ctx, args_get(args, 's'), &src)) == NULL) + return (-1); + if ((idx = cmd_find_index(ctx, args_get(args, 't'), &dst)) == -2) + return (-1); + + kflag = args_has(self->args, 'k'); + dflag = args_has(self->args, 'd'); + if (server_link_window(src, wl, dst, idx, kflag, !dflag, &cause) != 0) { + ctx->error(ctx, "can't move window: %s", cause); + xfree(cause); + return (-1); + } + server_unlink_window(src, wl); + recalculate_sizes(); + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-new-session.c b/external/bsd/tmux/dist/cmd-new-session.c new file mode 100644 index 000000000..e18ba13d5 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-new-session.c @@ -0,0 +1,277 @@ +/* $Id: cmd-new-session.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include +#include +#include + +#include "tmux.h" + +/* + * Create a new session and attach to the current terminal unless -d is given. + */ + +int cmd_new_session_check(struct args *); +int cmd_new_session_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_new_session_entry = { + "new-session", "new", + "dn:s:t:x:y:", 0, 1, + "[-d] [-n window-name] [-s session-name] [-t target-session] " + "[-x width] [-y height] [command]", + CMD_STARTSERVER|CMD_CANTNEST|CMD_SENDENVIRON, + NULL, + cmd_new_session_check, + cmd_new_session_exec +}; + +int +cmd_new_session_check(struct args *args) +{ + if (args_has(args, 't') && (args->argc != 0 || args_has(args, 'n'))) + return (-1); + return (0); +} + +int +cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct session *s, *old_s, *groupwith; + struct window *w; + struct window_pane *wp; + struct environ env; + struct termios tio, *tiop; + struct passwd *pw; + const char *newname, *target, *update, *cwd, *errstr; + char *overrides, *cmd, *cause; + int detached, idx; + u_int sx, sy, i; + + newname = args_get(args, 's'); + if (newname != NULL) { + if (!session_check_name(newname)) { + ctx->error(ctx, "bad session name: %s", newname); + return (-1); + } + if (session_find(newname) != NULL) { + ctx->error(ctx, "duplicate session: %s", newname); + return (-1); + } + } + + target = args_get(args, 't'); + if (target != NULL) { + groupwith = cmd_find_session(ctx, target, 0); + if (groupwith == NULL) + return (-1); + } else + groupwith = NULL; + + /* + * There are three cases: + * + * 1. If cmdclient is non-NULL, new-session has been called from the + * command-line - cmdclient is to become a new attached, interactive + * client. Unless -d is given, the terminal must be opened and then + * the client sent MSG_READY. + * + * 2. If cmdclient is NULL, new-session has been called from an + * existing client (such as a key binding). + * + * 3. Both are NULL, the command was in the configuration file. Treat + * this as if -d was given even if it was not. + * + * In all cases, a new additional session needs to be created and + * (unless -d) set as the current session for the client. + */ + + /* Set -d if no client. */ + detached = args_has(args, 'd'); + if (ctx->cmdclient == NULL && ctx->curclient == NULL) + detached = 1; + + /* + * Save the termios settings, part of which is used for new windows in + * this session. + * + * This is read again with tcgetattr() rather than using tty.tio as if + * detached, tty_open won't be called. Because of this, it must be done + * before opening the terminal as that calls tcsetattr() to prepare for + * tmux taking over. + */ + if (ctx->cmdclient != NULL && ctx->cmdclient->tty.fd != -1) { + if (tcgetattr(ctx->cmdclient->tty.fd, &tio) != 0) + fatal("tcgetattr failed"); + tiop = &tio; + } else + tiop = NULL; + + /* Open the terminal if necessary. */ + if (!detached && ctx->cmdclient != NULL) { + if (!(ctx->cmdclient->flags & CLIENT_TERMINAL)) { + ctx->error(ctx, "not a terminal"); + return (-1); + } + + overrides = + options_get_string(&global_s_options, "terminal-overrides"); + if (tty_open(&ctx->cmdclient->tty, overrides, &cause) != 0) { + ctx->error(ctx, "open terminal failed: %s", cause); + xfree(cause); + return (-1); + } + } + + /* Get the new session working directory. */ + if (ctx->cmdclient != NULL && ctx->cmdclient->cwd != NULL) + cwd = ctx->cmdclient->cwd; + else { + pw = getpwuid(getuid()); + if (pw->pw_dir != NULL && *pw->pw_dir != '\0') + cwd = pw->pw_dir; + else + cwd = "/"; + } + + /* Find new session size. */ + if (detached) { + sx = 80; + sy = 24; + if (args_has(args, 'x')) { + sx = strtonum( + args_get(args, 'x'), 1, USHRT_MAX, &errstr); + if (errstr != NULL) { + ctx->error(ctx, "width %s", errstr); + return (-1); + } + } + if (args_has(args, 'y')) { + sy = strtonum( + args_get(args, 'y'), 1, USHRT_MAX, &errstr); + if (errstr != NULL) { + ctx->error(ctx, "height %s", errstr); + return (-1); + } + } + } else if (ctx->cmdclient != NULL) { + sx = ctx->cmdclient->tty.sx; + sy = ctx->cmdclient->tty.sy; + } else { + sx = ctx->curclient->tty.sx; + sy = ctx->curclient->tty.sy; + } + if (sy > 0 && options_get_number(&global_s_options, "status")) + sy--; + if (sx == 0) + sx = 1; + if (sy == 0) + sy = 1; + + /* Figure out the command for the new window. */ + if (target != NULL) + cmd = NULL; + else if (args->argc != 0) + cmd = args->argv[0]; + else + cmd = options_get_string(&global_s_options, "default-command"); + + /* Construct the environment. */ + environ_init(&env); + update = options_get_string(&global_s_options, "update-environment"); + if (ctx->cmdclient != NULL) + environ_update(update, &ctx->cmdclient->environ, &env); + + /* Create the new session. */ + idx = -1 - options_get_number(&global_s_options, "base-index"); + s = session_create(newname, cmd, cwd, &env, tiop, idx, sx, sy, &cause); + if (s == NULL) { + ctx->error(ctx, "create session failed: %s", cause); + xfree(cause); + return (-1); + } + environ_free(&env); + + /* Set the initial window name if one given. */ + if (cmd != NULL && args_has(args, 'n')) { + w = s->curw->window; + + xfree(w->name); + w->name = xstrdup(args_get(args, 'n')); + + options_set_number(&w->options, "automatic-rename", 0); + } + + /* + * If a target session is given, this is to be part of a session group, + * so add it to the group and synchronize. + */ + if (groupwith != NULL) { + session_group_add(groupwith, s); + session_group_synchronize_to(s); + session_select(s, RB_ROOT(&s->windows)->idx); + } + + /* + * Set the client to the new session. If a command client exists, it is + * taking this session and needs to get MSG_READY and stay around. + */ + if (!detached) { + if (ctx->cmdclient != NULL) { + server_write_client(ctx->cmdclient, MSG_READY, NULL, 0); + + old_s = ctx->cmdclient->session; + if (old_s != NULL) + ctx->cmdclient->last_session = old_s; + ctx->cmdclient->session = s; + session_update_activity(s); + server_redraw_client(ctx->cmdclient); + } else { + old_s = ctx->curclient->session; + if (old_s != NULL) + ctx->curclient->last_session = old_s; + ctx->curclient->session = s; + session_update_activity(s); + server_redraw_client(ctx->curclient); + } + } + recalculate_sizes(); + server_update_socket(); + + /* + * If there are still configuration file errors to display, put the new + * session's current window into more mode and display them now. + */ + if (cfg_finished && !ARRAY_EMPTY(&cfg_causes)) { + wp = s->curw->window->active; + window_pane_set_mode(wp, &window_copy_mode); + window_copy_init_for_output(wp); + for (i = 0; i < ARRAY_LENGTH(&cfg_causes); i++) { + cause = ARRAY_ITEM(&cfg_causes, i); + window_copy_add(wp, "%s", cause); + xfree(cause); + } + ARRAY_FREE(&cfg_causes); + } + + return (!detached); /* 1 means don't tell command client to exit */ +} diff --git a/external/bsd/tmux/dist/cmd-new-window.c b/external/bsd/tmux/dist/cmd-new-window.c new file mode 100644 index 000000000..3fdc67689 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-new-window.c @@ -0,0 +1,126 @@ +/* $Id: cmd-new-window.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * Create a new window. + */ + +int cmd_new_window_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_new_window_entry = { + "new-window", "neww", + "adkn:Pt:", 0, 1, + "[-adk] [-n window-name] [-t target-window] [command]", + 0, + NULL, + NULL, + cmd_new_window_exec +}; + +int +cmd_new_window_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct session *s; + struct winlink *wl; + char *cmd, *cwd, *cause; + int idx, last, detached; + + if (args_has(args, 'a')) { + wl = cmd_find_window(ctx, args_get(args, 't'), &s); + if (wl == NULL) + return (-1); + idx = wl->idx + 1; + + /* Find the next free index. */ + for (last = idx; last < INT_MAX; last++) { + if (winlink_find_by_index(&s->windows, last) == NULL) + break; + } + if (last == INT_MAX) { + ctx->error(ctx, "no free window indexes"); + return (-1); + } + + /* Move everything from last - 1 to idx up a bit. */ + for (; last > idx; last--) { + wl = winlink_find_by_index(&s->windows, last - 1); + server_link_window(s, wl, s, last, 0, 0, NULL); + server_unlink_window(s, wl); + } + } else { + if ((idx = cmd_find_index(ctx, args_get(args, 't'), &s)) == -2) + return (-1); + } + detached = args_has(args, 'd'); + + wl = NULL; + if (idx != -1) + wl = winlink_find_by_index(&s->windows, idx); + if (wl != NULL && args_has(args, 'k')) { + /* + * Can't use session_detach as it will destroy session if this + * makes it empty. + */ + wl->flags &= ~WINLINK_ALERTFLAGS; + winlink_stack_remove(&s->lastw, wl); + winlink_remove(&s->windows, wl); + + /* Force select/redraw if current. */ + if (wl == s->curw) { + detached = 0; + s->curw = NULL; + } + } + + if (args->argc == 0) + cmd = options_get_string(&s->options, "default-command"); + else + cmd = args->argv[0]; + cwd = options_get_string(&s->options, "default-path"); + if (*cwd == '\0') { + if (ctx->cmdclient != NULL && ctx->cmdclient->cwd != NULL) + cwd = ctx->cmdclient->cwd; + else + cwd = s->cwd; + } + + if (idx == -1) + idx = -1 - options_get_number(&s->options, "base-index"); + wl = session_new(s, args_get(args, 'n'), cmd, cwd, idx, &cause); + if (wl == NULL) { + ctx->error(ctx, "create window failed: %s", cause); + xfree(cause); + return (-1); + } + if (!detached) { + session_select(s, wl->idx); + server_redraw_session_group(s); + } else + server_status_session_group(s); + + if (args_has(args, 'P')) + ctx->print(ctx, "%s:%u", s->name, wl->idx); + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-paste-buffer.c b/external/bsd/tmux/dist/cmd-paste-buffer.c new file mode 100644 index 000000000..37fbaf238 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-paste-buffer.c @@ -0,0 +1,121 @@ +/* $Id: cmd-paste-buffer.c,v 1.3 2011/08/17 18:48:35 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +/* + * Paste paste buffer if present. + */ + +int cmd_paste_buffer_exec(struct cmd *, struct cmd_ctx *); + +void cmd_paste_buffer_filter( + struct window_pane *, const char *, size_t, const char *); + +const struct cmd_entry cmd_paste_buffer_entry = { + "paste-buffer", "pasteb", + "db:rs:t:", 0, 0, + "[-dr] [-s separator] [-b buffer-index] [-t target-pane]", + 0, + NULL, + NULL, + cmd_paste_buffer_exec +}; + +int +cmd_paste_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct window_pane *wp; + struct session *s; + struct paste_buffer *pb; + const char *sepstr; + char *cause; + int buffer; + + if (cmd_find_pane(ctx, args_get(args, 't'), &s, &wp) == NULL) + return (-1); + + if (!args_has(args, 'b')) + buffer = -1; + else { + buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); + if (cause != NULL) { + ctx->error(ctx, "buffer %s", cause); + xfree(cause); + return (-1); + } + } + + if (buffer == -1) + pb = paste_get_top(&global_buffers); + else { + pb = paste_get_index(&global_buffers, buffer); + if (pb == NULL) { + ctx->error(ctx, "no buffer %d", buffer); + return (-1); + } + } + + if (pb != NULL) { + sepstr = args_get(args, 's'); + if (sepstr == NULL) { + if (args_has(args, 'r')) + sepstr = "\n"; + else + sepstr = "\r"; + } + cmd_paste_buffer_filter(wp, pb->data, pb->size, sepstr); + } + + /* Delete the buffer if -d. */ + if (args_has(args, 'd')) { + if (buffer == -1) + paste_free_top(&global_buffers); + else + paste_free_index(&global_buffers, buffer); + } + + return (0); +} + +/* Add bytes to a buffer and filter '\n' according to separator. */ +void +cmd_paste_buffer_filter( + struct window_pane *wp, const char *data, size_t size, const char *sep) +{ + const char *end = data + size; + const char *lf; + size_t seplen; + + seplen = strlen(sep); + while ((lf = memchr(data, '\n', end - data)) != NULL) { + if (lf != data) + bufferevent_write(wp->event, data, lf - data); + bufferevent_write(wp->event, sep, seplen); + data = lf + 1; + } + + if (end != data) + bufferevent_write(wp->event, data, end - data); +} diff --git a/external/bsd/tmux/dist/cmd-pipe-pane.c b/external/bsd/tmux/dist/cmd-pipe-pane.c new file mode 100644 index 000000000..efdc08261 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-pipe-pane.c @@ -0,0 +1,145 @@ +/* $Id: cmd-pipe-pane.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "tmux.h" + +/* + * Open pipe to redirect pane output. If already open, close first. + */ + +int cmd_pipe_pane_exec(struct cmd *, struct cmd_ctx *); + +void cmd_pipe_pane_error_callback(struct bufferevent *, short, void *); + +const struct cmd_entry cmd_pipe_pane_entry = { + "pipe-pane", "pipep", + "ot:", 0, 1, + CMD_TARGET_PANE_USAGE "[-o] [command]", + 0, + NULL, + NULL, + cmd_pipe_pane_exec +}; + +int +cmd_pipe_pane_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct client *c; + struct window_pane *wp; + char *command; + int old_fd, pipe_fd[2], null_fd; + + if ((c = cmd_find_client(ctx, NULL)) == NULL) + return (-1); + + if (cmd_find_pane(ctx, args_get(args, 't'), NULL, &wp) == NULL) + return (-1); + + /* Destroy the old pipe. */ + old_fd = wp->pipe_fd; + if (wp->pipe_fd != -1) { + bufferevent_free(wp->pipe_event); + close(wp->pipe_fd); + wp->pipe_fd = -1; + } + + /* If no pipe command, that is enough. */ + if (args->argc == 0 || *args->argv[0] == '\0') + return (0); + + /* + * With -o, only open the new pipe if there was no previous one. This + * allows a pipe to be toggled with a single key, for example: + * + * bind ^p pipep -o 'cat >>~/output' + */ + if (args_has(self->args, 'o') && old_fd != -1) + return (0); + + /* Open the new pipe. */ + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_fd) != 0) { + ctx->error(ctx, "socketpair error: %s", strerror(errno)); + return (-1); + } + + /* Fork the child. */ + switch (fork()) { + case -1: + ctx->error(ctx, "fork error: %s", strerror(errno)); + return (-1); + case 0: + /* Child process. */ + close(pipe_fd[0]); + clear_signals(1); + + if (dup2(pipe_fd[1], STDIN_FILENO) == -1) + _exit(1); + if (pipe_fd[1] != STDIN_FILENO) + close(pipe_fd[1]); + + null_fd = open(_PATH_DEVNULL, O_WRONLY, 0); + if (dup2(null_fd, STDOUT_FILENO) == -1) + _exit(1); + if (dup2(null_fd, STDERR_FILENO) == -1) + _exit(1); + if (null_fd != STDOUT_FILENO && null_fd != STDERR_FILENO) + close(null_fd); + + closefrom(STDERR_FILENO + 1); + + command = status_replace( + c, NULL, NULL, NULL, args->argv[0], time(NULL), 0); + execl(_PATH_BSHELL, "sh", "-c", command, (char *) NULL); + _exit(1); + default: + /* Parent process. */ + close(pipe_fd[1]); + + wp->pipe_fd = pipe_fd[0]; + wp->pipe_off = EVBUFFER_LENGTH(wp->event->input); + + wp->pipe_event = bufferevent_new(wp->pipe_fd, + NULL, NULL, cmd_pipe_pane_error_callback, wp); + bufferevent_enable(wp->pipe_event, EV_WRITE); + + setblocking(wp->pipe_fd, 0); + return (0); + } +} + +/* ARGSUSED */ +void +cmd_pipe_pane_error_callback( + unused struct bufferevent *bufev, unused short what, void *data) +{ + struct window_pane *wp = data; + + bufferevent_free(wp->pipe_event); + close(wp->pipe_fd); + wp->pipe_fd = -1; +} diff --git a/external/bsd/tmux/dist/cmd-refresh-client.c b/external/bsd/tmux/dist/cmd-refresh-client.c new file mode 100644 index 000000000..2b84263d2 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-refresh-client.c @@ -0,0 +1,51 @@ +/* $Id: cmd-refresh-client.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "tmux.h" + +/* + * Refresh client. + */ + +int cmd_refresh_client_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_refresh_client_entry = { + "refresh-client", "refresh", + "t:", 0, 0, + CMD_TARGET_CLIENT_USAGE, + 0, + NULL, + NULL, + cmd_refresh_client_exec +}; + +int +cmd_refresh_client_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct client *c; + + if ((c = cmd_find_client(ctx, args_get(args, 't'))) == NULL) + return (-1); + + server_redraw_client(c); + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-rename-session.c b/external/bsd/tmux/dist/cmd-rename-session.c new file mode 100644 index 000000000..7c19b653f --- /dev/null +++ b/external/bsd/tmux/dist/cmd-rename-session.c @@ -0,0 +1,69 @@ +/* $Id: cmd-rename-session.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * Change session name. + */ + +int cmd_rename_session_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_rename_session_entry = { + "rename-session", "rename", + "t:", 1, 1, + CMD_TARGET_SESSION_USAGE " new-name", + 0, + NULL, + NULL, + cmd_rename_session_exec +}; + +int +cmd_rename_session_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct session *s; + const char *newname; + + newname = args->argv[0]; + if (!session_check_name(newname)) { + ctx->error(ctx, "bad session name: %s", newname); + return (-1); + } + if (session_find(newname) != NULL) { + ctx->error(ctx, "duplicate session: %s", newname); + return (-1); + } + + if ((s = cmd_find_session(ctx, args_get(args, 't'), 0)) == NULL) + return (-1); + + RB_REMOVE(sessions, &sessions, s); + xfree(s->name); + s->name = xstrdup(newname); + RB_INSERT(sessions, &sessions, s); + + server_status_session(s); + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-rename-window.c b/external/bsd/tmux/dist/cmd-rename-window.c new file mode 100644 index 000000000..d9e674204 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-rename-window.c @@ -0,0 +1,58 @@ +/* $Id: cmd-rename-window.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * Rename a window. + */ + +int cmd_rename_window_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_rename_window_entry = { + "rename-window", "renamew", + "t:", 1, 1, + CMD_TARGET_WINDOW_USAGE " new-name", + 0, + NULL, + NULL, + cmd_rename_window_exec +}; + +int +cmd_rename_window_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct session *s; + struct winlink *wl; + + if ((wl = cmd_find_window(ctx, args_get(args, 't'), &s)) == NULL) + return (-1); + + xfree(wl->window->name); + wl->window->name = xstrdup(args->argv[0]); + options_set_number(&wl->window->options, "automatic-rename", 0); + + server_status_window(wl->window); + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-resize-pane.c b/external/bsd/tmux/dist/cmd-resize-pane.c new file mode 100644 index 000000000..bb6b6494c --- /dev/null +++ b/external/bsd/tmux/dist/cmd-resize-pane.c @@ -0,0 +1,117 @@ +/* $Id: cmd-resize-pane.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * Increase or decrease pane size. + */ + +void cmd_resize_pane_key_binding(struct cmd *, int); +int cmd_resize_pane_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_resize_pane_entry = { + "resize-pane", "resizep", + "DLRt:U", 0, 1, + "[-DLRU] " CMD_TARGET_PANE_USAGE " [adjustment]", + 0, + cmd_resize_pane_key_binding, + NULL, + cmd_resize_pane_exec +}; + +void +cmd_resize_pane_key_binding(struct cmd *self, int key) +{ + switch (key) { + case KEYC_UP | KEYC_CTRL: + self->args = args_create(0); + args_set(self->args, 'U', NULL); + break; + case KEYC_DOWN | KEYC_CTRL: + self->args = args_create(0); + args_set(self->args, 'D', NULL); + break; + case KEYC_LEFT | KEYC_CTRL: + self->args = args_create(0); + args_set(self->args, 'L', NULL); + break; + case KEYC_RIGHT | KEYC_CTRL: + self->args = args_create(0); + args_set(self->args, 'R', NULL); + break; + case KEYC_UP | KEYC_ESCAPE: + self->args = args_create(1, "5"); + args_set(self->args, 'U', NULL); + break; + case KEYC_DOWN | KEYC_ESCAPE: + self->args = args_create(1, "5"); + args_set(self->args, 'D', NULL); + break; + case KEYC_LEFT | KEYC_ESCAPE: + self->args = args_create(1, "5"); + args_set(self->args, 'L', NULL); + break; + case KEYC_RIGHT | KEYC_ESCAPE: + self->args = args_create(1, "5"); + args_set(self->args, 'R', NULL); + break; + default: + self->args = args_create(0); + break; + } +} + +int +cmd_resize_pane_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct winlink *wl; + const char *errstr; + struct window_pane *wp; + u_int adjust; + + if ((wl = cmd_find_pane(ctx, args_get(args, 't'), NULL, &wp)) == NULL) + return (-1); + + if (args->argc == 0) + adjust = 1; + else { + adjust = strtonum(args->argv[0], 1, INT_MAX, &errstr); + if (errstr != NULL) { + ctx->error(ctx, "adjustment %s", errstr); + return (-1); + } + } + + if (args_has(self->args, 'L')) + layout_resize_pane(wp, LAYOUT_LEFTRIGHT, -adjust); + else if (args_has(self->args, 'R')) + layout_resize_pane(wp, LAYOUT_LEFTRIGHT, adjust); + else if (args_has(self->args, 'U')) + layout_resize_pane(wp, LAYOUT_TOPBOTTOM, -adjust); + else if (args_has(self->args, 'D')) + layout_resize_pane(wp, LAYOUT_TOPBOTTOM, adjust); + server_redraw_window(wl->window); + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-respawn-pane.c b/external/bsd/tmux/dist/cmd-respawn-pane.c new file mode 100644 index 000000000..c76878e3f --- /dev/null +++ b/external/bsd/tmux/dist/cmd-respawn-pane.c @@ -0,0 +1,88 @@ +/* $Id: cmd-respawn-pane.c,v 1.1.1.1 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2008 Nicholas Marriott + * Copyright (c) 2011 Marcel P. Partap + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * Respawn a pane (restart the command). Kill existing if -k given. + */ + +int cmd_respawn_pane_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_respawn_pane_entry = { + "respawn-pane", "respawnp", + "kt:", 0, 1, + "[-k] " CMD_TARGET_PANE_USAGE " [command]", + 0, + NULL, + NULL, + cmd_respawn_pane_exec +}; + +int +cmd_respawn_pane_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct winlink *wl; + struct window *w; + struct window_pane *wp; + struct session *s; + struct environ env; + const char *cmd; + char *cause; + + if ((wl = cmd_find_pane(ctx, args_get(args, 't'), &s, &wp)) == NULL) + return (-1); + w = wl->window; + + if (!args_has(self->args, 'k') && wp->fd != -1) { + ctx->error(ctx, "pane still active: %s:%u.%u", + s->name, wl->idx, window_pane_index(w, wp)); + return (-1); + } + + environ_init(&env); + environ_copy(&global_environ, &env); + environ_copy(&s->environ, &env); + server_fill_environ(s, &env); + + window_pane_reset_mode(wp); + screen_reinit(&wp->base); + input_init(wp); + + if (args->argc != 0) + cmd = args->argv[0]; + else + cmd = NULL; + if (window_pane_spawn(wp, cmd, NULL, NULL, &env, s->tio, &cause) != 0) { + ctx->error(ctx, "respawn pane failed: %s", cause); + xfree(cause); + environ_free(&env); + return (-1); + } + wp->flags |= PANE_REDRAW; + server_status_window(w); + + environ_free(&env); + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-respawn-window.c b/external/bsd/tmux/dist/cmd-respawn-window.c new file mode 100644 index 000000000..73aa1e2b7 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-respawn-window.c @@ -0,0 +1,100 @@ +/* $Id: cmd-respawn-window.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2008 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * Respawn a window (restart the command). Kill existing if -k given. + */ + +int cmd_respawn_window_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_respawn_window_entry = { + "respawn-window", "respawnw", + "kt:", 0, 1, + "[-k] " CMD_TARGET_WINDOW_USAGE " [command]", + 0, + NULL, + NULL, + cmd_respawn_window_exec +}; + +int +cmd_respawn_window_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct winlink *wl; + struct window *w; + struct window_pane *wp; + struct session *s; + struct environ env; + const char *cmd; + char *cause; + + if ((wl = cmd_find_window(ctx, args_get(args, 't'), &s)) == NULL) + return (-1); + w = wl->window; + + if (!args_has(self->args, 'k')) { + TAILQ_FOREACH(wp, &w->panes, entry) { + if (wp->fd == -1) + continue; + ctx->error(ctx, + "window still active: %s:%d", s->name, wl->idx); + return (-1); + } + } + + environ_init(&env); + environ_copy(&global_environ, &env); + environ_copy(&s->environ, &env); + server_fill_environ(s, &env); + + wp = TAILQ_FIRST(&w->panes); + TAILQ_REMOVE(&w->panes, wp, entry); + layout_free(w); + window_destroy_panes(w); + TAILQ_INSERT_HEAD(&w->panes, wp, entry); + window_pane_resize(wp, w->sx, w->sy); + if (args->argc != 0) + cmd = args->argv[0]; + else + cmd = NULL; + if (window_pane_spawn(wp, cmd, NULL, NULL, &env, s->tio, &cause) != 0) { + ctx->error(ctx, "respawn window failed: %s", cause); + xfree(cause); + environ_free(&env); + server_destroy_pane(wp); + return (-1); + } + layout_init(w); + window_pane_reset_mode(wp); + screen_reinit(&wp->base); + input_init(wp); + window_set_active_pane(w, wp); + + recalculate_sizes(); + server_redraw_window(w); + + environ_free(&env); + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-rotate-window.c b/external/bsd/tmux/dist/cmd-rotate-window.c new file mode 100644 index 000000000..57f552088 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-rotate-window.c @@ -0,0 +1,119 @@ +/* $Id: cmd-rotate-window.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "tmux.h" + +/* + * Rotate the panes in a window. + */ + +void cmd_rotate_window_key_binding(struct cmd *, int); +int cmd_rotate_window_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_rotate_window_entry = { + "rotate-window", "rotatew", + "Dt:U", 0, 0, + "[-DU] " CMD_TARGET_WINDOW_USAGE, + 0, + cmd_rotate_window_key_binding, + NULL, + cmd_rotate_window_exec +}; + +void +cmd_rotate_window_key_binding(struct cmd *self, int key) +{ + self->args = args_create(0); + if (key == ('o' | KEYC_ESCAPE)) + args_set(self->args, 'D', NULL); +} + +int +cmd_rotate_window_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct winlink *wl; + struct window *w; + struct window_pane *wp, *wp2; + struct layout_cell *lc; + u_int sx, sy, xoff, yoff; + + if ((wl = cmd_find_window(ctx, args_get(args, 't'), NULL)) == NULL) + return (-1); + w = wl->window; + + if (args_has(self->args, 'D')) { + wp = TAILQ_LAST(&w->panes, window_panes); + TAILQ_REMOVE(&w->panes, wp, entry); + TAILQ_INSERT_HEAD(&w->panes, wp, entry); + + lc = wp->layout_cell; + xoff = wp->xoff; yoff = wp->yoff; + sx = wp->sx; sy = wp->sy; + TAILQ_FOREACH(wp, &w->panes, entry) { + if ((wp2 = TAILQ_NEXT(wp, entry)) == NULL) + break; + wp->layout_cell = wp2->layout_cell; + if (wp->layout_cell != NULL) + wp->layout_cell->wp = wp; + wp->xoff = wp2->xoff; wp->yoff = wp2->yoff; + window_pane_resize(wp, wp2->sx, wp2->sy); + } + wp->layout_cell = lc; + if (wp->layout_cell != NULL) + wp->layout_cell->wp = wp; + wp->xoff = xoff; wp->yoff = yoff; + window_pane_resize(wp, sx, sy); + + if ((wp = TAILQ_PREV(w->active, window_panes, entry)) == NULL) + wp = TAILQ_LAST(&w->panes, window_panes); + window_set_active_pane(w, wp); + server_redraw_window(w); + } else { + wp = TAILQ_FIRST(&w->panes); + TAILQ_REMOVE(&w->panes, wp, entry); + TAILQ_INSERT_TAIL(&w->panes, wp, entry); + + lc = wp->layout_cell; + xoff = wp->xoff; yoff = wp->yoff; + sx = wp->sx; sy = wp->sy; + TAILQ_FOREACH_REVERSE(wp, &w->panes, window_panes, entry) { + if ((wp2 = TAILQ_PREV(wp, window_panes, entry)) == NULL) + break; + wp->layout_cell = wp2->layout_cell; + if (wp->layout_cell != NULL) + wp->layout_cell->wp = wp; + wp->xoff = wp2->xoff; wp->yoff = wp2->yoff; + window_pane_resize(wp, wp2->sx, wp2->sy); + } + wp->layout_cell = lc; + if (wp->layout_cell != NULL) + wp->layout_cell->wp = wp; + wp->xoff = xoff; wp->yoff = yoff; + window_pane_resize(wp, sx, sy); + + if ((wp = TAILQ_NEXT(w->active, entry)) == NULL) + wp = TAILQ_FIRST(&w->panes); + window_set_active_pane(w, wp); + server_redraw_window(w); + } + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-run-shell.c b/external/bsd/tmux/dist/cmd-run-shell.c new file mode 100644 index 000000000..48f9cee99 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-run-shell.c @@ -0,0 +1,141 @@ +/* $Id: cmd-run-shell.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Tiago Cunha + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include + +#include "tmux.h" + +/* + * Runs a command without a window. + */ + +int cmd_run_shell_exec(struct cmd *, struct cmd_ctx *); + +void cmd_run_shell_callback(struct job *); +void cmd_run_shell_free(void *); + +const struct cmd_entry cmd_run_shell_entry = { + "run-shell", "run", + "", 1, 1, + "command", + 0, + NULL, + NULL, + cmd_run_shell_exec +}; + +struct cmd_run_shell_data { + char *cmd; + struct cmd_ctx ctx; +}; + +int +cmd_run_shell_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct cmd_run_shell_data *cdata; + const char *shellcmd = args->argv[0]; + + cdata = xmalloc(sizeof *cdata); + cdata->cmd = xstrdup(args->argv[0]); + memcpy(&cdata->ctx, ctx, sizeof cdata->ctx); + + if (ctx->cmdclient != NULL) + ctx->cmdclient->references++; + if (ctx->curclient != NULL) + ctx->curclient->references++; + + job_run(shellcmd, cmd_run_shell_callback, cmd_run_shell_free, cdata); + + return (1); /* don't let client exit */ +} + +void +cmd_run_shell_callback(struct job *job) +{ + struct cmd_run_shell_data *cdata = job->data; + struct cmd_ctx *ctx = &cdata->ctx; + char *cmd, *msg, *line; + size_t size; + int retcode; + u_int lines; + + if (ctx->cmdclient != NULL && ctx->cmdclient->flags & CLIENT_DEAD) + return; + if (ctx->curclient != NULL && ctx->curclient->flags & CLIENT_DEAD) + return; + + lines = 0; + do { + if ((line = evbuffer_readline(job->event->input)) != NULL) { + ctx->print(ctx, "%s", line); + lines++; + } + } while (line != NULL); + + size = EVBUFFER_LENGTH(job->event->input); + if (size != 0) { + line = xmalloc(size + 1); + memcpy(line, EVBUFFER_DATA(job->event->input), size); + line[size] = '\0'; + + ctx->print(ctx, "%s", line); + lines++; + + xfree(line); + } + + cmd = cdata->cmd; + + msg = NULL; + if (WIFEXITED(job->status)) { + if ((retcode = WEXITSTATUS(job->status)) != 0) + xasprintf(&msg, "'%s' returned %d", cmd, retcode); + } else if (WIFSIGNALED(job->status)) { + retcode = WTERMSIG(job->status); + xasprintf(&msg, "'%s' terminated by signal %d", cmd, retcode); + } + if (msg != NULL) { + if (lines != 0) + ctx->print(ctx, "%s", msg); + else + ctx->info(ctx, "%s", msg); + xfree(msg); + } +} + +void +cmd_run_shell_free(void *data) +{ + struct cmd_run_shell_data *cdata = data; + struct cmd_ctx *ctx = &cdata->ctx; + + if (ctx->cmdclient != NULL) { + ctx->cmdclient->references--; + ctx->cmdclient->flags |= CLIENT_EXIT; + } + if (ctx->curclient != NULL) + ctx->curclient->references--; + + xfree(cdata->cmd); + xfree(cdata); +} diff --git a/external/bsd/tmux/dist/cmd-save-buffer.c b/external/bsd/tmux/dist/cmd-save-buffer.c new file mode 100644 index 000000000..c36d2c309 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-save-buffer.c @@ -0,0 +1,102 @@ +/* $Id: cmd-save-buffer.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Tiago Cunha + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include +#include + +#include "tmux.h" + +/* + * Saves a session paste buffer to a file. + */ + +int cmd_save_buffer_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_save_buffer_entry = { + "save-buffer", "saveb", + "ab:", 1, 1, + "[-a] " CMD_BUFFER_USAGE, + 0, + NULL, + NULL, + cmd_save_buffer_exec +}; + +int +cmd_save_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct client *c = ctx->cmdclient; + struct paste_buffer *pb; + const char *path; + char *cause; + int buffer; + mode_t mask; + FILE *f; + + if (!args_has(args, 'b')) { + if ((pb = paste_get_top(&global_buffers)) == NULL) { + ctx->error(ctx, "no buffers"); + return (-1); + } + } else { + buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); + if (cause != NULL) { + ctx->error(ctx, "buffer %s", cause); + xfree(cause); + return (-1); + } + + pb = paste_get_index(&global_buffers, buffer); + if (pb == NULL) { + ctx->error(ctx, "no buffer %d", buffer); + return (-1); + } + } + + path = args->argv[0]; + if (strcmp(path, "-") == 0) { + if (c == NULL) { + ctx->error(ctx, "%s: can't write to stdout", path); + return (-1); + } + bufferevent_write(c->stdout_event, pb->data, pb->size); + } else { + mask = umask(S_IRWXG | S_IRWXO); + if (args_has(self->args, 'a')) + f = fopen(path, "ab"); + else + f = fopen(path, "wb"); + umask(mask); + if (f == NULL) { + ctx->error(ctx, "%s: %s", path, strerror(errno)); + return (-1); + } + if (fwrite(pb->data, 1, pb->size, f) != pb->size) { + ctx->error(ctx, "%s: fwrite error", path); + fclose(f); + return (-1); + } + fclose(f); + } + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-select-layout.c b/external/bsd/tmux/dist/cmd-select-layout.c new file mode 100644 index 000000000..a47451793 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-select-layout.c @@ -0,0 +1,133 @@ +/* $Id: cmd-select-layout.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "tmux.h" + +/* + * Switch window to selected layout. + */ + +void cmd_select_layout_key_binding(struct cmd *, int); +int cmd_select_layout_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_select_layout_entry = { + "select-layout", "selectl", + "npt:", 0, 1, + "[-np] " CMD_TARGET_WINDOW_USAGE " [layout-name]", + 0, + cmd_select_layout_key_binding, + NULL, + cmd_select_layout_exec +}; + +const struct cmd_entry cmd_next_layout_entry = { + "next-layout", "nextl", + "t:", 0, 0, + CMD_TARGET_WINDOW_USAGE, + 0, + NULL, + NULL, + cmd_select_layout_exec +}; + +const struct cmd_entry cmd_previous_layout_entry = { + "previous-layout", "prevl", + "t:", 0, 0, + CMD_TARGET_WINDOW_USAGE, + 0, + NULL, + NULL, + cmd_select_layout_exec +}; + +void +cmd_select_layout_key_binding(struct cmd *self, int key) +{ + switch (key) { + case '1' | KEYC_ESCAPE: + self->args = args_create(1, "even-horizontal"); + break; + case '2' | KEYC_ESCAPE: + self->args = args_create(1, "even-vertical"); + break; + case '3' | KEYC_ESCAPE: + self->args = args_create(1, "main-horizontal"); + break; + case '4' | KEYC_ESCAPE: + self->args = args_create(1, "main-vertical"); + break; + case '5' | KEYC_ESCAPE: + self->args = args_create(1, "tiled"); + break; + default: + self->args = args_create(0); + break; + } +} + +int +cmd_select_layout_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct winlink *wl; + const char *layoutname; + int next, previous, layout; + + if ((wl = cmd_find_window(ctx, args_get(args, 't'), NULL)) == NULL) + return (-1); + + next = self->entry == &cmd_next_layout_entry; + if (args_has(self->args, 'n')) + next = 1; + previous = self->entry == &cmd_previous_layout_entry; + if (args_has(self->args, 'p')) + previous = 1; + + if (next || previous) { + if (next) + layout = layout_set_next(wl->window); + else + layout = layout_set_previous(wl->window); + ctx->info(ctx, "arranging in: %s", layout_set_name(layout)); + return (0); + } + + if (args->argc == 0) + layout = wl->window->lastlayout; + else + layout = layout_set_lookup(args->argv[0]); + if (layout != -1) { + layout = layout_set_select(wl->window, layout); + ctx->info(ctx, "arranging in: %s", layout_set_name(layout)); + return (0); + } + + if (args->argc != 0) { + layoutname = args->argv[0]; + if (layout_parse(wl->window, layoutname) == -1) { + ctx->error(ctx, "can't set layout: %s", layoutname); + return (-1); + } + ctx->info(ctx, "arranging in: %s", layoutname); + return (0); + } + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-select-pane.c b/external/bsd/tmux/dist/cmd-select-pane.c new file mode 100644 index 000000000..8836c7f43 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-select-pane.c @@ -0,0 +1,116 @@ +/* $Id: cmd-select-pane.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "tmux.h" + +/* + * Select pane. + */ + +void cmd_select_pane_key_binding(struct cmd *, int); +int cmd_select_pane_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_select_pane_entry = { + "select-pane", "selectp", + "lDLRt:U", 0, 0, + "[-lDLRU] " CMD_TARGET_PANE_USAGE, + 0, + cmd_select_pane_key_binding, + NULL, + cmd_select_pane_exec +}; + +const struct cmd_entry cmd_last_pane_entry = { + "last-pane", "lastp", + "t:", 0, 0, + CMD_TARGET_WINDOW_USAGE, + 0, + NULL, + NULL, + cmd_select_pane_exec +}; + +void +cmd_select_pane_key_binding(struct cmd *self, int key) +{ + self->args = args_create(0); + if (key == KEYC_UP) + args_set(self->args, 'U', NULL); + if (key == KEYC_DOWN) + args_set(self->args, 'D', NULL); + if (key == KEYC_LEFT) + args_set(self->args, 'L', NULL); + if (key == KEYC_RIGHT) + args_set(self->args, 'R', NULL); + if (key == 'o') + args_set(self->args, 't', ":.+"); +} + +int +cmd_select_pane_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct winlink *wl; + struct window_pane *wp; + + if (self->entry == &cmd_last_pane_entry || args_has(args, 'l')) { + wl = cmd_find_window(ctx, args_get(args, 't'), NULL); + if (wl == NULL) + return (-1); + + if (wl->window->last == NULL) { + ctx->error(ctx, "no last pane"); + return (-1); + } + + window_set_active_pane(wl->window, wl->window->last); + server_status_window(wl->window); + server_redraw_window_borders(wl->window); + + return (0); + } + + if ((wl = cmd_find_pane(ctx, args_get(args, 't'), NULL, &wp)) == NULL) + return (-1); + + if (!window_pane_visible(wp)) { + ctx->error(ctx, "pane not visible"); + return (-1); + } + + if (args_has(self->args, 'L')) + wp = window_pane_find_left(wp); + else if (args_has(self->args, 'R')) + wp = window_pane_find_right(wp); + else if (args_has(self->args, 'U')) + wp = window_pane_find_up(wp); + else if (args_has(self->args, 'D')) + wp = window_pane_find_down(wp); + if (wp == NULL) { + ctx->error(ctx, "pane not found"); + return (-1); + } + + window_set_active_pane(wl->window, wp); + server_status_window(wl->window); + server_redraw_window_borders(wl->window); + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-select-window.c b/external/bsd/tmux/dist/cmd-select-window.c new file mode 100644 index 000000000..481766cea --- /dev/null +++ b/external/bsd/tmux/dist/cmd-select-window.c @@ -0,0 +1,139 @@ +/* $Id: cmd-select-window.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * Select window by index. + */ + +void cmd_select_window_key_binding(struct cmd *, int); +int cmd_select_window_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_select_window_entry = { + "select-window", "selectw", + "lnpt:", 0, 0, + "[-lnp] " CMD_TARGET_WINDOW_USAGE, + 0, + cmd_select_window_key_binding, + NULL, + cmd_select_window_exec +}; + +const struct cmd_entry cmd_next_window_entry = { + "next-window", "next", + "at:", 0, 0, + "[-a] " CMD_TARGET_SESSION_USAGE, + 0, + cmd_select_window_key_binding, + NULL, + cmd_select_window_exec +}; + +const struct cmd_entry cmd_previous_window_entry = { + "previous-window", "prev", + "at:", 0, 0, + "[-a] " CMD_TARGET_SESSION_USAGE, + 0, + cmd_select_window_key_binding, + NULL, + cmd_select_window_exec +}; + +const struct cmd_entry cmd_last_window_entry = { + "last-window", "last", + "t:", 0, 0, + CMD_TARGET_SESSION_USAGE, + 0, + NULL, + NULL, + cmd_select_window_exec +}; + +void +cmd_select_window_key_binding(struct cmd *self, int key) +{ + char tmp[16]; + + self->args = args_create(0); + if (key >= '0' && key <= '9') { + xsnprintf(tmp, sizeof tmp, ":%d", key - '0'); + args_set(self->args, 't', tmp); + } + if (key == ('n' | KEYC_ESCAPE) || key == ('p' | KEYC_ESCAPE)) + args_set(self->args, 'a', NULL); +} + +int +cmd_select_window_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct winlink *wl; + struct session *s; + int next, previous, last, activity; + + next = self->entry == &cmd_next_window_entry; + if (args_has(self->args, 'n')) + next = 1; + previous = self->entry == &cmd_previous_window_entry; + if (args_has(self->args, 'p')) + previous = 1; + last = self->entry == &cmd_last_window_entry; + if (args_has(self->args, 'l')) + last = 1; + + if (next || previous || last) { + s = cmd_find_session(ctx, args_get(args, 't'), 0); + if (s == NULL) + return (-1); + + activity = args_has(self->args, 'a'); + if (next) { + if (session_next(s, activity) != 0) { + ctx->error(ctx, "no next window"); + return (-1); + } + } else if (previous) { + if (session_previous(s, activity) != 0) { + ctx->error(ctx, "no previous window"); + return (-1); + } + } else { + if (session_last(s) != 0) { + ctx->error(ctx, "no last window"); + return (-1); + } + } + + server_redraw_session(s); + } else { + wl = cmd_find_window(ctx, args_get(args, 't'), &s); + if (wl == NULL) + return (-1); + + if (session_select(s, wl->idx) == 0) + server_redraw_session(s); + } + recalculate_sizes(); + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-send-keys.c b/external/bsd/tmux/dist/cmd-send-keys.c new file mode 100644 index 000000000..5c0833d84 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-send-keys.c @@ -0,0 +1,65 @@ +/* $Id: cmd-send-keys.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2008 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * Send keys to client. + */ + +int cmd_send_keys_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_send_keys_entry = { + "send-keys", "send", + "t:", 0, -1, + "[-t target-pane] key ...", + 0, + NULL, + NULL, + cmd_send_keys_exec +}; + +int +cmd_send_keys_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct window_pane *wp; + struct session *s; + const char *str; + int i, key; + + if (cmd_find_pane(ctx, args_get(args, 't'), &s, &wp) == NULL) + return (-1); + + for (i = 0; i < args->argc; i++) { + str = args->argv[i]; + + if ((key = key_string_lookup_string(str)) != KEYC_NONE) { + window_pane_key(wp, s, key); + } else { + for (; *str != '\0'; str++) + window_pane_key(wp, s, *str); + } + } + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-send-prefix.c b/external/bsd/tmux/dist/cmd-send-prefix.c new file mode 100644 index 000000000..ca99249ce --- /dev/null +++ b/external/bsd/tmux/dist/cmd-send-prefix.c @@ -0,0 +1,54 @@ +/* $Id: cmd-send-prefix.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "tmux.h" + +/* + * Send prefix key as a key. + */ + +int cmd_send_prefix_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_send_prefix_entry = { + "send-prefix", NULL, + "t:", 0, 0, + CMD_TARGET_PANE_USAGE, + 0, + NULL, + NULL, + cmd_send_prefix_exec +}; + +int +cmd_send_prefix_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct session *s; + struct window_pane *wp; + struct keylist *keylist; + + if (cmd_find_pane(ctx, args_get(args, 't'), &s, &wp) == NULL) + return (-1); + + keylist = options_get_data(&s->options, "prefix"); + window_pane_key(wp, s, ARRAY_FIRST(keylist)); + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-server-info.c b/external/bsd/tmux/dist/cmd-server-info.c new file mode 100644 index 000000000..e0956e570 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-server-info.c @@ -0,0 +1,185 @@ +/* $Id: cmd-server-info.c,v 1.3 2011/08/17 18:48:35 jmmv Exp $ */ + +/* + * Copyright (c) 2008 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include +#include +#include +#include + +#include "tmux.h" + +/* + * Show various information about server. + */ + +int cmd_server_info_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_server_info_entry = { + "server-info", "info", + "", 0, 0, + "", + 0, + NULL, + NULL, + cmd_server_info_exec +}; + +/* ARGSUSED */ +int +cmd_server_info_exec(unused struct cmd *self, struct cmd_ctx *ctx) +{ + struct tty_term *term; + struct client *c; + struct session *s; + struct winlink *wl; + struct window *w; + struct window_pane *wp; + struct tty_code *code; + const struct tty_term_code_entry *ent; + struct utsname un; + struct job *job; + struct grid *gd; + struct grid_line *gl; + u_int i, j, k; + char *tim; + time_t t; + u_int lines, ulines; + size_t size, usize; + + tim = ctime(&start_time); + *strchr(tim, '\n') = '\0'; + ctx->print(ctx, + "tmux " VERSION ", pid %ld, started %s", (long) getpid(), tim); + ctx->print( + ctx, "socket path %s, debug level %d", socket_path, debug_level); + if (uname(&un) >= 0) { + ctx->print(ctx, "system is %s %s %s %s", + un.sysname, un.release, un.version, un.machine); + } + if (cfg_file != NULL) + ctx->print(ctx, "configuration file is %s", cfg_file); + else + ctx->print(ctx, "configuration file not specified"); + ctx->print(ctx, "protocol version is %d", PROTOCOL_VERSION); + ctx->print(ctx, "%s", ""); + + ctx->print(ctx, "Clients:"); + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session == NULL) + continue; + + ctx->print(ctx,"%2d: %s (%d, %d): %s [%ux%u %s bs=%hho] " + "[flags=0x%x/0x%x, references=%u]", i, c->tty.path, + c->ibuf.fd, c->tty.fd, c->session->name, + c->tty.sx, c->tty.sy, c->tty.termname, + c->tty.tio.c_cc[VERASE], c->flags, + c->tty.flags, c->references); + } + ctx->print(ctx, "%s", ""); + + ctx->print(ctx, "Sessions: [%zu/%zu]", + sizeof (struct grid_cell), sizeof (struct grid_utf8)); + RB_FOREACH(s, sessions, &sessions) { + t = s->creation_time.tv_sec; + tim = ctime(&t); + *strchr(tim, '\n') = '\0'; + + ctx->print(ctx, "%2u: %s: %u windows (created %s) [%ux%u] " + "[flags=0x%x]", s->idx, s->name, + winlink_count(&s->windows), tim, s->sx, s->sy, s->flags); + RB_FOREACH(wl, winlinks, &s->windows) { + w = wl->window; + ctx->print(ctx, "%4u: %s [%ux%u] [flags=0x%x, " + "references=%u, last layout=%d]", wl->idx, w->name, + w->sx, w->sy, w->flags, w->references, + w->lastlayout); + j = 0; + TAILQ_FOREACH(wp, &w->panes, entry) { + lines = ulines = size = usize = 0; + gd = wp->base.grid; + for (k = 0; k < gd->hsize + gd->sy; k++) { + gl = &gd->linedata[k]; + if (gl->celldata != NULL) { + lines++; + size += gl->cellsize * + sizeof *gl->celldata; + } + if (gl->utf8data != NULL) { + ulines++; + usize += gl->utf8size * + sizeof *gl->utf8data; + } + } + ctx->print(ctx, "%6u: %s %lu %d %u/%u, %zu " + "bytes; UTF-8 %u/%u, %zu bytes", j, + wp->tty, (u_long) wp->pid, wp->fd, lines, + gd->hsize + gd->sy, size, ulines, + gd->hsize + gd->sy, usize); + j++; + } + } + } + ctx->print(ctx, "%s", ""); + + ctx->print(ctx, "Terminals:"); + LIST_FOREACH(term, &tty_terms, entry) { + ctx->print(ctx, "%s [references=%u, flags=0x%x]:", + term->name, term->references, term->flags); + for (i = 0; i < NTTYCODE; i++) { + ent = &tty_term_codes[i]; + code = &term->codes[ent->code]; + switch (code->type) { + case TTYCODE_NONE: + ctx->print(ctx, "%2u: %s: [missing]", + ent->code, ent->name); + break; + case TTYCODE_STRING: { + size_t slen = strlen(code->value.string); + char out[slen * 4 + 1]; + strvisx(out, code->value.string, slen, + VIS_OCTAL|VIS_TAB|VIS_NL); + ctx->print(ctx, "%2u: %s: (string) %s", + ent->code, ent->name, out); + break; + } + case TTYCODE_NUMBER: + ctx->print(ctx, "%2u: %s: (number) %d", + ent->code, ent->name, code->value.number); + break; + case TTYCODE_FLAG: + ctx->print(ctx, "%2u: %s: (flag) %s", + ent->code, ent->name, + code->value.flag ? "true" : "false"); + break; + } + } + } + ctx->print(ctx, "%s", ""); + + ctx->print(ctx, "Jobs:"); + LIST_FOREACH(job, &all_jobs, lentry) { + ctx->print(ctx, "%s [fd=%d, pid=%d, status=%d]", + job->cmd, job->fd, job->pid, job->status); + } + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-set-buffer.c b/external/bsd/tmux/dist/cmd-set-buffer.c new file mode 100644 index 000000000..b60d758a1 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-set-buffer.c @@ -0,0 +1,75 @@ +/* $Id: cmd-set-buffer.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * Add or set a session paste buffer. + */ + +int cmd_set_buffer_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_set_buffer_entry = { + "set-buffer", "setb", + "b:", 1, 1, + CMD_BUFFER_USAGE " data", + 0, + NULL, + NULL, + cmd_set_buffer_exec +}; + +int +cmd_set_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + u_int limit; + char *pdata, *cause; + size_t psize; + int buffer; + + limit = options_get_number(&global_options, "buffer-limit"); + + pdata = xstrdup(args->argv[0]); + psize = strlen(pdata); + + if (!args_has(args, 'b')) { + paste_add(&global_buffers, pdata, psize, limit); + return (0); + } + + buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); + if (cause != NULL) { + ctx->error(ctx, "buffer %s", cause); + xfree(cause); + xfree(pdata); + return (-1); + } + + if (paste_replace(&global_buffers, buffer, pdata, psize) != 0) { + ctx->error(ctx, "no buffer %d", buffer); + xfree(pdata); + return (-1); + } + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-set-environment.c b/external/bsd/tmux/dist/cmd-set-environment.c new file mode 100644 index 000000000..140a8643f --- /dev/null +++ b/external/bsd/tmux/dist/cmd-set-environment.c @@ -0,0 +1,94 @@ +/* $Id: cmd-set-environment.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +/* + * Set an environment variable. + */ + +int cmd_set_environment_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_set_environment_entry = { + "set-environment", "setenv", + "grt:u", 1, 2, + "[-gru] " CMD_TARGET_SESSION_USAGE " name [value]", + 0, + NULL, + NULL, + cmd_set_environment_exec +}; + +int +cmd_set_environment_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct session *s; + struct environ *env; + const char *name, *value; + + name = args->argv[0]; + if (*name == '\0') { + ctx->error(ctx, "empty variable name"); + return (-1); + } + if (strchr(name, '=') != NULL) { + ctx->error(ctx, "variable name contains ="); + return (-1); + } + + if (args->argc < 1) + value = NULL; + else + value = args->argv[1]; + + if (args_has(self->args, 'g')) + env = &global_environ; + else { + if ((s = cmd_find_session(ctx, args_get(args, 't'), 0)) == NULL) + return (-1); + env = &s->environ; + } + + if (args_has(self->args, 'u')) { + if (value != NULL) { + ctx->error(ctx, "can't specify a value with -u"); + return (-1); + } + environ_unset(env, name); + } else if (args_has(self->args, 'r')) { + if (value != NULL) { + ctx->error(ctx, "can't specify a value with -r"); + return (-1); + } + environ_set(env, name, NULL); + } else { + if (value == NULL) { + ctx->error(ctx, "no value specified"); + return (-1); + } + environ_set(env, name, value); + } + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-set-option.c b/external/bsd/tmux/dist/cmd-set-option.c new file mode 100644 index 000000000..7e1a389d6 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-set-option.c @@ -0,0 +1,411 @@ +/* $Id: cmd-set-option.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +/* + * Set an option. + */ + +int cmd_set_option_exec(struct cmd *, struct cmd_ctx *); + +int cmd_set_option_find(const char *, const struct options_table_entry **, + const struct options_table_entry **); + +int cmd_set_option_unset(struct cmd *, struct cmd_ctx *, + const struct options_table_entry *, struct options *, + const char *); +int cmd_set_option_set(struct cmd *, struct cmd_ctx *, + const struct options_table_entry *, struct options *, + const char *); + +struct options_entry *cmd_set_option_string(struct cmd *, struct cmd_ctx *, + const struct options_table_entry *, struct options *, + const char *); +struct options_entry *cmd_set_option_number(struct cmd *, struct cmd_ctx *, + const struct options_table_entry *, struct options *, + const char *); +struct options_entry *cmd_set_option_keys(struct cmd *, struct cmd_ctx *, + const struct options_table_entry *, struct options *, + const char *); +struct options_entry *cmd_set_option_colour(struct cmd *, struct cmd_ctx *, + const struct options_table_entry *, struct options *, + const char *); +struct options_entry *cmd_set_option_attributes(struct cmd *, struct cmd_ctx *, + const struct options_table_entry *, struct options *, + const char *); +struct options_entry *cmd_set_option_flag(struct cmd *, struct cmd_ctx *, + const struct options_table_entry *, struct options *, + const char *); +struct options_entry *cmd_set_option_choice(struct cmd *, struct cmd_ctx *, + const struct options_table_entry *, struct options *, + const char *); + +const struct cmd_entry cmd_set_option_entry = { + "set-option", "set", + "agst:uw", 1, 2, + "[-agsuw] [-t target-session|target-window] option [value]", + 0, + NULL, + NULL, + cmd_set_option_exec +}; + +const struct cmd_entry cmd_set_window_option_entry = { + "set-window-option", "setw", + "agt:u", 1, 2, + "[-agu] " CMD_TARGET_WINDOW_USAGE " option [value]", + 0, + NULL, + NULL, + cmd_set_option_exec +}; + +/* Look for an option in all three tables. */ +int +cmd_set_option_find( + const char *optstr, const struct options_table_entry **table, + const struct options_table_entry **oe) +{ + static const struct options_table_entry *tables[] = { + server_options_table, + window_options_table, + session_options_table + }; + const struct options_table_entry *oe_loop; + u_int i; + + for (i = 0; i < nitems(tables); i++) { + for (oe_loop = tables[i]; oe_loop->name != NULL; oe_loop++) { + if (strncmp(oe_loop->name, optstr, strlen(optstr)) != 0) + continue; + + /* If already found, ambiguous. */ + if (*oe != NULL) + return (-1); + *oe = oe_loop; + *table = tables[i]; + + /* Bail now if an exact match. */ + if (strcmp((*oe)->name, optstr) == 0) + break; + } + } + return (0); +} + +int +cmd_set_option_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + const struct options_table_entry *table, *oe; + struct session *s; + struct winlink *wl; + struct client *c; + struct options *oo; + const char *optstr, *valstr; + u_int i; + + /* Get the option name and value. */ + optstr = args->argv[0]; + if (*optstr == '\0') { + ctx->error(ctx, "invalid option"); + return (-1); + } + if (args->argc < 2) + valstr = NULL; + else + valstr = args->argv[1]; + + /* Find the option entry, try each table. */ + table = oe = NULL; + if (cmd_set_option_find(optstr, &table, &oe) != 0) { + ctx->error(ctx, "ambiguous option: %s", optstr); + return (-1); + } + if (oe == NULL) { + ctx->error(ctx, "unknown option: %s", optstr); + return (-1); + } + + /* Work out the tree from the table. */ + if (table == server_options_table) + oo = &global_options; + else if (table == window_options_table) { + if (args_has(self->args, 'g')) + oo = &global_w_options; + else { + wl = cmd_find_window(ctx, args_get(args, 't'), NULL); + if (wl == NULL) + return (-1); + oo = &wl->window->options; + } + } else if (table == session_options_table) { + if (args_has(self->args, 'g')) + oo = &global_s_options; + else { + s = cmd_find_session(ctx, args_get(args, 't'), 0); + if (s == NULL) + return (-1); + oo = &s->options; + } + } else { + ctx->error(ctx, "unknown table"); + return (-1); + } + + /* Unset or set the option. */ + if (args_has(args, 'u')) { + if (cmd_set_option_unset(self, ctx, oe, oo, valstr) != 0) + return (-1); + } else { + if (cmd_set_option_set(self, ctx, oe, oo, valstr) != 0) + return (-1); + } + + /* Update sizes and redraw. May not need it but meh. */ + recalculate_sizes(); + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c != NULL && c->session != NULL) + server_redraw_client(c); + } + + return (0); +} + +/* Unset an option. */ +int +cmd_set_option_unset(struct cmd *self, struct cmd_ctx *ctx, + const struct options_table_entry *oe, struct options *oo, const char *value) +{ + struct args *args = self->args; + + if (args_has(args, 'g')) { + ctx->error(ctx, "can't unset global option: %s", oe->name); + return (-1); + } + if (value != NULL) { + ctx->error(ctx, "value passed to unset option: %s", oe->name); + return (-1); + } + + options_remove(oo, oe->name); + ctx->info(ctx, "unset option: %s", oe->name); + return (0); +} + +/* Set an option. */ +int +cmd_set_option_set(struct cmd *self, struct cmd_ctx *ctx, + const struct options_table_entry *oe, struct options *oo, const char *value) +{ + struct options_entry *o; + const char *s; + + if (oe->type != OPTIONS_TABLE_FLAG && value == NULL) { + ctx->error(ctx, "empty value"); + return (-1); + } + + o = NULL; + switch (oe->type) { + case OPTIONS_TABLE_STRING: + o = cmd_set_option_string(self, ctx, oe, oo, value); + break; + case OPTIONS_TABLE_NUMBER: + o = cmd_set_option_number(self, ctx, oe, oo, value); + break; + case OPTIONS_TABLE_KEYS: + o = cmd_set_option_keys(self, ctx, oe, oo, value); + break; + case OPTIONS_TABLE_COLOUR: + o = cmd_set_option_colour(self, ctx, oe, oo, value); + break; + case OPTIONS_TABLE_ATTRIBUTES: + o = cmd_set_option_attributes(self, ctx, oe, oo, value); + break; + case OPTIONS_TABLE_FLAG: + o = cmd_set_option_flag(self, ctx, oe, oo, value); + break; + case OPTIONS_TABLE_CHOICE: + o = cmd_set_option_choice(self, ctx, oe, oo, value); + break; + } + if (o == NULL) + return (-1); + + s = options_table_print_entry(oe, o); + ctx->info(ctx, "set option: %s -> %s", oe->name, s); + return (0); +} + +/* Set a string option. */ +struct options_entry * +cmd_set_option_string(struct cmd *self, unused struct cmd_ctx *ctx, + const struct options_table_entry *oe, struct options *oo, const char *value) +{ + struct args *args = self->args; + struct options_entry *o; + char *oldval, *newval; + + if (args_has(args, 'a')) { + oldval = options_get_string(oo, oe->name); + xasprintf(&newval, "%s%s", oldval, value); + } else + newval = xstrdup(value); + + o = options_set_string(oo, oe->name, "%s", newval); + + xfree(newval); + return (o); +} + +/* Set a number option. */ +struct options_entry * +cmd_set_option_number(unused struct cmd *self, struct cmd_ctx *ctx, + const struct options_table_entry *oe, struct options *oo, const char *value) +{ + long long ll; + const char *errstr; + + ll = strtonum(value, oe->minimum, oe->maximum, &errstr); + if (errstr != NULL) { + ctx->error(ctx, "value is %s: %s", errstr, value); + return (NULL); + } + + return (options_set_number(oo, oe->name, ll)); +} + +/* Set a keys option. */ +struct options_entry * +cmd_set_option_keys(unused struct cmd *self, struct cmd_ctx *ctx, + const struct options_table_entry *oe, struct options *oo, const char *value) +{ + struct keylist *keylist; + char *copy, *ptr, *s; + int key; + + keylist = xmalloc(sizeof *keylist); + ARRAY_INIT(keylist); + + ptr = copy = xstrdup(value); + while ((s = strsep(&ptr, ",")) != NULL) { + if ((key = key_string_lookup_string(s)) == KEYC_NONE) { + ctx->error(ctx, "unknown key: %s", s); + xfree(copy); + xfree(keylist); + return (NULL); + } + ARRAY_ADD(keylist, key); + } + xfree(copy); + + return (options_set_data(oo, oe->name, keylist, xfree)); +} + +/* Set a colour option. */ +struct options_entry * +cmd_set_option_colour(unused struct cmd *self, struct cmd_ctx *ctx, + const struct options_table_entry *oe, struct options *oo, const char *value) +{ + int colour; + + if ((colour = colour_fromstring(value)) == -1) { + ctx->error(ctx, "bad colour: %s", value); + return (NULL); + } + + return (options_set_number(oo, oe->name, colour)); +} + +/* Set an attributes option. */ +struct options_entry * +cmd_set_option_attributes(unused struct cmd *self, struct cmd_ctx *ctx, + const struct options_table_entry *oe, struct options *oo, const char *value) +{ + int attr; + + if ((attr = attributes_fromstring(value)) == -1) { + ctx->error(ctx, "bad attributes: %s", value); + return (NULL); + } + + return (options_set_number(oo, oe->name, attr)); +} + +/* Set a flag option. */ +struct options_entry * +cmd_set_option_flag(unused struct cmd *self, struct cmd_ctx *ctx, + const struct options_table_entry *oe, struct options *oo, const char *value) +{ + int flag; + + if (value == NULL || *value == '\0') + flag = !options_get_number(oo, oe->name); + else { + if ((value[0] == '1' && value[1] == '\0') || + strcasecmp(value, "on") == 0 || + strcasecmp(value, "yes") == 0) + flag = 1; + else if ((value[0] == '0' && value[1] == '\0') || + strcasecmp(value, "off") == 0 || + strcasecmp(value, "no") == 0) + flag = 0; + else { + ctx->error(ctx, "bad value: %s", value); + return (NULL); + } + } + + return (options_set_number(oo, oe->name, flag)); +} + +/* Set a choice option. */ +struct options_entry * +cmd_set_option_choice(unused struct cmd *self, struct cmd_ctx *ctx, + const struct options_table_entry *oe, struct options *oo, const char *value) +{ + const char **choicep; + int n, choice = -1; + + n = 0; + for (choicep = oe->choices; *choicep != NULL; choicep++) { + n++; + if (strncmp(*choicep, value, strlen(value)) != 0) + continue; + + if (choice != -1) { + ctx->error(ctx, "ambiguous value: %s", value); + return (NULL); + } + choice = n - 1; + } + if (choice == -1) { + ctx->error(ctx, "unknown value: %s", value); + return (NULL); + } + + return (options_set_number(oo, oe->name, choice)); +} diff --git a/external/bsd/tmux/dist/cmd-show-buffer.c b/external/bsd/tmux/dist/cmd-show-buffer.c new file mode 100644 index 000000000..6a7e38e74 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-show-buffer.c @@ -0,0 +1,109 @@ +/* $Id: cmd-show-buffer.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "tmux.h" + +/* + * Show a session paste buffer. + */ + +int cmd_show_buffer_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_show_buffer_entry = { + "show-buffer", "showb", + "b:", 0, 0, + CMD_BUFFER_USAGE, + 0, + NULL, + NULL, + cmd_show_buffer_exec +}; + +int +cmd_show_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct session *s; + struct paste_buffer *pb; + int buffer; + char *in, *buf, *ptr, *cause; + size_t size, len; + u_int width; + + if ((s = cmd_find_session(ctx, NULL, 0)) == NULL) + return (-1); + + if (!args_has(args, 'b')) { + if ((pb = paste_get_top(&global_buffers)) == NULL) { + ctx->error(ctx, "no buffers"); + return (-1); + } + } else { + buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); + if (cause != NULL) { + ctx->error(ctx, "buffer %s", cause); + xfree(cause); + return (-1); + } + + pb = paste_get_index(&global_buffers, buffer); + if (pb == NULL) { + ctx->error(ctx, "no buffer %d", buffer); + return (-1); + } + } + + size = pb->size; + if (size > SIZE_MAX / 4 - 1) + size = SIZE_MAX / 4 - 1; + in = xmalloc(size * 4 + 1); + strvisx(in, pb->data, size, VIS_OCTAL|VIS_TAB); + + width = s->sx; + if (ctx->cmdclient != NULL) + width = ctx->cmdclient->tty.sx; + + buf = xmalloc(width + 1); + len = 0; + + ptr = in; + do { + buf[len++] = *ptr++; + + if (len == width || buf[len - 1] == '\n') { + if (buf[len - 1] == '\n') + len--; + buf[len] = '\0'; + + ctx->print(ctx, "%s", buf); + len = 0; + } + } while (*ptr != '\0'); + + if (len != 0) { + buf[len] = '\0'; + ctx->print(ctx, "%s", buf); + } + xfree(buf); + + xfree(in); + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-show-environment.c b/external/bsd/tmux/dist/cmd-show-environment.c new file mode 100644 index 000000000..30f66d286 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-show-environment.c @@ -0,0 +1,66 @@ +/* $Id: cmd-show-environment.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +/* + * Show environment. + */ + +int cmd_show_environment_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_show_environment_entry = { + "show-environment", "showenv", + "gt:", 0, 0, + "[-g] " CMD_TARGET_SESSION_USAGE, + 0, + NULL, + NULL, + cmd_show_environment_exec +}; + +int +cmd_show_environment_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct session *s; + struct environ *env; + struct environ_entry *envent; + + if (args_has(self->args, 'g')) + env = &global_environ; + else { + if ((s = cmd_find_session(ctx, args_get(args, 't'), 0)) == NULL) + return (-1); + env = &s->environ; + } + + RB_FOREACH(envent, environ, env) { + if (envent->value != NULL) + ctx->print(ctx, "%s=%s", envent->name, envent->value); + else + ctx->print(ctx, "-%s", envent->name); + } + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-show-messages.c b/external/bsd/tmux/dist/cmd-show-messages.c new file mode 100644 index 000000000..350077709 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-show-messages.c @@ -0,0 +1,64 @@ +/* $Id: cmd-show-messages.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +/* + * Show client message log. + */ + +int cmd_show_messages_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_show_messages_entry = { + "show-messages", "showmsgs", + "t:", 0, 0, + CMD_TARGET_CLIENT_USAGE, + 0, + NULL, + NULL, + cmd_show_messages_exec +}; + +int +cmd_show_messages_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct client *c; + struct message_entry *msg; + char *tim; + u_int i; + + if ((c = cmd_find_client(ctx, args_get(args, 't'))) == NULL) + return (-1); + + for (i = 0; i < ARRAY_LENGTH(&c->message_log); i++) { + msg = &ARRAY_ITEM(&c->message_log, i); + + tim = ctime(&msg->msg_time); + *strchr(tim, '\n') = '\0'; + + ctx->print(ctx, "%s %s", tim, msg->msg); + } + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-show-options.c b/external/bsd/tmux/dist/cmd-show-options.c new file mode 100644 index 000000000..1d2ea9b42 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-show-options.c @@ -0,0 +1,97 @@ +/* $Id: cmd-show-options.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +/* + * Show options. + */ + +int cmd_show_options_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_show_options_entry = { + "show-options", "show", + "gst:w", 0, 0, + "[-gsw] [-t target-session|target-window]", + 0, + NULL, + NULL, + cmd_show_options_exec +}; + +const struct cmd_entry cmd_show_window_options_entry = { + "show-window-options", "showw", + "gt:", 0, 0, + "[-g] " CMD_TARGET_WINDOW_USAGE, + 0, + NULL, + NULL, + cmd_show_options_exec +}; + +int +cmd_show_options_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + const struct options_table_entry *table, *oe; + struct session *s; + struct winlink *wl; + struct options *oo; + struct options_entry *o; + const char *optval; + + if (args_has(self->args, 's')) { + oo = &global_options; + table = server_options_table; + } else if (args_has(self->args, 'w') || + self->entry == &cmd_show_window_options_entry) { + table = window_options_table; + if (args_has(self->args, 'g')) + oo = &global_w_options; + else { + wl = cmd_find_window(ctx, args_get(args, 't'), NULL); + if (wl == NULL) + return (-1); + oo = &wl->window->options; + } + } else { + table = session_options_table; + if (args_has(self->args, 'g')) + oo = &global_s_options; + else { + s = cmd_find_session(ctx, args_get(args, 't'), 0); + if (s == NULL) + return (-1); + oo = &s->options; + } + } + + for (oe = table; oe->name != NULL; oe++) { + if ((o = options_find1(oo, oe->name)) == NULL) + continue; + optval = options_table_print_entry(oe, o); + ctx->print(ctx, "%s %s", oe->name, optval); + } + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-source-file.c b/external/bsd/tmux/dist/cmd-source-file.c new file mode 100644 index 000000000..ff65718fa --- /dev/null +++ b/external/bsd/tmux/dist/cmd-source-file.c @@ -0,0 +1,74 @@ +/* $Id: cmd-source-file.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2008 Tiago Cunha + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "tmux.h" + +/* + * Sources a configuration file. + */ + +int cmd_source_file_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_source_file_entry = { + "source-file", "source", + "", 1, 1, + "path", + 0, + NULL, + NULL, + cmd_source_file_exec +}; + +int +cmd_source_file_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct causelist causes; + char *cause; + struct window_pane *wp; + int retval; + u_int i; + + ARRAY_INIT(&causes); + + retval = load_cfg(args->argv[0], ctx, &causes); + if (ARRAY_EMPTY(&causes)) + return (retval); + + if (retval == 1 && !RB_EMPTY(&sessions) && ctx->cmdclient != NULL) { + wp = RB_MIN(sessions, &sessions)->curw->window->active; + window_pane_set_mode(wp, &window_copy_mode); + window_copy_init_for_output(wp); + for (i = 0; i < ARRAY_LENGTH(&causes); i++) { + cause = ARRAY_ITEM(&causes, i); + window_copy_add(wp, "%s", cause); + xfree(cause); + } + } else { + for (i = 0; i < ARRAY_LENGTH(&causes); i++) { + cause = ARRAY_ITEM(&causes, i); + ctx->print(ctx, "%s", cause); + xfree(cause); + } + } + ARRAY_FREE(&causes); + + return (retval); +} diff --git a/external/bsd/tmux/dist/cmd-split-window.c b/external/bsd/tmux/dist/cmd-split-window.c new file mode 100644 index 000000000..2ccfab785 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-split-window.c @@ -0,0 +1,152 @@ +/* $Id: cmd-split-window.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +/* + * Split a window (add a new pane). + */ + +void cmd_split_window_key_binding(struct cmd *, int); +int cmd_split_window_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_split_window_entry = { + "split-window", "splitw", + "dl:hp:Pt:v", 0, 1, + "[-dhvP] [-p percentage|-l size] [-t target-pane] [command]", + 0, + cmd_split_window_key_binding, + NULL, + cmd_split_window_exec +}; + +void +cmd_split_window_key_binding(struct cmd *self, int key) +{ + self->args = args_create(0); + if (key == '%') + args_set(self->args, 'h', NULL); +} + +int +cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct session *s; + struct winlink *wl; + struct window *w; + struct window_pane *wp, *new_wp = NULL; + struct environ env; + char *cmd, *cwd, *cause; + const char *shell; + u_int hlimit, paneidx; + int size, percentage; + enum layout_type type; + struct layout_cell *lc; + + if ((wl = cmd_find_pane(ctx, args_get(args, 't'), &s, &wp)) == NULL) + return (-1); + w = wl->window; + + environ_init(&env); + environ_copy(&global_environ, &env); + environ_copy(&s->environ, &env); + server_fill_environ(s, &env); + + if (args->argc == 0) + cmd = options_get_string(&s->options, "default-command"); + else + cmd = args->argv[0]; + cwd = options_get_string(&s->options, "default-path"); + if (*cwd == '\0') { + if (ctx->cmdclient != NULL && ctx->cmdclient->cwd != NULL) + cwd = ctx->cmdclient->cwd; + else + cwd = s->cwd; + } + + type = LAYOUT_TOPBOTTOM; + if (args_has(args, 'h')) + type = LAYOUT_LEFTRIGHT; + + size = -1; + if (args_has(args, 'l')) { + size = args_strtonum(args, 'l', 0, INT_MAX, &cause); + if (cause != NULL) { + ctx->error(ctx, "size %s", cause); + xfree(cause); + return (-1); + } + } else if (args_has(args, 'p')) { + percentage = args_strtonum(args, 'p', 0, INT_MAX, &cause); + if (cause != NULL) { + ctx->error(ctx, "percentage %s", cause); + xfree(cause); + return (-1); + } + if (type == LAYOUT_TOPBOTTOM) + size = (wp->sy * percentage) / 100; + else + size = (wp->sx * percentage) / 100; + } + hlimit = options_get_number(&s->options, "history-limit"); + + shell = options_get_string(&s->options, "default-shell"); + if (*shell == '\0' || areshell(shell)) + shell = _PATH_BSHELL; + + if ((lc = layout_split_pane(wp, type, size)) == NULL) { + cause = xstrdup("pane too small"); + goto error; + } + new_wp = window_add_pane(w, hlimit); + if (window_pane_spawn( + new_wp, cmd, shell, cwd, &env, s->tio, &cause) != 0) + goto error; + layout_assign_pane(lc, new_wp); + + server_redraw_window(w); + + if (!args_has(args, 'd')) { + window_set_active_pane(w, new_wp); + session_select(s, wl->idx); + server_redraw_session(s); + } else + server_status_session(s); + + environ_free(&env); + + if (args_has(args, 'P')) { + paneidx = window_pane_index(wl->window, new_wp); + ctx->print(ctx, "%s:%u.%u", s->name, wl->idx, paneidx); + } + return (0); + +error: + environ_free(&env); + if (new_wp != NULL) + window_remove_pane(w, new_wp); + ctx->error(ctx, "create pane failed: %s", cause); + xfree(cause); + return (-1); +} diff --git a/external/bsd/tmux/dist/cmd-start-server.c b/external/bsd/tmux/dist/cmd-start-server.c new file mode 100644 index 000000000..3a7d84158 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-start-server.c @@ -0,0 +1,44 @@ +/* $Id: cmd-start-server.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "tmux.h" + +/* + * Start the server and do nothing else. + */ + +int cmd_start_server_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_start_server_entry = { + "start-server", "start", + "", 0, 0, + "", + CMD_STARTSERVER, + NULL, + NULL, + cmd_start_server_exec +}; + +/* ARGSUSED */ +int +cmd_start_server_exec(unused struct cmd *self, unused struct cmd_ctx *ctx) +{ + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-string.c b/external/bsd/tmux/dist/cmd-string.c new file mode 100644 index 000000000..a1405c48b --- /dev/null +++ b/external/bsd/tmux/dist/cmd-string.c @@ -0,0 +1,349 @@ +/* $Id: cmd-string.c,v 1.4 2011/10/07 10:38:02 joerg Exp $ */ + +/* + * Copyright (c) 2008 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "tmux.h" + +/* + * Parse a command from a string. + */ + +int cmd_string_getc(const char *, size_t *); +void cmd_string_ungetc(size_t *); +char *cmd_string_string(const char *, size_t *, char, int); +char *cmd_string_variable(const char *, size_t *); +char *cmd_string_expand_tilde(const char *, size_t *); + +int +cmd_string_getc(const char *s, size_t *p) +{ + const char *ucs = s; + + if (ucs[*p] == '\0') + return (EOF); + return (u_char)(ucs[(*p)++]); +} + +void +cmd_string_ungetc(size_t *p) +{ + (*p)--; +} + +/* + * Parse command string. Returns -1 on error. If returning -1, cause is error + * string, or NULL for empty command. + */ +int +cmd_string_parse(const char *s, struct cmd_list **cmdlist, char **cause) +{ + size_t p; + int ch, i, argc, rval; + char **argv, *buf, *t; + const char *whitespace, *equals; + size_t len, len2; + + argv = NULL; + argc = 0; + + buf = NULL; + len = 0; + + *cause = NULL; + + *cmdlist = NULL; + rval = -1; + + p = 0; + for (;;) { + ch = cmd_string_getc(s, &p); + switch (ch) { + case '\'': + if ((t = cmd_string_string(s, &p, '\'', 0)) == NULL) + goto error; + len2 = strlen(t); + buf = xrealloc(buf, 1, len + len2 + 1); + memcpy(buf + len, t, len2 + 1); + len += len2; + xfree(t); + break; + case '"': + if ((t = cmd_string_string(s, &p, '"', 1)) == NULL) + goto error; + len2 = strlen(t); + buf = xrealloc(buf, 1, len + len2 + 1); + memcpy(buf + len, t, len2 + 1); + len += len2; + xfree(t); + break; + case '$': + if ((t = cmd_string_variable(s, &p)) == NULL) + goto error; + len2 = strlen(t); + buf = xrealloc(buf, 1, len + len2 + 1); + strlcpy(buf + len, t, len2 + 1); + len += len2; + xfree(t); + break; + case '#': + /* Comment: discard rest of line. */ + while ((ch = cmd_string_getc(s, &p)) != EOF) + ; + /* FALLTHROUGH */ + case EOF: + case ' ': + case '\t': + if (buf != NULL) { + buf = xrealloc(buf, 1, len + 1); + buf[len] = '\0'; + + argv = xrealloc(argv, argc + 1, sizeof *argv); + argv[argc++] = buf; + + buf = NULL; + len = 0; + } + + if (ch != EOF) + break; + + while (argc != 0) { + equals = strchr(argv[0], '='); + whitespace = argv[0] + strcspn(argv[0], " \t"); + if (equals == NULL || equals > whitespace) + break; + environ_put(&global_environ, argv[0]); + argc--; + memmove(argv, argv + 1, argc * (sizeof *argv)); + } + if (argc == 0) + goto out; + + *cmdlist = cmd_list_parse(argc, argv, cause); + if (*cmdlist == NULL) + goto out; + + rval = 0; + goto out; + case '~': + if (buf == NULL) { + if ((t = cmd_string_expand_tilde(s, &p)) == NULL) + goto error; + len2 = strlen(t); + buf = xrealloc(buf, 1, len + len2 + 1); + memcpy(buf + len, t, len2 + 1); + len += len2; + xfree(t); + break; + } + /* FALLTHROUGH */ + default: + if (len >= SIZE_MAX - 2) + goto error; + + buf = xrealloc(buf, 1, len + 1); + buf[len++] = ch; + break; + } + } + +error: + xasprintf(cause, "invalid or unknown command: %s", s); + +out: + if (buf != NULL) + xfree(buf); + + if (argv != NULL) { + for (i = 0; i < argc; i++) + xfree(argv[i]); + xfree(argv); + } + + return (rval); +} + +char * +cmd_string_string(const char *s, size_t *p, char endch, int esc) +{ + int ch; + char *buf, *t; + size_t len, len2; + + buf = NULL; + len = 0; + + while ((ch = cmd_string_getc(s, p)) != endch) { + switch (ch) { + case EOF: + goto error; + case '\\': + if (!esc) + break; + switch (ch = cmd_string_getc(s, p)) { + case EOF: + goto error; + case 'e': + ch = '\033'; + break; + case 'r': + ch = '\r'; + break; + case 'n': + ch = '\n'; + break; + case 't': + ch = '\t'; + break; + } + break; + case '$': + if (!esc) + break; + if ((t = cmd_string_variable(s, p)) == NULL) + goto error; + len2 = strlen(t); + buf = xrealloc(buf, 1, len + len2 + 1); + memcpy(buf + len, t, len2 + 1); + len += len2; + xfree(t); + continue; + } + + if (len >= SIZE_MAX - 2) + goto error; + buf = xrealloc(buf, 1, len + 1); + buf[len++] = ch; + } + + buf = xrealloc(buf, 1, len + 1); + buf[len] = '\0'; + return (buf); + +error: + if (buf != NULL) + xfree(buf); + return (NULL); +} + +char * +cmd_string_variable(const char *s, size_t *p) +{ + int ch, fch; + char *buf, *t; + size_t len; + struct environ_entry *envent; + +#define cmd_string_first(ch) ((ch) == '_' || \ + ((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z')) +#define cmd_string_other(ch) ((ch) == '_' || \ + ((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z') || \ + ((ch) >= '0' && (ch) <= '9')) + + buf = NULL; + len = 0; + + fch = EOF; + switch (ch = cmd_string_getc(s, p)) { + case EOF: + goto error; + case '{': + fch = '{'; + + ch = cmd_string_getc(s, p); + if (!cmd_string_first(ch)) + goto error; + /* FALLTHROUGH */ + default: + if (!cmd_string_first(ch)) { + xasprintf(&t, "$%c", ch); + return (t); + } + + buf = xrealloc(buf, 1, len + 1); + buf[len++] = ch; + + for (;;) { + ch = cmd_string_getc(s, p); + if (ch == EOF || !cmd_string_other(ch)) + break; + else { + if (len >= SIZE_MAX - 3) + goto error; + buf = xrealloc(buf, 1, len + 1); + buf[len++] = ch; + } + } + } + + if (fch == '{' && ch != '}') + goto error; + if (ch != EOF && fch != '{') + cmd_string_ungetc(p); /* ch */ + + buf = xrealloc(buf, 1, len + 1); + buf[len] = '\0'; + + envent = environ_find(&global_environ, buf); + xfree(buf); + if (envent == NULL) + return (xstrdup("")); + return (xstrdup(envent->value)); + +error: + if (buf != NULL) + xfree(buf); + return (NULL); +} + +char * +cmd_string_expand_tilde(const char *s, size_t *p) +{ + struct passwd *pw; + struct environ_entry *envent; + char *home, *path, *username; + + home = NULL; + if (cmd_string_getc(s, p) == '/') { + envent = environ_find(&global_environ, "HOME"); + if (envent != NULL && *envent->value != '\0') + home = envent->value; + else if ((pw = getpwuid(getuid())) != NULL) + home = pw->pw_dir; + } else { + cmd_string_ungetc(p); + if ((username = cmd_string_string(s, p, '/', 0)) == NULL) + return (NULL); + if ((pw = getpwnam(username)) != NULL) + home = pw->pw_dir; + xfree(username); + } + if (home == NULL) + return (NULL); + + xasprintf(&path, "%s/", home); + return (path); +} diff --git a/external/bsd/tmux/dist/cmd-suspend-client.c b/external/bsd/tmux/dist/cmd-suspend-client.c new file mode 100644 index 000000000..b36983a2c --- /dev/null +++ b/external/bsd/tmux/dist/cmd-suspend-client.c @@ -0,0 +1,56 @@ +/* $Id: cmd-suspend-client.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +/* + * Suspend client with SIGTSTP. + */ + +int cmd_suspend_client_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_suspend_client_entry = { + "suspend-client", "suspendc", + "t:", 0, 0, + CMD_TARGET_CLIENT_USAGE, + 0, + NULL, + NULL, + cmd_suspend_client_exec +}; + +int +cmd_suspend_client_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct client *c; + + if ((c = cmd_find_client(ctx, args_get(args, 't'))) == NULL) + return (-1); + + tty_stop_tty(&c->tty); + c->flags |= CLIENT_SUSPENDED; + server_write_client(c, MSG_SUSPEND, NULL, 0); + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-swap-pane.c b/external/bsd/tmux/dist/cmd-swap-pane.c new file mode 100644 index 000000000..79fe49582 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-swap-pane.c @@ -0,0 +1,142 @@ +/* $Id: cmd-swap-pane.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * Swap two panes. + */ + +void cmd_swap_pane_key_binding(struct cmd *, int); +int cmd_swap_pane_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_swap_pane_entry = { + "swap-pane", "swapp", + "dDs:t:U", 0, 0, + "[-dDU] " CMD_SRCDST_PANE_USAGE, + 0, + cmd_swap_pane_key_binding, + NULL, + cmd_swap_pane_exec +}; + +void +cmd_swap_pane_key_binding(struct cmd *self, int key) +{ + self->args = args_create(0); + if (key == '{') + args_set(self->args, 'U', NULL); + else if (key == '}') + args_set(self->args, 'D', NULL); +} + +int +cmd_swap_pane_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct winlink *src_wl, *dst_wl; + struct window *src_w, *dst_w; + struct window_pane *tmp_wp, *src_wp, *dst_wp; + struct layout_cell *src_lc, *dst_lc; + u_int sx, sy, xoff, yoff; + + dst_wl = cmd_find_pane(ctx, args_get(args, 't'), NULL, &dst_wp); + if (dst_wl == NULL) + return (-1); + dst_w = dst_wl->window; + + if (!args_has(args, 's')) { + src_w = dst_w; + if (args_has(self->args, 'D')) { + src_wp = TAILQ_NEXT(dst_wp, entry); + if (src_wp == NULL) + src_wp = TAILQ_FIRST(&dst_w->panes); + } else if (args_has(self->args, 'U')) { + src_wp = TAILQ_PREV(dst_wp, window_panes, entry); + if (src_wp == NULL) + src_wp = TAILQ_LAST(&dst_w->panes, window_panes); + } else + return (0); + } else { + src_wl = cmd_find_pane(ctx, args_get(args, 's'), NULL, &src_wp); + if (src_wl == NULL) + return (-1); + src_w = src_wl->window; + } + + if (src_wp == dst_wp) + return (0); + + tmp_wp = TAILQ_PREV(dst_wp, window_panes, entry); + TAILQ_REMOVE(&dst_w->panes, dst_wp, entry); + TAILQ_REPLACE(&src_w->panes, src_wp, dst_wp, entry); + if (tmp_wp == src_wp) + tmp_wp = dst_wp; + if (tmp_wp == NULL) + TAILQ_INSERT_HEAD(&dst_w->panes, src_wp, entry); + else + TAILQ_INSERT_AFTER(&dst_w->panes, tmp_wp, src_wp, entry); + + src_lc = src_wp->layout_cell; + dst_lc = dst_wp->layout_cell; + src_lc->wp = dst_wp; + dst_wp->layout_cell = src_lc; + dst_lc->wp = src_wp; + src_wp->layout_cell = dst_lc; + + src_wp->window = dst_w; + dst_wp->window = src_w; + + sx = src_wp->sx; sy = src_wp->sy; + xoff = src_wp->xoff; yoff = src_wp->yoff; + src_wp->xoff = dst_wp->xoff; src_wp->yoff = dst_wp->yoff; + window_pane_resize(src_wp, dst_wp->sx, dst_wp->sy); + dst_wp->xoff = xoff; dst_wp->yoff = yoff; + window_pane_resize(dst_wp, sx, sy); + + if (!args_has(self->args, 'd')) { + if (src_w != dst_w) { + window_set_active_pane(src_w, dst_wp); + window_set_active_pane(dst_w, src_wp); + } else { + tmp_wp = dst_wp; + if (!window_pane_visible(tmp_wp)) + tmp_wp = src_wp; + window_set_active_pane(src_w, tmp_wp); + } + } else { + if (src_w->active == src_wp) + window_set_active_pane(src_w, dst_wp); + if (dst_w->active == dst_wp) + window_set_active_pane(dst_w, src_wp); + } + if (src_w != dst_w) { + if (src_w->last == src_wp) + src_w->last = NULL; + if (dst_w->last == dst_wp) + dst_w->last = NULL; + } + server_redraw_window(src_w); + server_redraw_window(dst_w); + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-swap-window.c b/external/bsd/tmux/dist/cmd-swap-window.c new file mode 100644 index 000000000..3241e3ae8 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-swap-window.c @@ -0,0 +1,87 @@ +/* $Id: cmd-swap-window.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * Swap one window with another. + */ + +int cmd_swap_window_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_swap_window_entry = { + "swap-window", "swapw", + "ds:t:", 0, 0, + "[-d] " CMD_SRCDST_WINDOW_USAGE, + 0, + NULL, + NULL, + cmd_swap_window_exec +}; + +int +cmd_swap_window_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + const char *target_src, *target_dst; + struct session *src, *dst; + struct session_group *sg_src, *sg_dst; + struct winlink *wl_src, *wl_dst; + struct window *w; + + target_src = args_get(args, 's'); + if ((wl_src = cmd_find_window(ctx, target_src, &src)) == NULL) + return (-1); + target_dst = args_get(args, 't'); + if ((wl_dst = cmd_find_window(ctx, target_dst, &dst)) == NULL) + return (-1); + + sg_src = session_group_find(src); + sg_dst = session_group_find(dst); + if (src != dst && + sg_src != NULL && sg_dst != NULL && sg_src == sg_dst) { + ctx->error(ctx, "can't move window, sessions are grouped"); + return (-1); + } + + if (wl_dst->window == wl_src->window) + return (0); + + w = wl_dst->window; + wl_dst->window = wl_src->window; + wl_src->window = w; + + if (!args_has(self->args, 'd')) { + session_select(dst, wl_dst->idx); + if (src != dst) + session_select(src, wl_src->idx); + } + session_group_synchronize_from(src); + server_redraw_session_group(src); + if (src != dst) { + session_group_synchronize_from(dst); + server_redraw_session_group(dst); + } + recalculate_sizes(); + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-switch-client.c b/external/bsd/tmux/dist/cmd-switch-client.c new file mode 100644 index 000000000..6f1627854 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-switch-client.c @@ -0,0 +1,103 @@ +/* $Id: cmd-switch-client.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +/* + * Switch client to a different session. + */ + +void cmd_switch_client_key_binding(struct cmd *, int); +int cmd_switch_client_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_switch_client_entry = { + "switch-client", "switchc", + "lc:npt:", 0, 0, + "[-lnp] [-c target-client] [-t target-session]", + 0, + cmd_switch_client_key_binding, + NULL, + cmd_switch_client_exec +}; + +void +cmd_switch_client_key_binding(struct cmd *self, int key) +{ + self->args = args_create(0); + switch (key) { + case '(': + args_set(self->args, 'p', NULL); + break; + case ')': + args_set(self->args, 'n', NULL); + break; + case 'L': + args_set(self->args, 'l', NULL); + break; + } +} + +int +cmd_switch_client_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct client *c; + struct session *s; + + if ((c = cmd_find_client(ctx, args_get(args, 'c'))) == NULL) + return (-1); + + s = NULL; + if (args_has(args, 'n')) { + if ((s = session_next_session(c->session)) == NULL) { + ctx->error(ctx, "can't find next session"); + return (-1); + } + } else if (args_has(args, 'p')) { + if ((s = session_previous_session(c->session)) == NULL) { + ctx->error(ctx, "can't find previous session"); + return (-1); + } + } else if (args_has(args, 'l')) { + if (c->last_session != NULL && session_alive(c->last_session)) + s = c->last_session; + if (s == NULL) { + ctx->error(ctx, "can't find last session"); + return (-1); + } + } else + s = cmd_find_session(ctx, args_get(args, 't'), 0); + if (s == NULL) + return (-1); + + if (c->session != NULL) + c->last_session = c->session; + c->session = s; + session_update_activity(s); + + recalculate_sizes(); + server_check_unattached(); + server_redraw_client(c); + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-unbind-key.c b/external/bsd/tmux/dist/cmd-unbind-key.c new file mode 100644 index 000000000..87f8736f7 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-unbind-key.c @@ -0,0 +1,105 @@ +/* $Id: cmd-unbind-key.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "tmux.h" + +/* + * Unbind key from command. + */ + +int cmd_unbind_key_check(struct args *); +int cmd_unbind_key_exec(struct cmd *, struct cmd_ctx *); + +int cmd_unbind_key_table(struct cmd *, struct cmd_ctx *, int); + +const struct cmd_entry cmd_unbind_key_entry = { + "unbind-key", "unbind", + "acnt:", 0, 1, + "[-acn] [-t key-table] key", + 0, + NULL, + cmd_unbind_key_check, + cmd_unbind_key_exec +}; + +int +cmd_unbind_key_check(struct args *args) +{ + if (args_has(args, 'a') && (args->argc != 0 || args_has(args, 't'))) + return (-1); + if (!args_has(args, 'a') && args->argc != 1) + return (-1); + return (0); +} + +int +cmd_unbind_key_exec(struct cmd *self, unused struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct key_binding *bd; + int key; + + if (args_has(args, 'a')) { + while (!SPLAY_EMPTY(&key_bindings)) { + bd = SPLAY_ROOT(&key_bindings); + SPLAY_REMOVE(key_bindings, &key_bindings, bd); + cmd_list_free(bd->cmdlist); + xfree(bd); + } + return (0); + } + + key = key_string_lookup_string(args->argv[0]); + if (key == KEYC_NONE) { + ctx->error(ctx, "unknown key: %s", args->argv[0]); + return (-1); + } + + if (args_has(args, 't')) + return (cmd_unbind_key_table(self, ctx, key)); + + if (!args_has(args, 'n')) + key |= KEYC_PREFIX; + key_bindings_remove(key); + return (0); +} + +int +cmd_unbind_key_table(struct cmd *self, struct cmd_ctx *ctx, int key) +{ + struct args *args = self->args; + const char *tablename; + const struct mode_key_table *mtab; + struct mode_key_binding *mbind, mtmp; + + tablename = args_get(args, 't'); + if ((mtab = mode_key_findtable(tablename)) == NULL) { + ctx->error(ctx, "unknown key table: %s", tablename); + return (-1); + } + + mtmp.key = key; + mtmp.mode = !!args_has(args, 'c'); + if ((mbind = SPLAY_FIND(mode_key_tree, mtab->tree, &mtmp)) != NULL) { + SPLAY_REMOVE(mode_key_tree, mtab->tree, mbind); + xfree(mbind); + } + return (0); +} diff --git a/external/bsd/tmux/dist/cmd-unlink-window.c b/external/bsd/tmux/dist/cmd-unlink-window.c new file mode 100644 index 000000000..e3f570c14 --- /dev/null +++ b/external/bsd/tmux/dist/cmd-unlink-window.c @@ -0,0 +1,70 @@ +/* $Id: cmd-unlink-window.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "tmux.h" + +/* + * Unlink a window, unless it would be destroyed by doing so (only one link). + */ + +int cmd_unlink_window_exec(struct cmd *, struct cmd_ctx *); + +const struct cmd_entry cmd_unlink_window_entry = { + "unlink-window", "unlinkw", + "kt:", 0, 0, + "[-k] " CMD_TARGET_WINDOW_USAGE, + 0, + NULL, + NULL, + cmd_unlink_window_exec +}; + +int +cmd_unlink_window_exec(struct cmd *self, struct cmd_ctx *ctx) +{ + struct args *args = self->args; + struct winlink *wl; + struct window *w; + struct session *s, *s2; + struct session_group *sg; + u_int references; + + if ((wl = cmd_find_window(ctx, args_get(args, 't'), &s)) == NULL) + return (-1); + w = wl->window; + + sg = session_group_find(s); + if (sg != NULL) { + references = 0; + TAILQ_FOREACH(s2, &sg->sessions, gentry) + references++; + } else + references = 1; + + if (!args_has(self->args, 'k') && w->references == references) { + ctx->error(ctx, "window is only linked to one session"); + return (-1); + } + + server_unlink_window(s, wl); + recalculate_sizes(); + + return (0); +} diff --git a/external/bsd/tmux/dist/cmd.c b/external/bsd/tmux/dist/cmd.c new file mode 100644 index 000000000..19b234bc0 --- /dev/null +++ b/external/bsd/tmux/dist/cmd.c @@ -0,0 +1,1214 @@ +/* $Id: cmd.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include +#include +#include +#include + +#include "tmux.h" + +const struct cmd_entry *cmd_table[] = { + &cmd_attach_session_entry, + &cmd_bind_key_entry, + &cmd_break_pane_entry, + &cmd_capture_pane_entry, + &cmd_choose_buffer_entry, + &cmd_choose_client_entry, + &cmd_choose_session_entry, + &cmd_choose_window_entry, + &cmd_clear_history_entry, + &cmd_clock_mode_entry, + &cmd_command_prompt_entry, + &cmd_confirm_before_entry, + &cmd_copy_mode_entry, + &cmd_delete_buffer_entry, + &cmd_detach_client_entry, + &cmd_display_message_entry, + &cmd_display_panes_entry, + &cmd_find_window_entry, + &cmd_has_session_entry, + &cmd_if_shell_entry, + &cmd_join_pane_entry, + &cmd_kill_pane_entry, + &cmd_kill_server_entry, + &cmd_kill_session_entry, + &cmd_kill_window_entry, + &cmd_last_pane_entry, + &cmd_last_window_entry, + &cmd_link_window_entry, + &cmd_list_buffers_entry, + &cmd_list_clients_entry, + &cmd_list_commands_entry, + &cmd_list_keys_entry, + &cmd_list_panes_entry, + &cmd_list_sessions_entry, + &cmd_list_windows_entry, + &cmd_load_buffer_entry, + &cmd_lock_client_entry, + &cmd_lock_server_entry, + &cmd_lock_session_entry, + &cmd_move_window_entry, + &cmd_new_session_entry, + &cmd_new_window_entry, + &cmd_next_layout_entry, + &cmd_next_window_entry, + &cmd_paste_buffer_entry, + &cmd_pipe_pane_entry, + &cmd_previous_layout_entry, + &cmd_previous_window_entry, + &cmd_refresh_client_entry, + &cmd_rename_session_entry, + &cmd_rename_window_entry, + &cmd_resize_pane_entry, + &cmd_respawn_pane_entry, + &cmd_respawn_window_entry, + &cmd_rotate_window_entry, + &cmd_run_shell_entry, + &cmd_save_buffer_entry, + &cmd_select_layout_entry, + &cmd_select_pane_entry, + &cmd_select_window_entry, + &cmd_send_keys_entry, + &cmd_send_prefix_entry, + &cmd_server_info_entry, + &cmd_set_buffer_entry, + &cmd_set_environment_entry, + &cmd_set_option_entry, + &cmd_set_window_option_entry, + &cmd_show_buffer_entry, + &cmd_show_environment_entry, + &cmd_show_messages_entry, + &cmd_show_options_entry, + &cmd_show_window_options_entry, + &cmd_source_file_entry, + &cmd_split_window_entry, + &cmd_start_server_entry, + &cmd_suspend_client_entry, + &cmd_swap_pane_entry, + &cmd_swap_window_entry, + &cmd_switch_client_entry, + &cmd_unbind_key_entry, + &cmd_unlink_window_entry, + NULL +}; + +struct session *cmd_choose_session_list(struct sessionslist *); +struct session *cmd_choose_session(int); +struct client *cmd_choose_client(struct clients *); +struct client *cmd_lookup_client(const char *); +struct session *cmd_lookup_session(const char *, int *); +struct winlink *cmd_lookup_window(struct session *, const char *, int *); +int cmd_lookup_index(struct session *, const char *, int *); +struct window_pane *cmd_lookup_paneid(const char *); +struct session *cmd_pane_session(struct cmd_ctx *, + struct window_pane *, struct winlink **); +struct winlink *cmd_find_window_offset(const char *, struct session *, int *); +int cmd_find_index_offset(const char *, struct session *, int *); +struct window_pane *cmd_find_pane_offset(const char *, struct winlink *); + +int +cmd_pack_argv(int argc, char **argv, char *buf, size_t len) +{ + size_t arglen; + int i; + + *buf = '\0'; + for (i = 0; i < argc; i++) { + if (strlcpy(buf, argv[i], len) >= len) + return (-1); + arglen = strlen(argv[i]) + 1; + buf += arglen; + len -= arglen; + } + + return (0); +} + +int +cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv) +{ + int i; + size_t arglen; + + if (argc == 0) + return (0); + *argv = xcalloc(argc, sizeof **argv); + + buf[len - 1] = '\0'; + for (i = 0; i < argc; i++) { + if (len == 0) { + cmd_free_argv(argc, *argv); + return (-1); + } + + arglen = strlen(buf) + 1; + (*argv)[i] = xstrdup(buf); + buf += arglen; + len -= arglen; + } + + return (0); +} + +char ** +cmd_copy_argv(int argc, char *const *argv) +{ + char **new_argv; + int i; + + if (argc == 0) + return (NULL); + new_argv = xcalloc(argc, sizeof *new_argv); + for (i = 0; i < argc; i++) { + if (argv[i] != NULL) + new_argv[i] = xstrdup(argv[i]); + } + return (new_argv); +} + +void +cmd_free_argv(int argc, char **argv) +{ + int i; + + if (argc == 0) + return; + for (i = 0; i < argc; i++) { + if (argv[i] != NULL) + xfree(argv[i]); + } + xfree(argv); +} + +struct cmd * +cmd_parse(int argc, char **argv, char **cause) +{ + const struct cmd_entry **entryp, *entry; + struct cmd *cmd; + struct args *args; + char s[BUFSIZ]; + int ambiguous = 0; + + *cause = NULL; + if (argc == 0) { + xasprintf(cause, "no command"); + return (NULL); + } + + entry = NULL; + for (entryp = cmd_table; *entryp != NULL; entryp++) { + if ((*entryp)->alias != NULL && + strcmp((*entryp)->alias, argv[0]) == 0) { + ambiguous = 0; + entry = *entryp; + break; + } + + if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0) + continue; + if (entry != NULL) + ambiguous = 1; + entry = *entryp; + + /* Bail now if an exact match. */ + if (strcmp(entry->name, argv[0]) == 0) + break; + } + if (ambiguous) + goto ambiguous; + if (entry == NULL) { + xasprintf(cause, "unknown command: %s", argv[0]); + return (NULL); + } + + args = args_parse(entry->args_template, argc, argv); + if (args == NULL) + goto usage; + if (entry->args_lower != -1 && args->argc < entry->args_lower) + goto usage; + if (entry->args_upper != -1 && args->argc > entry->args_upper) + goto usage; + if (entry->check != NULL && entry->check(args) != 0) + goto usage; + + cmd = xmalloc(sizeof *cmd); + cmd->entry = entry; + cmd->args = args; + return (cmd); + +ambiguous: + *s = '\0'; + for (entryp = cmd_table; *entryp != NULL; entryp++) { + if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0) + continue; + if (strlcat(s, (*entryp)->name, sizeof s) >= sizeof s) + break; + if (strlcat(s, ", ", sizeof s) >= sizeof s) + break; + } + s[strlen(s) - 2] = '\0'; + xasprintf(cause, "ambiguous command: %s, could be: %s", argv[0], s); + return (NULL); + +usage: + if (args != NULL) + args_free(args); + xasprintf(cause, "usage: %s %s", entry->name, entry->usage); + return (NULL); +} + +int +cmd_exec(struct cmd *cmd, struct cmd_ctx *ctx) +{ + return (cmd->entry->exec(cmd, ctx)); +} + +void +cmd_free(struct cmd *cmd) +{ + if (cmd->args != NULL) + args_free(cmd->args); + xfree(cmd); +} + +size_t +cmd_print(struct cmd *cmd, char *buf, size_t len) +{ + size_t off, used; + + off = xsnprintf(buf, len, "%s ", cmd->entry->name); + if (off < len) { + used = args_print(cmd->args, buf + off, len - off); + if (used == 0) + buf[off - 1] = '\0'; + else { + off += used; + buf[off] = '\0'; + } + } + return (off); +} + +/* + * Figure out the current session. Use: 1) the current session, if the command + * context has one; 2) the most recently used session containing the pty of the + * calling client, if any; 3) the session specified in the TMUX variable from + * the environment (as passed from the client); 4) the most recently used + * session from all sessions. + */ +struct session * +cmd_current_session(struct cmd_ctx *ctx, int prefer_unattached) +{ + struct msg_command_data *data = ctx->msgdata; + struct client *c = ctx->cmdclient; + struct session *s; + struct sessionslist ss; + struct winlink *wl; + struct window_pane *wp; + int found; + + if (ctx->curclient != NULL && ctx->curclient->session != NULL) + return (ctx->curclient->session); + + /* + * If the name of the calling client's pty is know, build a list of the + * sessions that contain it and if any choose either the first or the + * newest. + */ + if (c != NULL && c->tty.path != NULL) { + ARRAY_INIT(&ss); + RB_FOREACH(s, sessions, &sessions) { + found = 0; + RB_FOREACH(wl, winlinks, &s->windows) { + TAILQ_FOREACH(wp, &wl->window->panes, entry) { + if (strcmp(wp->tty, c->tty.path) == 0) { + found = 1; + break; + } + } + if (found) + break; + } + if (found) + ARRAY_ADD(&ss, s); + } + + s = cmd_choose_session_list(&ss); + ARRAY_FREE(&ss); + if (s != NULL) + return (s); + } + + /* Use the session from the TMUX environment variable. */ + if (data != NULL && data->pid == getpid() && data->idx != -1) { + s = session_find_by_index(data->idx); + if (s != NULL) + return (s); + } + + return (cmd_choose_session(prefer_unattached)); +} + +/* + * Find the most recently used session, preferring unattached if the flag is + * set. + */ +struct session * +cmd_choose_session(int prefer_unattached) +{ + struct session *s, *sbest; + struct timeval *tv = NULL; + + sbest = NULL; + RB_FOREACH(s, sessions, &sessions) { + if (tv == NULL || timercmp(&s->activity_time, tv, >) || + (prefer_unattached && + !(sbest->flags & SESSION_UNATTACHED) && + (s->flags & SESSION_UNATTACHED))) { + sbest = s; + tv = &s->activity_time; + } + } + + return (sbest); +} + +/* Find the most recently used session from a list. */ +struct session * +cmd_choose_session_list(struct sessionslist *ss) +{ + struct session *s, *sbest; + struct timeval *tv = NULL; + u_int i; + + sbest = NULL; + for (i = 0; i < ARRAY_LENGTH(ss); i++) { + if ((s = ARRAY_ITEM(ss, i)) == NULL) + continue; + + if (tv == NULL || timercmp(&s->activity_time, tv, >)) { + sbest = s; + tv = &s->activity_time; + } + } + + return (sbest); +} + +/* + * Find the current client. First try the current client if set, then pick the + * most recently used of the clients attached to the current session if any, + * then of all clients. + */ +struct client * +cmd_current_client(struct cmd_ctx *ctx) +{ + struct session *s; + struct client *c; + struct clients cc; + u_int i; + + if (ctx->curclient != NULL) + return (ctx->curclient); + + /* + * No current client set. Find the current session and return the + * newest of its clients. + */ + s = cmd_current_session(ctx, 0); + if (s != NULL && !(s->flags & SESSION_UNATTACHED)) { + ARRAY_INIT(&cc); + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + if ((c = ARRAY_ITEM(&clients, i)) == NULL) + continue; + if (s == c->session) + ARRAY_ADD(&cc, c); + } + + c = cmd_choose_client(&cc); + ARRAY_FREE(&cc); + if (c != NULL) + return (c); + } + + return (cmd_choose_client(&clients)); +} + +/* Choose the most recently used client from a list. */ +struct client * +cmd_choose_client(struct clients *cc) +{ + struct client *c, *cbest; + struct timeval *tv = NULL; + u_int i; + + cbest = NULL; + for (i = 0; i < ARRAY_LENGTH(cc); i++) { + if ((c = ARRAY_ITEM(cc, i)) == NULL) + continue; + if (c->session == NULL) + continue; + + if (tv == NULL || timercmp(&c->activity_time, tv, >)) { + cbest = c; + tv = &c->activity_time; + } + } + + return (cbest); +} + +/* Find the target client or report an error and return NULL. */ +struct client * +cmd_find_client(struct cmd_ctx *ctx, const char *arg) +{ + struct client *c; + char *tmparg; + size_t arglen; + + /* A NULL argument means the current client. */ + if (arg == NULL) + return (cmd_current_client(ctx)); + tmparg = xstrdup(arg); + + /* Trim a single trailing colon if any. */ + arglen = strlen(tmparg); + if (arglen != 0 && tmparg[arglen - 1] == ':') + tmparg[arglen - 1] = '\0'; + + /* Find the client, if any. */ + c = cmd_lookup_client(tmparg); + + /* If no client found, report an error. */ + if (c == NULL) + ctx->error(ctx, "client not found: %s", tmparg); + + xfree(tmparg); + return (c); +} + +/* + * Lookup a client by device path. Either of a full match and a match without a + * leading _PATH_DEV ("/dev/") is accepted. + */ +struct client * +cmd_lookup_client(const char *name) +{ + struct client *c; + const char *path; + u_int i; + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session == NULL) + continue; + path = c->tty.path; + + /* Check for exact matches. */ + if (strcmp(name, path) == 0) + return (c); + + /* Check without leading /dev if present. */ + if (strncmp(path, _PATH_DEV, (sizeof _PATH_DEV) - 1) != 0) + continue; + if (strcmp(name, path + (sizeof _PATH_DEV) - 1) == 0) + return (c); + } + + return (NULL); +} + +/* Lookup a session by name. If no session is found, NULL is returned. */ +struct session * +cmd_lookup_session(const char *name, int *ambiguous) +{ + struct session *s, *sfound; + + *ambiguous = 0; + + /* + * Look for matches. First look for exact matches - session names must + * be unique so an exact match can't be ambigious and can just be + * returned. + */ + if ((s = session_find(name)) != NULL) + return (s); + + /* + * Otherwise look for partial matches, returning early if it is found to + * be ambiguous. + */ + sfound = NULL; + RB_FOREACH(s, sessions, &sessions) { + if (strncmp(name, s->name, strlen(name)) == 0 || + fnmatch(name, s->name, 0) == 0) { + if (sfound != NULL) { + *ambiguous = 1; + return (NULL); + } + sfound = s; + } + } + return (sfound); +} + +/* + * Lookup a window or return -1 if not found or ambigious. First try as an + * index and if invalid, use fnmatch or leading prefix. Return NULL but fill in + * idx if the window index is a valid number but there is no window with that + * index. + */ +struct winlink * +cmd_lookup_window(struct session *s, const char *name, int *ambiguous) +{ + struct winlink *wl, *wlfound; + const char *errstr; + u_int idx; + + *ambiguous = 0; + + /* First see if this is a valid window index in this session. */ + idx = strtonum(name, 0, INT_MAX, &errstr); + if (errstr == NULL) { + if ((wl = winlink_find_by_index(&s->windows, idx)) != NULL) + return (wl); + } + + /* Look for exact matches, error if more than one. */ + wlfound = NULL; + RB_FOREACH(wl, winlinks, &s->windows) { + if (strcmp(name, wl->window->name) == 0) { + if (wlfound != NULL) { + *ambiguous = 1; + return (NULL); + } + wlfound = wl; + } + } + if (wlfound != NULL) + return (wlfound); + + /* Now look for pattern matches, again error if multiple. */ + wlfound = NULL; + RB_FOREACH(wl, winlinks, &s->windows) { + if (strncmp(name, wl->window->name, strlen(name)) == 0 || + fnmatch(name, wl->window->name, 0) == 0) { + if (wlfound != NULL) { + *ambiguous = 1; + return (NULL); + } + wlfound = wl; + } + } + if (wlfound != NULL) + return (wlfound); + + return (NULL); +} + +/* + * Find a window index - if the window doesn't exist, check if it is a + * potential index and return it anyway. + */ +int +cmd_lookup_index(struct session *s, const char *name, int *ambiguous) +{ + struct winlink *wl; + const char *errstr; + u_int idx; + + if ((wl = cmd_lookup_window(s, name, ambiguous)) != NULL) + return (wl->idx); + if (*ambiguous) + return (-1); + + idx = strtonum(name, 0, INT_MAX, &errstr); + if (errstr == NULL) + return (idx); + + return (-1); +} + +/* + * Lookup pane id. An initial % means a pane id. sp must already point to the + * current session. + */ +struct window_pane * +cmd_lookup_paneid(const char *arg) +{ + const char *errstr; + u_int paneid; + + if (*arg != '%') + return (NULL); + + paneid = strtonum(arg + 1, 0, UINT_MAX, &errstr); + if (errstr != NULL) + return (NULL); + return (window_pane_find_by_id(paneid)); +} + +/* Find session and winlink for pane. */ +struct session * +cmd_pane_session(struct cmd_ctx *ctx, struct window_pane *wp, + struct winlink **wlp) +{ + struct session *s; + struct sessionslist ss; + struct winlink *wl; + + /* If this pane is in the current session, return that winlink. */ + s = cmd_current_session(ctx, 0); + if (s != NULL) { + wl = winlink_find_by_window(&s->windows, wp->window); + if (wl != NULL) { + if (wlp != NULL) + *wlp = wl; + return (s); + } + } + + /* Otherwise choose from all sessions with this pane. */ + ARRAY_INIT(&ss); + RB_FOREACH(s, sessions, &sessions) { + if (winlink_find_by_window(&s->windows, wp->window) != NULL) + ARRAY_ADD(&ss, s); + } + s = cmd_choose_session_list(&ss); + ARRAY_FREE(&ss); + if (wlp != NULL) + *wlp = winlink_find_by_window(&s->windows, wp->window); + return (s); +} + +/* Find the target session or report an error and return NULL. */ +struct session * +cmd_find_session(struct cmd_ctx *ctx, const char *arg, int prefer_unattached) +{ + struct session *s; + struct window_pane *wp; + struct client *c; + char *tmparg; + size_t arglen; + int ambiguous; + + /* A NULL argument means the current session. */ + if (arg == NULL) + return (cmd_current_session(ctx, prefer_unattached)); + + /* Lookup as pane id. */ + if ((wp = cmd_lookup_paneid(arg)) != NULL) + return (cmd_pane_session(ctx, wp, NULL)); + + /* Trim a single trailing colon if any. */ + tmparg = xstrdup(arg); + arglen = strlen(tmparg); + if (arglen != 0 && tmparg[arglen - 1] == ':') + tmparg[arglen - 1] = '\0'; + + /* An empty session name is the current session. */ + if (*tmparg == '\0') { + xfree(tmparg); + return (cmd_current_session(ctx, prefer_unattached)); + } + + /* Find the session, if any. */ + s = cmd_lookup_session(tmparg, &ambiguous); + + /* If it doesn't, try to match it as a client. */ + if (s == NULL && (c = cmd_lookup_client(tmparg)) != NULL) + s = c->session; + + /* If no session found, report an error. */ + if (s == NULL) { + if (ambiguous) + ctx->error(ctx, "more than one session: %s", tmparg); + else + ctx->error(ctx, "session not found: %s", tmparg); + } + + xfree(tmparg); + return (s); +} + +/* Find the target session and window or report an error and return NULL. */ +struct winlink * +cmd_find_window(struct cmd_ctx *ctx, const char *arg, struct session **sp) +{ + struct session *s; + struct winlink *wl; + struct window_pane *wp; + const char *winptr; + char *sessptr = NULL; + int ambiguous = 0; + + /* + * Find the current session. There must always be a current session, if + * it can't be found, report an error. + */ + if ((s = cmd_current_session(ctx, 0)) == NULL) { + ctx->error(ctx, "can't establish current session"); + return (NULL); + } + + /* A NULL argument means the current session and window. */ + if (arg == NULL) { + if (sp != NULL) + *sp = s; + return (s->curw); + } + + /* Lookup as pane id. */ + if ((wp = cmd_lookup_paneid(arg)) != NULL) { + s = cmd_pane_session(ctx, wp, &wl); + if (sp != NULL) + *sp = s; + return (wl); + } + + /* Time to look at the argument. If it is empty, that is an error. */ + if (*arg == '\0') + goto not_found; + + /* Find the separating colon and split into window and session. */ + winptr = strchr(arg, ':'); + if (winptr == NULL) + goto no_colon; + winptr++; /* skip : */ + sessptr = xstrdup(arg); + *strchr(sessptr, ':') = '\0'; + + /* Try to lookup the session if present. */ + if (*sessptr != '\0') { + if ((s = cmd_lookup_session(sessptr, &ambiguous)) == NULL) + goto no_session; + } + if (sp != NULL) + *sp = s; + + /* + * Then work out the window. An empty string is the current window, + * otherwise try special cases then to look it up in the session. + */ + if (*winptr == '\0') + wl = s->curw; + else if (winptr[0] == '!' && winptr[1] == '\0') + wl = TAILQ_FIRST(&s->lastw); + else if (winptr[0] == '+' || winptr[0] == '-') + wl = cmd_find_window_offset(winptr, s, &ambiguous); + else + wl = cmd_lookup_window(s, winptr, &ambiguous); + if (wl == NULL) + goto not_found; + + if (sessptr != NULL) + xfree(sessptr); + return (wl); + +no_colon: + /* + * No colon in the string, first try special cases, then as a window + * and lastly as a session. + */ + if (arg[0] == '!' && arg[1] == '\0') { + if ((wl = TAILQ_FIRST(&s->lastw)) == NULL) + goto not_found; + } else if (arg[0] == '+' || arg[0] == '-') { + if ((wl = cmd_find_window_offset(arg, s, &ambiguous)) == NULL) + goto lookup_session; + } else if ((wl = cmd_lookup_window(s, arg, &ambiguous)) == NULL) + goto lookup_session; + + if (sp != NULL) + *sp = s; + + return (wl); + +lookup_session: + if (ambiguous) + goto not_found; + if (*arg != '\0' && (s = cmd_lookup_session(arg, &ambiguous)) == NULL) + goto no_session; + + if (sp != NULL) + *sp = s; + + return (s->curw); + +no_session: + if (ambiguous) + ctx->error(ctx, "multiple sessions: %s", arg); + else + ctx->error(ctx, "session not found: %s", arg); + if (sessptr != NULL) + xfree(sessptr); + return (NULL); + +not_found: + if (ambiguous) + ctx->error(ctx, "multiple windows: %s", arg); + else + ctx->error(ctx, "window not found: %s", arg); + if (sessptr != NULL) + xfree(sessptr); + return (NULL); +} + +struct winlink * +cmd_find_window_offset(const char *winptr, struct session *s, int *ambiguous) +{ + struct winlink *wl; + int offset = 1; + + if (winptr[1] != '\0') + offset = strtonum(winptr + 1, 1, INT_MAX, NULL); + if (offset == 0) + wl = cmd_lookup_window(s, winptr, ambiguous); + else { + if (winptr[0] == '+') + wl = winlink_next_by_number(s->curw, s, offset); + else + wl = winlink_previous_by_number(s->curw, s, offset); + } + + return (wl); +} + +/* + * Find the target session and window index, whether or not it exists in the + * session. Return -2 on error or -1 if no window index is specified. This is + * used when parsing an argument for a window target that may not exist (for + * example if it is going to be created). + */ +int +cmd_find_index(struct cmd_ctx *ctx, const char *arg, struct session **sp) +{ + struct session *s; + struct winlink *wl; + const char *winptr; + char *sessptr = NULL; + int idx, ambiguous = 0; + + /* + * Find the current session. There must always be a current session, if + * it can't be found, report an error. + */ + if ((s = cmd_current_session(ctx, 0)) == NULL) { + ctx->error(ctx, "can't establish current session"); + return (-2); + } + + /* A NULL argument means the current session and "no window" (-1). */ + if (arg == NULL) { + if (sp != NULL) + *sp = s; + return (-1); + } + + /* Time to look at the argument. If it is empty, that is an error. */ + if (*arg == '\0') + goto not_found; + + /* Find the separating colon. If none, assume the current session. */ + winptr = strchr(arg, ':'); + if (winptr == NULL) + goto no_colon; + winptr++; /* skip : */ + sessptr = xstrdup(arg); + *strchr(sessptr, ':') = '\0'; + + /* Try to lookup the session if present. */ + if (sessptr != NULL && *sessptr != '\0') { + if ((s = cmd_lookup_session(sessptr, &ambiguous)) == NULL) + goto no_session; + } + if (sp != NULL) + *sp = s; + + /* + * Then work out the window. An empty string is a new window otherwise + * try to look it up in the session. + */ + if (*winptr == '\0') + idx = -1; + else if (winptr[0] == '!' && winptr[1] == '\0') { + if ((wl = TAILQ_FIRST(&s->lastw)) == NULL) + goto not_found; + idx = wl->idx; + } else if (winptr[0] == '+' || winptr[0] == '-') { + if ((idx = cmd_find_index_offset(winptr, s, &ambiguous)) < 0) + goto invalid_index; + } else if ((idx = cmd_lookup_index(s, winptr, &ambiguous)) == -1) + goto invalid_index; + + if (sessptr != NULL) + xfree(sessptr); + return (idx); + +no_colon: + /* + * No colon in the string, first try special cases, then as a window + * and lastly as a session. + */ + if (arg[0] == '!' && arg[1] == '\0') { + if ((wl = TAILQ_FIRST(&s->lastw)) == NULL) + goto not_found; + idx = wl->idx; + } else if (arg[0] == '+' || arg[0] == '-') { + if ((idx = cmd_find_index_offset(arg, s, &ambiguous)) < 0) + goto lookup_session; + } else if ((idx = cmd_lookup_index(s, arg, &ambiguous)) == -1) + goto lookup_session; + + if (sp != NULL) + *sp = s; + + return (idx); + +lookup_session: + if (ambiguous) + goto not_found; + if (*arg != '\0' && (s = cmd_lookup_session(arg, &ambiguous)) == NULL) + goto no_session; + + if (sp != NULL) + *sp = s; + + return (-1); + +no_session: + if (ambiguous) + ctx->error(ctx, "multiple sessions: %s", arg); + else + ctx->error(ctx, "session not found: %s", arg); + if (sessptr != NULL) + xfree(sessptr); + return (-2); + +invalid_index: + if (ambiguous) + goto not_found; + ctx->error(ctx, "invalid index: %s", arg); + + if (sessptr != NULL) + xfree(sessptr); + return (-2); + +not_found: + if (ambiguous) + ctx->error(ctx, "multiple windows: %s", arg); + else + ctx->error(ctx, "window not found: %s", arg); + if (sessptr != NULL) + xfree(sessptr); + return (-2); +} + +int +cmd_find_index_offset(const char *winptr, struct session *s, int *ambiguous) +{ + int idx, offset = 1; + + if (winptr[1] != '\0') + offset = strtonum(winptr + 1, 1, INT_MAX, NULL); + if (offset == 0) + idx = cmd_lookup_index(s, winptr, ambiguous); + else { + if (winptr[0] == '+') { + if (s->curw->idx == INT_MAX) + idx = cmd_lookup_index(s, winptr, ambiguous); + else + idx = s->curw->idx + offset; + } else { + if (s->curw->idx == 0) + idx = cmd_lookup_index(s, winptr, ambiguous); + else + idx = s->curw->idx - offset; + } + } + + return (idx); +} + +/* + * Find the target session, window and pane number or report an error and + * return NULL. The pane number is separated from the session:window by a ., + * such as mysession:mywindow.0. + */ +struct winlink * +cmd_find_pane(struct cmd_ctx *ctx, + const char *arg, struct session **sp, struct window_pane **wpp) +{ + struct session *s; + struct winlink *wl; + const char *period, *errstr; + char *winptr, *paneptr; + u_int idx; + + /* Get the current session. */ + if ((s = cmd_current_session(ctx, 0)) == NULL) { + ctx->error(ctx, "can't establish current session"); + return (NULL); + } + if (sp != NULL) + *sp = s; + + /* A NULL argument means the current session, window and pane. */ + if (arg == NULL) { + *wpp = s->curw->window->active; + return (s->curw); + } + + /* Lookup as pane id. */ + if ((*wpp = cmd_lookup_paneid(arg)) != NULL) { + s = cmd_pane_session(ctx, *wpp, &wl); + if (sp != NULL) + *sp = s; + return (wl); + } + + /* Look for a separating period. */ + if ((period = strrchr(arg, '.')) == NULL) + goto no_period; + + /* Pull out the window part and parse it. */ + winptr = xstrdup(arg); + winptr[period - arg] = '\0'; + if (*winptr == '\0') + wl = s->curw; + else if ((wl = cmd_find_window(ctx, winptr, sp)) == NULL) + goto error; + + /* Find the pane section and look it up. */ + paneptr = winptr + (period - arg) + 1; + if (*paneptr == '\0') + *wpp = wl->window->active; + else if (paneptr[0] == '+' || paneptr[0] == '-') + *wpp = cmd_find_pane_offset(paneptr, wl); + else { + idx = strtonum(paneptr, 0, INT_MAX, &errstr); + if (errstr != NULL) + goto lookup_string; + *wpp = window_pane_at_index(wl->window, idx); + if (*wpp == NULL) + goto lookup_string; + } + + xfree(winptr); + return (wl); + +lookup_string: + /* Try pane string description. */ + if ((*wpp = window_find_string(wl->window, paneptr)) == NULL) { + ctx->error(ctx, "can't find pane: %s", paneptr); + goto error; + } + + xfree(winptr); + return (wl); + +no_period: + /* Try as a pane number alone. */ + idx = strtonum(arg, 0, INT_MAX, &errstr); + if (errstr != NULL) + goto lookup_window; + + /* Try index in the current session and window. */ + if ((*wpp = window_pane_at_index(s->curw->window, idx)) == NULL) + goto lookup_window; + + return (s->curw); + +lookup_window: + /* Try pane string description. */ + if ((*wpp = window_find_string(s->curw->window, arg)) != NULL) + return (s->curw); + + /* Try as a window and use the active pane. */ + if ((wl = cmd_find_window(ctx, arg, sp)) != NULL) + *wpp = wl->window->active; + return (wl); + +error: + xfree(winptr); + return (NULL); +} + +struct window_pane * +cmd_find_pane_offset(const char *paneptr, struct winlink *wl) +{ + struct window *w = wl->window; + struct window_pane *wp = w->active; + u_int offset = 1; + + if (paneptr[1] != '\0') + offset = strtonum(paneptr + 1, 1, INT_MAX, NULL); + if (offset > 0) { + if (paneptr[0] == '+') + wp = window_pane_next_by_number(w, wp, offset); + else + wp = window_pane_previous_by_number(w, wp, offset); + } + + return (wp); +} + +/* Replace the first %% or %idx in template by s. */ +char * +cmd_template_replace(char *template, const char *s, int idx) +{ + char ch; + char *buf, *ptr; + int replaced; + size_t len; + + if (strstr(template, "%") == NULL) + return (xstrdup(template)); + + buf = xmalloc(1); + *buf = '\0'; + len = 0; + replaced = 0; + + ptr = template; + while (*ptr != '\0') { + switch (ch = *ptr++) { + case '%': + if (*ptr < '1' || *ptr > '9' || *ptr - '0' != idx) { + if (*ptr != '%' || replaced) + break; + replaced = 1; + } + ptr++; + + len += strlen(s); + buf = xrealloc(buf, 1, len + 1); + strlcat(buf, s, len + 1); + continue; + } + buf = xrealloc(buf, 1, len + 2); + buf[len++] = ch; + buf[len] = '\0'; + } + + return (buf); +} diff --git a/external/bsd/tmux/dist/colour.c b/external/bsd/tmux/dist/colour.c new file mode 100644 index 000000000..3bb832d08 --- /dev/null +++ b/external/bsd/tmux/dist/colour.c @@ -0,0 +1,276 @@ +/* $Id: colour.c,v 1.3 2011/08/22 09:19:51 he Exp $ */ + +/* + * Copyright (c) 2008 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include + +#include "tmux.h" + +/* + * Colour to string conversion functions. Bit 8 of the colour means it is one + * of the 256 colour palette. + */ + +/* An RGB colour. */ +struct colour_rgb { + u_char r; + u_char g; + u_char b; +}; + +/* 256 colour RGB table, generated on first use. */ +struct colour_rgb *colour_rgb_256; + +void colour_rgb_generate256(void); +u_int colour_rgb_distance(struct colour_rgb *, struct colour_rgb *); +int colour_rgb_find(struct colour_rgb *); + +/* Generate 256 colour RGB table. */ +void +colour_rgb_generate256(void) +{ + struct colour_rgb *rgb; + u_int i, r, g, b; + + /* + * Allocate the table. The first 16 colours are often changed by users + * and terminals so don't include them. + */ + colour_rgb_256 = xcalloc(240, sizeof *colour_rgb_256); + + /* Add the colours first. */ + r = g = b = 0; + for (i = 240; i > 24; i--) { + rgb = &colour_rgb_256[240 - i]; + + if (r != 0) + rgb->r = (r * 40) + 55; + if (g != 0) + rgb->g = (g * 40) + 55; + if (b != 0) + rgb->b = (b * 40) + 55; + + b++; + if (b > 5) { + b = 0; + g++; + } + if (g > 5) { + g = 0; + r++; + } + } + + /* Then add the greys. */ + for (i = 24; i > 0; i--) { + rgb = &colour_rgb_256[240 - i]; + + rgb->r = 8 + (24 - i) * 10; + rgb->g = 8 + (24 - i) * 10; + rgb->b = 8 + (24 - i) * 10; + } +} + +/* Get a measure of colour RGB distance. */ +u_int +colour_rgb_distance(struct colour_rgb *rgb1, struct colour_rgb *rgb2) +{ + int r, g, b; + + r = rgb1->r - rgb2->r; + g = rgb1->g - rgb2->g; + b = rgb1->b - rgb2->b; + return (r * r + g * g + b * b); +} + +/* Work out the nearest colour from the 256 colour set. */ +int +colour_rgb_find(struct colour_rgb *rgb) +{ + u_int distance, lowest; + u_int colour, i; + + if (colour_rgb_256 == NULL) + colour_rgb_generate256(); + + colour = 16; + lowest = UINT_MAX; + for (i = 0; i < 240; i++) { + distance = colour_rgb_distance(&colour_rgb_256[i], rgb); + if (distance < lowest) { + lowest = distance; + colour = 16 + i; + } + } + return (colour); +} + +/* Set grid cell foreground colour. */ +void +colour_set_fg(struct grid_cell *gc, int c) +{ + if (c & 0x100) + gc->flags |= GRID_FLAG_FG256; + gc->fg = c; +} + +/* Set grid cell background colour. */ +void +colour_set_bg(struct grid_cell *gc, int c) +{ + if (c & 0x100) + gc->flags |= GRID_FLAG_BG256; + gc->bg = c; +} + +/* Convert colour to a string. */ +const char * +colour_tostring(int c) +{ + static char s[32]; + + if (c & 0x100) { + xsnprintf(s, sizeof s, "colour%u", c & ~0x100); + return (s); + } + + switch (c) { + case 0: + return ("black"); + case 1: + return ("red"); + case 2: + return ("green"); + case 3: + return ("yellow"); + case 4: + return ("blue"); + case 5: + return ("magenta"); + case 6: + return ("cyan"); + case 7: + return ("white"); + case 8: + return ("default"); + } + return (NULL); +} + +/* Convert colour from string. */ +int +colour_fromstring(const char *s) +{ + const char *errstr; + const char *cp; + struct colour_rgb rgb; + int n; + + if (*s == '#' && strlen(s) == 7) { + for (cp = s + 1; isxdigit((u_char) *cp); cp++) + ; + if (*cp != '\0') + return (-1); + n = sscanf(s + 1, "%2hhx%2hhx%2hhx", &rgb.r, &rgb.g, &rgb.b); + if (n != 3) + return (-1); + return (colour_rgb_find(&rgb) | 0x100); + } + + if (strncasecmp(s, "colour", (sizeof "colour") - 1) == 0) { + n = strtonum(s + (sizeof "colour") - 1, 0, 255, &errstr); + if (errstr != NULL) + return (-1); + return (n | 0x100); + } + + if (strcasecmp(s, "black") == 0 || (s[0] == '0' && s[1] == '\0')) + return (0); + if (strcasecmp(s, "red") == 0 || (s[0] == '1' && s[1] == '\0')) + return (1); + if (strcasecmp(s, "green") == 0 || (s[0] == '2' && s[1] == '\0')) + return (2); + if (strcasecmp(s, "yellow") == 0 || (s[0] == '3' && s[1] == '\0')) + return (3); + if (strcasecmp(s, "blue") == 0 || (s[0] == '4' && s[1] == '\0')) + return (4); + if (strcasecmp(s, "magenta") == 0 || (s[0] == '5' && s[1] == '\0')) + return (5); + if (strcasecmp(s, "cyan") == 0 || (s[0] == '6' && s[1] == '\0')) + return (6); + if (strcasecmp(s, "white") == 0 || (s[0] == '7' && s[1] == '\0')) + return (7); + if (strcasecmp(s, "default") == 0 || (s[0] == '8' && s[1] == '\0')) + return (8); + return (-1); +} + +/* Convert 256 colour palette to 16. */ +u_char +colour_256to16(u_char c) +{ + static const u_char table[256] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 0, 4, 4, 4, 12, 12, 2, 6, 4, 4, 12, 12, 2, 2, 6, 4, + 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, + 10, 10, 10, 14, 1, 5, 4, 4, 12, 12, 3, 8, 4, 4, 12, 12, + 2, 2, 6, 4, 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, + 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 5, 4, 12, 12, 1, 1, + 5, 4, 12, 12, 3, 3, 8, 4, 12, 12, 2, 2, 2, 6, 12, 12, + 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 1, 5, + 12, 12, 1, 1, 1, 5, 12, 12, 1, 1, 1, 5, 12, 12, 3, 3, + 3, 7, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14, + 9, 9, 9, 9, 13, 12, 9, 9, 9, 9, 13, 12, 9, 9, 9, 9, + 13, 12, 9, 9, 9, 9, 13, 12, 11, 11, 11, 11, 7, 12, 10, 10, + 10, 10, 10, 14, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13, + 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, + 9, 13, 11, 11, 11, 11, 11, 15, 0, 0, 0, 0, 0, 0, 8, 8, + 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15 + }; + + return (table[c]); +} + +/* Convert 256 colour palette to 88. */ +u_char +colour_256to88(u_char c) +{ + static const u_char table[256] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 17, 18, 18, 19, 20, 21, 21, 22, 22, 23, 20, 21, 21, 22, + 22, 23, 24, 25, 25, 26, 26, 27, 24, 25, 25, 26, 26, 27, 28, 29, + 29, 30, 30, 31, 32, 33, 33, 34, 34, 35, 36, 37, 37, 38, 38, 39, + 36, 37, 37, 38, 38, 39, 40, 41, 41, 42, 42, 43, 40, 41, 41, 42, + 42, 43, 44, 45, 45, 46, 46, 47, 32, 33, 33, 34, 34, 35, 36, 37, + 37, 38, 38, 39, 36, 37, 37, 38, 38, 39, 40, 41, 41, 42, 42, 43, + 40, 41, 41, 42, 42, 43, 44, 45, 45, 46, 46, 47, 48, 49, 49, 50, + 50, 51, 52, 53, 53, 54, 54, 55, 52, 53, 53, 54, 54, 55, 56, 57, + 57, 58, 58, 59, 56, 57, 57, 58, 58, 59, 60, 61, 61, 62, 62, 63, + 48, 49, 49, 50, 50, 51, 52, 53, 53, 54, 54, 55, 52, 53, 53, 54, + 54, 55, 56, 57, 57, 58, 58, 59, 56, 57, 57, 58, 58, 59, 60, 61, + 61, 62, 62, 63, 64, 65, 65, 66, 66, 67, 68, 69, 69, 70, 70, 71, + 68, 69, 69, 70, 70, 71, 72, 73, 73, 74, 74, 75, 72, 73, 73, 74, + 74, 75, 76, 77, 77, 78, 78, 79, 0, 0, 80, 80, 80, 81, 81, 81, + 82, 82, 82, 83, 83, 83, 84, 84, 84, 85, 85, 85, 86, 86, 86, 87 + }; + + return (table[c]); +} diff --git a/external/bsd/tmux/dist/compat.h b/external/bsd/tmux/dist/compat.h new file mode 100644 index 000000000..ac5f513fe --- /dev/null +++ b/external/bsd/tmux/dist/compat.h @@ -0,0 +1,240 @@ +/* $Id: compat.h,v 1.1.1.2 2011/08/17 18:40:05 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * 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 +#else +#include "compat/queue.h" +#endif + +#ifdef HAVE_TREE_H +#include +#else +#include "compat/tree.h" +#endif + +#ifdef HAVE_BITSTRING_H +#include +#else +#include "compat/bitstring.h" +#endif + +#ifdef HAVE_PATHS_H +#include +#endif + +#ifdef HAVE_FORKPTY +#ifdef HAVE_LIBUTIL_H +#include +#endif +#ifdef HAVE_PTY_H +#include +#endif +#ifdef HAVE_UTIL_H +#include +#endif +#endif + +#ifdef HAVE_VIS +#include +#else +#include "compat/vis.h" +#endif + +#ifdef HAVE_IMSG +#include +#else +#include "compat/imsg.h" +#endif + +#ifdef HAVE_STDINT_H +#include +#else +#include +#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 +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 +#else +/* getopt.c */ +extern int BSDopterr; +extern int BSDoptind; +extern int BSDoptopt; +extern int BSDoptreset; +extern char *BSDoptarg; +int BSDgetopt(int, char *const *, const char *); +#define getopt(ac, av, o) BSDgetopt(ac, av, o) +#define opterr BSDopterr +#define optind BSDoptind +#define optopt BSDoptopt +#define optreset BSDoptreset +#define optarg BSDoptarg +#endif + +#endif /* COMPAT_H */ diff --git a/external/bsd/tmux/dist/compat/imsg-buffer.c b/external/bsd/tmux/dist/compat/imsg-buffer.c new file mode 100644 index 000000000..08c4d1dcf --- /dev/null +++ b/external/bsd/tmux/dist/compat/imsg-buffer.c @@ -0,0 +1,303 @@ +/* $Id: imsg-buffer.c,v 1.5 2012/01/20 14:08:04 joerg Exp $ */ +/* $OpenBSD: imsg-buffer.c,v 1.3 2010/05/26 13:56:07 nicm Exp $ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. 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 +#include +#include + +#include +#include +#include +#include + +#include "tmux.h" + +int ibuf_realloc(struct ibuf *, size_t); +void ibuf_enqueue(struct msgbuf *, struct ibuf *); +void ibuf_dequeue(struct msgbuf *, struct ibuf *); + +struct ibuf * +ibuf_open(size_t len) +{ + struct ibuf *buf; + + if ((buf = calloc(1, sizeof(struct ibuf))) == NULL) + return (NULL); + if ((buf->buf = malloc(len)) == NULL) { + free(buf); + return (NULL); + } + buf->size = buf->max = len; + buf->fd = -1; + + return (buf); +} + +struct ibuf * +ibuf_dynamic(size_t len, size_t max) +{ + struct ibuf *buf; + + if (max < len) + return (NULL); + + if ((buf = ibuf_open(len)) == NULL) + return (NULL); + + if (max > 0) + buf->max = max; + + return (buf); +} + +int +ibuf_realloc(struct ibuf *buf, size_t len) +{ + u_char *b; + + /* on static buffers max is eq size and so the following fails */ + if (buf->wpos + len > buf->max) { + errno = ENOMEM; + return (-1); + } + + b = realloc(buf->buf, buf->wpos + len); + if (b == NULL) + return (-1); + buf->buf = b; + buf->size = buf->wpos + len; + + return (0); +} + +int +ibuf_add(struct ibuf *buf, const void *data, size_t len) +{ + if (buf->wpos + len > buf->size) + if (ibuf_realloc(buf, len) == -1) + return (-1); + + memcpy(buf->buf + buf->wpos, data, len); + buf->wpos += len; + return (0); +} + +void * +ibuf_reserve(struct ibuf *buf, size_t len) +{ + void *b; + + if (buf->wpos + len > buf->size) + if (ibuf_realloc(buf, len) == -1) + return (NULL); + + b = buf->buf + buf->wpos; + buf->wpos += len; + return (b); +} + +void * +ibuf_seek(struct ibuf *buf, size_t pos, size_t len) +{ + /* only allowed to seek in already written parts */ + if (pos + len > buf->wpos) + return (NULL); + + return (buf->buf + pos); +} + +size_t +ibuf_size(struct ibuf *buf) +{ + return (buf->wpos); +} + +size_t +ibuf_left(struct ibuf *buf) +{ + return (buf->max - buf->wpos); +} + +void +ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf) +{ + ibuf_enqueue(msgbuf, buf); +} + +int +ibuf_write(struct msgbuf *msgbuf) +{ + struct iovec iov[IOV_MAX]; + struct ibuf *buf; + unsigned int i = 0; + ssize_t n; + + bzero(&iov, sizeof(iov)); + TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { + if (i >= IOV_MAX) + break; + iov[i].iov_base = buf->buf + buf->rpos; + iov[i].iov_len = buf->wpos - buf->rpos; + i++; + } + + if ((n = writev(msgbuf->fd, iov, i)) == -1) { + if (errno == EAGAIN || errno == ENOBUFS || + errno == EINTR) /* try later */ + return (0); + else + return (-1); + } + + if (n == 0) { /* connection closed */ + errno = 0; + return (-2); + } + + msgbuf_drain(msgbuf, n); + + return (0); +} + +void +ibuf_free(struct ibuf *buf) +{ + free(buf->buf); + free(buf); +} + +void +msgbuf_init(struct msgbuf *msgbuf) +{ + msgbuf->queued = 0; + msgbuf->fd = -1; + TAILQ_INIT(&msgbuf->bufs); +} + +void +msgbuf_drain(struct msgbuf *msgbuf, size_t n) +{ + struct ibuf *buf, *next; + + for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0; + buf = next) { + next = TAILQ_NEXT(buf, entry); + if (buf->rpos + n >= buf->wpos) { + n -= buf->wpos - buf->rpos; + ibuf_dequeue(msgbuf, buf); + } else { + buf->rpos += n; + n = 0; + } + } +} + +void +msgbuf_clear(struct msgbuf *msgbuf) +{ + struct ibuf *buf; + + while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL) + ibuf_dequeue(msgbuf, buf); +} + +int +msgbuf_write(struct msgbuf *msgbuf) +{ + struct iovec iov[IOV_MAX]; + struct ibuf *buf; + unsigned int i = 0; + ssize_t n; + struct msghdr msg; + struct cmsghdr *cmsg; + union { + struct cmsghdr hdr; + char buf[CMSG_SPACE(sizeof(int))]; + } cmsgbuf; + + bzero(&iov, sizeof(iov)); + bzero(&msg, sizeof(msg)); + TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { + if (i >= IOV_MAX) + break; + iov[i].iov_base = buf->buf + buf->rpos; + iov[i].iov_len = buf->wpos - buf->rpos; + i++; + if (buf->fd != -1) + break; + } + + msg.msg_iov = iov; + msg.msg_iovlen = i; + + if (buf != NULL && buf->fd != -1) { + msg.msg_control = (caddr_t)&cmsgbuf.buf; + msg.msg_controllen = CMSG_SPACE(sizeof(int)); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + *(int *)CMSG_DATA(cmsg) = buf->fd; + } + + if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) { + if (errno == EAGAIN || errno == ENOBUFS || + errno == EINTR) /* try later */ + return (0); + else + return (-1); + } + + if (n == 0) { /* connection closed */ + errno = 0; + return (-2); + } + + /* + * assumption: fd got sent if sendmsg sent anything + * this works because fds are passed one at a time + */ + if (buf != NULL && buf->fd != -1) { + close(buf->fd); + buf->fd = -1; + } + + msgbuf_drain(msgbuf, n); + + return (0); +} + +void +ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf) +{ + TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry); + msgbuf->queued++; +} + +void +ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf) +{ + TAILQ_REMOVE(&msgbuf->bufs, buf, entry); + + if (buf->fd != -1) + close(buf->fd); + + msgbuf->queued--; + ibuf_free(buf); +} diff --git a/external/bsd/tmux/dist/compat/imsg.c b/external/bsd/tmux/dist/compat/imsg.c new file mode 100644 index 000000000..4cf36f3f1 --- /dev/null +++ b/external/bsd/tmux/dist/compat/imsg.c @@ -0,0 +1,271 @@ +/* $Id: imsg.c,v 1.5 2012/01/20 14:08:04 joerg Exp $ */ +/* $OpenBSD: imsg.c,v 1.3 2010/05/26 13:56:07 nicm Exp $ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. 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 +#include +#include + +#include +#include +#include +#include + +#include "tmux.h" + +int imsg_get_fd(struct imsgbuf *); + +void +imsg_init(struct imsgbuf *ibuf, int fd) +{ + msgbuf_init(&ibuf->w); + bzero(&ibuf->r, sizeof(ibuf->r)); + ibuf->fd = fd; + ibuf->w.fd = fd; + ibuf->pid = getpid(); + TAILQ_INIT(&ibuf->fds); +} + +ssize_t +imsg_read(struct imsgbuf *ibuf) +{ + struct msghdr msg; + struct cmsghdr *cmsg; + union { + struct cmsghdr hdr; + char buf[CMSG_SPACE(sizeof(int) * 16)]; + } cmsgbuf; + struct iovec iov; + ssize_t n; + int fd; + struct imsg_fd *ifd; + + bzero(&msg, sizeof(msg)); + + iov.iov_base = ibuf->r.buf + ibuf->r.wpos; + iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = &cmsgbuf.buf; + msg.msg_controllen = CMSG_SPACE(sizeof(int) * 16); + + if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) { + if (errno != EINTR && errno != EAGAIN) { + return (-1); + } + return (-2); + } + + ibuf->r.wpos += n; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS) { + fd = (*(int *)CMSG_DATA(cmsg)); + if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL) { + close(fd); + return (-1); + } + ifd->fd = fd; + TAILQ_INSERT_TAIL(&ibuf->fds, ifd, entry); + } + /* we do not handle other ctl data level */ + } + + return (n); +} + +ssize_t +imsg_get(struct imsgbuf *ibuf, struct imsg *imsg) +{ + size_t av, left, datalen; + + av = ibuf->r.wpos; + + if (IMSG_HEADER_SIZE > av) + return (0); + + memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr)); + if (imsg->hdr.len < IMSG_HEADER_SIZE || + imsg->hdr.len > MAX_IMSGSIZE) { + errno = ERANGE; + return (-1); + } + if (imsg->hdr.len > av) + return (0); + datalen = imsg->hdr.len - IMSG_HEADER_SIZE; + ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE; + if ((imsg->data = malloc(datalen)) == NULL && datalen != 0) + return (-1); + + if (imsg->hdr.flags & IMSGF_HASFD) + imsg->fd = imsg_get_fd(ibuf); + else + imsg->fd = -1; + + memcpy(imsg->data, ibuf->r.rptr, datalen); + + if (imsg->hdr.len < av) { + left = av - imsg->hdr.len; + memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left); + ibuf->r.wpos = left; + } else + ibuf->r.wpos = 0; + + return (datalen + IMSG_HEADER_SIZE); +} + +int +imsg_compose(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid, + pid_t pid, int fd, void *data, u_int16_t datalen) +{ + struct ibuf *wbuf; + + if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL) + return (-1); + + if (imsg_add(wbuf, data, datalen) == -1) + return (-1); + + wbuf->fd = fd; + + imsg_close(ibuf, wbuf); + + return (1); +} + +int +imsg_composev(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid, + pid_t pid, int fd, const struct iovec *iov, int iovcnt) +{ + struct ibuf *wbuf; + int i, datalen = 0; + + for (i = 0; i < iovcnt; i++) + datalen += iov[i].iov_len; + + if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL) + return (-1); + + for (i = 0; i < iovcnt; i++) + if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1) + return (-1); + + wbuf->fd = fd; + + imsg_close(ibuf, wbuf); + + return (1); +} + +/* ARGSUSED */ +struct ibuf * +imsg_create(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid, + pid_t pid, u_int16_t datalen) +{ + struct ibuf *wbuf; + struct imsg_hdr hdr; + + datalen += IMSG_HEADER_SIZE; + if (datalen > MAX_IMSGSIZE) { + errno = ERANGE; + return (NULL); + } + + hdr.type = type; + hdr.flags = 0; + hdr.peerid = peerid; + if ((hdr.pid = pid) == 0) + hdr.pid = ibuf->pid; + if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) { + return (NULL); + } + if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1) + return (NULL); + + return (wbuf); +} + +int +imsg_add(struct ibuf *msg, void *data, u_int16_t datalen) +{ + if (datalen) + if (ibuf_add(msg, data, datalen) == -1) { + ibuf_free(msg); + return (-1); + } + return (datalen); +} + +void +imsg_close(struct imsgbuf *ibuf, struct ibuf *msg) +{ + struct imsg_hdr *hdr; + + hdr = (struct imsg_hdr *)msg->buf; + + hdr->flags &= ~IMSGF_HASFD; + if (msg->fd != -1) + hdr->flags |= IMSGF_HASFD; + + hdr->len = (u_int16_t)msg->wpos; + + ibuf_close(&ibuf->w, msg); +} + +void +imsg_free(struct imsg *imsg) +{ + free(imsg->data); +} + +int +imsg_get_fd(struct imsgbuf *ibuf) +{ + int fd; + struct imsg_fd *ifd; + + if ((ifd = TAILQ_FIRST(&ibuf->fds)) == NULL) + return (-1); + + fd = ifd->fd; + TAILQ_REMOVE(&ibuf->fds, ifd, entry); + free(ifd); + + return (fd); +} + +int +imsg_flush(struct imsgbuf *ibuf) +{ + while (ibuf->w.queued) + if (msgbuf_write(&ibuf->w) < 0) + return (-1); + return (0); +} + +void +imsg_clear(struct imsgbuf *ibuf) +{ + int fd; + + msgbuf_clear(&ibuf->w); + while ((fd = imsg_get_fd(ibuf)) != -1) + close(fd); +} diff --git a/external/bsd/tmux/dist/compat/imsg.h b/external/bsd/tmux/dist/compat/imsg.h new file mode 100644 index 000000000..f5bb79e98 --- /dev/null +++ b/external/bsd/tmux/dist/compat/imsg.h @@ -0,0 +1,110 @@ +/* $Id: imsg.h,v 1.1.1.2 2011/08/17 18:40:06 jmmv Exp $ */ +/* $OpenBSD: imsg.h,v 1.4 2010/05/26 13:56:07 nicm Exp $ */ + +/* + * Copyright (c) 2006, 2007 Pierre-Yves Ritschard + * Copyright (c) 2006, 2007, 2008 Reyk Floeter + * Copyright (c) 2003, 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "tmux.h" + +#define IBUF_READ_SIZE 65535 +#define IMSG_HEADER_SIZE sizeof(struct imsg_hdr) +#define MAX_IMSGSIZE 16384 + +struct ibuf { + TAILQ_ENTRY(ibuf) entry; + u_char *buf; + size_t size; + size_t max; + size_t wpos; + size_t rpos; + int fd; +}; + +struct msgbuf { + TAILQ_HEAD(, ibuf) bufs; + u_int32_t queued; + int fd; +}; + +struct ibuf_read { + u_char buf[IBUF_READ_SIZE]; + u_char *rptr; + size_t wpos; +}; + +struct imsg_fd { + TAILQ_ENTRY(imsg_fd) entry; + int fd; +}; + +struct imsgbuf { + TAILQ_HEAD(, imsg_fd) fds; + struct ibuf_read r; + struct msgbuf w; + int fd; + pid_t pid; +}; + +#define IMSGF_HASFD 1 + +struct imsg_hdr { + u_int32_t type; + u_int16_t len; + u_int16_t flags; + u_int32_t peerid; + u_int32_t pid; +}; + +struct imsg { + struct imsg_hdr hdr; + int fd; + void *data; +}; + + +/* buffer.c */ +struct ibuf *ibuf_open(size_t); +struct ibuf *ibuf_dynamic(size_t, size_t); +int ibuf_add(struct ibuf *, const void *, size_t); +void *ibuf_reserve(struct ibuf *, size_t); +void *ibuf_seek(struct ibuf *, size_t, size_t); +size_t ibuf_size(struct ibuf *); +size_t ibuf_left(struct ibuf *); +void ibuf_close(struct msgbuf *, struct ibuf *); +int ibuf_write(struct msgbuf *); +void ibuf_free(struct ibuf *); +void msgbuf_init(struct msgbuf *); +void msgbuf_clear(struct msgbuf *); +int msgbuf_write(struct msgbuf *); +void msgbuf_drain(struct msgbuf *, size_t); + +/* imsg.c */ +void imsg_init(struct imsgbuf *, int); +ssize_t imsg_read(struct imsgbuf *); +ssize_t imsg_get(struct imsgbuf *, struct imsg *); +int imsg_compose(struct imsgbuf *, u_int32_t, u_int32_t, pid_t, + int, void *, u_int16_t); +int imsg_composev(struct imsgbuf *, u_int32_t, u_int32_t, pid_t, + int, const struct iovec *, int); +struct ibuf *imsg_create(struct imsgbuf *, u_int32_t, u_int32_t, pid_t, + u_int16_t); +int imsg_add(struct ibuf *, void *, u_int16_t); +void imsg_close(struct imsgbuf *, struct ibuf *); +void imsg_free(struct imsg *); +int imsg_flush(struct imsgbuf *); +void imsg_clear(struct imsgbuf *); diff --git a/external/bsd/tmux/dist/compat/strtonum.c b/external/bsd/tmux/dist/compat/strtonum.c new file mode 100644 index 000000000..c605d870d --- /dev/null +++ b/external/bsd/tmux/dist/compat/strtonum.c @@ -0,0 +1,68 @@ +/* $Id: strtonum.c,v 1.1.1.2 2011/08/17 18:40:06 jmmv Exp $ */ +/* $OpenBSD: strtonum.c,v 1.6 2004/08/03 19:38:01 millert Exp $ */ + +/* + * Copyright (c) 2004 Ted Unangst and Todd Miller + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include "tmux.h" + +#define INVALID 1 +#define TOOSMALL 2 +#define TOOLARGE 3 + +long long +strtonum(const char *numstr, long long minval, long long maxval, + const char **errstrp) +{ + long long ll = 0; + char *ep; + int error = 0; + struct errval { + const char *errstr; + int err; + } ev[4] = { + { NULL, 0 }, + { "invalid", EINVAL }, + { "too small", ERANGE }, + { "too large", ERANGE }, + }; + + ev[0].err = errno; + errno = 0; + if (minval > maxval) + error = INVALID; + else { + ll = strtoll(numstr, &ep, 10); + if (numstr == ep || *ep != '\0') + error = INVALID; + else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval) + error = TOOSMALL; + else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval) + error = TOOLARGE; + } + if (errstrp != NULL) + *errstrp = ev[error].errstr; + errno = ev[error].err; + if (error) + ll = 0; + + return (ll); +} diff --git a/external/bsd/tmux/dist/environ.c b/external/bsd/tmux/dist/environ.c new file mode 100644 index 000000000..e46253fab --- /dev/null +++ b/external/bsd/tmux/dist/environ.c @@ -0,0 +1,181 @@ +/* $Id: environ.c,v 1.3 2011/08/17 18:48:36 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +/* + * Environment - manipulate a set of environment variables. + */ + +RB_GENERATE(environ, environ_entry, entry, environ_cmp); + +int +environ_cmp(struct environ_entry *envent1, struct environ_entry *envent2) +{ + return (strcmp(envent1->name, envent2->name)); +} + +/* Initialise the environment. */ +void +environ_init(struct environ *env) +{ + RB_INIT(env); +} + +/* Free an environment. */ +void +environ_free(struct environ *env) +{ + struct environ_entry *envent; + + while (!RB_EMPTY(env)) { + envent = RB_ROOT(env); + RB_REMOVE(environ, env, envent); + xfree(envent->name); + if (envent->value != NULL) + xfree(envent->value); + xfree(envent); + } +} + +/* Copy one environment into another. */ +void +environ_copy(struct environ *srcenv, struct environ *dstenv) +{ + struct environ_entry *envent; + + RB_FOREACH(envent, environ, srcenv) + environ_set(dstenv, envent->name, envent->value); +} + +/* Find an environment variable. */ +struct environ_entry * +environ_find(struct environ *env, const char *name) +{ + struct environ_entry envent; + + envent.name = __UNCONST(name); + return (RB_FIND(environ, env, &envent)); +} + +/* Set an environment variable. */ +void +environ_set(struct environ *env, const char *name, const char *value) +{ + struct environ_entry *envent; + + if ((envent = environ_find(env, name)) != NULL) { + if (envent->value != NULL) + xfree(envent->value); + if (value != NULL) + envent->value = xstrdup(value); + else + envent->value = NULL; + } else { + envent = xmalloc(sizeof *envent); + envent->name = xstrdup(name); + if (value != NULL) + envent->value = xstrdup(value); + else + envent->value = NULL; + RB_INSERT(environ, env, envent); + } +} + +/* Set an environment variable from a NAME=VALUE string. */ +void +environ_put(struct environ *env, const char *var) +{ + char *name, *value; + + value = strchr(var, '='); + if (value == NULL) + return; + value++; + + name = xstrdup(var); + name[strcspn(name, "=")] = '\0'; + + environ_set(env, name, value); + xfree(name); +} + +/* Unset an environment variable. */ +void +environ_unset(struct environ *env, const char *name) +{ + struct environ_entry *envent; + + if ((envent = environ_find(env, name)) == NULL) + return; + RB_REMOVE(environ, env, envent); + xfree(envent->name); + if (envent->value != NULL) + xfree(envent->value); + xfree(envent); +} + +/* + * Copy a space-separated list of variables from a destination into a source + * environment. + */ +void +environ_update(const char *vars, struct environ *srcenv, struct environ *dstenv) +{ + struct environ_entry *envent; + char *copyvars, *var, *next; + + copyvars = next = xstrdup(vars); + while ((var = strsep(&next, " ")) != NULL) { + if ((envent = environ_find(srcenv, var)) == NULL) + environ_set(dstenv, var, NULL); + else + environ_set(dstenv, envent->name, envent->value); + } + xfree(copyvars); +} + +/* Push environment into the real environment - use after fork(). */ +void +environ_push(struct environ *env) +{ + ARRAY_DECL(, char *) varlist; + struct environ_entry *envent; + char **varp, *var; + u_int i; + + ARRAY_INIT(&varlist); + for (varp = environ; *varp != NULL; varp++) { + var = xstrdup(*varp); + var[strcspn(var, "=")] = '\0'; + ARRAY_ADD(&varlist, var); + } + for (i = 0; i < ARRAY_LENGTH(&varlist); i++) + unsetenv(ARRAY_ITEM(&varlist, i)); + ARRAY_FREE(&varlist); + + RB_FOREACH(envent, environ, env) { + if (envent->value != NULL) + setenv(envent->name, envent->value, 1); + } +} diff --git a/external/bsd/tmux/dist/examples/bash_completion_tmux.sh b/external/bsd/tmux/dist/examples/bash_completion_tmux.sh new file mode 100644 index 000000000..74728b91e --- /dev/null +++ b/external/bsd/tmux/dist/examples/bash_completion_tmux.sh @@ -0,0 +1,105 @@ +# START tmux completion +# This file is in the public domain +# See: http://www.debian-administration.org/articles/317 for how to write more. +# Usage: Put "source bash_completion_tmux.sh" into your .bashrc +_tmux() +{ + local cur prev opts + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" + + opts=" \ + attach-session \ + bind-key \ + break-pane \ + capture-pane \ + choose-client \ + choose-session \ + choose-window \ + clear-history \ + clock-mode \ + command-prompt \ + confirm-before \ + copy-buffer \ + copy-mode \ + delete-buffer \ + detach-client \ + display-message \ + display-panes \ + down-pane \ + find-window \ + has-session \ + if-shell \ + join-pane \ + kill-pane \ + kill-server \ + kill-session \ + kill-window \ + last-window \ + link-window \ + list-buffers \ + list-clients \ + list-commands \ + list-keys \ + list-panes \ + list-sessions \ + list-windows \ + load-buffer \ + lock-client \ + lock-server \ + lock-session \ + move-window \ + new-session \ + new-window \ + next-layout \ + next-window \ + paste-buffer \ + pipe-pane \ + previous-layout \ + previous-window \ + refresh-client \ + rename-session \ + rename-window \ + resize-pane \ + respawn-window \ + rotate-window \ + run-shell \ + save-buffer \ + select-layout \ + select-pane \ + select-prompt \ + select-window \ + send-keys \ + send-prefix \ + server-info \ + set-buffer \ + set-environment \ + set-option \ + set-window-option \ + show-buffer \ + show-environment \ + show-messages \ + show-options \ + show-window-options \ + source-file \ + split-window \ + start-server \ + suspend-client \ + swap-pane \ + swap-window \ + switch-client \ + unbind-key \ + unlink-window \ + up-pane" + + COMPREPLY=($(compgen -W "${opts}" -- ${cur})) + return 0 + +} +complete -F _tmux tmux + +# END tmux completion + + + diff --git a/external/bsd/tmux/dist/examples/h-boetes.conf b/external/bsd/tmux/dist/examples/h-boetes.conf new file mode 100644 index 000000000..c44394144 --- /dev/null +++ b/external/bsd/tmux/dist/examples/h-boetes.conf @@ -0,0 +1,42 @@ +# $Id: h-boetes.conf,v 1.1.1.2 2011/08/17 18:40:06 jmmv Exp $ +# +# From Han Boetes. + +set -g default-command zsh +set -g status-right "#(uptime|awk '{print $11}') #(date)" + +# Statusbar properties. +set -g display-time 3000 +set -g status-bg black +set -g status-fg cyan +set-window-option -g window-status-current-attr bright,reverse +set-window-option -g window-status-current-bg cyan +set-window-option -g window-status-current-fg black + +# Use c-t instead of c-b as the prefix +unbind C-b +set -g prefix C-t +bind C-t send-prefix +bind t send-prefix + +# Bind function keys. +bind -n F1 select-window -t 1 +bind -n F2 select-window -t 2 +bind -n F3 select-window -t 3 +bind -n F4 select-window -t 4 +bind -n F5 select-window -t 5 +bind -n F6 select-window -t 6 +bind -n F7 select-window -t 7 +bind -n F8 select-window -t 8 + +# All new windows started at startup. +new emacs +neww irssi +neww mutt +neww +neww +neww +neww +neww + +select-window -t 1 diff --git a/external/bsd/tmux/dist/examples/n-marriott.conf b/external/bsd/tmux/dist/examples/n-marriott.conf new file mode 100644 index 000000000..5d7458605 --- /dev/null +++ b/external/bsd/tmux/dist/examples/n-marriott.conf @@ -0,0 +1,110 @@ +# $Id: n-marriott.conf,v 1.1.1.2 2011/08/17 18:40:06 jmmv Exp $ +# +# By Nicholas Marriott. Public domain. + +# Default global options. +set -g status-bg green +set -g status-right "%H:%M" # %d-%b-%y +set -g bell-action none +set -g lock-after-time 1800 + +# Default global window options. +setw -g remain-on-exit on +setw -g window-status-current-attr "underscore" +#setw -g xterm-keys on + +# Prefix key. +set -g prefix C-a +unbind C-b +bind C-a send-prefix + +# Keys to switch session. +bind Q switchc -t0 +bind W switchc -t1 +bind E switchc -t2 + +# Other key bindings. +bind F1 selectw -t:10 +bind F2 selectw -t:11 +bind F3 selectw -t:12 +bind F4 selectw -t:13 +bind F5 selectw -t:14 +bind F6 selectw -t:15 +bind F7 selectw -t:16 +bind F8 selectw -t:17 +bind F9 selectw -t:18 +bind F10 selectw -t:19 +bind F11 selectw -t:20 +bind F12 selectw -t:21 + +bind m setw monitor-activity + +bind y setw force-width 81 +bind u setw force-width 0 + +bind -n F1 run-shell 'mpc toggle >/dev/null 2>&1' +bind -n F2 run-shell 'mpc' +bind -n F3 run-shell 'mpc prev >/dev/null 2>&1' +bind -n F4 run-shell 'mpc next >/dev/null 2>&1' +bind -n F5 run-shell 'mpc volume -5 >/dev/null 2>&1' +bind -n F6 run-shell 'mpc volume +5 >/dev/null 2>&1' + +# Hide and show window name from status line +bind '-' setw window-status-format '#I'\; setw window-status-current-format '#I' +bind '+' setw window-status-format '#I:#W#F'\; setw window-status-current-format '#I:#W#F' + +# First session. +new -d -s0 -nirssi 'exec ssh -t natalya exec sh ~/bin/tmux-start' +setw -t0:0 monitor-activity on +setw -t0:0 aggressive-resize on +set -t0 status-bg green +neww -d -ntodo 'exec emacs ~/TODO' +setw -t0:1 aggressive-resize on +neww -d -ntodo2 'exec emacs ~/TODO2' +setw -t0:2 aggressive-resize on +neww -d -nncmpc 'exec ncmpc -f ~/.ncmpc.conf' +setw -t0:3 aggressive-resize on +neww -d -nmutt 'exec mutt' +setw -t0:4 aggressive-resize on +neww -d +neww -d +neww -d +neww -d +neww -d +neww -d +neww -d +neww -d +neww -d +neww -d +neww -d +neww -d + +# Second session. +new -d -s1 +set -t1 status-bg cyan +linkw -dk -t0 -s0:0 +linkw -dk -t1 -s0:1 +linkw -dk -t2 -s0:2 +linkw -dk -t3 -s0:3 +linkw -dk -t4 -s0:4 +neww -d +neww -d +neww -d +neww -d +neww -d +neww -d + +# Third session. +new -d -s2 +set -t2 status-bg yellow +linkw -dk -t0 -s0:0 +linkw -dk -t1 -s0:1 +linkw -dk -t2 -s0:2 +linkw -dk -t3 -s0:3 +linkw -dk -t4 -s0:4 +neww -d +neww -d +neww -d +neww -d +neww -d +neww -d diff --git a/external/bsd/tmux/dist/examples/screen-keys.conf b/external/bsd/tmux/dist/examples/screen-keys.conf new file mode 100644 index 000000000..da2ce41b0 --- /dev/null +++ b/external/bsd/tmux/dist/examples/screen-keys.conf @@ -0,0 +1,102 @@ +# $Id: screen-keys.conf,v 1.1.1.2 2011/08/17 18:40:06 jmmv Exp $ +# +# By Nicholas Marriott. Public domain. +# +# This configuration file binds many of the common GNU screen key bindings to +# appropriate tmux key bindings. Note that for some key bindings there is no +# tmux analogue and also that this set omits binding some commands available in +# tmux but not in screen. +# +# Note this is only a selection of key bindings and they are in addition to the +# normal tmux key bindings. This is intended as an example not as to be used +# as-is. + +# Set the prefix to ^A. +unbind C-b +set -g prefix ^A +bind a send-prefix + +# Bind appropriate commands similar to screen. +# lockscreen ^X x +unbind ^X +bind ^X lock-server +unbind x +bind x lock-server + +# screen ^C c +unbind ^C +bind ^C new-window +bind c +bind c new-window + +# detach ^D d +unbind ^D +bind ^D detach + +# displays * +unbind * +bind * list-clients + +# next ^@ ^N sp n +unbind ^@ +bind ^@ next-window +unbind ^N +bind ^N next-window +unbind " " +bind " " next-window +unbind n +bind n next-window + +# title A +unbind A +bind A command-prompt "rename-window %%" + +# other ^A +unbind ^A +bind ^A last-window + +# prev ^H ^P p ^? +unbind ^H +bind ^H previous-window +unbind ^P +bind ^P previous-window +unbind p +bind p previous-window +unbind BSpace +bind BSpace previous-window + +# windows ^W w +unbind ^W +bind ^W list-windows +unbind w +bind w list-windows + +# quit \ +unbind \ +bind \ confirm-before "kill-server" + +# kill K k +unbind K +bind K confirm-before "kill-window" +unbind k +bind k confirm-before "kill-window" + +# redisplay ^L l +unbind ^L +bind ^L refresh-client +unbind l +bind l refresh-client + +# split -v | +unbind | +bind | split-window + +# :kB: focus up +unbind Tab +bind Tab select-pane -t:.+ +unbind BTab +bind BTab select-pane -t:.- + +# " windowlist -b +unbind '"' +bind '"' choose-window diff --git a/external/bsd/tmux/dist/examples/t-williams.conf b/external/bsd/tmux/dist/examples/t-williams.conf new file mode 100644 index 000000000..fb593d4e6 --- /dev/null +++ b/external/bsd/tmux/dist/examples/t-williams.conf @@ -0,0 +1,104 @@ +# $Id: t-williams.conf,v 1.1.1.2 2011/08/17 18:40:06 jmmv Exp $ +# +# ~/.tmux.conf - tmux terminal multiplexer config +# Thayer Williams (http://cinderwick.ca) +# "Feel free to do whatever you like with it." + +# I typically start tmux from ~/.xinitrc with the following: +# +# urxvt -e bash -c "tmux attach -d -t mysession" & +# +# and recall it any time thereafter with xbindkeys (Mod4+s): +# +# "urxvt -e bash -c 'tmux attach -d -t mysession'" +# m:0x50 + c:39 + + +# set prefix key to ctrl+a until I have time to adapt +unbind C-b +set -g prefix C-a + +# send the prefix to client inside window (ala nested sessions) +bind-key a send-prefix + +# toggle last window like screen +bind-key C-a last-window + +# confirm before killing a window or the server +bind-key k confirm kill-window +bind-key K confirm kill-server + +# toggle statusbar +bind-key b set-option status + +# ctrl+left/right cycles thru windows +bind-key -n C-right next +bind-key -n C-left prev + +# open a man page in new window +bind / command-prompt "split-window 'exec man %%'" + +# quick view of processes +bind '~' split-window "exec htop" + +# scrollback buffer n lines +set -g history-limit 5000 + +# listen for activity on all windows +set -g bell-action any + +# on-screen time for display-panes in ms +set -g display-panes-time 2000 + +# start window indexing at one instead of zero +set -g base-index 1 + +# enable wm window titles +set -g set-titles on + +# wm window title string (uses statusbar variables) +set -g set-titles-string "tmux.#I.#W" + +# session initialization +new -s mysession mutt +neww -t 2 +neww -d -t 3 +neww -d -t 5 mocp +neww -d -t 6 rtorrent +selectw -t 1 + +# statusbar -------------------------------------------------------------- + +set -g display-time 2000 + +# default statusbar colors +set -g status-fg white +set -g status-bg default +set -g status-attr default + +# default window title colors +set-window-option -g window-status-fg cyan +set-window-option -g window-status-bg default +set-window-option -g window-status-attr dim + +# active window title colors +set-window-option -g window-status-current-fg white +set-window-option -g window-status-current-bg default +set-window-option -g window-status-current-attr bright + +# command/message line colors +set -g message-fg white +set -g message-bg black +set -g message-attr bright + +# center align the window list +set -g status-justify centre + +# show some useful stats but only when tmux is started +# outside of Xorg, otherwise dwm statusbar shows these already +set -g status-right "" +set -g status-left "" +if '[ -z "$DISPLAY" ]' 'set -g status-left "[#[fg=green] #H #[default]]"' +if '[ -z "$DISPLAY" ]' 'set -g status-right "[ #[fg=magenta]#(cat /proc/loadavg | cut -d \" \" -f 1,2,3)#[default] ][ #[fg=cyan,bright]%a %Y-%m-%d %H:%M #[default]]"' +if '[ -z "$DISPLAY" ]' 'set -g status-right-length 50' + diff --git a/external/bsd/tmux/dist/examples/tmux.vim b/external/bsd/tmux/dist/examples/tmux.vim new file mode 100644 index 000000000..dce95e02d --- /dev/null +++ b/external/bsd/tmux/dist/examples/tmux.vim @@ -0,0 +1,104 @@ +" Vim syntax file +" Language: tmux(1) configuration file +" Maintainer: Tiago Cunha +" Last Change: $Date: 2011/08/17 18:40:06 $ +" License: This file is placed in the public domain. + +if version < 600 + syntax clear +elseif exists("b:current_syntax") + finish +endif + +setlocal iskeyword+=- +syntax case match + +syn keyword tmuxAction any current none +syn keyword tmuxBoolean off on + +syn keyword tmuxCmds detach[-client] ls list-sessions neww new-window +syn keyword tmuxCmds bind[-key] unbind[-key] prev[ious-window] last[-window] +syn keyword tmuxCmds lsk list-keys set[-option] renamew rename-window selectw +syn keyword tmuxCmds select-window lsw list-windows attach[-session] +syn keyword tmuxCmds send-prefix refresh[-client] killw kill-window lsc +syn keyword tmuxCmds list-clients linkw link-window unlinkw unlink-window +syn keyword tmuxCmds next[-window] send[-keys] swapw swap-window +syn keyword tmuxCmds rename[-session] kill-session switchc switch-client +syn keyword tmuxCmds has[-session] copy-mode pasteb paste-buffer +syn keyword tmuxCmds new[-session] start[-server] kill-server setw +syn keyword tmuxCmds set-window-option show[-options] showw show-window-options +syn keyword tmuxCmds command-prompt setb set-buffer showb show-buffer lsb +syn keyword tmuxCmds list-buffers deleteb delete-buffer lscm list-commands +syn keyword tmuxCmds movew move-window respawnw respawn-window +syn keyword tmuxCmds source[-file] info server-info clock-mode lock[-server] +syn keyword tmuxCmds saveb save-buffer killp +syn keyword tmuxCmds kill-pane resizep resize-pane selectp select-pane swapp +syn keyword tmuxCmds swap-pane splitw split-window choose-session +syn keyword tmuxCmds choose-window loadb load-buffer copyb copy-buffer suspendc +syn keyword tmuxCmds suspend-client findw find-window breakp break-pane nextl +syn keyword tmuxCmds next-layout rotatew rotate-window confirm[-before] +syn keyword tmuxCmds clearhist clear-history selectl select-layout if[-shell] +syn keyword tmuxCmds display[-message] setenv set-environment showenv +syn keyword tmuxCmds show-environment choose-client displayp display-panes +syn keyword tmuxCmds run[-shell] lockc lock-client locks lock-session lsp +syn keyword tmuxCmds list-panes pipep pipe-pane showmsgs show-messages capturep +syn keyword tmuxCmds capture-pane joinp join-pane choose-buffer + +syn keyword tmuxOptsSet prefix status status-fg status-bg bell-action +syn keyword tmuxOptsSet default-command history-limit status-left status-right +syn keyword tmuxOptsSet status-interval set-titles display-time buffer-limit +syn keyword tmuxOptsSet status-left-length status-right-length message-fg +syn keyword tmuxOptsSet message-bg lock-after-time default-path repeat-time +syn keyword tmuxOptsSet message-attr status-attr status-keys set-remain-on-exit +syn keyword tmuxOptsSet status-utf8 default-terminal visual-activity +syn keyword tmuxOptsSet visual-bell visual-content status-justify +syn keyword tmuxOptsSet terminal-overrides status-left-attr status-left-bg +syn keyword tmuxOptsSet status-left-fg status-right-attr status-right-bg +syn keyword tmuxOptsSet status-right-fg update-environment base-index +syn keyword tmuxOptsSet display-panes-colour display-panes-time default-shell +syn keyword tmuxOptsSet set-titles-string lock-command lock-server +syn keyword tmuxOptsSet mouse-select-pane message-limit quiet escape-time +syn keyword tmuxOptsSet pane-active-border-bg pane-active-border-fg +syn keyword tmuxOptsSet pane-border-bg pane-border-fg +syn keyword tmuxOptsSet display-panes-active-colour alternate-screen +syn keyword tmuxOptsSet detach-on-destroy + +syn keyword tmuxOptsSetw monitor-activity aggressive-resize force-width +syn keyword tmuxOptsSetw force-height remain-on-exit uft8 mode-fg mode-bg +syn keyword tmuxOptsSetw mode-keys clock-mode-colour clock-mode-style +syn keyword tmuxOptsSetw xterm-keys mode-attr window-status-attr +syn keyword tmuxOptsSetw window-status-bg window-status-fg automatic-rename +syn keyword tmuxOptsSetw main-pane-width main-pane-height monitor-content +syn keyword tmuxOptsSetw window-status-current-attr window-status-current-bg +syn keyword tmuxOptsSetw window-status-current-fg mode-mouse synchronize-panes +syn keyword tmuxOptsSetw window-status-format window-status-current-format +syn keyword tmuxOptsSetw word-separators window-status-alert-alert +syn keyword tmuxOptsSetw window-status-alert-bg window-status-alert-fg + +syn keyword tmuxTodo FIXME NOTE TODO XXX contained + +syn match tmuxKey /\(C-\|M-\|\^\)\p/ display +syn match tmuxNumber /\d\+/ display +syn match tmuxOptions /\s-\a\+/ display +syn match tmuxVariable /\w\+=/ display +syn match tmuxVariableExpansion /\${\=\w\+}\=/ display + +syn region tmuxComment start=/#/ end=/$/ contains=tmuxTodo display oneline +syn region tmuxString start=/"/ end=/"/ display oneline +syn region tmuxString start=/'/ end=/'/ display oneline + +hi def link tmuxAction Boolean +hi def link tmuxBoolean Boolean +hi def link tmuxCmds Keyword +hi def link tmuxComment Comment +hi def link tmuxKey Special +hi def link tmuxNumber Number +hi def link tmuxOptions Identifier +hi def link tmuxOptsSet Function +hi def link tmuxOptsSetw Function +hi def link tmuxString String +hi def link tmuxTodo Todo +hi def link tmuxVariable Constant +hi def link tmuxVariableExpansion Constant + +let b:current_syntax = "tmux" diff --git a/external/bsd/tmux/dist/examples/tmux_backup.sh b/external/bsd/tmux/dist/examples/tmux_backup.sh new file mode 100644 index 000000000..bc0bf370c --- /dev/null +++ b/external/bsd/tmux/dist/examples/tmux_backup.sh @@ -0,0 +1,81 @@ +#!/bin/bash +# +# By Victor Orlikowski. Public domain. +# +# This script maintains snapshots of each pane's +# history buffer, for each tmux session you are running. +# +# It is intended to be run by cron, on whatever interval works +# for you. + +# Maximum number of snapshots to keep. +max_backups=12 +# Names of sessions you may wish to exclude from snapshotting, +# space separated. +ignore_sessions="" +# The directory into which you want your snapshots placed. +# The default is probably "good enough." +backup_dir=~/.tmux_backup/snapshot + +######################################################################## + +# Rotate previous backups. +i=${max_backups} +while [[ ${i} != 0 ]] ; do +if [ -d ${backup_dir}.${i} ] ; then + if [[ ${i} = ${max_backups} ]] ; then + rm -r ${backup_dir}.${i} + else + mv ${backup_dir}.${i} ${backup_dir}.$((${i}+1)) + fi +fi +i=$((${i}-1)) +done + +if [ -d ${backup_dir} ] ; then + mv ${backup_dir} ${backup_dir}.1 +fi + +## Dump hardcopy from all windows in all available tmux sessions. +unset TMUX +for session in $(tmux list-sessions | cut -d' ' -f1 | sed -e 's/:$//') ; do + for ignore_session in ${ignore_sessions} ; do + if [ ${session} = ${ignore_session} ] ; then + continue 2 + fi + done + + # Session name can contain the colon character (":"). + # This can screw up addressing of windows within tmux, since + # target windows are specified as target-session:target-window. + # + # We use uuidgen to create a "safe" temporary session name, + # which we then use to create a "detached" session that "links" + # to the "real" session that we want to back up. + tmpsession=$(uuidgen) + tmux new-session -d -s "$tmpsession" -t "$session" + HISTSIZE=$(tmux show-options -g -t "$tmpsession" | grep "history-limit" | awk '{print $2}') + for win in $(tmux list-windows -t "$tmpsession" | grep -v "^\s" | cut -d' ' -f1 | sed -e 's/:$//'); do + session_dir=$(echo "$session" | sed -e 's/ /_/g' | sed -e 's%/%|%g') + win_spec="$tmpsession":"$win" + + if [ ! -d ${backup_dir}/${session_dir}/${win} ] ; then + mkdir -p ${backup_dir}/${session_dir}/${win} + fi + + for pane in $(tmux list-panes -t "$win_spec" | cut -d' ' -f1 | sed -e 's/:$//'); do + pane_path=${backup_dir}/${session_dir}/${win}/${pane} + pane_spec="$win_spec"."$pane" + + tmux capture-pane -t "$pane_spec" -S -${HISTSIZE} + tmux save-buffer ${pane_path} + + if [ ! -s ${pane_path} ] ; then + sleep 1 + rm ${pane_path} + fi + done + done + tmux kill-session -t "$tmpsession" + +done diff --git a/external/bsd/tmux/dist/examples/vim-keys.conf b/external/bsd/tmux/dist/examples/vim-keys.conf new file mode 100644 index 000000000..7c381ee93 --- /dev/null +++ b/external/bsd/tmux/dist/examples/vim-keys.conf @@ -0,0 +1,36 @@ +# $Id: vim-keys.conf,v 1.1.1.2 2011/08/17 18:40:06 jmmv Exp $ +# +# vim-keys.conf, v1.2 2010/09/12 +# +# By Daniel Thau. Public domain. +# +# This configuration file binds many vi- and vim-like bindings to the +# appropriate tmux key bindings. Note that for many key bindings there is no +# tmux analogue. This is intended for tmux 1.3, which handles pane selection +# differently from the previous versions + +# split windows like vim +# vim's definition of a horizontal/vertical split is reversed from tmux's +bind s split-window -v +bind v split-window -h + +# move around panes with hjkl, as one would in vim after pressing ctrl-w +bind h select-pane -L +bind j select-pane -D +bind k select-pane -U +bind l select-pane -R + +# resize panes like vim +# feel free to change the "1" to however many lines you want to resize by, only +# one at a time can be slow +bind < resize-pane -L 1 +bind > resize-pane -R 1 +bind - resize-pane -D 1 +bind + resize-pane -U 1 + +# bind : to command-prompt like vim +# this is the default in tmux already +bind : command-prompt + +# vi-style controls for copy mode +setw -g mode-keys vi diff --git a/external/bsd/tmux/dist/grid-utf8.c b/external/bsd/tmux/dist/grid-utf8.c new file mode 100644 index 000000000..397e4160f --- /dev/null +++ b/external/bsd/tmux/dist/grid-utf8.c @@ -0,0 +1,96 @@ +/* $Id: grid-utf8.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * Grid UTF-8 utility functions. + */ + +/* Calculate UTF-8 grid cell size. Data is terminated by 0xff. */ +size_t +grid_utf8_size(const struct grid_utf8 *gu) +{ + size_t size; + + for (size = 0; size < sizeof gu->data; size++) { + if (gu->data[size] == 0xff) + break; + } + return (size); +} + +/* Copy UTF-8 out into a buffer. */ +size_t +grid_utf8_copy(const struct grid_utf8 *gu, char *buf, size_t len) +{ + size_t size; + + size = grid_utf8_size(gu); + if (size > len) + fatalx("UTF-8 copy overflow"); + memcpy(buf, gu->data, size); + return (size); +} + +/* Set UTF-8 grid data from input UTF-8. */ +void +grid_utf8_set(struct grid_utf8 *gu, const struct utf8_data *utf8data) +{ + if (utf8data->size == 0) + fatalx("UTF-8 data empty"); + if (utf8data->size > sizeof gu->data) + fatalx("UTF-8 data too long"); + memcpy(gu->data, utf8data->data, utf8data->size); + if (utf8data->size != sizeof gu->data) + gu->data[utf8data->size] = 0xff; + gu->width = utf8data->width; +} + +/* Append UTF-8 character onto the cell data (for combined characters). */ +int +grid_utf8_append(struct grid_utf8 *gu, const struct utf8_data *utf8data) +{ + size_t old_size; + + old_size = grid_utf8_size(gu); + if (old_size + utf8data->size > sizeof gu->data) + return (-1); + memcpy(gu->data + old_size, utf8data->data, utf8data->size); + if (old_size + utf8data->size != sizeof gu->data) + gu->data[old_size + utf8data->size] = 0xff; + return (0); +} + +/* Compare two UTF-8 cells. */ +int +grid_utf8_compare(const struct grid_utf8 *gu1, const struct grid_utf8 *gu2) +{ + size_t size; + + size = grid_utf8_size(gu1); + if (size != grid_utf8_size(gu2)) + return (0); + if (memcmp(gu1->data, gu2->data, size) != 0) + return (0); + return (1); +} diff --git a/external/bsd/tmux/dist/grid-view.c b/external/bsd/tmux/dist/grid-view.c new file mode 100644 index 000000000..b404147f8 --- /dev/null +++ b/external/bsd/tmux/dist/grid-view.c @@ -0,0 +1,258 @@ +/* $Id: grid-view.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2008 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * Grid view functions. These work using coordinates relative to the visible + * screen area. + */ + +#define grid_view_x(gd, x) (x) +#define grid_view_y(gd, y) ((gd)->hsize + (y)) + +/* Get cell for reading. */ +const struct grid_cell * +grid_view_peek_cell(struct grid *gd, u_int px, u_int py) +{ + return (grid_peek_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py))); +} + +/* Get cell for writing. */ +struct grid_cell * +grid_view_get_cell(struct grid *gd, u_int px, u_int py) +{ + return (grid_get_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py))); +} + +/* Set cell. */ +void +grid_view_set_cell( + struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) +{ + grid_set_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py), gc); +} + +/* Get UTF-8 for reading. */ +const struct grid_utf8 * +grid_view_peek_utf8(struct grid *gd, u_int px, u_int py) +{ + return (grid_peek_utf8(gd, grid_view_x(gd, px), grid_view_y(gd, py))); +} + +/* Get UTF-8 for writing. */ +struct grid_utf8 * +grid_view_get_utf8(struct grid *gd, u_int px, u_int py) +{ + return (grid_get_utf8(gd, grid_view_x(gd, px), grid_view_y(gd, py))); +} + +/* Set UTF-8. */ +void +grid_view_set_utf8( + struct grid *gd, u_int px, u_int py, const struct grid_utf8 *gu) +{ + grid_set_utf8(gd, grid_view_x(gd, px), grid_view_y(gd, py), gu); +} + +/* Clear into history. */ +void +grid_view_clear_history(struct grid *gd) +{ + struct grid_line *gl; + u_int yy, last; + + GRID_DEBUG(gd, ""); + + /* Find the last used line. */ + last = 0; + for (yy = 0; yy < gd->sy; yy++) { + gl = &gd->linedata[grid_view_y(gd, yy)]; + if (gl->cellsize != 0 || gl->utf8size != 0) + last = yy + 1; + } + if (last == 0) + return; + + /* Scroll the lines into the history. */ + for (yy = 0; yy < last; yy++) + grid_scroll_history(gd); +} + +/* Clear area. */ +void +grid_view_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny) +{ + GRID_DEBUG(gd, "px=%u, py=%u, nx=%u, ny=%u", px, py, nx, ny); + + px = grid_view_x(gd, px); + py = grid_view_y(gd, py); + + grid_clear(gd, px, py, nx, ny); +} + +/* Scroll region up. */ +void +grid_view_scroll_region_up(struct grid *gd, u_int rupper, u_int rlower) +{ + GRID_DEBUG(gd, "rupper=%u, rlower=%u", rupper, rlower); + + if (gd->flags & GRID_HISTORY) { + grid_collect_history(gd); + if (rupper == 0 && rlower == gd->sy - 1) + grid_scroll_history(gd); + else { + rupper = grid_view_y(gd, rupper); + rlower = grid_view_y(gd, rlower); + grid_scroll_history_region(gd, rupper, rlower); + } + } else { + rupper = grid_view_y(gd, rupper); + rlower = grid_view_y(gd, rlower); + grid_move_lines(gd, rupper, rupper + 1, rlower - rupper); + } +} + +/* Scroll region down. */ +void +grid_view_scroll_region_down(struct grid *gd, u_int rupper, u_int rlower) +{ + GRID_DEBUG(gd, "rupper=%u, rlower=%u", rupper, rlower); + + rupper = grid_view_y(gd, rupper); + rlower = grid_view_y(gd, rlower); + + grid_move_lines(gd, rupper + 1, rupper, rlower - rupper); +} + +/* Insert lines. */ +void +grid_view_insert_lines(struct grid *gd, u_int py, u_int ny) +{ + u_int sy; + + GRID_DEBUG(gd, "py=%u, ny=%u", py, ny); + + py = grid_view_y(gd, py); + + sy = grid_view_y(gd, gd->sy); + + grid_move_lines(gd, py + ny, py, sy - py - ny); +} + +/* Insert lines in region. */ +void +grid_view_insert_lines_region(struct grid *gd, u_int rlower, u_int py, u_int ny) +{ + u_int ny2; + + GRID_DEBUG(gd, "rlower=%u, py=%u, ny=%u", rlower, py, ny); + + rlower = grid_view_y(gd, rlower); + + py = grid_view_y(gd, py); + + ny2 = rlower + 1 - py - ny; + grid_move_lines(gd, rlower + 1 - ny2, py, ny2); + grid_clear(gd, 0, py + ny2, gd->sx, ny - ny2); +} + +/* Delete lines. */ +void +grid_view_delete_lines(struct grid *gd, u_int py, u_int ny) +{ + u_int sy; + + GRID_DEBUG(gd, "py=%u, ny=%u", py, ny); + + py = grid_view_y(gd, py); + + sy = grid_view_y(gd, gd->sy); + + grid_move_lines(gd, py, py + ny, sy - py - ny); + grid_clear(gd, 0, sy - ny, gd->sx, py + ny - (sy - ny)); +} + +/* Delete lines inside scroll region. */ +void +grid_view_delete_lines_region(struct grid *gd, u_int rlower, u_int py, u_int ny) +{ + u_int ny2; + + GRID_DEBUG(gd, "rlower=%u, py=%u, ny=%u", rlower, py, ny); + + rlower = grid_view_y(gd, rlower); + + py = grid_view_y(gd, py); + + ny2 = rlower + 1 - py - ny; + grid_move_lines(gd, py, py + ny, ny2); + grid_clear(gd, 0, py + ny2, gd->sx, ny - ny2); +} + +/* Insert characters. */ +void +grid_view_insert_cells(struct grid *gd, u_int px, u_int py, u_int nx) +{ + u_int sx; + + GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); + + px = grid_view_x(gd, px); + py = grid_view_y(gd, py); + + sx = grid_view_x(gd, gd->sx); + + if (px == sx - 1) + grid_clear(gd, px, py, 1, 1); + else + grid_move_cells(gd, px + nx, px, py, sx - px - nx); +} + +/* Delete characters. */ +void +grid_view_delete_cells(struct grid *gd, u_int px, u_int py, u_int nx) +{ + u_int sx; + + GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); + + px = grid_view_x(gd, px); + py = grid_view_y(gd, py); + + sx = grid_view_x(gd, gd->sx); + + grid_move_cells(gd, px, px + nx, py, sx - px - nx); + grid_clear(gd, sx - nx, py, px + nx - (sx - nx), 1); +} + +/* Convert cells into a string. */ +char * +grid_view_string_cells(struct grid *gd, u_int px, u_int py, u_int nx) +{ + GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); + + px = grid_view_x(gd, px); + py = grid_view_y(gd, py); + + return (grid_string_cells(gd, px, py, nx)); +} diff --git a/external/bsd/tmux/dist/grid.c b/external/bsd/tmux/dist/grid.c new file mode 100644 index 000000000..16f8ed418 --- /dev/null +++ b/external/bsd/tmux/dist/grid.c @@ -0,0 +1,545 @@ +/* $Id: grid.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2008 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * Grid data. This is the basic data structure that represents what is shown on + * screen. + * + * A grid is a grid of cells (struct grid_cell). Lines are not allocated until + * cells in that line are written to. The grid is split into history and + * viewable data with the history starting at row (line) 0 and extending to + * (hsize - 1); from hsize to hsize + (sy - 1) is the viewable data. All + * functions in this file work on absolute coordinates, grid-view.c has + * functions which work on the screen data. + */ + +/* Default grid cell data. */ +const struct grid_cell grid_default_cell = { 0, 0, 8, 8, ' ' }; + +#define grid_put_cell(gd, px, py, gc) do { \ + memcpy(&gd->linedata[py].celldata[px], \ + gc, sizeof gd->linedata[py].celldata[px]); \ +} while (0) +#define grid_put_utf8(gd, px, py, gc) do { \ + memcpy(&gd->linedata[py].utf8data[px], \ + gc, sizeof gd->linedata[py].utf8data[px]); \ +} while (0) + +int grid_check_y(struct grid *, u_int); + +#ifdef DEBUG +int +grid_check_y(struct grid *gd, u_int py) +{ + if ((py) >= (gd)->hsize + (gd)->sy) + log_fatalx("y out of range: %u", py); + return (0); +} +#else +int +grid_check_y(struct grid *gd, u_int py) +{ + if ((py) >= (gd)->hsize + (gd)->sy) { + log_debug("y out of range: %u", py); + return (-1); + } + return (0); +} +#endif + +/* Create a new grid. */ +struct grid * +grid_create(u_int sx, u_int sy, u_int hlimit) +{ + struct grid *gd; + + gd = xmalloc(sizeof *gd); + gd->sx = sx; + gd->sy = sy; + + gd->flags = GRID_HISTORY; + + gd->hsize = 0; + gd->hlimit = hlimit; + + gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata); + + return (gd); +} + +/* Destroy grid. */ +void +grid_destroy(struct grid *gd) +{ + struct grid_line *gl; + u_int yy; + + for (yy = 0; yy < gd->hsize + gd->sy; yy++) { + gl = &gd->linedata[yy]; + if (gl->celldata != NULL) + xfree(gl->celldata); + if (gl->utf8data != NULL) + xfree(gl->utf8data); + } + + xfree(gd->linedata); + + xfree(gd); +} + +/* Compare grids. */ +int +grid_compare(struct grid *ga, struct grid *gb) +{ + struct grid_line *gla, *glb; + struct grid_cell *gca, *gcb; + struct grid_utf8 *gua, *gub; + u_int xx, yy; + + if (ga->sx != gb->sx || ga->sy != ga->sy) + return (1); + + for (yy = 0; yy < ga->sy; yy++) { + gla = &ga->linedata[yy]; + glb = &gb->linedata[yy]; + if (gla->cellsize != glb->cellsize) + return (1); + for (xx = 0; xx < ga->sx; xx++) { + gca = &gla->celldata[xx]; + gcb = &glb->celldata[xx]; + if (memcmp(gca, gcb, sizeof (struct grid_cell)) != 0) + return (1); + if (!(gca->flags & GRID_FLAG_UTF8)) + continue; + gua = &gla->utf8data[xx]; + gub = &glb->utf8data[xx]; + if (memcmp(gua, gub, sizeof (struct grid_utf8)) != 0) + return (1); + } + } + + return (0); +} + +/* + * Collect lines from the history if at the limit. Free the top (oldest) 10% + * and shift up. + */ +void +grid_collect_history(struct grid *gd) +{ + u_int yy; + + GRID_DEBUG(gd, ""); + + if (gd->hsize < gd->hlimit) + return; + + yy = gd->hlimit / 10; + if (yy < 1) + yy = 1; + + grid_move_lines(gd, 0, yy, gd->hsize + gd->sy - yy); + gd->hsize -= yy; +} + +/* + * Scroll the entire visible screen, moving one line into the history. Just + * allocate a new line at the bottom and move the history size indicator. + */ +void +grid_scroll_history(struct grid *gd) +{ + u_int yy; + + GRID_DEBUG(gd, ""); + + yy = gd->hsize + gd->sy; + gd->linedata = xrealloc(gd->linedata, yy + 1, sizeof *gd->linedata); + memset(&gd->linedata[yy], 0, sizeof gd->linedata[yy]); + + gd->hsize++; +} + +/* Scroll a region up, moving the top line into the history. */ +void +grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower) +{ + struct grid_line *gl_history, *gl_upper, *gl_lower; + u_int yy; + + GRID_DEBUG(gd, "upper=%u, lower=%u", upper, lower); + + /* Create a space for a new line. */ + yy = gd->hsize + gd->sy; + gd->linedata = xrealloc(gd->linedata, yy + 1, sizeof *gd->linedata); + + /* Move the entire screen down to free a space for this line. */ + gl_history = &gd->linedata[gd->hsize]; + memmove(gl_history + 1, gl_history, gd->sy * sizeof *gl_history); + + /* Adjust the region and find its start and end. */ + upper++; + gl_upper = &gd->linedata[upper]; + lower++; + gl_lower = &gd->linedata[lower]; + + /* Move the line into the history. */ + memcpy(gl_history, gl_upper, sizeof *gl_history); + + /* Then move the region up and clear the bottom line. */ + memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper); + memset(gl_lower, 0, sizeof *gl_lower); + + /* Move the history offset down over the line. */ + gd->hsize++; +} + +/* Expand line to fit to cell. */ +void +grid_expand_line(struct grid *gd, u_int py, u_int sx) +{ + struct grid_line *gl; + u_int xx; + + gl = &gd->linedata[py]; + if (sx <= gl->cellsize) + return; + + gl->celldata = xrealloc(gl->celldata, sx, sizeof *gl->celldata); + for (xx = gl->cellsize; xx < sx; xx++) + grid_put_cell(gd, xx, py, &grid_default_cell); + gl->cellsize = sx; +} + +/* Expand line to fit to cell for UTF-8. */ +void +grid_expand_line_utf8(struct grid *gd, u_int py, u_int sx) +{ + struct grid_line *gl; + + gl = &gd->linedata[py]; + if (sx <= gl->utf8size) + return; + + gl->utf8data = xrealloc(gl->utf8data, sx, sizeof *gl->utf8data); + gl->utf8size = sx; +} + +/* Get cell for reading. */ +const struct grid_cell * +grid_peek_cell(struct grid *gd, u_int px, u_int py) +{ + if (grid_check_y(gd, py) != 0) + return (&grid_default_cell); + + if (px >= gd->linedata[py].cellsize) + return (&grid_default_cell); + return (&gd->linedata[py].celldata[px]); +} + +/* Get cell at relative position (for writing). */ +struct grid_cell * +grid_get_cell(struct grid *gd, u_int px, u_int py) +{ + if (grid_check_y(gd, py) != 0) + return (NULL); + + grid_expand_line(gd, py, px + 1); + return (&gd->linedata[py].celldata[px]); +} + +/* Set cell at relative position. */ +void +grid_set_cell( + struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) +{ + if (grid_check_y(gd, py) != 0) + return; + + grid_expand_line(gd, py, px + 1); + grid_put_cell(gd, px, py, gc); +} + +/* Get UTF-8 for reading. */ +const struct grid_utf8 * +grid_peek_utf8(struct grid *gd, u_int px, u_int py) +{ + if (grid_check_y(gd, py) != 0) + return (NULL); + + if (px >= gd->linedata[py].utf8size) + return (NULL); + return (&gd->linedata[py].utf8data[px]); +} + +/* Get utf8 at relative position (for writing). */ +struct grid_utf8 * +grid_get_utf8(struct grid *gd, u_int px, u_int py) +{ + if (grid_check_y(gd, py) != 0) + return (NULL); + + grid_expand_line_utf8(gd, py, px + 1); + return (&gd->linedata[py].utf8data[px]); +} + +/* Set utf8 at relative position. */ +void +grid_set_utf8( + struct grid *gd, u_int px, u_int py, const struct grid_utf8 *gc) +{ + if (grid_check_y(gd, py) != 0) + return; + + grid_expand_line_utf8(gd, py, px + 1); + grid_put_utf8(gd, px, py, gc); +} + +/* Clear area. */ +void +grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny) +{ + u_int xx, yy; + + GRID_DEBUG(gd, "px=%u, py=%u, nx=%u, ny=%u", px, py, nx, ny); + + if (nx == 0 || ny == 0) + return; + + if (px == 0 && nx == gd->sx) { + grid_clear_lines(gd, py, ny); + return; + } + + if (grid_check_y(gd, py) != 0) + return; + if (grid_check_y(gd, py + ny - 1) != 0) + return; + + for (yy = py; yy < py + ny; yy++) { + if (px >= gd->linedata[yy].cellsize) + continue; + if (px + nx >= gd->linedata[yy].cellsize) { + gd->linedata[yy].cellsize = px; + continue; + } + for (xx = px; xx < px + nx; xx++) { + if (xx >= gd->linedata[yy].cellsize) + break; + grid_put_cell(gd, xx, yy, &grid_default_cell); + } + } +} + +/* Clear lines. This just frees and truncates the lines. */ +void +grid_clear_lines(struct grid *gd, u_int py, u_int ny) +{ + struct grid_line *gl; + u_int yy; + + GRID_DEBUG(gd, "py=%u, ny=%u", py, ny); + + if (ny == 0) + return; + + if (grid_check_y(gd, py) != 0) + return; + if (grid_check_y(gd, py + ny - 1) != 0) + return; + + for (yy = py; yy < py + ny; yy++) { + gl = &gd->linedata[yy]; + if (gl->celldata != NULL) + xfree(gl->celldata); + if (gl->utf8data != NULL) + xfree(gl->utf8data); + memset(gl, 0, sizeof *gl); + } +} + +/* Move a group of lines. */ +void +grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny) +{ + u_int yy; + + GRID_DEBUG(gd, "dy=%u, py=%u, ny=%u", dy, py, ny); + + if (ny == 0 || py == dy) + return; + + if (grid_check_y(gd, py) != 0) + return; + if (grid_check_y(gd, py + ny - 1) != 0) + return; + if (grid_check_y(gd, dy) != 0) + return; + if (grid_check_y(gd, dy + ny - 1) != 0) + return; + + /* Free any lines which are being replaced. */ + for (yy = dy; yy < dy + ny; yy++) { + if (yy >= py && yy < py + ny) + continue; + grid_clear_lines(gd, yy, 1); + } + + memmove( + &gd->linedata[dy], &gd->linedata[py], ny * (sizeof *gd->linedata)); + + /* Wipe any lines that have been moved (without freeing them). */ + for (yy = py; yy < py + ny; yy++) { + if (yy >= dy && yy < dy + ny) + continue; + memset(&gd->linedata[yy], 0, sizeof gd->linedata[yy]); + } +} + +/* Move a group of cells. */ +void +grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx) +{ + struct grid_line *gl; + u_int xx; + + GRID_DEBUG(gd, "dx=%u, px=%u, py=%u, nx=%u", dx, px, py, nx); + + if (nx == 0 || px == dx) + return; + + if (grid_check_y(gd, py) != 0) + return; + gl = &gd->linedata[py]; + + grid_expand_line(gd, py, px + nx); + grid_expand_line(gd, py, dx + nx); + memmove( + &gl->celldata[dx], &gl->celldata[px], nx * sizeof *gl->celldata); + + if (gl->utf8data != NULL) { + grid_expand_line_utf8(gd, py, px + nx); + grid_expand_line_utf8(gd, py, dx + nx); + memmove(&gl->utf8data[dx], + &gl->utf8data[px], nx * sizeof *gl->utf8data); + } + + /* Wipe any cells that have been moved. */ + for (xx = px; xx < px + nx; xx++) { + if (xx >= dx && xx < dx + nx) + continue; + grid_put_cell(gd, xx, py, &grid_default_cell); + } +} + +/* Convert cells into a string. */ +char * +grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx) +{ + const struct grid_cell *gc; + const struct grid_utf8 *gu; + char *buf; + size_t len, off, size; + u_int xx; + + GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); + + len = 128; + buf = xmalloc(len); + off = 0; + + for (xx = px; xx < px + nx; xx++) { + gc = grid_peek_cell(gd, xx, py); + if (gc->flags & GRID_FLAG_PADDING) + continue; + + if (gc->flags & GRID_FLAG_UTF8) { + gu = grid_peek_utf8(gd, xx, py); + + size = grid_utf8_size(gu); + while (len < off + size + 1) { + buf = xrealloc(buf, 2, len); + len *= 2; + } + + off += grid_utf8_copy(gu, buf + off, len - off); + } else { + while (len < off + 2) { + buf = xrealloc(buf, 2, len); + len *= 2; + } + + buf[off++] = gc->data; + } + } + + while (off > 0 && buf[off - 1] == ' ') + off--; + buf[off] = '\0'; + return (buf); +} + +/* + * Duplicate a set of lines between two grids. If there aren't enough lines in + * either source or destination, the number of lines is limited to the number + * available. + */ +void +grid_duplicate_lines( + struct grid *dst, u_int dy, struct grid *src, u_int sy, u_int ny) +{ + struct grid_line *dstl, *srcl; + u_int yy; + + GRID_DEBUG(src, "dy=%u, sy=%u, ny=%u", dy, sy, ny); + + if (dy + ny > dst->hsize + dst->sy) + ny = dst->hsize + dst->sy - dy; + if (sy + ny > src->hsize + src->sy) + ny = src->hsize + src->sy - sy; + grid_clear_lines(dst, dy, ny); + + for (yy = 0; yy < ny; yy++) { + srcl = &src->linedata[sy]; + dstl = &dst->linedata[dy]; + + memcpy(dstl, srcl, sizeof *dstl); + if (srcl->cellsize != 0) { + dstl->celldata = xcalloc( + srcl->cellsize, sizeof *dstl->celldata); + memcpy(dstl->celldata, srcl->celldata, + srcl->cellsize * sizeof *dstl->celldata); + } + if (srcl->utf8size != 0) { + dstl->utf8data = xcalloc( + srcl->utf8size, sizeof *dstl->utf8data); + memcpy(dstl->utf8data, srcl->utf8data, + srcl->utf8size * sizeof *dstl->utf8data); + } + + sy++; + dy++; + } +} diff --git a/external/bsd/tmux/dist/input-keys.c b/external/bsd/tmux/dist/input-keys.c new file mode 100644 index 000000000..77893a7df --- /dev/null +++ b/external/bsd/tmux/dist/input-keys.c @@ -0,0 +1,233 @@ +/* $Id: input-keys.c,v 1.2 2011/08/17 19:28:36 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +/* + * This file is rather misleadingly named, it contains the code which takes a + * key code and translates it into something suitable to be sent to the + * application running in a pane (similar to input.c does in the other + * direction with output). + */ + +struct input_key_ent { + int key; + const char *data; + + int flags; +#define INPUTKEY_KEYPAD 0x1 /* keypad key */ +#define INPUTKEY_CURSOR 0x2 /* cursor key */ +}; + +const struct input_key_ent input_keys[] = { + /* Backspace key. */ + { KEYC_BSPACE, "\177", 0 }, + + /* Function keys. */ + { KEYC_F1, "\033OP", 0 }, + { KEYC_F2, "\033OQ", 0 }, + { KEYC_F3, "\033OR", 0 }, + { KEYC_F4, "\033OS", 0 }, + { KEYC_F5, "\033[15~", 0 }, + { KEYC_F6, "\033[17~", 0 }, + { KEYC_F7, "\033[18~", 0 }, + { KEYC_F8, "\033[19~", 0 }, + { KEYC_F9, "\033[20~", 0 }, + { KEYC_F10, "\033[21~", 0 }, + { KEYC_F11, "\033[23~", 0 }, + { KEYC_F12, "\033[24~", 0 }, + { KEYC_F13, "\033[25~", 0 }, + { KEYC_F14, "\033[26~", 0 }, + { KEYC_F15, "\033[28~", 0 }, + { KEYC_F16, "\033[29~", 0 }, + { KEYC_F17, "\033[31~", 0 }, + { KEYC_F18, "\033[32~", 0 }, + { KEYC_F19, "\033[33~", 0 }, + { KEYC_F20, "\033[34~", 0 }, + { KEYC_IC, "\033[2~", 0 }, + { KEYC_DC, "\033[3~", 0 }, + { KEYC_HOME, "\033[1~", 0 }, + { KEYC_END, "\033[4~", 0 }, + { KEYC_NPAGE, "\033[6~", 0 }, + { KEYC_PPAGE, "\033[5~", 0 }, + { KEYC_BTAB, "\033[Z", 0 }, + + /* + * Arrow keys. Cursor versions must come first. The codes are toggled + * between CSI and SS3 versions when ctrl is pressed. + */ + { KEYC_UP|KEYC_CTRL, "\033[A", INPUTKEY_CURSOR }, + { KEYC_DOWN|KEYC_CTRL, "\033[B", INPUTKEY_CURSOR }, + { KEYC_RIGHT|KEYC_CTRL, "\033[C", INPUTKEY_CURSOR }, + { KEYC_LEFT|KEYC_CTRL, "\033[D", INPUTKEY_CURSOR }, + + { KEYC_UP, "\033OA", INPUTKEY_CURSOR }, + { KEYC_DOWN, "\033OB", INPUTKEY_CURSOR }, + { KEYC_RIGHT, "\033OC", INPUTKEY_CURSOR }, + { KEYC_LEFT, "\033OD", INPUTKEY_CURSOR }, + + { KEYC_UP|KEYC_CTRL, "\033OA", 0 }, + { KEYC_DOWN|KEYC_CTRL, "\033OB", 0 }, + { KEYC_RIGHT|KEYC_CTRL, "\033OC", 0 }, + { KEYC_LEFT|KEYC_CTRL, "\033OD", 0 }, + + { KEYC_UP, "\033[A", 0 }, + { KEYC_DOWN, "\033[B", 0 }, + { KEYC_RIGHT, "\033[C", 0 }, + { KEYC_LEFT, "\033[D", 0 }, + + /* Keypad keys. Keypad versions must come first. */ + { KEYC_KP_SLASH, "\033Oo", INPUTKEY_KEYPAD }, + { KEYC_KP_STAR, "\033Oj", INPUTKEY_KEYPAD }, + { KEYC_KP_MINUS, "\033Om", INPUTKEY_KEYPAD }, + { KEYC_KP_SEVEN, "\033Ow", INPUTKEY_KEYPAD }, + { KEYC_KP_EIGHT, "\033Ox", INPUTKEY_KEYPAD }, + { KEYC_KP_NINE, "\033Oy", INPUTKEY_KEYPAD }, + { KEYC_KP_PLUS, "\033Ok", INPUTKEY_KEYPAD }, + { KEYC_KP_FOUR, "\033Ot", INPUTKEY_KEYPAD }, + { KEYC_KP_FIVE, "\033Ou", INPUTKEY_KEYPAD }, + { KEYC_KP_SIX, "\033Ov", INPUTKEY_KEYPAD }, + { KEYC_KP_ONE, "\033Oq", INPUTKEY_KEYPAD }, + { KEYC_KP_TWO, "\033Or", INPUTKEY_KEYPAD }, + { KEYC_KP_THREE, "\033Os", INPUTKEY_KEYPAD }, + { KEYC_KP_ENTER, "\033OM", INPUTKEY_KEYPAD }, + { KEYC_KP_ZERO, "\033Op", INPUTKEY_KEYPAD }, + { KEYC_KP_PERIOD, "\033On", INPUTKEY_KEYPAD }, + + { KEYC_KP_SLASH, "/", 0 }, + { KEYC_KP_STAR, "*", 0 }, + { KEYC_KP_MINUS, "-", 0 }, + { KEYC_KP_SEVEN, "7", 0 }, + { KEYC_KP_EIGHT, "8", 0 }, + { KEYC_KP_NINE, "9", 0 }, + { KEYC_KP_PLUS, "+", 0 }, + { KEYC_KP_FOUR, "4", 0 }, + { KEYC_KP_FIVE, "5", 0 }, + { KEYC_KP_SIX, "6", 0 }, + { KEYC_KP_ONE, "1", 0 }, + { KEYC_KP_TWO, "2", 0 }, + { KEYC_KP_THREE, "3", 0 }, + { KEYC_KP_ENTER, "\n", 0 }, + { KEYC_KP_ZERO, "0", 0 }, + { KEYC_KP_PERIOD, ".", 0 }, +}; + +/* Translate a key code into an output key sequence. */ +void +input_key(struct window_pane *wp, int key) +{ + const struct input_key_ent *ike; + u_int i; + size_t dlen; + char *out; + u_char ch; + + log_debug2("writing key 0x%x", key); + + /* + * If this is a normal 7-bit key, just send it, with a leading escape + * if necessary. + */ + if (key != KEYC_NONE && (key & ~KEYC_ESCAPE) < 0x100) { + if (key & KEYC_ESCAPE) + bufferevent_write(wp->event, "\033", 1); + ch = key & ~KEYC_ESCAPE; + bufferevent_write(wp->event, &ch, 1); + return; + } + + /* + * Then try to look this up as an xterm key, if the flag to output them + * is set. + */ + if (options_get_number(&wp->window->options, "xterm-keys")) { + if ((out = xterm_keys_lookup(key)) != NULL) { + bufferevent_write(wp->event, out, strlen(out)); + xfree(out); + return; + } + } + + /* Otherwise look the key up in the table. */ + for (i = 0; i < nitems(input_keys); i++) { + ike = &input_keys[i]; + + if ((ike->flags & INPUTKEY_KEYPAD) && + !(wp->screen->mode & MODE_KKEYPAD)) + continue; + if ((ike->flags & INPUTKEY_CURSOR) && + !(wp->screen->mode & MODE_KCURSOR)) + continue; + + if ((key & KEYC_ESCAPE) && (ike->key | KEYC_ESCAPE) == key) + break; + if (ike->key == key) + break; + } + if (i == nitems(input_keys)) { + log_debug2("key 0x%x missing", key); + return; + } + dlen = strlen(ike->data); + log_debug2("found key 0x%x: \"%s\"", key, ike->data); + + /* Prefix a \033 for escape. */ + if (key & KEYC_ESCAPE) + bufferevent_write(wp->event, "\033", 1); + bufferevent_write(wp->event, ike->data, dlen); +} + +/* Translate mouse and output. */ +void +input_mouse(struct window_pane *wp, struct mouse_event *m) +{ + char buf[10]; + size_t len; + + if (wp->screen->mode & ALL_MOUSE_MODES) { + if (wp->screen->mode & MODE_MOUSE_UTF8) { + len = xsnprintf(buf, sizeof buf, "\033[M"); + len += utf8_split2(m->b + 32, + (unsigned char *)&buf[len]); + len += utf8_split2(m->x + 33, + (unsigned char *)&buf[len]); + len += utf8_split2(m->y + 33, + (unsigned char *)&buf[len]); + } else { + if (m->b > 223 || m->x >= 222 || m->y > 222) + return; + len = xsnprintf(buf, sizeof buf, "\033[M"); + buf[len++] = m->b + 32; + buf[len++] = m->x + 33; + buf[len++] = m->y + 33; + } + bufferevent_write(wp->event, buf, len); + } else if ((m->b & MOUSE_BUTTON) != MOUSE_2) { + if (options_get_number(&wp->window->options, "mode-mouse") && + window_pane_set_mode(wp, &window_copy_mode) == 0) { + window_copy_init_from_pane(wp); + if (wp->mode->mouse != NULL) + wp->mode->mouse(wp, NULL, m); + } + } +} diff --git a/external/bsd/tmux/dist/input.c b/external/bsd/tmux/dist/input.c new file mode 100644 index 000000000..3a1bbe296 --- /dev/null +++ b/external/bsd/tmux/dist/input.c @@ -0,0 +1,1572 @@ +/* $Id: input.c,v 1.4 2011/08/17 19:28:36 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +/* + * Based on the description by Paul Williams at: + * + * http://vt100.net/emu/dec_ansi_parser + * + * With the following changes: + * + * - 7-bit only. + * + * - Support for UTF-8. + * + * - OSC (but not APC) may be terminated by \007 as well as ST. + * + * - A state for APC similar to OSC. Some terminals appear to use this to set + * the title. + * + * - A state for the screen \033k...\033\\ sequence to rename a window. This is + * pretty stupid but not supporting it is more trouble than it is worth. + * + * - Special handling for ESC inside a DCS to allow arbitrary byte sequences to + * be passed to the underlying teminal(s). + */ + +/* Helper functions. */ +int input_split(struct input_ctx *); +int input_get(struct input_ctx *, u_int, int, int); +void input_reply(struct input_ctx *, const char *, ...); + +/* Transition entry/exit handlers. */ +void input_clear(struct input_ctx *); +void input_enter_osc(struct input_ctx *); +void input_exit_osc(struct input_ctx *); +void input_enter_apc(struct input_ctx *); +void input_exit_apc(struct input_ctx *); +void input_enter_rename(struct input_ctx *); +void input_exit_rename(struct input_ctx *); + +/* Input state handlers. */ +int input_print(struct input_ctx *); +int input_intermediate(struct input_ctx *); +int input_parameter(struct input_ctx *); +int input_input(struct input_ctx *); +int input_c0_dispatch(struct input_ctx *); +int input_esc_dispatch(struct input_ctx *); +int input_csi_dispatch(struct input_ctx *); +void input_csi_dispatch_sgr(struct input_ctx *); +int input_dcs_dispatch(struct input_ctx *); +int input_utf8_open(struct input_ctx *); +int input_utf8_add(struct input_ctx *); +int input_utf8_close(struct input_ctx *); + +/* Command table comparison function. */ +int input_table_compare(const void *, const void *); + +/* Command table entry. */ +struct input_table_entry { + int ch; + const char *interm; + int type; +}; + +/* Escape commands. */ +enum input_esc_type { + INPUT_ESC_DECALN, + INPUT_ESC_DECKPAM, + INPUT_ESC_DECKPNM, + INPUT_ESC_DECRC, + INPUT_ESC_DECSC, + INPUT_ESC_HTS, + INPUT_ESC_IND, + INPUT_ESC_NEL, + INPUT_ESC_RI, + INPUT_ESC_RIS, + INPUT_ESC_SCSOFF_G0, + INPUT_ESC_SCSON_G0, +}; + +/* Escape command table. */ +const struct input_table_entry input_esc_table[] = { + { '0', "(", INPUT_ESC_SCSOFF_G0 }, + { '7', "", INPUT_ESC_DECSC }, + { '8', "", INPUT_ESC_DECRC }, + { '8', "#", INPUT_ESC_DECALN }, + { '=', "", INPUT_ESC_DECKPAM }, + { '>', "", INPUT_ESC_DECKPNM }, + { 'B', "(", INPUT_ESC_SCSON_G0 }, + { 'D', "", INPUT_ESC_IND }, + { 'E', "", INPUT_ESC_NEL }, + { 'H', "", INPUT_ESC_HTS }, + { 'M', "", INPUT_ESC_RI }, + { 'c', "", INPUT_ESC_RIS }, +}; + +/* Control (CSI) commands. */ +enum input_csi_type { + INPUT_CSI_CBT, + INPUT_CSI_CUB, + INPUT_CSI_CUD, + INPUT_CSI_CUF, + INPUT_CSI_CUP, + INPUT_CSI_CUU, + INPUT_CSI_DA, + INPUT_CSI_DCH, + INPUT_CSI_DECSCUSR, + INPUT_CSI_DECSTBM, + INPUT_CSI_DL, + INPUT_CSI_DSR, + INPUT_CSI_ED, + INPUT_CSI_EL, + INPUT_CSI_HPA, + INPUT_CSI_ICH, + INPUT_CSI_IL, + INPUT_CSI_RM, + INPUT_CSI_RM_PRIVATE, + INPUT_CSI_SGR, + INPUT_CSI_SM, + INPUT_CSI_SM_PRIVATE, + INPUT_CSI_TBC, + INPUT_CSI_VPA, +}; + +/* Control (CSI) command table. */ +const struct input_table_entry input_csi_table[] = { + { '@', "", INPUT_CSI_ICH }, + { 'A', "", INPUT_CSI_CUU }, + { 'B', "", INPUT_CSI_CUD }, + { 'C', "", INPUT_CSI_CUF }, + { 'D', "", INPUT_CSI_CUB }, + { 'G', "", INPUT_CSI_HPA }, + { 'H', "", INPUT_CSI_CUP }, + { 'J', "", INPUT_CSI_ED }, + { 'K', "", INPUT_CSI_EL }, + { 'L', "", INPUT_CSI_IL }, + { 'M', "", INPUT_CSI_DL }, + { 'P', "", INPUT_CSI_DCH }, + { 'Z', "", INPUT_CSI_CBT }, + { 'c', "", INPUT_CSI_DA }, + { 'd', "", INPUT_CSI_VPA }, + { 'f', "", INPUT_CSI_CUP }, + { 'g', "", INPUT_CSI_TBC }, + { 'h', "", INPUT_CSI_SM }, + { 'h', "?", INPUT_CSI_SM_PRIVATE }, + { 'l', "", INPUT_CSI_RM }, + { 'l', "?", INPUT_CSI_RM_PRIVATE }, + { 'm', "", INPUT_CSI_SGR }, + { 'n', "", INPUT_CSI_DSR }, + { 'q', " ", INPUT_CSI_DECSCUSR }, + { 'r', "", INPUT_CSI_DECSTBM }, +}; + +/* Input transition. */ +struct input_transition { + int first; + int last; + + int (*handler)(struct input_ctx *); + const struct input_state *state; +}; + +/* Input state. */ +struct input_state { + const char *name; + void (*enter)(struct input_ctx *); + void (*exit)(struct input_ctx *); + const struct input_transition *transitions; +}; + +/* State transitions available from all states. */ +#define INPUT_STATE_ANYWHERE \ + { 0x18, 0x18, input_c0_dispatch, &input_state_ground }, \ + { 0x1a, 0x1a, input_c0_dispatch, &input_state_ground }, \ + { 0x1b, 0x1b, NULL, &input_state_esc_enter } + +/* Forward declarations of state tables. */ +const struct input_transition input_state_ground_table[]; +const struct input_transition input_state_esc_enter_table[]; +const struct input_transition input_state_esc_intermediate_table[]; +const struct input_transition input_state_csi_enter_table[]; +const struct input_transition input_state_csi_parameter_table[]; +const struct input_transition input_state_csi_intermediate_table[]; +const struct input_transition input_state_csi_ignore_table[]; +const struct input_transition input_state_dcs_enter_table[]; +const struct input_transition input_state_dcs_parameter_table[]; +const struct input_transition input_state_dcs_intermediate_table[]; +const struct input_transition input_state_dcs_handler_table[]; +const struct input_transition input_state_dcs_escape_table[]; +const struct input_transition input_state_dcs_ignore_table[]; +const struct input_transition input_state_osc_string_table[]; +const struct input_transition input_state_apc_string_table[]; +const struct input_transition input_state_rename_string_table[]; +const struct input_transition input_state_consume_st_table[]; +const struct input_transition input_state_utf8_three_table[]; +const struct input_transition input_state_utf8_two_table[]; +const struct input_transition input_state_utf8_one_table[]; + +/* ground state definition. */ +const struct input_state input_state_ground = { + "ground", + NULL, NULL, + input_state_ground_table +}; + +/* esc_enter state definition. */ +const struct input_state input_state_esc_enter = { + "esc_enter", + input_clear, NULL, + input_state_esc_enter_table +}; + +/* esc_intermediate state definition. */ +const struct input_state input_state_esc_intermediate = { + "esc_intermediate", + NULL, NULL, + input_state_esc_intermediate_table +}; + +/* csi_enter state definition. */ +const struct input_state input_state_csi_enter = { + "csi_enter", + input_clear, NULL, + input_state_csi_enter_table +}; + +/* csi_parameter state definition. */ +const struct input_state input_state_csi_parameter = { + "csi_parameter", + NULL, NULL, + input_state_csi_parameter_table +}; + +/* csi_intermediate state definition. */ +const struct input_state input_state_csi_intermediate = { + "csi_intermediate", + NULL, NULL, + input_state_csi_intermediate_table +}; + +/* csi_ignore state definition. */ +const struct input_state input_state_csi_ignore = { + "csi_ignore", + NULL, NULL, + input_state_csi_ignore_table +}; + +/* dcs_enter state definition. */ +const struct input_state input_state_dcs_enter = { + "dcs_enter", + input_clear, NULL, + input_state_dcs_enter_table +}; + +/* dcs_parameter state definition. */ +const struct input_state input_state_dcs_parameter = { + "dcs_parameter", + NULL, NULL, + input_state_dcs_parameter_table +}; + +/* dcs_intermediate state definition. */ +const struct input_state input_state_dcs_intermediate = { + "dcs_intermediate", + NULL, NULL, + input_state_dcs_intermediate_table +}; + +/* dcs_handler state definition. */ +const struct input_state input_state_dcs_handler = { + "dcs_handler", + NULL, NULL, + input_state_dcs_handler_table +}; + +/* dcs_escape state definition. */ +const struct input_state input_state_dcs_escape = { + "dcs_escape", + NULL, NULL, + input_state_dcs_escape_table +}; + +/* dcs_ignore state definition. */ +const struct input_state input_state_dcs_ignore = { + "dcs_ignore", + NULL, NULL, + input_state_dcs_ignore_table +}; + +/* osc_string state definition. */ +const struct input_state input_state_osc_string = { + "osc_string", + input_enter_osc, input_exit_osc, + input_state_osc_string_table +}; + +/* apc_string state definition. */ +const struct input_state input_state_apc_string = { + "apc_string", + input_enter_apc, input_exit_apc, + input_state_apc_string_table +}; + +/* rename_string state definition. */ +const struct input_state input_state_rename_string = { + "rename_string", + input_enter_rename, input_exit_rename, + input_state_rename_string_table +}; + +/* consume_st state definition. */ +const struct input_state input_state_consume_st = { + "consume_st", + NULL, NULL, + input_state_consume_st_table +}; + +/* utf8_three state definition. */ +const struct input_state input_state_utf8_three = { + "utf8_three", + NULL, NULL, + input_state_utf8_three_table +}; + +/* utf8_two state definition. */ +const struct input_state input_state_utf8_two = { + "utf8_two", + NULL, NULL, + input_state_utf8_two_table +}; + +/* utf8_one state definition. */ +const struct input_state input_state_utf8_one = { + "utf8_one", + NULL, NULL, + input_state_utf8_one_table +}; + +/* ground state table. */ +const struct input_transition input_state_ground_table[] = { + INPUT_STATE_ANYWHERE, + + { 0x00, 0x17, input_c0_dispatch, NULL }, + { 0x19, 0x19, input_c0_dispatch, NULL }, + { 0x1c, 0x1f, input_c0_dispatch, NULL }, + { 0x20, 0x7e, input_print, NULL }, + { 0x7f, 0x7f, NULL, NULL }, + { 0x80, 0xc1, input_print, NULL }, + { 0xc2, 0xdf, input_utf8_open, &input_state_utf8_one }, + { 0xe0, 0xef, input_utf8_open, &input_state_utf8_two }, + { 0xf0, 0xf4, input_utf8_open, &input_state_utf8_three }, + { 0xf5, 0xff, input_print, NULL }, + + { -1, -1, NULL, NULL } +}; + +/* esc_enter state table. */ +const struct input_transition input_state_esc_enter_table[] = { + INPUT_STATE_ANYWHERE, + + { 0x00, 0x17, input_c0_dispatch, NULL }, + { 0x19, 0x19, input_c0_dispatch, NULL }, + { 0x1c, 0x1f, input_c0_dispatch, NULL }, + { 0x20, 0x2f, input_intermediate, &input_state_esc_intermediate }, + { 0x30, 0x4f, input_esc_dispatch, &input_state_ground }, + { 0x50, 0x50, NULL, &input_state_dcs_enter }, + { 0x51, 0x57, input_esc_dispatch, &input_state_ground }, + { 0x58, 0x58, NULL, &input_state_consume_st }, + { 0x59, 0x59, input_esc_dispatch, &input_state_ground }, + { 0x5a, 0x5a, input_esc_dispatch, &input_state_ground }, + { 0x5b, 0x5b, NULL, &input_state_csi_enter }, + { 0x5c, 0x5c, input_esc_dispatch, &input_state_ground }, + { 0x5d, 0x5d, NULL, &input_state_osc_string }, + { 0x5e, 0x5e, NULL, &input_state_consume_st }, + { 0x5f, 0x5f, NULL, &input_state_apc_string }, + { 0x60, 0x6a, input_esc_dispatch, &input_state_ground }, + { 0x6b, 0x6b, NULL, &input_state_rename_string }, + { 0x6c, 0x7e, input_esc_dispatch, &input_state_ground }, + { 0x7f, 0xff, NULL, NULL }, + + { -1, -1, NULL, NULL } +}; + +/* esc_interm state table. */ +const struct input_transition input_state_esc_intermediate_table[] = { + INPUT_STATE_ANYWHERE, + + { 0x00, 0x17, input_c0_dispatch, NULL }, + { 0x19, 0x19, input_c0_dispatch, NULL }, + { 0x1c, 0x1f, input_c0_dispatch, NULL }, + { 0x20, 0x2f, input_intermediate, NULL }, + { 0x30, 0x7e, input_esc_dispatch, &input_state_ground }, + { 0x7f, 0xff, NULL, NULL }, + + { -1, -1, NULL, NULL } +}; + +/* csi_enter state table. */ +const struct input_transition input_state_csi_enter_table[] = { + INPUT_STATE_ANYWHERE, + + { 0x00, 0x17, input_c0_dispatch, NULL }, + { 0x19, 0x19, input_c0_dispatch, NULL }, + { 0x1c, 0x1f, input_c0_dispatch, NULL }, + { 0x20, 0x2f, input_intermediate, &input_state_csi_intermediate }, + { 0x30, 0x39, input_parameter, &input_state_csi_parameter }, + { 0x3a, 0x3a, NULL, &input_state_csi_ignore }, + { 0x3b, 0x3b, input_parameter, &input_state_csi_parameter }, + { 0x3c, 0x3f, input_intermediate, &input_state_csi_parameter }, + { 0x40, 0x7e, input_csi_dispatch, &input_state_ground }, + { 0x7f, 0xff, NULL, NULL }, + + { -1, -1, NULL, NULL } +}; + +/* csi_parameter state table. */ +const struct input_transition input_state_csi_parameter_table[] = { + INPUT_STATE_ANYWHERE, + + { 0x00, 0x17, input_c0_dispatch, NULL }, + { 0x19, 0x19, input_c0_dispatch, NULL }, + { 0x1c, 0x1f, input_c0_dispatch, NULL }, + { 0x20, 0x2f, input_intermediate, &input_state_csi_intermediate }, + { 0x30, 0x39, input_parameter, NULL }, + { 0x3a, 0x3a, NULL, &input_state_csi_ignore }, + { 0x3b, 0x3b, input_parameter, NULL }, + { 0x3c, 0x3f, NULL, &input_state_csi_ignore }, + { 0x40, 0x7e, input_csi_dispatch, &input_state_ground }, + { 0x7f, 0xff, NULL, NULL }, + + { -1, -1, NULL, NULL } +}; + +/* csi_intermediate state table. */ +const struct input_transition input_state_csi_intermediate_table[] = { + INPUT_STATE_ANYWHERE, + + { 0x00, 0x17, input_c0_dispatch, NULL }, + { 0x19, 0x19, input_c0_dispatch, NULL }, + { 0x1c, 0x1f, input_c0_dispatch, NULL }, + { 0x20, 0x2f, input_intermediate, NULL }, + { 0x30, 0x3f, NULL, &input_state_csi_ignore }, + { 0x40, 0x7e, input_csi_dispatch, &input_state_ground }, + { 0x7f, 0xff, NULL, NULL }, + + { -1, -1, NULL, NULL } +}; + +/* csi_ignore state table. */ +const struct input_transition input_state_csi_ignore_table[] = { + INPUT_STATE_ANYWHERE, + + { 0x00, 0x17, input_c0_dispatch, NULL }, + { 0x19, 0x19, input_c0_dispatch, NULL }, + { 0x1c, 0x1f, input_c0_dispatch, NULL }, + { 0x20, 0x3f, NULL, NULL }, + { 0x40, 0x7e, NULL, &input_state_ground }, + { 0x7f, 0xff, NULL, NULL }, + + { -1, -1, NULL, NULL } +}; + +/* dcs_enter state table. */ +const struct input_transition input_state_dcs_enter_table[] = { + INPUT_STATE_ANYWHERE, + + { 0x00, 0x17, NULL, NULL }, + { 0x19, 0x19, NULL, NULL }, + { 0x1c, 0x1f, NULL, NULL }, + { 0x20, 0x2f, input_intermediate, &input_state_dcs_intermediate }, + { 0x30, 0x39, input_parameter, &input_state_dcs_parameter }, + { 0x3a, 0x3a, NULL, &input_state_dcs_ignore }, + { 0x3b, 0x3b, input_parameter, &input_state_dcs_parameter }, + { 0x3c, 0x3f, input_intermediate, &input_state_dcs_parameter }, + { 0x40, 0x7e, input_input, &input_state_dcs_handler }, + { 0x7f, 0xff, NULL, NULL }, + + { -1, -1, NULL, NULL } +}; + +/* dcs_parameter state table. */ +const struct input_transition input_state_dcs_parameter_table[] = { + INPUT_STATE_ANYWHERE, + + { 0x00, 0x17, NULL, NULL }, + { 0x19, 0x19, NULL, NULL }, + { 0x1c, 0x1f, NULL, NULL }, + { 0x20, 0x2f, input_intermediate, &input_state_dcs_intermediate }, + { 0x30, 0x39, input_parameter, NULL }, + { 0x3a, 0x3a, NULL, &input_state_dcs_ignore }, + { 0x3b, 0x3b, input_parameter, NULL }, + { 0x3c, 0x3f, NULL, &input_state_dcs_ignore }, + { 0x40, 0x7e, input_input, &input_state_dcs_handler }, + { 0x7f, 0xff, NULL, NULL }, + + { -1, -1, NULL, NULL } +}; + +/* dcs_interm state table. */ +const struct input_transition input_state_dcs_intermediate_table[] = { + INPUT_STATE_ANYWHERE, + + { 0x00, 0x17, NULL, NULL }, + { 0x19, 0x19, NULL, NULL }, + { 0x1c, 0x1f, NULL, NULL }, + { 0x20, 0x2f, input_intermediate, NULL }, + { 0x30, 0x3f, NULL, &input_state_dcs_ignore }, + { 0x40, 0x7e, input_input, &input_state_dcs_handler }, + { 0x7f, 0xff, NULL, NULL }, + + { -1, -1, NULL, NULL } +}; + +/* dcs_handler state table. */ +const struct input_transition input_state_dcs_handler_table[] = { + /* No INPUT_STATE_ANYWHERE */ + + { 0x00, 0x1a, input_input, NULL }, + { 0x1b, 0x1b, NULL, &input_state_dcs_escape }, + { 0x1c, 0xff, input_input, NULL }, + + { -1, -1, NULL, NULL } +}; + +/* dcs_escape state table. */ +const struct input_transition input_state_dcs_escape_table[] = { + /* No INPUT_STATE_ANYWHERE */ + + { 0x00, 0x5b, input_input, &input_state_dcs_handler }, + { 0x5c, 0x5c, input_dcs_dispatch, &input_state_ground }, + { 0x5d, 0xff, input_input, &input_state_dcs_handler }, + + { -1, -1, NULL, NULL } +}; + +/* dcs_ignore state table. */ +const struct input_transition input_state_dcs_ignore_table[] = { + INPUT_STATE_ANYWHERE, + + { 0x00, 0x17, NULL, NULL }, + { 0x19, 0x19, NULL, NULL }, + { 0x1c, 0x1f, NULL, NULL }, + { 0x20, 0xff, NULL, NULL }, + + { -1, -1, NULL, NULL } +}; + +/* osc_string state table. */ +const struct input_transition input_state_osc_string_table[] = { + INPUT_STATE_ANYWHERE, + + { 0x00, 0x06, NULL, NULL }, + { 0x07, 0x07, NULL, &input_state_ground }, + { 0x08, 0x17, NULL, NULL }, + { 0x19, 0x19, NULL, NULL }, + { 0x1c, 0x1f, NULL, NULL }, + { 0x20, 0xff, input_input, NULL }, + + { -1, -1, NULL, NULL } +}; + +/* apc_string state table. */ +const struct input_transition input_state_apc_string_table[] = { + INPUT_STATE_ANYWHERE, + + { 0x00, 0x17, NULL, NULL }, + { 0x19, 0x19, NULL, NULL }, + { 0x1c, 0x1f, NULL, NULL }, + { 0x20, 0xff, input_input, NULL }, + + { -1, -1, NULL, NULL } +}; + +/* rename_string state table. */ +const struct input_transition input_state_rename_string_table[] = { + INPUT_STATE_ANYWHERE, + + { 0x00, 0x17, NULL, NULL }, + { 0x19, 0x19, NULL, NULL }, + { 0x1c, 0x1f, NULL, NULL }, + { 0x20, 0xff, input_input, NULL }, + + { -1, -1, NULL, NULL } +}; + +/* consume_st state table. */ +const struct input_transition input_state_consume_st_table[] = { + INPUT_STATE_ANYWHERE, + + { 0x00, 0x17, NULL, NULL }, + { 0x19, 0x19, NULL, NULL }, + { 0x1c, 0x1f, NULL, NULL }, + { 0x20, 0xff, NULL, NULL }, + + { -1, -1, NULL, NULL } +}; + +/* utf8_three state table. */ +const struct input_transition input_state_utf8_three_table[] = { + /* No INPUT_STATE_ANYWHERE */ + + { 0x00, 0x7f, NULL, &input_state_ground }, + { 0x80, 0xbf, input_utf8_add, &input_state_utf8_two }, + { 0xc0, 0xff, NULL, &input_state_ground }, + + { -1, -1, NULL, NULL } +}; + +/* utf8_two state table. */ +const struct input_transition input_state_utf8_two_table[] = { + /* No INPUT_STATE_ANYWHERE */ + + { 0x00, 0x7f, NULL, &input_state_ground }, + { 0x80, 0xbf, input_utf8_add, &input_state_utf8_one }, + { 0xc0, 0xff, NULL, &input_state_ground }, + + { -1, -1, NULL, NULL } +}; + +/* utf8_one state table. */ +const struct input_transition input_state_utf8_one_table[] = { + /* No INPUT_STATE_ANYWHERE */ + + { 0x00, 0x7f, NULL, &input_state_ground }, + { 0x80, 0xbf, input_utf8_close, &input_state_ground }, + { 0xc0, 0xff, NULL, &input_state_ground }, + + { -1, -1, NULL, NULL } +}; + +/* Input table compare. */ +int +input_table_compare(const void *key, const void *value) +{ + const struct input_ctx *ictx = key; + const struct input_table_entry *entry = value; + + if (ictx->ch != entry->ch) + return (ictx->ch - entry->ch); + return (strcmp((const char *)ictx->interm_buf, entry->interm)); +} + +/* Initialise input parser. */ +void +input_init(struct window_pane *wp) +{ + struct input_ctx *ictx = &wp->ictx; + + memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell); + + memcpy(&ictx->old_cell, &grid_default_cell, sizeof ictx->old_cell); + ictx->old_cx = 0; + ictx->old_cy = 0; + + *ictx->interm_buf = '\0'; + ictx->interm_len = 0; + + *ictx->param_buf = '\0'; + ictx->param_len = 0; + + ictx->state = &input_state_ground; + ictx->flags = 0; +} + +/* Destroy input parser. */ +void +input_free(unused struct window_pane *wp) +{ +} + +/* Parse input. */ +void +input_parse(struct window_pane *wp) +{ + struct input_ctx *ictx = &wp->ictx; + const struct input_transition *itr; + struct evbuffer *evb = wp->event->input; + u_char *buf; + size_t len, off; + + if (EVBUFFER_LENGTH(evb) == 0) + return; + + wp->window->flags |= WINDOW_ACTIVITY; + wp->window->flags &= ~WINDOW_SILENCE; + + /* + * Open the screen. Use NULL wp if there is a mode set as don't want to + * update the tty. + */ + if (wp->mode == NULL) + screen_write_start(&ictx->ctx, wp, &wp->base); + else + screen_write_start(&ictx->ctx, NULL, &wp->base); + ictx->wp = wp; + + buf = EVBUFFER_DATA(evb); + len = EVBUFFER_LENGTH(evb); + off = 0; + + /* Parse the input. */ + while (off < len) { + ictx->ch = buf[off++]; + log_debug("%s: '%c' %s", __func__, ictx->ch, ictx->state->name); + + /* Find the transition. */ + itr = ictx->state->transitions; + while (itr->first != -1 && itr->last != -1) { + if (ictx->ch >= itr->first && ictx->ch <= itr->last) + break; + itr++; + } + if (itr->first == -1 || itr->last == -1) { + /* No transition? Eh? */ + fatalx("No transition from state!"); + } + + /* + * Execute the handler, if any. Don't switch state if it + * returns non-zero. + */ + if (itr->handler != NULL && itr->handler(ictx) != 0) + continue; + + /* And switch state, if necessary. */ + if (itr->state != NULL) { + if (ictx->state->exit != NULL) + ictx->state->exit(ictx); + ictx->state = itr->state; + if (ictx->state->enter != NULL) + ictx->state->enter(ictx); + } + } + + /* Close the screen. */ + screen_write_stop(&ictx->ctx); + + evbuffer_drain(evb, len); +} + +/* Split the parameter list (if any). */ +int +input_split(struct input_ctx *ictx) + +{ + const char *errstr; + char *ptr, *out; + int n; + + ictx->param_list_len = 0; + if (ictx->param_len == 0) + return (0); + + ptr = (char *)ictx->param_buf; + while ((out = strsep(&ptr, ";")) != NULL) { + if (*out == '\0') + n = -1; + else { + n = strtonum(out, 0, INT_MAX, &errstr); + if (errstr != NULL) + return (-1); + } + + ictx->param_list[ictx->param_list_len++] = n; + if (ictx->param_list_len == nitems(ictx->param_list)) + return (-1); + } + + return (0); +} + +/* Get an argument or return default value. */ +int +input_get(struct input_ctx *ictx, u_int validx, int minval, int defval) +{ + int retval; + + if (validx >= ictx->param_list_len) + return (defval); + + retval = ictx->param_list[validx]; + if (retval == -1) + return (defval); + if (retval < minval) + return (minval); + return (retval); +} + +/* Reply to terminal query. */ +void +input_reply(struct input_ctx *ictx, const char *fmt, ...) +{ + va_list ap; + char *reply; + + va_start(ap, fmt); + vasprintf(&reply, fmt, ap); + va_end(ap); + + bufferevent_write(ictx->wp->event, reply, strlen(reply)); + xfree(reply); +} + +/* Clear saved state. */ +void +input_clear(struct input_ctx *ictx) +{ + *ictx->interm_buf = '\0'; + ictx->interm_len = 0; + + *ictx->param_buf = '\0'; + ictx->param_len = 0; + + *ictx->input_buf = '\0'; + ictx->input_len = 0; + + ictx->flags &= ~INPUT_DISCARD; +} + +/* Output this character to the screen. */ +int +input_print(struct input_ctx *ictx) +{ + ictx->cell.data = ictx->ch; + screen_write_cell(&ictx->ctx, &ictx->cell, NULL); + + return (0); +} + +/* Collect intermediate string. */ +int +input_intermediate(struct input_ctx *ictx) +{ + if (ictx->interm_len == (sizeof ictx->interm_buf) - 1) + ictx->flags |= INPUT_DISCARD; + else { + ictx->interm_buf[ictx->interm_len++] = ictx->ch; + ictx->interm_buf[ictx->interm_len] = '\0'; + } + + return (0); +} + +/* Collect parameter string. */ +int +input_parameter(struct input_ctx *ictx) +{ + if (ictx->param_len == (sizeof ictx->param_buf) - 1) + ictx->flags |= INPUT_DISCARD; + else { + ictx->param_buf[ictx->param_len++] = ictx->ch; + ictx->param_buf[ictx->param_len] = '\0'; + } + + return (0); +} + +/* Collect input string. */ +int +input_input(struct input_ctx *ictx) +{ + if (ictx->input_len == (sizeof ictx->input_buf) - 1) + ictx->flags |= INPUT_DISCARD; + else { + ictx->input_buf[ictx->input_len++] = ictx->ch; + ictx->input_buf[ictx->input_len] = '\0'; + } + + return (0); +} + +/* Execute C0 control sequence. */ +int +input_c0_dispatch(struct input_ctx *ictx) +{ + struct screen_write_ctx *sctx = &ictx->ctx; + struct window_pane *wp = ictx->wp; + struct screen *s = sctx->s; + + log_debug("%s: '%c", __func__, ictx->ch); + + switch (ictx->ch) { + case '\000': /* NUL */ + break; + case '\007': /* BEL */ + wp->window->flags |= WINDOW_BELL; + break; + case '\010': /* BS */ + screen_write_backspace(sctx); + break; + case '\011': /* HT */ + /* Don't tab beyond the end of the line. */ + if (s->cx >= screen_size_x(s) - 1) + break; + + /* Find the next tab point, or use the last column if none. */ + do { + s->cx++; + if (bit_test(s->tabs, s->cx)) + break; + } while (s->cx < screen_size_x(s) - 1); + break; + case '\012': /* LF */ + case '\013': /* VT */ + case '\014': /* FF */ + screen_write_linefeed(sctx, 0); + break; + case '\015': /* CR */ + screen_write_carriagereturn(sctx); + break; + case '\016': /* SO */ + ictx->cell.attr |= GRID_ATTR_CHARSET; + break; + case '\017': /* SI */ + ictx->cell.attr &= ~GRID_ATTR_CHARSET; + break; + default: + log_debug("%s: unknown '%c'", __func__, ictx->ch); + break; + } + + return (0); +} + +/* Execute escape sequence. */ +int +input_esc_dispatch(struct input_ctx *ictx) +{ + struct screen_write_ctx *sctx = &ictx->ctx; + struct screen *s = sctx->s; + struct input_table_entry *entry; + + if (ictx->flags & INPUT_DISCARD) + return (0); + log_debug("%s: '%c', %s", __func__, ictx->ch, ictx->interm_buf); + + entry = bsearch(ictx, input_esc_table, nitems(input_esc_table), + sizeof input_esc_table[0], input_table_compare); + if (entry == NULL) { + log_debug("%s: unknown '%c'", __func__, ictx->ch); + return (0); + } + + switch (entry->type) { + case INPUT_ESC_RIS: + memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell); + memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); + ictx->old_cx = 0; + ictx->old_cy = 0; + + screen_reset_tabs(sctx->s); + + screen_write_scrollregion(sctx, 0, screen_size_y(sctx->s) - 1); + + screen_write_insertmode(sctx, 0); + screen_write_kcursormode(sctx, 0); + screen_write_kkeypadmode(sctx, 0); + screen_write_mousemode_off(sctx); + + screen_write_clearscreen(sctx); + screen_write_cursormove(sctx, 0, 0); + break; + case INPUT_ESC_IND: + screen_write_linefeed(sctx, 0); + break; + case INPUT_ESC_NEL: + screen_write_carriagereturn(sctx); + screen_write_linefeed(sctx, 0); + break; + case INPUT_ESC_HTS: + if (s->cx < screen_size_x(s)) + bit_set(s->tabs, s->cx); + break; + case INPUT_ESC_RI: + screen_write_reverseindex(sctx); + break; + case INPUT_ESC_DECKPAM: + screen_write_kkeypadmode(sctx, 1); + break; + case INPUT_ESC_DECKPNM: + screen_write_kkeypadmode(sctx, 0); + break; + case INPUT_ESC_DECSC: + memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); + ictx->old_cx = s->cx; + ictx->old_cy = s->cy; + break; + case INPUT_ESC_DECRC: + memcpy(&ictx->cell, &ictx->old_cell, sizeof ictx->cell); + screen_write_cursormove(sctx, ictx->old_cx, ictx->old_cy); + break; + case INPUT_ESC_DECALN: + screen_write_alignmenttest(sctx); + break; + case INPUT_ESC_SCSON_G0: + /* + * Not really supported, but fake it up enough for those that + * use it to switch character sets (by redefining G0 to + * graphics set, rather than switching to G1). + */ + ictx->cell.attr &= ~GRID_ATTR_CHARSET; + break; + case INPUT_ESC_SCSOFF_G0: + ictx->cell.attr |= GRID_ATTR_CHARSET; + break; + } + + return (0); +} + +/* Execute control sequence. */ +int +input_csi_dispatch(struct input_ctx *ictx) +{ + struct screen_write_ctx *sctx = &ictx->ctx; + struct window_pane *wp = ictx->wp; + struct screen *s = sctx->s; + struct input_table_entry *entry; + int n, m; + + if (ictx->flags & INPUT_DISCARD) + return (0); + if (input_split(ictx) != 0) + return (0); + log_debug("%s: '%c' \"%s\" \"%s\"", + __func__, ictx->ch, ictx->interm_buf, ictx->param_buf); + + entry = bsearch(ictx, input_csi_table, nitems(input_csi_table), + sizeof input_csi_table[0], input_table_compare); + if (entry == NULL) { + log_debug("%s: unknown '%c'", __func__, ictx->ch); + return (0); + } + + switch (entry->type) { + case INPUT_CSI_CBT: + /* Find the previous tab point, n times. */ + n = input_get(ictx, 0, 1, 1); + while (s->cx > 0 && n-- > 0) { + do + s->cx--; + while (s->cx > 0 && !bit_test(s->tabs, s->cx)); + } + break; + case INPUT_CSI_CUB: + screen_write_cursorleft(sctx, input_get(ictx, 0, 1, 1)); + break; + case INPUT_CSI_CUD: + screen_write_cursordown(sctx, input_get(ictx, 0, 1, 1)); + break; + case INPUT_CSI_CUF: + screen_write_cursorright(sctx, input_get(ictx, 0, 1, 1)); + break; + case INPUT_CSI_CUP: + n = input_get(ictx, 0, 1, 1); + m = input_get(ictx, 1, 1, 1); + screen_write_cursormove(sctx, m - 1, n - 1); + break; + case INPUT_CSI_CUU: + screen_write_cursorup(sctx, input_get(ictx, 0, 1, 1)); + break; + case INPUT_CSI_DA: + switch (input_get(ictx, 0, 0, 0)) { + case 0: + input_reply(ictx, "\033[?1;2c"); + break; + default: + log_debug("%s: unknown '%c'", __func__, ictx->ch); + break; + } + break; + case INPUT_CSI_DCH: + screen_write_deletecharacter(sctx, input_get(ictx, 0, 1, 1)); + break; + case INPUT_CSI_DECSTBM: + n = input_get(ictx, 0, 1, 1); + m = input_get(ictx, 1, 1, screen_size_y(s)); + screen_write_scrollregion(sctx, n - 1, m - 1); + break; + case INPUT_CSI_DL: + screen_write_deleteline(sctx, input_get(ictx, 0, 1, 1)); + break; + case INPUT_CSI_DSR: + switch (input_get(ictx, 0, 0, 0)) { + case 5: + input_reply(ictx, "\033[0n"); + break; + case 6: + input_reply(ictx, "\033[%u;%uR", s->cy + 1, s->cx + 1); + break; + default: + log_debug("%s: unknown '%c'", __func__, ictx->ch); + break; + } + break; + case INPUT_CSI_ED: + switch (input_get(ictx, 0, 0, 0)) { + case 0: + screen_write_clearendofscreen(sctx); + break; + case 1: + screen_write_clearstartofscreen(sctx); + break; + case 2: + screen_write_clearscreen(sctx); + break; + default: + log_debug("%s: unknown '%c'", __func__, ictx->ch); + break; + } + break; + case INPUT_CSI_EL: + switch (input_get(ictx, 0, 0, 0)) { + case 0: + screen_write_clearendofline(sctx); + break; + case 1: + screen_write_clearstartofline(sctx); + break; + case 2: + screen_write_clearline(sctx); + break; + default: + log_debug("%s: unknown '%c'", __func__, ictx->ch); + break; + } + break; + case INPUT_CSI_HPA: + n = input_get(ictx, 0, 1, 1); + screen_write_cursormove(sctx, n - 1, s->cy); + break; + case INPUT_CSI_ICH: + screen_write_insertcharacter(sctx, input_get(ictx, 0, 1, 1)); + break; + case INPUT_CSI_IL: + screen_write_insertline(sctx, input_get(ictx, 0, 1, 1)); + break; + case INPUT_CSI_RM: + switch (input_get(ictx, 0, 0, -1)) { + case 4: /* IRM */ + screen_write_insertmode(&ictx->ctx, 0); + break; + default: + log_debug("%s: unknown '%c'", __func__, ictx->ch); + break; + } + break; + case INPUT_CSI_RM_PRIVATE: + switch (input_get(ictx, 0, 0, -1)) { + case 1: /* GATM */ + screen_write_kcursormode(&ictx->ctx, 0); + break; + case 3: /* DECCOLM */ + screen_write_cursormove(&ictx->ctx, 0, 0); + screen_write_clearscreen(&ictx->ctx); + break; + case 25: /* TCEM */ + screen_write_cursormode(&ictx->ctx, 0); + break; + case 1000: + case 1001: + case 1002: + case 1003: + screen_write_mousemode_off(&ictx->ctx); + break; + case 1005: + screen_write_utf8mousemode(&ictx->ctx, 0); + break; + case 1049: + window_pane_alternate_off(wp, &ictx->cell); + break; + default: + log_debug("%s: unknown '%c'", __func__, ictx->ch); + break; + } + break; + case INPUT_CSI_SGR: + input_csi_dispatch_sgr(ictx); + break; + case INPUT_CSI_SM: + switch (input_get(ictx, 0, 0, -1)) { + case 4: /* IRM */ + screen_write_insertmode(&ictx->ctx, 1); + break; + default: + log_debug("%s: unknown '%c'", __func__, ictx->ch); + break; + } + break; + case INPUT_CSI_SM_PRIVATE: + switch (input_get(ictx, 0, 0, -1)) { + case 1: /* GATM */ + screen_write_kcursormode(&ictx->ctx, 1); + break; + case 3: /* DECCOLM */ + screen_write_cursormove(&ictx->ctx, 0, 0); + screen_write_clearscreen(&ictx->ctx); + break; + case 25: /* TCEM */ + screen_write_cursormode(&ictx->ctx, 1); + break; + case 1000: + screen_write_mousemode_on( + &ictx->ctx, MODE_MOUSE_STANDARD); + break; + case 1002: + screen_write_mousemode_on( + &ictx->ctx, MODE_MOUSE_BUTTON); + break; + case 1003: + screen_write_mousemode_on(&ictx->ctx, MODE_MOUSE_ANY); + break; + case 1005: + screen_write_utf8mousemode(&ictx->ctx, 1); + break; + case 1049: + window_pane_alternate_on(wp, &ictx->cell); + break; + default: + log_debug("%s: unknown '%c'", __func__, ictx->ch); + break; + } + break; + case INPUT_CSI_TBC: + switch (input_get(ictx, 0, 0, 0)) { + case 0: + if (s->cx < screen_size_x(s)) + bit_clear(s->tabs, s->cx); + break; + case 3: + bit_nclear(s->tabs, 0, screen_size_x(s) - 1); + break; + default: + log_debug("%s: unknown '%c'", __func__, ictx->ch); + break; + } + break; + case INPUT_CSI_VPA: + n = input_get(ictx, 0, 1, 1); + screen_write_cursormove(sctx, s->cx, n - 1); + break; + case INPUT_CSI_DECSCUSR: + n = input_get(ictx, 0, 0, 0); + screen_set_cursor_style(s, n); + break; + } + + return (0); +} + +/* Handle CSI SGR. */ +void +input_csi_dispatch_sgr(struct input_ctx *ictx) +{ + struct grid_cell *gc = &ictx->cell; + u_int i; + int n, m; + u_char attr; + + if (ictx->param_list_len == 0) { + attr = gc->attr; + memcpy(gc, &grid_default_cell, sizeof *gc); + gc->attr |= (attr & GRID_ATTR_CHARSET); + return; + } + + for (i = 0; i < ictx->param_list_len; i++) { + n = input_get(ictx, i, 0, 0); + + if (n == 38 || n == 48) { + i++; + if (input_get(ictx, i, 0, -1) != 5) + continue; + + i++; + m = input_get(ictx, i, 0, -1); + if (m == -1) { + if (n == 38) { + gc->flags &= ~GRID_FLAG_FG256; + gc->fg = 8; + } else if (n == 48) { + gc->flags &= ~GRID_FLAG_BG256; + gc->bg = 8; + } + + } else { + if (n == 38) { + gc->flags |= GRID_FLAG_FG256; + gc->fg = m; + } else if (n == 48) { + gc->flags |= GRID_FLAG_BG256; + gc->bg = m; + } + } + continue; + } + + switch (n) { + case 0: + case 10: + attr = gc->attr; + memcpy(gc, &grid_default_cell, sizeof *gc); + gc->attr |= (attr & GRID_ATTR_CHARSET); + break; + case 1: + gc->attr |= GRID_ATTR_BRIGHT; + break; + case 2: + gc->attr |= GRID_ATTR_DIM; + break; + case 3: + gc->attr |= GRID_ATTR_ITALICS; + break; + case 4: + gc->attr |= GRID_ATTR_UNDERSCORE; + break; + case 5: + gc->attr |= GRID_ATTR_BLINK; + break; + case 7: + gc->attr |= GRID_ATTR_REVERSE; + break; + case 8: + gc->attr |= GRID_ATTR_HIDDEN; + break; + case 22: + gc->attr &= ~(GRID_ATTR_BRIGHT|GRID_ATTR_DIM); + break; + case 23: + gc->attr &= ~GRID_ATTR_ITALICS; + break; + case 24: + gc->attr &= ~GRID_ATTR_UNDERSCORE; + break; + case 25: + gc->attr &= ~GRID_ATTR_BLINK; + break; + case 27: + gc->attr &= ~GRID_ATTR_REVERSE; + break; + case 30: + case 31: + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + gc->flags &= ~GRID_FLAG_FG256; + gc->fg = n - 30; + break; + case 39: + gc->flags &= ~GRID_FLAG_FG256; + gc->fg = 8; + break; + case 40: + case 41: + case 42: + case 43: + case 44: + case 45: + case 46: + case 47: + gc->flags &= ~GRID_FLAG_BG256; + gc->bg = n - 40; + break; + case 49: + gc->flags &= ~GRID_FLAG_BG256; + gc->bg = 8; + break; + case 90: + case 91: + case 92: + case 93: + case 94: + case 95: + case 96: + case 97: + gc->flags &= ~GRID_FLAG_FG256; + gc->fg = n; + break; + case 100: + case 101: + case 102: + case 103: + case 104: + case 105: + case 106: + case 107: + gc->flags &= ~GRID_FLAG_BG256; + gc->bg = n; + break; + } + } +} + +/* DCS terminator (ST) received. */ +int +input_dcs_dispatch(struct input_ctx *ictx) +{ + const char prefix[] = "tmux;"; + const u_int prefix_len = (sizeof prefix) - 1; + + if (ictx->flags & INPUT_DISCARD) + return (0); + + log_debug("%s: \"%s\"", __func__, ictx->input_buf); + + /* Check for tmux prefix. */ + if (ictx->input_len >= prefix_len && + strncmp((const char *)ictx->input_buf, prefix, prefix_len) == 0) { + screen_write_rawstring(&ictx->ctx, + ictx->input_buf + prefix_len, ictx->input_len - prefix_len); + } + + return (0); +} + +/* OSC string started. */ +void +input_enter_osc(struct input_ctx *ictx) +{ + log_debug("%s", __func__); + + input_clear(ictx); +} + +/* OSC terminator (ST) received. */ +void +input_exit_osc(struct input_ctx *ictx) +{ + u_char *p = ictx->input_buf; + int option; + + if (ictx->flags & INPUT_DISCARD) + return; + if (ictx->input_len < 1 || *p < '0' || *p > '9') + return; + + log_debug("%s: \"%s\"", __func__, p); + + option = 0; + while (*p >= '0' && *p <= '9') + option = option * 10 + *p++ - '0'; + if (*p == ';') + p++; + + switch (option) { + case 0: + case 2: + screen_set_title(ictx->ctx.s, (const char *)p); + server_status_window(ictx->wp->window); + break; + case 12: + screen_set_cursor_colour(ictx->ctx.s, (const char *)p); + break; + case 112: + if (*p == '\0') /* No arguments allowed. */ + screen_set_cursor_colour(ictx->ctx.s, ""); + break; + default: + log_debug("%s: unknown '%u'", __func__, option); + break; + } +} + +/* APC string started. */ +void +input_enter_apc(struct input_ctx *ictx) +{ + log_debug("%s", __func__); + + input_clear(ictx); +} + +/* APC terminator (ST) received. */ +void +input_exit_apc(struct input_ctx *ictx) +{ + if (ictx->flags & INPUT_DISCARD) + return; + log_debug("%s: \"%s\"", __func__, ictx->input_buf); + + screen_set_title(ictx->ctx.s, (const char *)ictx->input_buf); + server_status_window(ictx->wp->window); +} + +/* Rename string started. */ +void +input_enter_rename(struct input_ctx *ictx) +{ + log_debug("%s", __func__); + + input_clear(ictx); +} + +/* Rename terminator (ST) received. */ +void +input_exit_rename(struct input_ctx *ictx) +{ + if (ictx->flags & INPUT_DISCARD) + return; + log_debug("%s: \"%s\"", __func__, ictx->input_buf); + + xfree(ictx->wp->window->name); + ictx->wp->window->name = xstrdup((const char *)ictx->input_buf); + options_set_number(&ictx->wp->window->options, "automatic-rename", 0); + + server_status_window(ictx->wp->window); +} + +/* Open UTF-8 character. */ +int +input_utf8_open(struct input_ctx *ictx) +{ + if (!options_get_number(&ictx->wp->window->options, "utf8")) { + /* Print, and do not switch state. */ + input_print(ictx); + return (-1); + } + log_debug("%s", __func__); + + utf8_open(&ictx->utf8data, ictx->ch); + return (0); +} + +/* Append to UTF-8 character. */ +int +input_utf8_add(struct input_ctx *ictx) +{ + log_debug("%s", __func__); + + utf8_append(&ictx->utf8data, ictx->ch); + return (0); +} + +/* Close UTF-8 string. */ +int +input_utf8_close(struct input_ctx *ictx) +{ + log_debug("%s", __func__); + + utf8_append(&ictx->utf8data, ictx->ch); + + ictx->cell.flags |= GRID_FLAG_UTF8; + screen_write_cell(&ictx->ctx, &ictx->cell, &ictx->utf8data); + ictx->cell.flags &= ~GRID_FLAG_UTF8; + + return (0); +} diff --git a/external/bsd/tmux/dist/job.c b/external/bsd/tmux/dist/job.c new file mode 100644 index 000000000..897d84d31 --- /dev/null +++ b/external/bsd/tmux/dist/job.c @@ -0,0 +1,168 @@ +/* $Id: job.c,v 1.3 2011/08/17 18:48:36 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include +#include +#include + +#include "tmux.h" + +/* + * Job scheduling. Run queued commands in the background and record their + * output. + */ + +void job_callback(struct bufferevent *, short, void *); + +/* All jobs list. */ +struct joblist all_jobs = LIST_HEAD_INITIALIZER(all_jobs); + +/* Start a job running, if it isn't already. */ +struct job * +job_run(const char *cmd, + void (*callbackfn)(struct job *), void (*freefn)(void *), void *data) +{ + struct job *job; + struct environ env; + pid_t pid; + int nullfd, out[2]; + + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0) + return (NULL); + + environ_init(&env); + environ_copy(&global_environ, &env); + server_fill_environ(NULL, &env); + + switch (pid = fork()) { + case -1: + environ_free(&env); + return (NULL); + case 0: /* child */ + clear_signals(1); + + environ_push(&env); + environ_free(&env); + + if (dup2(out[1], STDOUT_FILENO) == -1) + fatal("dup2 failed"); + if (out[1] != STDOUT_FILENO) + close(out[1]); + close(out[0]); + + nullfd = open(_PATH_DEVNULL, O_RDWR, 0); + if (nullfd < 0) + fatal("open failed"); + if (dup2(nullfd, STDIN_FILENO) == -1) + fatal("dup2 failed"); + if (dup2(nullfd, STDERR_FILENO) == -1) + fatal("dup2 failed"); + if (nullfd != STDIN_FILENO && nullfd != STDERR_FILENO) + close(nullfd); + + closefrom(STDERR_FILENO + 1); + + execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL); + fatal("execl failed"); + } + + /* parent */ + environ_free(&env); + close(out[1]); + + job = xmalloc(sizeof *job); + job->cmd = xstrdup(cmd); + job->pid = pid; + job->status = 0; + + LIST_INSERT_HEAD(&all_jobs, job, lentry); + + job->callbackfn = callbackfn; + job->freefn = freefn; + job->data = data; + + job->fd = out[0]; + setblocking(job->fd, 0); + + job->event = bufferevent_new(job->fd, NULL, NULL, job_callback, job); + bufferevent_enable(job->event, EV_READ); + + log_debug("run job %p: %s, pid %ld", job, job->cmd, (long) job->pid); + return (job); +} + +/* Kill and free an individual job. */ +void +job_free(struct job *job) +{ + log_debug("free job %p: %s", job, job->cmd); + + LIST_REMOVE(job, lentry); + xfree(job->cmd); + + if (job->freefn != NULL && job->data != NULL) + job->freefn(job->data); + + if (job->pid != -1) + kill(job->pid, SIGTERM); + if (job->fd != -1) + close(job->fd); + if (job->event != NULL) + bufferevent_free(job->event); + + xfree(job); +} + +/* Job buffer error callback. */ +/* ARGSUSED */ +void +job_callback(unused struct bufferevent *bufev, unused short events, void *data) +{ + struct job *job = data; + + log_debug("job error %p: %s, pid %ld", job, job->cmd, (long) job->pid); + + if (job->pid == -1) { + if (job->callbackfn != NULL) + job->callbackfn(job); + job_free(job); + } else { + bufferevent_disable(job->event, EV_READ); + close(job->fd); + job->fd = -1; + } +} + +/* Job died (waitpid() returned its pid). */ +void +job_died(struct job *job, int status) +{ + log_debug("job died %p: %s, pid %ld", job, job->cmd, (long) job->pid); + + job->status = status; + + if (job->fd == -1) { + if (job->callbackfn != NULL) + job->callbackfn(job); + job_free(job); + } else + job->pid = -1; +} diff --git a/external/bsd/tmux/dist/key-bindings.c b/external/bsd/tmux/dist/key-bindings.c new file mode 100644 index 000000000..ba1fbbe0a --- /dev/null +++ b/external/bsd/tmux/dist/key-bindings.c @@ -0,0 +1,279 @@ +/* $Id: key-bindings.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include + +#include "tmux.h" + +SPLAY_GENERATE(key_bindings, key_binding, entry, key_bindings_cmp); + +struct key_bindings key_bindings; +struct key_bindings dead_key_bindings; + +int +key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2) +{ + int key1, key2; + + key1 = bd1->key & ~KEYC_PREFIX; + key2 = bd2->key & ~KEYC_PREFIX; + if (key1 != key2) + return (key1 - key2); + + if (bd1->key & KEYC_PREFIX && !(bd2->key & KEYC_PREFIX)) + return (-1); + if (bd2->key & KEYC_PREFIX && !(bd1->key & KEYC_PREFIX)) + return (1); + return (0); +} + +struct key_binding * +key_bindings_lookup(int key) +{ + struct key_binding bd; + + bd.key = key; + return (SPLAY_FIND(key_bindings, &key_bindings, &bd)); +} + +void +key_bindings_add(int key, int can_repeat, struct cmd_list *cmdlist) +{ + struct key_binding *bd; + + key_bindings_remove(key); + + bd = xmalloc(sizeof *bd); + bd->key = key; + SPLAY_INSERT(key_bindings, &key_bindings, bd); + + bd->can_repeat = can_repeat; + bd->cmdlist = cmdlist; +} + +void +key_bindings_remove(int key) +{ + struct key_binding *bd; + + if ((bd = key_bindings_lookup(key)) == NULL) + return; + SPLAY_REMOVE(key_bindings, &key_bindings, bd); + SPLAY_INSERT(key_bindings, &dead_key_bindings, bd); +} + +void +key_bindings_clean(void) +{ + struct key_binding *bd; + + while (!SPLAY_EMPTY(&dead_key_bindings)) { + bd = SPLAY_ROOT(&dead_key_bindings); + SPLAY_REMOVE(key_bindings, &dead_key_bindings, bd); + cmd_list_free(bd->cmdlist); + xfree(bd); + } +} + +void +key_bindings_init(void) +{ + static const struct { + int key; + int can_repeat; + const struct cmd_entry *entry; + } table[] = { + { ' ', 0, &cmd_next_layout_entry }, + { '!', 0, &cmd_break_pane_entry }, + { '"', 0, &cmd_split_window_entry }, + { '#', 0, &cmd_list_buffers_entry }, + { '$', 0, &cmd_command_prompt_entry }, + { '%', 0, &cmd_split_window_entry }, + { '&', 0, &cmd_confirm_before_entry }, + { '(', 0, &cmd_switch_client_entry }, + { ')', 0, &cmd_switch_client_entry }, + { ',', 0, &cmd_command_prompt_entry }, + { '-', 0, &cmd_delete_buffer_entry }, + { '.', 0, &cmd_command_prompt_entry }, + { '0', 0, &cmd_select_window_entry }, + { '1', 0, &cmd_select_window_entry }, + { '2', 0, &cmd_select_window_entry }, + { '3', 0, &cmd_select_window_entry }, + { '4', 0, &cmd_select_window_entry }, + { '5', 0, &cmd_select_window_entry }, + { '6', 0, &cmd_select_window_entry }, + { '7', 0, &cmd_select_window_entry }, + { '8', 0, &cmd_select_window_entry }, + { '9', 0, &cmd_select_window_entry }, + { ':', 0, &cmd_command_prompt_entry }, + { ';', 0, &cmd_last_pane_entry }, + { '=', 0, &cmd_choose_buffer_entry }, + { '?', 0, &cmd_list_keys_entry }, + { 'D', 0, &cmd_choose_client_entry }, + { 'L', 0, &cmd_switch_client_entry }, + { '[', 0, &cmd_copy_mode_entry }, + { '\'', 0, &cmd_command_prompt_entry }, + { '\002', /* C-b */ 0, &cmd_send_prefix_entry }, + { '\017', /* C-o */ 0, &cmd_rotate_window_entry }, + { '\032', /* C-z */ 0, &cmd_suspend_client_entry }, + { ']', 0, &cmd_paste_buffer_entry }, + { 'c', 0, &cmd_new_window_entry }, + { 'd', 0, &cmd_detach_client_entry }, + { 'f', 0, &cmd_command_prompt_entry }, + { 'i', 0, &cmd_display_message_entry }, + { 'l', 0, &cmd_last_window_entry }, + { 'n', 0, &cmd_next_window_entry }, + { 'o', 0, &cmd_select_pane_entry }, + { 'p', 0, &cmd_previous_window_entry }, + { 'q', 0, &cmd_display_panes_entry }, + { 'r', 0, &cmd_refresh_client_entry }, + { 's', 0, &cmd_choose_session_entry }, + { 't', 0, &cmd_clock_mode_entry }, + { 'w', 0, &cmd_choose_window_entry }, + { 'x', 0, &cmd_confirm_before_entry }, + { '{', 0, &cmd_swap_pane_entry }, + { '}', 0, &cmd_swap_pane_entry }, + { '~', 0, &cmd_show_messages_entry }, + { '1' | KEYC_ESCAPE, 0, &cmd_select_layout_entry }, + { '2' | KEYC_ESCAPE, 0, &cmd_select_layout_entry }, + { '3' | KEYC_ESCAPE, 0, &cmd_select_layout_entry }, + { '4' | KEYC_ESCAPE, 0, &cmd_select_layout_entry }, + { '5' | KEYC_ESCAPE, 0, &cmd_select_layout_entry }, + { KEYC_PPAGE, 0, &cmd_copy_mode_entry }, + { 'n' | KEYC_ESCAPE, 0, &cmd_next_window_entry }, + { 'o' | KEYC_ESCAPE, 0, &cmd_rotate_window_entry }, + { 'p' | KEYC_ESCAPE, 0, &cmd_previous_window_entry }, + { KEYC_UP, 1, &cmd_select_pane_entry }, + { KEYC_DOWN, 1, &cmd_select_pane_entry }, + { KEYC_LEFT, 1, &cmd_select_pane_entry }, + { KEYC_RIGHT, 1, &cmd_select_pane_entry }, + { KEYC_UP | KEYC_ESCAPE, 1, &cmd_resize_pane_entry }, + { KEYC_DOWN | KEYC_ESCAPE, 1, &cmd_resize_pane_entry }, + { KEYC_LEFT | KEYC_ESCAPE, 1, &cmd_resize_pane_entry }, + { KEYC_RIGHT | KEYC_ESCAPE, 1, &cmd_resize_pane_entry }, + { KEYC_UP | KEYC_CTRL, 1, &cmd_resize_pane_entry }, + { KEYC_DOWN | KEYC_CTRL, 1, &cmd_resize_pane_entry }, + { KEYC_LEFT | KEYC_CTRL, 1, &cmd_resize_pane_entry }, + { KEYC_RIGHT | KEYC_CTRL, 1, &cmd_resize_pane_entry }, + }; + u_int i; + struct cmd *cmd; + struct cmd_list *cmdlist; + + SPLAY_INIT(&key_bindings); + + for (i = 0; i < nitems(table); i++) { + cmdlist = xmalloc(sizeof *cmdlist); + TAILQ_INIT(&cmdlist->list); + cmdlist->references = 1; + + cmd = xmalloc(sizeof *cmd); + cmd->entry = table[i].entry; + if (cmd->entry->key_binding != NULL) + cmd->entry->key_binding(cmd, table[i].key); + else + cmd->args = args_create(0); + TAILQ_INSERT_HEAD(&cmdlist->list, cmd, qentry); + + key_bindings_add( + table[i].key | KEYC_PREFIX, table[i].can_repeat, cmdlist); + } +} + +void printflike2 +key_bindings_error(struct cmd_ctx *ctx, const char *fmt, ...) +{ + va_list ap; + char *msg; + + va_start(ap, fmt); + xvasprintf(&msg, fmt, ap); + va_end(ap); + + *msg = toupper((u_char) *msg); + status_message_set(ctx->curclient, "%s", msg); + xfree(msg); +} + +void printflike2 +key_bindings_print(struct cmd_ctx *ctx, const char *fmt, ...) +{ + struct winlink *wl = ctx->curclient->session->curw; + va_list ap; + + if (wl->window->active->mode != &window_copy_mode) { + window_pane_reset_mode(wl->window->active); + window_pane_set_mode(wl->window->active, &window_copy_mode); + window_copy_init_for_output(wl->window->active); + } + + va_start(ap, fmt); + window_copy_vadd(wl->window->active, fmt, ap); + va_end(ap); +} + +void printflike2 +key_bindings_info(struct cmd_ctx *ctx, const char *fmt, ...) +{ + va_list ap; + char *msg; + + if (options_get_number(&global_options, "quiet")) + return; + + va_start(ap, fmt); + xvasprintf(&msg, fmt, ap); + va_end(ap); + + *msg = toupper((u_char) *msg); + status_message_set(ctx->curclient, "%s", msg); + xfree(msg); +} + +void +key_bindings_dispatch(struct key_binding *bd, struct client *c) +{ + struct cmd_ctx ctx; + struct cmd *cmd; + int readonly; + + ctx.msgdata = NULL; + ctx.curclient = c; + + ctx.error = key_bindings_error; + ctx.print = key_bindings_print; + ctx.info = key_bindings_info; + + ctx.cmdclient = NULL; + + readonly = 1; + TAILQ_FOREACH(cmd, &bd->cmdlist->list, qentry) { + if (!(cmd->entry->flags & CMD_READONLY)) + readonly = 0; + } + if (!readonly && c->flags & CLIENT_READONLY) { + key_bindings_info(&ctx, "Client is read-only"); + return; + } + + cmd_list_exec(bd->cmdlist, &ctx); +} diff --git a/external/bsd/tmux/dist/key-string.c b/external/bsd/tmux/dist/key-string.c new file mode 100644 index 000000000..8b40ce0a0 --- /dev/null +++ b/external/bsd/tmux/dist/key-string.c @@ -0,0 +1,233 @@ +/* $Id: key-string.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +int key_string_search_table(const char *); +int key_string_get_modifiers(const char **); + +const struct { + const char *string; + int key; +} key_string_table[] = { + /* Function keys. */ + { "F1", KEYC_F1 }, + { "F2", KEYC_F2 }, + { "F3", KEYC_F3 }, + { "F4", KEYC_F4 }, + { "F5", KEYC_F5 }, + { "F6", KEYC_F6 }, + { "F7", KEYC_F7 }, + { "F8", KEYC_F8 }, + { "F9", KEYC_F9 }, + { "F10", KEYC_F10 }, + { "F11", KEYC_F11 }, + { "F12", KEYC_F12 }, + { "F13", KEYC_F13 }, + { "F14", KEYC_F14 }, + { "F15", KEYC_F15 }, + { "F16", KEYC_F16 }, + { "F17", KEYC_F17 }, + { "F18", KEYC_F18 }, + { "F19", KEYC_F19 }, + { "F20", KEYC_F20 }, + { "IC", KEYC_IC }, + { "DC", KEYC_DC }, + { "Home", KEYC_HOME }, + { "End", KEYC_END }, + { "NPage", KEYC_NPAGE }, + { "PPage", KEYC_PPAGE }, + { "Tab", '\011' }, + { "BTab", KEYC_BTAB }, + { "Space", ' ' }, + { "BSpace", KEYC_BSPACE }, + { "Enter", '\r' }, + { "Escape", '\033' }, + + /* Arrow keys. */ + { "Up", KEYC_UP }, + { "Down", KEYC_DOWN }, + { "Left", KEYC_LEFT }, + { "Right", KEYC_RIGHT }, + + /* Numeric keypad. */ + { "KP/", KEYC_KP_SLASH }, + { "KP*", KEYC_KP_STAR }, + { "KP-", KEYC_KP_MINUS }, + { "KP7", KEYC_KP_SEVEN }, + { "KP8", KEYC_KP_EIGHT }, + { "KP9", KEYC_KP_NINE }, + { "KP+", KEYC_KP_PLUS }, + { "KP4", KEYC_KP_FOUR }, + { "KP5", KEYC_KP_FIVE }, + { "KP6", KEYC_KP_SIX }, + { "KP1", KEYC_KP_ONE }, + { "KP2", KEYC_KP_TWO }, + { "KP3", KEYC_KP_THREE }, + { "KPEnter", KEYC_KP_ENTER }, + { "KP0", KEYC_KP_ZERO }, + { "KP.", KEYC_KP_PERIOD }, +}; + +/* Find key string in table. */ +int +key_string_search_table(const char *string) +{ + u_int i; + + for (i = 0; i < nitems(key_string_table); i++) { + if (strcasecmp(string, key_string_table[i].string) == 0) + return (key_string_table[i].key); + } + return (KEYC_NONE); +} + +/* Find modifiers. */ +int +key_string_get_modifiers(const char **string) +{ + int modifiers; + + modifiers = 0; + while (((*string)[0] != '\0') && (*string)[1] == '-') { + switch ((*string)[0]) { + case 'C': + case 'c': + modifiers |= KEYC_CTRL; + break; + case 'M': + case 'm': + modifiers |= KEYC_ESCAPE; + break; + case 'S': + case 's': + modifiers |= KEYC_SHIFT; + break; + } + *string += 2; + } + return (modifiers); +} + +/* Lookup a string and convert to a key value. */ +int +key_string_lookup_string(const char *string) +{ + int key, modifiers; + + /* Check for modifiers. */ + modifiers = 0; + if (string[0] == '^' && string[1] != '\0') { + modifiers |= KEYC_CTRL; + string++; + } + modifiers |= key_string_get_modifiers(&string); + if (string[0] == '\0') + return (KEYC_NONE); + + /* Is this a standard ASCII key? */ + if (string[1] == '\0') { + key = (u_char) string[0]; + if (key < 32 || key == 127 || key > 255) + return (KEYC_NONE); + } else { + /* Otherwise look the key up in the table. */ + key = key_string_search_table(string); + if (key == KEYC_NONE) + return (KEYC_NONE); + } + + /* Convert the standard control keys. */ + if (key < KEYC_BASE && (modifiers & KEYC_CTRL)) { + if (key >= 97 && key <= 122) + key -= 96; + else if (key >= 64 && key <= 95) + key -= 64; + else if (key == 32) + key = 0; + else if (key == 63) + key = KEYC_BSPACE; + else + return (KEYC_NONE); + modifiers &= ~KEYC_CTRL; + } + + return (key | modifiers); +} + +/* Convert a key code into string format, with prefix if necessary. */ +const char * +key_string_lookup_key(int key) +{ + static char out[24]; + char tmp[8]; + u_int i; + + *out = '\0'; + + /* + * Special case: display C-@ as C-Space. Could do this below in + * the (key >= 0 && key <= 32), but this way we let it be found + * in key_string_table, for the unlikely chance that we might + * change its name. + */ + if ((key & KEYC_MASK_KEY) == 0) + key = ' ' | KEYC_CTRL | (key & KEYC_MASK_MOD); + + /* Fill in the modifiers. */ + if (key & KEYC_CTRL) + strlcat(out, "C-", sizeof out); + if (key & KEYC_ESCAPE) + strlcat(out, "M-", sizeof out); + if (key & KEYC_SHIFT) + strlcat(out, "S-", sizeof out); + key &= KEYC_MASK_KEY; + + /* Try the key against the string table. */ + for (i = 0; i < nitems(key_string_table); i++) { + if (key == key_string_table[i].key) + break; + } + if (i != nitems(key_string_table)) { + strlcat(out, key_string_table[i].string, sizeof out); + return (out); + } + + /* Invalid keys are errors. */ + if (key == 127 || key > 255) + return (NULL); + + /* Check for standard or control key. */ + if (key >= 0 && key <= 32) { + if (key == 0 || key > 26) + xsnprintf(tmp, sizeof tmp, "C-%c", 64 + key); + else + xsnprintf(tmp, sizeof tmp, "C-%c", 96 + key); + } else if (key >= 32 && key <= 126) { + tmp[0] = key; + tmp[1] = '\0'; + } else if (key >= 128) + xsnprintf(tmp, sizeof tmp, "\\%o", key); + + strlcat(out, tmp, sizeof out); + return (out); +} diff --git a/external/bsd/tmux/dist/layout-custom.c b/external/bsd/tmux/dist/layout-custom.c new file mode 100644 index 000000000..5fb320e2f --- /dev/null +++ b/external/bsd/tmux/dist/layout-custom.c @@ -0,0 +1,275 @@ +/* $Id: layout-custom.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2010 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +struct layout_cell *layout_find_bottomright(struct layout_cell *); +u_short layout_checksum(const char *); +int layout_append(struct layout_cell *, char *, size_t); +struct layout_cell *layout_construct(struct layout_cell *, const char **); +void layout_assign(struct window_pane **, struct layout_cell *); + +/* Find the bottom-right cell. */ +struct layout_cell * +layout_find_bottomright(struct layout_cell *lc) +{ + if (lc->type == LAYOUT_WINDOWPANE) + return (lc); + lc = TAILQ_LAST(&lc->cells, layout_cells); + return (layout_find_bottomright(lc)); +} + +/* Calculate layout checksum. */ +u_short +layout_checksum(const char *layout) +{ + u_short csum; + + csum = 0; + for (; *layout != '\0'; layout++) { + csum = (csum >> 1) + ((csum & 1) << 15); + csum += *layout; + } + return (csum); +} + +/* Dump layout as a string. */ +char * +layout_dump(struct window *w) +{ + char layout[BUFSIZ], *out; + + *layout = '\0'; + if (layout_append(w->layout_root, layout, sizeof layout) != 0) + return (NULL); + + xasprintf(&out, "%4x,%s", layout_checksum(layout), layout); + return (out); +} + +/* Append information for a single cell. */ +int +layout_append(struct layout_cell *lc, char *buf, size_t len) +{ + struct layout_cell *lcchild; + char tmp[64]; + size_t tmplen; + const char *brackets = "]["; + + if (len == 0) + return (-1); + + tmplen = xsnprintf(tmp, sizeof tmp, + "%ux%u,%u,%u", lc->sx, lc->sy, lc->xoff, lc->yoff); + if (tmplen > (sizeof tmp) - 1) + return (-1); + if (strlcat(buf, tmp, len) >= len) + return (-1); + + switch (lc->type) { + case LAYOUT_LEFTRIGHT: + brackets = "}{"; + /* FALLTHROUGH */ + case LAYOUT_TOPBOTTOM: + if (strlcat(buf, &brackets[1], len) >= len) + return (-1); + TAILQ_FOREACH(lcchild, &lc->cells, entry) { + if (layout_append(lcchild, buf, len) != 0) + return (-1); + if (strlcat(buf, ",", len) >= len) + return (-1); + } + buf[strlen(buf) - 1] = brackets[0]; + break; + case LAYOUT_WINDOWPANE: + break; + } + + return (0); +} + +/* Parse a layout string and arrange window as layout. */ +int +layout_parse(struct window *w, const char *layout) +{ + struct layout_cell *lc, *lcchild; + struct window_pane *wp; + u_int npanes, ncells, sx, sy; + u_short csum; + + /* Check validity. */ + if (sscanf(layout, "%hx,", &csum) != 1) + return (-1); + layout += 5; + if (csum != layout_checksum(layout)) + return (-1); + + /* Build the layout. */ + lc = layout_construct(NULL, &layout); + if (lc == NULL) + return (-1); + if (*layout != '\0') + goto fail; + + /* Check this window will fit into the layout. */ + for (;;) { + npanes = window_count_panes(w); + ncells = layout_count_cells(lc); + if (npanes > ncells) + goto fail; + if (npanes == ncells) + break; + + /* Fewer panes than cells - close the bottom right. */ + lcchild = layout_find_bottomright(lc); + layout_destroy_cell(lcchild, &lc); + } + + /* Save the old window size and resize to the layout size. */ + sx = w->sx; sy = w->sy; + window_resize(w, lc->sx, lc->sy); + + /* Destroy the old layout and swap to the new. */ + layout_free_cell(w->layout_root); + w->layout_root = lc; + + /* Assign the panes into the cells. */ + wp = TAILQ_FIRST(&w->panes); + layout_assign(&wp, lc); + + /* Update pane offsets and sizes. */ + layout_fix_offsets(lc); + layout_fix_panes(w, lc->sx, lc->sy); + + /* Then resize the layout back to the original window size. */ + layout_resize(w, sx, sy); + window_resize(w, sx, sy); + + layout_print_cell(lc, __func__, 0); + + return (0); + +fail: + layout_free_cell(lc); + return (-1); +} + +/* Assign panes into cells. */ +void +layout_assign(struct window_pane **wp, struct layout_cell *lc) +{ + struct layout_cell *lcchild; + + switch (lc->type) { + case LAYOUT_WINDOWPANE: + layout_make_leaf(lc, *wp); + *wp = TAILQ_NEXT(*wp, entry); + return; + case LAYOUT_LEFTRIGHT: + case LAYOUT_TOPBOTTOM: + TAILQ_FOREACH(lcchild, &lc->cells, entry) + layout_assign(wp, lcchild); + return; + } +} + +/* Construct a cell from all or part of a layout tree. */ +struct layout_cell * +layout_construct(struct layout_cell *lcparent, const char **layout) +{ + struct layout_cell *lc, *lcchild; + u_int sx, sy, xoff, yoff; + + if (!isdigit((u_char) **layout)) + return (NULL); + if (sscanf(*layout, "%ux%u,%u,%u", &sx, &sy, &xoff, &yoff) != 4) + return (NULL); + + while (isdigit((u_char) **layout)) + (*layout)++; + if (**layout != 'x') + return (NULL); + (*layout)++; + while (isdigit((u_char) **layout)) + (*layout)++; + if (**layout != ',') + return (NULL); + (*layout)++; + while (isdigit((u_char) **layout)) + (*layout)++; + if (**layout != ',') + return (NULL); + (*layout)++; + while (isdigit((u_char) **layout)) + (*layout)++; + + lc = layout_create_cell(lcparent); + lc->sx = sx; + lc->sy = sy; + lc->xoff = xoff; + lc->yoff = yoff; + + switch (**layout) { + case ',': + case '}': + case ']': + case '\0': + return (lc); + case '{': + lc->type = LAYOUT_LEFTRIGHT; + break; + case '[': + lc->type = LAYOUT_TOPBOTTOM; + break; + default: + goto fail; + } + + do { + (*layout)++; + lcchild = layout_construct(lc, layout); + if (lcchild == NULL) + goto fail; + TAILQ_INSERT_TAIL(&lc->cells, lcchild, entry); + } while (**layout == ','); + + switch (lc->type) { + case LAYOUT_LEFTRIGHT: + if (**layout != '}') + goto fail; + break; + case LAYOUT_TOPBOTTOM: + if (**layout != ']') + goto fail; + break; + default: + goto fail; + } + (*layout)++; + + return (lc); + +fail: + layout_free_cell(lc); + return (NULL); +} diff --git a/external/bsd/tmux/dist/layout-set.c b/external/bsd/tmux/dist/layout-set.c new file mode 100644 index 000000000..e9562caca --- /dev/null +++ b/external/bsd/tmux/dist/layout-set.c @@ -0,0 +1,569 @@ +/* $Id: layout-set.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * Set window layouts - predefined methods to arrange windows. These are one-off + * and generate a layout tree. + */ + +void layout_set_even_h(struct window *); +void layout_set_even_v(struct window *); +void layout_set_main_h(struct window *); +void layout_set_main_v(struct window *); +void layout_set_tiled(struct window *); + +const struct { + const char *name; + void (*arrange)(struct window *); +} layout_sets[] = { + { "even-horizontal", layout_set_even_h }, + { "even-vertical", layout_set_even_v }, + { "main-horizontal", layout_set_main_h }, + { "main-vertical", layout_set_main_v }, + { "tiled", layout_set_tiled }, +}; + +const char * +layout_set_name(u_int layout) +{ + return (layout_sets[layout].name); +} + +int +layout_set_lookup(const char *name) +{ + u_int i; + int matched = -1; + + for (i = 0; i < nitems(layout_sets); i++) { + if (strncmp(layout_sets[i].name, name, strlen(name)) == 0) { + if (matched != -1) /* ambiguous */ + return (-1); + matched = i; + } + } + + return (matched); +} + +u_int +layout_set_select(struct window *w, u_int layout) +{ + if (layout > nitems(layout_sets) - 1) + layout = nitems(layout_sets) - 1; + + if (layout_sets[layout].arrange != NULL) + layout_sets[layout].arrange(w); + + w->lastlayout = layout; + return (layout); +} + +u_int +layout_set_next(struct window *w) +{ + u_int layout; + + if (w->lastlayout == -1) + layout = 0; + else { + layout = w->lastlayout + 1; + if (layout > nitems(layout_sets) - 1) + layout = 0; + } + + if (layout_sets[layout].arrange != NULL) + layout_sets[layout].arrange(w); + w->lastlayout = layout; + return (layout); +} + +u_int +layout_set_previous(struct window *w) +{ + u_int layout; + + if (w->lastlayout == -1) + layout = nitems(layout_sets) - 1; + else { + layout = w->lastlayout; + if (layout == 0) + layout = nitems(layout_sets) - 1; + else + layout--; + } + + if (layout_sets[layout].arrange != NULL) + layout_sets[layout].arrange(w); + w->lastlayout = layout; + return (layout); +} + +void +layout_set_even_h(struct window *w) +{ + struct window_pane *wp; + struct layout_cell *lc, *lcnew; + u_int i, n, width, xoff; + + layout_print_cell(w->layout_root, __func__, 1); + + /* Get number of panes. */ + n = window_count_panes(w); + if (n <= 1) + return; + + /* How many can we fit? */ + width = (w->sx - (n - 1)) / n; + if (width < PANE_MINIMUM) + width = PANE_MINIMUM; + + /* Free the old root and construct a new. */ + layout_free(w); + lc = w->layout_root = layout_create_cell(NULL); + layout_set_size(lc, w->sx, w->sy, 0, 0); + layout_make_node(lc, LAYOUT_LEFTRIGHT); + + /* Build new leaf cells. */ + i = xoff = 0; + TAILQ_FOREACH(wp, &w->panes, entry) { + /* Create child cell. */ + lcnew = layout_create_cell(lc); + layout_set_size(lcnew, width, w->sy, xoff, 0); + layout_make_leaf(lcnew, wp); + TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry); + + i++; + xoff += width + 1; + } + + /* Allocate any remaining space. */ + if (w->sx > xoff - 1) { + lc = TAILQ_LAST(&lc->cells, layout_cells); + layout_resize_adjust(lc, LAYOUT_LEFTRIGHT, w->sx - (xoff - 1)); + } + + /* Fix cell offsets. */ + layout_fix_offsets(lc); + layout_fix_panes(w, w->sx, w->sy); + + layout_print_cell(w->layout_root, __func__, 1); + + server_redraw_window(w); +} + +void +layout_set_even_v(struct window *w) +{ + struct window_pane *wp; + struct layout_cell *lc, *lcnew; + u_int i, n, height, yoff; + + layout_print_cell(w->layout_root, __func__, 1); + + /* Get number of panes. */ + n = window_count_panes(w); + if (n <= 1) + return; + + /* How many can we fit? */ + height = (w->sy - (n - 1)) / n; + if (height < PANE_MINIMUM) + height = PANE_MINIMUM; + + /* Free the old root and construct a new. */ + layout_free(w); + lc = w->layout_root = layout_create_cell(NULL); + layout_set_size(lc, w->sx, w->sy, 0, 0); + layout_make_node(lc, LAYOUT_TOPBOTTOM); + + /* Build new leaf cells. */ + i = yoff = 0; + TAILQ_FOREACH(wp, &w->panes, entry) { + /* Create child cell. */ + lcnew = layout_create_cell(lc); + layout_set_size(lcnew, w->sx, height, 0, yoff); + layout_make_leaf(lcnew, wp); + TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry); + + i++; + yoff += height + 1; + } + + /* Allocate any remaining space. */ + if (w->sy > yoff - 1) { + lc = TAILQ_LAST(&lc->cells, layout_cells); + layout_resize_adjust(lc, LAYOUT_TOPBOTTOM, w->sy - (yoff - 1)); + } + + /* Fix cell offsets. */ + layout_fix_offsets(lc); + layout_fix_panes(w, w->sx, w->sy); + + layout_print_cell(w->layout_root, __func__, 1); + + server_redraw_window(w); +} + +void +layout_set_main_h(struct window *w) +{ + struct window_pane *wp; + struct layout_cell *lc, *lcmain, *lcrow, *lcchild; + u_int n, mainheight, otherheight, width, height; + u_int used, i, j, columns, rows, totalrows; + + layout_print_cell(w->layout_root, __func__, 1); + + /* Get number of panes. */ + n = window_count_panes(w); + if (n <= 1) + return; + n--; /* take off main pane */ + + /* How many rows and columns will be needed, not counting main? */ + columns = (w->sx + 1) / (PANE_MINIMUM + 1); /* maximum columns */ + if (columns == 0) + columns = 1; + rows = 1 + (n - 1) / columns; + columns = 1 + (n - 1) / rows; + width = (w->sx - (n - 1)) / columns; + + /* Get the main pane height and add one for separator line. */ + mainheight = options_get_number(&w->options, "main-pane-height") + 1; + + /* Get the optional other pane height and add one for separator line. */ + otherheight = options_get_number(&w->options, "other-pane-height") + 1; + + /* + * If an other pane height was specified, honour it so long as it + * doesn't shrink the main height to less than the main-pane-height + */ + if (otherheight > 1 && w->sx - otherheight > mainheight) + mainheight = w->sx - otherheight; + if (mainheight < PANE_MINIMUM + 1) + mainheight = PANE_MINIMUM + 1; + + /* Try and make everything fit. */ + totalrows = rows * (PANE_MINIMUM + 1) - 1; + if (mainheight + totalrows > w->sy) { + if (totalrows + PANE_MINIMUM + 1 > w->sy) + mainheight = PANE_MINIMUM + 2; + else + mainheight = w->sy - totalrows; + height = PANE_MINIMUM; + } else + height = (w->sy - mainheight - (rows - 1)) / rows; + + /* Free old tree and create a new root. */ + layout_free(w); + lc = w->layout_root = layout_create_cell(NULL); + layout_set_size(lc, w->sx, mainheight + rows * (height + 1) - 1, 0, 0); + layout_make_node(lc, LAYOUT_TOPBOTTOM); + + /* Create the main pane. */ + lcmain = layout_create_cell(lc); + layout_set_size(lcmain, w->sx, mainheight - 1, 0, 0); + layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes)); + TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry); + + /* Create a grid of the remaining cells. */ + wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry); + for (j = 0; j < rows; j++) { + /* If this is the last cell, all done. */ + if (wp == NULL) + break; + + /* Create the new row. */ + lcrow = layout_create_cell(lc); + layout_set_size(lcrow, w->sx, height, 0, 0); + TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry); + + /* If only one column, just use the row directly. */ + if (columns == 1) { + layout_make_leaf(lcrow, wp); + wp = TAILQ_NEXT(wp, entry); + continue; + } + + /* Add in the columns. */ + layout_make_node(lcrow, LAYOUT_LEFTRIGHT); + for (i = 0; i < columns; i++) { + /* Create and add a pane cell. */ + lcchild = layout_create_cell(lcrow); + layout_set_size(lcchild, width, height, 0, 0); + layout_make_leaf(lcchild, wp); + TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry); + + /* Move to the next cell. */ + if ((wp = TAILQ_NEXT(wp, entry)) == NULL) + break; + } + + /* Adjust the row to fit the full width if necessary. */ + if (i == columns) + i--; + used = ((i + 1) * (width + 1)) - 1; + if (w->sx <= used) + continue; + lcchild = TAILQ_LAST(&lcrow->cells, layout_cells); + layout_resize_adjust(lcchild, LAYOUT_LEFTRIGHT, w->sx - used); + } + + /* Adjust the last row height to fit if necessary. */ + used = mainheight + (rows * height) + rows - 1; + if (w->sy > used) { + lcrow = TAILQ_LAST(&lc->cells, layout_cells); + layout_resize_adjust(lcrow, LAYOUT_TOPBOTTOM, w->sy - used); + } + + /* Fix cell offsets. */ + layout_fix_offsets(lc); + layout_fix_panes(w, w->sx, w->sy); + + layout_print_cell(w->layout_root, __func__, 1); + + server_redraw_window(w); +} + +void +layout_set_main_v(struct window *w) +{ + struct window_pane *wp; + struct layout_cell *lc, *lcmain, *lccolumn, *lcchild; + u_int n, mainwidth, otherwidth, width, height; + u_int used, i, j, columns, rows, totalcolumns; + + layout_print_cell(w->layout_root, __func__, 1); + + /* Get number of panes. */ + n = window_count_panes(w); + if (n <= 1) + return; + n--; /* take off main pane */ + + /* How many rows and columns will be needed, not counting main? */ + rows = (w->sy + 1) / (PANE_MINIMUM + 1); /* maximum rows */ + if (rows == 0) + rows = 1; + columns = 1 + (n - 1) / rows; + rows = 1 + (n - 1) / columns; + height = (w->sy - (n - 1)) / rows; + + /* Get the main pane width and add one for separator line. */ + mainwidth = options_get_number(&w->options, "main-pane-width") + 1; + + /* Get the optional other pane width and add one for separator line. */ + otherwidth = options_get_number(&w->options, "other-pane-width") + 1; + + /* + * If an other pane width was specified, honour it so long as it + * doesn't shrink the main width to less than the main-pane-width + */ + if (otherwidth > 1 && w->sx - otherwidth > mainwidth) + mainwidth = w->sx - otherwidth; + if (mainwidth < PANE_MINIMUM + 1) + mainwidth = PANE_MINIMUM + 1; + + /* Try and make everything fit. */ + totalcolumns = columns * (PANE_MINIMUM + 1) - 1; + if (mainwidth + totalcolumns > w->sx) { + if (totalcolumns + PANE_MINIMUM + 1 > w->sx) + mainwidth = PANE_MINIMUM + 2; + else + mainwidth = w->sx - totalcolumns; + width = PANE_MINIMUM; + } else + width = (w->sx - mainwidth - (columns - 1)) / columns; + + /* Free old tree and create a new root. */ + layout_free(w); + lc = w->layout_root = layout_create_cell(NULL); + layout_set_size(lc, mainwidth + columns * (width + 1) - 1, w->sy, 0, 0); + layout_make_node(lc, LAYOUT_LEFTRIGHT); + + /* Create the main pane. */ + lcmain = layout_create_cell(lc); + layout_set_size(lcmain, mainwidth - 1, w->sy, 0, 0); + layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes)); + TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry); + + /* Create a grid of the remaining cells. */ + wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry); + for (j = 0; j < columns; j++) { + /* If this is the last cell, all done. */ + if (wp == NULL) + break; + + /* Create the new column. */ + lccolumn = layout_create_cell(lc); + layout_set_size(lccolumn, width, w->sy, 0, 0); + TAILQ_INSERT_TAIL(&lc->cells, lccolumn, entry); + + /* If only one row, just use the row directly. */ + if (rows == 1) { + layout_make_leaf(lccolumn, wp); + wp = TAILQ_NEXT(wp, entry); + continue; + } + + /* Add in the rows. */ + layout_make_node(lccolumn, LAYOUT_TOPBOTTOM); + for (i = 0; i < rows; i++) { + /* Create and add a pane cell. */ + lcchild = layout_create_cell(lccolumn); + layout_set_size(lcchild, width, height, 0, 0); + layout_make_leaf(lcchild, wp); + TAILQ_INSERT_TAIL(&lccolumn->cells, lcchild, entry); + + /* Move to the next cell. */ + if ((wp = TAILQ_NEXT(wp, entry)) == NULL) + break; + } + + /* Adjust the column to fit the full height if necessary. */ + if (i == rows) + i--; + used = ((i + 1) * (height + 1)) - 1; + if (w->sy <= used) + continue; + lcchild = TAILQ_LAST(&lccolumn->cells, layout_cells); + layout_resize_adjust(lcchild, LAYOUT_TOPBOTTOM, w->sy - used); + } + + /* Adjust the last column width to fit if necessary. */ + used = mainwidth + (columns * width) + columns - 1; + if (w->sx > used) { + lccolumn = TAILQ_LAST(&lc->cells, layout_cells); + layout_resize_adjust(lccolumn, LAYOUT_LEFTRIGHT, w->sx - used); + } + + /* Fix cell offsets. */ + layout_fix_offsets(lc); + layout_fix_panes(w, w->sx, w->sy); + + layout_print_cell(w->layout_root, __func__, 1); + + server_redraw_window(w); +} + +void +layout_set_tiled(struct window *w) +{ + struct window_pane *wp; + struct layout_cell *lc, *lcrow, *lcchild; + u_int n, width, height, used; + u_int i, j, columns, rows; + + layout_print_cell(w->layout_root, __func__, 1); + + /* Get number of panes. */ + n = window_count_panes(w); + if (n <= 1) + return; + + /* How many rows and columns are wanted? */ + rows = columns = 1; + while (rows * columns < n) { + rows++; + if (rows * columns < n) + columns++; + } + + /* What width and height should they be? */ + width = (w->sx - (columns - 1)) / columns; + if (width < PANE_MINIMUM) + width = PANE_MINIMUM; + height = (w->sy - (rows - 1)) / rows; + if (height < PANE_MINIMUM) + height = PANE_MINIMUM; + + /* Free old tree and create a new root. */ + layout_free(w); + lc = w->layout_root = layout_create_cell(NULL); + layout_set_size(lc, (width + 1) * columns - 1, + (height + 1) * rows - 1, 0, 0); + layout_make_node(lc, LAYOUT_TOPBOTTOM); + + /* Create a grid of the cells. */ + wp = TAILQ_FIRST(&w->panes); + for (j = 0; j < rows; j++) { + /* If this is the last cell, all done. */ + if (wp == NULL) + break; + + /* Create the new row. */ + lcrow = layout_create_cell(lc); + layout_set_size(lcrow, w->sx, height, 0, 0); + TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry); + + /* If only one column, just use the row directly. */ + if (n - (j * columns) == 1 || columns == 1) { + layout_make_leaf(lcrow, wp); + wp = TAILQ_NEXT(wp, entry); + continue; + } + + /* Add in the columns. */ + layout_make_node(lcrow, LAYOUT_LEFTRIGHT); + for (i = 0; i < columns; i++) { + /* Create and add a pane cell. */ + lcchild = layout_create_cell(lcrow); + layout_set_size(lcchild, width, height, 0, 0); + layout_make_leaf(lcchild, wp); + TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry); + + /* Move to the next cell. */ + if ((wp = TAILQ_NEXT(wp, entry)) == NULL) + break; + } + + /* + * Adjust the row and columns to fit the full width if + * necessary. + */ + if (i == columns) + i--; + used = ((i + 1) * (width + 1)) - 1; + if (w->sx <= used) + continue; + lcchild = TAILQ_LAST(&lcrow->cells, layout_cells); + layout_resize_adjust(lcchild, LAYOUT_LEFTRIGHT, w->sx - used); + } + + /* Adjust the last row height to fit if necessary. */ + used = (rows * height) + rows - 1; + if (w->sy > used) { + lcrow = TAILQ_LAST(&lc->cells, layout_cells); + layout_resize_adjust(lcrow, LAYOUT_TOPBOTTOM, w->sy - used); + } + + /* Fix cell offsets. */ + layout_fix_offsets(lc); + layout_fix_panes(w, w->sx, w->sy); + + layout_print_cell(w->layout_root, __func__, 1); + + server_redraw_window(w); +} diff --git a/external/bsd/tmux/dist/layout.c b/external/bsd/tmux/dist/layout.c new file mode 100644 index 000000000..717652725 --- /dev/null +++ b/external/bsd/tmux/dist/layout.c @@ -0,0 +1,730 @@ +/* $Id: layout.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * The window layout is a tree of cells each of which can be one of: a + * left-right container for a list of cells, a top-bottom container for a list + * of cells, or a container for a window pane. + * + * Each window has a pointer to the root of its layout tree (containing its + * panes), every pane has a pointer back to the cell containing it, and each + * cell a pointer to its parent cell. + */ + +int layout_resize_pane_grow(struct layout_cell *, enum layout_type, int); +int layout_resize_pane_shrink(struct layout_cell *, enum layout_type, int); + +struct layout_cell * +layout_create_cell(struct layout_cell *lcparent) +{ + struct layout_cell *lc; + + lc = xmalloc(sizeof *lc); + lc->type = LAYOUT_WINDOWPANE; + lc->parent = lcparent; + + TAILQ_INIT(&lc->cells); + + lc->sx = UINT_MAX; + lc->sy = UINT_MAX; + + lc->xoff = UINT_MAX; + lc->yoff = UINT_MAX; + + lc->wp = NULL; + + return (lc); +} + +void +layout_free_cell(struct layout_cell *lc) +{ + struct layout_cell *lcchild; + + switch (lc->type) { + case LAYOUT_LEFTRIGHT: + case LAYOUT_TOPBOTTOM: + while (!TAILQ_EMPTY(&lc->cells)) { + lcchild = TAILQ_FIRST(&lc->cells); + TAILQ_REMOVE(&lc->cells, lcchild, entry); + layout_free_cell(lcchild); + } + break; + case LAYOUT_WINDOWPANE: + if (lc->wp != NULL) + lc->wp->layout_cell = NULL; + break; + } + + xfree(lc); +} + +void +layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n) +{ + struct layout_cell *lcchild; + + log_debug( + "%s:%*s%p type %u [parent %p] wp=%p [%u,%u %ux%u]", hdr, n, " ", lc, + lc->type, lc->parent, lc->wp, lc->xoff, lc->yoff, lc->sx, lc->sy); + switch (lc->type) { + case LAYOUT_LEFTRIGHT: + case LAYOUT_TOPBOTTOM: + TAILQ_FOREACH(lcchild, &lc->cells, entry) + layout_print_cell(lcchild, hdr, n + 1); + break; + case LAYOUT_WINDOWPANE: + break; + } +} + +void +layout_set_size( + struct layout_cell *lc, u_int sx, u_int sy, u_int xoff, u_int yoff) +{ + lc->sx = sx; + lc->sy = sy; + + lc->xoff = xoff; + lc->yoff = yoff; +} + +void +layout_make_leaf(struct layout_cell *lc, struct window_pane *wp) +{ + lc->type = LAYOUT_WINDOWPANE; + + TAILQ_INIT(&lc->cells); + + wp->layout_cell = lc; + lc->wp = wp; +} + +void +layout_make_node(struct layout_cell *lc, enum layout_type type) +{ + if (type == LAYOUT_WINDOWPANE) + fatalx("bad layout type"); + lc->type = type; + + TAILQ_INIT(&lc->cells); + + if (lc->wp != NULL) + lc->wp->layout_cell = NULL; + lc->wp = NULL; +} + +/* Fix cell offsets based on their sizes. */ +void +layout_fix_offsets(struct layout_cell *lc) +{ + struct layout_cell *lcchild; + u_int xoff, yoff; + + if (lc->type == LAYOUT_LEFTRIGHT) { + xoff = lc->xoff; + TAILQ_FOREACH(lcchild, &lc->cells, entry) { + lcchild->xoff = xoff; + lcchild->yoff = lc->yoff; + if (lcchild->type != LAYOUT_WINDOWPANE) + layout_fix_offsets(lcchild); + xoff += lcchild->sx + 1; + } + } else { + yoff = lc->yoff; + TAILQ_FOREACH(lcchild, &lc->cells, entry) { + lcchild->xoff = lc->xoff; + lcchild->yoff = yoff; + if (lcchild->type != LAYOUT_WINDOWPANE) + layout_fix_offsets(lcchild); + yoff += lcchild->sy + 1; + } + } +} + +/* Update pane offsets and sizes based on their cells. */ +void +layout_fix_panes(struct window *w, u_int wsx, u_int wsy) +{ + struct window_pane *wp; + struct layout_cell *lc; + u_int sx, sy; + + TAILQ_FOREACH(wp, &w->panes, entry) { + if ((lc = wp->layout_cell) == NULL) + continue; + wp->xoff = lc->xoff; + wp->yoff = lc->yoff; + + /* + * Layout cells are limited by the smallest size of other cells + * within the same row or column; if this isn't the case + * resizing becomes difficult. + * + * However, panes do not have to take up their entire cell, so + * they can be cropped to the window edge if the layout + * overflows and they are partly visible. + * + * This stops cells being hidden unnecessarily. + */ + + /* + * Work out the horizontal size. If the pane is actually + * outside the window or the entire pane is already visible, + * don't crop. + */ + if (lc->xoff >= wsx || lc->xoff + lc->sx < wsx) + sx = lc->sx; + else { + sx = wsx - lc->xoff; + if (sx < 1) + sx = lc->sx; + } + + /* + * Similarly for the vertical size; the minimum vertical size + * is two because scroll regions cannot be one line. + */ + if (lc->yoff >= wsy || lc->yoff + lc->sy < wsy) + sy = lc->sy; + else { + sy = wsy - lc->yoff; + if (sy < 2) + sy = lc->sy; + } + + window_pane_resize(wp, sx, sy); + } +} + +/* Count the number of available cells in a layout. */ +u_int +layout_count_cells(struct layout_cell *lc) +{ + struct layout_cell *lcchild; + u_int n; + + switch (lc->type) { + case LAYOUT_WINDOWPANE: + return (1); + case LAYOUT_LEFTRIGHT: + case LAYOUT_TOPBOTTOM: + n = 0; + TAILQ_FOREACH(lcchild, &lc->cells, entry) + n += layout_count_cells(lcchild); + return (n); + default: + fatalx("bad layout type"); + } +} + +/* Calculate how much size is available to be removed from a cell. */ +u_int +layout_resize_check(struct layout_cell *lc, enum layout_type type) +{ + struct layout_cell *lcchild; + u_int available, minimum; + + if (lc->type == LAYOUT_WINDOWPANE) { + /* Space available in this cell only. */ + if (type == LAYOUT_LEFTRIGHT) + available = lc->sx; + else + available = lc->sy; + + if (available > PANE_MINIMUM) + available -= PANE_MINIMUM; + else + available = 0; + } else if (lc->type == type) { + /* Same type: total of available space in all child cells. */ + available = 0; + TAILQ_FOREACH(lcchild, &lc->cells, entry) + available += layout_resize_check(lcchild, type); + } else { + /* Different type: minimum of available space in child cells. */ + minimum = UINT_MAX; + TAILQ_FOREACH(lcchild, &lc->cells, entry) { + available = layout_resize_check(lcchild, type); + if (available < minimum) + minimum = available; + } + available = minimum; + } + + return (available); +} + +/* + * Adjust cell size evenly, including altering its children. This function + * expects the change to have already been bounded to the space available. + */ +void +layout_resize_adjust(struct layout_cell *lc, enum layout_type type, int change) +{ + struct layout_cell *lcchild; + + /* Adjust the cell size. */ + if (type == LAYOUT_LEFTRIGHT) + lc->sx += change; + else + lc->sy += change; + + /* If this is a leaf cell, that is all that is necessary. */ + if (type == LAYOUT_WINDOWPANE) + return; + + /* Child cell runs in a different direction. */ + if (lc->type != type) { + TAILQ_FOREACH(lcchild, &lc->cells, entry) + layout_resize_adjust(lcchild, type, change); + return; + } + + /* + * Child cell runs in the same direction. Adjust each child equally + * until no further change is possible. + */ + while (change != 0) { + TAILQ_FOREACH(lcchild, &lc->cells, entry) { + if (change == 0) + break; + if (change > 0) { + layout_resize_adjust(lcchild, type, 1); + change--; + continue; + } + if (layout_resize_check(lcchild, type) > 0) { + layout_resize_adjust(lcchild, type, -1); + change++; + } + } + } +} + +/* Destroy a cell and redistribute the space. */ +void +layout_destroy_cell(struct layout_cell *lc, struct layout_cell **lcroot) +{ + struct layout_cell *lcother, *lcparent; + + /* + * If no parent, this is the last pane so window close is imminent and + * there is no need to resize anything. + */ + lcparent = lc->parent; + if (lcparent == NULL) { + layout_free_cell(lc); + *lcroot = NULL; + return; + } + + /* Merge the space into the previous or next cell. */ + if (lc == TAILQ_FIRST(&lcparent->cells)) + lcother = TAILQ_NEXT(lc, entry); + else + lcother = TAILQ_PREV(lc, layout_cells, entry); + if (lcparent->type == LAYOUT_LEFTRIGHT) + layout_resize_adjust(lcother, lcparent->type, lc->sx + 1); + else + layout_resize_adjust(lcother, lcparent->type, lc->sy + 1); + + /* Remove this from the parent's list. */ + TAILQ_REMOVE(&lcparent->cells, lc, entry); + layout_free_cell(lc); + + /* + * If the parent now has one cell, remove the parent from the tree and + * replace it by that cell. + */ + lc = TAILQ_FIRST(&lcparent->cells); + if (TAILQ_NEXT(lc, entry) == NULL) { + TAILQ_REMOVE(&lcparent->cells, lc, entry); + + lc->parent = lcparent->parent; + if (lc->parent == NULL) { + lc->xoff = 0; lc->yoff = 0; + *lcroot = lc; + } else + TAILQ_REPLACE(&lc->parent->cells, lcparent, lc, entry); + + layout_free_cell(lcparent); + } +} + +void +layout_init(struct window *w) +{ + struct layout_cell *lc; + + lc = w->layout_root = layout_create_cell(NULL); + layout_set_size(lc, w->sx, w->sy, 0, 0); + layout_make_leaf(lc, TAILQ_FIRST(&w->panes)); + + layout_fix_panes(w, w->sx, w->sy); +} + +void +layout_free(struct window *w) +{ + layout_free_cell(w->layout_root); +} + +/* Resize the entire layout after window resize. */ +void +layout_resize(struct window *w, u_int sx, u_int sy) +{ + struct layout_cell *lc = w->layout_root; + int xlimit, ylimit, xchange, ychange; + + /* + * Adjust horizontally. Do not attempt to reduce the layout lower than + * the minimum (more than the amount returned by layout_resize_check). + * + * This can mean that the window size is smaller than the total layout + * size: redrawing this is handled at a higher level, but it does leave + * a problem with growing the window size here: if the current size is + * < the minimum, growing proportionately by adding to each pane is + * wrong as it would keep the layout size larger than the window size. + * Instead, spread the difference between the minimum and the new size + * out proportionately - this should leave the layout fitting the new + * window size. + */ + xchange = sx - w->sx; + xlimit = layout_resize_check(lc, LAYOUT_LEFTRIGHT); + if (xchange < 0 && xchange < -xlimit) + xchange = -xlimit; + if (xlimit == 0) { + if (sx <= lc->sx) /* lc->sx is minimum possible */ + xchange = 0; + else + xchange = sx - lc->sx; + } + if (xchange != 0) + layout_resize_adjust(lc, LAYOUT_LEFTRIGHT, xchange); + + /* Adjust vertically in a similar fashion. */ + ychange = sy - w->sy; + ylimit = layout_resize_check(lc, LAYOUT_TOPBOTTOM); + if (ychange < 0 && ychange < -ylimit) + ychange = -ylimit; + if (ylimit == 0) { + if (sy <= lc->sy) /* lc->sy is minimum possible */ + ychange = 0; + else + ychange = sy - lc->sy; + } + if (ychange != 0) + layout_resize_adjust(lc, LAYOUT_TOPBOTTOM, ychange); + + /* Fix cell offsets. */ + layout_fix_offsets(lc); + layout_fix_panes(w, sx, sy); +} + +/* Resize a single pane within the layout. */ +void +layout_resize_pane(struct window_pane *wp, enum layout_type type, int change) +{ + struct layout_cell *lc, *lcparent; + int needed, size; + + lc = wp->layout_cell; + + /* Find next parent of the same type. */ + lcparent = lc->parent; + while (lcparent != NULL && lcparent->type != type) { + lc = lcparent; + lcparent = lc->parent; + } + if (lcparent == NULL) + return; + + /* If this is the last cell, move back one. */ + if (lc == TAILQ_LAST(&lcparent->cells, layout_cells)) + lc = TAILQ_PREV(lc, layout_cells, entry); + + /* Grow or shrink the cell. */ + needed = change; + while (needed != 0) { + if (change > 0) { + size = layout_resize_pane_grow(lc, type, needed); + needed -= size; + } else { + size = layout_resize_pane_shrink(lc, type, needed); + needed += size; + } + + if (size == 0) /* no more change possible */ + break; + } + + /* Fix cell offsets. */ + layout_fix_offsets(wp->window->layout_root); + layout_fix_panes(wp->window, wp->window->sx, wp->window->sy); +} + +void +layout_resize_pane_mouse(struct client *c, struct mouse_event *mouse) +{ + struct window *w; + struct window_pane *wp; + int pane_border; + + w = c->session->curw->window; + + pane_border = 0; + if ((c->last_mouse.b & MOUSE_BUTTON) != MOUSE_UP && + (c->last_mouse.b & MOUSE_RESIZE_PANE)) { + TAILQ_FOREACH(wp, &w->panes, entry) { + if (wp->xoff + wp->sx == c->last_mouse.x && + wp->yoff <= 1 + c->last_mouse.y && + wp->yoff + wp->sy >= c->last_mouse.y) { + layout_resize_pane(wp, LAYOUT_LEFTRIGHT, + mouse->x - c->last_mouse.x); + pane_border = 1; + } + if (wp->yoff + wp->sy == c->last_mouse.y && + wp->xoff <= 1 + c->last_mouse.x && + wp->xoff + wp->sx >= c->last_mouse.x) { + layout_resize_pane(wp, LAYOUT_TOPBOTTOM, + mouse->y - c->last_mouse.y); + pane_border = 1; + } + } + if (pane_border) + server_redraw_window(w); + } else if (mouse->b != MOUSE_UP && + mouse->b == (mouse->b & MOUSE_BUTTON)) { + TAILQ_FOREACH(wp, &w->panes, entry) { + if ((wp->xoff + wp->sx == mouse->x && + wp->yoff <= 1 + mouse->y && + wp->yoff + wp->sy >= mouse->y) || + (wp->yoff + wp->sy == mouse->y && + wp->xoff <= 1 + mouse->x && + wp->xoff + wp->sx >= mouse->x)) { + pane_border = 1; + } + } + } + if (pane_border) + mouse->b |= MOUSE_RESIZE_PANE; +} + +int +layout_resize_pane_grow( + struct layout_cell *lc, enum layout_type type, int needed) +{ + struct layout_cell *lcadd, *lcremove; + u_int size; + + /* Growing. Always add to the current cell. */ + lcadd = lc; + + /* Look towards the tail for a suitable cell for reduction. */ + lcremove = TAILQ_NEXT(lc, entry); + while (lcremove != NULL) { + size = layout_resize_check(lcremove, type); + if (size > 0) + break; + lcremove = TAILQ_NEXT(lcremove, entry); + } + + /* If none found, look towards the head. */ + if (lcremove == NULL) { + lcremove = TAILQ_PREV(lc, layout_cells, entry); + while (lcremove != NULL) { + size = layout_resize_check(lcremove, type); + if (size > 0) + break; + lcremove = TAILQ_PREV(lcremove, layout_cells, entry); + } + if (lcremove == NULL) + return (0); + } + + /* Change the cells. */ + if (size > (u_int) needed) + size = needed; + layout_resize_adjust(lcadd, type, size); + layout_resize_adjust(lcremove, type, -size); + return (size); +} + +int +layout_resize_pane_shrink( + struct layout_cell *lc, enum layout_type type, int needed) +{ + struct layout_cell *lcadd, *lcremove; + u_int size; + + /* Shrinking. Find cell to remove from by walking towards head. */ + lcremove = lc; + do { + size = layout_resize_check(lcremove, type); + if (size != 0) + break; + lcremove = TAILQ_PREV(lcremove, layout_cells, entry); + } while (lcremove != NULL); + if (lcremove == NULL) + return (0); + + /* And add onto the next cell (from the original cell). */ + lcadd = TAILQ_NEXT(lc, entry); + if (lcadd == NULL) + return (0); + + /* Change the cells. */ + if (size > (u_int) -needed) + size = -needed; + layout_resize_adjust(lcadd, type, size); + layout_resize_adjust(lcremove, type, -size); + return (size); +} + +/* Assign window pane to newly split cell. */ +void +layout_assign_pane(struct layout_cell *lc, struct window_pane *wp) +{ + layout_make_leaf(lc, wp); + layout_fix_panes(wp->window, wp->window->sx, wp->window->sy); +} + +/* + * Split a pane into two. size is a hint, or -1 for default half/half + * split. This must be followed by layout_assign_pane before much else happens! + **/ +struct layout_cell * +layout_split_pane(struct window_pane *wp, enum layout_type type, int size) +{ + struct layout_cell *lc, *lcparent, *lcnew; + u_int sx, sy, xoff, yoff, size1, size2; + + lc = wp->layout_cell; + + /* Copy the old cell size. */ + sx = lc->sx; + sy = lc->sy; + xoff = lc->xoff; + yoff = lc->yoff; + + /* Check there is enough space for the two new panes. */ + switch (type) { + case LAYOUT_LEFTRIGHT: + if (sx < PANE_MINIMUM * 2 + 1) + return (NULL); + break; + case LAYOUT_TOPBOTTOM: + if (sy < PANE_MINIMUM * 2 + 1) + return (NULL); + break; + default: + fatalx("bad layout type"); + } + + if (lc->parent != NULL && lc->parent->type == type) { + /* + * If the parent exists and is of the same type as the split, + * create a new cell and insert it after this one. + */ + + /* Create the new child cell. */ + lcnew = layout_create_cell(lc->parent); + TAILQ_INSERT_AFTER(&lc->parent->cells, lc, lcnew, entry); + } else { + /* + * Otherwise create a new parent and insert it. + */ + + /* Create and insert the replacement parent. */ + lcparent = layout_create_cell(lc->parent); + layout_make_node(lcparent, type); + layout_set_size(lcparent, sx, sy, xoff, yoff); + if (lc->parent == NULL) + wp->window->layout_root = lcparent; + else + TAILQ_REPLACE(&lc->parent->cells, lc, lcparent, entry); + + /* Insert the old cell. */ + lc->parent = lcparent; + TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry); + + /* Create the new child cell. */ + lcnew = layout_create_cell(lcparent); + TAILQ_INSERT_TAIL(&lcparent->cells, lcnew, entry); + } + + /* Set new cell sizes. size is the target size or -1 for middle split, + * size1 is the size of the top/left and size2 the bottom/right. + */ + switch (type) { + case LAYOUT_LEFTRIGHT: + if (size < 0) + size2 = ((sx + 1) / 2) - 1; + else + size2 = size; + if (size2 < PANE_MINIMUM) + size2 = PANE_MINIMUM; + else if (size2 > sx - 2) + size2 = sx - 2; + size1 = sx - 1 - size2; + layout_set_size(lc, size1, sy, xoff, yoff); + layout_set_size(lcnew, size2, sy, xoff + lc->sx + 1, yoff); + break; + case LAYOUT_TOPBOTTOM: + if (size < 0) + size2 = ((sy + 1) / 2) - 1; + else + size2 = size; + if (size2 < PANE_MINIMUM) + size2 = PANE_MINIMUM; + else if (size2 > sy - 2) + size2 = sy - 2; + size1 = sy - 1 - size2; + layout_set_size(lc, sx, size1, xoff, yoff); + layout_set_size(lcnew, sx, size2, xoff, yoff + lc->sy + 1); + break; + default: + fatalx("bad layout type"); + } + + /* Assign the panes. */ + layout_make_leaf(lc, wp); + + return (lcnew); +} + +/* Destroy the cell associated with a pane. */ +void +layout_close_pane(struct window_pane *wp) +{ + /* Remove the cell. */ + layout_destroy_cell(wp->layout_cell, &wp->window->layout_root); + + /* Fix pane offsets and sizes. */ + if (wp->window->layout_root != NULL) { + layout_fix_offsets(wp->window->layout_root); + layout_fix_panes(wp->window, wp->window->sx, wp->window->sy); + } +} diff --git a/external/bsd/tmux/dist/log.c b/external/bsd/tmux/dist/log.c new file mode 100644 index 000000000..f689d69d2 --- /dev/null +++ b/external/bsd/tmux/dist/log.c @@ -0,0 +1,211 @@ +/* $Id: log.c,v 1.2 2011/08/25 16:41:51 joerg Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "tmux.h" + +/* Logging type. */ +#define LOG_TYPE_OFF 0 +#define LOG_TYPE_TTY 1 +#define LOG_TYPE_FILE 2 +int log_type = LOG_TYPE_OFF; + +/* Log file, if needed. */ +FILE *log_file; + +/* Debug level. */ +int log_level; + +void log_vwrite(int, const char *, va_list); +__dead void log_vfatal(const char *, va_list); + +/* Open logging to tty. */ +void +log_open_tty(int level) +{ + log_type = LOG_TYPE_TTY; + log_level = level; + + setlinebuf(stderr); + setlinebuf(stdout); + + tzset(); +} + +/* Open logging to file. */ +void +log_open_file(int level, const char *path) +{ + log_file = fopen(path, "w"); + if (log_file == NULL) + return; + + log_type = LOG_TYPE_FILE; + log_level = level; + + setlinebuf(log_file); + + tzset(); +} + +/* Close logging. */ +void +log_close(void) +{ + if (log_type == LOG_TYPE_FILE) + fclose(log_file); + + log_type = LOG_TYPE_OFF; +} + +/* Write a log message. */ +void +log_vwrite(int pri, const char *msg, va_list ap) +{ + FILE *f = log_file; + + switch (log_type) { + case LOG_TYPE_TTY: + if (pri == LOG_INFO) + f = stdout; + else + f = stderr; + /* FALLTHROUGH */ + case LOG_TYPE_FILE: + if (vfprintf(f, msg, ap) == -1) + exit(1); + if (putc('\n', f) == -1) + exit(1); + fflush(f); + break; + } +} + +/* Log a warning with error string. */ +void printflike1 +log_warn(const char *msg, ...) +{ + va_list ap; + char *fmt; + + va_start(ap, msg); + if (asprintf(&fmt, "%s: %s", msg, strerror(errno)) == -1) + exit(1); + log_vwrite(LOG_CRIT, fmt, ap); + free(fmt); + va_end(ap); +} + +/* Log a warning. */ +void printflike1 +log_warnx(const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + log_vwrite(LOG_CRIT, msg, ap); + va_end(ap); +} + +/* Log an informational message. */ +void printflike1 +log_info(const char *msg, ...) +{ + va_list ap; + + if (log_level > -1) { + va_start(ap, msg); + log_vwrite(LOG_INFO, msg, ap); + va_end(ap); + } +} + +/* Log a debug message. */ +void printflike1 +log_debug(const char *msg, ...) +{ + va_list ap; + + if (log_level > 0) { + va_start(ap, msg); + log_vwrite(LOG_DEBUG, msg, ap); + va_end(ap); + } +} + +/* Log a debug message at level 2. */ +void printflike1 +log_debug2(const char *msg, ...) +{ + va_list ap; + + if (log_level > 1) { + va_start(ap, msg); + log_vwrite(LOG_DEBUG, msg, ap); + va_end(ap); + } +} + +/* Log a critical error, with error string if necessary, and die. */ +__dead void +log_vfatal(const char *msg, va_list ap) +{ + char *fmt; + + if (errno != 0) { + if (asprintf(&fmt, "fatal: %s: %s", msg, strerror(errno)) == -1) + exit(1); + log_vwrite(LOG_CRIT, fmt, ap); + } else { + if (asprintf(&fmt, "fatal: %s", msg) == -1) + exit(1); + log_vwrite(LOG_CRIT, fmt, ap); + } + free(fmt); + + exit(1); +} + +/* Log a critical error, with error string, and die. */ +__dead void printflike1 +log_fatal(const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + log_vfatal(msg, ap); +} + +/* Log a critical error and die. */ +__dead void printflike1 +log_fatalx(const char *msg, ...) +{ + va_list ap; + + errno = 0; + va_start(ap, msg); + log_vfatal(msg, ap); +} diff --git a/external/bsd/tmux/dist/mode-key.c b/external/bsd/tmux/dist/mode-key.c new file mode 100644 index 000000000..eec0c0462 --- /dev/null +++ b/external/bsd/tmux/dist/mode-key.c @@ -0,0 +1,471 @@ +/* $Id: mode-key.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv Exp $ */ + +/* + * Copyright (c) 2008 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * Mode keys. These are the key bindings used when editing (status prompt), and + * in the modes. They are split into two sets of three tables, one set of three + * for vi and the other for emacs key bindings. The three tables are for + * editing, for menu-like modes (choice, more), and for copy modes (copy, + * scroll). + * + * The fixed tables of struct mode_key_entry below are the defaults: they are + * built into a tree of struct mode_key_binding by mode_key_init_trees, which + * can then be modified. + * + * vi command mode is handled by having a mode flag in the struct which allows + * two sets of bindings to be swapped between. A couple of editing commands + * (MODEKEYEDIT_SWITCHMODE and MODEKEYEDIT_SWITCHMODEAPPEND) are special-cased + * to do this. + */ + +/* Edit keys command strings. */ +const struct mode_key_cmdstr mode_key_cmdstr_edit[] = { + { MODEKEYEDIT_BACKSPACE, "backspace" }, + { MODEKEYEDIT_CANCEL, "cancel" }, + { MODEKEYEDIT_COMPLETE, "complete" }, + { MODEKEYEDIT_CURSORLEFT, "cursor-left" }, + { MODEKEYEDIT_CURSORRIGHT, "cursor-right" }, + { MODEKEYEDIT_DELETE, "delete" }, + { MODEKEYEDIT_DELETELINE, "delete-line" }, + { MODEKEYEDIT_DELETETOENDOFLINE, "delete-end-of-line" }, + { MODEKEYEDIT_ENDOFLINE, "end-of-line" }, + { MODEKEYEDIT_ENTER, "enter" }, + { MODEKEYEDIT_HISTORYDOWN, "history-down" }, + { MODEKEYEDIT_HISTORYUP, "history-up" }, + { MODEKEYEDIT_PASTE, "paste" }, + { MODEKEYEDIT_STARTOFLINE, "start-of-line" }, + { MODEKEYEDIT_SWITCHMODE, "switch-mode" }, + { MODEKEYEDIT_SWITCHMODEAPPEND, "switch-mode-append" }, + { MODEKEYEDIT_TRANSPOSECHARS, "transpose-chars" }, + + { 0, NULL } +}; + +/* Choice keys command strings. */ +const struct mode_key_cmdstr mode_key_cmdstr_choice[] = { + { MODEKEYCHOICE_CANCEL, "cancel" }, + { MODEKEYCHOICE_CHOOSE, "choose" }, + { MODEKEYCHOICE_DOWN, "down" }, + { MODEKEYCHOICE_PAGEDOWN, "page-down" }, + { MODEKEYCHOICE_PAGEUP, "page-up" }, + { MODEKEYCHOICE_SCROLLDOWN, "scroll-down" }, + { MODEKEYCHOICE_SCROLLUP, "scroll-up" }, + { MODEKEYCHOICE_UP, "up" }, + + { 0, NULL } +}; + +/* Copy keys command strings. */ +const struct mode_key_cmdstr mode_key_cmdstr_copy[] = { + { MODEKEYCOPY_BACKTOINDENTATION, "back-to-indentation" }, + { MODEKEYCOPY_BOTTOMLINE, "bottom-line" }, + { MODEKEYCOPY_CANCEL, "cancel" }, + { MODEKEYCOPY_CLEARSELECTION, "clear-selection" }, + { MODEKEYCOPY_COPYLINE, "copy-line" }, + { MODEKEYCOPY_COPYENDOFLINE, "copy-end-of-line" }, + { MODEKEYCOPY_COPYSELECTION, "copy-selection" }, + { MODEKEYCOPY_DOWN, "cursor-down" }, + { MODEKEYCOPY_ENDOFLINE, "end-of-line" }, + { MODEKEYCOPY_GOTOLINE, "goto-line" }, + { MODEKEYCOPY_HISTORYBOTTOM, "history-bottom" }, + { MODEKEYCOPY_HISTORYTOP, "history-top" }, + { MODEKEYCOPY_JUMP, "jump-forward" }, + { MODEKEYCOPY_JUMPAGAIN, "jump-again" }, + { MODEKEYCOPY_JUMPREVERSE, "jump-reverse" }, + { MODEKEYCOPY_JUMPBACK, "jump-backward" }, + { MODEKEYCOPY_LEFT, "cursor-left" }, + { MODEKEYCOPY_RECTANGLETOGGLE, "rectangle-toggle" }, + { MODEKEYCOPY_MIDDLELINE, "middle-line" }, + { MODEKEYCOPY_NEXTPAGE, "page-down" }, + { MODEKEYCOPY_NEXTSPACE, "next-space" }, + { MODEKEYCOPY_NEXTSPACEEND, "next-space-end" }, + { MODEKEYCOPY_NEXTWORD, "next-word" }, + { MODEKEYCOPY_NEXTWORDEND, "next-word-end" }, + { MODEKEYCOPY_PREVIOUSPAGE, "page-up" }, + { MODEKEYCOPY_PREVIOUSSPACE, "previous-space" }, + { MODEKEYCOPY_PREVIOUSWORD, "previous-word" }, + { MODEKEYCOPY_RIGHT, "cursor-right" }, + { MODEKEYCOPY_SCROLLDOWN, "scroll-down" }, + { MODEKEYCOPY_SCROLLUP, "scroll-up" }, + { MODEKEYCOPY_SEARCHAGAIN, "search-again" }, + { MODEKEYCOPY_SEARCHDOWN, "search-forward" }, + { MODEKEYCOPY_SEARCHREVERSE, "search-reverse" }, + { MODEKEYCOPY_SEARCHUP, "search-backward" }, + { MODEKEYCOPY_SELECTLINE, "select-line" }, + { MODEKEYCOPY_STARTNUMBERPREFIX, "start-number-prefix" }, + { MODEKEYCOPY_STARTOFLINE, "start-of-line" }, + { MODEKEYCOPY_STARTSELECTION, "begin-selection" }, + { MODEKEYCOPY_TOPLINE, "top-line" }, + { MODEKEYCOPY_UP, "cursor-up" }, + + { 0, NULL } +}; + +/* vi editing keys. */ +const struct mode_key_entry mode_key_vi_edit[] = { + { '\003' /* C-c */, 0, MODEKEYEDIT_CANCEL }, + { '\010' /* C-h */, 0, MODEKEYEDIT_BACKSPACE }, + { '\025' /* C-u */, 0, MODEKEYEDIT_DELETELINE }, + { '\011' /* Tab */, 0, MODEKEYEDIT_COMPLETE }, + { '\033' /* Escape */, 0, MODEKEYEDIT_SWITCHMODE }, + { '\r', 0, MODEKEYEDIT_ENTER }, + { KEYC_BSPACE, 0, MODEKEYEDIT_BACKSPACE }, + { KEYC_DC, 0, MODEKEYEDIT_DELETE }, + + { '$', 1, MODEKEYEDIT_ENDOFLINE }, + { '0', 1, MODEKEYEDIT_STARTOFLINE }, + { 'D', 1, MODEKEYEDIT_DELETETOENDOFLINE }, + { '\003' /* C-c */, 1, MODEKEYEDIT_CANCEL }, + { '\010' /* C-h */, 1, MODEKEYEDIT_BACKSPACE }, + { '\r', 1, MODEKEYEDIT_ENTER }, + { '^', 1, MODEKEYEDIT_STARTOFLINE }, + { 'a', 1, MODEKEYEDIT_SWITCHMODEAPPEND }, + { 'd', 1, MODEKEYEDIT_DELETELINE }, + { 'h', 1, MODEKEYEDIT_CURSORLEFT }, + { 'i', 1, MODEKEYEDIT_SWITCHMODE }, + { 'j', 1, MODEKEYEDIT_HISTORYDOWN }, + { 'k', 1, MODEKEYEDIT_HISTORYUP }, + { 'l', 1, MODEKEYEDIT_CURSORRIGHT }, + { 'p', 1, MODEKEYEDIT_PASTE }, + { KEYC_BSPACE, 1, MODEKEYEDIT_BACKSPACE }, + { KEYC_DC, 1, MODEKEYEDIT_DELETE }, + { KEYC_DOWN, 1, MODEKEYEDIT_HISTORYDOWN }, + { KEYC_LEFT, 1, MODEKEYEDIT_CURSORLEFT }, + { KEYC_RIGHT, 1, MODEKEYEDIT_CURSORRIGHT }, + { KEYC_UP, 1, MODEKEYEDIT_HISTORYUP }, + + { 0, -1, 0 } +}; +struct mode_key_tree mode_key_tree_vi_edit; + +/* vi choice selection keys. */ +const struct mode_key_entry mode_key_vi_choice[] = { + { '\002' /* C-b */, 0, MODEKEYCHOICE_PAGEUP }, + { '\003' /* C-c */, 0, MODEKEYCHOICE_CANCEL }, + { '\005' /* C-e */, 0, MODEKEYCHOICE_SCROLLDOWN }, + { '\006' /* C-f */, 0, MODEKEYCHOICE_PAGEDOWN }, + { '\031' /* C-y */, 0, MODEKEYCHOICE_SCROLLUP }, + { '\r', 0, MODEKEYCHOICE_CHOOSE }, + { 'j', 0, MODEKEYCHOICE_DOWN }, + { 'k', 0, MODEKEYCHOICE_UP }, + { 'q', 0, MODEKEYCHOICE_CANCEL }, + { KEYC_DOWN | KEYC_CTRL,0, MODEKEYCHOICE_SCROLLDOWN }, + { KEYC_DOWN, 0, MODEKEYCHOICE_DOWN }, + { KEYC_NPAGE, 0, MODEKEYCHOICE_PAGEDOWN }, + { KEYC_PPAGE, 0, MODEKEYCHOICE_PAGEUP }, + { KEYC_UP | KEYC_CTRL, 0, MODEKEYCHOICE_SCROLLUP }, + { KEYC_UP, 0, MODEKEYCHOICE_UP }, + + { 0, -1, 0 } +}; +struct mode_key_tree mode_key_tree_vi_choice; + +/* vi copy mode keys. */ +const struct mode_key_entry mode_key_vi_copy[] = { + { ' ', 0, MODEKEYCOPY_STARTSELECTION }, + { '$', 0, MODEKEYCOPY_ENDOFLINE }, + { ',', 0, MODEKEYCOPY_JUMPREVERSE }, + { ';', 0, MODEKEYCOPY_JUMPAGAIN }, + { '/', 0, MODEKEYCOPY_SEARCHDOWN }, + { '0', 0, MODEKEYCOPY_STARTOFLINE }, + { '1', 0, MODEKEYCOPY_STARTNUMBERPREFIX }, + { '2', 0, MODEKEYCOPY_STARTNUMBERPREFIX }, + { '3', 0, MODEKEYCOPY_STARTNUMBERPREFIX }, + { '4', 0, MODEKEYCOPY_STARTNUMBERPREFIX }, + { '5', 0, MODEKEYCOPY_STARTNUMBERPREFIX }, + { '6', 0, MODEKEYCOPY_STARTNUMBERPREFIX }, + { '7', 0, MODEKEYCOPY_STARTNUMBERPREFIX }, + { '8', 0, MODEKEYCOPY_STARTNUMBERPREFIX }, + { '9', 0, MODEKEYCOPY_STARTNUMBERPREFIX }, + { ':', 0, MODEKEYCOPY_GOTOLINE }, + { '?', 0, MODEKEYCOPY_SEARCHUP }, + { 'B', 0, MODEKEYCOPY_PREVIOUSSPACE }, + { 'D', 0, MODEKEYCOPY_COPYENDOFLINE }, + { 'E', 0, MODEKEYCOPY_NEXTSPACEEND }, + { 'F', 0, MODEKEYCOPY_JUMPBACK }, + { 'G', 0, MODEKEYCOPY_HISTORYBOTTOM }, + { 'H', 0, MODEKEYCOPY_TOPLINE }, + { 'J', 0, MODEKEYCOPY_SCROLLDOWN }, + { 'K', 0, MODEKEYCOPY_SCROLLUP }, + { 'L', 0, MODEKEYCOPY_BOTTOMLINE }, + { 'M', 0, MODEKEYCOPY_MIDDLELINE }, + { 'N', 0, MODEKEYCOPY_SEARCHREVERSE }, + { 'W', 0, MODEKEYCOPY_NEXTSPACE }, + { '\002' /* C-b */, 0, MODEKEYCOPY_PREVIOUSPAGE }, + { '\003' /* C-c */, 0, MODEKEYCOPY_CANCEL }, + { '\004' /* C-d */, 0, MODEKEYCOPY_HALFPAGEDOWN }, + { '\005' /* C-e */, 0, MODEKEYCOPY_SCROLLDOWN }, + { '\006' /* C-f */, 0, MODEKEYCOPY_NEXTPAGE }, + { '\010' /* C-h */, 0, MODEKEYCOPY_LEFT }, + { '\025' /* C-u */, 0, MODEKEYCOPY_HALFPAGEUP }, + { '\031' /* C-y */, 0, MODEKEYCOPY_SCROLLUP }, + { '\033' /* Escape */, 0, MODEKEYCOPY_CLEARSELECTION }, + { '\r', 0, MODEKEYCOPY_COPYSELECTION }, + { '^', 0, MODEKEYCOPY_BACKTOINDENTATION }, + { 'b', 0, MODEKEYCOPY_PREVIOUSWORD }, + { 'e', 0, MODEKEYCOPY_NEXTWORDEND }, + { 'f', 0, MODEKEYCOPY_JUMP }, + { 'g', 0, MODEKEYCOPY_HISTORYTOP }, + { 'h', 0, MODEKEYCOPY_LEFT }, + { 'j', 0, MODEKEYCOPY_DOWN }, + { 'k', 0, MODEKEYCOPY_UP }, + { 'l', 0, MODEKEYCOPY_RIGHT }, + { 'n', 0, MODEKEYCOPY_SEARCHAGAIN }, + { 'q', 0, MODEKEYCOPY_CANCEL }, + { 'v', 0, MODEKEYCOPY_RECTANGLETOGGLE }, + { 'w', 0, MODEKEYCOPY_NEXTWORD }, + { KEYC_BSPACE, 0, MODEKEYCOPY_LEFT }, + { KEYC_DOWN | KEYC_CTRL,0, MODEKEYCOPY_SCROLLDOWN }, + { KEYC_DOWN, 0, MODEKEYCOPY_DOWN }, + { KEYC_LEFT, 0, MODEKEYCOPY_LEFT }, + { KEYC_NPAGE, 0, MODEKEYCOPY_NEXTPAGE }, + { KEYC_PPAGE, 0, MODEKEYCOPY_PREVIOUSPAGE }, + { KEYC_RIGHT, 0, MODEKEYCOPY_RIGHT }, + { KEYC_UP | KEYC_CTRL, 0, MODEKEYCOPY_SCROLLUP }, + { KEYC_UP, 0, MODEKEYCOPY_UP }, + + { 0, -1, 0 } +}; +struct mode_key_tree mode_key_tree_vi_copy; + +/* emacs editing keys. */ +const struct mode_key_entry mode_key_emacs_edit[] = { + { '\001' /* C-a */, 0, MODEKEYEDIT_STARTOFLINE }, + { '\002' /* C-b */, 0, MODEKEYEDIT_CURSORLEFT }, + { '\003' /* C-c */, 0, MODEKEYEDIT_CANCEL }, + { '\004' /* C-d */, 0, MODEKEYEDIT_DELETE }, + { '\005' /* C-e */, 0, MODEKEYEDIT_ENDOFLINE }, + { '\006' /* C-f */, 0, MODEKEYEDIT_CURSORRIGHT }, + { '\010' /* C-H */, 0, MODEKEYEDIT_BACKSPACE }, + { '\011' /* Tab */, 0, MODEKEYEDIT_COMPLETE }, + { '\013' /* C-k */, 0, MODEKEYEDIT_DELETETOENDOFLINE }, + { '\016' /* C-n */, 0, MODEKEYEDIT_HISTORYDOWN }, + { '\020' /* C-p */, 0, MODEKEYEDIT_HISTORYUP }, + { '\024' /* C-t */, 0, MODEKEYEDIT_TRANSPOSECHARS }, + { '\025' /* C-u */, 0, MODEKEYEDIT_DELETELINE }, + { '\031' /* C-y */, 0, MODEKEYEDIT_PASTE }, + { '\033' /* Escape */, 0, MODEKEYEDIT_CANCEL }, + { '\r', 0, MODEKEYEDIT_ENTER }, + { 'm' | KEYC_ESCAPE, 0, MODEKEYEDIT_STARTOFLINE }, + { KEYC_BSPACE, 0, MODEKEYEDIT_BACKSPACE }, + { KEYC_DC, 0, MODEKEYEDIT_DELETE }, + { KEYC_DOWN, 0, MODEKEYEDIT_HISTORYDOWN }, + { KEYC_LEFT, 0, MODEKEYEDIT_CURSORLEFT }, + { KEYC_RIGHT, 0, MODEKEYEDIT_CURSORRIGHT }, + { KEYC_UP, 0, MODEKEYEDIT_HISTORYUP }, + + { 0, -1, 0 } +}; +struct mode_key_tree mode_key_tree_emacs_edit; + +/* emacs choice selection keys. */ +const struct mode_key_entry mode_key_emacs_choice[] = { + { '\003' /* C-c */, 0, MODEKEYCHOICE_CANCEL }, + { '\016' /* C-n */, 0, MODEKEYCHOICE_DOWN }, + { '\020' /* C-p */, 0, MODEKEYCHOICE_UP }, + { '\026' /* C-v */, 0, MODEKEYCHOICE_PAGEDOWN }, + { '\033' /* Escape */, 0, MODEKEYCHOICE_CANCEL }, + { '\r', 0, MODEKEYCHOICE_CHOOSE }, + { 'q', 0, MODEKEYCHOICE_CANCEL }, + { 'v' | KEYC_ESCAPE, 0, MODEKEYCHOICE_PAGEUP }, + { KEYC_DOWN | KEYC_CTRL,0, MODEKEYCHOICE_SCROLLDOWN }, + { KEYC_DOWN, 0, MODEKEYCHOICE_DOWN }, + { KEYC_NPAGE, 0, MODEKEYCHOICE_PAGEDOWN }, + { KEYC_PPAGE, 0, MODEKEYCHOICE_PAGEUP }, + { KEYC_UP | KEYC_CTRL, 0, MODEKEYCHOICE_SCROLLUP }, + { KEYC_UP, 0, MODEKEYCHOICE_UP }, + + { 0, -1, 0 } +}; +struct mode_key_tree mode_key_tree_emacs_choice; + +/* emacs copy mode keys. */ +const struct mode_key_entry mode_key_emacs_copy[] = { + { ' ', 0, MODEKEYCOPY_NEXTPAGE }, + { ',', 0, MODEKEYCOPY_JUMPREVERSE }, + { ';', 0, MODEKEYCOPY_JUMPAGAIN }, + { '1' | KEYC_ESCAPE, 0, MODEKEYCOPY_STARTNUMBERPREFIX }, + { '2' | KEYC_ESCAPE, 0, MODEKEYCOPY_STARTNUMBERPREFIX }, + { '3' | KEYC_ESCAPE, 0, MODEKEYCOPY_STARTNUMBERPREFIX }, + { '4' | KEYC_ESCAPE, 0, MODEKEYCOPY_STARTNUMBERPREFIX }, + { '5' | KEYC_ESCAPE, 0, MODEKEYCOPY_STARTNUMBERPREFIX }, + { '6' | KEYC_ESCAPE, 0, MODEKEYCOPY_STARTNUMBERPREFIX }, + { '7' | KEYC_ESCAPE, 0, MODEKEYCOPY_STARTNUMBERPREFIX }, + { '8' | KEYC_ESCAPE, 0, MODEKEYCOPY_STARTNUMBERPREFIX }, + { '9' | KEYC_ESCAPE, 0, MODEKEYCOPY_STARTNUMBERPREFIX }, + { '<' | KEYC_ESCAPE, 0, MODEKEYCOPY_HISTORYTOP }, + { '>' | KEYC_ESCAPE, 0, MODEKEYCOPY_HISTORYBOTTOM }, + { 'F', 0, MODEKEYCOPY_JUMPBACK }, + { 'N', 0, MODEKEYCOPY_SEARCHREVERSE }, + { 'R' | KEYC_ESCAPE, 0, MODEKEYCOPY_TOPLINE }, + { 'R', 0, MODEKEYCOPY_RECTANGLETOGGLE }, + { '\000' /* C-Space */, 0, MODEKEYCOPY_STARTSELECTION }, + { '\001' /* C-a */, 0, MODEKEYCOPY_STARTOFLINE }, + { '\002' /* C-b */, 0, MODEKEYCOPY_LEFT }, + { '\003' /* C-c */, 0, MODEKEYCOPY_CANCEL }, + { '\005' /* C-e */, 0, MODEKEYCOPY_ENDOFLINE }, + { '\006' /* C-f */, 0, MODEKEYCOPY_RIGHT }, + { '\007' /* C-g */, 0, MODEKEYCOPY_CLEARSELECTION }, + { '\013' /* C-k */, 0, MODEKEYCOPY_COPYENDOFLINE }, + { '\016' /* C-n */, 0, MODEKEYCOPY_DOWN }, + { '\020' /* C-p */, 0, MODEKEYCOPY_UP }, + { '\022' /* C-r */, 0, MODEKEYCOPY_SEARCHUP }, + { '\023' /* C-s */, 0, MODEKEYCOPY_SEARCHDOWN }, + { '\026' /* C-v */, 0, MODEKEYCOPY_NEXTPAGE }, + { '\027' /* C-w */, 0, MODEKEYCOPY_COPYSELECTION }, + { '\033' /* Escape */, 0, MODEKEYCOPY_CANCEL }, + { 'N', 0, MODEKEYCOPY_SEARCHREVERSE }, + { 'b' | KEYC_ESCAPE, 0, MODEKEYCOPY_PREVIOUSWORD }, + { 'f', 0, MODEKEYCOPY_JUMP }, + { 'f' | KEYC_ESCAPE, 0, MODEKEYCOPY_NEXTWORDEND }, + { 'g', 0, MODEKEYCOPY_GOTOLINE }, + { 'm' | KEYC_ESCAPE, 0, MODEKEYCOPY_BACKTOINDENTATION }, + { 'n', 0, MODEKEYCOPY_SEARCHAGAIN }, + { 'q', 0, MODEKEYCOPY_CANCEL }, + { 'r' | KEYC_ESCAPE, 0, MODEKEYCOPY_MIDDLELINE }, + { 'v' | KEYC_ESCAPE, 0, MODEKEYCOPY_PREVIOUSPAGE }, + { 'w' | KEYC_ESCAPE, 0, MODEKEYCOPY_COPYSELECTION }, + { KEYC_DOWN | KEYC_CTRL,0, MODEKEYCOPY_SCROLLDOWN }, + { KEYC_DOWN | KEYC_ESCAPE, 0, MODEKEYCOPY_HALFPAGEDOWN }, + { KEYC_DOWN, 0, MODEKEYCOPY_DOWN }, + { KEYC_LEFT, 0, MODEKEYCOPY_LEFT }, + { KEYC_NPAGE, 0, MODEKEYCOPY_NEXTPAGE }, + { KEYC_PPAGE, 0, MODEKEYCOPY_PREVIOUSPAGE }, + { KEYC_RIGHT, 0, MODEKEYCOPY_RIGHT }, + { KEYC_UP | KEYC_CTRL, 0, MODEKEYCOPY_SCROLLUP }, + { KEYC_UP | KEYC_ESCAPE, 0, MODEKEYCOPY_HALFPAGEUP }, + { KEYC_UP, 0, MODEKEYCOPY_UP }, + + { 0, -1, 0 } +}; +struct mode_key_tree mode_key_tree_emacs_copy; + +/* Table mapping key table names to default settings and trees. */ +const struct mode_key_table mode_key_tables[] = { + { "vi-edit", mode_key_cmdstr_edit, + &mode_key_tree_vi_edit, mode_key_vi_edit }, + { "vi-choice", mode_key_cmdstr_choice, + &mode_key_tree_vi_choice, mode_key_vi_choice }, + { "vi-copy", mode_key_cmdstr_copy, + &mode_key_tree_vi_copy, mode_key_vi_copy }, + { "emacs-edit", mode_key_cmdstr_edit, + &mode_key_tree_emacs_edit, mode_key_emacs_edit }, + { "emacs-choice", mode_key_cmdstr_choice, + &mode_key_tree_emacs_choice, mode_key_emacs_choice }, + { "emacs-copy", mode_key_cmdstr_copy, + &mode_key_tree_emacs_copy, mode_key_emacs_copy }, + + { NULL, NULL, NULL, NULL } +}; + +SPLAY_GENERATE(mode_key_tree, mode_key_binding, entry, mode_key_cmp); + +int +mode_key_cmp(struct mode_key_binding *mbind1, struct mode_key_binding *mbind2) +{ + if (mbind1->mode != mbind2->mode) + return (mbind1->mode - mbind2->mode); + return (mbind1->key - mbind2->key); +} + +const char * +mode_key_tostring(const struct mode_key_cmdstr *cmdstr, enum mode_key_cmd cmd) +{ + for (; cmdstr->name != NULL; cmdstr++) { + if (cmdstr->cmd == cmd) + return (cmdstr->name); + } + return (NULL); +} + +enum mode_key_cmd +mode_key_fromstring(const struct mode_key_cmdstr *cmdstr, const char *name) +{ + for (; cmdstr->name != NULL; cmdstr++) { + if (strcasecmp(cmdstr->name, name) == 0) + return (cmdstr->cmd); + } + return (MODEKEY_NONE); +} + +const struct mode_key_table * +mode_key_findtable(const char *name) +{ + const struct mode_key_table *mtab; + + for (mtab = mode_key_tables; mtab->name != NULL; mtab++) { + if (strcasecmp(name, mtab->name) == 0) + return (mtab); + } + return (NULL); +} + +void +mode_key_init_trees(void) +{ + const struct mode_key_table *mtab; + const struct mode_key_entry *ment; + struct mode_key_binding *mbind; + + for (mtab = mode_key_tables; mtab->name != NULL; mtab++) { + SPLAY_INIT(mtab->tree); + for (ment = mtab->table; ment->mode != -1; ment++) { + mbind = xmalloc(sizeof *mbind); + mbind->key = ment->key; + mbind->mode = ment->mode; + mbind->cmd = ment->cmd; + SPLAY_INSERT(mode_key_tree, mtab->tree, mbind); + } + } +} + +void +mode_key_init(struct mode_key_data *mdata, struct mode_key_tree *mtree) +{ + mdata->tree = mtree; + mdata->mode = 0; +} + +enum mode_key_cmd +mode_key_lookup(struct mode_key_data *mdata, int key) +{ + struct mode_key_binding *mbind, mtmp; + + mtmp.key = key; + mtmp.mode = mdata->mode; + if ((mbind = SPLAY_FIND(mode_key_tree, mdata->tree, &mtmp)) == NULL) { + if (mdata->mode != 0) + return (MODEKEY_NONE); + return (MODEKEY_OTHER); + } + + switch (mbind->cmd) { + case MODEKEYEDIT_SWITCHMODE: + case MODEKEYEDIT_SWITCHMODEAPPEND: + mdata->mode = 1 - mdata->mode; + /* FALLTHROUGH */ + default: + return (mbind->cmd); + } +} diff --git a/external/bsd/tmux/dist/names.c b/external/bsd/tmux/dist/names.c new file mode 100644 index 000000000..ef2ec88d2 --- /dev/null +++ b/external/bsd/tmux/dist/names.c @@ -0,0 +1,125 @@ +/* $Id: names.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include +#include + +#include "tmux.h" + +void window_name_callback(unused int, unused short, void *); +char *parse_window_name(const char *); + +void +queue_window_name(struct window *w) +{ + struct timeval tv; + + tv.tv_sec = 0; + tv.tv_usec = NAME_INTERVAL * 1000L; + + evtimer_del(&w->name_timer); + evtimer_set(&w->name_timer, window_name_callback, w); + evtimer_add(&w->name_timer, &tv); +} + +/* ARGSUSED */ +void +window_name_callback(unused int fd, unused short events, void *data) +{ + struct window *w = data; + char *name, *wname; + + queue_window_name(w); /* XXX even if the option is off? */ + if (!options_get_number(&w->options, "automatic-rename")) + return; + + if (w->active->screen != &w->active->base) + name = NULL; + else + name = osdep_get_name(w->active->fd, w->active->tty); + if (name == NULL) + wname = default_window_name(w); + else { + /* + * If tmux is using the default command, it will be a login + * shell and argv[0] may have a - prefix. Remove this if it is + * present. Ick. + */ + if (w->active->cmd != NULL && *w->active->cmd == '\0' && + name != NULL && name[0] == '-' && name[1] != '\0') + wname = parse_window_name(name + 1); + else + wname = parse_window_name(name); + xfree(name); + } + + if (w->active->fd == -1) { + xasprintf(&name, "%s[dead]", wname); + xfree(wname); + wname = name; + } + + if (strcmp(wname, w->name) == 0) + xfree(wname); + else { + xfree(w->name); + w->name = wname; + server_status_window(w); + } +} + +char * +default_window_name(struct window *w) +{ + if (w->active->screen != &w->active->base) + return (xstrdup("[tmux]")); + if (w->active->cmd != NULL && *w->active->cmd != '\0') + return (parse_window_name(w->active->cmd)); + return (parse_window_name(w->active->shell)); +} + +char * +parse_window_name(const char *in) +{ + char *copy, *name, *ptr; + + name = copy = xstrdup(in); + if (strncmp(name, "exec ", (sizeof "exec ") - 1) == 0) + name = name + (sizeof "exec ") - 1; + + while (*name == ' ') + name++; + if ((ptr = strchr(name, ' ')) != NULL) + *ptr = '\0'; + + if (*name != '\0') { + ptr = name + strlen(name) - 1; + while (ptr > name && !isalnum((u_char)*ptr)) + *ptr-- = '\0'; + } + + if (*name == '/') + name = basename(name); + name = xstrdup(name); + xfree(copy); + return (name); +} diff --git a/external/bsd/tmux/dist/options-table.c b/external/bsd/tmux/dist/options-table.c new file mode 100644 index 000000000..50c620e9c --- /dev/null +++ b/external/bsd/tmux/dist/options-table.c @@ -0,0 +1,666 @@ +/* $Id: options-table.c,v 1.1.1.1 2011/08/17 18:40:05 jmmv Exp $ */ + +/* + * Copyright (c) 2011 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * This file has a tables with all the server, session and window + * options. These tables are the master copy of the options with their real + * (user-visible) types, range limits and default values. At start these are + * copied into the runtime global options trees (which only has number and + * string types). These tables are then used to loop up the real type when + * the user sets an option or its value needs to be shown. + */ + +/* Choice option type lists. */ +const char *options_table_mode_keys_list[] = { + "emacs", "vi", NULL +}; +const char *options_table_clock_mode_style_list[] = { + "12", "24", NULL +}; +const char *options_table_status_keys_list[] = { + "emacs", "vi", NULL +}; +const char *options_table_status_justify_list[] = { + "left", "centre", "right", NULL +}; +const char *options_table_bell_action_list[] = { + "none", "any", "current", NULL +}; + +/* Server options. */ +const struct options_table_entry server_options_table[] = { + { .name = "buffer-limit", + .type = OPTIONS_TABLE_NUMBER, + .minimum = 1, + .maximum = INT_MAX, + .default_num = 20 + }, + + { .name = "escape-time", + .type = OPTIONS_TABLE_NUMBER, + .minimum = 0, + .maximum = INT_MAX, + .default_num = 500 + }, + + { .name = "exit-unattached", + .type = OPTIONS_TABLE_FLAG, + .default_num = 0 + }, + + { .name = "quiet", + .type = OPTIONS_TABLE_FLAG, + .default_num = 0 /* overridden in main() */ + }, + + { .name = "set-clipboard", + .type = OPTIONS_TABLE_FLAG, + .default_num = 1 + }, + + { .name = NULL } +}; + +/* Session options. */ +const struct options_table_entry session_options_table[] = { + { .name = "base-index", + .type = OPTIONS_TABLE_NUMBER, + .minimum = 0, + .maximum = INT_MAX, + .default_num = 0 + }, + + { .name = "bell-action", + .type = OPTIONS_TABLE_CHOICE, + .choices = options_table_bell_action_list, + .default_num = BELL_ANY + }, + + { .name = "bell-on-alert", + .type = OPTIONS_TABLE_FLAG, + .default_num = 0 + }, + + { .name = "default-command", + .type = OPTIONS_TABLE_STRING, + .default_str = "" + }, + + { .name = "default-path", + .type = OPTIONS_TABLE_STRING, + .default_str = "" + }, + + { .name = "default-shell", + .type = OPTIONS_TABLE_STRING, + .default_str = _PATH_BSHELL + }, + + { .name = "default-terminal", + .type = OPTIONS_TABLE_STRING, + .default_str = "screen" + }, + + { .name = "destroy-unattached", + .type = OPTIONS_TABLE_FLAG, + .default_num = 0 + }, + + { .name = "detach-on-destroy", + .type = OPTIONS_TABLE_FLAG, + .default_num = 1 + }, + + { .name = "display-panes-active-colour", + .type = OPTIONS_TABLE_COLOUR, + .default_num = 1 + }, + + { .name = "display-panes-colour", + .type = OPTIONS_TABLE_COLOUR, + .default_num = 4 + }, + + { .name = "display-panes-time", + .type = OPTIONS_TABLE_NUMBER, + .minimum = 1, + .maximum = INT_MAX, + .default_num = 1000 + }, + + { .name = "display-time", + .type = OPTIONS_TABLE_NUMBER, + .minimum = 1, + .maximum = INT_MAX, + .default_num = 750 + }, + + { .name = "history-limit", + .type = OPTIONS_TABLE_NUMBER, + .minimum = 0, + .maximum = INT_MAX, + .default_num = 2000 + }, + + { .name = "lock-after-time", + .type = OPTIONS_TABLE_NUMBER, + .minimum = 0, + .maximum = INT_MAX, + .default_num = 0 + }, + + { .name = "lock-command", + .type = OPTIONS_TABLE_STRING, + .default_str = "lock -np" + }, + + { .name = "lock-server", + .type = OPTIONS_TABLE_FLAG, + .default_num = 1 + }, + + { .name = "message-attr", + .type = OPTIONS_TABLE_ATTRIBUTES, + .default_num = 0 + }, + + { .name = "message-bg", + .type = OPTIONS_TABLE_COLOUR, + .default_num = 3 + }, + + { .name = "message-fg", + .type = OPTIONS_TABLE_COLOUR, + .default_num = 0 + }, + + { .name = "message-limit", + .type = OPTIONS_TABLE_NUMBER, + .minimum = 0, + .maximum = INT_MAX, + .default_num = 20 + }, + + { .name = "mouse-resize-pane", + .type = OPTIONS_TABLE_FLAG, + .default_num = 0 + }, + + { .name = "mouse-select-pane", + .type = OPTIONS_TABLE_FLAG, + .default_num = 0 + }, + + { .name = "mouse-select-window", + .type = OPTIONS_TABLE_FLAG, + .default_num = 0 + }, + + { .name = "mouse-utf8", + .type = OPTIONS_TABLE_FLAG, + .default_num = 0 + }, + + { .name = "pane-active-border-bg", + .type = OPTIONS_TABLE_COLOUR, + .default_num = 8 + }, + + { .name = "pane-active-border-fg", + .type = OPTIONS_TABLE_COLOUR, + .default_num = 2 + }, + + { .name = "pane-border-bg", + .type = OPTIONS_TABLE_COLOUR, + .default_num = 8 + }, + + { .name = "pane-border-fg", + .type = OPTIONS_TABLE_COLOUR, + .default_num = 8 + }, + + { .name = "prefix", + .type = OPTIONS_TABLE_KEYS, + /* set in main() */ + }, + + { .name = "repeat-time", + .type = OPTIONS_TABLE_NUMBER, + .minimum = 0, + .maximum = SHRT_MAX, + .default_num = 500 + }, + + { .name = "set-remain-on-exit", + .type = OPTIONS_TABLE_FLAG, + .default_num = 0 + }, + + { .name = "set-titles", + .type = OPTIONS_TABLE_FLAG, + .default_num = 0 + }, + + { .name = "set-titles-string", + .type = OPTIONS_TABLE_STRING, + .default_str = "#S:#I:#W - \"#T\"" + }, + + { .name = "status", + .type = OPTIONS_TABLE_FLAG, + .default_num = 1 + }, + + { .name = "status-attr", + .type = OPTIONS_TABLE_ATTRIBUTES, + .default_num = 0 + }, + + { .name = "status-bg", + .type = OPTIONS_TABLE_COLOUR, + .default_num = 2 + }, + + { .name = "status-fg", + .type = OPTIONS_TABLE_COLOUR, + .default_num = 0 + }, + + { .name = "status-interval", + .type = OPTIONS_TABLE_NUMBER, + .minimum = 0, + .maximum = INT_MAX, + .default_num = 15 + }, + + { .name = "status-justify", + .type = OPTIONS_TABLE_CHOICE, + .choices = options_table_status_justify_list, + .default_num = 0 + }, + + { .name = "status-keys", + .type = OPTIONS_TABLE_CHOICE, + .choices = options_table_status_keys_list, + .default_num = MODEKEY_EMACS + }, + + { .name = "status-left", + .type = OPTIONS_TABLE_STRING, + .default_str = "[#S]" + }, + + { .name = "status-left-attr", + .type = OPTIONS_TABLE_ATTRIBUTES, + .default_num = 0 + }, + + { .name = "status-left-bg", + .type = OPTIONS_TABLE_COLOUR, + .default_num = 8 + }, + + { .name = "status-left-fg", + .type = OPTIONS_TABLE_COLOUR, + .default_num = 8 + }, + + { .name = "status-left-length", + .type = OPTIONS_TABLE_NUMBER, + .minimum = 0, + .maximum = SHRT_MAX, + .default_num = 10 + }, + + { .name = "status-right", + .type = OPTIONS_TABLE_STRING, + .default_str = "\"#22T\" %H:%M %d-%b-%y" + }, + + { .name = "status-right-attr", + .type = OPTIONS_TABLE_ATTRIBUTES, + .default_num = 0 + }, + + { .name = "status-right-bg", + .type = OPTIONS_TABLE_COLOUR, + .default_num = 8 + }, + + { .name = "status-right-fg", + .type = OPTIONS_TABLE_COLOUR, + .default_num = 8 + }, + + { .name = "status-right-length", + .type = OPTIONS_TABLE_NUMBER, + .minimum = 0, + .maximum = SHRT_MAX, + .default_num = 40 + }, + + { .name = "status-utf8", + .type = OPTIONS_TABLE_FLAG, + .default_num = 0 /* overridden in main() */ + }, + + { .name = "terminal-overrides", + .type = OPTIONS_TABLE_STRING, + .default_str = "*88col*:colors=88,*256col*:colors=256" + ",xterm*:XT:Ms=\\E]52;%p1%s;%p2%s\\007" + ":Cc=\\E]12;%p1%s\\007:Cr=\\E]112\\007" + ":Cs=\\E[%p1%d q:Csr=\\E[2 q" + }, + + { .name = "update-environment", + .type = OPTIONS_TABLE_STRING, + .default_str = "DISPLAY SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID " + "SSH_CONNECTION WINDOWID XAUTHORITY" + + }, + + { .name = "visual-activity", + .type = OPTIONS_TABLE_FLAG, + .default_num = 0 + }, + + { .name = "visual-bell", + .type = OPTIONS_TABLE_FLAG, + .default_num = 0 + }, + + { .name = "visual-content", + .type = OPTIONS_TABLE_FLAG, + .default_num = 0 + }, + + { .name = "visual-silence", + .type = OPTIONS_TABLE_FLAG, + .default_num = 0 + }, + + { .name = NULL } +}; + +/* Window options. */ +const struct options_table_entry window_options_table[] = { + { .name = "aggressive-resize", + .type = OPTIONS_TABLE_FLAG, + .default_num = 0 + }, + + { .name = "alternate-screen", + .type = OPTIONS_TABLE_FLAG, + .default_num = 1 + }, + + { .name = "automatic-rename", + .type = OPTIONS_TABLE_FLAG, + .default_num = 1 + }, + + { .name = "clock-mode-colour", + .type = OPTIONS_TABLE_COLOUR, + .default_num = 4 + }, + + { .name = "clock-mode-style", + .type = OPTIONS_TABLE_CHOICE, + .choices = options_table_clock_mode_style_list, + .default_num = 1 + }, + + { .name = "force-height", + .type = OPTIONS_TABLE_NUMBER, + .minimum = 0, + .maximum = INT_MAX, + .default_num = 0 + }, + + { .name = "force-width", + .type = OPTIONS_TABLE_NUMBER, + .minimum = 0, + .maximum = INT_MAX, + .default_num = 0 + }, + + { .name = "main-pane-height", + .type = OPTIONS_TABLE_NUMBER, + .minimum = 1, + .maximum = INT_MAX, + .default_num = 24 + }, + + { .name = "main-pane-width", + .type = OPTIONS_TABLE_NUMBER, + .minimum = 1, + .maximum = INT_MAX, + .default_num = 80 + }, + + { .name = "mode-attr", + .type = OPTIONS_TABLE_ATTRIBUTES, + .default_num = 0 + }, + + { .name = "mode-bg", + .type = OPTIONS_TABLE_COLOUR, + .default_num = 3 + }, + + { .name = "mode-fg", + .type = OPTIONS_TABLE_COLOUR, + .default_num = 0 + }, + + { .name = "mode-keys", + .type = OPTIONS_TABLE_CHOICE, + .choices = options_table_mode_keys_list, + .default_num = MODEKEY_EMACS + }, + + { .name = "mode-mouse", + .type = OPTIONS_TABLE_FLAG, + .default_num = 0 + }, + + { .name = "monitor-activity", + .type = OPTIONS_TABLE_FLAG, + .default_num = 0 + }, + + { .name = "monitor-content", + .type = OPTIONS_TABLE_STRING, + .default_str = "" + }, + + { .name = "monitor-silence", + .type = OPTIONS_TABLE_NUMBER, + .minimum = 0, + .maximum = INT_MAX, + .default_num = 0 + }, + + { .name = "other-pane-height", + .type = OPTIONS_TABLE_NUMBER, + .minimum = 0, + .maximum = INT_MAX, + .default_num = 0 + }, + + { .name = "other-pane-width", + .type = OPTIONS_TABLE_NUMBER, + .minimum = 0, + .maximum = INT_MAX, + .default_num = 0 + }, + + { .name = "remain-on-exit", + .type = OPTIONS_TABLE_FLAG, + .default_num = 0 + }, + + { .name = "synchronize-panes", + .type = OPTIONS_TABLE_FLAG, + .default_num = 0 + }, + + { .name = "utf8", + .type = OPTIONS_TABLE_FLAG, + .default_num = 0 /* overridden in main() */ + }, + + { .name = "window-status-alert-attr", + .type = OPTIONS_TABLE_ATTRIBUTES, + .default_num = GRID_ATTR_REVERSE + }, + + { .name = "window-status-alert-bg", + .type = OPTIONS_TABLE_COLOUR, + .default_num = 8 + }, + + { .name = "window-status-alert-fg", + .type = OPTIONS_TABLE_COLOUR, + .default_num = 8 + }, + + { .name = "window-status-attr", + .type = OPTIONS_TABLE_ATTRIBUTES, + .default_num = 0 + }, + + { .name = "window-status-bg", + .type = OPTIONS_TABLE_COLOUR, + .default_num = 8 + }, + + { .name = "window-status-current-attr", + .type = OPTIONS_TABLE_ATTRIBUTES, + .default_num = 0 + }, + + { .name = "window-status-current-bg", + .type = OPTIONS_TABLE_COLOUR, + .default_num = 8 + }, + + { .name = "window-status-current-fg", + .type = OPTIONS_TABLE_COLOUR, + .default_num = 8 + }, + + { .name = "window-status-current-format", + .type = OPTIONS_TABLE_STRING, + .default_str = "#I:#W#F" + }, + + { .name = "window-status-fg", + .type = OPTIONS_TABLE_COLOUR, + .default_num = 8 + }, + + { .name = "window-status-format", + .type = OPTIONS_TABLE_STRING, + .default_str = "#I:#W#F" + }, + + { .name = "word-separators", + .type = OPTIONS_TABLE_STRING, + .default_str = " -_@" + }, + + { .name = "xterm-keys", + .type = OPTIONS_TABLE_FLAG, + .default_num = 0 + }, + + { .name = NULL } +}; + +/* Populate an options tree from a table. */ +void +options_table_populate_tree( + const struct options_table_entry *table, struct options *oo) +{ + const struct options_table_entry *oe; + + for (oe = table; oe->name != NULL; oe++) { + if (oe->default_str != NULL) + options_set_string(oo, oe->name, "%s", oe->default_str); + else + options_set_number(oo, oe->name, oe->default_num); + } +} + +/* Print an option using its type from the table. */ +const char * +options_table_print_entry( + const struct options_table_entry *oe, struct options_entry *o) +{ + static char out[BUFSIZ]; + const char *s; + struct keylist *keylist; + u_int i; + + *out = '\0'; + switch (oe->type) { + case OPTIONS_TABLE_STRING: + xsnprintf(out, sizeof out, "\"%s\"", o->str); + break; + case OPTIONS_TABLE_NUMBER: + xsnprintf(out, sizeof out, "%lld", o->num); + break; + case OPTIONS_TABLE_KEYS: + keylist = o->data; + for (i = 0; i < ARRAY_LENGTH(keylist); i++) { + s = key_string_lookup_key(ARRAY_ITEM(keylist, i)); + strlcat(out, s, sizeof out); + if (i != ARRAY_LENGTH(keylist) - 1) + strlcat(out, ",", sizeof out); + } + break; + case OPTIONS_TABLE_COLOUR: + s = colour_tostring(o->num); + xsnprintf(out, sizeof out, "%s", s); + break; + case OPTIONS_TABLE_ATTRIBUTES: + s = attributes_tostring(o->num); + xsnprintf(out, sizeof out, "%s", s); + break; + case OPTIONS_TABLE_FLAG: + if (o->num) + strlcpy(out, "on", sizeof out); + else + strlcpy(out, "off", sizeof out); + break; + case OPTIONS_TABLE_CHOICE: + s = oe->choices[o->num]; + xsnprintf(out, sizeof out, "%s", s); + break; + } + return (out); +} diff --git a/external/bsd/tmux/dist/options.c b/external/bsd/tmux/dist/options.c new file mode 100644 index 000000000..ae8662d99 --- /dev/null +++ b/external/bsd/tmux/dist/options.c @@ -0,0 +1,201 @@ +/* $Id: options.c,v 1.3 2011/08/17 18:48:36 jmmv Exp $ */ + +/* + * Copyright (c) 2008 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +/* + * Option handling; each option has a name, type and value and is stored in + * a splay tree. + */ + +SPLAY_GENERATE(options_tree, options_entry, entry, options_cmp); + +int +options_cmp(struct options_entry *o1, struct options_entry *o2) +{ + return (strcmp(o1->name, o2->name)); +} + +void +options_init(struct options *oo, struct options *parent) +{ + SPLAY_INIT(&oo->tree); + oo->parent = parent; +} + +void +options_free(struct options *oo) +{ + struct options_entry *o; + + while (!SPLAY_EMPTY(&oo->tree)) { + o = SPLAY_ROOT(&oo->tree); + SPLAY_REMOVE(options_tree, &oo->tree, o); + xfree(o->name); + if (o->type == OPTIONS_STRING) + xfree(o->str); + else if (o->type == OPTIONS_DATA) + o->freefn(o->data); + xfree(o); + } +} + +struct options_entry * +options_find1(struct options *oo, const char *name) +{ + struct options_entry p; + + p.name = __UNCONST(name); + return (SPLAY_FIND(options_tree, &oo->tree, &p)); +} + +struct options_entry * +options_find(struct options *oo, const char *name) +{ + struct options_entry *o, p; + + p.name = __UNCONST(name); + o = SPLAY_FIND(options_tree, &oo->tree, &p); + while (o == NULL) { + oo = oo->parent; + if (oo == NULL) + break; + o = SPLAY_FIND(options_tree, &oo->tree, &p); + } + return (o); +} + +void +options_remove(struct options *oo, const char *name) +{ + struct options_entry *o; + + if ((o = options_find1(oo, name)) == NULL) + return; + + SPLAY_REMOVE(options_tree, &oo->tree, o); + xfree(o->name); + if (o->type == OPTIONS_STRING) + xfree(o->str); + else if (o->type == OPTIONS_DATA) + o->freefn(o->data); + xfree(o); +} + +struct options_entry *printflike3 +options_set_string(struct options *oo, const char *name, const char *fmt, ...) +{ + struct options_entry *o; + va_list ap; + + if ((o = options_find1(oo, name)) == NULL) { + o = xmalloc(sizeof *o); + o->name = xstrdup(name); + SPLAY_INSERT(options_tree, &oo->tree, o); + } else if (o->type == OPTIONS_STRING) + xfree(o->str); + else if (o->type == OPTIONS_DATA) + o->freefn(o->data); + + va_start(ap, fmt); + o->type = OPTIONS_STRING; + xvasprintf(&o->str, fmt, ap); + va_end(ap); + return (o); +} + +char * +options_get_string(struct options *oo, const char *name) +{ + struct options_entry *o; + + if ((o = options_find(oo, name)) == NULL) + fatalx("missing option"); + if (o->type != OPTIONS_STRING) + fatalx("option not a string"); + return (o->str); +} + +struct options_entry * +options_set_number(struct options *oo, const char *name, long long value) +{ + struct options_entry *o; + + if ((o = options_find1(oo, name)) == NULL) { + o = xmalloc(sizeof *o); + o->name = xstrdup(name); + SPLAY_INSERT(options_tree, &oo->tree, o); + } else if (o->type == OPTIONS_STRING) + xfree(o->str); + else if (o->type == OPTIONS_DATA) + o->freefn(o->data); + + o->type = OPTIONS_NUMBER; + o->num = value; + return (o); +} + +long long +options_get_number(struct options *oo, const char *name) +{ + struct options_entry *o; + + if ((o = options_find(oo, name)) == NULL) + fatalx("missing option"); + if (o->type != OPTIONS_NUMBER) + fatalx("option not a number"); + return (o->num); +} + +struct options_entry * +options_set_data( + struct options *oo, const char *name, void *value, void (*freefn)(void *)) +{ + struct options_entry *o; + + if ((o = options_find1(oo, name)) == NULL) { + o = xmalloc(sizeof *o); + o->name = xstrdup(name); + SPLAY_INSERT(options_tree, &oo->tree, o); + } else if (o->type == OPTIONS_STRING) + xfree(o->str); + else if (o->type == OPTIONS_DATA) + o->freefn(o->data); + + o->type = OPTIONS_DATA; + o->data = value; + o->freefn = freefn; + return (o); +} + +void * +options_get_data(struct options *oo, const char *name) +{ + struct options_entry *o; + + if ((o = options_find(oo, name)) == NULL) + fatalx("missing option"); + if (o->type != OPTIONS_DATA) + fatalx("option not data"); + return (o->data); +} diff --git a/external/bsd/tmux/dist/osdep-aix.c b/external/bsd/tmux/dist/osdep-aix.c new file mode 100644 index 000000000..039733f67 --- /dev/null +++ b/external/bsd/tmux/dist/osdep-aix.c @@ -0,0 +1,35 @@ +/* $Id: osdep-aix.c,v 1.1.1.1 2011/08/17 18:40:05 jmmv Exp $ */ + +/* + * Copyright (c) 2011 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +char * +osdep_get_name(unused int fd, unused char *tty) +{ + return (NULL); +} + +struct event_base * +osdep_event_init(void) +{ + return (event_init()); +} diff --git a/external/bsd/tmux/dist/osdep-darwin.c b/external/bsd/tmux/dist/osdep-darwin.c new file mode 100644 index 000000000..d62277649 --- /dev/null +++ b/external/bsd/tmux/dist/osdep-darwin.c @@ -0,0 +1,61 @@ +/* $Id: osdep-darwin.c,v 1.1.1.2 2011/08/17 18:40:06 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Joshua Elsasser + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include +#include +#include +#include + +char *osdep_get_name(int, char *); +struct event_base *osdep_event_init(void); + +#define unused __attribute__ ((unused)) + +char * +osdep_get_name(int fd, unused char *tty) +{ + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, 0 }; + size_t size; + struct kinfo_proc kp; + + if ((mib[3] = tcgetpgrp(fd)) == -1) + return (NULL); + + size = sizeof kp; + if (sysctl(mib, 4, &kp, &size, NULL, 0) == -1) + return (NULL); + if (*kp.kp_proc.p_comm == '\0') + return (NULL); + + return (strdup(kp.kp_proc.p_comm)); +} + +struct event_base * +osdep_event_init(void) +{ + /* + * On OS X, kqueue and poll are both completely broken and don't + * work on anything except socket file descriptors (yes, really). + */ + setenv("EVENT_NOKQUEUE", "1", 1); + setenv("EVENT_NOPOLL", "1", 1); + return (event_init()); +} diff --git a/external/bsd/tmux/dist/osdep-freebsd.c b/external/bsd/tmux/dist/osdep-freebsd.c new file mode 100644 index 000000000..2462452b0 --- /dev/null +++ b/external/bsd/tmux/dist/osdep-freebsd.c @@ -0,0 +1,142 @@ +/* $Id: osdep-freebsd.c,v 1.1.1.2 2011/08/17 18:40:06 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +struct kinfo_proc *cmp_procs(struct kinfo_proc *, struct kinfo_proc *); +char *osdep_get_name(int, char *); +struct event_base *osdep_event_init(void); + +#ifndef nitems +#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) +#endif + +#define is_runnable(p) \ + ((p)->ki_stat == SRUN || (p)->ki_stat == SIDL) +#define is_stopped(p) \ + ((p)->ki_stat == SSTOP || (p)->ki_stat == SZOMB) + +struct kinfo_proc * +cmp_procs(struct kinfo_proc *p1, struct kinfo_proc *p2) +{ + if (is_runnable(p1) && !is_runnable(p2)) + return (p1); + if (!is_runnable(p1) && is_runnable(p2)) + return (p2); + + if (is_stopped(p1) && !is_stopped(p2)) + return (p1); + if (!is_stopped(p1) && is_stopped(p2)) + return (p2); + + if (p1->ki_estcpu > p2->ki_estcpu) + return (p1); + if (p1->ki_estcpu < p2->ki_estcpu) + return (p2); + + if (p1->ki_slptime < p2->ki_slptime) + return (p1); + if (p1->ki_slptime > p2->ki_slptime) + return (p2); + + if (strcmp(p1->ki_comm, p2->ki_comm) < 0) + return (p1); + if (strcmp(p1->ki_comm, p2->ki_comm) > 0) + return (p2); + + if (p1->ki_pid > p2->ki_pid) + return (p1); + return (p2); +} + +char * +osdep_get_name(int fd, char *tty) +{ + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PGRP, 0 }; + struct stat sb; + size_t len; + struct kinfo_proc *buf, *newbuf, *bestp; + u_int i; + char *name; + + buf = NULL; + + if (stat(tty, &sb) == -1) + return (NULL); + if ((mib[3] = tcgetpgrp(fd)) == -1) + return (NULL); + +retry: + if (sysctl(mib, nitems(mib), NULL, &len, NULL, 0) == -1) + return (NULL); + len = (len * 5) / 4; + + if ((newbuf = realloc(buf, len)) == NULL) + goto error; + buf = newbuf; + + if (sysctl(mib, nitems(mib), buf, &len, NULL, 0) == -1) { + if (errno == ENOMEM) + goto retry; + goto error; + } + + bestp = NULL; + for (i = 0; i < len / sizeof (struct kinfo_proc); i++) { + if (buf[i].ki_tdev != sb.st_rdev) + continue; + if (bestp == NULL) + bestp = &buf[i]; + else + bestp = cmp_procs(&buf[i], bestp); + } + + name = NULL; + if (bestp != NULL) + name = strdup(bestp->ki_comm); + + free(buf); + return (name); + +error: + free(buf); + return (NULL); +} + +struct event_base * +osdep_event_init(void) +{ + /* + * On some versions of FreeBSD, kqueue doesn't work properly on tty + * file descriptors. This is fixed in recent FreeBSD versions. + */ + setenv("EVENT_NOKQUEUE", "1", 1); + return (event_init()); +} diff --git a/external/bsd/tmux/dist/osdep-hpux.c b/external/bsd/tmux/dist/osdep-hpux.c new file mode 100644 index 000000000..03bc97961 --- /dev/null +++ b/external/bsd/tmux/dist/osdep-hpux.c @@ -0,0 +1,35 @@ +/* $Id: osdep-hpux.c,v 1.1.1.1 2011/08/17 18:40:06 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +char * +osdep_get_name(unused int fd, unused char *tty) +{ + return (NULL); +} + +struct event_base * +osdep_event_init(void) +{ + return (event_init()); +} diff --git a/external/bsd/tmux/dist/osdep-linux.c b/external/bsd/tmux/dist/osdep-linux.c new file mode 100644 index 000000000..98210004f --- /dev/null +++ b/external/bsd/tmux/dist/osdep-linux.c @@ -0,0 +1,76 @@ +/* $Id: osdep-linux.c,v 1.1.1.2 2011/08/17 18:40:06 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include +#include +#include +#include + +#include "tmux.h" + +char * +osdep_get_name(int fd, unused char *tty) +{ + FILE *f; + char *path, *buf; + size_t len; + int ch; + pid_t pgrp; + + if ((pgrp = tcgetpgrp(fd)) == -1) + return (NULL); + + xasprintf(&path, "/proc/%lld/cmdline", (long long) pgrp); + if ((f = fopen(path, "r")) == NULL) { + xfree(path); + return (NULL); + } + xfree(path); + + len = 0; + buf = NULL; + while ((ch = fgetc(f)) != EOF) { + if (ch == '\0') + break; + buf = xrealloc(buf, 1, len + 2); + buf[len++] = ch; + } + if (buf != NULL) + buf[len] = '\0'; + + fclose(f); + return (buf); +} + +struct event_base * +osdep_event_init(void) +{ + /* + * On Linux, epoll doesn't work on /dev/null (yes, really). + * + * This has been commented because libevent versions up until the very + * latest (1.4 git or 2.0.10) do not handle signals properly when using + * poll or select, causing hangs. + * + */ + /* setenv("EVENT_NOEPOLL", "1", 1); */ + return (event_init()); +} diff --git a/external/bsd/tmux/dist/osdep-netbsd.c b/external/bsd/tmux/dist/osdep-netbsd.c new file mode 100644 index 000000000..161bb6baa --- /dev/null +++ b/external/bsd/tmux/dist/osdep-netbsd.c @@ -0,0 +1,130 @@ +/* $Id: osdep-netbsd.c,v 1.1.1.2 2011/08/17 18:40:06 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define is_runnable(p) \ + ((p)->p_stat == LSRUN || (p)->p_stat == SIDL) +#define is_stopped(p) \ + ((p)->p_stat == SSTOP || (p)->p_stat == SZOMB) + +struct kinfo_proc2 *cmp_procs(struct kinfo_proc2 *, struct kinfo_proc2 *); +char *osdep_get_name(int, char *); +struct event_base *osdep_event_init(void); + +struct kinfo_proc2 * +cmp_procs(struct kinfo_proc2 *p1, struct kinfo_proc2 *p2) +{ + if (is_runnable(p1) && !is_runnable(p2)) + return (p1); + if (!is_runnable(p1) && is_runnable(p2)) + return (p2); + + if (is_stopped(p1) && !is_stopped(p2)) + return (p1); + if (!is_stopped(p1) && is_stopped(p2)) + return (p2); + + if (p1->p_estcpu > p2->p_estcpu) + return (p1); + if (p1->p_estcpu < p2->p_estcpu) + return (p2); + + if (p1->p_slptime < p2->p_slptime) + return (p1); + if (p1->p_slptime > p2->p_slptime) + return (p2); + + if (p1->p_pid > p2->p_pid) + return (p1); + return (p2); +} + +char * +osdep_get_name(int fd, __unused char *tty) +{ + int mib[6]; + struct stat sb; + size_t len, i; + struct kinfo_proc2 *buf, *newbuf, *bestp; + char *name; + + if (stat(tty, &sb) == -1) + return (NULL); + if ((mib[3] = tcgetpgrp(fd)) == -1) + return (NULL); + + buf = NULL; + len = sizeof(bestp); + mib[0] = CTL_KERN; + mib[1] = KERN_PROC2; + mib[2] = KERN_PROC_PGRP; + mib[4] = sizeof (*buf); + mib[5] = 0; + +retry: + if (sysctl(mib, __arraycount(mib), NULL, &len, NULL, 0) == -1) + return (NULL); + + if ((newbuf = realloc(buf, len * sizeof (*buf))) == NULL) + goto error; + buf = newbuf; + + mib[5] = len / sizeof(*buf); + if (sysctl(mib, __arraycount(mib), buf, &len, NULL, 0) == -1) { + if (errno == ENOMEM) + goto retry; /* possible infinite loop? */ + goto error; + } + + bestp = NULL; + for (i = 0; i < len / sizeof (*buf); i++) { + if (buf[i].p_tdev != sb.st_rdev) + continue; + if (bestp == NULL) + bestp = &buf[i]; + else + bestp = cmp_procs(&buf[i], bestp); + } + + name = NULL; + if (bestp != NULL) + name = strdup(bestp->p_comm); + + free(buf); + return (name); + +error: + free(buf); + return (NULL); +} + +struct event_base * +osdep_event_init(void) +{ + return (event_init()); +} diff --git a/external/bsd/tmux/dist/osdep-openbsd.c b/external/bsd/tmux/dist/osdep-openbsd.c new file mode 100644 index 000000000..c94da3135 --- /dev/null +++ b/external/bsd/tmux/dist/osdep-openbsd.c @@ -0,0 +1,140 @@ +/* $Id: osdep-openbsd.c,v 1.1.1.2 2011/08/17 18:40:06 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifndef nitems +#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) +#endif + +#define is_runnable(p) \ + ((p)->p_stat == SRUN || (p)->p_stat == SIDL || (p)->p_stat == SONPROC) +#define is_stopped(p) \ + ((p)->p_stat == SSTOP || (p)->p_stat == SZOMB || (p)->p_stat == SDEAD) + +struct kinfo_proc *cmp_procs(struct kinfo_proc *, struct kinfo_proc *); +char *osdep_get_name(int, char *); +struct event_base *osdep_event_init(void); + +struct kinfo_proc * +cmp_procs(struct kinfo_proc *p1, struct kinfo_proc *p2) +{ + if (is_runnable(p1) && !is_runnable(p2)) + return (p1); + if (!is_runnable(p1) && is_runnable(p2)) + return (p2); + + if (is_stopped(p1) && !is_stopped(p2)) + return (p1); + if (!is_stopped(p1) && is_stopped(p2)) + return (p2); + + if (p1->p_estcpu > p2->p_estcpu) + return (p1); + if (p1->p_estcpu < p2->p_estcpu) + return (p2); + + if (p1->p_slptime < p2->p_slptime) + return (p1); + if (p1->p_slptime > p2->p_slptime) + return (p2); + + if ((p1->p_flag & P_SINTR) && !(p2->p_flag & P_SINTR)) + return (p1); + if (!(p1->p_flag & P_SINTR) && (p2->p_flag & P_SINTR)) + return (p2); + + if (strcmp(p1->p_comm, p2->p_comm) < 0) + return (p1); + if (strcmp(p1->p_comm, p2->p_comm) > 0) + return (p2); + + if (p1->p_pid > p2->p_pid) + return (p1); + return (p2); +} + +char * +osdep_get_name(int fd, char *tty) +{ + int mib[6] = { CTL_KERN, KERN_PROC, KERN_PROC_PGRP, 0, + sizeof(struct kinfo_proc), 0 }; + struct stat sb; + size_t len; + struct kinfo_proc *buf, *newbuf, *bestp; + u_int i; + char *name; + + buf = NULL; + + if (stat(tty, &sb) == -1) + return (NULL); + if ((mib[3] = tcgetpgrp(fd)) == -1) + return (NULL); + +retry: + if (sysctl(mib, nitems(mib), NULL, &len, NULL, 0) == -1) + return (NULL); + len = (len * 5) / 4; + + if ((newbuf = realloc(buf, len)) == NULL) + goto error; + buf = newbuf; + + mib[5] = (int)(len / sizeof(struct kinfo_proc)); + if (sysctl(mib, nitems(mib), buf, &len, NULL, 0) == -1) { + if (errno == ENOMEM) + goto retry; + goto error; + } + + bestp = NULL; + for (i = 0; i < len / sizeof (struct kinfo_proc); i++) { + if ((dev_t)buf[i].p_tdev != sb.st_rdev) + continue; + if (bestp == NULL) + bestp = &buf[i]; + else + bestp = cmp_procs(&buf[i], bestp); + } + + name = NULL; + if (bestp != NULL) + name = strdup(bestp->p_comm); + + free(buf); + return (name); + +error: + free(buf); + return (NULL); +} + +struct event_base * +osdep_event_init(void) +{ + return (event_init()); +} diff --git a/external/bsd/tmux/dist/osdep-sunos.c b/external/bsd/tmux/dist/osdep-sunos.c new file mode 100644 index 000000000..298e9116a --- /dev/null +++ b/external/bsd/tmux/dist/osdep-sunos.c @@ -0,0 +1,72 @@ +/* $Id: osdep-sunos.c,v 1.1.1.2 2011/08/17 18:40:06 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Todd Carson + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "tmux.h" + +char * +osdep_get_name(int fd, char *tty) +{ + struct psinfo p; + struct stat st; + char *path; + ssize_t bytes; + int f; + pid_t pgrp; + + if ((f = open(tty, O_RDONLY)) < 0) + return (NULL); + + if ((fstat(f, &st) != 0) || + (ioctl(f, TIOCGPGRP, &pgrp) != 0)) { + close(f); + return (NULL); + } + close(f); + + xasprintf(&path, "/proc/%hu/psinfo", pgrp); + f = open(path, O_RDONLY); + xfree(path); + if (f < 0) + return (NULL); + + bytes = read(f, &p, sizeof(p)); + close(f); + if (bytes != sizeof(p)) + return (NULL); + + if (p.pr_ttydev != st.st_rdev) + return (NULL); + + return (xstrdup(p.pr_fname)); +} + +struct event_base * +osdep_event_init(void) +{ + return (event_init()); +} diff --git a/external/bsd/tmux/dist/osdep-unknown.c b/external/bsd/tmux/dist/osdep-unknown.c new file mode 100644 index 000000000..9903ee601 --- /dev/null +++ b/external/bsd/tmux/dist/osdep-unknown.c @@ -0,0 +1,35 @@ +/* $Id: osdep-unknown.c,v 1.1.1.2 2011/08/17 18:40:06 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +char * +osdep_get_name(unused int fd, unused char *tty) +{ + return (NULL); +} + +struct event_base * +osdep_event_init(void) +{ + return (event_init()); +} diff --git a/external/bsd/tmux/dist/paste.c b/external/bsd/tmux/dist/paste.c new file mode 100644 index 000000000..8f3d6358e --- /dev/null +++ b/external/bsd/tmux/dist/paste.c @@ -0,0 +1,169 @@ +/* $Id: paste.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include + +#include "tmux.h" + +/* + * Stack of paste buffers. Note that paste buffer data is not necessarily a C + * string! + */ + +/* Return each item of the stack in turn. */ +struct paste_buffer * +paste_walk_stack(struct paste_stack *ps, u_int *idx) +{ + struct paste_buffer *pb; + + pb = paste_get_index(ps, *idx); + (*idx)++; + return (pb); +} + +/* Get the top item on the stack. */ +struct paste_buffer * +paste_get_top(struct paste_stack *ps) +{ + if (ARRAY_LENGTH(ps) == 0) + return (NULL); + return (ARRAY_FIRST(ps)); +} + +/* Get an item by its index. */ +struct paste_buffer * +paste_get_index(struct paste_stack *ps, u_int idx) +{ + if (idx >= ARRAY_LENGTH(ps)) + return (NULL); + return (ARRAY_ITEM(ps, idx)); +} + +/* Free the top item on the stack. */ +int +paste_free_top(struct paste_stack *ps) +{ + struct paste_buffer *pb; + + if (ARRAY_LENGTH(ps) == 0) + return (-1); + + pb = ARRAY_FIRST(ps); + ARRAY_REMOVE(ps, 0); + + xfree(pb->data); + xfree(pb); + + return (0); +} + +/* Free an item by index. */ +int +paste_free_index(struct paste_stack *ps, u_int idx) +{ + struct paste_buffer *pb; + + if (idx >= ARRAY_LENGTH(ps)) + return (-1); + + pb = ARRAY_ITEM(ps, idx); + ARRAY_REMOVE(ps, idx); + + xfree(pb->data); + xfree(pb); + + return (0); +} + +/* + * Add an item onto the top of the stack, freeing the bottom if at limit. Note + * that the caller is responsible for allocating data. + */ +void +paste_add(struct paste_stack *ps, char *data, size_t size, u_int limit) +{ + struct paste_buffer *pb; + + if (size == 0) + return; + + while (ARRAY_LENGTH(ps) >= limit) { + pb = ARRAY_LAST(ps); + xfree(pb->data); + xfree(pb); + ARRAY_TRUNC(ps, 1); + } + + pb = xmalloc(sizeof *pb); + ARRAY_INSERT(ps, 0, pb); + + pb->data = data; + pb->size = size; +} + + +/* + * Replace an item on the stack. Note that the caller is responsible for + * allocating data. + */ +int +paste_replace(struct paste_stack *ps, u_int idx, char *data, size_t size) +{ + struct paste_buffer *pb; + + if (size == 0) + return (0); + + if (idx >= ARRAY_LENGTH(ps)) + return (-1); + + pb = ARRAY_ITEM(ps, idx); + xfree(pb->data); + + pb->data = data; + pb->size = size; + + return (0); +} + +/* Convert a buffer into a visible string. */ +char * +paste_print(struct paste_buffer *pb, size_t width) +{ + char *buf; + size_t len, used; + + if (width < 3) + width = 3; + buf = xmalloc(width * 4 + 1); + + len = pb->size; + if (len > width) + len = width; + + used = strvisx(buf, pb->data, len, VIS_OCTAL|VIS_TAB|VIS_NL); + if (pb->size > width || used > width) { + buf[width - 3] = '\0'; + strlcat(buf, "...", width); + } + + return (buf); +} diff --git a/external/bsd/tmux/dist/resize.c b/external/bsd/tmux/dist/resize.c new file mode 100644 index 000000000..b213047c5 --- /dev/null +++ b/external/bsd/tmux/dist/resize.c @@ -0,0 +1,145 @@ +/* $Id: resize.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * Recalculate window and session sizes. + * + * Every session has the size of the smallest client it is attached to and + * every window the size of the smallest session it is attached to. + * + * So, when a client is resized or a session attached to or detached from a + * client, the window sizes must be recalculated. For each session, find the + * smallest client it is attached to, and resize it to that size. Then for + * every window, find the smallest session it is attached to, resize it to that + * size and clear and redraw every client with it as the current window. + * + * This is quite inefficient - better/additional data structures are needed + * to make it better. + * + * As a side effect, this function updates the SESSION_UNATTACHED flag. This + * flag is necessary to make sure unattached sessions do not limit the size of + * windows that are attached both to them and to other (attached) sessions. + */ + +void +recalculate_sizes(void) +{ + struct session *s; + struct client *c; + struct window *w; + struct window_pane *wp; + u_int i, j, ssx, ssy, has, limit; + int flag; + + RB_FOREACH(s, sessions, &sessions) { + ssx = ssy = UINT_MAX; + for (j = 0; j < ARRAY_LENGTH(&clients); j++) { + c = ARRAY_ITEM(&clients, j); + if (c == NULL || c->flags & CLIENT_SUSPENDED) + continue; + if (c->session == s) { + if (c->tty.sx < ssx) + ssx = c->tty.sx; + if (c->tty.sy < ssy) + ssy = c->tty.sy; + } + } + if (ssx == UINT_MAX || ssy == UINT_MAX) { + s->flags |= SESSION_UNATTACHED; + continue; + } + s->flags &= ~SESSION_UNATTACHED; + + if (options_get_number(&s->options, "status")) { + if (ssy == 0) + ssy = 1; + else + ssy--; + } + if (s->sx == ssx && s->sy == ssy) + continue; + + log_debug( + "session size %u,%u (was %u,%u)", ssx, ssy, s->sx, s->sy); + + s->sx = ssx; + s->sy = ssy; + } + + for (i = 0; i < ARRAY_LENGTH(&windows); i++) { + w = ARRAY_ITEM(&windows, i); + if (w == NULL) + continue; + flag = options_get_number(&w->options, "aggressive-resize"); + + ssx = ssy = UINT_MAX; + RB_FOREACH(s, sessions, &sessions) { + if (s->flags & SESSION_UNATTACHED) + continue; + if (flag) + has = s->curw->window == w; + else + has = session_has(s, w) != NULL; + if (has) { + if (s->sx < ssx) + ssx = s->sx; + if (s->sy < ssy) + ssy = s->sy; + } + } + if (ssx == UINT_MAX || ssy == UINT_MAX) + continue; + + limit = options_get_number(&w->options, "force-width"); + if (limit != 0 && ssx > limit) + ssx = limit; + limit = options_get_number(&w->options, "force-height"); + if (limit != 0 && ssy > limit) + ssy = limit; + + if (w->sx == ssx && w->sy == ssy) + continue; + + log_debug( + "window size %u,%u (was %u,%u)", ssx, ssy, w->sx, w->sy); + + layout_resize(w, ssx, ssy); + window_resize(w, ssx, ssy); + + /* + * If the current pane is now not visible, move to the next + * that is. + */ + wp = w->active; + while (!window_pane_visible(w->active)) { + w->active = TAILQ_PREV(w->active, window_panes, entry); + if (w->active == NULL) + w->active = TAILQ_LAST(&w->panes, window_panes); + if (w->active == wp) + break; + } + + server_redraw_window(w); + } +} diff --git a/external/bsd/tmux/dist/screen-redraw.c b/external/bsd/tmux/dist/screen-redraw.c new file mode 100644 index 000000000..1feef3bd2 --- /dev/null +++ b/external/bsd/tmux/dist/screen-redraw.c @@ -0,0 +1,323 @@ +/* $Id: screen-redraw.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +int screen_redraw_cell_border1(struct window_pane *, u_int, u_int); +int screen_redraw_cell_border(struct client *, u_int, u_int); +int screen_redraw_check_cell(struct client *, u_int, u_int); +void screen_redraw_draw_number(struct client *, struct window_pane *); + +#define CELL_INSIDE 0 +#define CELL_LEFTRIGHT 1 +#define CELL_TOPBOTTOM 2 +#define CELL_TOPLEFT 3 +#define CELL_TOPRIGHT 4 +#define CELL_BOTTOMLEFT 5 +#define CELL_BOTTOMRIGHT 6 +#define CELL_TOPJOIN 7 +#define CELL_BOTTOMJOIN 8 +#define CELL_LEFTJOIN 9 +#define CELL_RIGHTJOIN 10 +#define CELL_JOIN 11 +#define CELL_OUTSIDE 12 + +#define CELL_BORDERS " xqlkmjwvtun~" + +/* Check if cell is on the border of a particular pane. */ +int +screen_redraw_cell_border1(struct window_pane *wp, u_int px, u_int py) +{ + /* Inside pane. */ + if (px >= wp->xoff && px < wp->xoff + wp->sx && + py >= wp->yoff && py < wp->yoff + wp->sy) + return (0); + + /* Left/right borders. */ + if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= wp->yoff + wp->sy) { + if (wp->xoff != 0 && px == wp->xoff - 1) + return (1); + if (px == wp->xoff + wp->sx) + return (1); + } + + /* Top/bottom borders. */ + if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= wp->xoff + wp->sx) { + if (wp->yoff != 0 && py == wp->yoff - 1) + return (1); + if (py == wp->yoff + wp->sy) + return (1); + } + + /* Outside pane. */ + return (-1); +} + +/* Check if a cell is on the pane border. */ +int +screen_redraw_cell_border(struct client *c, u_int px, u_int py) +{ + struct window *w = c->session->curw->window; + struct window_pane *wp; + int retval; + + /* Check all the panes. */ + TAILQ_FOREACH(wp, &w->panes, entry) { + if (!window_pane_visible(wp)) + continue; + if ((retval = screen_redraw_cell_border1(wp, px, py)) != -1) + return (retval); + } + + return (0); +} + +/* Check if cell inside a pane. */ +int +screen_redraw_check_cell(struct client *c, u_int px, u_int py) +{ + struct window *w = c->session->curw->window; + struct window_pane *wp; + int borders; + + if (px > w->sx || py > w->sy) + return (CELL_OUTSIDE); + + TAILQ_FOREACH(wp, &w->panes, entry) { + if (!window_pane_visible(wp)) + continue; + + /* If outside the pane and its border, skip it. */ + if ((wp->xoff != 0 && px < wp->xoff - 1) || + px > wp->xoff + wp->sx || + (wp->yoff != 0 && py < wp->yoff - 1) || + py > wp->yoff + wp->sy) + continue; + + /* If definitely inside, return so. */ + if (!screen_redraw_cell_border(c, px, py)) + return (CELL_INSIDE); + + /* + * Construct a bitmask of whether the cells to the left (bit + * 4), right, top, and bottom (bit 1) of this cell are borders. + */ + borders = 0; + if (px == 0 || screen_redraw_cell_border(c, px - 1, py)) + borders |= 8; + if (px <= w->sx && screen_redraw_cell_border(c, px + 1, py)) + borders |= 4; + if (py == 0 || screen_redraw_cell_border(c, px, py - 1)) + borders |= 2; + if (py <= w->sy && screen_redraw_cell_border(c, px, py + 1)) + borders |= 1; + + /* + * Figure out what kind of border this cell is. Only one bit + * set doesn't make sense (can't have a border cell with no + * others connected). + */ + switch (borders) { + case 15: /* 1111, left right top bottom */ + return (CELL_JOIN); + case 14: /* 1110, left right top */ + return (CELL_BOTTOMJOIN); + case 13: /* 1101, left right bottom */ + return (CELL_TOPJOIN); + case 12: /* 1100, left right */ + return (CELL_TOPBOTTOM); + case 11: /* 1011, left top bottom */ + return (CELL_RIGHTJOIN); + case 10: /* 1010, left top */ + return (CELL_BOTTOMRIGHT); + case 9: /* 1001, left bottom */ + return (CELL_TOPRIGHT); + case 7: /* 0111, right top bottom */ + return (CELL_LEFTJOIN); + case 6: /* 0110, right top */ + return (CELL_BOTTOMLEFT); + case 5: /* 0101, right bottom */ + return (CELL_TOPLEFT); + case 3: /* 0011, top bottom */ + return (CELL_LEFTRIGHT); + } + } + + return (CELL_OUTSIDE); +} + +/* Redraw entire screen. */ +void +screen_redraw_screen(struct client *c, int status_only, int borders_only) +{ + struct window *w = c->session->curw->window; + struct tty *tty = &c->tty; + struct window_pane *wp; + struct grid_cell active_gc, other_gc; + u_int i, j, type; + int status, fg, bg; + + /* Suspended clients should not be updated. */ + if (c->flags & CLIENT_SUSPENDED) + return; + + /* Get status line, er, status. */ + if (c->message_string != NULL || c->prompt_string != NULL) + status = 1; + else + status = options_get_number(&c->session->options, "status"); + + /* If only drawing status and it is present, don't need the rest. */ + if (status_only && status) { + tty_draw_line(tty, &c->status, 0, 0, tty->sy - 1); + tty_reset(tty); + return; + } + + /* Set up pane border attributes. */ + memcpy(&other_gc, &grid_default_cell, sizeof other_gc); + memcpy(&active_gc, &grid_default_cell, sizeof active_gc); + active_gc.data = other_gc.data = 'x'; /* not space */ + active_gc.attr = other_gc.attr = GRID_ATTR_CHARSET; + fg = options_get_number(&c->session->options, "pane-border-fg"); + colour_set_fg(&other_gc, fg); + bg = options_get_number(&c->session->options, "pane-border-bg"); + colour_set_bg(&other_gc, bg); + fg = options_get_number(&c->session->options, "pane-active-border-fg"); + colour_set_fg(&active_gc, fg); + bg = options_get_number(&c->session->options, "pane-active-border-bg"); + colour_set_bg(&active_gc, bg); + + /* Draw background and borders. */ + for (j = 0; j < tty->sy - status; j++) { + if (status_only && j != tty->sy - 1) + continue; + for (i = 0; i < tty->sx; i++) { + type = screen_redraw_check_cell(c, i, j); + if (type == CELL_INSIDE) + continue; + if (screen_redraw_cell_border1(w->active, i, j) == 1) + tty_attributes(tty, &active_gc); + else + tty_attributes(tty, &other_gc); + tty_cursor(tty, i, j); + tty_putc(tty, CELL_BORDERS[type]); + } + } + + /* If only drawing borders, that's it. */ + if (borders_only) + return; + + /* Draw the panes, if necessary. */ + TAILQ_FOREACH(wp, &w->panes, entry) { + if (!window_pane_visible(wp)) + continue; + for (i = 0; i < wp->sy; i++) { + if (status_only && wp->yoff + i != tty->sy - 1) + continue; + tty_draw_line(tty, wp->screen, i, wp->xoff, wp->yoff); + } + if (c->flags & CLIENT_IDENTIFY) + screen_redraw_draw_number(c, wp); + } + + /* Draw the status line. */ + if (status) + tty_draw_line(tty, &c->status, 0, 0, tty->sy - 1); + tty_reset(tty); +} + +/* Draw a single pane. */ +void +screen_redraw_pane(struct client *c, struct window_pane *wp) +{ + u_int i; + + for (i = 0; i < wp->sy; i++) + tty_draw_line(&c->tty, wp->screen, i, wp->xoff, wp->yoff); + tty_reset(&c->tty); +} + +/* Draw number on a pane. */ +void +screen_redraw_draw_number(struct client *c, struct window_pane *wp) +{ + struct tty *tty = &c->tty; + struct session *s = c->session; + struct options *oo = &s->options; + struct window *w = wp->window; + struct grid_cell gc; + u_int idx, px, py, i, j, xoff, yoff; + int colour, active_colour; + char buf[16], *ptr; + size_t len; + + idx = window_pane_index(w, wp); + len = xsnprintf(buf, sizeof buf, "%u", idx); + + if (wp->sx < len) + return; + colour = options_get_number(oo, "display-panes-colour"); + active_colour = options_get_number(oo, "display-panes-active-colour"); + + px = wp->sx / 2; py = wp->sy / 2; + xoff = wp->xoff; yoff = wp->yoff; + + if (wp->sx < len * 6 || wp->sy < 5) { + tty_cursor(tty, xoff + px - len / 2, yoff + py); + memcpy(&gc, &grid_default_cell, sizeof gc); + gc.data = '_'; /* not space */ + if (w->active == wp) + colour_set_fg(&gc, active_colour); + else + colour_set_fg(&gc, colour); + tty_attributes(tty, &gc); + tty_puts(tty, buf); + return; + } + + px -= len * 3; + py -= 2; + + memcpy(&gc, &grid_default_cell, sizeof gc); + gc.data = '_'; /* not space */ + if (w->active == wp) + colour_set_bg(&gc, active_colour); + else + colour_set_bg(&gc, colour); + tty_attributes(tty, &gc); + for (ptr = buf; *ptr != '\0'; ptr++) { + if (*ptr < '0' || *ptr > '9') + continue; + idx = *ptr - '0'; + + for (j = 0; j < 5; j++) { + for (i = px; i < px + 5; i++) { + tty_cursor(tty, xoff + i, yoff + py + j); + if (clock_table[idx][j][i - px]) + tty_putc(tty, ' '); + } + } + px += 6; + } +} diff --git a/external/bsd/tmux/dist/screen-write.c b/external/bsd/tmux/dist/screen-write.c new file mode 100644 index 000000000..2fdc36159 --- /dev/null +++ b/external/bsd/tmux/dist/screen-write.c @@ -0,0 +1,1220 @@ +/* $Id: screen-write.c,v 1.3 2011/08/17 18:48:36 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +void screen_write_initctx(struct screen_write_ctx *, struct tty_ctx *, int); +void screen_write_overwrite(struct screen_write_ctx *, u_int); +int screen_write_combine( + struct screen_write_ctx *, const struct utf8_data *); + +/* Initialise writing with a window. */ +void +screen_write_start( + struct screen_write_ctx *ctx, struct window_pane *wp, struct screen *s) +{ + ctx->wp = wp; + if (wp != NULL && s == NULL) + ctx->s = wp->screen; + else + ctx->s = s; +} + +/* Finish writing. */ +/* ARGSUSED */ +void +screen_write_stop(unused struct screen_write_ctx *ctx) +{ +} + +/* Write character. */ +void +screen_write_putc( + struct screen_write_ctx *ctx, struct grid_cell *gc, u_char ch) +{ + gc->data = ch; + screen_write_cell(ctx, gc, NULL); +} + +/* Calculate string length, with embedded formatting. */ +size_t printflike2 +screen_write_cstrlen(int utf8flag, const char *fmt, ...) +{ + va_list ap; + char *msg, *msg2, *ptr, *ptr2; + size_t size; + + va_start(ap, fmt); + xvasprintf(&msg, fmt, ap); + va_end(ap); + msg2 = xmalloc(strlen(msg) + 1); + + ptr = msg; + ptr2 = msg2; + while (*ptr != '\0') { + if (ptr[0] == '#' && ptr[1] == '[') { + while (*ptr != ']' && *ptr != '\0') + ptr++; + if (*ptr == ']') + ptr++; + continue; + } + *ptr2++ = *ptr++; + } + *ptr2 = '\0'; + + size = screen_write_strlen(utf8flag, "%s", msg2); + + xfree(msg); + xfree(msg2); + + return (size); +} + +/* Calculate string length. */ +size_t printflike2 +screen_write_strlen(int utf8flag, const char *fmt, ...) +{ + va_list ap; + char *msg; + struct utf8_data utf8data; + char *ptr; + size_t left, size = 0; + + va_start(ap, fmt); + xvasprintf(&msg, fmt, ap); + va_end(ap); + + ptr = msg; + while (*ptr != '\0') { + if (utf8flag && *ptr & 0x80 && utf8_open(&utf8data, (u_char)*ptr)) { + ptr++; + + left = strlen(ptr); + if (left < utf8data.size - 1) + break; + while (utf8_append(&utf8data, (u_char)*ptr)) + ptr++; + ptr++; + + size += utf8data.width; + } else { + size++; + ptr++; + } + } + + xfree(msg); + return (size); +} + +/* Write simple string (no UTF-8 or maximum length). */ +void printflike3 +screen_write_puts( + struct screen_write_ctx *ctx, struct grid_cell *gc, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + screen_write_vnputs(ctx, -1, gc, 0, fmt, ap); + va_end(ap); +} + +/* Write string with length limit (-1 for unlimited). */ +void printflike5 +screen_write_nputs(struct screen_write_ctx *ctx, + ssize_t maxlen, struct grid_cell *gc, int utf8flag, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + screen_write_vnputs(ctx, maxlen, gc, utf8flag, fmt, ap); + va_end(ap); +} + +void +screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, + struct grid_cell *gc, int utf8flag, const char *fmt, va_list ap) +{ + char *msg; + struct utf8_data utf8data; + char *ptr; + size_t left, size = 0; + + xvasprintf(&msg, fmt, ap); + + ptr = msg; + while (*ptr != '\0') { + if (utf8flag && *ptr & 0x80 && utf8_open(&utf8data, (u_char)*ptr)) { + ptr++; + + left = strlen(ptr); + if (left < utf8data.size - 1) + break; + while (utf8_append(&utf8data, (u_char)*ptr)) + ptr++; + ptr++; + + if (maxlen > 0 && + size + utf8data.width > (size_t) maxlen) { + while (size < (size_t) maxlen) { + screen_write_putc(ctx, gc, ' '); + size++; + } + break; + } + size += utf8data.width; + + gc->flags |= GRID_FLAG_UTF8; + screen_write_cell(ctx, gc, &utf8data); + gc->flags &= ~GRID_FLAG_UTF8; + } else { + if (maxlen > 0 && size + 1 > (size_t) maxlen) + break; + + size++; + screen_write_putc(ctx, gc, (u_char)*ptr); + ptr++; + } + } + + xfree(msg); +} + +/* Write string, similar to nputs, but with embedded formatting (#[]). */ +void printflike5 +screen_write_cnputs(struct screen_write_ctx *ctx, + ssize_t maxlen, struct grid_cell *gc, int utf8flag, const char *fmt, ...) +{ + struct grid_cell lgc; + struct utf8_data utf8data; + va_list ap; + char *msg; + char *ptr, *last; + size_t left, size = 0; + + va_start(ap, fmt); + xvasprintf(&msg, fmt, ap); + va_end(ap); + + memcpy(&lgc, gc, sizeof lgc); + + ptr = msg; + while (*ptr != '\0') { + if (ptr[0] == '#' && ptr[1] == '[') { + ptr += 2; + last = ptr + strcspn(ptr, "]"); + if (*last == '\0') { + /* No ]. Not much point in doing anything. */ + break; + } + *last = '\0'; + + screen_write_parsestyle(gc, &lgc, ptr); + ptr = last + 1; + continue; + } + + if (utf8flag && *ptr & 0x80 && utf8_open(&utf8data, (u_char)*ptr)) { + ptr++; + + left = strlen(ptr); + if (left < utf8data.size - 1) + break; + while (utf8_append(&utf8data, (u_char)*ptr)) + ptr++; + ptr++; + + if (maxlen > 0 && + size + utf8data.width > (size_t) maxlen) { + while (size < (size_t) maxlen) { + screen_write_putc(ctx, gc, ' '); + size++; + } + break; + } + size += utf8data.width; + + lgc.flags |= GRID_FLAG_UTF8; + screen_write_cell(ctx, &lgc, &utf8data); + lgc.flags &= ~GRID_FLAG_UTF8; + } else { + if (maxlen > 0 && size + 1 > (size_t) maxlen) + break; + + size++; + screen_write_putc(ctx, &lgc, (u_char)*ptr); + ptr++; + } + } + + xfree(msg); +} + +/* Parse an embedded style of the form "fg=colour,bg=colour,bright,...". */ +void +screen_write_parsestyle( + struct grid_cell *defgc, struct grid_cell *gc, const char *in) +{ + const char delimiters[] = " ,"; + char tmp[32]; + int val; + size_t end; + u_char fg, bg, attr, flags; + + if (*in == '\0') + return; + if (strchr(delimiters, in[strlen(in) - 1]) != NULL) + return; + + fg = gc->fg; + bg = gc->bg; + attr = gc->attr; + flags = gc->flags; + do { + end = strcspn(in, delimiters); + if (end > (sizeof tmp) - 1) + return; + memcpy(tmp, in, end); + tmp[end] = '\0'; + + if (strcasecmp(tmp, "default") == 0) { + fg = defgc->fg; + bg = defgc->bg; + attr = defgc->attr; + } else if (end > 3 && strncasecmp(tmp + 1, "g=", 2) == 0) { + if ((val = colour_fromstring(tmp + 3)) == -1) + return; + if (*in == 'f' || *in == 'F') { + if (val != 8) { + if (val & 0x100) { + flags |= GRID_FLAG_FG256; + val &= ~0x100; + } else + flags &= ~GRID_FLAG_FG256; + fg = val; + } else + fg = defgc->fg; + } else if (*in == 'b' || *in == 'B') { + if (val != 8) { + if (val & 0x100) { + flags |= GRID_FLAG_BG256; + val &= ~0x100; + } else + flags &= ~GRID_FLAG_BG256; + bg = val; + } else + bg = defgc->bg; + } else + return; + } else if (end > 2 && strncasecmp(tmp, "no", 2) == 0) { + if ((val = attributes_fromstring(tmp + 2)) == -1) + return; + attr &= ~val; + } else { + if ((val = attributes_fromstring(tmp)) == -1) + return; + attr |= val; + } + + in += end + strspn(in + end, delimiters); + } while (*in != '\0'); + gc->fg = fg; + gc->bg = bg; + gc->attr = attr; + gc->flags = flags; +} + +/* Copy from another screen. */ +void +screen_write_copy(struct screen_write_ctx *ctx, + struct screen *src, u_int px, u_int py, u_int nx, u_int ny) +{ + struct screen *s = ctx->s; + struct grid *gd = src->grid; + struct grid_line *gl; + const struct grid_cell *gc; + const struct grid_utf8 *gu; + struct utf8_data utf8data; + u_int xx, yy, cx, cy, ax, bx; + + cx = s->cx; + cy = s->cy; + for (yy = py; yy < py + ny; yy++) { + gl = &gd->linedata[yy]; + if (yy < gd->hsize + gd->sy) { + /* + * Find start and end position and copy between + * them. Limit to the real end of the line then use a + * clear EOL only if copying to the end, otherwise + * could overwrite whatever is there already. + */ + if (px > gl->cellsize) + ax = gl->cellsize; + else + ax = px; + if (px + nx == gd->sx && px + nx > gl->cellsize) + bx = gl->cellsize; + else + bx = px + nx; + + for (xx = ax; xx < bx; xx++) { + if (xx >= gl->cellsize) + gc = &grid_default_cell; + else + gc = &gl->celldata[xx]; + if (!(gc->flags & GRID_FLAG_UTF8)) { + screen_write_cell(ctx, gc, NULL); + continue; + } + /* Reinject the UTF-8 sequence. */ + gu = &gl->utf8data[xx]; + utf8data.size = grid_utf8_copy(gu, + (char *)utf8data.data, + sizeof utf8data.data); + utf8data.width = gu->width; + screen_write_cell(ctx, gc, &utf8data); + } + if (px + nx == gd->sx && px + nx > gl->cellsize) + screen_write_clearendofline(ctx); + } else + screen_write_clearline(ctx); + cy++; + screen_write_cursormove(ctx, cx, cy); + } +} + +/* Set up context for TTY command. */ +void +screen_write_initctx( + struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, int save_last) +{ + struct screen *s = ctx->s; + struct grid *gd = s->grid; + const struct grid_cell *gc; + const struct grid_utf8 *gu; + u_int xx; + + ttyctx->wp = ctx->wp; + + ttyctx->ocx = s->cx; + ttyctx->ocy = s->cy; + + ttyctx->orlower = s->rlower; + ttyctx->orupper = s->rupper; + + if (!save_last) + return; + + /* Save the last cell on the screen. */ + gc = &grid_default_cell; + for (xx = 1; xx <= screen_size_x(s); xx++) { + gc = grid_view_peek_cell(gd, screen_size_x(s) - xx, s->cy); + if (!(gc->flags & GRID_FLAG_PADDING)) + break; + } + ttyctx->last_width = xx; + memcpy(&ttyctx->last_cell, gc, sizeof ttyctx->last_cell); + if (gc->flags & GRID_FLAG_UTF8) { + gu = grid_view_peek_utf8(gd, screen_size_x(s) - xx, s->cy); + memcpy(&ttyctx->last_utf8, gu, sizeof ttyctx->last_utf8); + } +} + +/* Cursor up by ny. */ +void +screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny) +{ + struct screen *s = ctx->s; + + if (ny == 0) + ny = 1; + + if (s->cy < s->rupper) { + /* Above region. */ + if (ny > s->cy) + ny = s->cy; + } else { + /* Below region. */ + if (ny > s->cy - s->rupper) + ny = s->cy - s->rupper; + } + if (ny == 0) + return; + + s->cy -= ny; +} + +/* Cursor down by ny. */ +void +screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny) +{ + struct screen *s = ctx->s; + + if (ny == 0) + ny = 1; + + if (s->cy > s->rlower) { + /* Below region. */ + if (ny > screen_size_y(s) - 1 - s->cy) + ny = screen_size_y(s) - 1 - s->cy; + } else { + /* Above region. */ + if (ny > s->rlower - s->cy) + ny = s->rlower - s->cy; + } + if (ny == 0) + return; + + s->cy += ny; +} + +/* Cursor right by nx. */ +void +screen_write_cursorright(struct screen_write_ctx *ctx, u_int nx) +{ + struct screen *s = ctx->s; + + if (nx == 0) + nx = 1; + + if (nx > screen_size_x(s) - 1 - s->cx) + nx = screen_size_x(s) - 1 - s->cx; + if (nx == 0) + return; + + s->cx += nx; +} + +/* Cursor left by nx. */ +void +screen_write_cursorleft(struct screen_write_ctx *ctx, u_int nx) +{ + struct screen *s = ctx->s; + + if (nx == 0) + nx = 1; + + if (nx > s->cx) + nx = s->cx; + if (nx == 0) + return; + + s->cx -= nx; +} + +/* Backspace; cursor left unless at start of wrapped line when can move up. */ +void +screen_write_backspace(struct screen_write_ctx *ctx) +{ + struct screen *s = ctx->s; + struct grid_line *gl; + + if (s->cx == 0) { + if (s->cy == 0) + return; + gl = &s->grid->linedata[s->grid->hsize + s->cy - 1]; + if (gl->flags & GRID_LINE_WRAPPED) { + s->cy--; + s->cx = screen_size_x(s) - 1; + } + } else + s->cx--; +} + +/* VT100 alignment test. */ +void +screen_write_alignmenttest(struct screen_write_ctx *ctx) +{ + struct screen *s = ctx->s; + struct tty_ctx ttyctx; + struct grid_cell gc; + u_int xx, yy; + + screen_write_initctx(ctx, &ttyctx, 0); + + memcpy(&gc, &grid_default_cell, sizeof gc); + gc.data = 'E'; + + for (yy = 0; yy < screen_size_y(s); yy++) { + for (xx = 0; xx < screen_size_x(s); xx++) + grid_view_set_cell(s->grid, xx, yy, &gc); + } + + s->cx = 0; + s->cy = 0; + + s->rupper = 0; + + s->rlower = screen_size_y(s) - 1; + + tty_write(tty_cmd_alignmenttest, &ttyctx); +} + +/* Insert nx characters. */ +void +screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx) +{ + struct screen *s = ctx->s; + struct tty_ctx ttyctx; + + if (nx == 0) + nx = 1; + + if (nx > screen_size_x(s) - s->cx) + nx = screen_size_x(s) - s->cx; + if (nx == 0) + return; + + screen_write_initctx(ctx, &ttyctx, 0); + + if (s->cx <= screen_size_x(s) - 1) + grid_view_insert_cells(s->grid, s->cx, s->cy, nx); + + ttyctx.num = nx; + tty_write(tty_cmd_insertcharacter, &ttyctx); +} + +/* Delete nx characters. */ +void +screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx) +{ + struct screen *s = ctx->s; + struct tty_ctx ttyctx; + + if (nx == 0) + nx = 1; + + if (nx > screen_size_x(s) - s->cx) + nx = screen_size_x(s) - s->cx; + if (nx == 0) + return; + + screen_write_initctx(ctx, &ttyctx, 0); + + if (s->cx <= screen_size_x(s) - 1) + grid_view_delete_cells(s->grid, s->cx, s->cy, nx); + + ttyctx.num = nx; + tty_write(tty_cmd_deletecharacter, &ttyctx); +} + +/* Insert ny lines. */ +void +screen_write_insertline(struct screen_write_ctx *ctx, u_int ny) +{ + struct screen *s = ctx->s; + struct tty_ctx ttyctx; + + if (ny == 0) + ny = 1; + + if (s->cy < s->rupper || s->cy > s->rlower) { + if (ny > screen_size_y(s) - s->cy) + ny = screen_size_y(s) - s->cy; + if (ny == 0) + return; + + screen_write_initctx(ctx, &ttyctx, 0); + + grid_view_insert_lines(s->grid, s->cy, ny); + + ttyctx.num = ny; + tty_write(tty_cmd_insertline, &ttyctx); + return; + } + + if (ny > s->rlower + 1 - s->cy) + ny = s->rlower + 1 - s->cy; + if (ny == 0) + return; + + screen_write_initctx(ctx, &ttyctx, 0); + + if (s->cy < s->rupper || s->cy > s->rlower) + grid_view_insert_lines(s->grid, s->cy, ny); + else + grid_view_insert_lines_region(s->grid, s->rlower, s->cy, ny); + + ttyctx.num = ny; + tty_write(tty_cmd_insertline, &ttyctx); +} + +/* Delete ny lines. */ +void +screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny) +{ + struct screen *s = ctx->s; + struct tty_ctx ttyctx; + + if (ny == 0) + ny = 1; + + if (s->cy < s->rupper || s->cy > s->rlower) { + if (ny > screen_size_y(s) - s->cy) + ny = screen_size_y(s) - s->cy; + if (ny == 0) + return; + + screen_write_initctx(ctx, &ttyctx, 0); + + grid_view_delete_lines(s->grid, s->cy, ny); + + ttyctx.num = ny; + tty_write(tty_cmd_deleteline, &ttyctx); + return; + } + + if (ny > s->rlower + 1 - s->cy) + ny = s->rlower + 1 - s->cy; + if (ny == 0) + return; + + screen_write_initctx(ctx, &ttyctx, 0); + + if (s->cy < s->rupper || s->cy > s->rlower) + grid_view_delete_lines(s->grid, s->cy, ny); + else + grid_view_delete_lines_region(s->grid, s->rlower, s->cy, ny); + + ttyctx.num = ny; + tty_write(tty_cmd_deleteline, &ttyctx); +} + +/* Clear line at cursor. */ +void +screen_write_clearline(struct screen_write_ctx *ctx) +{ + struct screen *s = ctx->s; + struct tty_ctx ttyctx; + + screen_write_initctx(ctx, &ttyctx, 0); + + grid_view_clear(s->grid, 0, s->cy, screen_size_x(s), 1); + + tty_write(tty_cmd_clearline, &ttyctx); +} + +/* Clear to end of line from cursor. */ +void +screen_write_clearendofline(struct screen_write_ctx *ctx) +{ + struct screen *s = ctx->s; + struct tty_ctx ttyctx; + u_int sx; + + screen_write_initctx(ctx, &ttyctx, 0); + + sx = screen_size_x(s); + + if (s->cx <= sx - 1) + grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1); + + tty_write(tty_cmd_clearendofline, &ttyctx); +} + +/* Clear to start of line from cursor. */ +void +screen_write_clearstartofline(struct screen_write_ctx *ctx) +{ + struct screen *s = ctx->s; + struct tty_ctx ttyctx; + u_int sx; + + screen_write_initctx(ctx, &ttyctx, 0); + + sx = screen_size_x(s); + + if (s->cx > sx - 1) + grid_view_clear(s->grid, 0, s->cy, sx, 1); + else + grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1); + + tty_write(tty_cmd_clearstartofline, &ttyctx); +} + +/* Move cursor to px,py. */ +void +screen_write_cursormove(struct screen_write_ctx *ctx, u_int px, u_int py) +{ + struct screen *s = ctx->s; + + if (px > screen_size_x(s) - 1) + px = screen_size_x(s) - 1; + if (py > screen_size_y(s) - 1) + py = screen_size_y(s) - 1; + + s->cx = px; + s->cy = py; +} + +/* Set cursor mode. */ +void +screen_write_cursormode(struct screen_write_ctx *ctx, int state) +{ + struct screen *s = ctx->s; + + if (state) + s->mode |= MODE_CURSOR; + else + s->mode &= ~MODE_CURSOR; +} + +/* Reverse index (up with scroll). */ +void +screen_write_reverseindex(struct screen_write_ctx *ctx) +{ + struct screen *s = ctx->s; + struct tty_ctx ttyctx; + + screen_write_initctx(ctx, &ttyctx, 0); + + if (s->cy == s->rupper) + grid_view_scroll_region_down(s->grid, s->rupper, s->rlower); + else if (s->cy > 0) + s->cy--; + + tty_write(tty_cmd_reverseindex, &ttyctx); +} + +/* Set scroll region. */ +void +screen_write_scrollregion( + struct screen_write_ctx *ctx, u_int rupper, u_int rlower) +{ + struct screen *s = ctx->s; + + if (rupper > screen_size_y(s) - 1) + rupper = screen_size_y(s) - 1; + if (rlower > screen_size_y(s) - 1) + rlower = screen_size_y(s) - 1; + if (rupper >= rlower) /* cannot be one line */ + return; + + /* Cursor moves to top-left. */ + s->cx = 0; + s->cy = 0; + + s->rupper = rupper; + s->rlower = rlower; +} + +/* Set insert mode. */ +void +screen_write_insertmode(struct screen_write_ctx *ctx, int state) +{ + struct screen *s = ctx->s; + + if (state) + s->mode |= MODE_INSERT; + else + s->mode &= ~MODE_INSERT; +} + +/* Set UTF-8 mouse mode. */ +void +screen_write_utf8mousemode(struct screen_write_ctx *ctx, int state) +{ + struct screen *s = ctx->s; + + if (state) + s->mode |= MODE_MOUSE_UTF8; + else + s->mode &= ~MODE_MOUSE_UTF8; +} + +/* Set mouse mode off. */ +void +screen_write_mousemode_off(struct screen_write_ctx *ctx) +{ + struct screen *s = ctx->s; + + s->mode &= ~ALL_MOUSE_MODES; +} + +/* Set mouse mode on. */ +void +screen_write_mousemode_on(struct screen_write_ctx *ctx, int mode) +{ + struct screen *s = ctx->s; + + s->mode &= ~ALL_MOUSE_MODES; + s->mode |= mode; +} + +/* Line feed. */ +void +screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped) +{ + struct screen *s = ctx->s; + struct grid_line *gl; + struct tty_ctx ttyctx; + + screen_write_initctx(ctx, &ttyctx, 0); + + gl = &s->grid->linedata[s->grid->hsize + s->cy]; + if (wrapped) + gl->flags |= GRID_LINE_WRAPPED; + else + gl->flags &= ~GRID_LINE_WRAPPED; + + if (s->cy == s->rlower) + grid_view_scroll_region_up(s->grid, s->rupper, s->rlower); + else if (s->cy < screen_size_y(s) - 1) + s->cy++; + + ttyctx.num = wrapped; + tty_write(tty_cmd_linefeed, &ttyctx); +} + +/* Carriage return (cursor to start of line). */ +void +screen_write_carriagereturn(struct screen_write_ctx *ctx) +{ + struct screen *s = ctx->s; + + s->cx = 0; +} + +/* Set keypad cursor keys mode. */ +void +screen_write_kcursormode(struct screen_write_ctx *ctx, int state) +{ + struct screen *s = ctx->s; + + if (state) + s->mode |= MODE_KCURSOR; + else + s->mode &= ~MODE_KCURSOR; +} + +/* Set keypad number keys mode. */ +void +screen_write_kkeypadmode(struct screen_write_ctx *ctx, int state) +{ + struct screen *s = ctx->s; + + if (state) + s->mode |= MODE_KKEYPAD; + else + s->mode &= ~MODE_KKEYPAD; +} + +/* Clear to end of screen from cursor. */ +void +screen_write_clearendofscreen(struct screen_write_ctx *ctx) +{ + struct screen *s = ctx->s; + struct tty_ctx ttyctx; + u_int sx, sy; + + screen_write_initctx(ctx, &ttyctx, 0); + + sx = screen_size_x(s); + sy = screen_size_y(s); + + /* Scroll into history if it is enabled and clearing entire screen. */ + if (s->cy == 0 && s->grid->flags & GRID_HISTORY) + grid_view_clear_history(s->grid); + else { + if (s->cx <= sx - 1) + grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1); + grid_view_clear(s->grid, 0, s->cy + 1, sx, sy - (s->cy + 1)); + } + + tty_write(tty_cmd_clearendofscreen, &ttyctx); +} + +/* Clear to start of screen. */ +void +screen_write_clearstartofscreen(struct screen_write_ctx *ctx) +{ + struct screen *s = ctx->s; + struct tty_ctx ttyctx; + u_int sx; + + screen_write_initctx(ctx, &ttyctx, 0); + + sx = screen_size_x(s); + + if (s->cy > 0) + grid_view_clear(s->grid, 0, 0, sx, s->cy); + if (s->cx > sx - 1) + grid_view_clear(s->grid, 0, s->cy, sx, 1); + else + grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1); + + tty_write(tty_cmd_clearstartofscreen, &ttyctx); +} + +/* Clear entire screen. */ +void +screen_write_clearscreen(struct screen_write_ctx *ctx) +{ + struct screen *s = ctx->s; + struct tty_ctx ttyctx; + + screen_write_initctx(ctx, &ttyctx, 0); + + /* Scroll into history if it is enabled. */ + if (s->grid->flags & GRID_HISTORY) + grid_view_clear_history(s->grid); + else { + grid_view_clear( + s->grid, 0, 0, screen_size_x(s), screen_size_y(s)); + } + + tty_write(tty_cmd_clearscreen, &ttyctx); +} + +/* Write cell data. */ +void +screen_write_cell(struct screen_write_ctx *ctx, + const struct grid_cell *gc, const struct utf8_data *utf8data) +{ + struct screen *s = ctx->s; + struct grid *gd = s->grid; + struct tty_ctx ttyctx; + struct grid_utf8 gu; + u_int width, xx; + struct grid_cell tmp_gc, *tmp_gcp; + int insert = 0; + + /* Ignore padding. */ + if (gc->flags & GRID_FLAG_PADDING) + return; + + /* Find character width. */ + if (gc->flags & GRID_FLAG_UTF8) + width = utf8data->width; + else + width = 1; + + /* + * If this is a wide character and there is no room on the screen, for + * the entire character, don't print it. + */ + if (!(s->mode & MODE_WRAP) + && (width > 1 && (width > screen_size_x(s) || + (s->cx != screen_size_x(s) + && s->cx > screen_size_x(s) - width)))) + return; + + /* + * If the width is zero, combine onto the previous character, if + * there is space. + */ + if (width == 0) { + if (screen_write_combine(ctx, utf8data) == 0) { + screen_write_initctx(ctx, &ttyctx, 0); + tty_write(tty_cmd_utf8character, &ttyctx); + } + return; + } + + /* Initialise the redraw context, saving the last cell. */ + screen_write_initctx(ctx, &ttyctx, 1); + + /* If in insert mode, make space for the cells. */ + if (s->mode & MODE_INSERT && s->cx <= screen_size_x(s) - width) { + xx = screen_size_x(s) - s->cx - width; + grid_move_cells(s->grid, s->cx + width, s->cx, s->cy, xx); + insert = 1; + } + + /* Check this will fit on the current line and wrap if not. */ + if ((s->mode & MODE_WRAP) && s->cx > screen_size_x(s) - width) { + screen_write_linefeed(ctx, 1); + s->cx = 0; /* carriage return */ + } + + /* Sanity checks. */ + if (((s->mode & MODE_WRAP) && s->cx > screen_size_x(s) - width) + || s->cy > screen_size_y(s) - 1) + return; + + /* Handle overwriting of UTF-8 characters. */ + screen_write_overwrite(ctx, width); + + /* + * If the new character is UTF-8 wide, fill in padding cells. Have + * already ensured there is enough room. + */ + for (xx = s->cx + 1; xx < s->cx + width; xx++) { + tmp_gcp = grid_view_get_cell(gd, xx, s->cy); + if (tmp_gcp != NULL) + tmp_gcp->flags |= GRID_FLAG_PADDING; + } + + /* Set the cell. */ + grid_view_set_cell(gd, s->cx, s->cy, gc); + if (gc->flags & GRID_FLAG_UTF8) { + /* Construct UTF-8 and write it. */ + grid_utf8_set(&gu, utf8data); + grid_view_set_utf8(gd, s->cx, s->cy, &gu); + } + + /* Move the cursor. */ + s->cx += width; + + /* Draw to the screen if necessary. */ + if (insert) { + ttyctx.num = width; + tty_write(tty_cmd_insertcharacter, &ttyctx); + } + ttyctx.utf8 = &gu; + if (screen_check_selection(s, s->cx - width, s->cy)) { + memcpy(&tmp_gc, &s->sel.cell, sizeof tmp_gc); + tmp_gc.data = gc->data; + tmp_gc.flags = gc->flags & + ~(GRID_FLAG_FG256|GRID_FLAG_BG256); + tmp_gc.flags |= s->sel.cell.flags & + (GRID_FLAG_FG256|GRID_FLAG_BG256); + ttyctx.cell = &tmp_gc; + tty_write(tty_cmd_cell, &ttyctx); + } else { + ttyctx.cell = gc; + tty_write(tty_cmd_cell, &ttyctx); + } +} + +/* Combine a UTF-8 zero-width character onto the previous. */ +int +screen_write_combine( + struct screen_write_ctx *ctx, const struct utf8_data *utf8data) +{ + struct screen *s = ctx->s; + struct grid *gd = s->grid; + struct grid_cell *gc; + struct grid_utf8 *gu, tmp_gu; + u_int i; + + /* Can't combine if at 0. */ + if (s->cx == 0) + return (-1); + + /* Empty utf8data is out. */ + if (utf8data->size == 0) + fatalx("UTF-8 data empty"); + + /* Retrieve the previous cell and convert to UTF-8 if not already. */ + gc = grid_view_get_cell(gd, s->cx - 1, s->cy); + if (!(gc->flags & GRID_FLAG_UTF8)) { + tmp_gu.data[0] = gc->data; + tmp_gu.data[1] = 0xff; + tmp_gu.width = 1; + + grid_view_set_utf8(gd, s->cx - 1, s->cy, &tmp_gu); + gc->flags |= GRID_FLAG_UTF8; + } + + /* Append the current cell. */ + gu = grid_view_get_utf8(gd, s->cx - 1, s->cy); + if (grid_utf8_append(gu, utf8data) != 0) { + /* Failed: scrap this character and replace with underscores. */ + if (gu->width == 1) { + gc->data = '_'; + gc->flags &= ~GRID_FLAG_UTF8; + } else { + for (i = 0; i < gu->width && i != sizeof gu->data; i++) + gu->data[i] = '_'; + if (i != sizeof gu->data) + gu->data[i] = 0xff; + gu->width = i; + } + } + + return (0); +} + +/* + * UTF-8 wide characters are a bit of an annoyance. They take up more than one + * cell on the screen, so following cells must not be drawn by marking them as + * padding. + * + * So far, so good. The problem is, when overwriting a padding cell, or a UTF-8 + * character, it is necessary to also overwrite any other cells which covered + * by the same character. + */ +void +screen_write_overwrite(struct screen_write_ctx *ctx, u_int width) +{ + struct screen *s = ctx->s; + struct grid *gd = s->grid; + const struct grid_cell *gc; + u_int xx; + + gc = grid_view_peek_cell(gd, s->cx, s->cy); + if (gc->flags & GRID_FLAG_PADDING) { + /* + * A padding cell, so clear any following and leading padding + * cells back to the character. Don't overwrite the current + * cell as that happens later anyway. + */ + xx = s->cx + 1; + while (--xx > 0) { + gc = grid_view_peek_cell(gd, xx, s->cy); + if (!(gc->flags & GRID_FLAG_PADDING)) + break; + grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); + } + + /* Overwrite the character at the start of this padding. */ + grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); + } + + /* + * Overwrite any padding cells that belong to a UTF-8 character + * we'll be overwriting with the current character. + */ + xx = s->cx + width - 1; + while (++xx < screen_size_x(s)) { + gc = grid_view_peek_cell(gd, xx, s->cy); + if (!(gc->flags & GRID_FLAG_PADDING)) + break; + grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); + } +} + +void +screen_write_setselection(struct screen_write_ctx *ctx, u_char *str, u_int len) +{ + struct tty_ctx ttyctx; + + screen_write_initctx(ctx, &ttyctx, 0); + ttyctx.ptr = str; + ttyctx.num = len; + + tty_write(tty_cmd_setselection, &ttyctx); +} + +void +screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len) +{ + struct tty_ctx ttyctx; + + screen_write_initctx(ctx, &ttyctx, 0); + ttyctx.ptr = str; + ttyctx.num = len; + + tty_write(tty_cmd_rawstring, &ttyctx); +} diff --git a/external/bsd/tmux/dist/screen.c b/external/bsd/tmux/dist/screen.c new file mode 100644 index 000000000..d0b7ac464 --- /dev/null +++ b/external/bsd/tmux/dist/screen.c @@ -0,0 +1,362 @@ +/* $Id: screen.c,v 1.3 2011/08/17 18:48:36 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include +#include + +#include "tmux.h" + +void screen_resize_x(struct screen *, u_int); +void screen_resize_y(struct screen *, u_int); + +/* Create a new screen. */ +void +screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit) +{ + char hn[MAXHOSTNAMELEN]; + + s->grid = grid_create(sx, sy, hlimit); + + if (gethostname(hn, MAXHOSTNAMELEN) == 0) + s->title = xstrdup(hn); + else + s->title = xstrdup(""); + + s->cstyle = 0; + s->ccolour = xstrdup(""); + s->tabs = NULL; + + screen_reinit(s); +} + +/* Reinitialise screen. */ +void +screen_reinit(struct screen *s) +{ + s->cx = 0; + s->cy = 0; + + s->rupper = 0; + s->rlower = screen_size_y(s) - 1; + + s->mode = MODE_CURSOR | MODE_WRAP; + + screen_reset_tabs(s); + + grid_clear_lines(s->grid, s->grid->hsize, s->grid->sy); + + screen_clear_selection(s); +} + +/* Destroy a screen. */ +void +screen_free(struct screen *s) +{ + if (s->tabs != NULL) + xfree(s->tabs); + xfree(s->title); + xfree(s->ccolour); + grid_destroy(s->grid); +} + +/* Reset tabs to default, eight spaces apart. */ +void +screen_reset_tabs(struct screen *s) +{ + u_int i; + + if (s->tabs != NULL) + xfree(s->tabs); + + if ((s->tabs = bit_alloc(screen_size_x(s))) == NULL) + fatal("bit_alloc failed"); + for (i = 8; i < screen_size_x(s); i += 8) + bit_set(s->tabs, i); +} + +/* Set screen cursor style. */ +void +screen_set_cursor_style(struct screen *s, u_int style) +{ + if (style <= 4) + s->cstyle = style; +} + +/* Set screen cursor colour. */ +void +screen_set_cursor_colour(struct screen *s, const char *colour_string) +{ + xfree(s->ccolour); + s->ccolour = xstrdup(colour_string); +} + +/* Set screen title. */ +void +screen_set_title(struct screen *s, const char *title) +{ + size_t slen = strlen(title); + char tmp[slen * 4 + 1]; + + strvisx(tmp, title, slen, VIS_OCTAL|VIS_TAB|VIS_NL); + + xfree(s->title); + s->title = xstrdup(tmp); +} + +/* Resize screen. */ +void +screen_resize(struct screen *s, u_int sx, u_int sy) +{ + if (sx < 1) + sx = 1; + if (sy < 1) + sy = 1; + + if (sx != screen_size_x(s)) { + screen_resize_x(s, sx); + + /* + * It is unclear what should happen to tabs on resize. xterm + * seems to try and maintain them, rxvt resets them. Resetting + * is simpler and more reliable so let's do that. + */ + screen_reset_tabs(s); + } + + if (sy != screen_size_y(s)) + screen_resize_y(s, sy); +} + +void +screen_resize_x(struct screen *s, u_int sx) +{ + struct grid *gd = s->grid; + + if (sx == 0) + fatalx("zero size"); + + /* + * Treat resizing horizontally simply: just ensure the cursor is + * on-screen and change the size. Don't bother to truncate any lines - + * then the data should be accessible if the size is then incrased. + * + * The only potential wrinkle is if UTF-8 double-width characters are + * left in the last column, but UTF-8 terminals should deal with this + * sanely. + */ + if (s->cx >= sx) + s->cx = sx - 1; + gd->sx = sx; +} + +void +screen_resize_y(struct screen *s, u_int sy) +{ + struct grid *gd = s->grid; + u_int needed, available, oldy, i; + + if (sy == 0) + fatalx("zero size"); + oldy = screen_size_y(s); + + /* + * When resizing: + * + * If the height is decreasing, delete lines from the bottom until + * hitting the cursor, then push lines from the top into the history. + * + * When increasing, pull as many lines as possible from the history to + * the top, then fill the remaining with blanks at the bottom. + */ + + /* Size decreasing. */ + if (sy < oldy) { + needed = oldy - sy; + + /* Delete as many lines as possible from the bottom. */ + available = oldy - 1 - s->cy; + if (available > 0) { + if (available > needed) + available = needed; + grid_view_delete_lines(gd, oldy - available, available); + } + needed -= available; + + /* + * Now just increase the history size, if possible, to take + * over the lines which are left. If history is off, delete + * lines from the top. + * + * XXX Should apply history limit? + */ + available = s->cy; + if (gd->flags & GRID_HISTORY) + gd->hsize += needed; + else if (needed > 0 && available > 0) { + if (available > needed) + available = needed; + grid_view_delete_lines(gd, 0, available); + } + s->cy -= needed; + } + + /* Resize line arrays. */ + gd->linedata = xrealloc( + gd->linedata, gd->hsize + sy, sizeof *gd->linedata); + + /* Size increasing. */ + if (sy > oldy) { + needed = sy - oldy; + + /* + * Try to pull as much as possible out of the history, if is + * is enabled. + */ + available = gd->hsize; + if (gd->flags & GRID_HISTORY && available > 0) { + if (available > needed) + available = needed; + gd->hsize -= available; + s->cy += available; + } else + available = 0; + needed -= available; + + /* Then fill the rest in with blanks. */ + for (i = gd->hsize + sy - needed; i < gd->hsize + sy; i++) + memset(&gd->linedata[i], 0, sizeof gd->linedata[i]); + } + + /* Set the new size, and reset the scroll region. */ + gd->sy = sy; + s->rupper = 0; + s->rlower = screen_size_y(s) - 1; +} + +/* Set selection. */ +void +screen_set_selection(struct screen *s, u_int sx, u_int sy, + u_int ex, u_int ey, u_int rectflag, struct grid_cell *gc) +{ + struct screen_sel *sel = &s->sel; + + memcpy(&sel->cell, gc, sizeof sel->cell); + sel->flag = 1; + sel->rectflag = rectflag; + + sel->sx = sx; sel->sy = sy; + sel->ex = ex; sel->ey = ey; +} + +/* Clear selection. */ +void +screen_clear_selection(struct screen *s) +{ + struct screen_sel *sel = &s->sel; + + sel->flag = 0; +} + +/* Check if cell in selection. */ +int +screen_check_selection(struct screen *s, u_int px, u_int py) +{ + struct screen_sel *sel = &s->sel; + + if (!sel->flag) + return (0); + + if (sel->rectflag) { + if (sel->sy < sel->ey) { + /* start line < end line -- downward selection. */ + if (py < sel->sy || py > sel->ey) + return (0); + } else if (sel->sy > sel->ey) { + /* start line > end line -- upward selection. */ + if (py > sel->sy || py < sel->ey) + return (0); + } else { + /* starting line == ending line. */ + if (py != sel->sy) + return (0); + } + + /* + * Need to include the selection start row, but not the cursor + * row, which means the selection changes depending on which + * one is on the left. + */ + if (sel->ex < sel->sx) { + /* Cursor (ex) is on the left. */ + if (px < sel->ex) + return (0); + + if (px > sel->sx) + return (0); + } else { + /* Selection start (sx) is on the left. */ + if (px < sel->sx) + return (0); + + if (px > sel->ex) + return (0); + } + } else { + /* + * Like emacs, keep the top-left-most character, and drop the + * bottom-right-most, regardless of copy direction. + */ + if (sel->sy < sel->ey) { + /* starting line < ending line -- downward selection. */ + if (py < sel->sy || py > sel->ey) + return (0); + + if ((py == sel->sy && px < sel->sx) + || (py == sel->ey && px > sel->ex)) + return (0); + } else if (sel->sy > sel->ey) { + /* starting line > ending line -- upward selection. */ + if (py > sel->sy || py < sel->ey) + return (0); + + if ((py == sel->sy && px >= sel->sx) + || (py == sel->ey && px < sel->ex)) + return (0); + } else { + /* starting line == ending line. */ + if (py != sel->sy) + return (0); + + if (sel->ex < sel->sx) { + /* cursor (ex) is on the left */ + if (px > sel->sx || px < sel->ex) + return (0); + } else { + /* selection start (sx) is on the left */ + if (px < sel->sx || px > sel->ex) + return (0); + } + } + } + + return (1); +} diff --git a/external/bsd/tmux/dist/server-client.c b/external/bsd/tmux/dist/server-client.c new file mode 100644 index 000000000..e3a62e356 --- /dev/null +++ b/external/bsd/tmux/dist/server-client.c @@ -0,0 +1,1004 @@ +/* $Id: server-client.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include +#include +#include + +#include "tmux.h" + +void server_client_handle_key(int, struct mouse_event *, void *); +void server_client_repeat_timer(int, short, void *); +void server_client_check_exit(struct client *); +void server_client_check_backoff(struct client *); +void server_client_check_redraw(struct client *); +void server_client_set_title(struct client *); +void server_client_reset_state(struct client *); +void server_client_in_callback(struct bufferevent *, short, void *); +void server_client_out_callback(struct bufferevent *, short, void *); +void server_client_err_callback(struct bufferevent *, short, void *); + +int server_client_msg_dispatch(struct client *); +void server_client_msg_command(struct client *, struct msg_command_data *); +void server_client_msg_identify( + struct client *, struct msg_identify_data *, int); +void server_client_msg_shell(struct client *); + +void printflike2 server_client_msg_error(struct cmd_ctx *, const char *, ...); +void printflike2 server_client_msg_print(struct cmd_ctx *, const char *, ...); +void printflike2 server_client_msg_info(struct cmd_ctx *, const char *, ...); + +/* Create a new client. */ +void +server_client_create(int fd) +{ + struct client *c; + u_int i; + + setblocking(fd, 0); + + c = xcalloc(1, sizeof *c); + c->references = 0; + imsg_init(&c->ibuf, fd); + server_update_event(c); + + if (gettimeofday(&c->creation_time, NULL) != 0) + fatal("gettimeofday failed"); + memcpy(&c->activity_time, &c->creation_time, sizeof c->activity_time); + + c->stdin_event = NULL; + c->stdout_event = NULL; + c->stderr_event = NULL; + + c->tty.fd = -1; + c->title = NULL; + + c->session = NULL; + c->last_session = NULL; + c->tty.sx = 80; + c->tty.sy = 24; + + screen_init(&c->status, c->tty.sx, 1, 0); + RB_INIT(&c->status_new); + RB_INIT(&c->status_old); + + c->message_string = NULL; + ARRAY_INIT(&c->message_log); + + c->prompt_string = NULL; + c->prompt_buffer = NULL; + c->prompt_index = 0; + + c->last_mouse.b = MOUSE_UP; + c->last_mouse.x = c->last_mouse.y = -1; + + evtimer_set(&c->repeat_timer, server_client_repeat_timer, c); + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + if (ARRAY_ITEM(&clients, i) == NULL) { + ARRAY_SET(&clients, i, c); + return; + } + } + ARRAY_ADD(&clients, c); + log_debug("new client %d", fd); +} + +/* Lost a client. */ +void +server_client_lost(struct client *c) +{ + struct message_entry *msg; + u_int i; + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + if (ARRAY_ITEM(&clients, i) == c) + ARRAY_SET(&clients, i, NULL); + } + log_debug("lost client %d", c->ibuf.fd); + + /* + * If CLIENT_TERMINAL hasn't been set, then tty_init hasn't been called + * and tty_free might close an unrelated fd. + */ + if (c->flags & CLIENT_TERMINAL) + tty_free(&c->tty); + + if (c->stdin_fd != -1) { + setblocking(c->stdin_fd, 1); + close(c->stdin_fd); + } + if (c->stdin_event != NULL) + bufferevent_free(c->stdin_event); + if (c->stdout_fd != -1) { + setblocking(c->stdout_fd, 1); + close(c->stdout_fd); + } + if (c->stdout_event != NULL) + bufferevent_free(c->stdout_event); + if (c->stderr_fd != -1) { + setblocking(c->stderr_fd, 1); + close(c->stderr_fd); + } + if (c->stderr_event != NULL) + bufferevent_free(c->stderr_event); + + status_free_jobs(&c->status_new); + status_free_jobs(&c->status_old); + screen_free(&c->status); + + if (c->title != NULL) + xfree(c->title); + + evtimer_del(&c->repeat_timer); + + evtimer_del(&c->identify_timer); + + if (c->message_string != NULL) + xfree(c->message_string); + evtimer_del(&c->message_timer); + for (i = 0; i < ARRAY_LENGTH(&c->message_log); i++) { + msg = &ARRAY_ITEM(&c->message_log, i); + xfree(msg->msg); + } + ARRAY_FREE(&c->message_log); + + if (c->prompt_string != NULL) + xfree(c->prompt_string); + if (c->prompt_buffer != NULL) + xfree(c->prompt_buffer); + + if (c->cwd != NULL) + xfree(c->cwd); + + close(c->ibuf.fd); + imsg_clear(&c->ibuf); + event_del(&c->event); + + for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) { + if (ARRAY_ITEM(&dead_clients, i) == NULL) { + ARRAY_SET(&dead_clients, i, c); + break; + } + } + if (i == ARRAY_LENGTH(&dead_clients)) + ARRAY_ADD(&dead_clients, c); + c->flags |= CLIENT_DEAD; + + recalculate_sizes(); + server_check_unattached(); + server_update_socket(); +} + +/* Process a single client event. */ +void +server_client_callback(int fd, short events, void *data) +{ + struct client *c = data; + + if (c->flags & CLIENT_DEAD) + return; + + if (fd == c->ibuf.fd) { + if (events & EV_WRITE && msgbuf_write(&c->ibuf.w) < 0) + goto client_lost; + + if (c->flags & CLIENT_BAD) { + if (c->ibuf.w.queued == 0) + goto client_lost; + return; + } + + if (events & EV_READ && server_client_msg_dispatch(c) != 0) + goto client_lost; + } + + server_update_event(c); + return; + +client_lost: + server_client_lost(c); +} + +/* Handle client status timer. */ +void +server_client_status_timer(void) +{ + struct client *c; + struct session *s; + struct timeval tv; + u_int i; + int interval; + time_t difference; + + if (gettimeofday(&tv, NULL) != 0) + fatal("gettimeofday failed"); + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session == NULL) + continue; + if (c->message_string != NULL || c->prompt_string != NULL) { + /* + * Don't need timed redraw for messages/prompts so bail + * now. The status timer isn't reset when they are + * redrawn anyway. + */ + continue; + } + s = c->session; + + if (!options_get_number(&s->options, "status")) + continue; + interval = options_get_number(&s->options, "status-interval"); + + difference = tv.tv_sec - c->status_timer.tv_sec; + if (difference >= interval) { + status_update_jobs(c); + c->flags |= CLIENT_STATUS; + } + } +} + +/* Handle data key input from client. */ +void +server_client_handle_key(int key, struct mouse_event *mouse, void *data) +{ + struct client *c = data; + struct session *s; + struct window *w; + struct window_pane *wp; + struct options *oo; + struct timeval tv; + struct key_binding *bd; + struct keylist *keylist; + int xtimeout, isprefix; + u_int i; + + /* Check the client is good to accept input. */ + if ((c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0) + return; + if (c->session == NULL) + return; + s = c->session; + + /* Update the activity timer. */ + if (gettimeofday(&c->activity_time, NULL) != 0) + fatal("gettimeofday failed"); + memcpy(&s->activity_time, &c->activity_time, sizeof s->activity_time); + + w = c->session->curw->window; + wp = w->active; + oo = &c->session->options; + + /* Special case: number keys jump to pane in identify mode. */ + if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') { + if (c->flags & CLIENT_READONLY) + return; + wp = window_pane_at_index(w, key - '0'); + if (wp != NULL && window_pane_visible(wp)) + window_set_active_pane(w, wp); + server_clear_identify(c); + return; + } + + /* Handle status line. */ + if (!(c->flags & CLIENT_READONLY)) { + status_message_clear(c); + server_clear_identify(c); + } + if (c->prompt_string != NULL) { + if (!(c->flags & CLIENT_READONLY)) + status_prompt_key(c, key); + return; + } + + /* Check for mouse keys. */ + if (key == KEYC_MOUSE) { + if (c->flags & CLIENT_READONLY) + return; + if (options_get_number(oo, "mouse-select-pane") && + ((!(mouse->b & MOUSE_DRAG) && mouse->b != MOUSE_UP) || + wp->mode != &window_copy_mode)) { + /* + * Allow pane switching in copy mode only by mouse down + * (click). + */ + window_set_active_at(w, mouse->x, mouse->y); + server_redraw_window_borders(w); + wp = w->active; + } + if (mouse->y + 1 == c->tty.sy && + options_get_number(oo, "mouse-select-window") && + options_get_number(oo, "status")) { + if (mouse->b == MOUSE_UP && + c->last_mouse.b != MOUSE_UP) { + status_set_window_at(c, mouse->x); + return; + } + if (mouse->b & MOUSE_45) { + if ((mouse->b & MOUSE_BUTTON) == MOUSE_1) { + session_previous(c->session, 0); + server_redraw_session(s); + } + if ((mouse->b & MOUSE_BUTTON) == MOUSE_2) { + session_next(c->session, 0); + server_redraw_session(s); + } + return; + } + } + if (options_get_number(oo, "mouse-resize-pane")) + layout_resize_pane_mouse(c, mouse); + memcpy(&c->last_mouse, mouse, sizeof c->last_mouse); + window_pane_mouse(wp, c->session, mouse); + return; + } + + /* Is this a prefix key? */ + keylist = options_get_data(&c->session->options, "prefix"); + isprefix = 0; + for (i = 0; i < ARRAY_LENGTH(keylist); i++) { + if (key == ARRAY_ITEM(keylist, i)) { + isprefix = 1; + break; + } + } + + /* No previous prefix key. */ + if (!(c->flags & CLIENT_PREFIX)) { + if (isprefix) + c->flags |= CLIENT_PREFIX; + else { + /* Try as a non-prefix key binding. */ + if ((bd = key_bindings_lookup(key)) == NULL) { + if (!(c->flags & CLIENT_READONLY)) + window_pane_key(wp, c->session, key); + } else + key_bindings_dispatch(bd, c); + } + return; + } + + /* Prefix key already pressed. Reset prefix and lookup key. */ + c->flags &= ~CLIENT_PREFIX; + if ((bd = key_bindings_lookup(key | KEYC_PREFIX)) == NULL) { + /* If repeating, treat this as a key, else ignore. */ + if (c->flags & CLIENT_REPEAT) { + c->flags &= ~CLIENT_REPEAT; + if (isprefix) + c->flags |= CLIENT_PREFIX; + else if (!(c->flags & CLIENT_READONLY)) + window_pane_key(wp, c->session, key); + } + return; + } + + /* If already repeating, but this key can't repeat, skip it. */ + if (c->flags & CLIENT_REPEAT && !bd->can_repeat) { + c->flags &= ~CLIENT_REPEAT; + if (isprefix) + c->flags |= CLIENT_PREFIX; + else if (!(c->flags & CLIENT_READONLY)) + window_pane_key(wp, c->session, key); + return; + } + + /* If this key can repeat, reset the repeat flags and timer. */ + xtimeout = options_get_number(&c->session->options, "repeat-time"); + if (xtimeout != 0 && bd->can_repeat) { + c->flags |= CLIENT_PREFIX|CLIENT_REPEAT; + + tv.tv_sec = xtimeout / 1000; + tv.tv_usec = (xtimeout % 1000) * 1000L; + evtimer_del(&c->repeat_timer); + evtimer_add(&c->repeat_timer, &tv); + } + + /* Dispatch the command. */ + key_bindings_dispatch(bd, c); +} + +/* Client functions that need to happen every loop. */ +void +server_client_loop(void) +{ + struct client *c; + struct window *w; + struct window_pane *wp; + u_int i; + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL) + continue; + + server_client_check_exit(c); + if (c->session != NULL) { + server_client_check_redraw(c); + server_client_reset_state(c); + } + } + + /* + * Any windows will have been redrawn as part of clients, so clear + * their flags now. + */ + for (i = 0; i < ARRAY_LENGTH(&windows); i++) { + w = ARRAY_ITEM(&windows, i); + if (w == NULL) + continue; + + w->flags &= ~WINDOW_REDRAW; + TAILQ_FOREACH(wp, &w->panes, entry) + wp->flags &= ~PANE_REDRAW; + } +} + +/* + * Update cursor position and mode settings. The scroll region and attributes + * are cleared when idle (waiting for an event) as this is the most likely time + * a user may interrupt tmux, for example with ~^Z in ssh(1). This is a + * compromise between excessive resets and likelihood of an interrupt. + * + * tty_region/tty_reset/tty_update_mode already take care of not resetting + * things that are already in their default state. + */ +void +server_client_reset_state(struct client *c) +{ + struct window *w = c->session->curw->window; + struct window_pane *wp = w->active; + struct screen *s = wp->screen; + struct options *oo = &c->session->options; + struct options *wo = &w->options; + int status, mode; + + if (c->flags & CLIENT_SUSPENDED) + return; + + tty_region(&c->tty, 0, c->tty.sy - 1); + + status = options_get_number(oo, "status"); + if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status) + tty_cursor(&c->tty, 0, 0); + else + tty_cursor(&c->tty, wp->xoff + s->cx, wp->yoff + s->cy); + + /* + * Resizing panes with the mouse requires at least button mode to give + * a smooth appearance. + */ + mode = s->mode; + if ((c->last_mouse.b & MOUSE_RESIZE_PANE) && + !(mode & (MODE_MOUSE_BUTTON|MODE_MOUSE_ANY))) + mode |= MODE_MOUSE_BUTTON; + + /* + * Any mode will do for mouse-select-pane, but set standard mode if + * none. + */ + if ((mode & ALL_MOUSE_MODES) == 0) { + if (TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry) != NULL && + options_get_number(oo, "mouse-select-pane")) + mode |= MODE_MOUSE_STANDARD; + else if (options_get_number(oo, "mouse-resize-pane")) + mode |= MODE_MOUSE_STANDARD; + else if (options_get_number(oo, "mouse-select-window")) + mode |= MODE_MOUSE_STANDARD; + else if (options_get_number(wo, "mode-mouse")) + mode |= MODE_MOUSE_STANDARD; + } + + /* + * Set UTF-8 mouse input if required. If the terminal is UTF-8, the + * user has set mouse-utf8 and any mouse mode is in effect, turn on + * UTF-8 mouse input. If the receiving terminal hasn't requested it + * (that is, it isn't in s->mode), then it'll be converted in + * input_mouse. + */ + if ((c->tty.flags & TTY_UTF8) && + (mode & ALL_MOUSE_MODES) && options_get_number(oo, "mouse-utf8")) + mode |= MODE_MOUSE_UTF8; + else + mode &= ~MODE_MOUSE_UTF8; + + /* Set the terminal mode and reset attributes. */ + tty_update_mode(&c->tty, mode, s); + tty_reset(&c->tty); +} + +/* Repeat time callback. */ +/* ARGSUSED */ +void +server_client_repeat_timer(unused int fd, unused short events, void *data) +{ + struct client *c = data; + + if (c->flags & CLIENT_REPEAT) + c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT); +} + +/* Check if client should be exited. */ +void +server_client_check_exit(struct client *c) +{ + struct msg_exit_data exitdata; + + if (!(c->flags & CLIENT_EXIT)) + return; + + if (c->stdout_fd != -1 && c->stdout_event != NULL && + EVBUFFER_LENGTH(c->stdout_event->output) != 0) + return; + if (c->stderr_fd != -1 && c->stderr_event != NULL && + EVBUFFER_LENGTH(c->stderr_event->output) != 0) + return; + + exitdata.retcode = c->retcode; + server_write_client(c, MSG_EXIT, &exitdata, sizeof exitdata); + + c->flags &= ~CLIENT_EXIT; +} + +/* + * Check if the client should backoff. During backoff, data from external + * programs is not written to the terminal. When the existing data drains, the + * client is redrawn. + * + * There are two backoff phases - both the tty and client have backoff flags - + * the first to allow existing data to drain and the latter to ensure backoff + * is disabled until the redraw has finished and prevent the redraw triggering + * another backoff. + */ +void +server_client_check_backoff(struct client *c) +{ + struct tty *tty = &c->tty; + size_t used; + + used = EVBUFFER_LENGTH(tty->event->output); + + /* + * If in the second backoff phase (redrawing), don't check backoff + * until the redraw has completed (or enough of it to drop below the + * backoff threshold). + */ + if (c->flags & CLIENT_BACKOFF) { + if (used > BACKOFF_THRESHOLD) + return; + c->flags &= ~CLIENT_BACKOFF; + return; + } + + /* Once drained, allow data through again and schedule redraw. */ + if (tty->flags & TTY_BACKOFF) { + if (used != 0) + return; + tty->flags &= ~TTY_BACKOFF; + c->flags |= (CLIENT_BACKOFF|CLIENT_REDRAWWINDOW|CLIENT_STATUS); + return; + } + + /* If too much data, start backoff. */ + if (used > BACKOFF_THRESHOLD) + tty->flags |= TTY_BACKOFF; +} + +/* Check for client redraws. */ +void +server_client_check_redraw(struct client *c) +{ + struct session *s = c->session; + struct window_pane *wp; + int flags, redraw; + + flags = c->tty.flags & TTY_FREEZE; + c->tty.flags &= ~TTY_FREEZE; + + if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) { + if (options_get_number(&s->options, "set-titles")) + server_client_set_title(c); + + if (c->message_string != NULL) + redraw = status_message_redraw(c); + else if (c->prompt_string != NULL) + redraw = status_prompt_redraw(c); + else + redraw = status_redraw(c); + if (!redraw) + c->flags &= ~CLIENT_STATUS; + } + + if (c->flags & CLIENT_REDRAW) { + screen_redraw_screen(c, 0, 0); + c->flags &= ~(CLIENT_STATUS|CLIENT_BORDERS); + } else if (c->flags & CLIENT_REDRAWWINDOW) { + TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) + screen_redraw_pane(c, wp); + c->flags &= ~CLIENT_REDRAWWINDOW; + } else { + TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) { + if (wp->flags & PANE_REDRAW) + screen_redraw_pane(c, wp); + } + } + + if (c->flags & CLIENT_BORDERS) + screen_redraw_screen(c, 0, 1); + + if (c->flags & CLIENT_STATUS) + screen_redraw_screen(c, 1, 0); + + c->tty.flags |= flags; + + c->flags &= ~(CLIENT_REDRAW|CLIENT_STATUS|CLIENT_BORDERS); +} + +/* Set client title. */ +void +server_client_set_title(struct client *c) +{ + struct session *s = c->session; + const char *template; + char *title; + + template = options_get_string(&s->options, "set-titles-string"); + + title = status_replace(c, NULL, NULL, NULL, template, time(NULL), 1); + if (c->title == NULL || strcmp(title, c->title) != 0) { + if (c->title != NULL) + xfree(c->title); + c->title = xstrdup(title); + tty_set_title(&c->tty, c->title); + } + xfree(title); +} + +/* + * Error callback for client stdin. Caller must increase reference count when + * enabling event! + */ +void +server_client_in_callback( + unused struct bufferevent *bufev, unused short what, void *data) +{ + struct client *c = data; + + c->references--; + if (c->flags & CLIENT_DEAD) + return; + + bufferevent_disable(c->stdin_event, EV_READ|EV_WRITE); + setblocking(c->stdin_fd, 1); + close(c->stdin_fd); + c->stdin_fd = -1; + + if (c->stdin_callback != NULL) + c->stdin_callback(c, c->stdin_data); +} + +/* Error callback for client stdout. */ +void +server_client_out_callback( + unused struct bufferevent *bufev, unused short what, unused void *data) +{ + struct client *c = data; + + bufferevent_disable(c->stdout_event, EV_READ|EV_WRITE); + setblocking(c->stdout_fd, 1); + close(c->stdout_fd); + c->stdout_fd = -1; +} + +/* Error callback for client stderr. */ +void +server_client_err_callback( + unused struct bufferevent *bufev, unused short what, unused void *data) +{ + struct client *c = data; + + bufferevent_disable(c->stderr_event, EV_READ|EV_WRITE); + setblocking(c->stderr_fd, 1); + close(c->stderr_fd); + c->stderr_fd = -1; +} + +/* Dispatch message from client. */ +int +server_client_msg_dispatch(struct client *c) +{ + struct imsg imsg; + struct msg_command_data commanddata; + struct msg_identify_data identifydata; + struct msg_environ_data environdata; + ssize_t n, datalen; + + if ((n = imsg_read(&c->ibuf)) == -1 || n == 0) + return (-1); + + for (;;) { + if ((n = imsg_get(&c->ibuf, &imsg)) == -1) + return (-1); + if (n == 0) + return (0); + datalen = imsg.hdr.len - IMSG_HEADER_SIZE; + + if (imsg.hdr.peerid != PROTOCOL_VERSION) { + server_write_client(c, MSG_VERSION, NULL, 0); + c->flags |= CLIENT_BAD; + imsg_free(&imsg); + continue; + } + + log_debug("got %d from client %d", imsg.hdr.type, c->ibuf.fd); + switch (imsg.hdr.type) { + case MSG_COMMAND: + if (datalen != sizeof commanddata) + fatalx("bad MSG_COMMAND size"); + memcpy(&commanddata, imsg.data, sizeof commanddata); + + server_client_msg_command(c, &commanddata); + break; + case MSG_IDENTIFY: + if (datalen != sizeof identifydata) + fatalx("bad MSG_IDENTIFY size"); + if (imsg.fd == -1) + fatalx("MSG_IDENTIFY missing fd"); + memcpy(&identifydata, imsg.data, sizeof identifydata); + + c->stdin_fd = imsg.fd; + c->stdin_event = bufferevent_new(c->stdin_fd, + NULL, NULL, server_client_in_callback, c); + if (c->stdin_event == NULL) + fatalx("failed to create stdin event"); + setblocking(c->stdin_fd, 0); + + server_client_msg_identify(c, &identifydata, imsg.fd); + break; + case MSG_STDOUT: + if (datalen != 0) + fatalx("bad MSG_STDOUT size"); + if (imsg.fd == -1) + fatalx("MSG_STDOUT missing fd"); + + c->stdout_fd = imsg.fd; + c->stdout_event = bufferevent_new(c->stdout_fd, + NULL, NULL, server_client_out_callback, c); + if (c->stdout_event == NULL) + fatalx("failed to create stdout event"); + setblocking(c->stdout_fd, 0); + + break; + case MSG_STDERR: + if (datalen != 0) + fatalx("bad MSG_STDERR size"); + if (imsg.fd == -1) + fatalx("MSG_STDERR missing fd"); + + c->stderr_fd = imsg.fd; + c->stderr_event = bufferevent_new(c->stderr_fd, + NULL, NULL, server_client_err_callback, c); + if (c->stderr_event == NULL) + fatalx("failed to create stderr event"); + setblocking(c->stderr_fd, 0); + + break; + case MSG_RESIZE: + if (datalen != 0) + fatalx("bad MSG_RESIZE size"); + + if (tty_resize(&c->tty)) { + recalculate_sizes(); + server_redraw_client(c); + } + break; + case MSG_EXITING: + if (datalen != 0) + fatalx("bad MSG_EXITING size"); + + c->session = NULL; + tty_close(&c->tty); + server_write_client(c, MSG_EXITED, NULL, 0); + break; + case MSG_WAKEUP: + case MSG_UNLOCK: + if (datalen != 0) + fatalx("bad MSG_WAKEUP size"); + + if (!(c->flags & CLIENT_SUSPENDED)) + break; + c->flags &= ~CLIENT_SUSPENDED; + + if (gettimeofday(&c->activity_time, NULL) != 0) + fatal("gettimeofday"); + if (c->session != NULL) + session_update_activity(c->session); + + tty_start_tty(&c->tty); + server_redraw_client(c); + recalculate_sizes(); + break; + case MSG_ENVIRON: + if (datalen != sizeof environdata) + fatalx("bad MSG_ENVIRON size"); + memcpy(&environdata, imsg.data, sizeof environdata); + + environdata.var[(sizeof environdata.var) - 1] = '\0'; + if (strchr(environdata.var, '=') != NULL) + environ_put(&c->environ, environdata.var); + break; + case MSG_SHELL: + if (datalen != 0) + fatalx("bad MSG_SHELL size"); + + server_client_msg_shell(c); + break; + default: + fatalx("unexpected message"); + } + + imsg_free(&imsg); + } +} + +/* Callback to send error message to client. */ +void printflike2 +server_client_msg_error(struct cmd_ctx *ctx, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + evbuffer_add_vprintf(ctx->cmdclient->stderr_event->output, fmt, ap); + va_end(ap); + + bufferevent_write(ctx->cmdclient->stderr_event, "\n", 1); + ctx->cmdclient->retcode = 1; +} + +/* Callback to send print message to client. */ +void printflike2 +server_client_msg_print(struct cmd_ctx *ctx, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + evbuffer_add_vprintf(ctx->cmdclient->stdout_event->output, fmt, ap); + va_end(ap); + + bufferevent_write(ctx->cmdclient->stdout_event, "\n", 1); +} + +/* Callback to send print message to client, if not quiet. */ +void printflike2 +server_client_msg_info(struct cmd_ctx *ctx, const char *fmt, ...) +{ + va_list ap; + + if (options_get_number(&global_options, "quiet")) + return; + + va_start(ap, fmt); + evbuffer_add_vprintf(ctx->cmdclient->stdout_event->output, fmt, ap); + va_end(ap); + + bufferevent_write(ctx->cmdclient->stdout_event, "\n", 1); +} + +/* Handle command message. */ +void +server_client_msg_command(struct client *c, struct msg_command_data *data) +{ + struct cmd_ctx ctx; + struct cmd_list *cmdlist = NULL; + int argc; + char **argv, *cause; + + ctx.error = server_client_msg_error; + ctx.print = server_client_msg_print; + ctx.info = server_client_msg_info; + + ctx.msgdata = data; + ctx.curclient = NULL; + + ctx.cmdclient = c; + + argc = data->argc; + data->argv[(sizeof data->argv) - 1] = '\0'; + if (cmd_unpack_argv(data->argv, sizeof data->argv, argc, &argv) != 0) { + server_client_msg_error(&ctx, "command too long"); + goto error; + } + + if (argc == 0) { + argc = 1; + argv = xcalloc(1, sizeof *argv); + *argv = xstrdup("new-session"); + } + + if ((cmdlist = cmd_list_parse(argc, argv, &cause)) == NULL) { + server_client_msg_error(&ctx, "%s", cause); + cmd_free_argv(argc, argv); + goto error; + } + cmd_free_argv(argc, argv); + + if (cmd_list_exec(cmdlist, &ctx) != 1) + c->flags |= CLIENT_EXIT; + cmd_list_free(cmdlist); + return; + +error: + if (cmdlist != NULL) + cmd_list_free(cmdlist); + c->flags |= CLIENT_EXIT; +} + +/* Handle identify message. */ +void +server_client_msg_identify( + struct client *c, struct msg_identify_data *data, int fd) +{ + int tty_fd; + + c->cwd = NULL; + data->cwd[(sizeof data->cwd) - 1] = '\0'; + if (*data->cwd != '\0') + c->cwd = xstrdup(data->cwd); + + if (!isatty(fd)) + return; + if ((tty_fd = dup(fd)) == -1) + fatal("dup failed"); + data->term[(sizeof data->term) - 1] = '\0'; + tty_init(&c->tty, tty_fd, data->term); + if (data->flags & IDENTIFY_UTF8) + c->tty.flags |= TTY_UTF8; + if (data->flags & IDENTIFY_256COLOURS) + c->tty.term_flags |= TERM_256COLOURS; + else if (data->flags & IDENTIFY_88COLOURS) + c->tty.term_flags |= TERM_88COLOURS; + c->tty.key_callback = server_client_handle_key; + c->tty.key_data = c; + + tty_resize(&c->tty); + + c->flags |= CLIENT_TERMINAL; +} + +/* Handle shell message. */ +void +server_client_msg_shell(struct client *c) +{ + struct msg_shell_data data; + const char *shell; + + shell = options_get_string(&global_s_options, "default-shell"); + + if (*shell == '\0' || areshell(shell)) + shell = _PATH_BSHELL; + if (strlcpy(data.shell, shell, sizeof data.shell) >= sizeof data.shell) + strlcpy(data.shell, _PATH_BSHELL, sizeof data.shell); + + server_write_client(c, MSG_SHELL, &data, sizeof data); + c->flags |= CLIENT_BAD; /* it will die after exec */ +} diff --git a/external/bsd/tmux/dist/server-fn.c b/external/bsd/tmux/dist/server-fn.c new file mode 100644 index 000000000..82b196db4 --- /dev/null +++ b/external/bsd/tmux/dist/server-fn.c @@ -0,0 +1,480 @@ +/* $Id: server-fn.c,v 1.3 2011/08/17 18:48:36 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include + +#include "tmux.h" + +struct session *server_next_session(struct session *); +void server_callback_identify(int, short, void *); + +void +server_fill_environ(struct session *s, struct environ *env) +{ + char var[MAXPATHLEN], *term; + u_int idx; + long pid; + + if (s != NULL) { + term = options_get_string(&s->options, "default-terminal"); + environ_set(env, "TERM", term); + + idx = s->idx; + } else + idx = -1; + pid = getpid(); + xsnprintf(var, sizeof var, "%s,%ld,%d", socket_path, pid, idx); + environ_set(env, "TMUX", var); +} + +void +server_write_client( + struct client *c, enum msgtype type, const void *buf, size_t len) +{ + struct imsgbuf *ibuf = &c->ibuf; + + if (c->flags & CLIENT_BAD) + return; + log_debug("writing %d to client %d", type, c->ibuf.fd); + imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, -1, __UNCONST(buf), len); + server_update_event(c); +} + +void +server_write_session( + struct session *s, enum msgtype type, const void *buf, size_t len) +{ + struct client *c; + u_int i; + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session == NULL) + continue; + if (c->session == s) + server_write_client(c, type, buf, len); + } +} + +void +server_redraw_client(struct client *c) +{ + c->flags |= CLIENT_REDRAW; +} + +void +server_status_client(struct client *c) +{ + c->flags |= CLIENT_STATUS; +} + +void +server_redraw_session(struct session *s) +{ + struct client *c; + u_int i; + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session == NULL) + continue; + if (c->session == s) + server_redraw_client(c); + } +} + +void +server_redraw_session_group(struct session *s) +{ + struct session_group *sg; + + if ((sg = session_group_find(s)) == NULL) + server_redraw_session(s); + else { + TAILQ_FOREACH(s, &sg->sessions, gentry) + server_redraw_session(s); + } +} + +void +server_status_session(struct session *s) +{ + struct client *c; + u_int i; + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session == NULL) + continue; + if (c->session == s) + server_status_client(c); + } +} + +void +server_status_session_group(struct session *s) +{ + struct session_group *sg; + + if ((sg = session_group_find(s)) == NULL) + server_status_session(s); + else { + TAILQ_FOREACH(s, &sg->sessions, gentry) + server_status_session(s); + } +} + +void +server_redraw_window(struct window *w) +{ + struct client *c; + u_int i; + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session == NULL) + continue; + if (c->session->curw->window == w) + server_redraw_client(c); + } + w->flags |= WINDOW_REDRAW; +} + +void +server_redraw_window_borders(struct window *w) +{ + struct client *c; + u_int i; + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session == NULL) + continue; + if (c->session->curw->window == w) + c->flags |= CLIENT_BORDERS; + } +} + +void +server_status_window(struct window *w) +{ + struct session *s; + + /* + * This is slightly different. We want to redraw the status line of any + * clients containing this window rather than any where it is the + * current window. + */ + + RB_FOREACH(s, sessions, &sessions) { + if (session_has(s, w) != NULL) + server_status_session(s); + } +} + +void +server_lock(void) +{ + struct client *c; + u_int i; + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session == NULL) + continue; + server_lock_client(c); + } +} + +void +server_lock_session(struct session *s) +{ + struct client *c; + u_int i; + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session == NULL || c->session != s) + continue; + server_lock_client(c); + } +} + +void +server_lock_client(struct client *c) +{ + const char *cmd; + size_t cmdlen; + struct msg_lock_data lockdata; + + if (c->flags & CLIENT_SUSPENDED) + return; + + cmd = options_get_string(&c->session->options, "lock-command"); + cmdlen = strlcpy(lockdata.cmd, cmd, sizeof lockdata.cmd); + if (cmdlen >= sizeof lockdata.cmd) + return; + + tty_stop_tty(&c->tty); + tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_SMCUP)); + tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_CLEAR)); + + c->flags |= CLIENT_SUSPENDED; + server_write_client(c, MSG_LOCK, &lockdata, sizeof lockdata); +} + +void +server_kill_window(struct window *w) +{ + struct session *s, *next_s; + struct winlink *wl; + + next_s = RB_MIN(sessions, &sessions); + while (next_s != NULL) { + s = next_s; + next_s = RB_NEXT(sessions, &sessions, s); + + if (session_has(s, w) == NULL) + continue; + while ((wl = winlink_find_by_window(&s->windows, w)) != NULL) { + if (session_detach(s, wl)) { + server_destroy_session_group(s); + break; + } else + server_redraw_session_group(s); + } + } +} + +int +server_link_window(struct session *src, struct winlink *srcwl, + struct session *dst, int dstidx, int killflag, int selectflag, char **cause) +{ + struct winlink *dstwl; + struct session_group *srcsg, *dstsg; + + srcsg = session_group_find(src); + dstsg = session_group_find(dst); + if (src != dst && srcsg != NULL && dstsg != NULL && srcsg == dstsg) { + xasprintf(cause, "sessions are grouped"); + return (-1); + } + + dstwl = NULL; + if (dstidx != -1) + dstwl = winlink_find_by_index(&dst->windows, dstidx); + if (dstwl != NULL) { + if (dstwl->window == srcwl->window) { + xasprintf(cause, "same index: %d", dstidx); + return (-1); + } + if (killflag) { + /* + * Can't use session_detach as it will destroy session + * if this makes it empty. + */ + dstwl->flags &= ~WINLINK_ALERTFLAGS; + winlink_stack_remove(&dst->lastw, dstwl); + winlink_remove(&dst->windows, dstwl); + + /* Force select/redraw if current. */ + if (dstwl == dst->curw) { + selectflag = 1; + dst->curw = NULL; + } + } + } + + if (dstidx == -1) + dstidx = -1 - options_get_number(&dst->options, "base-index"); + dstwl = session_attach(dst, srcwl->window, dstidx, cause); + if (dstwl == NULL) + return (-1); + + if (selectflag) + session_select(dst, dstwl->idx); + server_redraw_session_group(dst); + + return (0); +} + +void +server_unlink_window(struct session *s, struct winlink *wl) +{ + if (session_detach(s, wl)) + server_destroy_session_group(s); + else + server_redraw_session_group(s); +} + +void +server_destroy_pane(struct window_pane *wp) +{ + struct window *w = wp->window; + + if (wp->fd != -1) { + close(wp->fd); + bufferevent_free(wp->event); + wp->fd = -1; + } + + if (options_get_number(&w->options, "remain-on-exit")) + return; + + layout_close_pane(wp); + window_remove_pane(w, wp); + + if (TAILQ_EMPTY(&w->panes)) + server_kill_window(w); + else + server_redraw_window(w); +} + +void +server_destroy_session_group(struct session *s) +{ + struct session_group *sg; + + if ((sg = session_group_find(s)) == NULL) + server_destroy_session(s); + else { + TAILQ_FOREACH(s, &sg->sessions, gentry) + server_destroy_session(s); + TAILQ_REMOVE(&session_groups, sg, entry); + xfree(sg); + } +} + +struct session * +server_next_session(struct session *s) +{ + struct session *s_loop, *s_out; + + s_out = NULL; + RB_FOREACH(s_loop, sessions, &sessions) { + if (s_loop == s) + continue; + if (s_out == NULL || + timercmp(&s_loop->activity_time, &s_out->activity_time, <)) + s_out = s_loop; + } + return (s_out); +} + +void +server_destroy_session(struct session *s) +{ + struct client *c; + struct session *s_new; + u_int i; + + if (!options_get_number(&s->options, "detach-on-destroy")) + s_new = server_next_session(s); + else + s_new = NULL; + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session != s) + continue; + if (s_new == NULL) { + c->session = NULL; + c->flags |= CLIENT_EXIT; + } else { + c->last_session = NULL; + c->session = s_new; + session_update_activity(s_new); + server_redraw_client(c); + } + } + recalculate_sizes(); +} + +void +server_check_unattached (void) +{ + struct session *s; + + /* + * If any sessions are no longer attached and have destroy-unattached + * set, collect them. + */ + RB_FOREACH(s, sessions, &sessions) { + if (!(s->flags & SESSION_UNATTACHED)) + continue; + if (options_get_number (&s->options, "destroy-unattached")) + session_destroy(s); + } +} + +void +server_set_identify(struct client *c) +{ + struct timeval tv; + int delay; + + delay = options_get_number(&c->session->options, "display-panes-time"); + tv.tv_sec = delay / 1000; + tv.tv_usec = (delay % 1000) * 1000L; + + evtimer_del(&c->identify_timer); + evtimer_set(&c->identify_timer, server_callback_identify, c); + evtimer_add(&c->identify_timer, &tv); + + c->flags |= CLIENT_IDENTIFY; + c->tty.flags |= (TTY_FREEZE|TTY_NOCURSOR); + server_redraw_client(c); +} + +void +server_clear_identify(struct client *c) +{ + if (c->flags & CLIENT_IDENTIFY) { + c->flags &= ~CLIENT_IDENTIFY; + c->tty.flags &= ~(TTY_FREEZE|TTY_NOCURSOR); + server_redraw_client(c); + } +} + +/* ARGSUSED */ +void +server_callback_identify(unused int fd, unused short events, void *data) +{ + struct client *c = data; + + server_clear_identify(c); +} + +void +server_update_event(struct client *c) +{ + short events; + + events = 0; + if (!(c->flags & CLIENT_BAD)) + events |= EV_READ; + if (c->ibuf.w.queued > 0) + events |= EV_WRITE; + event_del(&c->event); + event_set(&c->event, c->ibuf.fd, events, server_client_callback, c); + event_add(&c->event, NULL); +} diff --git a/external/bsd/tmux/dist/server-window.c b/external/bsd/tmux/dist/server-window.c new file mode 100644 index 000000000..1084ed2f5 --- /dev/null +++ b/external/bsd/tmux/dist/server-window.c @@ -0,0 +1,259 @@ +/* $Id: server-window.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +int server_window_check_bell(struct session *, struct winlink *); +int server_window_check_activity(struct session *, struct winlink *); +int server_window_check_silence(struct session *, struct winlink *); +int server_window_check_content( + struct session *, struct winlink *, struct window_pane *); +void ring_bell(struct session *); + +/* Window functions that need to happen every loop. */ +void +server_window_loop(void) +{ + struct window *w; + struct winlink *wl; + struct window_pane *wp; + struct session *s; + u_int i; + + for (i = 0; i < ARRAY_LENGTH(&windows); i++) { + w = ARRAY_ITEM(&windows, i); + if (w == NULL) + continue; + + RB_FOREACH(s, sessions, &sessions) { + wl = session_has(s, w); + if (wl == NULL) + continue; + + if (server_window_check_bell(s, wl) || + server_window_check_activity(s, wl) || + server_window_check_silence(s, wl)) + server_status_session(s); + TAILQ_FOREACH(wp, &w->panes, entry) + server_window_check_content(s, wl, wp); + } + w->flags &= ~(WINDOW_BELL|WINDOW_ACTIVITY); + } +} + +/* Check for bell in window. */ +int +server_window_check_bell(struct session *s, struct winlink *wl) +{ + struct client *c; + struct window *w = wl->window; + u_int i; + int action, visual; + + if (!(w->flags & WINDOW_BELL) || wl->flags & WINLINK_BELL) + return (0); + if (s->curw != wl) + wl->flags |= WINLINK_BELL; + + action = options_get_number(&s->options, "bell-action"); + switch (action) { + case BELL_ANY: + if (s->flags & SESSION_UNATTACHED) + break; + visual = options_get_number(&s->options, "visual-bell"); + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session != s) + continue; + if (!visual) { + tty_putcode(&c->tty, TTYC_BEL); + continue; + } + if (c->session->curw->window == w) { + status_message_set(c, "Bell in current window"); + continue; + } + status_message_set(c, "Bell in window %u", + winlink_find_by_window(&s->windows, w)->idx); + } + break; + case BELL_CURRENT: + if (s->flags & SESSION_UNATTACHED) + break; + visual = options_get_number(&s->options, "visual-bell"); + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session != s) + continue; + if (c->session->curw->window != w) + continue; + if (!visual) { + tty_putcode(&c->tty, TTYC_BEL); + continue; + } + status_message_set(c, "Bell in current window"); + } + break; + } + + return (1); +} + +/* Check for activity in window. */ +int +server_window_check_activity(struct session *s, struct winlink *wl) +{ + struct client *c; + struct window *w = wl->window; + u_int i; + + if (!(w->flags & WINDOW_ACTIVITY) || wl->flags & WINLINK_ACTIVITY) + return (0); + if (s->curw == wl) + return (0); + + if (!options_get_number(&w->options, "monitor-activity")) + return (0); + + if (options_get_number(&s->options, "bell-on-alert")) + ring_bell(s); + wl->flags |= WINLINK_ACTIVITY; + + if (options_get_number(&s->options, "visual-activity")) { + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session != s) + continue; + status_message_set(c, "Activity in window %u", + winlink_find_by_window(&s->windows, w)->idx); + } + } + + return (1); +} + +/* Check for silence in window. */ +int +server_window_check_silence(struct session *s, struct winlink *wl) +{ + struct client *c; + struct window *w = wl->window; + struct timeval timer; + u_int i; + int silence_interval, timer_difference; + + if (!(w->flags & WINDOW_SILENCE) || wl->flags & WINLINK_SILENCE) + return (0); + + if (s->curw == wl) { + /* + * Reset the timer for this window if we've focused it. We + * don't want the timer tripping as soon as we've switched away + * from this window. + */ + if (gettimeofday(&w->silence_timer, NULL) != 0) + fatal("gettimeofday failed."); + + return (0); + } + + silence_interval = options_get_number(&w->options, "monitor-silence"); + if (silence_interval == 0) + return (0); + + if (gettimeofday(&timer, NULL) != 0) + fatal("gettimeofday"); + timer_difference = timer.tv_sec - w->silence_timer.tv_sec; + if (timer_difference <= silence_interval) + return (0); + + if (options_get_number(&s->options, "bell-on-alert")) + ring_bell(s); + wl->flags |= WINLINK_SILENCE; + + if (options_get_number(&s->options, "visual-silence")) { + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session != s) + continue; + status_message_set(c, "Silence in window %u", + winlink_find_by_window(&s->windows, w)->idx); + } + } + + return (1); +} + +/* Check for content change in window. */ +int +server_window_check_content( + struct session *s, struct winlink *wl, struct window_pane *wp) +{ + struct client *c; + struct window *w = wl->window; + u_int i; + char *found, *ptr; + + /* Activity flag must be set for new content. */ + if (!(w->flags & WINDOW_ACTIVITY) || wl->flags & WINLINK_CONTENT) + return (0); + if (s->curw == wl) + return (0); + + ptr = options_get_string(&w->options, "monitor-content"); + if (ptr == NULL || *ptr == '\0') + return (0); + if ((found = window_pane_search(wp, ptr, NULL)) == NULL) + return (0); + xfree(found); + + if (options_get_number(&s->options, "bell-on-alert")) + ring_bell(s); + wl->flags |= WINLINK_CONTENT; + + if (options_get_number(&s->options, "visual-content")) { + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session != s) + continue; + status_message_set(c, "Content in window %u", + winlink_find_by_window(&s->windows, w)->idx); + } + } + + return (1); +} + +/* Ring terminal bell. */ +void +ring_bell(struct session *s) +{ + struct client *c; + u_int i; + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c != NULL && c->session == s) + tty_putcode(&c->tty, TTYC_BEL); + } +} diff --git a/external/bsd/tmux/dist/server.c b/external/bsd/tmux/dist/server.c new file mode 100644 index 000000000..cbcd520af --- /dev/null +++ b/external/bsd/tmux/dist/server.c @@ -0,0 +1,532 @@ +/* $Id: server.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tmux.h" + +/* + * Main server functions. + */ + +/* Client list. */ +struct clients clients; +struct clients dead_clients; + +int server_fd; +int server_shutdown; +struct event server_ev_accept; +struct event server_ev_second; + +struct paste_stack global_buffers; + +int server_create_socket(void); +void server_loop(void); +int server_should_shutdown(void); +void server_send_shutdown(void); +void server_clean_dead(void); +void server_accept_callback(int, short, void *); +void server_signal_callback(int, short, void *); +void server_child_signal(void); +void server_child_exited(pid_t, int); +void server_child_stopped(pid_t, int); +void server_second_callback(int, short, void *); +void server_lock_server(void); +void server_lock_sessions(void); + +/* Create server socket. */ +int +server_create_socket(void) +{ + struct sockaddr_un sa; + size_t size; + mode_t mask; + int fd; + + memset(&sa, 0, sizeof sa); + sa.sun_family = AF_UNIX; + size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path); + if (size >= sizeof sa.sun_path) { + errno = ENAMETOOLONG; + fatal("socket failed"); + } + unlink(sa.sun_path); + +#ifndef __minix + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) +#else + if ((fd = socket(AF_UNIX, SOCK_SEQPACKET, 0)) == -1) +#endif + fatal("socket failed"); + + mask = umask(S_IXUSR|S_IXGRP|S_IRWXO); + if (bind(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) + fatal("bind failed"); + umask(mask); + + if (listen(fd, 16) == -1) + fatal("listen failed"); + setblocking(fd, 0); + + server_update_socket(); + + return (fd); +} + +/* Fork new server. */ +int +server_start(void) +{ + struct window_pane *wp; + int pair[2]; + char *cause; + struct timeval tv; + u_int i; + + /* The first client is special and gets a socketpair; create it. */ +#ifndef __minix + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) +#else + if (socketpair(AF_UNIX, SOCK_SEQPACKET, PF_UNSPEC, pair) != 0) +#endif + fatal("socketpair failed"); + + switch (fork()) { + case -1: + fatal("fork failed"); + case 0: + break; + default: + close(pair[1]); + return (pair[0]); + } + close(pair[0]); + + /* + * Must daemonise before loading configuration as the PID changes so + * $TMUX would be wrong for sessions created in the config file. + */ + if (daemon(1, 0) != 0) + fatal("daemon failed"); + + /* event_init() was called in our parent, need to reinit. */ + if (event_reinit(ev_base) != 0) + fatal("event_reinit failed"); + clear_signals(0); + + logfile("server"); + log_debug("server started, pid %ld", (long) getpid()); + + ARRAY_INIT(&windows); + RB_INIT(&all_window_panes); + ARRAY_INIT(&clients); + ARRAY_INIT(&dead_clients); + RB_INIT(&sessions); + RB_INIT(&dead_sessions); + TAILQ_INIT(&session_groups); + ARRAY_INIT(&global_buffers); + mode_key_init_trees(); + key_bindings_init(); + utf8_build(); + + start_time = time(NULL); + log_debug("socket path %s", socket_path); +#ifdef HAVE_SETPROCTITLE + setproctitle("server (%s)", socket_path); +#endif + + server_fd = server_create_socket(); + server_client_create(pair[1]); + + if (access(SYSTEM_CFG, R_OK) == 0) + load_cfg(SYSTEM_CFG, NULL, &cfg_causes); + else if (errno != ENOENT) { + cfg_add_cause( + &cfg_causes, "%s: %s", strerror(errno), SYSTEM_CFG); + } + if (cfg_file != NULL) + load_cfg(cfg_file, NULL, &cfg_causes); + + /* + * If there is a session already, put the current window and pane into + * more mode. + */ + if (!RB_EMPTY(&sessions) && !ARRAY_EMPTY(&cfg_causes)) { + wp = RB_MIN(sessions, &sessions)->curw->window->active; + window_pane_set_mode(wp, &window_copy_mode); + window_copy_init_for_output(wp); + for (i = 0; i < ARRAY_LENGTH(&cfg_causes); i++) { + cause = ARRAY_ITEM(&cfg_causes, i); + window_copy_add(wp, "%s", cause); + xfree(cause); + } + ARRAY_FREE(&cfg_causes); + } + cfg_finished = 1; + + event_set(&server_ev_accept, + server_fd, EV_READ|EV_PERSIST, server_accept_callback, NULL); + event_add(&server_ev_accept, NULL); + + memset(&tv, 0, sizeof tv); + tv.tv_sec = 1; + evtimer_set(&server_ev_second, server_second_callback, NULL); + evtimer_add(&server_ev_second, &tv); + + set_signals(server_signal_callback); + server_loop(); + exit(0); +} + +/* Main server loop. */ +void +server_loop(void) +{ + while (!server_should_shutdown()) { + event_loop(EVLOOP_ONCE); + + server_window_loop(); + server_client_loop(); + + key_bindings_clean(); + server_clean_dead(); + } +} + +/* Check if the server should be shutting down (no more clients or sessions). */ +int +server_should_shutdown(void) +{ + u_int i; + + if (!options_get_number(&global_options, "exit-unattached")) { + if (!RB_EMPTY(&sessions)) + return (0); + } + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + if (ARRAY_ITEM(&clients, i) != NULL) + return (0); + } + return (1); +} + +/* Shutdown the server by killing all clients and windows. */ +void +server_send_shutdown(void) +{ + struct client *c; + struct session *s, *next_s; + u_int i; + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c != NULL) { + if (c->flags & (CLIENT_BAD|CLIENT_SUSPENDED)) + server_client_lost(c); + else + server_write_client(c, MSG_SHUTDOWN, NULL, 0); + c->session = NULL; + } + } + + s = RB_MIN(sessions, &sessions); + while (s != NULL) { + next_s = RB_NEXT(sessions, &sessions, s); + session_destroy(s); + s = next_s; + } +} + +/* Free dead, unreferenced clients and sessions. */ +void +server_clean_dead(void) +{ + struct session *s, *next_s; + struct client *c; + u_int i; + + s = RB_MIN(sessions, &dead_sessions); + while (s != NULL) { + next_s = RB_NEXT(sessions, &dead_sessions, s); + if (s->references == 0) { + RB_REMOVE(sessions, &dead_sessions, s); + xfree(s->name); + xfree(s); + } + s = next_s; + } + + for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) { + c = ARRAY_ITEM(&dead_clients, i); + if (c == NULL || c->references != 0) + continue; + ARRAY_SET(&dead_clients, i, NULL); + xfree(c); + } +} + +/* Update socket execute permissions based on whether sessions are attached. */ +void +server_update_socket(void) +{ + struct session *s; + static int last = -1; + int n, mode; + struct stat sb; + + n = 0; + RB_FOREACH(s, sessions, &sessions) { + if (!(s->flags & SESSION_UNATTACHED)) { + n++; + break; + } + } + + if (n != last) { + last = n; + + if (stat(socket_path, &sb) != 0) + return; + mode = sb.st_mode; + if (n != 0) { + if (mode & S_IRUSR) + mode |= S_IXUSR; + if (mode & S_IRGRP) + mode |= S_IXGRP; + if (mode & S_IROTH) + mode |= S_IXOTH; + } else + mode &= ~(S_IXUSR|S_IXGRP|S_IXOTH); + chmod(socket_path, mode); + } +} + +/* Callback for server socket. */ +/* ARGSUSED */ +void +server_accept_callback(int fd, short events, unused void *data) +{ + struct sockaddr_storage sa; + socklen_t slen = sizeof sa; + int newfd; + + if (!(events & EV_READ)) + return; + + newfd = accept(fd, (struct sockaddr *) &sa, &slen); + if (newfd == -1) { + if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED) + return; + fatal("accept failed"); + } + if (server_shutdown) { + close(newfd); + return; + } + server_client_create(newfd); +} + +/* Signal handler. */ +/* ARGSUSED */ +void +server_signal_callback(int sig, unused short events, unused void *data) +{ + switch (sig) { + case SIGTERM: + server_shutdown = 1; + server_send_shutdown(); + break; + case SIGCHLD: + server_child_signal(); + break; + case SIGUSR1: + event_del(&server_ev_accept); + close(server_fd); + server_fd = server_create_socket(); + event_set(&server_ev_accept, server_fd, + EV_READ|EV_PERSIST, server_accept_callback, NULL); + event_add(&server_ev_accept, NULL); + break; + } +} + +/* Handle SIGCHLD. */ +void +server_child_signal(void) +{ + int status; + pid_t pid; + + for (;;) { + switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) { + case -1: + if (errno == ECHILD) + return; + fatal("waitpid failed"); + case 0: + return; + } + if (WIFSTOPPED(status)) + server_child_stopped(pid, status); + else if (WIFEXITED(status) || WIFSIGNALED(status)) + server_child_exited(pid, status); + } +} + +/* Handle exited children. */ +void +server_child_exited(pid_t pid, int status) +{ + struct window *w; + struct window_pane *wp; + struct job *job; + u_int i; + + for (i = 0; i < ARRAY_LENGTH(&windows); i++) { + if ((w = ARRAY_ITEM(&windows, i)) == NULL) + continue; + TAILQ_FOREACH(wp, &w->panes, entry) { + if (wp->pid == pid) { + server_destroy_pane(wp); + break; + } + } + } + + LIST_FOREACH(job, &all_jobs, lentry) { + if (pid == job->pid) { + job_died(job, status); /* might free job */ + break; + } + } +} + +/* Handle stopped children. */ +void +server_child_stopped(pid_t pid, int status) +{ + struct window *w; + struct window_pane *wp; + u_int i; + + if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU) + return; + + for (i = 0; i < ARRAY_LENGTH(&windows); i++) { + if ((w = ARRAY_ITEM(&windows, i)) == NULL) + continue; + TAILQ_FOREACH(wp, &w->panes, entry) { + if (wp->pid == pid) { + if (killpg(pid, SIGCONT) != 0) + kill(pid, SIGCONT); + } + } + } +} + +/* Handle once-per-second timer events. */ +/* ARGSUSED */ +void +server_second_callback(unused int fd, unused short events, unused void *arg) +{ + struct window *w; + struct window_pane *wp; + struct timeval tv; + u_int i; + + if (options_get_number(&global_s_options, "lock-server")) + server_lock_server(); + else + server_lock_sessions(); + + for (i = 0; i < ARRAY_LENGTH(&windows); i++) { + w = ARRAY_ITEM(&windows, i); + if (w == NULL) + continue; + + TAILQ_FOREACH(wp, &w->panes, entry) { + if (wp->mode != NULL && wp->mode->timer != NULL) + wp->mode->timer(wp); + } + } + + server_client_status_timer(); + + evtimer_del(&server_ev_second); + memset(&tv, 0, sizeof tv); + tv.tv_sec = 1; + evtimer_add(&server_ev_second, &tv); +} + +/* Lock the server if ALL sessions have hit the time limit. */ +void +server_lock_server(void) +{ + struct session *s; + int timeout; + time_t t; + + t = time(NULL); + RB_FOREACH(s, sessions, &sessions) { + if (s->flags & SESSION_UNATTACHED) + continue; + timeout = options_get_number(&s->options, "lock-after-time"); + if (timeout <= 0 || t <= s->activity_time.tv_sec + timeout) + return; /* not timed out */ + } + + server_lock(); + recalculate_sizes(); +} + +/* Lock any sessions which have timed out. */ +void +server_lock_sessions(void) +{ + struct session *s; + int timeout; + time_t t; + + t = time(NULL); + RB_FOREACH(s, sessions, &sessions) { + if (s->flags & SESSION_UNATTACHED) + continue; + timeout = options_get_number(&s->options, "lock-after-time"); + if (timeout > 0 && t > s->activity_time.tv_sec + timeout) { + server_lock_session(s); + recalculate_sizes(); + } + } +} diff --git a/external/bsd/tmux/dist/session.c b/external/bsd/tmux/dist/session.c new file mode 100644 index 000000000..d9780a43c --- /dev/null +++ b/external/bsd/tmux/dist/session.c @@ -0,0 +1,573 @@ +/* $Id: session.c,v 1.3 2011/08/17 18:48:36 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include +#include +#include +#include + +#include "tmux.h" + +/* Global session list. */ +struct sessions sessions; +struct sessions dead_sessions; +u_int next_session; +struct session_groups session_groups; + +struct winlink *session_next_alert(struct winlink *); +struct winlink *session_previous_alert(struct winlink *); + +RB_GENERATE(sessions, session, entry, session_cmp); + +int +session_cmp(struct session *s1, struct session *s2) +{ + return (strcmp(s1->name, s2->name)); +} + +/* + * Find if session is still alive. This is true if it is still on the global + * sessions list. + */ +int +session_alive(struct session *s) +{ + struct session *s_loop; + + RB_FOREACH(s_loop, sessions, &sessions) { + if (s_loop == s) + return (1); + } + return (0); +} + +/* Find session by name. */ +struct session * +session_find(const char *name) +{ + struct session s; + + s.name = __UNCONST(name); + return (RB_FIND(sessions, &sessions, &s)); +} + +/* Find session by index. */ +struct session * +session_find_by_index(u_int idx) +{ + struct session *s; + + RB_FOREACH(s, sessions, &sessions) { + if (s->idx == idx) + return (s); + } + return (NULL); +} + +/* Create a new session. */ +struct session * +session_create(const char *name, const char *cmd, const char *cwd, + struct environ *env, struct termios *tio, int idx, u_int sx, u_int sy, + char **cause) +{ + struct session *s; + + s = xmalloc(sizeof *s); + s->references = 0; + s->flags = 0; + + if (gettimeofday(&s->creation_time, NULL) != 0) + fatal("gettimeofday failed"); + session_update_activity(s); + + s->cwd = xstrdup(cwd); + + s->curw = NULL; + TAILQ_INIT(&s->lastw); + RB_INIT(&s->windows); + + options_init(&s->options, &global_s_options); + environ_init(&s->environ); + if (env != NULL) + environ_copy(env, &s->environ); + + s->tio = NULL; + if (tio != NULL) { + s->tio = xmalloc(sizeof *s->tio); + memcpy(s->tio, tio, sizeof *s->tio); + } + + s->sx = sx; + s->sy = sy; + + s->idx = next_session++; + if (name != NULL) + s->name = xstrdup(name); + else + xasprintf(&s->name, "%u", s->idx); + RB_INSERT(sessions, &sessions, s); + + if (cmd != NULL) { + if (session_new(s, NULL, cmd, cwd, idx, cause) == NULL) { + session_destroy(s); + return (NULL); + } + session_select(s, RB_ROOT(&s->windows)->idx); + } + + log_debug("session %s created", s->name); + + return (s); +} + +/* Destroy a session. */ +void +session_destroy(struct session *s) +{ + log_debug("session %s destroyed", s->name); + + RB_REMOVE(sessions, &sessions, s); + + if (s->tio != NULL) + xfree(s->tio); + + session_group_remove(s); + environ_free(&s->environ); + options_free(&s->options); + + while (!TAILQ_EMPTY(&s->lastw)) + winlink_stack_remove(&s->lastw, TAILQ_FIRST(&s->lastw)); + while (!RB_EMPTY(&s->windows)) + winlink_remove(&s->windows, RB_ROOT(&s->windows)); + + xfree(s->cwd); + + RB_INSERT(sessions, &dead_sessions, s); +} + +/* Check a session name is valid: not empty and no colons. */ +int +session_check_name(const char *name) +{ + return (*name != '\0' && strchr(name, ':') == NULL); +} + +/* Update session active time. */ +void +session_update_activity(struct session *s) +{ + if (gettimeofday(&s->activity_time, NULL) != 0) + fatal("gettimeofday"); +} + +/* Find the next usable session. */ +struct session * +session_next_session(struct session *s) +{ + struct session *s2; + + if (RB_EMPTY(&sessions) || !session_alive(s)) + return (NULL); + + s2 = RB_NEXT(sessions, &sessions, s); + if (s2 == NULL) + s2 = RB_MIN(sessions, &sessions); + if (s2 == s) + return (NULL); + return (s2); +} + +/* Find the previous usable session. */ +struct session * +session_previous_session(struct session *s) +{ + struct session *s2; + + if (RB_EMPTY(&sessions) || !session_alive(s)) + return (NULL); + + s2 = RB_PREV(sessions, &sessions, s); + if (s2 == NULL) + s2 = RB_MAX(sessions, &sessions); + if (s2 == s) + return (NULL); + return (s2); +} + +/* Create a new window on a session. */ +struct winlink * +session_new(struct session *s, + const char *name, const char *cmd, const char *cwd, int idx, char **cause) +{ + struct window *w; + struct winlink *wl; + struct environ env; + const char *shell; + u_int hlimit; + + if ((wl = winlink_add(&s->windows, idx)) == NULL) { + xasprintf(cause, "index in use: %d", idx); + return (NULL); + } + + environ_init(&env); + environ_copy(&global_environ, &env); + environ_copy(&s->environ, &env); + server_fill_environ(s, &env); + + shell = options_get_string(&s->options, "default-shell"); + if (*shell == '\0' || areshell(shell)) + shell = _PATH_BSHELL; + + hlimit = options_get_number(&s->options, "history-limit"); + w = window_create( + name, cmd, shell, cwd, &env, s->tio, s->sx, s->sy, hlimit, cause); + if (w == NULL) { + winlink_remove(&s->windows, wl); + environ_free(&env); + return (NULL); + } + winlink_set_window(wl, w); + environ_free(&env); + + if (options_get_number(&s->options, "set-remain-on-exit")) + options_set_number(&w->options, "remain-on-exit", 1); + + session_group_synchronize_from(s); + return (wl); +} + +/* Attach a window to a session. */ +struct winlink * +session_attach(struct session *s, struct window *w, int idx, char **cause) +{ + struct winlink *wl; + + if ((wl = winlink_add(&s->windows, idx)) == NULL) { + xasprintf(cause, "index in use: %d", idx); + return (NULL); + } + winlink_set_window(wl, w); + + session_group_synchronize_from(s); + return (wl); +} + +/* Detach a window from a session. */ +int +session_detach(struct session *s, struct winlink *wl) +{ + if (s->curw == wl && + session_last(s) != 0 && session_previous(s, 0) != 0) + session_next(s, 0); + + wl->flags &= ~WINLINK_ALERTFLAGS; + winlink_stack_remove(&s->lastw, wl); + winlink_remove(&s->windows, wl); + session_group_synchronize_from(s); + if (RB_EMPTY(&s->windows)) { + session_destroy(s); + return (1); + } + return (0); +} + +/* Return if session has window. */ +struct winlink * +session_has(struct session *s, struct window *w) +{ + struct winlink *wl; + + RB_FOREACH(wl, winlinks, &s->windows) { + if (wl->window == w) + return (wl); + } + return (NULL); +} + +struct winlink * +session_next_alert(struct winlink *wl) +{ + while (wl != NULL) { + if (wl->flags & WINLINK_ALERTFLAGS) + break; + wl = winlink_next(wl); + } + return (wl); +} + +/* Move session to next window. */ +int +session_next(struct session *s, int alert) +{ + struct winlink *wl; + + if (s->curw == NULL) + return (-1); + + wl = winlink_next(s->curw); + if (alert) + wl = session_next_alert(wl); + if (wl == NULL) { + wl = RB_MIN(winlinks, &s->windows); + if (alert && ((wl = session_next_alert(wl)) == NULL)) + return (-1); + } + if (wl == s->curw) + return (1); + winlink_stack_remove(&s->lastw, wl); + winlink_stack_push(&s->lastw, s->curw); + s->curw = wl; + wl->flags &= ~WINLINK_ALERTFLAGS; + return (0); +} + +struct winlink * +session_previous_alert(struct winlink *wl) +{ + while (wl != NULL) { + if (wl->flags & WINLINK_ALERTFLAGS) + break; + wl = winlink_previous(wl); + } + return (wl); +} + +/* Move session to previous window. */ +int +session_previous(struct session *s, int alert) +{ + struct winlink *wl; + + if (s->curw == NULL) + return (-1); + + wl = winlink_previous(s->curw); + if (alert) + wl = session_previous_alert(wl); + if (wl == NULL) { + wl = RB_MAX(winlinks, &s->windows); + if (alert && (wl = session_previous_alert(wl)) == NULL) + return (-1); + } + if (wl == s->curw) + return (1); + winlink_stack_remove(&s->lastw, wl); + winlink_stack_push(&s->lastw, s->curw); + s->curw = wl; + wl->flags &= ~WINLINK_ALERTFLAGS; + return (0); +} + +/* Move session to specific window. */ +int +session_select(struct session *s, int idx) +{ + struct winlink *wl; + + wl = winlink_find_by_index(&s->windows, idx); + if (wl == NULL) + return (-1); + if (wl == s->curw) + return (1); + winlink_stack_remove(&s->lastw, wl); + winlink_stack_push(&s->lastw, s->curw); + s->curw = wl; + wl->flags &= ~WINLINK_ALERTFLAGS; + return (0); +} + +/* Move session to last used window. */ +int +session_last(struct session *s) +{ + struct winlink *wl; + + wl = TAILQ_FIRST(&s->lastw); + if (wl == NULL) + return (-1); + if (wl == s->curw) + return (1); + + winlink_stack_remove(&s->lastw, wl); + winlink_stack_push(&s->lastw, s->curw); + s->curw = wl; + wl->flags &= ~WINLINK_ALERTFLAGS; + return (0); +} + +/* Find the session group containing a session. */ +struct session_group * +session_group_find(struct session *target) +{ + struct session_group *sg; + struct session *s; + + TAILQ_FOREACH(sg, &session_groups, entry) { + TAILQ_FOREACH(s, &sg->sessions, gentry) { + if (s == target) + return (sg); + } + } + return (NULL); +} + +/* Find session group index. */ +u_int +session_group_index(struct session_group *sg) +{ + struct session_group *sg2; + u_int i; + + i = 0; + TAILQ_FOREACH(sg2, &session_groups, entry) { + if (sg == sg2) + return (i); + i++; + } + + fatalx("session group not found"); +} + +/* + * Add a session to the session group containing target, creating it if + * necessary. + */ +void +session_group_add(struct session *target, struct session *s) +{ + struct session_group *sg; + + if ((sg = session_group_find(target)) == NULL) { + sg = xmalloc(sizeof *sg); + TAILQ_INSERT_TAIL(&session_groups, sg, entry); + TAILQ_INIT(&sg->sessions); + TAILQ_INSERT_TAIL(&sg->sessions, target, gentry); + } + TAILQ_INSERT_TAIL(&sg->sessions, s, gentry); +} + +/* Remove a session from its group and destroy the group if empty. */ +void +session_group_remove(struct session *s) +{ + struct session_group *sg; + + if ((sg = session_group_find(s)) == NULL) + return; + TAILQ_REMOVE(&sg->sessions, s, gentry); + if (TAILQ_NEXT(TAILQ_FIRST(&sg->sessions), gentry) == NULL) + TAILQ_REMOVE(&sg->sessions, TAILQ_FIRST(&sg->sessions), gentry); + if (TAILQ_EMPTY(&sg->sessions)) { + TAILQ_REMOVE(&session_groups, sg, entry); + xfree(sg); + } +} + +/* Synchronize a session to its session group. */ +void +session_group_synchronize_to(struct session *s) +{ + struct session_group *sg; + struct session *target; + + if ((sg = session_group_find(s)) == NULL) + return; + + target = NULL; + TAILQ_FOREACH(target, &sg->sessions, gentry) { + if (target != s) + break; + } + session_group_synchronize1(target, s); +} + +/* Synchronize a session group to a session. */ +void +session_group_synchronize_from(struct session *target) +{ + struct session_group *sg; + struct session *s; + + if ((sg = session_group_find(target)) == NULL) + return; + + TAILQ_FOREACH(s, &sg->sessions, gentry) { + if (s != target) + session_group_synchronize1(target, s); + } +} + +/* + * Synchronize a session with a target session. This means destroying all + * winlinks then recreating them, then updating the current window, last window + * stack and alerts. + */ +void +session_group_synchronize1(struct session *target, struct session *s) +{ + struct winlinks old_windows, *ww; + struct winlink_stack old_lastw; + struct winlink *wl, *wl2; + + /* Don't do anything if the session is empty (it'll be destroyed). */ + ww = &target->windows; + if (RB_EMPTY(ww)) + return; + + /* If the current window has vanished, move to the next now. */ + if (s->curw != NULL && + winlink_find_by_index(ww, s->curw->idx) == NULL && + session_last(s) != 0 && session_previous(s, 0) != 0) + session_next(s, 0); + + /* Save the old pointer and reset it. */ + memcpy(&old_windows, &s->windows, sizeof old_windows); + RB_INIT(&s->windows); + + /* Link all the windows from the target. */ + RB_FOREACH(wl, winlinks, ww) { + wl2 = winlink_add(&s->windows, wl->idx); + winlink_set_window(wl2, wl->window); + wl2->flags |= wl->flags & WINLINK_ALERTFLAGS; + } + + /* Fix up the current window. */ + if (s->curw != NULL) + s->curw = winlink_find_by_index(&s->windows, s->curw->idx); + else + s->curw = winlink_find_by_index(&s->windows, target->curw->idx); + + /* Fix up the last window stack. */ + memcpy(&old_lastw, &s->lastw, sizeof old_lastw); + TAILQ_INIT(&s->lastw); + TAILQ_FOREACH(wl, &old_lastw, sentry) { + wl2 = winlink_find_by_index(&s->windows, wl->idx); + if (wl2 != NULL) + TAILQ_INSERT_TAIL(&s->lastw, wl2, sentry); + } + + /* Then free the old winlinks list. */ + while (!RB_EMPTY(&old_windows)) { + wl = RB_ROOT(&old_windows); + winlink_remove(&old_windows, wl); + } +} diff --git a/external/bsd/tmux/dist/signal.c b/external/bsd/tmux/dist/signal.c new file mode 100644 index 000000000..d3e1d0f56 --- /dev/null +++ b/external/bsd/tmux/dist/signal.c @@ -0,0 +1,103 @@ +/* $Id: signal.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2010 Romain Francoise + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include "tmux.h" + +struct event ev_sighup; +struct event ev_sigchld; +struct event ev_sigcont; +struct event ev_sigterm; +struct event ev_sigusr1; +struct event ev_sigwinch; + +void +set_signals(void(*handler)(int, short, unused void *)) +{ + struct sigaction sigact; + + memset(&sigact, 0, sizeof sigact); + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = SA_RESTART; + sigact.sa_handler = SIG_IGN; + if (sigaction(SIGINT, &sigact, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGPIPE, &sigact, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGUSR2, &sigact, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGTSTP, &sigact, NULL) != 0) + fatal("sigaction failed"); + + signal_set(&ev_sighup, SIGHUP, handler, NULL); + signal_add(&ev_sighup, NULL); + signal_set(&ev_sigchld, SIGCHLD, handler, NULL); + signal_add(&ev_sigchld, NULL); + signal_set(&ev_sigcont, SIGCONT, handler, NULL); + signal_add(&ev_sigcont, NULL); + signal_set(&ev_sigterm, SIGTERM, handler, NULL); + signal_add(&ev_sigterm, NULL); + signal_set(&ev_sigusr1, SIGUSR1, handler, NULL); + signal_add(&ev_sigusr1, NULL); + signal_set(&ev_sigwinch, SIGWINCH, handler, NULL); + signal_add(&ev_sigwinch, NULL); +} + +void +clear_signals(int after_fork) +{ + struct sigaction sigact; + + memset(&sigact, 0, sizeof sigact); + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = SA_RESTART; + sigact.sa_handler = SIG_DFL; + if (sigaction(SIGINT, &sigact, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGPIPE, &sigact, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGUSR2, &sigact, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGTSTP, &sigact, NULL) != 0) + fatal("sigaction failed"); + + if (after_fork) { + if (sigaction(SIGHUP, &sigact, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGCHLD, &sigact, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGCONT, &sigact, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGTERM, &sigact, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGUSR1, &sigact, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGWINCH, &sigact, NULL) != 0) + fatal("sigaction failed"); + } else { + event_del(&ev_sighup); + event_del(&ev_sigchld); + event_del(&ev_sigcont); + event_del(&ev_sigterm); + event_del(&ev_sigusr1); + event_del(&ev_sigwinch); + } +} diff --git a/external/bsd/tmux/dist/status.c b/external/bsd/tmux/dist/status.c new file mode 100644 index 000000000..8bd009f1c --- /dev/null +++ b/external/bsd/tmux/dist/status.c @@ -0,0 +1,1284 @@ +/* $Id: status.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "tmux.h" + +char *status_redraw_get_left( + struct client *, time_t, int, struct grid_cell *, size_t *); +char *status_redraw_get_right( + struct client *, time_t, int, struct grid_cell *, size_t *); +char *status_find_job(struct client *, char **); +void status_job_free(void *); +void status_job_callback(struct job *); +char *status_print( + struct client *, struct winlink *, time_t, struct grid_cell *); +void status_replace1(struct client *, struct session *, struct winlink *, + struct window_pane *, char **, char **, char *, size_t, int); +void status_message_callback(int, short, void *); + +const char *status_prompt_up_history(u_int *); +const char *status_prompt_down_history(u_int *); +void status_prompt_add_history(const char *); +char *status_prompt_complete(const char *); + +/* Status prompt history. */ +ARRAY_DECL(, char *) status_prompt_history = ARRAY_INITIALIZER; + +/* Status output tree. */ +RB_GENERATE(status_out_tree, status_out, entry, status_out_cmp); + +/* Output tree comparison function. */ +int +status_out_cmp(struct status_out *so1, struct status_out *so2) +{ + return (strcmp(so1->cmd, so2->cmd)); +} + +/* Retrieve options for left string. */ +char * +status_redraw_get_left(struct client *c, + time_t t, int utf8flag, struct grid_cell *gc, size_t *size) +{ + struct session *s = c->session; + char *left; + u_char fg, bg, attr; + size_t leftlen; + + fg = options_get_number(&s->options, "status-left-fg"); + if (fg != 8) + colour_set_fg(gc, fg); + bg = options_get_number(&s->options, "status-left-bg"); + if (bg != 8) + colour_set_bg(gc, bg); + attr = options_get_number(&s->options, "status-left-attr"); + if (attr != 0) + gc->attr = attr; + + left = status_replace(c, NULL, + NULL, NULL, options_get_string(&s->options, "status-left"), t, 1); + + *size = options_get_number(&s->options, "status-left-length"); + leftlen = screen_write_cstrlen(utf8flag, "%s", left); + if (leftlen < *size) + *size = leftlen; + return (left); +} + +/* Retrieve options for right string. */ +char * +status_redraw_get_right(struct client *c, + time_t t, int utf8flag, struct grid_cell *gc, size_t *size) +{ + struct session *s = c->session; + char *right; + u_char fg, bg, attr; + size_t rightlen; + + fg = options_get_number(&s->options, "status-right-fg"); + if (fg != 8) + colour_set_fg(gc, fg); + bg = options_get_number(&s->options, "status-right-bg"); + if (bg != 8) + colour_set_bg(gc, bg); + attr = options_get_number(&s->options, "status-right-attr"); + if (attr != 0) + gc->attr = attr; + + right = status_replace(c, NULL, + NULL, NULL, options_get_string(&s->options, "status-right"), t, 1); + + *size = options_get_number(&s->options, "status-right-length"); + rightlen = screen_write_cstrlen(utf8flag, "%s", right); + if (rightlen < *size) + *size = rightlen; + return (right); +} + +/* Set window at window list position. */ +void +status_set_window_at(struct client *c, u_int x) +{ + struct session *s = c->session; + struct winlink *wl; + + x += s->wlmouse; + RB_FOREACH(wl, winlinks, &s->windows) { + if (x < wl->status_width && + session_select(s, wl->idx) == 0) { + server_redraw_session(s); + } + x -= wl->status_width + 1; + } +} + +/* Draw status for client on the last lines of given context. */ +int +status_redraw(struct client *c) +{ + struct screen_write_ctx ctx; + struct session *s = c->session; + struct winlink *wl; + struct screen old_status, window_list; + struct grid_cell stdgc, lgc, rgc, gc; + time_t t; + char *left, *right; + u_int offset, needed; + u_int wlstart, wlwidth, wlavailable, wloffset, wlsize; + size_t llen, rlen; + int larrow, rarrow, utf8flag; + + /* No status line? */ + if (c->tty.sy == 0 || !options_get_number(&s->options, "status")) + return (1); + left = right = NULL; + larrow = rarrow = 0; + + /* Update status timer. */ + if (gettimeofday(&c->status_timer, NULL) != 0) + fatal("gettimeofday failed"); + t = c->status_timer.tv_sec; + + /* Set up default colour. */ + memcpy(&stdgc, &grid_default_cell, sizeof gc); + colour_set_fg(&stdgc, options_get_number(&s->options, "status-fg")); + colour_set_bg(&stdgc, options_get_number(&s->options, "status-bg")); + stdgc.attr |= options_get_number(&s->options, "status-attr"); + + /* Create the target screen. */ + memcpy(&old_status, &c->status, sizeof old_status); + screen_init(&c->status, c->tty.sx, 1, 0); + screen_write_start(&ctx, NULL, &c->status); + for (offset = 0; offset < c->tty.sx; offset++) + screen_write_putc(&ctx, &stdgc, ' '); + screen_write_stop(&ctx); + + /* If the height is one line, blank status line. */ + if (c->tty.sy <= 1) + goto out; + + /* Get UTF-8 flag. */ + utf8flag = options_get_number(&s->options, "status-utf8"); + + /* Work out left and right strings. */ + memcpy(&lgc, &stdgc, sizeof lgc); + left = status_redraw_get_left(c, t, utf8flag, &lgc, &llen); + memcpy(&rgc, &stdgc, sizeof rgc); + right = status_redraw_get_right(c, t, utf8flag, &rgc, &rlen); + + /* + * Figure out how much space we have for the window list. If there + * isn't enough space, just show a blank status line. + */ + needed = 0; + if (llen != 0) + needed += llen + 1; + if (rlen != 0) + needed += rlen + 1; + if (c->tty.sx == 0 || c->tty.sx <= needed) + goto out; + wlavailable = c->tty.sx - needed; + + /* Calculate the total size needed for the window list. */ + wlstart = wloffset = wlwidth = 0; + RB_FOREACH(wl, winlinks, &s->windows) { + if (wl->status_text != NULL) + xfree(wl->status_text); + memcpy(&wl->status_cell, &stdgc, sizeof wl->status_cell); + wl->status_text = status_print(c, wl, t, &wl->status_cell); + wl->status_width = + screen_write_cstrlen(utf8flag, "%s", wl->status_text); + + if (wl == s->curw) + wloffset = wlwidth; + wlwidth += wl->status_width + 1; + } + + /* Create a new screen for the window list. */ + screen_init(&window_list, wlwidth, 1, 0); + + /* And draw the window list into it. */ + screen_write_start(&ctx, NULL, &window_list); + RB_FOREACH(wl, winlinks, &s->windows) { + screen_write_cnputs(&ctx, + -1, &wl->status_cell, utf8flag, "%s", wl->status_text); + screen_write_putc(&ctx, &stdgc, ' '); + } + screen_write_stop(&ctx); + + /* If there is enough space for the total width, skip to draw now. */ + if (wlwidth <= wlavailable) + goto draw; + + /* Find size of current window text. */ + wlsize = s->curw->status_width; + + /* + * If the current window is already on screen, good to draw from the + * start and just leave off the end. + */ + if (wloffset + wlsize < wlavailable) { + if (wlavailable > 0) { + rarrow = 1; + wlavailable--; + } + wlwidth = wlavailable; + } else { + /* + * Work out how many characters we need to omit from the + * start. There are wlavailable characters to fill, and + * wloffset + wlsize must be the last. So, the start character + * is wloffset + wlsize - wlavailable. + */ + if (wlavailable > 0) { + larrow = 1; + wlavailable--; + } + + wlstart = wloffset + wlsize - wlavailable; + if (wlavailable > 0 && wlwidth > wlstart + wlavailable + 1) { + rarrow = 1; + wlstart++; + wlavailable--; + } + wlwidth = wlavailable; + } + + /* Bail if anything is now too small too. */ + if (wlwidth == 0 || wlavailable == 0) { + screen_free(&window_list); + goto out; + } + + /* + * Now the start position is known, work out the state of the left and + * right arrows. + */ + offset = 0; + RB_FOREACH(wl, winlinks, &s->windows) { + if (wl->flags & WINLINK_ALERTFLAGS && + larrow == 1 && offset < wlstart) + larrow = -1; + + offset += wl->status_width; + + if (wl->flags & WINLINK_ALERTFLAGS && + rarrow == 1 && offset > wlstart + wlwidth) + rarrow = -1; + } + +draw: + /* Begin drawing. */ + screen_write_start(&ctx, NULL, &c->status); + + /* Draw the left string and arrow. */ + screen_write_cursormove(&ctx, 0, 0); + if (llen != 0) { + screen_write_cnputs(&ctx, llen, &lgc, utf8flag, "%s", left); + screen_write_putc(&ctx, &stdgc, ' '); + } + if (larrow != 0) { + memcpy(&gc, &stdgc, sizeof gc); + if (larrow == -1) + gc.attr ^= GRID_ATTR_REVERSE; + screen_write_putc(&ctx, &gc, '<'); + } + + /* Draw the right string and arrow. */ + if (rarrow != 0) { + screen_write_cursormove(&ctx, c->tty.sx - rlen - 2, 0); + memcpy(&gc, &stdgc, sizeof gc); + if (rarrow == -1) + gc.attr ^= GRID_ATTR_REVERSE; + screen_write_putc(&ctx, &gc, '>'); + } else + screen_write_cursormove(&ctx, c->tty.sx - rlen - 1, 0); + if (rlen != 0) { + screen_write_putc(&ctx, &stdgc, ' '); + screen_write_cnputs(&ctx, rlen, &rgc, utf8flag, "%s", right); + } + + /* Figure out the offset for the window list. */ + if (llen != 0) + wloffset = llen + 1; + else + wloffset = 0; + if (wlwidth < wlavailable) { + switch (options_get_number(&s->options, "status-justify")) { + case 1: /* centered */ + wloffset += (wlavailable - wlwidth) / 2; + break; + case 2: /* right */ + wloffset += (wlavailable - wlwidth); + break; + } + } + if (larrow != 0) + wloffset++; + + /* Copy the window list. */ + s->wlmouse = -wloffset + wlstart; + screen_write_cursormove(&ctx, wloffset, 0); + screen_write_copy(&ctx, &window_list, wlstart, 0, wlwidth, 1); + screen_free(&window_list); + + screen_write_stop(&ctx); + +out: + if (left != NULL) + xfree(left); + if (right != NULL) + xfree(right); + + if (grid_compare(c->status.grid, old_status.grid) == 0) { + screen_free(&old_status); + return (0); + } + screen_free(&old_status); + return (1); +} + +/* Replace a single special sequence (prefixed by #). */ +void +status_replace1(struct client *c, struct session *s, struct winlink *wl, + struct window_pane *wp, char **iptr, char **optr, char *out, + size_t outsize, int jobsflag) +{ + char ch, tmp[256], *ptr, *endptr, *freeptr; + size_t ptrlen; + long limit; + + if (s == NULL) + s = c->session; + if (wl == NULL) + wl = s->curw; + if (wp == NULL) + wp = wl->window->active; + + errno = 0; + limit = strtol(*iptr, &endptr, 10); + if ((limit == 0 && errno != EINVAL) || + (limit == LONG_MIN && errno != ERANGE) || + (limit == LONG_MAX && errno != ERANGE) || + limit != 0) + *iptr = endptr; + if (limit <= 0) + limit = LONG_MAX; + + freeptr = NULL; + + switch (*(*iptr)++) { + case '(': + if (!jobsflag) { + ch = ')'; + goto skip_to; + } + if ((ptr = status_find_job(c, iptr)) == NULL) + return; + goto do_replace; + case 'D': + xsnprintf(tmp, sizeof tmp, "%%%u", wp->id); + ptr = tmp; + goto do_replace; + case 'H': + if (gethostname(tmp, sizeof tmp) != 0) + fatal("gethostname failed"); + ptr = tmp; + goto do_replace; + case 'h': + if (gethostname(tmp, sizeof tmp) != 0) + fatal("gethostname failed"); + if ((ptr = strchr(tmp, '.')) != NULL) + *ptr = '\0'; + ptr = tmp; + goto do_replace; + case 'I': + xsnprintf(tmp, sizeof tmp, "%d", wl->idx); + ptr = tmp; + goto do_replace; + case 'P': + xsnprintf( + tmp, sizeof tmp, "%u", window_pane_index(wl->window, wp)); + ptr = tmp; + goto do_replace; + case 'S': + ptr = s->name; + goto do_replace; + case 'T': + ptr = wp->base.title; + goto do_replace; + case 'W': + ptr = wl->window->name; + goto do_replace; + case 'F': + ptr = window_printable_flags(s, wl); + freeptr = ptr; + goto do_replace; + case '[': + /* + * Embedded style, handled at display time. Leave present and + * skip input until ]. + */ + ch = ']'; + goto skip_to; + case '#': + *(*optr)++ = '#'; + break; + } + + return; + +do_replace: + ptrlen = strlen(ptr); + if ((size_t) limit < ptrlen) + ptrlen = limit; + + if (*optr + ptrlen >= out + outsize - 1) + return; + while (ptrlen > 0 && *ptr != '\0') { + *(*optr)++ = *ptr++; + ptrlen--; + } + + if (freeptr != NULL) + xfree(freeptr); + return; + +skip_to: + *(*optr)++ = '#'; + + (*iptr)--; /* include ch */ + while (**iptr != ch && **iptr != '\0') { + if (*optr >= out + outsize - 1) + break; + *(*optr)++ = *(*iptr)++; + } +} + +/* Replace special sequences in fmt. */ +char * +status_replace(struct client *c, struct session *s, struct winlink *wl, + struct window_pane *wp, const char *fmt, time_t t, int jobsflag) +{ + static char out[BUFSIZ]; + char in[BUFSIZ], ch, *iptr, *optr; + + strftime(in, sizeof in, fmt, localtime(&t)); + in[(sizeof in) - 1] = '\0'; + + iptr = in; + optr = out; + + while (*iptr != '\0') { + if (optr >= out + (sizeof out) - 1) + break; + ch = *iptr++; + + if (ch != '#' || *iptr == '\0') { + *optr++ = ch; + continue; + } + status_replace1( + c, s, wl, wp, &iptr, &optr, out, sizeof out, jobsflag); + } + *optr = '\0'; + + return (xstrdup(out)); +} + +/* Figure out job name and get its result, starting it off if necessary. */ +char * +status_find_job(struct client *c, char **iptr) +{ + struct status_out *so, so_find; + char *cmd; + int lastesc; + size_t len; + + if (**iptr == '\0') + return (NULL); + if (**iptr == ')') { /* no command given */ + (*iptr)++; + return (NULL); + } + + cmd = xmalloc(strlen(*iptr) + 1); + len = 0; + + lastesc = 0; + for (; **iptr != '\0'; (*iptr)++) { + if (!lastesc && **iptr == ')') + break; /* unescaped ) is the end */ + if (!lastesc && **iptr == '\\') { + lastesc = 1; + continue; /* skip \ if not escaped */ + } + lastesc = 0; + cmd[len++] = **iptr; + } + if (**iptr == '\0') /* no terminating ) */ { + xfree(cmd); + return (NULL); + } + (*iptr)++; /* skip final ) */ + cmd[len] = '\0'; + + /* First try in the new tree. */ + so_find.cmd = cmd; + so = RB_FIND(status_out_tree, &c->status_new, &so_find); + if (so != NULL && so->out != NULL) + return (so->out); + + /* If not found at all, start the job and add to the tree. */ + if (so == NULL) { + job_run(cmd, status_job_callback, status_job_free, c); + c->references++; + + so = xmalloc(sizeof *so); + so->cmd = xstrdup(cmd); + so->out = NULL; + RB_INSERT(status_out_tree, &c->status_new, so); + } + + /* Lookup in the old tree. */ + so_find.cmd = cmd; + so = RB_FIND(status_out_tree, &c->status_old, &so_find); + xfree(cmd); + if (so != NULL) + return (so->out); + return (NULL); +} + +/* Free job tree. */ +void +status_free_jobs(struct status_out_tree *sotree) +{ + struct status_out *so, *so_next; + + so_next = RB_MIN(status_out_tree, sotree); + while (so_next != NULL) { + so = so_next; + so_next = RB_NEXT(status_out_tree, sotree, so); + + RB_REMOVE(status_out_tree, sotree, so); + if (so->out != NULL) + xfree(so->out); + xfree(so->cmd); + xfree(so); + } +} + +/* Update jobs on status interval. */ +void +status_update_jobs(struct client *c) +{ + /* Free the old tree. */ + status_free_jobs(&c->status_old); + + /* Move the new to old. */ + memcpy(&c->status_old, &c->status_new, sizeof c->status_old); + RB_INIT(&c->status_new); +} + +/* Free status job. */ +void +status_job_free(void *data) +{ + struct client *c = data; + + c->references--; +} + +/* Job has finished: save its result. */ +void +status_job_callback(struct job *job) +{ + struct client *c = job->data; + struct status_out *so, so_find; + char *line, *buf; + size_t len; + + if (c->flags & CLIENT_DEAD) + return; + + so_find.cmd = job->cmd; + so = RB_FIND(status_out_tree, &c->status_new, &so_find); + if (so == NULL || so->out != NULL) + return; + + buf = NULL; + if ((line = evbuffer_readline(job->event->input)) == NULL) { + len = EVBUFFER_LENGTH(job->event->input); + buf = xmalloc(len + 1); + if (len != 0) + memcpy(buf, EVBUFFER_DATA(job->event->input), len); + buf[len] = '\0'; + } else + buf = xstrdup(line); + + so->out = buf; + server_status_client(c); +} + +/* Return winlink status line entry and adjust gc as necessary. */ +char * +status_print( + struct client *c, struct winlink *wl, time_t t, struct grid_cell *gc) +{ + struct options *oo = &wl->window->options; + struct session *s = c->session; + const char *fmt; + char *text; + u_char fg, bg, attr; + + fg = options_get_number(oo, "window-status-fg"); + if (fg != 8) + colour_set_fg(gc, fg); + bg = options_get_number(oo, "window-status-bg"); + if (bg != 8) + colour_set_bg(gc, bg); + attr = options_get_number(oo, "window-status-attr"); + if (attr != 0) + gc->attr = attr; + fmt = options_get_string(oo, "window-status-format"); + if (wl == s->curw) { + fg = options_get_number(oo, "window-status-current-fg"); + if (fg != 8) + colour_set_fg(gc, fg); + bg = options_get_number(oo, "window-status-current-bg"); + if (bg != 8) + colour_set_bg(gc, bg); + attr = options_get_number(oo, "window-status-current-attr"); + if (attr != 0) + gc->attr = attr; + fmt = options_get_string(oo, "window-status-current-format"); + } + + if (wl->flags & WINLINK_ALERTFLAGS) { + fg = options_get_number(oo, "window-status-alert-fg"); + if (fg != 8) + colour_set_fg(gc, fg); + bg = options_get_number(oo, "window-status-alert-bg"); + if (bg != 8) + colour_set_bg(gc, bg); + attr = options_get_number(oo, "window-status-alert-attr"); + if (attr != 0) + gc->attr = attr; + } + + text = status_replace(c, NULL, wl, NULL, fmt, t, 1); + return (text); +} + +/* Set a status line message. */ +void printflike2 +status_message_set(struct client *c, const char *fmt, ...) +{ + struct timeval tv; + struct session *s = c->session; + struct message_entry *msg; + va_list ap; + int delay; + u_int i, limit; + + status_prompt_clear(c); + status_message_clear(c); + + va_start(ap, fmt); + xvasprintf(&c->message_string, fmt, ap); + va_end(ap); + + ARRAY_EXPAND(&c->message_log, 1); + msg = &ARRAY_LAST(&c->message_log); + msg->msg_time = time(NULL); + msg->msg = xstrdup(c->message_string); + + if (s == NULL) + limit = 0; + else + limit = options_get_number(&s->options, "message-limit"); + if (ARRAY_LENGTH(&c->message_log) > limit) { + limit = ARRAY_LENGTH(&c->message_log) - limit; + for (i = 0; i < limit; i++) { + msg = &ARRAY_FIRST(&c->message_log); + xfree(msg->msg); + ARRAY_REMOVE(&c->message_log, 0); + } + } + + delay = options_get_number(&c->session->options, "display-time"); + tv.tv_sec = delay / 1000; + tv.tv_usec = (delay % 1000) * 1000L; + + evtimer_del(&c->message_timer); + evtimer_set(&c->message_timer, status_message_callback, c); + evtimer_add(&c->message_timer, &tv); + + c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE); + c->flags |= CLIENT_STATUS; +} + +/* Clear status line message. */ +void +status_message_clear(struct client *c) +{ + if (c->message_string == NULL) + return; + + xfree(c->message_string); + c->message_string = NULL; + + c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE); + c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */ + + screen_reinit(&c->status); +} + +/* Clear status line message after timer expires. */ +/* ARGSUSED */ +void +status_message_callback(unused int fd, unused short event, void *data) +{ + struct client *c = data; + + status_message_clear(c); +} + +/* Draw client message on status line of present else on last line. */ +int +status_message_redraw(struct client *c) +{ + struct screen_write_ctx ctx; + struct session *s = c->session; + struct screen old_status; + size_t len; + struct grid_cell gc; + int utf8flag; + + if (c->tty.sx == 0 || c->tty.sy == 0) + return (0); + memcpy(&old_status, &c->status, sizeof old_status); + screen_init(&c->status, c->tty.sx, 1, 0); + + utf8flag = options_get_number(&s->options, "status-utf8"); + + len = screen_write_strlen(utf8flag, "%s", c->message_string); + if (len > c->tty.sx) + len = c->tty.sx; + + memcpy(&gc, &grid_default_cell, sizeof gc); + colour_set_fg(&gc, options_get_number(&s->options, "message-fg")); + colour_set_bg(&gc, options_get_number(&s->options, "message-bg")); + gc.attr |= options_get_number(&s->options, "message-attr"); + + screen_write_start(&ctx, NULL, &c->status); + + screen_write_cursormove(&ctx, 0, 0); + screen_write_nputs(&ctx, len, &gc, utf8flag, "%s", c->message_string); + for (; len < c->tty.sx; len++) + screen_write_putc(&ctx, &gc, ' '); + + screen_write_stop(&ctx); + + if (grid_compare(c->status.grid, old_status.grid) == 0) { + screen_free(&old_status); + return (0); + } + screen_free(&old_status); + return (1); +} + +/* Enable status line prompt. */ +void +status_prompt_set(struct client *c, const char *msg, const char *input, + int (*callbackfn)(void *, const char *), void (*freefn)(void *), + void *data, int flags) +{ + int keys; + + status_message_clear(c); + status_prompt_clear(c); + + c->prompt_string = status_replace(c, NULL, NULL, NULL, msg, + time(NULL), 0); + + if (input == NULL) + input = ""; + c->prompt_buffer = status_replace(c, NULL, NULL, NULL, input, + time(NULL), 0); + c->prompt_index = strlen(c->prompt_buffer); + + c->prompt_callbackfn = callbackfn; + c->prompt_freefn = freefn; + c->prompt_data = data; + + c->prompt_hindex = 0; + + c->prompt_flags = flags; + + keys = options_get_number(&c->session->options, "status-keys"); + if (keys == MODEKEY_EMACS) + mode_key_init(&c->prompt_mdata, &mode_key_tree_emacs_edit); + else + mode_key_init(&c->prompt_mdata, &mode_key_tree_vi_edit); + + c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE); + c->flags |= CLIENT_STATUS; +} + +/* Remove status line prompt. */ +void +status_prompt_clear(struct client *c) +{ + if (c->prompt_string == NULL) + return; + + if (c->prompt_freefn != NULL && c->prompt_data != NULL) + c->prompt_freefn(c->prompt_data); + + xfree(c->prompt_string); + c->prompt_string = NULL; + + xfree(c->prompt_buffer); + c->prompt_buffer = NULL; + + c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE); + c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */ + + screen_reinit(&c->status); +} + +/* Update status line prompt with a new prompt string. */ +void +status_prompt_update(struct client *c, const char *msg, const char *input) +{ + xfree(c->prompt_string); + c->prompt_string = status_replace(c, NULL, NULL, NULL, msg, + time(NULL), 0); + + xfree(c->prompt_buffer); + if (input == NULL) + input = ""; + c->prompt_buffer = status_replace(c, NULL, NULL, NULL, input, + time(NULL), 0); + c->prompt_index = strlen(c->prompt_buffer); + + c->prompt_hindex = 0; + + c->flags |= CLIENT_STATUS; +} + +/* Draw client prompt on status line of present else on last line. */ +int +status_prompt_redraw(struct client *c) +{ + struct screen_write_ctx ctx; + struct session *s = c->session; + struct screen old_status; + size_t i, size, left, len, off; + struct grid_cell gc, *gcp; + int utf8flag; + + if (c->tty.sx == 0 || c->tty.sy == 0) + return (0); + memcpy(&old_status, &c->status, sizeof old_status); + screen_init(&c->status, c->tty.sx, 1, 0); + + utf8flag = options_get_number(&s->options, "status-utf8"); + + len = screen_write_strlen(utf8flag, "%s", c->prompt_string); + if (len > c->tty.sx) + len = c->tty.sx; + off = 0; + + memcpy(&gc, &grid_default_cell, sizeof gc); + colour_set_fg(&gc, options_get_number(&s->options, "message-fg")); + colour_set_bg(&gc, options_get_number(&s->options, "message-bg")); + gc.attr |= options_get_number(&s->options, "message-attr"); + + screen_write_start(&ctx, NULL, &c->status); + + screen_write_cursormove(&ctx, 0, 0); + screen_write_nputs(&ctx, len, &gc, utf8flag, "%s", c->prompt_string); + + left = c->tty.sx - len; + if (left != 0) { + size = screen_write_strlen(utf8flag, "%s", c->prompt_buffer); + if (c->prompt_index >= left) { + off = c->prompt_index - left + 1; + if (c->prompt_index == size) + left--; + size = left; + } + screen_write_nputs( + &ctx, left, &gc, utf8flag, "%s", c->prompt_buffer + off); + + for (i = len + size; i < c->tty.sx; i++) + screen_write_putc(&ctx, &gc, ' '); + } + + screen_write_stop(&ctx); + + /* Apply fake cursor. */ + off = len + c->prompt_index - off; + gcp = grid_view_get_cell(c->status.grid, off, 0); + gcp->attr ^= GRID_ATTR_REVERSE; + + if (grid_compare(c->status.grid, old_status.grid) == 0) { + screen_free(&old_status); + return (0); + } + screen_free(&old_status); + return (1); +} + +/* Handle keys in prompt. */ +void +status_prompt_key(struct client *c, int key) +{ + struct paste_buffer *pb; + char *s, *first, *last, word[64], swapc; + const char *histstr; + u_char ch; + size_t size, n, off, idx; + + size = strlen(c->prompt_buffer); + switch (mode_key_lookup(&c->prompt_mdata, key)) { + case MODEKEYEDIT_CURSORLEFT: + if (c->prompt_index > 0) { + c->prompt_index--; + c->flags |= CLIENT_STATUS; + } + break; + case MODEKEYEDIT_SWITCHMODEAPPEND: + case MODEKEYEDIT_CURSORRIGHT: + if (c->prompt_index < size) { + c->prompt_index++; + c->flags |= CLIENT_STATUS; + } + break; + case MODEKEYEDIT_STARTOFLINE: + if (c->prompt_index != 0) { + c->prompt_index = 0; + c->flags |= CLIENT_STATUS; + } + break; + case MODEKEYEDIT_ENDOFLINE: + if (c->prompt_index != size) { + c->prompt_index = size; + c->flags |= CLIENT_STATUS; + } + break; + case MODEKEYEDIT_COMPLETE: + if (*c->prompt_buffer == '\0') + break; + + idx = c->prompt_index; + if (idx != 0) + idx--; + + /* Find the word we are in. */ + first = c->prompt_buffer + idx; + while (first > c->prompt_buffer && *first != ' ') + first--; + while (*first == ' ') + first++; + last = c->prompt_buffer + idx; + while (*last != '\0' && *last != ' ') + last++; + while (*last == ' ') + last--; + if (*last != '\0') + last++; + if (last <= first || + ((size_t) (last - first)) > (sizeof word) - 1) + break; + memcpy(word, first, last - first); + word[last - first] = '\0'; + + /* And try to complete it. */ + if ((s = status_prompt_complete(word)) == NULL) + break; + + /* Trim out word. */ + n = size - (last - c->prompt_buffer) + 1; /* with \0 */ + memmove(first, last, n); + size -= last - first; + + /* Insert the new word. */ + size += strlen(s); + off = first - c->prompt_buffer; + c->prompt_buffer = xrealloc(c->prompt_buffer, 1, size + 1); + first = c->prompt_buffer + off; + memmove(first + strlen(s), first, n); + memcpy(first, s, strlen(s)); + + c->prompt_index = (first - c->prompt_buffer) + strlen(s); + xfree(s); + + c->flags |= CLIENT_STATUS; + break; + case MODEKEYEDIT_BACKSPACE: + if (c->prompt_index != 0) { + if (c->prompt_index == size) + c->prompt_buffer[--c->prompt_index] = '\0'; + else { + memmove(c->prompt_buffer + c->prompt_index - 1, + c->prompt_buffer + c->prompt_index, + size + 1 - c->prompt_index); + c->prompt_index--; + } + c->flags |= CLIENT_STATUS; + } + break; + case MODEKEYEDIT_DELETE: + if (c->prompt_index != size) { + memmove(c->prompt_buffer + c->prompt_index, + c->prompt_buffer + c->prompt_index + 1, + size + 1 - c->prompt_index); + c->flags |= CLIENT_STATUS; + } + break; + case MODEKEYEDIT_DELETELINE: + *c->prompt_buffer = '\0'; + c->prompt_index = 0; + c->flags |= CLIENT_STATUS; + break; + case MODEKEYEDIT_DELETETOENDOFLINE: + if (c->prompt_index < size) { + c->prompt_buffer[c->prompt_index] = '\0'; + c->flags |= CLIENT_STATUS; + } + break; + case MODEKEYEDIT_HISTORYUP: + histstr = status_prompt_up_history(&c->prompt_hindex); + if (histstr == NULL) + break; + xfree(c->prompt_buffer); + c->prompt_buffer = xstrdup(histstr); + c->prompt_index = strlen(c->prompt_buffer); + c->flags |= CLIENT_STATUS; + break; + case MODEKEYEDIT_HISTORYDOWN: + histstr = status_prompt_down_history(&c->prompt_hindex); + if (histstr == NULL) + break; + xfree(c->prompt_buffer); + c->prompt_buffer = xstrdup(histstr); + c->prompt_index = strlen(c->prompt_buffer); + c->flags |= CLIENT_STATUS; + break; + case MODEKEYEDIT_PASTE: + if ((pb = paste_get_top(&global_buffers)) == NULL) + break; + for (n = 0; n < pb->size; n++) { + ch = (u_char) pb->data[n]; + if (ch < 32 || ch == 127) + break; + } + + c->prompt_buffer = xrealloc(c->prompt_buffer, 1, size + n + 1); + if (c->prompt_index == size) { + memcpy(c->prompt_buffer + c->prompt_index, pb->data, n); + c->prompt_index += n; + c->prompt_buffer[c->prompt_index] = '\0'; + } else { + memmove(c->prompt_buffer + c->prompt_index + n, + c->prompt_buffer + c->prompt_index, + size + 1 - c->prompt_index); + memcpy(c->prompt_buffer + c->prompt_index, pb->data, n); + c->prompt_index += n; + } + + c->flags |= CLIENT_STATUS; + break; + case MODEKEYEDIT_TRANSPOSECHARS: + idx = c->prompt_index; + if (idx < size) + idx++; + if (idx >= 2) { + swapc = c->prompt_buffer[idx - 2]; + c->prompt_buffer[idx - 2] = c->prompt_buffer[idx - 1]; + c->prompt_buffer[idx - 1] = swapc; + c->prompt_index = idx; + c->flags |= CLIENT_STATUS; + } + break; + case MODEKEYEDIT_ENTER: + if (*c->prompt_buffer != '\0') + status_prompt_add_history(c->prompt_buffer); + if (c->prompt_callbackfn(c->prompt_data, c->prompt_buffer) == 0) + status_prompt_clear(c); + break; + case MODEKEYEDIT_CANCEL: + if (c->prompt_callbackfn(c->prompt_data, NULL) == 0) + status_prompt_clear(c); + break; + case MODEKEY_OTHER: + if ((key & 0xff00) != 0 || key < 32 || key == 127) + break; + c->prompt_buffer = xrealloc(c->prompt_buffer, 1, size + 2); + + if (c->prompt_index == size) { + c->prompt_buffer[c->prompt_index++] = key; + c->prompt_buffer[c->prompt_index] = '\0'; + } else { + memmove(c->prompt_buffer + c->prompt_index + 1, + c->prompt_buffer + c->prompt_index, + size + 1 - c->prompt_index); + c->prompt_buffer[c->prompt_index++] = key; + } + + if (c->prompt_flags & PROMPT_SINGLE) { + if (c->prompt_callbackfn( + c->prompt_data, c->prompt_buffer) == 0) + status_prompt_clear(c); + } + + c->flags |= CLIENT_STATUS; + break; + default: + break; + } +} + +/* Get previous line from the history. */ +const char * +status_prompt_up_history(u_int *idx) +{ + u_int size; + + /* + * History runs from 0 to size - 1. + * + * Index is from 0 to size. Zero is empty. + */ + + size = ARRAY_LENGTH(&status_prompt_history); + if (size == 0 || *idx == size) + return (NULL); + (*idx)++; + return (ARRAY_ITEM(&status_prompt_history, size - *idx)); +} + +/* Get next line from the history. */ +const char * +status_prompt_down_history(u_int *idx) +{ + u_int size; + + size = ARRAY_LENGTH(&status_prompt_history); + if (size == 0 || *idx == 0) + return (""); + (*idx)--; + if (*idx == 0) + return (""); + return (ARRAY_ITEM(&status_prompt_history, size - *idx)); +} + +/* Add line to the history. */ +void +status_prompt_add_history(const char *line) +{ + u_int size; + + size = ARRAY_LENGTH(&status_prompt_history); + if (size > 0 && strcmp(ARRAY_LAST(&status_prompt_history), line) == 0) + return; + + if (size == PROMPT_HISTORY) { + xfree(ARRAY_FIRST(&status_prompt_history)); + ARRAY_REMOVE(&status_prompt_history, 0); + } + + ARRAY_ADD(&status_prompt_history, xstrdup(line)); +} + +/* Complete word. */ +char * +status_prompt_complete(const char *s) +{ + const struct cmd_entry **cmdent; + const struct options_table_entry *oe; + ARRAY_DECL(, const char *) list; + char *prefix, *s2; + u_int i; + size_t j; + + if (*s == '\0') + return (NULL); + + /* First, build a list of all the possible matches. */ + ARRAY_INIT(&list); + for (cmdent = cmd_table; *cmdent != NULL; cmdent++) { + if (strncmp((*cmdent)->name, s, strlen(s)) == 0) + ARRAY_ADD(&list, (*cmdent)->name); + } + for (oe = server_options_table; oe->name != NULL; oe++) { + if (strncmp(oe->name, s, strlen(s)) == 0) + ARRAY_ADD(&list, oe->name); + } + for (oe = session_options_table; oe->name != NULL; oe++) { + if (strncmp(oe->name, s, strlen(s)) == 0) + ARRAY_ADD(&list, oe->name); + } + for (oe = window_options_table; oe->name != NULL; oe++) { + if (strncmp(oe->name, s, strlen(s)) == 0) + ARRAY_ADD(&list, oe->name); + } + + /* If none, bail now. */ + if (ARRAY_LENGTH(&list) == 0) { + ARRAY_FREE(&list); + return (NULL); + } + + /* If an exact match, return it, with a trailing space. */ + if (ARRAY_LENGTH(&list) == 1) { + xasprintf(&s2, "%s ", ARRAY_FIRST(&list)); + ARRAY_FREE(&list); + return (s2); + } + + /* Now loop through the list and find the longest common prefix. */ + prefix = xstrdup(ARRAY_FIRST(&list)); + for (i = 1; i < ARRAY_LENGTH(&list); i++) { + s = ARRAY_ITEM(&list, i); + + j = strlen(s); + if (j > strlen(prefix)) + j = strlen(prefix); + for (; j > 0; j--) { + if (prefix[j - 1] != s[j - 1]) + prefix[j - 1] = '\0'; + } + } + + ARRAY_FREE(&list); + return (prefix); +} diff --git a/external/bsd/tmux/dist/tmux.1 b/external/bsd/tmux/dist/tmux.1 new file mode 100644 index 000000000..22528b3ff --- /dev/null +++ b/external/bsd/tmux/dist/tmux.1 @@ -0,0 +1,3011 @@ +.\" $Id: tmux.1,v 1.2 2013/07/20 21:40:04 wiz Exp $ +.\" +.\" Copyright (c) 2007 Nicholas Marriott +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER +.\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING +.\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: July 8 2011 $ +.Dt TMUX 1 +.Os +.Sh NAME +.Nm tmux +.Nd terminal multiplexer +.Sh SYNOPSIS +.Nm tmux +.Bk -words +.Op Fl 28lquvV +.Op Fl c Ar shell-command +.Op Fl f Ar file +.Op Fl L Ar socket-name +.Op Fl S Ar socket-path +.Op Ar command Op Ar flags +.Ek +.Sh DESCRIPTION +.Nm +is a terminal multiplexer: +it enables a number of terminals to be created, accessed, and +controlled from a single screen. +.Nm +may be detached from a screen +and continue running in the background, +then later reattached. +.Pp +When +.Nm +is started it creates a new +.Em session +with a single +.Em window +and displays it on screen. +A status line at the bottom of the screen +shows information on the current session +and is used to enter interactive commands. +.Pp +A session is a single collection of +.Em pseudo terminals +under the management of +.Nm . +Each session has one or more +windows linked to it. +A window occupies the entire screen +and may be split into rectangular panes, +each of which is a separate pseudo terminal +(the +.Xr pty 4 +manual page documents the technical details of pseudo terminals). +Any number of +.Nm +instances may connect to the same session, +and any number of windows may be present in the same session. +Once all sessions are killed, +.Nm +exits. +.Pp +Each session is persistent and will survive accidental disconnection +(such as +.Xr ssh 1 +connection timeout) or intentional detaching (with the +.Ql C-b d +key strokes). +.Nm +may be reattached using: +.Pp +.Dl $ tmux attach +.Pp +In +.Nm , +a session is displayed on screen by a +.Em client +and all sessions are managed by a single +.Em server . +The server and each client are separate processes which communicate through a +socket in +.Pa /tmp . +.Pp +The options are as follows: +.Bl -tag -width "XXXXXXXXXXXX" +.It Fl 2 +Force +.Nm +to assume the terminal supports 256 colours. +.It Fl 8 +Like +.Fl 2 , +but indicates that the terminal supports 88 colours. +.It Fl c Ar shell-command +Execute +.Ar shell-command +using the default shell. +If necessary, the +.Nm +server will be started to retrieve the +.Ic default-shell +option. +This option is for compatibility with +.Xr sh 1 +when +.Nm +is used as a login shell. +.It Fl f Ar file +Specify an alternative configuration file. +By default, +.Nm +loads the system configuration file from +.Pa /etc/tmux.conf , +if present, then looks for a user configuration file at +.Pa ~/.tmux.conf . +The configuration file is a set of +.Nm +commands which are executed in sequence when the server is first started. +.Pp +If a command in the configuration file fails, +.Nm +will report an error and exit without executing further commands. +.It Fl L Ar socket-name +.Nm +stores the server socket in a directory under +.Pa /tmp +(or +.Ev TMPDIR +if set); +the default socket is named +.Em default . +This option allows a different socket name to be specified, allowing several +independent +.Nm +servers to be run. +Unlike +.Fl S +a full path is not necessary: the sockets are all created in the same +directory. +.Pp +If the socket is accidentally removed, the +.Dv SIGUSR1 +signal may be sent to the +.Nm +server process to recreate it. +.It Fl l +Behave as a login shell. +This flag currently has no effect and is for compatibility with other shells +when using tmux as a login shell. +.It Fl q +Set the +.Ic quiet +server option to prevent the server sending various informational messages. +.It Fl S Ar socket-path +Specify a full alternative path to the server socket. +If +.Fl S +is specified, the default socket directory is not used and any +.Fl L +flag is ignored. +.It Fl u +.Nm +attempts to guess if the terminal is likely to support UTF-8 by checking the +first of the +.Ev LC_ALL , +.Ev LC_CTYPE +and +.Ev LANG +environment variables to be set for the string "UTF-8". +This is not always correct: the +.Fl u +flag explicitly informs +.Nm +that UTF-8 is supported. +.Pp +If the server is started from a client passed +.Fl u +or where UTF-8 is detected, the +.Ic utf8 +and +.Ic status-utf8 +options are enabled in the global window and session options respectively. +.It Fl v +Request verbose logging. +This option may be specified multiple times for increasing verbosity. +Log messages will be saved into +.Pa tmux-client-PID.log +and +.Pa tmux-server-PID.log +files in the current directory, where +.Em PID +is the PID of the server or client process. +.It Fl V +Report the +.Nm +version. +.It Ar command Op Ar flags +This specifies one of a set of commands used to control +.Nm , +as described in the following sections. +If no commands are specified, the +.Ic new-session +command is assumed. +.El +.Sh KEY BINDINGS +.Nm +may be controlled from an attached client by using a key combination of a +prefix key, +.Ql C-b +(Ctrl-b) by default, followed by a command key. +.Pp +The default command key bindings are: +.Pp +.Bl -tag -width "XXXXXXXXXX" -offset indent -compact +.It C-b +Send the prefix key (C-b) through to the application. +.It C-o +Rotate the panes in the current window forwards. +.It C-z +Suspend the +.Nm +client. +.It ! +Break the current pane out of the window. +.It \&" +Split the current pane into two, top and bottom. +.It # +List all paste buffers. +.It $ +Rename the current session. +.It % +Split the current pane into two, left and right. +.It & +Kill the current window. +.It ' +Prompt for a window index to select. +.It , +Rename the current window. +.It - +Delete the most recently copied buffer of text. +.It . +Prompt for an index to move the current window. +.It 0 to 9 +Select windows 0 to 9. +.It : +Enter the +.Nm +command prompt. +.It ; +Move to the previously active pane. +.It = +Choose which buffer to paste interactively from a list. +.It \&? +List all key bindings. +.It D +Choose a client to detach. +.It \&[ +Enter copy mode to copy text or view the history. +.It \&] +Paste the most recently copied buffer of text. +.It c +Create a new window. +.It d +Detach the current client. +.It f +Prompt to search for text in open windows. +.It i +Display some information about the current window. +.It l +Move to the previously selected window. +.It n +Change to the next window. +.It o +Select the next pane in the current window. +.It p +Change to the previous window. +.It q +Briefly display pane indexes. +.It r +Force redraw of the attached client. +.It s +Select a new session for the attached client interactively. +.It L +Switch the attached client back to the last session. +.It t +Show the time. +.It w +Choose the current window interactively. +.It x +Kill the current pane. +.It { +Swap the current pane with the previous pane. +.It } +Swap the current pane with the next pane. +.It ~ +Show previous messages from +.Nm , +if any. +.It Page Up +Enter copy mode and scroll one page up. +.It Up, Down +.It Left, Right +Change to the pane above, below, to the left, or to the right of the current +pane. +.It M-1 to M-5 +Arrange panes in one of the five preset layouts: even-horizontal, +even-vertical, main-horizontal, main-vertical, or tiled. +.It M-n +Move to the next window with a bell or activity marker. +.It M-o +Rotate the panes in the current window backwards. +.It M-p +Move to the previous window with a bell or activity marker. +.It C-Up, C-Down +.It C-Left, C-Right +Resize the current pane in steps of one cell. +.It M-Up, M-Down +.It M-Left, M-Right +Resize the current pane in steps of five cells. +.El +.Pp +Key bindings may be changed with the +.Ic bind-key +and +.Ic unbind-key +commands. +.Sh COMMANDS +This section contains a list of the commands supported by +.Nm . +Most commands accept the optional +.Fl t +argument with one of +.Ar target-client , +.Ar target-session +.Ar target-window , +or +.Ar target-pane . +These specify the client, session, window or pane which a command should affect. +.Ar target-client +is the name of the +.Xr pty 4 +file to which the client is connected, for example either of +.Pa /dev/ttyp1 +or +.Pa ttyp1 +for the client attached to +.Pa /dev/ttyp1 . +If no client is specified, the current client is chosen, if possible, or an +error is reported. +Clients may be listed with the +.Ic list-clients +command. +.Pp +.Ar target-session +is either the name of a session (as listed by the +.Ic list-sessions +command) or the name of a client with the same syntax as +.Ar target-client , +in which case the session attached to the client is used. +When looking for the session name, +.Nm +initially searches for an exact match; if none is found, the session names +are checked for any for which +.Ar target-session +is a prefix or for which it matches as an +.Xr fnmatch 3 +pattern. +If a single match is found, it is used as the target session; multiple matches +produce an error. +If a session is omitted, the current session is used if available; if no +current session is available, the most recently used is chosen. +.Pp +.Ar target-window +specifies a window in the form +.Em session Ns \&: Ns Em window . +.Em session +follows the same rules as for +.Ar target-session , +and +.Em window +is looked for in order: as a window index, for example mysession:1; as an exact +window name, such as mysession:mywindow; then as an +.Xr fnmatch 3 +pattern or the start of a window name, such as mysession:mywin* or +mysession:mywin. +An empty window name specifies the next unused index if appropriate (for +example the +.Ic new-window +and +.Ic link-window +commands) +otherwise the current window in +.Em session +is chosen. +The special character +.Ql \&! +uses the last (previously current) window, or +.Ql + +and +.Ql - +are the next window or the previous window by number. +When the argument does not contain a colon, +.Nm +first attempts to parse it as window; if that fails, an attempt is made to +match a session. +.Pp +.Ar target-pane +takes a similar form to +.Ar target-window +but with the optional addition of a period followed by a pane index, for +example: mysession:mywindow.1. +If the pane index is omitted, the currently active pane in the specified +window is used. +If neither a colon nor period appears, +.Nm +first attempts to use the argument as a pane index; if that fails, it is looked +up as for +.Ar target-window . +A +.Ql + +or +.Ql - +indicate the next or previous pane index, respectively. +One of the strings +.Em top , +.Em bottom , +.Em left , +.Em right , +.Em top-left , +.Em top-right , +.Em bottom-left +or +.Em bottom-right +may be used instead of a pane index. +.Pp +The special characters +.Ql + +and +.Ql - +may be followed by an offset, for example: +.Bd -literal -offset indent +select-window -t:+2 +.Ed +.Pp +When dealing with a session that doesn't contain sequential window indexes, +they will be correctly skipped. +.Pp +.Nm +also gives each pane created in a server an identifier consisting of a +.Ql % +and a number, starting from zero. +A pane's identifier is unique for the life of the +.Nm +server and is passed to the child process of the pane in the +.Ev TMUX_PANE +environment variable. +It may be used alone to target a pane or the window containing it. +.Pp +.Ar shell-command +arguments are +.Xr sh 1 +commands. +These must be passed as a single item, which typically means quoting them, for +example: +.Bd -literal -offset indent +new-window 'vi /etc/passwd' +.Ed +.Pp +.Ar command +.Op Ar arguments +refers to a +.Nm +command, passed with the command and arguments separately, for example: +.Bd -literal -offset indent +bind-key F1 set-window-option force-width 81 +.Ed +.Pp +Or if using +.Xr sh 1 : +.Bd -literal -offset indent +$ tmux bind-key F1 set-window-option force-width 81 +.Ed +.Pp +Multiple commands may be specified together as part of a +.Em command sequence . +Each command should be separated by spaces and a semicolon; +commands are executed sequentially from left to right. +A literal semicolon may be included by escaping it with a backslash (for +example, when specifying a command sequence to +.Ic bind-key ) . +.Pp +Example +.Nm +commands include: +.Bd -literal -offset indent +refresh-client -t/dev/ttyp2 + +rename-session -tfirst newname + +set-window-option -t:0 monitor-activity on + +new-window ; split-window -d +.Ed +.Pp +Or from +.Xr sh 1 : +.Bd -literal -offset indent +$ tmux kill-window -t :1 + +$ tmux new-window \e; split-window -d + +$ tmux new-session -d 'vi /etc/passwd' \e; split-window -d \e; attach +.Ed +.Sh CLIENTS AND SESSIONS +The +.Nm +server manages clients, sessions, windows and panes. +Clients are attached to sessions to interact with them, either +when they are created with the +.Ic new-session +command, or later with the +.Ic attach-session +command. +Each session has one or more windows +.Em linked +into it. +Windows may be linked to multiple sessions and are made up of one or +more panes, +each of which contains a pseudo terminal. +Commands for creating, linking and otherwise manipulating windows +are covered +in the +.Sx WINDOWS AND PANES +section. +.Pp +The following commands are available to manage clients and sessions: +.Bl -tag -width Ds +.It Xo Ic attach-session +.Op Fl dr +.Op Fl t Ar target-session +.Xc +.D1 (alias: Ic attach ) +If run from outside +.Nm , +create a new client in the current terminal and attach it to +.Ar target-session . +If used from inside, switch the current client. +If +.Fl d +is specified, any other clients attached to the session are detached. +.Fl r +signifies the client is read-only (only keys bound to the +.Ic detach-client +command have any effect) +.Pp +If no server is started, +.Ic attach-session +will attempt to start it; this will fail unless sessions are created in the +configuration file. +.Pp +The +.Ar target-session +rules for +.Ic attach-session +are slightly adjusted: if +.Nm +needs to select the most recently used session, it will prefer the most +recently used +.Em unattached +session. +.It Xo Ic detach-client +.Op Fl P +.Op Fl s Ar target-session +.Op Fl t Ar target-client +.Xc +.D1 (alias: Ic detach ) +Detach the current client if bound to a key, the client specified with +.Fl t , +or all clients currently attached to to the session specified by +.Fl s . +If +.Fl P +is given, send SIGHUP to the parent process of the client, typically causing it +to exit. +.It Ic has-session Op Fl t Ar target-session +.D1 (alias: Ic has ) +Report an error and exit with 1 if the specified session does not exist. +If it does exist, exit with 0. +.It Ic kill-server +Kill the +.Nm +server and clients and destroy all sessions. +.It Ic kill-session Op Fl t Ar target-session +Destroy the given session, closing any windows linked to it and no other +sessions, and detaching all clients attached to it. +.It Ic list-clients Op Fl t Ar target-session +.D1 (alias: Ic lsc ) +List all clients attached to the server. +If +.Ar target-session +is specified, list only clients connected to that session. +.It Ic list-commands +.D1 (alias: Ic lscm ) +List the syntax of all commands supported by +.Nm . +.It Ic list-sessions +.D1 (alias: Ic ls ) +List all sessions managed by the server. +.It Ic lock-client Op Fl t Ar target-client +.D1 (alias: Ic lockc ) +Lock +.Ar target-client , +see the +.Ic lock-server +command. +.It Ic lock-session Op Fl t Ar target-session +.D1 (alias: Ic locks ) +Lock all clients attached to +.Ar target-session . +.It Xo Ic new-session +.Op Fl d +.Op Fl n Ar window-name +.Op Fl s Ar session-name +.Op Fl t Ar target-session +.Op Fl x Ar width +.Op Fl y Ar height +.Op Ar shell-command +.Xc +.D1 (alias: Ic new ) +Create a new session with name +.Ar session-name . +.Pp +The new session is attached to the current terminal unless +.Fl d +is given. +.Ar window-name +and +.Ar shell-command +are the name of and shell command to execute in the initial window. +If +.Fl d +is used, +.Fl x +and +.Fl y +specify the size of the initial window (80 by 24 if not given). +.Pp +If run from a terminal, any +.Xr termios 4 +special characters are saved and used for new windows in the new session. +.Pp +If +.Fl t +is given, the new session is +.Em grouped +with +.Ar target-session . +This means they share the same set of windows - all windows from +.Ar target-session +are linked to the new session and any subsequent new windows or windows being +closed are applied to both sessions. +The current and previous window and any session options remain independent and +either session may be killed without affecting the other. +Giving +.Fl n +or +.Ar shell-command +are invalid if +.Fl t +is used. +.It Ic refresh-client Op Fl t Ar target-client +.D1 (alias: Ic refresh ) +Refresh the current client if bound to a key, or a single client if one is given +with +.Fl t . +.It Xo Ic rename-session +.Op Fl t Ar target-session +.Ar new-name +.Xc +.D1 (alias: Ic rename ) +Rename the session to +.Ar new-name . +.It Xo Ic show-messages +.Op Fl t Ar target-client +.Xc +.D1 (alias: Ic showmsgs ) +Any messages displayed on the status line are saved in a per-client message +log, up to a maximum of the limit set by the +.Ar message-limit +session option for the session attached to that client. +This command displays the log for +.Ar target-client . +.It Ic source-file Ar path +.D1 (alias: Ic source ) +Execute commands from +.Ar path . +.It Ic start-server +.D1 (alias: Ic start ) +Start the +.Nm +server, if not already running, without creating any sessions. +.It Xo Ic suspend-client +.Op Fl t Ar target-client +.Xc +.D1 (alias: Ic suspendc ) +Suspend a client by sending +.Dv SIGTSTP +(tty stop). +.It Xo Ic switch-client +.Op Fl lnp +.Op Fl c Ar target-client +.Op Fl t Ar target-session +.Xc +.D1 (alias: Ic switchc ) +Switch the current session for client +.Ar target-client +to +.Ar target-session . +If +.Fl l , +.Fl n +or +.Fl p +is used, the client is moved to the last, next or previous session +respectively. +.El +.Sh WINDOWS AND PANES +A +.Nm +window may be in one of several modes. +The default permits direct access to the terminal attached to the window. +The other is copy mode, which permits a section of a window or its +history to be copied to a +.Em paste buffer +for later insertion into another window. +This mode is entered with the +.Ic copy-mode +command, bound to +.Ql \&[ +by default. +It is also entered when a command that produces output, such as +.Ic list-keys , +is executed from a key binding. +.Pp +The keys available depend on whether emacs or vi mode is selected +(see the +.Ic mode-keys +option). +The following keys are supported as appropriate for the mode: +.Bl -column "FunctionXXXXXXXXXXXXXXXXX" "viXXXXXXXXXX" "emacs" -offset indent +.It Sy "Function" Ta Sy "vi" Ta Sy "emacs" +.It Li "Back to indentation" Ta "^" Ta "M-m" +.It Li "Bottom of history" Ta "G" Ta "M-<" +.It Li "Clear selection" Ta "Escape" Ta "C-g" +.It Li "Copy selection" Ta "Enter" Ta "M-w" +.It Li "Cursor down" Ta "j" Ta "Down" +.It Li "Cursor left" Ta "h" Ta "Left" +.It Li "Cursor right" Ta "l" Ta "Right" +.It Li "Cursor to bottom line" Ta "L" Ta "" +.It Li "Cursor to middle line" Ta "M" Ta "M-r" +.It Li "Cursor to top line" Ta "H" Ta "M-R" +.It Li "Cursor up" Ta "k" Ta "Up" +.It Li "Delete entire line" Ta "d" Ta "C-u" +.It Li "Delete/Copy to end of line" Ta "D" Ta "C-k" +.It Li "End of line" Ta "$" Ta "C-e" +.It Li "Go to line" Ta ":" Ta "g" +.It Li "Half page down" Ta "C-d" Ta "M-Down" +.It Li "Half page up" Ta "C-u" Ta "M-Up" +.It Li "Jump forward" Ta "f" Ta "f" +.It Li "Jump backward" Ta "F" Ta "F" +.It Li "Jump again" Ta ";" Ta ";" +.It Li "Jump again in reverse" Ta "," Ta "," +.It Li "Next page" Ta "C-f" Ta "Page down" +.It Li "Next space" Ta "W" Ta "" +.It Li "Next space, end of word" Ta "E" Ta "" +.It Li "Next word" Ta "w" Ta "" +.It Li "Next word end" Ta "e" Ta "M-f" +.It Li "Paste buffer" Ta "p" Ta "C-y" +.It Li "Previous page" Ta "C-b" Ta "Page up" +.It Li "Previous word" Ta "b" Ta "M-b" +.It Li "Previous space" Ta "B" Ta "" +.It Li "Quit mode" Ta "q" Ta "Escape" +.It Li "Rectangle toggle" Ta "v" Ta "R" +.It Li "Scroll down" Ta "C-Down or C-e" Ta "C-Down" +.It Li "Scroll up" Ta "C-Up or C-y" Ta "C-Up" +.It Li "Search again" Ta "n" Ta "n" +.It Li "Search again in reverse" Ta "N" Ta "N" +.It Li "Search backward" Ta "?" Ta "C-r" +.It Li "Search forward" Ta "/" Ta "C-s" +.It Li "Start of line" Ta "0" Ta "C-a" +.It Li "Start selection" Ta "Space" Ta "C-Space" +.It Li "Top of history" Ta "g" Ta "M->" +.It Li "Transpose chars" Ta "" Ta "C-t" +.El +.Pp +The next and previous word keys use space and the +.Ql - , +.Ql _ +and +.Ql @ +characters as word delimiters by default, but this can be adjusted by +setting the +.Em word-separators +window option. +Next word moves to the start of the next word, next word end to the end of the +next word and previous word to the start of the previous word. +The three next and previous space keys work similarly but use a space alone as +the word separator. +.Pp +The jump commands enable quick movement within a line. +For instance, typing +.Ql f +followed by +.Ql / +will move the cursor to the next +.Ql / +character on the current line. +A +.Ql \&; +will then jump to the next occurrence. +.Pp +Commands in copy mode may be prefaced by an optional repeat count. +With vi key bindings, a prefix is entered using the number keys; with +emacs, the Alt (meta) key and a number begins prefix entry. +For example, to move the cursor forward by ten words, use +.Ql M-1 0 M-f +in emacs mode, and +.Ql 10w +in vi. +.Pp +Mode key bindings are defined in a set of named tables: +.Em vi-edit +and +.Em emacs-edit +for keys used when line editing at the command prompt; +.Em vi-choice +and +.Em emacs-choice +for keys used when choosing from lists (such as produced by the +.Ic choose-window +command); and +.Em vi-copy +and +.Em emacs-copy +used in copy mode. +The tables may be viewed with the +.Ic list-keys +command and keys modified or removed with +.Ic bind-key +and +.Ic unbind-key . +.Pp +The paste buffer key pastes the first line from the top paste buffer on the +stack. +.Pp +The synopsis for the +.Ic copy-mode +command is: +.Bl -tag -width Ds +.It Xo Ic copy-mode +.Op Fl u +.Op Fl t Ar target-pane +.Xc +Enter copy mode. +The +.Fl u +option scrolls one page up. +.El +.Pp +Each window displayed by +.Nm +may be split into one or more +.Em panes ; +each pane takes up a certain area of the display and is a separate terminal. +A window may be split into panes using the +.Ic split-window +command. +Windows may be split horizontally (with the +.Fl h +flag) or vertically. +Panes may be resized with the +.Ic resize-pane +command (bound to +.Ql C-up , +.Ql C-down +.Ql C-left +and +.Ql C-right +by default), the current pane may be changed with the +.Ic select-pane +command and the +.Ic rotate-window +and +.Ic swap-pane +commands may be used to swap panes without changing their position. +Panes are numbered beginning from zero in the order they are created. +.Pp +A number of preset +.Em layouts +are available. +These may be selected with the +.Ic select-layout +command or cycled with +.Ic next-layout +(bound to +.Ql Space +by default); once a layout is chosen, panes within it may be moved and resized +as normal. +.Pp +The following layouts are supported: +.Bl -tag -width Ds +.It Ic even-horizontal +Panes are spread out evenly from left to right across the window. +.It Ic even-vertical +Panes are spread evenly from top to bottom. +.It Ic main-horizontal +A large (main) pane is shown at the top of the window and the remaining panes +are spread from left to right in the leftover space at the bottom. +Use the +.Em main-pane-height +window option to specify the height of the top pane. +.It Ic main-vertical +Similar to +.Ic main-horizontal +but the large pane is placed on the left and the others spread from top to +bottom along the right. +See the +.Em main-pane-width +window option. +.It Ic tiled +Panes are spread out as evenly as possible over the window in both rows and +columns. +.El +.Pp +In addition, +.Ic select-layout +may be used to apply a previously used layout - the +.Ic list-windows +command displays the layout of each window in a form suitable for use with +.Ic select-layout . +For example: +.Bd -literal -offset indent +$ tmux list-windows +0: ksh [159x48] + layout: bb62,159x48,0,0{79x48,0,0,79x48,80,0} +$ tmux select-layout bb62,159x48,0,0{79x48,0,0,79x48,80,0} +.Ed +.Pp +.Nm +automatically adjusts the size of the layout for the current window size. +Note that a layout cannot be applied to a window with more panes than that +from which the layout was originally defined. +.Pp +Commands related to windows and panes are as follows: +.Bl -tag -width Ds +.It Xo Ic break-pane +.Op Fl d +.Op Fl t Ar target-pane +.Xc +.D1 (alias: Ic breakp ) +Break +.Ar target-pane +off from its containing window to make it the only pane in a new window. +If +.Fl d +is given, the new window does not become the current window. +.It Xo Ic capture-pane +.Op Fl b Ar buffer-index +.Op Fl E Ar end-line +.Op Fl S Ar start-line +.Op Fl t Ar target-pane +.Xc +.D1 (alias: Ic capturep ) +Capture the contents of a pane to the specified buffer, or a new buffer if none +is specified. +.Pp +.Fl S +and +.Fl E +specify the starting and ending line numbers, zero is the first line of the +visible pane and negative numbers are lines in the history. +The default is to capture only the visible contents of the pane. +.It Xo +.Ic choose-client +.Op Fl t Ar target-window +.Op Ar template +.Xc +Put a window into client choice mode, allowing a client to be selected +interactively from a list. +After a client is chosen, +.Ql %% +is replaced by the client +.Xr pty 4 +path in +.Ar template +and the result executed as a command. +If +.Ar template +is not given, "detach-client -t '%%'" is used. +This command works only from inside +.Nm . +.It Xo +.Ic choose-session +.Op Fl t Ar target-window +.Op Ar template +.Xc +Put a window into session choice mode, where a session may be selected +interactively from a list. +When one is chosen, +.Ql %% +is replaced by the session name in +.Ar template +and the result executed as a command. +If +.Ar template +is not given, "switch-client -t '%%'" is used. +This command works only from inside +.Nm . +.It Xo +.Ic choose-window +.Op Fl t Ar target-window +.Op Ar template +.Xc +Put a window into window choice mode, where a window may be chosen +interactively from a list. +After a window is selected, +.Ql %% +is replaced by the session name and window index in +.Ar template +and the result executed as a command. +If +.Ar template +is not given, "select-window -t '%%'" is used. +This command works only from inside +.Nm . +.It Ic display-panes Op Fl t Ar target-client +.D1 (alias: Ic displayp) +Display a visible indicator of each pane shown by +.Ar target-client . +See the +.Ic display-panes-time , +.Ic display-panes-colour , +and +.Ic display-panes-active-colour +session options. +While the indicator is on screen, a pane may be selected with the +.Ql 0 +to +.Ql 9 +keys. +.It Xo Ic find-window +.Op Fl t Ar target-window +.Ar match-string +.Xc +.D1 (alias: Ic findw ) +Search for the +.Xr fnmatch 3 +pattern +.Ar match-string +in window names, titles, and visible content (but not history). +If only one window is matched, it'll be automatically selected, otherwise a +choice list is shown. +This command only works from inside +.Nm . +.It Xo Ic join-pane +.Op Fl dhv +.Oo Fl l +.Ar size | +.Fl p Ar percentage Oc +.Op Fl s Ar src-pane +.Op Fl t Ar dst-pane +.Xc +.D1 (alias: Ic joinp ) +Like +.Ic split-window , +but instead of splitting +.Ar dst-pane +and creating a new pane, split it and move +.Ar src-pane +into the space. +This can be used to reverse +.Ic break-pane . +.It Xo Ic kill-pane +.Op Fl a +.Op Fl t Ar target-pane +.Xc +.D1 (alias: Ic killp ) +Destroy the given pane. +If no panes remain in the containing window, it is also destroyed. +The +.Fl a +option kills all but the pane given with +.Fl t . +.It Ic kill-window Op Fl t Ar target-window +.D1 (alias: Ic killw ) +Kill the current window or the window at +.Ar target-window , +removing it from any sessions to which it is linked. +.It Ic last-pane Op Fl t Ar target-window +.D1 (alias: Ic lastp ) +Select the last (previously selected) pane. +.It Ic last-window Op Fl t Ar target-session +.D1 (alias: Ic last ) +Select the last (previously selected) window. +If no +.Ar target-session +is specified, select the last window of the current session. +.It Xo Ic link-window +.Op Fl dk +.Op Fl s Ar src-window +.Op Fl t Ar dst-window +.Xc +.D1 (alias: Ic linkw ) +Link the window at +.Ar src-window +to the specified +.Ar dst-window . +If +.Ar dst-window +is specified and no such window exists, the +.Ar src-window +is linked there. +If +.Fl k +is given and +.Ar dst-window +exists, it is killed, otherwise an error is generated. +If +.Fl d +is given, the newly linked window is not selected. +.It Xo Ic list-panes +.Op Fl as +.Op Fl t Ar target +.Xc +.D1 (alias: Ic lsp ) +If +.Fl a +is given, +.Ar target +is ignored and all panes on the server are listed. +If +.Fl s +is given, +.Ar target +is a session (or the current session). +If neither is given, +.Ar target +is a window (or the current window). +.It Xo Ic list-windows +.Op Fl a +.Op Fl t Ar target-session +.Xc +.D1 (alias: Ic lsw ) +If +.Fl a +is given, list all windows on the server. +Otherwise, list windows in the current session or in +.Ar target-session . +.It Xo Ic move-window +.Op Fl dk +.Op Fl s Ar src-window +.Op Fl t Ar dst-window +.Xc +.D1 (alias: Ic movew ) +This is similar to +.Ic link-window , +except the window at +.Ar src-window +is moved to +.Ar dst-window . +.It Xo Ic new-window +.Op Fl adkP +.Op Fl n Ar window-name +.Op Fl t Ar target-window +.Op Ar shell-command +.Xc +.D1 (alias: Ic neww ) +Create a new window. +With +.Fl a , +the new window is inserted at the next index up from the specified +.Ar target-window , +moving windows up if necessary, +otherwise +.Ar target-window +is the new window location. +.Pp +If +.Fl d +is given, the session does not make the new window the current window. +.Ar target-window +represents the window to be created; if the target already exists an error is +shown, unless the +.Fl k +flag is used, in which case it is destroyed. +.Ar shell-command +is the command to execute. +If +.Ar shell-command +is not specified, the value of the +.Ic default-command +option is used. +.Pp +When the shell command completes, the window closes. +See the +.Ic remain-on-exit +option to change this behaviour. +.Pp +The +.Ev TERM +environment variable must be set to +.Dq screen +for all programs running +.Em inside +.Nm . +New windows will automatically have +.Dq TERM=screen +added to their environment, but care must be taken not to reset this in shell +start-up files. +.Pp +The +.Fl P +option prints the location of the new window after it has been created. +.It Ic next-layout Op Fl t Ar target-window +.D1 (alias: Ic nextl ) +Move a window to the next layout and rearrange the panes to fit. +.It Xo Ic next-window +.Op Fl a +.Op Fl t Ar target-session +.Xc +.D1 (alias: Ic next ) +Move to the next window in the session. +If +.Fl a +is used, move to the next window with a bell, activity or content alert. +.It Xo Ic pipe-pane +.Op Fl o +.Op Fl t Ar target-pane +.Op Ar shell-command +.Xc +.D1 (alias: Ic pipep ) +Pipe any output sent by the program in +.Ar target-pane +to a shell command. +A pane may only be piped to one command at a time, any existing pipe is +closed before +.Ar shell-command +is executed. +The +.Ar shell-command +string may contain the special character sequences supported by the +.Ic status-left +option. +If no +.Ar shell-command +is given, the current pipe (if any) is closed. +.Pp +The +.Fl o +option only opens a new pipe if no previous pipe exists, allowing a pipe to +be toggled with a single key, for example: +.Bd -literal -offset indent +bind-key C-p pipe-pane -o 'cat >>~/output.#I-#P' +.Ed +.It Xo Ic previous-layout +.Op Fl t Ar target-window +.Xc +.D1 (alias: Ic prevl ) +Move to the previous layout in the session. +.It Xo Ic previous-window +.Op Fl a +.Op Fl t Ar target-session +.Xc +.D1 (alias: Ic prev ) +Move to the previous window in the session. +With +.Fl a , +move to the previous window with a bell, activity or content alert. +.It Xo Ic rename-window +.Op Fl t Ar target-window +.Ar new-name +.Xc +.D1 (alias: Ic renamew ) +Rename the current window, or the window at +.Ar target-window +if specified, to +.Ar new-name . +.It Xo Ic resize-pane +.Op Fl DLRU +.Op Fl t Ar target-pane +.Op Ar adjustment +.Xc +.D1 (alias: Ic resizep ) +Resize a pane, upward with +.Fl U +(the default), downward with +.Fl D , +to the left with +.Fl L +and to the right with +.Fl R . +The +.Ar adjustment +is given in lines or cells (the default is 1). +.It Xo Ic respawn-pane +.Op Fl k +.Op Fl t Ar target-pane +.Op Ar shell-command +.Xc +.D1 (alias: Ic respawnp ) +Reactivate a pane in which the command has exited (see the +.Ic remain-on-exit +window option). +If +.Ar shell-command +is not given, the command used when the pane was created is executed. +The pane must be already inactive, unless +.Fl k +is given, in which case any existing command is killed. +.It Xo Ic respawn-window +.Op Fl k +.Op Fl t Ar target-window +.Op Ar shell-command +.Xc +.D1 (alias: Ic respawnw ) +Reactivate a window in which the command has exited (see the +.Ic remain-on-exit +window option). +If +.Ar shell-command +is not given, the command used when the window was created is executed. +The window must be already inactive, unless +.Fl k +is given, in which case any existing command is killed. +.It Xo Ic rotate-window +.Op Fl DU +.Op Fl t Ar target-window +.Xc +.D1 (alias: Ic rotatew ) +Rotate the positions of the panes within a window, either upward (numerically +lower) with +.Fl U +or downward (numerically higher). +.It Xo Ic select-layout +.Op Fl np +.Op Fl t Ar target-window +.Op Ar layout-name +.Xc +.D1 (alias: Ic selectl ) +Choose a specific layout for a window. +If +.Ar layout-name +is not given, the last preset layout used (if any) is reapplied. +.Fl n +and +.Fl p +are equivalent to the +.Ic next-layout +and +.Ic previous-layout +commands. +.It Xo Ic select-pane +.Op Fl lDLRU +.Op Fl t Ar target-pane +.Xc +.D1 (alias: Ic selectp ) +Make pane +.Ar target-pane +the active pane in window +.Ar target-window . +If one of +.Fl D , +.Fl L , +.Fl R , +or +.Fl U +is used, respectively the pane below, to the left, to the right, or above the +target pane is used. +.Fl l +is the same as using the +.Ic last-pane +command. +.It Xo Ic select-window +.Op Fl lnp +.Op Fl t Ar target-window +.Xc +.D1 (alias: Ic selectw ) +Select the window at +.Ar target-window . +.Fl l , +.Fl n +and +.Fl p +are equivalent to the +.Ic last-window , +.Ic next-window +and +.Ic previous-window +commands. +.It Xo Ic split-window +.Op Fl dhvP +.Oo Fl l +.Ar size | +.Fl p Ar percentage Oc +.Op Fl t Ar target-pane +.Op Ar shell-command +.Xc +.D1 (alias: Ic splitw ) +Create a new pane by splitting +.Ar target-pane : +.Fl h +does a horizontal split and +.Fl v +a vertical split; if neither is specified, +.Fl v +is assumed. +The +.Fl l +and +.Fl p +options specify the size of the new pane in lines (for vertical split) or in +cells (for horizontal split), or as a percentage, respectively. +All other options have the same meaning as for the +.Ic new-window +command. +.It Xo Ic swap-pane +.Op Fl dDU +.Op Fl s Ar src-pane +.Op Fl t Ar dst-pane +.Xc +.D1 (alias: Ic swapp ) +Swap two panes. +If +.Fl U +is used and no source pane is specified with +.Fl s , +.Ar dst-pane +is swapped with the previous pane (before it numerically); +.Fl D +swaps with the next pane (after it numerically). +.Fl d +instructs +.Nm +not to change the active pane. +.It Xo Ic swap-window +.Op Fl d +.Op Fl s Ar src-window +.Op Fl t Ar dst-window +.Xc +.D1 (alias: Ic swapw ) +This is similar to +.Ic link-window , +except the source and destination windows are swapped. +It is an error if no window exists at +.Ar src-window . +.It Xo Ic unlink-window +.Op Fl k +.Op Fl t Ar target-window +.Xc +.D1 (alias: Ic unlinkw ) +Unlink +.Ar target-window . +Unless +.Fl k +is given, a window may be unlinked only if it is linked to multiple sessions - +windows may not be linked to no sessions; +if +.Fl k +is specified and the window is linked to only one session, it is unlinked and +destroyed. +.El +.Sh KEY BINDINGS +.Nm +allows a command to be bound to most keys, with or without a prefix key. +When specifying keys, most represent themselves (for example +.Ql A +to +.Ql Z ) . +Ctrl keys may be prefixed with +.Ql C- +or +.Ql ^ , +and Alt (meta) with +.Ql M- . +In addition, the following special key names are accepted: +.Em Up , +.Em Down , +.Em Left , +.Em Right , +.Em BSpace , +.Em BTab , +.Em DC +(Delete), +.Em End , +.Em Enter , +.Em Escape , +.Em F1 +to +.Em F20 , +.Em Home , +.Em IC +(Insert), +.Em NPage +(Page Up), +.Em PPage +(Page Down), +.Em Space , +and +.Em Tab . +Note that to bind the +.Ql \&" +or +.Ql ' +keys, quotation marks are necessary, for example: +.Bd -literal -offset indent +bind-key '"' split-window +bind-key "'" new-window +.Ed +.Pp +Commands related to key bindings are as follows: +.Bl -tag -width Ds +.It Xo Ic bind-key +.Op Fl cnr +.Op Fl t Ar key-table +.Ar key Ar command Op Ar arguments +.Xc +.D1 (alias: Ic bind ) +Bind key +.Ar key +to +.Ar command . +By default (without +.Fl t ) +the primary key bindings are modified (those normally activated with the prefix +key); in this case, if +.Fl n +is specified, it is not necessary to use the prefix key, +.Ar command +is bound to +.Ar key +alone. +The +.Fl r +flag indicates this key may repeat, see the +.Ic repeat-time +option. +.Pp +If +.Fl t +is present, +.Ar key +is bound in +.Ar key-table : +the binding for command mode with +.Fl c +or for normal mode without. +To view the default bindings and possible commands, see the +.Ic list-keys +command. +.It Ic list-keys Op Fl t Ar key-table +.D1 (alias: Ic lsk ) +List all key bindings. +Without +.Fl t +the primary key bindings - those executed when preceded by the prefix key - +are printed. +Keys bound without the prefix key (see +.Ic bind-key +.Fl n ) +are marked with +.Ql (no prefix) . +.Pp +With +.Fl t , +the key bindings in +.Ar key-table +are listed; this may be one of: +.Em vi-edit , +.Em emacs-edit , +.Em vi-choice , +.Em emacs-choice , +.Em vi-copy +or +.Em emacs-copy . +.It Xo Ic send-keys +.Op Fl t Ar target-pane +.Ar key Ar ... +.Xc +.D1 (alias: Ic send ) +Send a key or keys to a window. +Each argument +.Ar key +is the name of the key (such as +.Ql C-a +or +.Ql npage +) to send; if the string is not recognised as a key, it is sent as a series of +characters. +All arguments are sent sequentially from first to last. +.It Ic send-prefix Op Fl t Ar target-pane +Send the prefix key to a window as if it was pressed. +If multiple prefix keys are configured, only the first is sent. +.It Xo Ic unbind-key +.Op Fl acn +.Op Fl t Ar key-table +.Ar key +.Xc +.D1 (alias: Ic unbind ) +Unbind the command bound to +.Ar key . +Without +.Fl t +the primary key bindings are modified; in this case, if +.Fl n +is specified, the command bound to +.Ar key +without a prefix (if any) is removed. +If +.Fl a +is present, all key bindings are removed. +.Pp +If +.Fl t +is present, +.Ar key +in +.Ar key-table +is unbound: the binding for command mode with +.Fl c +or for normal mode without. +.El +.Sh OPTIONS +The appearance and behaviour of +.Nm +may be modified by changing the value of various options. +There are three types of option: +.Em server options , +.Em session options +and +.Em window options . +.Pp +The +.Nm +server has a set of global options which do not apply to any particular +window or session. +These are altered with the +.Ic set-option +.Fl s +command, or displayed with the +.Ic show-options +.Fl s +command. +.Pp +In addition, each individual session may have a set of session options, and +there is a separate set of global session options. +Sessions which do not have a particular option configured inherit the value +from the global session options. +Session options are set or unset with the +.Ic set-option +command and may be listed with the +.Ic show-options +command. +The available server and session options are listed under the +.Ic set-option +command. +.Pp +Similarly, a set of window options is attached to each window, and there is +a set of global window options from which any unset options are inherited. +Window options are altered with the +.Ic set-window-option +command and can be listed with the +.Ic show-window-options +command. +All window options are documented with the +.Ic set-window-option +command. +.Pp +Commands which set options are as follows: +.Bl -tag -width Ds +.It Xo Ic set-option +.Op Fl agsuw +.Op Fl t Ar target-session | Ar target-window +.Ar option Ar value +.Xc +.D1 (alias: Ic set ) +Set a window option with +.Fl w +(equivalent to the +.Ic set-window-option +command), +a server option with +.Fl s , +otherwise a session option. +.Pp +If +.Fl g +is specified, the global session or window option is set. +With +.Fl a , +and if the option expects a string, +.Ar value +is appended to the existing setting. +The +.Fl u +flag unsets an option, so a session inherits the option from the global +options. +It is not possible to unset a global option. +.Pp +Available window options are listed under +.Ic set-window-option . +.Pp +Available server options are: +.Bl -tag -width Ds +.It Ic buffer-limit Ar number +Set the number of buffers; as new buffers are added to the top of the stack, +old ones are removed from the bottom if necessary to maintain this maximum +length. +.It Xo Ic set-clipboard +.Op Ic on | off +.Xc +Attempt to set the terminal clipboard content using the +\ee]52;...\e007 +.Xr xterm 1 +escape sequences. +This option is on by default if there is an +.Em \&Ms +entry in the +.Xr terminfo 5 +description for the client terminal. +Note that this feature needs to be enabled in +.Xr xterm 1 +by setting the resource: +.Bd -literal -offset indent +disallowedWindowOps: 20,21,SetXprop +.Ed +.Pp +Or changing this property from the +.Xr xterm 1 +interactive menu when required. +.It Ic escape-time Ar time +Set the time in milliseconds for which +.Nm +waits after an escape is input to determine if it is part of a function or meta +key sequences. +The default is 500 milliseconds. +.It Xo Ic exit-unattached +.Op Ic on | off +.Xc +If enabled, the server will exit when there are no attached clients. +.It Xo Ic quiet +.Op Ic on | off +.Xc +Enable or disable the display of various informational messages (see also the +.Fl q +command line flag). +.El +.Pp +Available session options are: +.Bl -tag -width Ds +.It Ic base-index Ar index +Set the base index from which an unused index should be searched when a new +window is created. +The default is zero. +.It Xo Ic bell-action +.Op Ic any | none | current +.Xc +Set action on window bell. +.Ic any +means a bell in any window linked to a session causes a bell in the current +window of that session, +.Ic none +means all bells are ignored and +.Ic current +means only bell in windows other than the current window are ignored. +.It Xo Ic bell-on-alert +.Op Ic on | off +.Xc +If on, ring the terminal bell when an activity, content or silence alert +occurs. +.It Ic default-command Ar shell-command +Set the command used for new windows (if not specified when the window is +created) to +.Ar shell-command , +which may be any +.Xr sh 1 +command. +The default is an empty string, which instructs +.Nm +to create a login shell using the value of the +.Ic default-shell +option. +.It Ic default-path Ar path +Set the default working directory for processes created from keys, or +interactively from the prompt. +The default is empty, which means to use the working directory of the shell +from which the server was started if it is available or the user's home if not. +.It Ic default-shell Ar path +Specify the default shell. +This is used as the login shell for new windows when the +.Ic default-command +option is set to empty, and must be the full path of the executable. +When started +.Nm +tries to set a default value from the first suitable of the +.Ev SHELL +environment variable, the shell returned by +.Xr getpwuid 3 , +or +.Pa /bin/sh . +This option should be configured when +.Nm +is used as a login shell. +.It Ic default-terminal Ar terminal +Set the default terminal for new windows created in this session - the +default value of the +.Ev TERM +environment variable. +For +.Nm +to work correctly, this +.Em must +be set to +.Ql screen +or a derivative of it. +.It Xo Ic destroy-unattached +.Op Ic on | off +.Xc +If enabled and the session is no longer attached to any clients, it is +destroyed. +.It Xo Ic detach-on-destroy +.Op Ic on | off +.Xc +If on (the default), the client is detached when the session it is attached to +is destroyed. +If off, the client is switched to the most recently active of the remaining +sessions. +.It Ic display-panes-active-colour Ar colour +Set the colour used by the +.Ic display-panes +command to show the indicator for the active pane. +.It Ic display-panes-colour Ar colour +Set the colour used by the +.Ic display-panes +command to show the indicators for inactive panes. +.It Ic display-panes-time Ar time +Set the time in milliseconds for which the indicators shown by the +.Ic display-panes +command appear. +.It Ic display-time Ar time +Set the amount of time for which status line messages and other on-screen +indicators are displayed. +.Ar time +is in milliseconds. +.It Ic history-limit Ar lines +Set the maximum number of lines held in window history. +This setting applies only to new windows - existing window histories are not +resized and retain the limit at the point they were created. +.It Ic lock-after-time Ar number +Lock the session (like the +.Ic lock-session +command) after +.Ar number +seconds of inactivity, or the entire server (all sessions) if the +.Ic lock-server +option is set. +The default is not to lock (set to 0). +.It Ic lock-command Ar shell-command +Command to run when locking each client. +The default is to run +.Xr lock 1 +with +.Fl np . +.It Xo Ic lock-server +.Op Ic on | off +.Xc +If this option is +.Ic on +(the default), +instead of each session locking individually as each has been +idle for +.Ic lock-after-time , +the entire server will lock after +.Em all +sessions would have locked. +This has no effect as a session option; it must be set as a global option. +.It Ic message-attr Ar attributes +Set status line message attributes, where +.Ar attributes +is either +.Ic none +or a comma-delimited list of one or more of: +.Ic bright +(or +.Ic bold ) , +.Ic dim , +.Ic underscore , +.Ic blink , +.Ic reverse , +.Ic hidden , +or +.Ic italics . +.It Ic message-bg Ar colour +Set status line message background colour, where +.Ar colour +is one of: +.Ic black , +.Ic red , +.Ic green , +.Ic yellow , +.Ic blue , +.Ic magenta , +.Ic cyan , +.Ic white , +.Ic colour0 +to +.Ic colour255 +from the 256-colour set, +.Ic default , +or a hexadecimal RGB string such as +.Ql #ffffff , +which chooses the closest match from the default 256-colour set. +.It Ic message-fg Ar colour +Set status line message foreground colour. +.It Ic message-limit Ar number +Set the number of error or information messages to save in the message log for +each client. +The default is 20. +.It Xo Ic mouse-resize-pane +.Op Ic on | off +.Xc +If on, +.Nm +captures the mouse and allows panes to be resized by dragging on their borders. +.It Xo Ic mouse-select-pane +.Op Ic on | off +.Xc +If on, +.Nm +captures the mouse and when a window is split into multiple panes the mouse may +be used to select the current pane. +The mouse click is also passed through to the application as normal. +.It Xo Ic mouse-select-window +.Op Ic on | off +.Xc +If on, clicking the mouse on a window name in the status line will select that +window. +.It Ic pane-active-border-bg Ar colour +.It Ic pane-active-border-fg Ar colour +Set the pane border colour for the currently active pane. +.It Ic pane-border-bg Ar colour +.It Ic pane-border-fg Ar colour +Set the pane border colour for panes aside from the active pane. +.It Ic prefix Ar keys +Set the keys accepted as a prefix key. +.Ar keys +is a comma-separated list of key names, each of which individually behave as +the prefix key. +.It Ic repeat-time Ar time +Allow multiple commands to be entered without pressing the prefix-key again +in the specified +.Ar time +milliseconds (the default is 500). +Whether a key repeats may be set when it is bound using the +.Fl r +flag to +.Ic bind-key . +Repeat is enabled for the default keys bound to the +.Ic resize-pane +command. +.It Xo Ic mouse-utf8 +.Op Ic on | off +.Xc +If enabled, request mouse input as UTF-8 on UTF-8 terminals. +.It Xo Ic set-remain-on-exit +.Op Ic on | off +.Xc +Set the +.Ic remain-on-exit +window option for any windows first created in this session. +When this option is true, windows in which the running program has +exited do not close, instead remaining open but inactivate. +Use the +.Ic respawn-window +command to reactivate such a window, or the +.Ic kill-window +command to destroy it. +.It Xo Ic set-titles +.Op Ic on | off +.Xc +Attempt to set the window title using the \ee]2;...\e007 xterm code if +the terminal appears to be an xterm. +This option is off by default. +Note that elinks +will only attempt to set the window title if the STY environment +variable is set. +.It Ic set-titles-string Ar string +String used to set the window title if +.Ic set-titles +is on. +Character sequences are replaced as for the +.Ic status-left +option. +.It Xo Ic status +.Op Ic on | off +.Xc +Show or hide the status line. +.It Ic status-attr Ar attributes +Set status line attributes. +.It Ic status-bg Ar colour +Set status line background colour. +.It Ic status-fg Ar colour +Set status line foreground colour. +.It Ic status-interval Ar interval +Update the status bar every +.Ar interval +seconds. +By default, updates will occur every 15 seconds. +A setting of zero disables redrawing at interval. +.It Xo Ic status-justify +.Op Ic left | centre | right +.Xc +Set the position of the window list component of the status line: left, centre +or right justified. +.It Xo Ic status-keys +.Op Ic vi | emacs +.Xc +Use vi or emacs-style +key bindings in the status line, for example at the command prompt. +The default is emacs, unless the +.Ev VISUAL +or +.Ev EDITOR +environment variables are set and contain the string +.Ql vi . +.It Ic status-left Ar string +Display +.Ar string +to the left of the status bar. +.Ar string +will be passed through +.Xr strftime 3 +before being used. +By default, the session name is shown. +.Ar string +may contain any of the following special character sequences: +.Bl -column "Character pair" "Replaced with" -offset indent +.It Sy "Character pair" Ta Sy "Replaced with" +.It Li "#(shell-command)" Ta "First line of the command's output" +.It Li "#[attributes]" Ta "Colour or attribute change" +.It Li "#H" Ta "Hostname of local host" +.It Li "#h" Ta "Hostname of local host without the domain name" +.It Li "#F" Ta "Current window flag" +.It Li "#I" Ta "Current window index" +.It Li "#P" Ta "Current pane index" +.It Li "#S" Ta "Session name" +.It Li "#T" Ta "Current window title" +.It Li "#W" Ta "Current window name" +.It Li "##" Ta "A literal" Ql # +.El +.Pp +The #(shell-command) form executes +.Ql shell-command +and inserts the first line of its output. +Note that shell commands are only executed once at the interval specified by +the +.Ic status-interval +option: if the status line is redrawn in the meantime, the previous result is +used. +Shell commands are executed with the +.Nm +global environment set (see the +.Sx ENVIRONMENT +section). +.Pp +The window title (#T) is the title set by the program running within the window +using the OSC title setting sequence, for example: +.Bd -literal -offset indent +$ printf '\e033]2;My Title\e033\e\e' +.Ed +.Pp +When a window is first created, its title is the hostname. +.Pp +#[attributes] allows a comma-separated list of attributes to be specified, +these may be +.Ql fg=colour +to set the foreground colour, +.Ql bg=colour +to set the background colour, the name of one of the attributes (listed under +the +.Ic message-attr +option) to turn an attribute on, or an attribute prefixed with +.Ql no +to turn one off, for example +.Ic nobright . +Examples are: +.Bd -literal -offset indent +#(sysctl vm.loadavg) +#[fg=yellow,bold]#(apm -l)%%#[default] [#S] +.Ed +.Pp +Where appropriate, special character sequences may be prefixed with a number to +specify the maximum length, for example +.Ql #24T . +.Pp +By default, UTF-8 in +.Ar string +is not interpreted, to enable UTF-8, use the +.Ic status-utf8 +option. +.It Ic status-left-attr Ar attributes +Set the attribute of the left part of the status line. +.It Ic status-left-bg Ar colour +Set the background colour of the left part of the status line. +.It Ic status-left-fg Ar colour +Set the foreground colour of the left part of the status line. +.It Ic status-left-length Ar length +Set the maximum +.Ar length +of the left component of the status bar. +The default is 10. +.It Ic status-right Ar string +Display +.Ar string +to the right of the status bar. +By default, the current window title in double quotes, the date and the time +are shown. +As with +.Ic status-left , +.Ar string +will be passed to +.Xr strftime 3 , +character pairs are replaced, and UTF-8 is dependent on the +.Ic status-utf8 +option. +.It Ic status-right-attr Ar attributes +Set the attribute of the right part of the status line. +.It Ic status-right-bg Ar colour +Set the background colour of the right part of the status line. +.It Ic status-right-fg Ar colour +Set the foreground colour of the right part of the status line. +.It Ic status-right-length Ar length +Set the maximum +.Ar length +of the right component of the status bar. +The default is 40. +.It Xo Ic status-utf8 +.Op Ic on | off +.Xc +Instruct +.Nm +to treat top-bit-set characters in the +.Ic status-left +and +.Ic status-right +strings as UTF-8; notably, this is important for wide characters. +This option defaults to off. +.It Ic terminal-overrides Ar string +Contains a list of entries which override terminal descriptions read using +.Xr terminfo 5 . +.Ar string +is a comma-separated list of items each a colon-separated string made up of a +terminal type pattern (matched using +.Xr fnmatch 3 ) +and a set of +.Em name=value +entries. +.Pp +For example, to set the +.Ql clear +.Xr terminfo 5 +entry to +.Ql \ee[H\ee[2J +for all terminal types and the +.Ql dch1 +entry to +.Ql \ee[P +for the +.Ql rxvt +terminal type, the option could be set to the string: +.Bd -literal -offset indent +"*:clear=\ee[H\ee[2J,rxvt:dch1=\ee[P" +.Ed +.Pp +The terminal entry value is passed through +.Xr strunvis 3 +before interpretation. +The default value forcibly corrects the +.Ql colors +entry for terminals which support 88 or 256 colours: +.Bd -literal -offset indent +"*88col*:colors=88,*256col*:colors=256,xterm*:XT" +.Ed +.It Ic update-environment Ar variables +Set a space-separated string containing a list of environment variables to be +copied into the session environment when a new session is created or an +existing session is attached. +Any variables that do not exist in the source environment are set to be +removed from the session environment (as if +.Fl r +was given to the +.Ic set-environment +command). +The default is +"DISPLAY SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID SSH_CONNECTION WINDOWID +XAUTHORITY". +.It Xo Ic visual-activity +.Op Ic on | off +.Xc +If on, display a status line message when activity occurs in a window +for which the +.Ic monitor-activity +window option is enabled. +.It Xo Ic visual-bell +.Op Ic on | off +.Xc +If this option is on, a message is shown on a bell instead of it being passed +through to the terminal (which normally makes a sound). +Also see the +.Ic bell-action +option. +.It Xo Ic visual-content +.Op Ic on | off +.Xc +Like +.Ic visual-activity , +display a message when content is present in a window +for which the +.Ic monitor-content +window option is enabled. +.It Xo Ic visual-silence +.Op Ic on | off +.Xc +If +.Ic monitor-silence +is enabled, prints a message after the interval has expired on a given window. +.El +.It Xo Ic set-window-option +.Op Fl agu +.Op Fl t Ar target-window +.Ar option Ar value +.Xc +.D1 (alias: Ic setw ) +Set a window option. +The +.Fl a , +.Fl g +and +.Fl u +flags work similarly to the +.Ic set-option +command. +.Pp +Supported window options are: +.Pp +.Bl -tag -width Ds -compact +.It Xo Ic aggressive-resize +.Op Ic on | off +.Xc +Aggressively resize the chosen window. +This means that +.Nm +will resize the window to the size of the smallest session for which it is the +current window, rather than the smallest session to which it is attached. +The window may resize when the current window is changed on another sessions; +this option is good for full-screen programs which support +.Dv SIGWINCH +and poor for interactive programs such as shells. +.Pp +.It Xo Ic alternate-screen +.Op Ic on | off +.Xc +This option configures whether programs running inside +.Nm +may use the terminal alternate screen feature, which allows the +.Em smcup +and +.Em rmcup +.Xr terminfo 5 +capabilities. +The alternate screen feature preserves the contents of the window when an +interactive application starts and restores it on exit, so that any output +visible before the application starts reappears unchanged after it exits. +The default is on. +.Pp +.It Xo Ic automatic-rename +.Op Ic on | off +.Xc +Control automatic window renaming. +When this setting is enabled, +.Nm +will attempt - on supported platforms - to rename the window to reflect the +command currently running in it. +This flag is automatically disabled for an individual window when a name +is specified at creation with +.Ic new-window +or +.Ic new-session , +or later with +.Ic rename-window . +It may be switched off globally with: +.Bd -literal -offset indent +set-window-option -g automatic-rename off +.Ed +.Pp +.It Ic clock-mode-colour Ar colour +Set clock colour. +.Pp +.It Xo Ic clock-mode-style +.Op Ic 12 | 24 +.Xc +Set clock hour format. +.Pp +.It Ic force-height Ar height +.It Ic force-width Ar width +Prevent +.Nm +from resizing a window to greater than +.Ar width +or +.Ar height . +A value of zero restores the default unlimited setting. +.Pp +.It Ic main-pane-height Ar height +.It Ic main-pane-width Ar width +Set the width or height of the main (left or top) pane in the +.Ic main-horizontal +or +.Ic main-vertical +layouts. +.Pp +.It Ic mode-attr Ar attributes +Set window modes attributes. +.Pp +.It Ic mode-bg Ar colour +Set window modes background colour. +.Pp +.It Ic mode-fg Ar colour +Set window modes foreground colour. +.Pp +.It Xo Ic mode-keys +.Op Ic vi | emacs +.Xc +Use vi or emacs-style key bindings in copy and choice modes. +As with the +.Ic status-keys +option, the default is emacs, unless +.Ev VISUAL +or +.Ev EDITOR +contains +.Ql vi . +.Pp +.It Xo Ic mode-mouse +.Op Ic on | off +.Xc +Mouse state in modes. +If on, the mouse may be used to enter copy mode and copy a selection by +dragging, to enter copy mode and scroll with the mouse wheel, or to select an +option in choice mode. +.Pp +.It Xo Ic monitor-activity +.Op Ic on | off +.Xc +Monitor for activity in the window. +Windows with activity are highlighted in the status line. +.Pp +.It Ic monitor-content Ar match-string +Monitor content in the window. +When +.Xr fnmatch 3 +pattern +.Ar match-string +appears in the window, it is highlighted in the status line. +.Pp +.It Xo Ic monitor-silence +.Op Ic interval +.Xc +Monitor for silence (no activity) in the window within +.Ic interval +seconds. +Windows that have been silent for the interval are highlighted in the +status line. +An interval of zero disables the monitoring. +.Pp +.It Ic other-pane-height Ar height +Set the height of the other panes (not the main pane) in the +.Ic main-horizontal +layout. +If this option is set to 0 (the default), it will have no effect. +If both the +.Ic main-pane-height +and +.Ic other-pane-height +options are set, the main pane will grow taller to make the other panes the +specified height, but will never shrink to do so. +.Pp +.It Ic other-pane-width Ar width +Like +.Ic other-pane-height , +but set the width of other panes in the +.Ic main-vertical +layout. +.Pp +.It Xo Ic remain-on-exit +.Op Ic on | off +.Xc +A window with this flag set is not destroyed when the program running in it +exits. +The window may be reactivated with the +.Ic respawn-window +command. +.Pp +.It Xo Ic synchronize-panes +.Op Ic on | off +.Xc +Duplicate input to any pane to all other panes in the same window (only +for panes that are not in any special mode). +.Pp +.It Xo Ic utf8 +.Op Ic on | off +.Xc +Instructs +.Nm +to expect UTF-8 sequences to appear in this window. +.Pp +.It Ic window-status-attr Ar attributes +Set status line attributes for a single window. +.Pp +.It Ic window-status-bg Ar colour +Set status line background colour for a single window. +.Pp +.It Ic window-status-fg Ar colour +Set status line foreground colour for a single window. +.Pp +.It Ic window-status-format Ar string +Set the format in which the window is displayed in the status line window list. +See the +.Ar status-left +option for details of special character sequences available. +The default is +.Ql #I:#W#F . +.Pp +.It Ic window-status-alert-attr Ar attributes +Set status line attributes for windows which have an alert (bell, activity +or content). +.Pp +.It Ic window-status-alert-bg Ar colour +Set status line background colour for windows with an alert. +.Pp +.It Ic window-status-alert-fg Ar colour +Set status line foreground colour for windows with an alert. +.Pp +.It Ic window-status-current-attr Ar attributes +Set status line attributes for the currently active window. +.Pp +.It Ic window-status-current-bg Ar colour +Set status line background colour for the currently active window. +.Pp +.It Ic window-status-current-fg Ar colour +Set status line foreground colour for the currently active window. +.Pp +.It Ic window-status-current-format Ar string +Like +.Ar window-status-format , +but is the format used when the window is the current window. +.Pp +.It Ic word-separators Ar string +Sets the window's conception of what characters are considered word +separators, for the purposes of the next and previous word commands in +copy mode. +The default is +.Ql \ -_@ . +.Pp +.It Xo Ic xterm-keys +.Op Ic on | off +.Xc +If this option is set, +.Nm +will generate +.Xr xterm 1 -style +function key sequences; these have a number included to indicate modifiers such +as Shift, Alt or Ctrl. +The default is off. +.El +.It Xo Ic show-options +.Op Fl gsw +.Op Fl t Ar target-session | Ar target-window +.Xc +.D1 (alias: Ic show ) +Show the window options with +.Fl w +(equivalent to +.Ic show-window-options ) , +the server options with +.Fl s , +otherwise the session options for +.Ar target session . +Global session or window options are listed if +.Fl g +is used. +.It Xo Ic show-window-options +.Op Fl g +.Op Fl t Ar target-window +.Xc +.D1 (alias: Ic showw ) +List the window options for +.Ar target-window , +or the global window options if +.Fl g +is used. +.El +.Sh ENVIRONMENT +When the server is started, +.Nm +copies the environment into the +.Em global environment ; +in addition, each session has a +.Em session environment . +When a window is created, the session and global environments are merged. +If a variable exists in both, the value from the session environment is used. +The result is the initial environment passed to the new process. +.Pp +The +.Ic update-environment +session option may be used to update the session environment from the client +when a new session is created or an old reattached. +.Nm +also initialises the +.Ev TMUX +variable with some internal information to allow commands to be executed +from inside, and the +.Ev TERM +variable with the correct terminal setting of +.Ql screen . +.Pp +Commands to alter and view the environment are: +.Bl -tag -width Ds +.It Xo Ic set-environment +.Op Fl gru +.Op Fl t Ar target-session +.Ar name Op Ar value +.Xc +.D1 (alias: Ic setenv ) +Set or unset an environment variable. +If +.Fl g +is used, the change is made in the global environment; otherwise, it is applied +to the session environment for +.Ar target-session . +The +.Fl u +flag unsets a variable. +.Fl r +indicates the variable is to be removed from the environment before starting a +new process. +.It Xo Ic show-environment +.Op Fl g +.Op Fl t Ar target-session +.Xc +.D1 (alias: Ic showenv ) +Display the environment for +.Ar target-session +or the global environment with +.Fl g . +Variables removed from the environment are prefixed with +.Ql - . +.El +.Sh STATUS LINE +.Nm +includes an optional status line which is displayed in the bottom line of each +terminal. +By default, the status line is enabled (it may be disabled with the +.Ic status +session option) and contains, from left-to-right: the name of the current +session in square brackets; the window list; the current window title in double +quotes; and the time and date. +.Pp +The status line is made of three parts: configurable left and right sections +(which may contain dynamic content such as the time or output from a shell +command, see the +.Ic status-left , +.Ic status-left-length , +.Ic status-right , +and +.Ic status-right-length +options below), and a central window list. +By default, the window list shows the index, name and (if any) flag of the +windows present in the current session in ascending numerical order. +It may be customised with the +.Ar window-status-format +and +.Ar window-status-current-format +options. +The flag is one of the following symbols appended to the window name: +.Bl -column "Symbol" "Meaning" -offset indent +.It Sy "Symbol" Ta Sy "Meaning" +.It Li "*" Ta "Denotes the current window." +.It Li "-" Ta "Marks the last window (previously selected)." +.It Li "#" Ta "Window is monitored and activity has been detected." +.It Li "!" Ta "A bell has occurred in the window." +.It Li "+" Ta "Window is monitored for content and it has appeared." +.It Li "~" Ta "The window has been silent for the monitor-silence interval." +.El +.Pp +The # symbol relates to the +.Ic monitor-activity +and + to the +.Ic monitor-content +window options. +The window name is printed in inverted colours if an alert (bell, activity or +content) is present. +.Pp +The colour and attributes of the status line may be configured, the entire +status line using the +.Ic status-attr , +.Ic status-fg +and +.Ic status-bg +session options and individual windows using the +.Ic window-status-attr , +.Ic window-status-fg +and +.Ic window-status-bg +window options. +.Pp +The status line is automatically refreshed at interval if it has changed, the +interval may be controlled with the +.Ic status-interval +session option. +.Pp +Commands related to the status line are as follows: +.Bl -tag -width Ds +.It Xo Ic command-prompt +.Op Fl I Ar inputs +.Op Fl p Ar prompts +.Op Fl t Ar target-client +.Op Ar template +.Xc +Open the command prompt in a client. +This may be used from inside +.Nm +to execute commands interactively. +.Pp +If +.Ar template +is specified, it is used as the command. +If present, +.Fl I +is a comma-separated list of the initial text for each prompt. +If +.Fl p +is given, +.Ar prompts +is a comma-separated list of prompts which are displayed in order; otherwise +a single prompt is displayed, constructed from +.Ar template +if it is present, or +.Ql \&: +if not. +.Pp +Both +.Ar inputs +and +.Ar prompts +may contain the special character sequences supported by the +.Ic status-left +option. +.Pp +Before the command is executed, the first occurrence of the string +.Ql %% +and all occurrences of +.Ql %1 +are replaced by the response to the first prompt, the second +.Ql %% +and all +.Ql %2 +are replaced with the response to the second prompt, and so on for further +prompts. +Up to nine prompt responses may be replaced +.Po +.Ql %1 +to +.Ql %9 +.Pc . +.It Xo Ic confirm-before +.Op Fl p Ar prompt +.Op Fl t Ar target-client +.Ar command +.Xc +.D1 (alias: Ic confirm ) +Ask for confirmation before executing +.Ar command . +If +.Fl p +is given, +.Ar prompt +is the prompt to display; otherwise a prompt is constructed from +.Ar command . +It may contain the special character sequences supported by the +.Ic status-left +option. +.Pp +This command works only from inside +.Nm . +.It Xo Ic display-message +.Op Fl p +.Op Fl c Ar target-client +.Op Fl t Ar target-pane +.Op Ar message +.Xc +.D1 (alias: Ic display ) +Display a message. +If +.Fl p +is given, the output is printed to stdout, otherwise it is displayed in the +.Ar target-client +status line. +The format of +.Ar message +is as for +.Ic status-left , +with the exception that #() are not handled; information is taken from +.Ar target-pane +if +.Fl t +is given, otherwise the active pane for the session attached to +.Ar target-client . +.El +.Sh BUFFERS +.Nm +maintains a stack of +.Em paste buffers . +Up to the value of the +.Ic buffer-limit +option are kept; when a new buffer is added, the buffer at the bottom of the +stack is removed. +Buffers may be added using +.Ic copy-mode +or the +.Ic set-buffer +command, and pasted into a window using the +.Ic paste-buffer +command. +.Pp +A configurable history buffer is also maintained for each window. +By default, up to 2000 lines are kept; this can be altered with the +.Ic history-limit +option (see the +.Ic set-option +command above). +.Pp +The buffer commands are as follows: +.Bl -tag -width Ds +.It Xo +.Ic choose-buffer +.Op Fl t Ar target-window +.Op Ar template +.Xc +Put a window into buffer choice mode, where a buffer may be chosen +interactively from a list. +After a buffer is selected, +.Ql %% +is replaced by the buffer index in +.Ar template +and the result executed as a command. +If +.Ar template +is not given, "paste-buffer -b '%%'" is used. +This command works only from inside +.Nm . +.It Ic clear-history Op Fl t Ar target-pane +.D1 (alias: Ic clearhist ) +Remove and free the history for the specified pane. +.It Ic delete-buffer Op Fl b Ar buffer-index +.D1 (alias: Ic deleteb ) +Delete the buffer at +.Ar buffer-index , +or the top buffer if not specified. +.It Ic list-buffers +.D1 (alias: Ic lsb ) +List the global buffers. +.It Xo Ic load-buffer +.Op Fl b Ar buffer-index +.Ar path +.Xc +.D1 (alias: Ic loadb ) +Load the contents of the specified paste buffer from +.Ar path . +.It Xo Ic paste-buffer +.Op Fl dr +.Op Fl b Ar buffer-index +.Op Fl s Ar separator +.Op Fl t Ar target-pane +.Xc +.D1 (alias: Ic pasteb ) +Insert the contents of a paste buffer into the specified pane. +If not specified, paste into the current one. +With +.Fl d , +also delete the paste buffer from the stack. +When output, any linefeed (LF) characters in the paste buffer are replaced with +a separator, by default carriage return (CR). +A custom separator may be specified using the +.Fl s +flag. +The +.Fl r +flag means to do no replacement (equivalent to a separator of LF). +.It Xo Ic save-buffer +.Op Fl a +.Op Fl b Ar buffer-index +.Ar path +.Xc +.D1 (alias: Ic saveb ) +Save the contents of the specified paste buffer to +.Ar path . +The +.Fl a +option appends to rather than overwriting the file. +.It Xo Ic set-buffer +.Op Fl b Ar buffer-index +.Ar data +.Xc +.D1 (alias: Ic setb ) +Set the contents of the specified buffer to +.Ar data . +.It Xo Ic show-buffer +.Op Fl b Ar buffer-index +.Xc +.D1 (alias: Ic showb ) +Display the contents of the specified buffer. +.El +.Sh MISCELLANEOUS +Miscellaneous commands are as follows: +.Bl -tag -width Ds +.It Ic clock-mode Op Fl t Ar target-pane +Display a large clock. +.It Ic if-shell Ar shell-command command +.D1 (alias: Ic if ) +Execute +.Ar command +if +.Ar shell-command +returns success. +.It Ic lock-server +.D1 (alias: Ic lock ) +Lock each client individually by running the command specified by the +.Ic lock-command +option. +.It Ic run-shell Ar shell-command +.D1 (alias: Ic run ) +Execute +.Ar shell-command +in the background without creating a window. +After it finishes, any output to stdout is displayed in copy mode. +If the command doesn't return success, the exit status is also displayed. +.It Ic server-info +.D1 (alias: Ic info ) +Show server information and terminal details. +.El +.Sh TERMINFO EXTENSIONS +.Nm +understands some extensions to +.Xr terminfo 5 : +.Bl -tag -width Ds +.It Em Cc , Cr +Set the cursor colour. +The first takes a single string argument and is used to set the colour; +the second takes no arguments and restores the default cursor colour. +If set, a sequence such as this may be used +to change the cursor colour from inside +.Nm : +.Bd -literal -offset indent +$ printf '\e033]12;red\e033\e\e' +.Ed +.It Em Cs , Csr +Change the cursor style. +If set, a sequence such as this may be used +to change the cursor to an underline: +.Bd -literal -offset indent +$ printf '\e033[4 q' +.Ed +.Pp +If +.Em Csr +is set, it will be used to reset the cursor style instead +of +.Em Cs . +.It Em \&Ms +This sequence can be used by +.Nm +to store the current buffer in the host terminal's selection (clipboard). +See the +.Em set-clipboard +option above and the +.Xr xterm 1 +man page. +.El +.Sh FILES +.Bl -tag -width "/etc/tmux.confXXX" -compact +.It Pa ~/.tmux.conf +Default +.Nm +configuration file. +.It Pa /etc/tmux.conf +System-wide configuration file. +.El +.Sh EXAMPLES +To create a new +.Nm +session running +.Xr vi 1 : +.Pp +.Dl $ tmux new-session vi +.Pp +Most commands have a shorter form, known as an alias. +For new-session, this is +.Ic new : +.Pp +.Dl $ tmux new vi +.Pp +Alternatively, the shortest unambiguous form of a command is accepted. +If there are several options, they are listed: +.Bd -literal -offset indent +$ tmux n +ambiguous command: n, could be: new-session, new-window, next-window +.Ed +.Pp +Within an active session, a new window may be created by typing +.Ql C-b c +(Ctrl +followed by the +.Ql b +key +followed by the +.Ql c +key). +.Pp +Windows may be navigated with: +.Ql C-b 0 +(to select window 0), +.Ql C-b 1 +(to select window 1), and so on; +.Ql C-b n +to select the next window; and +.Ql C-b p +to select the previous window. +.Pp +A session may be detached using +.Ql C-b d +(or by an external event such as +.Xr ssh 1 +disconnection) and reattached with: +.Pp +.Dl $ tmux attach-session +.Pp +Typing +.Ql C-b \&? +lists the current key bindings in the current window; up and down may be used +to navigate the list or +.Ql q +to exit from it. +.Pp +Commands to be run when the +.Nm +server is started may be placed in the +.Pa ~/.tmux.conf +configuration file. +Common examples include: +.Pp +Changing the default prefix key: +.Bd -literal -offset indent +set-option -g prefix C-a +unbind-key C-b +bind-key C-a send-prefix +.Ed +.Pp +Turning the status line off, or changing its colour: +.Bd -literal -offset indent +set-option -g status off +set-option -g status-bg blue +.Ed +.Pp +Setting other options, such as the default command, +or locking after 30 minutes of inactivity: +.Bd -literal -offset indent +set-option -g default-command "exec /bin/ksh" +set-option -g lock-after-time 1800 +.Ed +.Pp +Creating new key bindings: +.Bd -literal -offset indent +bind-key b set-option status +bind-key / command-prompt "split-window 'exec man %%'" +bind-key S command-prompt "new-window -n %1 'ssh %1'" +.Ed +.Sh SEE ALSO +.Xr pty 4 +.Sh AUTHORS +.An Nicholas Marriott Aq Mt nicm@users.sourceforge.net diff --git a/external/bsd/tmux/dist/tmux.c b/external/bsd/tmux/dist/tmux.c new file mode 100644 index 000000000..5ba7ed97d --- /dev/null +++ b/external/bsd/tmux/dist/tmux.c @@ -0,0 +1,413 @@ +/* $Id: tmux.c,v 1.2 2012/05/31 19:14:56 martin Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "tmux.h" + +#if defined(DEBUG) && defined(__OpenBSD__) +extern char *malloc_options; +#endif + +struct options global_options; /* server options */ +struct options global_s_options; /* session options */ +struct options global_w_options; /* window options */ +struct environ global_environ; + +struct event_base *ev_base; + +char *cfg_file; +char *shell_cmd; +int debug_level; +time_t start_time; +char socket_path[MAXPATHLEN]; +int login_shell; +char *environ_path; +pid_t environ_pid = -1; +int environ_idx = -1; + +__dead void usage(void); +void parseenvironment(void); +char *makesocketpath(const char *); + +#ifndef HAVE___PROGNAME +char *__progname = (char *) "tmux"; +#endif + +__dead void +usage(void) +{ + fprintf(stderr, + "usage: %s [-28lquvV] [-c shell-command] [-f file] [-L socket-name]\n" + " [-S socket-path] [command [flags]]\n", + __progname); + exit(1); +} + +void +logfile(const char *name) +{ + char *path; + + log_close(); + if (debug_level > 0) { + xasprintf(&path, "tmux-%s-%ld.log", name, (long) getpid()); + log_open_file(debug_level, path); + xfree(path); + } +} + +const char * +getshell(void) +{ + struct passwd *pw; + const char *shell; + + shell = getenv("SHELL"); + if (checkshell(shell)) + return (shell); + + pw = getpwuid(getuid()); + if (pw != NULL && checkshell(pw->pw_shell)) + return (pw->pw_shell); + + return (_PATH_BSHELL); +} + +int +checkshell(const char *shell) +{ + if (shell == NULL || *shell == '\0' || areshell(shell)) + return (0); + if (access(shell, X_OK) != 0) + return (0); + return (1); +} + +int +areshell(const char *shell) +{ + const char *progname, *ptr; + + if ((ptr = strrchr(shell, '/')) != NULL) + ptr++; + else + ptr = shell; + progname = __progname; + if (*progname == '-') + progname++; + if (strcmp(ptr, progname) == 0) + return (1); + return (0); +} + +void +parseenvironment(void) +{ + char *env, path[256]; + long pid; + int idx; + + if ((env = getenv("TMUX")) == NULL) + return; + + if (sscanf(env, "%255[^,],%ld,%d", path, &pid, &idx) != 3) + return; + environ_path = xstrdup(path); + environ_pid = pid; + environ_idx = idx; +} + +char * +makesocketpath(const char *label) +{ + char base[MAXPATHLEN], *path, *s; + struct stat sb; + u_int uid; + + uid = getuid(); + if ((s = getenv("TMPDIR")) == NULL || *s == '\0') + xsnprintf(base, sizeof base, "%s/tmux-%u", _PATH_TMP, uid); + else + xsnprintf(base, sizeof base, "%s/tmux-%u", s, uid); + + if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) + return (NULL); + + if (lstat(base, &sb) != 0) + return (NULL); + if (!S_ISDIR(sb.st_mode)) { + errno = ENOTDIR; + return (NULL); + } + if (sb.st_uid != uid || (sb.st_mode & (S_IRWXG|S_IRWXO)) != 0) { + errno = EACCES; + return (NULL); + } + + xasprintf(&path, "%s/%s", base, label); + return (path); +} + +void +setblocking(int fd, int state) +{ + int mode; + + if ((mode = fcntl(fd, F_GETFL)) != -1) { + if (!state) + mode |= O_NONBLOCK; + else + mode &= ~O_NONBLOCK; + fcntl(fd, F_SETFL, mode); + } +} + +__dead void +shell_exec(const char *shell, const char *shellcmd) +{ + const char *shellname, *ptr; + char *argv0; + + ptr = strrchr(shell, '/'); + if (ptr != NULL && *(ptr + 1) != '\0') + shellname = ptr + 1; + else + shellname = shell; + if (login_shell) + xasprintf(&argv0, "-%s", shellname); + else + xasprintf(&argv0, "%s", shellname); + setenv("SHELL", shell, 1); + + setblocking(STDIN_FILENO, 1); + setblocking(STDOUT_FILENO, 1); + setblocking(STDERR_FILENO, 1); + closefrom(STDERR_FILENO + 1); + + execl(shell, argv0, "-c", shellcmd, (char *) NULL); + fatal("execl failed"); +} + +static void +init_std_fds(void) +{ + int fd; + + /* + * Make sure the standard file descriptors are populated, so we + * don't end up forwarding (for example) the event descriptor + * instead of stdin to the server. + */ + + while ((fd = open("/dev/null", O_RDWR)) <= 2) + ; + close(fd); +} + +int +main(int argc, char **argv) +{ + struct passwd *pw; + struct keylist *keylist; + char *s, *path, *label, *home, **var; + int opt, flags, quiet, keys; + +#if defined(DEBUG) && defined(__OpenBSD__) + malloc_options = (char *) "AFGJPX"; +#endif + + quiet = flags = 0; + label = path = NULL; + login_shell = (**argv == '-'); + while ((opt = getopt(argc, argv, "28c:df:lL:qS:uUvV")) != -1) { + switch (opt) { + case '2': + flags |= IDENTIFY_256COLOURS; + flags &= ~IDENTIFY_88COLOURS; + break; + case '8': + flags |= IDENTIFY_88COLOURS; + flags &= ~IDENTIFY_256COLOURS; + break; + case 'c': + if (shell_cmd != NULL) + xfree(shell_cmd); + shell_cmd = xstrdup(optarg); + break; + case 'V': + printf("%s %s\n", __progname, VERSION); + exit(0); + case 'f': + if (cfg_file != NULL) + xfree(cfg_file); + cfg_file = xstrdup(optarg); + break; + case 'l': + login_shell = 1; + break; + case 'L': + if (label != NULL) + xfree(label); + label = xstrdup(optarg); + break; + case 'q': + quiet = 1; + break; + case 'S': + if (path != NULL) + xfree(path); + path = xstrdup(optarg); + break; + case 'u': + flags |= IDENTIFY_UTF8; + break; + case 'v': + debug_level++; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (shell_cmd != NULL && argc != 0) + usage(); + + init_std_fds(); + log_open_tty(debug_level); + + if (!(flags & IDENTIFY_UTF8)) { + /* + * If the user has set whichever of LC_ALL, LC_CTYPE or LANG + * exist (in that order) to contain UTF-8, it is a safe + * assumption that either they are using a UTF-8 terminal, or + * if not they know that output from UTF-8-capable programs may + * be wrong. + */ + if ((s = getenv("LC_ALL")) == NULL) { + if ((s = getenv("LC_CTYPE")) == NULL) + s = getenv("LANG"); + } + if (s != NULL && (strcasestr(s, "UTF-8") != NULL || + strcasestr(s, "UTF8") != NULL)) + flags |= IDENTIFY_UTF8; + } + + environ_init(&global_environ); + for (var = environ; *var != NULL; var++) + environ_put(&global_environ, *var); + + options_init(&global_options, NULL); + options_table_populate_tree(server_options_table, &global_options); + options_set_number(&global_options, "quiet", quiet); + + options_init(&global_s_options, NULL); + options_table_populate_tree(session_options_table, &global_s_options); + options_set_string(&global_s_options, "default-shell", "%s", getshell()); + + options_init(&global_w_options, NULL); + options_table_populate_tree(window_options_table, &global_w_options); + + /* Set the prefix option (its a list, so not in the table). */ + keylist = xmalloc(sizeof *keylist); + ARRAY_INIT(keylist); + ARRAY_ADD(keylist, '\002'); + options_set_data(&global_s_options, "prefix", keylist, xfree); + + /* Enable UTF-8 if the first client is on UTF-8 terminal. */ + if (flags & IDENTIFY_UTF8) { + options_set_number(&global_s_options, "status-utf8", 1); + options_set_number(&global_s_options, "mouse-utf8", 1); + options_set_number(&global_w_options, "utf8", 1); + } + + /* Override keys to vi if VISUAL or EDITOR are set. */ + if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) { + if (strrchr(s, '/') != NULL) + s = strrchr(s, '/') + 1; + if (strstr(s, "vi") != NULL) + keys = MODEKEY_VI; + else + keys = MODEKEY_EMACS; + options_set_number(&global_s_options, "status-keys", keys); + options_set_number(&global_w_options, "mode-keys", keys); + } + + /* Locate the configuration file. */ + if (cfg_file == NULL) { + home = getenv("HOME"); + if (home == NULL || *home == '\0') { + pw = getpwuid(getuid()); + if (pw != NULL) + home = pw->pw_dir; + } + xasprintf(&cfg_file, "%s/%s", home, DEFAULT_CFG); + if (access(cfg_file, R_OK) != 0 && errno == ENOENT) { + xfree(cfg_file); + cfg_file = NULL; + } + } + + /* + * Figure out the socket path. If specified on the command-line with -S + * or -L, use it, otherwise try $TMUX or assume -L default. + */ + parseenvironment(); + if (path == NULL) { + /* If no -L, use the environment. */ + if (label == NULL) { + if (environ_path != NULL) + path = xstrdup(environ_path); + else + label = xstrdup("default"); + } + + /* -L or default set. */ + if (label != NULL) { + if ((path = makesocketpath(label)) == NULL) { + log_warn("can't create socket"); + exit(1); + } + } + } + if (label != NULL) + xfree(label); + if (realpath(path, socket_path) == NULL) + strlcpy(socket_path, path, sizeof socket_path); + xfree(path); + +#ifdef HAVE_SETPROCTITLE + /* Set process title. */ + setproctitle("%s (%s)", __progname, socket_path); +#endif + + /* Pass control to the client. */ + ev_base = osdep_event_init(); + exit(client_main(argc, argv, flags)); +} diff --git a/external/bsd/tmux/dist/tmux.h b/external/bsd/tmux/dist/tmux.h new file mode 100644 index 000000000..e9b7aba4a --- /dev/null +++ b/external/bsd/tmux/dist/tmux.h @@ -0,0 +1,2077 @@ +/* $Id: tmux.h,v 1.3 2011/09/17 01:50:08 christos Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef TMUX_H +#define TMUX_H + +#define PROTOCOL_VERSION 6 + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "array.h" + +#include "compat.h" + +extern char *__progname; +extern char **environ; + +/* Default configuration files. */ +#define DEFAULT_CFG ".tmux.conf" +#define SYSTEM_CFG "/etc/tmux.conf" + +/* Default prompt history length. */ +#define PROMPT_HISTORY 100 + +/* + * Minimum layout cell size, NOT including separator line. The scroll region + * cannot be one line in height so this must be at least two. + */ +#define PANE_MINIMUM 2 + +/* Automatic name refresh interval, in milliseconds. */ +#define NAME_INTERVAL 500 + +/* Maximum data to buffer for output before suspending writing to a tty. */ +#define BACKOFF_THRESHOLD 16384 + +/* + * Maximum sizes of strings in message data. Don't forget to bump + * PROTOCOL_VERSION if any of these change! + */ +#define COMMAND_LENGTH 2048 /* packed argv size */ +#define TERMINAL_LENGTH 128 /* length of TERM environment variable */ +#define ENVIRON_LENGTH 1024 /* environment variable length */ + +/* + * UTF-8 data size. This must be big enough to hold combined characters as well + * as single. + */ +#define UTF8_SIZE 9 + +/* Fatal errors. */ +#define fatal(msg) log_fatal("%s: %s", __func__, msg); +#define fatalx(msg) log_fatalx("%s: %s", __func__, msg); + +/* Definition to shut gcc up about unused arguments. */ +#define unused __attribute__ ((unused)) + +/* Attribute to make gcc check printf-like arguments. */ +#define printflike1 __attribute__ ((format (printf, 1, 2))) +#define printflike2 __attribute__ ((format (printf, 2, 3))) +#define printflike3 __attribute__ ((format (printf, 3, 4))) +#define printflike4 __attribute__ ((format (printf, 4, 5))) +#define printflike5 __attribute__ ((format (printf, 5, 6))) + +/* Number of items in array. */ +#ifndef nitems +#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) +#endif + +/* Bell option values. */ +#define BELL_NONE 0 +#define BELL_ANY 1 +#define BELL_CURRENT 2 + +/* Special key codes. */ +#define KEYC_NONE 0xfff +#define KEYC_BASE 0x1000 + +/* Key modifier bits. */ +#define KEYC_ESCAPE 0x2000 +#define KEYC_CTRL 0x4000 +#define KEYC_SHIFT 0x8000 +#define KEYC_PREFIX 0x10000 + +/* Mask to obtain key w/o modifiers. */ +#define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT|KEYC_PREFIX) +#define KEYC_MASK_KEY (~KEYC_MASK_MOD) + +/* Other key codes. */ +enum key_code { + /* Mouse key. */ + KEYC_MOUSE = KEYC_BASE, + + /* Backspace key. */ + KEYC_BSPACE, + + /* Function keys. */ + KEYC_F1, + KEYC_F2, + KEYC_F3, + KEYC_F4, + KEYC_F5, + KEYC_F6, + KEYC_F7, + KEYC_F8, + KEYC_F9, + KEYC_F10, + KEYC_F11, + KEYC_F12, + KEYC_F13, + KEYC_F14, + KEYC_F15, + KEYC_F16, + KEYC_F17, + KEYC_F18, + KEYC_F19, + KEYC_F20, + KEYC_IC, + KEYC_DC, + KEYC_HOME, + KEYC_END, + KEYC_NPAGE, + KEYC_PPAGE, + KEYC_BTAB, + + /* Arrow keys. */ + KEYC_UP, + KEYC_DOWN, + KEYC_LEFT, + KEYC_RIGHT, + + /* Numeric keypad. */ + KEYC_KP_SLASH, + KEYC_KP_STAR, + KEYC_KP_MINUS, + KEYC_KP_SEVEN, + KEYC_KP_EIGHT, + KEYC_KP_NINE, + KEYC_KP_PLUS, + KEYC_KP_FOUR, + KEYC_KP_FIVE, + KEYC_KP_SIX, + KEYC_KP_ONE, + KEYC_KP_TWO, + KEYC_KP_THREE, + KEYC_KP_ENTER, + KEYC_KP_ZERO, + KEYC_KP_PERIOD, +}; + +/* Termcap codes. */ +enum tty_code_code { + TTYC_AX = 0, + TTYC_ACSC, /* acs_chars, ac */ + TTYC_BEL, /* bell, bl */ + TTYC_BLINK, /* enter_blink_mode, mb */ + TTYC_BOLD, /* enter_bold_mode, md */ + TTYC_CC, /* set colour cursor, Cc */ + TTYC_CIVIS, /* cursor_invisible, vi */ + TTYC_CLEAR, /* clear_screen, cl */ + TTYC_CNORM, /* cursor_normal, ve */ + TTYC_COLORS, /* max_colors, Co */ + TTYC_CR, /* restore cursor colour, Cr */ + TTYC_CS1, /* set cursor style, Cs */ + TTYC_CSR, /* change_scroll_region, cs */ + TTYC_CSR1, /* reset cursor style, Csr */ + TTYC_CUB, /* parm_left_cursor, LE */ + TTYC_CUB1, /* cursor_left, le */ + TTYC_CUD, /* parm_down_cursor, DO */ + TTYC_CUD1, /* cursor_down, do */ + TTYC_CUF, /* parm_right_cursor, RI */ + TTYC_CUF1, /* cursor_right, nd */ + TTYC_CUP, /* cursor_address, cm */ + TTYC_CUU, /* parm_up_cursor, UP */ + TTYC_CUU1, /* cursor_up, up */ + TTYC_DCH, /* parm_dch, DC */ + TTYC_DCH1, /* delete_character, dc */ + TTYC_DIM, /* enter_dim_mode, mh */ + TTYC_DL, /* parm_delete_line, DL */ + TTYC_DL1, /* delete_line, dl */ + TTYC_EL, /* clr_eol, ce */ + TTYC_EL1, /* clr_bol, cb */ + TTYC_ENACS, /* ena_acs, eA */ + TTYC_FSL, /* from_status_line, fsl */ + TTYC_HOME, /* cursor_home, ho */ + TTYC_HPA, /* column_address, ch */ + TTYC_ICH, /* parm_ich, IC */ + TTYC_ICH1, /* insert_character, ic */ + TTYC_IL, /* parm_insert_line, IL */ + TTYC_IL1, /* insert_line, il */ + TTYC_INVIS, /* enter_secure_mode, mk */ + TTYC_IS1, /* init_1string, i1 */ + TTYC_IS2, /* init_2string, i2 */ + TTYC_IS3, /* init_3string, i3 */ + TTYC_KCBT, /* key_btab, kB */ + TTYC_KCUB1, /* key_left, kl */ + TTYC_KCUD1, /* key_down, kd */ + TTYC_KCUF1, /* key_right, kr */ + TTYC_KCUU1, /* key_up, ku */ + TTYC_KDC2, + TTYC_KDC3, + TTYC_KDC4, + TTYC_KDC5, + TTYC_KDC6, + TTYC_KDC7, + TTYC_KDCH1, /* key_dc, kD */ + TTYC_KDN2, + TTYC_KDN3, + TTYC_KDN4, + TTYC_KDN5, + TTYC_KDN6, + TTYC_KDN7, + TTYC_KEND, /* key_end, ke */ + TTYC_KEND2, + TTYC_KEND3, + TTYC_KEND4, + TTYC_KEND5, + TTYC_KEND6, + TTYC_KEND7, + TTYC_KF1, /* key_f1, k1 */ + TTYC_KF10, /* key_f10, k; */ + TTYC_KF11, /* key_f11, F1 */ + TTYC_KF12, /* key_f12, F2 */ + TTYC_KF13, /* key_f13, F3 */ + TTYC_KF14, /* key_f14, F4 */ + TTYC_KF15, /* key_f15, F5 */ + TTYC_KF16, /* key_f16, F6 */ + TTYC_KF17, /* key_f17, F7 */ + TTYC_KF18, /* key_f18, F8 */ + TTYC_KF19, /* key_f19, F9 */ + TTYC_KF2, /* key_f2, k2 */ + TTYC_KF20, /* key_f20, F10 */ + TTYC_KF3, /* key_f3, k3 */ + TTYC_KF4, /* key_f4, k4 */ + TTYC_KF5, /* key_f5, k5 */ + TTYC_KF6, /* key_f6, k6 */ + TTYC_KF7, /* key_f7, k7 */ + TTYC_KF8, /* key_f8, k8 */ + TTYC_KF9, /* key_f9, k9 */ + TTYC_KHOM2, + TTYC_KHOM3, + TTYC_KHOM4, + TTYC_KHOM5, + TTYC_KHOM6, + TTYC_KHOM7, + TTYC_KHOME, /* key_home, kh */ + TTYC_KIC2, + TTYC_KIC3, + TTYC_KIC4, + TTYC_KIC5, + TTYC_KIC6, + TTYC_KIC7, + TTYC_KICH1, /* key_ic, kI */ + TTYC_KLFT2, + TTYC_KLFT3, + TTYC_KLFT4, + TTYC_KLFT5, + TTYC_KLFT6, + TTYC_KLFT7, + TTYC_KMOUS, /* key_mouse, Km */ + TTYC_KNP, /* key_npage, kN */ + TTYC_KNXT2, + TTYC_KNXT3, + TTYC_KNXT4, + TTYC_KNXT5, + TTYC_KNXT6, + TTYC_KNXT7, + TTYC_KPP, /* key_ppage, kP */ + TTYC_KPRV2, + TTYC_KPRV3, + TTYC_KPRV4, + TTYC_KPRV5, + TTYC_KPRV6, + TTYC_KPRV7, + TTYC_KRIT2, + TTYC_KRIT3, + TTYC_KRIT4, + TTYC_KRIT5, + TTYC_KRIT6, + TTYC_KRIT7, + TTYC_KUP2, + TTYC_KUP3, + TTYC_KUP4, + TTYC_KUP5, + TTYC_KUP6, + TTYC_KUP7, + TTYC_MS, /* modify xterm(1) selection */ + TTYC_OP, /* orig_pair, op */ + TTYC_REV, /* enter_reverse_mode, mr */ + TTYC_RI, /* scroll_reverse, sr */ + TTYC_RMACS, /* exit_alt_charset_mode */ + TTYC_RMCUP, /* exit_ca_mode, te */ + TTYC_RMIR, /* exit_insert_mode, ei */ + TTYC_RMKX, /* keypad_local, ke */ + TTYC_SETAB, /* set_a_background, AB */ + TTYC_SETAF, /* set_a_foreground, AF */ + TTYC_SGR0, /* exit_attribute_mode, me */ + TTYC_SITM, /* enter_italics_mode, it */ + TTYC_SMACS, /* enter_alt_charset_mode, as */ + TTYC_SMCUP, /* enter_ca_mode, ti */ + TTYC_SMIR, /* enter_insert_mode, im */ + TTYC_SMKX, /* keypad_xmit, ks */ + TTYC_SMSO, /* enter_standout_mode, so */ + TTYC_SMUL, /* enter_underline_mode, us */ + TTYC_TSL, /* to_status_line, tsl */ + TTYC_VPA, /* row_address, cv */ + TTYC_XENL, /* eat_newline_glitch, xn */ + TTYC_XT, /* xterm(1)-compatible title, XT */ +}; +#define NTTYCODE (TTYC_XT + 1) + +/* Termcap types. */ +enum tty_code_type { + TTYCODE_NONE = 0, + TTYCODE_STRING, + TTYCODE_NUMBER, + TTYCODE_FLAG, +}; + +/* Termcap code. */ +struct tty_code { + enum tty_code_type type; + union { + char *string; + int number; + int flag; + } value; +}; + +/* Entry in terminal code table. */ +struct tty_term_code_entry { + enum tty_code_code code; + enum tty_code_type type; + const char *name; +}; + +/* Message codes. */ +enum msgtype { + MSG_COMMAND, + MSG_DETACH, + MSG_ERROR, + MSG_EXIT, + MSG_EXITED, + MSG_EXITING, + MSG_IDENTIFY, + MSG_PRINT, + MSG_READY, + MSG_RESIZE, + MSG_SHUTDOWN, + MSG_SUSPEND, + MSG_VERSION, + MSG_WAKEUP, + MSG_ENVIRON, + MSG_UNLOCK, + MSG_LOCK, + MSG_SHELL, + MSG_STDERR, + MSG_STDOUT, + MSG_DETACHKILL +}; + +/* + * Message data. + * + * Don't forget to bump PROTOCOL_VERSION if any of these change! + */ +struct msg_command_data { + pid_t pid; /* PID from $TMUX or -1 */ + int idx; /* index from $TMUX or -1 */ + + int argc; + char argv[COMMAND_LENGTH]; +}; + +struct msg_identify_data { + char cwd[MAXPATHLEN]; + + char term[TERMINAL_LENGTH]; + +#define IDENTIFY_UTF8 0x1 +#define IDENTIFY_256COLOURS 0x2 +#define IDENTIFY_88COLOURS 0x4 + int flags; +}; + +struct msg_lock_data { + char cmd[COMMAND_LENGTH]; +}; + +struct msg_environ_data { + char var[ENVIRON_LENGTH]; +}; + +struct msg_shell_data { + char shell[MAXPATHLEN]; +}; + +struct msg_exit_data { + int retcode; +}; + +/* Mode key commands. */ +enum mode_key_cmd { + MODEKEY_NONE, + MODEKEY_OTHER, + + /* Editing keys. */ + MODEKEYEDIT_BACKSPACE, + MODEKEYEDIT_CANCEL, + MODEKEYEDIT_COMPLETE, + MODEKEYEDIT_CURSORLEFT, + MODEKEYEDIT_CURSORRIGHT, + MODEKEYEDIT_DELETE, + MODEKEYEDIT_DELETELINE, + MODEKEYEDIT_DELETETOENDOFLINE, + MODEKEYEDIT_ENDOFLINE, + MODEKEYEDIT_ENTER, + MODEKEYEDIT_HISTORYDOWN, + MODEKEYEDIT_HISTORYUP, + MODEKEYEDIT_PASTE, + MODEKEYEDIT_STARTOFLINE, + MODEKEYEDIT_SWITCHMODE, + MODEKEYEDIT_SWITCHMODEAPPEND, + MODEKEYEDIT_TRANSPOSECHARS, + + /* Menu (choice) keys. */ + MODEKEYCHOICE_CANCEL, + MODEKEYCHOICE_CHOOSE, + MODEKEYCHOICE_DOWN, + MODEKEYCHOICE_PAGEDOWN, + MODEKEYCHOICE_PAGEUP, + MODEKEYCHOICE_SCROLLDOWN, + MODEKEYCHOICE_SCROLLUP, + MODEKEYCHOICE_UP, + + /* Copy keys. */ + MODEKEYCOPY_BACKTOINDENTATION, + MODEKEYCOPY_BOTTOMLINE, + MODEKEYCOPY_CANCEL, + MODEKEYCOPY_CLEARSELECTION, + MODEKEYCOPY_COPYLINE, + MODEKEYCOPY_COPYENDOFLINE, + MODEKEYCOPY_COPYSELECTION, + MODEKEYCOPY_DOWN, + MODEKEYCOPY_ENDOFLINE, + MODEKEYCOPY_GOTOLINE, + MODEKEYCOPY_HALFPAGEDOWN, + MODEKEYCOPY_HALFPAGEUP, + MODEKEYCOPY_HISTORYBOTTOM, + MODEKEYCOPY_HISTORYTOP, + MODEKEYCOPY_JUMP, + MODEKEYCOPY_JUMPAGAIN, + MODEKEYCOPY_JUMPREVERSE, + MODEKEYCOPY_JUMPBACK, + MODEKEYCOPY_LEFT, + MODEKEYCOPY_MIDDLELINE, + MODEKEYCOPY_NEXTPAGE, + MODEKEYCOPY_NEXTSPACE, + MODEKEYCOPY_NEXTSPACEEND, + MODEKEYCOPY_NEXTWORD, + MODEKEYCOPY_NEXTWORDEND, + MODEKEYCOPY_PREVIOUSPAGE, + MODEKEYCOPY_PREVIOUSSPACE, + MODEKEYCOPY_PREVIOUSWORD, + MODEKEYCOPY_RECTANGLETOGGLE, + MODEKEYCOPY_RIGHT, + MODEKEYCOPY_SCROLLDOWN, + MODEKEYCOPY_SCROLLUP, + MODEKEYCOPY_SEARCHAGAIN, + MODEKEYCOPY_SEARCHDOWN, + MODEKEYCOPY_SEARCHREVERSE, + MODEKEYCOPY_SEARCHUP, + MODEKEYCOPY_SELECTLINE, + MODEKEYCOPY_STARTNUMBERPREFIX, + MODEKEYCOPY_STARTOFLINE, + MODEKEYCOPY_STARTSELECTION, + MODEKEYCOPY_TOPLINE, + MODEKEYCOPY_UP, +}; + +/* Entry in the default mode key tables. */ +struct mode_key_entry { + int key; + + /* + * Editing mode for vi: 0 is edit mode, keys not in the table are + * returned as MODEKEY_OTHER; 1 is command mode, keys not in the table + * are returned as MODEKEY_NONE. This is also matched on, allowing some + * keys to be bound in edit mode. + */ + int mode; + enum mode_key_cmd cmd; +}; + +/* Data required while mode keys are in use. */ +struct mode_key_data { + struct mode_key_tree *tree; + int mode; +}; +#define MODEKEY_EMACS 0 +#define MODEKEY_VI 1 + +/* Binding between a key and a command. */ +struct mode_key_binding { + int key; + + int mode; + enum mode_key_cmd cmd; + + SPLAY_ENTRY(mode_key_binding) entry; +}; +SPLAY_HEAD(mode_key_tree, mode_key_binding); + +/* Command to string mapping. */ +struct mode_key_cmdstr { + enum mode_key_cmd cmd; + const char *name; +}; + +/* Named mode key table description. */ +struct mode_key_table { + const char *name; + const struct mode_key_cmdstr *cmdstr; + struct mode_key_tree *tree; + const struct mode_key_entry *table; /* default entries */ +}; + +/* Modes. */ +#define MODE_CURSOR 0x1 +#define MODE_INSERT 0x2 +#define MODE_KCURSOR 0x4 +#define MODE_KKEYPAD 0x8 /* set = application, clear = number */ +#define MODE_WRAP 0x10 /* whether lines wrap */ +#define MODE_MOUSE_STANDARD 0x20 +#define MODE_MOUSE_BUTTON 0x40 +#define MODE_MOUSE_ANY 0x80 +#define MODE_MOUSE_UTF8 0x100 + +#define ALL_MOUSE_MODES (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON|MODE_MOUSE_ANY) + +/* + * A single UTF-8 character. + * + * The data member in this must be UTF8_SIZE to allow screen_write_copy to + * reinject stored UTF-8 data back into screen_write_cell after combining (ugh + * XXX XXX). + */ +struct utf8_data { + u_char data[UTF8_SIZE]; + + size_t have; + size_t size; + + u_int width; +}; + +/* Grid output. */ +#if defined(DEBUG) && \ + ((defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || \ + (defined(__GNUC__) && __GNUC__ >= 3)) +#define GRID_DEBUG(gd, fmt, ...) log_debug2("%s: (sx=%u, sy=%u, hsize=%u) " \ + fmt, __func__, (gd)->sx, (gd)->sy, (gd)->hsize, ## __VA_ARGS__) +#else +#define GRID_DEBUG(...) +#endif + +/* Grid attributes. */ +#define GRID_ATTR_BRIGHT 0x1 +#define GRID_ATTR_DIM 0x2 +#define GRID_ATTR_UNDERSCORE 0x4 +#define GRID_ATTR_BLINK 0x8 +#define GRID_ATTR_REVERSE 0x10 +#define GRID_ATTR_HIDDEN 0x20 +#define GRID_ATTR_ITALICS 0x40 +#define GRID_ATTR_CHARSET 0x80 /* alternative character set */ + +/* Grid flags. */ +#define GRID_FLAG_FG256 0x1 +#define GRID_FLAG_BG256 0x2 +#define GRID_FLAG_PADDING 0x4 +#define GRID_FLAG_UTF8 0x8 + +/* Grid line flags. */ +#define GRID_LINE_WRAPPED 0x1 + +/* Grid cell data. */ +struct grid_cell { + u_char attr; + u_char flags; + u_char fg; + u_char bg; + u_char data; +} __packed; + +/* Grid cell UTF-8 data. Used instead of data in grid_cell for UTF-8 cells. */ +struct grid_utf8 { + u_char width; + u_char data[UTF8_SIZE]; +} __packed; + +/* Grid line. */ +struct grid_line { + u_int cellsize; + struct grid_cell *celldata; + + u_int utf8size; + struct grid_utf8 *utf8data; + + int flags; +} __packed; + +/* Entire grid of cells. */ +struct grid { + int flags; +#define GRID_HISTORY 0x1 /* scroll lines into history */ + + u_int sx; + u_int sy; + + u_int hsize; + u_int hlimit; + + struct grid_line *linedata; +}; + +/* Option data structures. */ +struct options_entry { + char *name; + + enum { + OPTIONS_STRING, + OPTIONS_NUMBER, + OPTIONS_DATA, + } type; + + char *str; + long long num; + void *data; + + void (*freefn)(void *); + + SPLAY_ENTRY(options_entry) entry; +}; + +struct options { + SPLAY_HEAD(options_tree, options_entry) tree; + struct options *parent; +}; + +/* Key list for prefix option. */ +ARRAY_DECL(keylist, int); + +/* Scheduled job. */ +struct job { + char *cmd; + pid_t pid; + int status; + + int fd; + struct bufferevent *event; + + void (*callbackfn)(struct job *); + void (*freefn)(void *); + void *data; + + LIST_ENTRY(job) lentry; +}; +LIST_HEAD(joblist, job); + +/* Screen selection. */ +struct screen_sel { + int flag; + int rectflag; + + u_int sx; + u_int sy; + + u_int ex; + u_int ey; + + struct grid_cell cell; +}; + +/* Virtual screen. */ +struct screen { + char *title; + + struct grid *grid; /* grid data */ + + u_int cx; /* cursor x */ + u_int cy; /* cursor y */ + + u_int cstyle; /* cursor style */ + char *ccolour; /* cursor colour string */ + + u_int rupper; /* scroll region top */ + u_int rlower; /* scroll region bottom */ + + int mode; + + bitstr_t *tabs; + + struct screen_sel sel; +}; + +/* Screen write context. */ +struct screen_write_ctx { + struct window_pane *wp; + struct screen *s; +}; + +/* Screen size. */ +#define screen_size_x(s) ((s)->grid->sx) +#define screen_size_y(s) ((s)->grid->sy) +#define screen_hsize(s) ((s)->grid->hsize) +#define screen_hlimit(s) ((s)->grid->hlimit) + +/* Input parser context. */ +struct input_ctx { + struct window_pane *wp; + struct screen_write_ctx ctx; + + struct grid_cell cell; + + struct grid_cell old_cell; + u_int old_cx; + u_int old_cy; + + u_char interm_buf[4]; + size_t interm_len; + + u_char param_buf[64]; + size_t param_len; + + u_char input_buf[256]; + size_t input_len; + + int param_list[24]; /* -1 not present */ + u_int param_list_len; + + struct utf8_data utf8data; + + int ch; + int flags; +#define INPUT_DISCARD 0x1 + + const struct input_state *state; +}; + +/* + * Window mode. Windows can be in several modes and this is used to call the + * right function to handle input and output. + */ +struct session; +struct window; +struct mouse_event; +struct window_mode { + struct screen *(*init)(struct window_pane *); + void (*free)(struct window_pane *); + void (*resize)(struct window_pane *, u_int, u_int); + void (*key)(struct window_pane *, struct session *, int); + void (*mouse)(struct window_pane *, + struct session *, struct mouse_event *); + void (*timer)(struct window_pane *); +}; + +/* Child window structure. */ +struct window_utmp; +struct window_pane { + u_int id; + + struct window *window; + struct layout_cell *layout_cell; + + u_int sx; + u_int sy; + + u_int xoff; + u_int yoff; + + int flags; +#define PANE_REDRAW 0x1 + + char *cmd; + char *shell; + char *cwd; + + pid_t pid; + char tty[TTY_NAME_MAX]; + + int fd; + struct bufferevent *event; + + struct input_ctx ictx; + + int pipe_fd; + struct bufferevent *pipe_event; + size_t pipe_off; + + struct screen *screen; + struct screen base; + + /* Saved in alternative screen mode. */ + u_int saved_cx; + u_int saved_cy; + struct grid *saved_grid; + struct grid_cell saved_cell; + + const struct window_mode *mode; + void *modedata; + + struct window_utmp *utmp; + + TAILQ_ENTRY(window_pane) entry; + RB_ENTRY(window_pane) tree_entry; +}; +TAILQ_HEAD(window_panes, window_pane); +RB_HEAD(window_pane_tree, window_pane); + +/* Window structure. */ +struct window { + char *name; + struct event name_timer; + struct timeval silence_timer; + + struct window_pane *active; + struct window_pane *last; + struct window_panes panes; + + int lastlayout; + struct layout_cell *layout_root; + + u_int sx; + u_int sy; + + int flags; +#define WINDOW_BELL 0x1 +#define WINDOW_ACTIVITY 0x2 +#define WINDOW_REDRAW 0x4 +#define WINDOW_SILENCE 0x8 + + struct options options; + + u_int references; +}; +ARRAY_DECL(windows, struct window *); + +/* Entry on local window list. */ +struct winlink { + int idx; + struct window *window; + + size_t status_width; + struct grid_cell status_cell; + char *status_text; + + int flags; +#define WINLINK_BELL 0x1 +#define WINLINK_ACTIVITY 0x2 +#define WINLINK_CONTENT 0x4 +#define WINLINK_SILENCE 0x8 +#define WINLINK_ALERTFLAGS \ + (WINLINK_BELL|WINLINK_ACTIVITY|WINLINK_CONTENT|WINLINK_SILENCE) + + RB_ENTRY(winlink) entry; + TAILQ_ENTRY(winlink) sentry; +}; +RB_HEAD(winlinks, winlink); +TAILQ_HEAD(winlink_stack, winlink); + +/* Layout direction. */ +enum layout_type { + LAYOUT_LEFTRIGHT, + LAYOUT_TOPBOTTOM, + LAYOUT_WINDOWPANE +}; + +/* Layout cells queue. */ +TAILQ_HEAD(layout_cells, layout_cell); + +/* Layout cell. */ +struct layout_cell { + enum layout_type type; + + struct layout_cell *parent; + + u_int sx; + u_int sy; + + u_int xoff; + u_int yoff; + + struct window_pane *wp; + struct layout_cells cells; + + TAILQ_ENTRY(layout_cell) entry; +}; + +/* Paste buffer. */ +struct paste_buffer { + char *data; + size_t size; +}; +ARRAY_DECL(paste_stack, struct paste_buffer *); + +/* Environment variable. */ +struct environ_entry { + char *name; + char *value; + + RB_ENTRY(environ_entry) entry; +}; +RB_HEAD(environ, environ_entry); + +/* Client session. */ +struct session_group { + TAILQ_HEAD(, session) sessions; + + TAILQ_ENTRY(session_group) entry; +}; +TAILQ_HEAD(session_groups, session_group); + +struct session { + u_int idx; + + char *name; + char *cwd; + + struct timeval creation_time; + struct timeval activity_time; + + u_int sx; + u_int sy; + + struct winlink *curw; + struct winlink_stack lastw; + struct winlinks windows; + + struct options options; + +#define SESSION_UNATTACHED 0x1 /* not attached to any clients */ + int flags; + + struct termios *tio; + + struct environ environ; + + int wlmouse; + + int references; + + TAILQ_ENTRY(session) gentry; + RB_ENTRY(session) entry; +}; +RB_HEAD(sessions, session); +ARRAY_DECL(sessionslist, struct session *); + +/* TTY information. */ +struct tty_key { + char ch; + int key; + + struct tty_key *left; + struct tty_key *right; + + struct tty_key *next; +}; + +struct tty_term { + char *name; + u_int references; + + char acs[UCHAR_MAX + 1][2]; + + struct tty_code codes[NTTYCODE]; + +#define TERM_256COLOURS 0x1 +#define TERM_88COLOURS 0x2 +#define TERM_EARLYWRAP 0x4 + int flags; + + LIST_ENTRY(tty_term) entry; +}; +LIST_HEAD(tty_terms, tty_term); + +struct tty { + char *path; + + u_int sx; + u_int sy; + + u_int cx; + u_int cy; + u_int cstyle; + char *ccolour; + + int mode; + + u_int rlower; + u_int rupper; + + char *termname; + struct tty_term *term; + + int fd; + struct bufferevent *event; + + int log_fd; + + struct termios tio; + + struct grid_cell cell; + +#define TTY_NOCURSOR 0x1 +#define TTY_FREEZE 0x2 +#define TTY_ESCAPE 0x4 +#define TTY_UTF8 0x8 +#define TTY_STARTED 0x10 +#define TTY_OPENED 0x20 +#define TTY_BACKOFF 0x40 + int flags; + + int term_flags; + + void (*key_callback)(int, struct mouse_event *, void *); + void *key_data; + struct event key_timer; + struct tty_key *key_tree; +}; + +/* TTY command context and function pointer. */ +struct tty_ctx { + struct window_pane *wp; + + const struct grid_cell *cell; + const struct grid_utf8 *utf8; + + u_int num; + void *ptr; + + /* + * Cursor and region position before the screen was updated - this is + * where the command should be applied; the values in the screen have + * already been updated. + */ + u_int ocx; + u_int ocy; + + u_int orupper; + u_int orlower; + + /* Saved last cell on line. */ + struct grid_cell last_cell; + struct grid_utf8 last_utf8; + u_int last_width; +}; + +/* + * xterm mouse mode is fairly silly. Buttons are in the bottom two + * bits: 0 button 1; 1 button 2; 2 button 3; 3 buttons released. + * + * Bit 3 is shift; bit 4 is meta; bit 5 control. + * + * Bit 6 is added for mouse buttons 4 and 5. + */ +/* Mouse input. */ +struct mouse_event { + u_int b; +#define MOUSE_1 0 +#define MOUSE_2 1 +#define MOUSE_3 2 +#define MOUSE_UP 3 +#define MOUSE_BUTTON 3 +#define MOUSE_DRAG 32 +#define MOUSE_45 64 +#define MOUSE_RESIZE_PANE 128 /* marker for resizing */ + u_int x; + u_int y; +}; + +/* Saved message entry. */ +struct message_entry { + char *msg; + time_t msg_time; +}; + +/* Status output data from a job. */ +struct status_out { + char *cmd; + char *out; + + RB_ENTRY(status_out) entry; +}; +RB_HEAD(status_out_tree, status_out); + +/* Client connection. */ +struct client { + struct imsgbuf ibuf; + struct event event; + int retcode; + + struct timeval creation_time; + struct timeval activity_time; + + struct environ environ; + + char *title; + char *cwd; + + struct tty tty; + + int stdin_fd; + void *stdin_data; + void (*stdin_callback)(struct client *, void *); + struct bufferevent *stdin_event; + + int stdout_fd; + struct bufferevent *stdout_event; + + int stderr_fd; + struct bufferevent *stderr_event; + + struct event repeat_timer; + + struct status_out_tree status_old; + struct status_out_tree status_new; + struct timeval status_timer; + struct screen status; + +#define CLIENT_TERMINAL 0x1 +#define CLIENT_PREFIX 0x2 +#define CLIENT_EXIT 0x4 +#define CLIENT_REDRAW 0x8 +#define CLIENT_STATUS 0x10 +#define CLIENT_REPEAT 0x20 /* allow command to repeat within repeat time */ +#define CLIENT_SUSPENDED 0x40 +#define CLIENT_BAD 0x80 +#define CLIENT_IDENTIFY 0x100 +#define CLIENT_DEAD 0x200 +#define CLIENT_BORDERS 0x400 +#define CLIENT_READONLY 0x800 +#define CLIENT_BACKOFF 0x1000 +#define CLIENT_REDRAWWINDOW 0x2000 + int flags; + + struct event identify_timer; + + char *message_string; + struct event message_timer; + ARRAY_DECL(, struct message_entry) message_log; + + char *prompt_string; + char *prompt_buffer; + size_t prompt_index; + int (*prompt_callbackfn)(void *, const char *); + void (*prompt_freefn)(void *); + void *prompt_data; + u_int prompt_hindex; + +#define PROMPT_SINGLE 0x1 + int prompt_flags; + + struct mode_key_data prompt_mdata; + + struct session *session; + struct session *last_session; + + struct mouse_event last_mouse; + + int references; +}; +ARRAY_DECL(clients, struct client *); + +/* Parsed arguments. */ +struct args { + bitstr_t *flags; + char *values[SCHAR_MAX]; /* XXX This is awfully big. */ + + int argc; + char **argv; +}; + +/* Key/command line command. */ +struct cmd_ctx { + /* + * curclient is the client where this command was executed if inside + * tmux. This is NULL if the command came from the command-line. + * + * cmdclient is the client which sent the MSG_COMMAND to the server, if + * any. This is NULL unless the command came from the command-line. + * + * cmdclient and curclient may both be NULL if the command is in the + * configuration file. + */ + struct client *curclient; + struct client *cmdclient; + + struct msg_command_data *msgdata; + + /* gcc2 doesn't understand attributes on function pointers... */ +#if defined(__GNUC__) && __GNUC__ >= 3 + void printflike2 (*print)(struct cmd_ctx *, const char *, ...); + void printflike2 (*info)(struct cmd_ctx *, const char *, ...); + void printflike2 (*error)(struct cmd_ctx *, const char *, ...); +#else + void (*print)(struct cmd_ctx *, const char *, ...); + void (*info)(struct cmd_ctx *, const char *, ...); + void (*error)(struct cmd_ctx *, const char *, ...); +#endif +}; + +struct cmd { + const struct cmd_entry *entry; + struct args *args; + + TAILQ_ENTRY(cmd) qentry; +}; +struct cmd_list { + int references; + TAILQ_HEAD(, cmd) list; +}; + +struct cmd_entry { + const char *name; + const char *alias; + + const char *args_template; + int args_lower; + int args_upper; + + const char *usage; + +#define CMD_STARTSERVER 0x1 +#define CMD_CANTNEST 0x2 +#define CMD_SENDENVIRON 0x4 +#define CMD_READONLY 0x8 + int flags; + + void (*key_binding)(struct cmd *, int); + int (*check)(struct args *); + int (*exec)(struct cmd *, struct cmd_ctx *); +}; + +/* Key binding. */ +struct key_binding { + int key; + struct cmd_list *cmdlist; + int can_repeat; + + SPLAY_ENTRY(key_binding) entry; +}; +SPLAY_HEAD(key_bindings, key_binding); + +/* + * Option table entries. The option table is the user-visible part of the + * option, as opposed to the internal options (struct option) which are just + * number or string. + */ +enum options_table_type { + OPTIONS_TABLE_STRING, + OPTIONS_TABLE_NUMBER, + OPTIONS_TABLE_KEYS, + OPTIONS_TABLE_COLOUR, + OPTIONS_TABLE_ATTRIBUTES, + OPTIONS_TABLE_FLAG, + OPTIONS_TABLE_CHOICE +}; + +struct options_table_entry { + const char *name; + enum options_table_type type; + + u_int minimum; + u_int maximum; + const char **choices; + + const char *default_str; + long long default_num; +}; + +/* List of configuration causes. */ +ARRAY_DECL(causelist, char *); + +/* Common command usages. */ +#define CMD_TARGET_PANE_USAGE "[-t target-pane]" +#define CMD_TARGET_WINDOW_USAGE "[-t target-window]" +#define CMD_TARGET_SESSION_USAGE "[-t target-session]" +#define CMD_TARGET_CLIENT_USAGE "[-t target-client]" +#define CMD_SRCDST_PANE_USAGE "[-s src-pane] [-t dst-pane]" +#define CMD_SRCDST_WINDOW_USAGE "[-s src-window] [-t dst-window]" +#define CMD_SRCDST_SESSION_USAGE "[-s src-session] [-t dst-session]" +#define CMD_SRCDST_CLIENT_USAGE "[-s src-client] [-t dst-client]" +#define CMD_BUFFER_USAGE "[-b buffer-index]" + +/* tmux.c */ +extern struct options global_options; +extern struct options global_s_options; +extern struct options global_w_options; +extern struct environ global_environ; +extern struct event_base *ev_base; +extern char *cfg_file; +extern char *shell_cmd; +extern int debug_level; +extern time_t start_time; +extern char socket_path[MAXPATHLEN]; +extern int login_shell; +extern char *environ_path; +extern pid_t environ_pid; +extern int environ_idx; +void logfile(const char *); +const char *getshell(void); +int checkshell(const char *); +int areshell(const char *); +void setblocking(int, int); +__dead void shell_exec(const char *, const char *); + +/* cfg.c */ +extern int cfg_finished; +extern struct causelist cfg_causes; +void printflike2 cfg_add_cause(struct causelist *, const char *, ...); +int load_cfg(const char *, struct cmd_ctx *, struct causelist *); + +/* mode-key.c */ +extern const struct mode_key_table mode_key_tables[]; +extern struct mode_key_tree mode_key_tree_vi_edit; +extern struct mode_key_tree mode_key_tree_vi_choice; +extern struct mode_key_tree mode_key_tree_vi_copy; +extern struct mode_key_tree mode_key_tree_emacs_edit; +extern struct mode_key_tree mode_key_tree_emacs_choice; +extern struct mode_key_tree mode_key_tree_emacs_copy; +int mode_key_cmp(struct mode_key_binding *, struct mode_key_binding *); +SPLAY_PROTOTYPE(mode_key_tree, mode_key_binding, entry, mode_key_cmp); +const char *mode_key_tostring(const struct mode_key_cmdstr *, + enum mode_key_cmd); +enum mode_key_cmd mode_key_fromstring(const struct mode_key_cmdstr *, + const char *); +const struct mode_key_table *mode_key_findtable(const char *); +void mode_key_init_trees(void); +void mode_key_init(struct mode_key_data *, struct mode_key_tree *); +enum mode_key_cmd mode_key_lookup(struct mode_key_data *, int); + +/* options.c */ +int options_cmp(struct options_entry *, struct options_entry *); +SPLAY_PROTOTYPE(options_tree, options_entry, entry, options_cmp); +void options_init(struct options *, struct options *); +void options_free(struct options *); +struct options_entry *options_find1(struct options *, const char *); +struct options_entry *options_find(struct options *, const char *); +void options_remove(struct options *, const char *); +struct options_entry *printflike3 options_set_string( + struct options *, const char *, const char *, ...); +char *options_get_string(struct options *, const char *); +struct options_entry *options_set_number( + struct options *, const char *, long long); +long long options_get_number(struct options *, const char *); +struct options_entry *options_set_data( + struct options *, const char *, void *, void (*)(void *)); +void *options_get_data(struct options *, const char *); + +/* options-table.c */ +extern const struct options_table_entry server_options_table[]; +extern const struct options_table_entry session_options_table[]; +extern const struct options_table_entry window_options_table[]; +void options_table_populate_tree( + const struct options_table_entry *, struct options *); +const char *options_table_print_entry( + const struct options_table_entry *, struct options_entry *); + +/* job.c */ +extern struct joblist all_jobs; +struct job *job_run( + const char *, void (*)(struct job *), void (*)(void *), void *); +void job_free(struct job *); +void job_died(struct job *, int); + +/* environ.c */ +int environ_cmp(struct environ_entry *, struct environ_entry *); +RB_PROTOTYPE(environ, environ_entry, entry, environ_cmp); +void environ_init(struct environ *); +void environ_free(struct environ *); +void environ_copy(struct environ *, struct environ *); +struct environ_entry *environ_find(struct environ *, const char *); +void environ_set(struct environ *, const char *, const char *); +void environ_put(struct environ *, const char *); +void environ_unset(struct environ *, const char *); +void environ_update(const char *, struct environ *, struct environ *); +void environ_push(struct environ *); + +/* tty.c */ +void tty_raw(struct tty *, const char *); +void tty_attributes(struct tty *, const struct grid_cell *); +void tty_reset(struct tty *); +void tty_region_pane(struct tty *, const struct tty_ctx *, u_int, u_int); +void tty_region(struct tty *, u_int, u_int); +void tty_cursor_pane(struct tty *, const struct tty_ctx *, u_int, u_int); +void tty_cursor(struct tty *, u_int, u_int); +void tty_putcode(struct tty *, enum tty_code_code); +void tty_putcode1(struct tty *, enum tty_code_code, int); +void tty_putcode2(struct tty *, enum tty_code_code, int, int); +void tty_putcode_ptr1(struct tty *, enum tty_code_code, const void *); +void tty_putcode_ptr2(struct tty *, enum tty_code_code, const void *, const void *); +void tty_puts(struct tty *, const char *); +void tty_putc(struct tty *, u_char); +void tty_pututf8(struct tty *, const struct grid_utf8 *); +void tty_init(struct tty *, int, char *); +int tty_resize(struct tty *); +void tty_start_tty(struct tty *); +void tty_stop_tty(struct tty *); +void tty_set_title(struct tty *, const char *); +void tty_update_mode(struct tty *, int, struct screen *); +void tty_force_cursor_colour(struct tty *, const char *); +void tty_draw_line(struct tty *, struct screen *, u_int, u_int, u_int); +int tty_open(struct tty *, const char *, char **); +void tty_close(struct tty *); +void tty_free(struct tty *); +void tty_write(void (*)( + struct tty *, const struct tty_ctx *), const struct tty_ctx *); +void tty_cmd_alignmenttest(struct tty *, const struct tty_ctx *); +void tty_cmd_cell(struct tty *, const struct tty_ctx *); +void tty_cmd_clearendofline(struct tty *, const struct tty_ctx *); +void tty_cmd_clearendofscreen(struct tty *, const struct tty_ctx *); +void tty_cmd_clearline(struct tty *, const struct tty_ctx *); +void tty_cmd_clearscreen(struct tty *, const struct tty_ctx *); +void tty_cmd_clearstartofline(struct tty *, const struct tty_ctx *); +void tty_cmd_clearstartofscreen(struct tty *, const struct tty_ctx *); +void tty_cmd_deletecharacter(struct tty *, const struct tty_ctx *); +void tty_cmd_deleteline(struct tty *, const struct tty_ctx *); +void tty_cmd_erasecharacter(struct tty *, const struct tty_ctx *); +void tty_cmd_insertcharacter(struct tty *, const struct tty_ctx *); +void tty_cmd_insertline(struct tty *, const struct tty_ctx *); +void tty_cmd_linefeed(struct tty *, const struct tty_ctx *); +void tty_cmd_utf8character(struct tty *, const struct tty_ctx *); +void tty_cmd_reverseindex(struct tty *, const struct tty_ctx *); +void tty_cmd_setselection(struct tty *, const struct tty_ctx *); +void tty_cmd_rawstring(struct tty *, const struct tty_ctx *); + +/* tty-term.c */ +extern struct tty_terms tty_terms; +extern const struct tty_term_code_entry tty_term_codes[NTTYCODE]; +struct tty_term *tty_term_find(char *, int, const char *, char **); +void tty_term_free(struct tty_term *); +int tty_term_has(struct tty_term *, enum tty_code_code); +const char *tty_term_string(struct tty_term *, enum tty_code_code); +const char *tty_term_string1(struct tty_term *, enum tty_code_code, int); +const char *tty_term_string2( + struct tty_term *, enum tty_code_code, int, int); +const char *tty_term_ptr1( + struct tty_term *, enum tty_code_code, const void *); +const char *tty_term_ptr2( + struct tty_term *, enum tty_code_code, const void *, const void *); +int tty_term_number(struct tty_term *, enum tty_code_code); +int tty_term_flag(struct tty_term *, enum tty_code_code); + +/* tty-acs.c */ +const char *tty_acs_get(struct tty *, u_char); + +/* tty-keys.c */ +void tty_keys_init(struct tty *); +void tty_keys_free(struct tty *); +int tty_keys_next(struct tty *); + +/* paste.c */ +struct paste_buffer *paste_walk_stack(struct paste_stack *, u_int *); +struct paste_buffer *paste_get_top(struct paste_stack *); +struct paste_buffer *paste_get_index(struct paste_stack *, u_int); +int paste_free_top(struct paste_stack *); +int paste_free_index(struct paste_stack *, u_int); +void paste_add(struct paste_stack *, char *, size_t, u_int); +int paste_replace(struct paste_stack *, u_int, char *, size_t); +char *paste_print(struct paste_buffer *, size_t); + +/* clock.c */ +extern const char clock_table[14][5][5]; +void clock_draw(struct screen_write_ctx *, int, int); + +/* arguments.c */ +struct args *args_create(int, ...); +struct args *args_parse(const char *, int, char **); +void args_free(struct args *); +size_t args_print(struct args *, char *, size_t); +int args_has(struct args *, u_char); +void args_set(struct args *, u_char, const char *); +const char *args_get(struct args *, u_char); +long long args_strtonum( + struct args *, u_char, long long, long long, char **); + +/* cmd.c */ +int cmd_pack_argv(int, char **, char *, size_t); +int cmd_unpack_argv(char *, size_t, int, char ***); +char **cmd_copy_argv(int, char *const *); +void cmd_free_argv(int, char **); +struct cmd *cmd_parse(int, char **, char **); +int cmd_exec(struct cmd *, struct cmd_ctx *); +void cmd_free(struct cmd *); +size_t cmd_print(struct cmd *, char *, size_t); +struct session *cmd_current_session(struct cmd_ctx *, int); +struct client *cmd_current_client(struct cmd_ctx *); +struct client *cmd_find_client(struct cmd_ctx *, const char *); +struct session *cmd_find_session(struct cmd_ctx *, const char *, int); +struct winlink *cmd_find_window( + struct cmd_ctx *, const char *, struct session **); +int cmd_find_index( + struct cmd_ctx *, const char *, struct session **); +struct winlink *cmd_find_pane(struct cmd_ctx *, + const char *, struct session **, struct window_pane **); +char *cmd_template_replace(char *, const char *, int); +extern const struct cmd_entry *cmd_table[]; +extern const struct cmd_entry cmd_attach_session_entry; +extern const struct cmd_entry cmd_bind_key_entry; +extern const struct cmd_entry cmd_break_pane_entry; +extern const struct cmd_entry cmd_capture_pane_entry; +extern const struct cmd_entry cmd_choose_buffer_entry; +extern const struct cmd_entry cmd_choose_client_entry; +extern const struct cmd_entry cmd_choose_session_entry; +extern const struct cmd_entry cmd_choose_window_entry; +extern const struct cmd_entry cmd_clear_history_entry; +extern const struct cmd_entry cmd_clock_mode_entry; +extern const struct cmd_entry cmd_command_prompt_entry; +extern const struct cmd_entry cmd_confirm_before_entry; +extern const struct cmd_entry cmd_copy_mode_entry; +extern const struct cmd_entry cmd_delete_buffer_entry; +extern const struct cmd_entry cmd_detach_client_entry; +extern const struct cmd_entry cmd_display_message_entry; +extern const struct cmd_entry cmd_display_panes_entry; +extern const struct cmd_entry cmd_down_pane_entry; +extern const struct cmd_entry cmd_find_window_entry; +extern const struct cmd_entry cmd_has_session_entry; +extern const struct cmd_entry cmd_if_shell_entry; +extern const struct cmd_entry cmd_join_pane_entry; +extern const struct cmd_entry cmd_kill_pane_entry; +extern const struct cmd_entry cmd_kill_server_entry; +extern const struct cmd_entry cmd_kill_session_entry; +extern const struct cmd_entry cmd_kill_window_entry; +extern const struct cmd_entry cmd_last_pane_entry; +extern const struct cmd_entry cmd_last_window_entry; +extern const struct cmd_entry cmd_link_window_entry; +extern const struct cmd_entry cmd_list_buffers_entry; +extern const struct cmd_entry cmd_list_clients_entry; +extern const struct cmd_entry cmd_list_commands_entry; +extern const struct cmd_entry cmd_list_keys_entry; +extern const struct cmd_entry cmd_list_panes_entry; +extern const struct cmd_entry cmd_list_sessions_entry; +extern const struct cmd_entry cmd_list_windows_entry; +extern const struct cmd_entry cmd_load_buffer_entry; +extern const struct cmd_entry cmd_lock_client_entry; +extern const struct cmd_entry cmd_lock_server_entry; +extern const struct cmd_entry cmd_lock_session_entry; +extern const struct cmd_entry cmd_move_window_entry; +extern const struct cmd_entry cmd_new_session_entry; +extern const struct cmd_entry cmd_new_window_entry; +extern const struct cmd_entry cmd_next_layout_entry; +extern const struct cmd_entry cmd_next_window_entry; +extern const struct cmd_entry cmd_paste_buffer_entry; +extern const struct cmd_entry cmd_pipe_pane_entry; +extern const struct cmd_entry cmd_previous_layout_entry; +extern const struct cmd_entry cmd_previous_window_entry; +extern const struct cmd_entry cmd_refresh_client_entry; +extern const struct cmd_entry cmd_rename_session_entry; +extern const struct cmd_entry cmd_rename_window_entry; +extern const struct cmd_entry cmd_resize_pane_entry; +extern const struct cmd_entry cmd_respawn_pane_entry; +extern const struct cmd_entry cmd_respawn_window_entry; +extern const struct cmd_entry cmd_rotate_window_entry; +extern const struct cmd_entry cmd_run_shell_entry; +extern const struct cmd_entry cmd_save_buffer_entry; +extern const struct cmd_entry cmd_select_layout_entry; +extern const struct cmd_entry cmd_select_pane_entry; +extern const struct cmd_entry cmd_select_window_entry; +extern const struct cmd_entry cmd_send_keys_entry; +extern const struct cmd_entry cmd_send_prefix_entry; +extern const struct cmd_entry cmd_server_info_entry; +extern const struct cmd_entry cmd_set_buffer_entry; +extern const struct cmd_entry cmd_set_environment_entry; +extern const struct cmd_entry cmd_set_option_entry; +extern const struct cmd_entry cmd_set_window_option_entry; +extern const struct cmd_entry cmd_show_buffer_entry; +extern const struct cmd_entry cmd_show_environment_entry; +extern const struct cmd_entry cmd_show_messages_entry; +extern const struct cmd_entry cmd_show_options_entry; +extern const struct cmd_entry cmd_show_window_options_entry; +extern const struct cmd_entry cmd_source_file_entry; +extern const struct cmd_entry cmd_split_window_entry; +extern const struct cmd_entry cmd_start_server_entry; +extern const struct cmd_entry cmd_suspend_client_entry; +extern const struct cmd_entry cmd_swap_pane_entry; +extern const struct cmd_entry cmd_swap_window_entry; +extern const struct cmd_entry cmd_switch_client_entry; +extern const struct cmd_entry cmd_unbind_key_entry; +extern const struct cmd_entry cmd_unlink_window_entry; +extern const struct cmd_entry cmd_up_pane_entry; + +/* cmd-list.c */ +struct cmd_list *cmd_list_parse(int, char **, char **); +int cmd_list_exec(struct cmd_list *, struct cmd_ctx *); +void cmd_list_free(struct cmd_list *); +size_t cmd_list_print(struct cmd_list *, char *, size_t); + +/* cmd-string.c */ +int cmd_string_parse(const char *, struct cmd_list **, char **); + +/* client.c */ +int client_main(int, char **, int); + +/* key-bindings.c */ +extern struct key_bindings key_bindings; +int key_bindings_cmp(struct key_binding *, struct key_binding *); +SPLAY_PROTOTYPE(key_bindings, key_binding, entry, key_bindings_cmp); +struct key_binding *key_bindings_lookup(int); +void key_bindings_add(int, int, struct cmd_list *); +void key_bindings_remove(int); +void key_bindings_clean(void); +void key_bindings_init(void); +void key_bindings_dispatch(struct key_binding *, struct client *); +void printflike2 key_bindings_error(struct cmd_ctx *, const char *, ...); +void printflike2 key_bindings_print(struct cmd_ctx *, const char *, ...); +void printflike2 key_bindings_info(struct cmd_ctx *, const char *, ...); + +/* key-string.c */ +int key_string_lookup_string(const char *); +const char *key_string_lookup_key(int); + +/* server.c */ +extern struct clients clients; +extern struct clients dead_clients; +extern struct paste_stack global_buffers; +int server_start(void); +void server_update_socket(void); + +/* server-client.c */ +void server_client_create(int); +void server_client_lost(struct client *); +void server_client_callback(int, short, void *); +void server_client_status_timer(void); +void server_client_loop(void); + +/* server-window.c */ +void server_window_loop(void); + +/* server-fn.c */ +void server_fill_environ(struct session *, struct environ *); +void server_write_client( + struct client *, enum msgtype, const void *, size_t); +void server_write_session( + struct session *, enum msgtype, const void *, size_t); +void server_redraw_client(struct client *); +void server_status_client(struct client *); +void server_redraw_session(struct session *); +void server_redraw_session_group(struct session *); +void server_status_session(struct session *); +void server_status_session_group(struct session *); +void server_redraw_window(struct window *); +void server_redraw_window_borders(struct window *); +void server_status_window(struct window *); +void server_lock(void); +void server_lock_session(struct session *); +void server_lock_client(struct client *); +int server_unlock(const char *); +void server_kill_window(struct window *); +int server_link_window(struct session *, + struct winlink *, struct session *, int, int, int, char **); +void server_unlink_window(struct session *, struct winlink *); +void server_destroy_pane(struct window_pane *); +void server_destroy_session_group(struct session *); +void server_destroy_session(struct session *); +void server_check_unattached (void); +void server_set_identify(struct client *); +void server_clear_identify(struct client *); +void server_update_event(struct client *); + +/* status.c */ +int status_out_cmp(struct status_out *, struct status_out *); +RB_PROTOTYPE(status_out_tree, status_out, entry, status_out_cmp); +void status_free_jobs(struct status_out_tree *); +void status_update_jobs(struct client *); +void status_set_window_at(struct client *, u_int); +int status_redraw(struct client *); +char *status_replace(struct client *, struct session *, + struct winlink *, struct window_pane *, const char *, time_t, int) + __attribute__((__format__(__strftime__, 5, 0))); +void printflike2 status_message_set(struct client *, const char *, ...); +void status_message_clear(struct client *); +int status_message_redraw(struct client *); +void status_prompt_set(struct client *, const char *, const char *, + int (*)(void *, const char *), void (*)(void *), void *, int); +void status_prompt_clear(struct client *); +int status_prompt_redraw(struct client *); +void status_prompt_key(struct client *, int); +void status_prompt_update(struct client *, const char *, const char *); + +/* resize.c */ +void recalculate_sizes(void); + +/* input.c */ +void input_init(struct window_pane *); +void input_free(struct window_pane *); +void input_parse(struct window_pane *); + +/* input-key.c */ +void input_key(struct window_pane *, int); +void input_mouse(struct window_pane *, struct mouse_event *); + +/* xterm-keys.c */ +char *xterm_keys_lookup(int); +int xterm_keys_find(const char *, size_t, size_t *, int *); + +/* colour.c */ +void colour_set_fg(struct grid_cell *, int); +void colour_set_bg(struct grid_cell *, int); +const char *colour_tostring(int); +int colour_fromstring(const char *); +u_char colour_256to16(u_char); +u_char colour_256to88(u_char); + +/* attributes.c */ +const char *attributes_tostring(u_char); +int attributes_fromstring(const char *); + +/* grid.c */ +extern const struct grid_cell grid_default_cell; +struct grid *grid_create(u_int, u_int, u_int); +void grid_destroy(struct grid *); +int grid_compare(struct grid *, struct grid *); +void grid_collect_history(struct grid *); +void grid_scroll_history(struct grid *); +void grid_scroll_history_region(struct grid *, u_int, u_int); +void grid_expand_line(struct grid *, u_int, u_int); +void grid_expand_line_utf8(struct grid *, u_int, u_int); +const struct grid_cell *grid_peek_cell(struct grid *, u_int, u_int); +struct grid_cell *grid_get_cell(struct grid *, u_int, u_int); +void grid_set_cell(struct grid *, u_int, u_int, const struct grid_cell *); +const struct grid_utf8 *grid_peek_utf8(struct grid *, u_int, u_int); +struct grid_utf8 *grid_get_utf8(struct grid *, u_int, u_int); +void grid_set_utf8(struct grid *, u_int, u_int, const struct grid_utf8 *); +void grid_clear(struct grid *, u_int, u_int, u_int, u_int); +void grid_clear_lines(struct grid *, u_int, u_int); +void grid_move_lines(struct grid *, u_int, u_int, u_int); +void grid_move_cells(struct grid *, u_int, u_int, u_int, u_int); +char *grid_string_cells(struct grid *, u_int, u_int, u_int); +void grid_duplicate_lines( + struct grid *, u_int, struct grid *, u_int, u_int); + +/* grid-utf8.c */ +size_t grid_utf8_size(const struct grid_utf8 *); +size_t grid_utf8_copy(const struct grid_utf8 *, char *, size_t); +void grid_utf8_set(struct grid_utf8 *, const struct utf8_data *); +int grid_utf8_append(struct grid_utf8 *, const struct utf8_data *); +int grid_utf8_compare(const struct grid_utf8 *, const struct grid_utf8 *); + +/* grid-view.c */ +const struct grid_cell *grid_view_peek_cell(struct grid *, u_int, u_int); +struct grid_cell *grid_view_get_cell(struct grid *, u_int, u_int); +void grid_view_set_cell( + struct grid *, u_int, u_int, const struct grid_cell *); +const struct grid_utf8 *grid_view_peek_utf8(struct grid *, u_int, u_int); +struct grid_utf8 *grid_view_get_utf8(struct grid *, u_int, u_int); +void grid_view_set_utf8( + struct grid *, u_int, u_int, const struct grid_utf8 *); +void grid_view_clear_history(struct grid *); +void grid_view_clear(struct grid *, u_int, u_int, u_int, u_int); +void grid_view_scroll_region_up(struct grid *, u_int, u_int); +void grid_view_scroll_region_down(struct grid *, u_int, u_int); +void grid_view_insert_lines(struct grid *, u_int, u_int); +void grid_view_insert_lines_region(struct grid *, u_int, u_int, u_int); +void grid_view_delete_lines(struct grid *, u_int, u_int); +void grid_view_delete_lines_region(struct grid *, u_int, u_int, u_int); +void grid_view_insert_cells(struct grid *, u_int, u_int, u_int); +void grid_view_delete_cells(struct grid *, u_int, u_int, u_int); +char *grid_view_string_cells(struct grid *, u_int, u_int, u_int); + +/* screen-write.c */ +void screen_write_start( + struct screen_write_ctx *, struct window_pane *, struct screen *); +void screen_write_stop(struct screen_write_ctx *); +size_t printflike2 screen_write_cstrlen(int, const char *, ...); +void printflike5 screen_write_cnputs(struct screen_write_ctx *, + ssize_t, struct grid_cell *, int, const char *, ...); +size_t printflike2 screen_write_strlen(int, const char *, ...); +void printflike3 screen_write_puts(struct screen_write_ctx *, + struct grid_cell *, const char *, ...); +void printflike5 screen_write_nputs(struct screen_write_ctx *, + ssize_t, struct grid_cell *, int, const char *, ...); +void screen_write_vnputs(struct screen_write_ctx *, + ssize_t, struct grid_cell *, int, const char *, va_list); +void screen_write_parsestyle( + struct grid_cell *, struct grid_cell *, const char *); +void screen_write_putc( + struct screen_write_ctx *, struct grid_cell *, u_char); +void screen_write_copy(struct screen_write_ctx *, + struct screen *, u_int, u_int, u_int, u_int); +void screen_write_backspace(struct screen_write_ctx *); +void screen_write_cursorup(struct screen_write_ctx *, u_int); +void screen_write_cursordown(struct screen_write_ctx *, u_int); +void screen_write_cursorright(struct screen_write_ctx *, u_int); +void screen_write_cursorleft(struct screen_write_ctx *, u_int); +void screen_write_alignmenttest(struct screen_write_ctx *); +void screen_write_insertcharacter(struct screen_write_ctx *, u_int); +void screen_write_deletecharacter(struct screen_write_ctx *, u_int); +void screen_write_insertline(struct screen_write_ctx *, u_int); +void screen_write_deleteline(struct screen_write_ctx *, u_int); +void screen_write_clearline(struct screen_write_ctx *); +void screen_write_clearendofline(struct screen_write_ctx *); +void screen_write_clearstartofline(struct screen_write_ctx *); +void screen_write_cursormove(struct screen_write_ctx *, u_int, u_int); +void screen_write_cursormode(struct screen_write_ctx *, int); +void screen_write_reverseindex(struct screen_write_ctx *); +void screen_write_scrollregion(struct screen_write_ctx *, u_int, u_int); +void screen_write_insertmode(struct screen_write_ctx *, int); +void screen_write_utf8mousemode(struct screen_write_ctx *, int); +void screen_write_mousemode_on(struct screen_write_ctx *, int); +void screen_write_mousemode_off(struct screen_write_ctx *); +void screen_write_linefeed(struct screen_write_ctx *, int); +void screen_write_linefeedscreen(struct screen_write_ctx *, int); +void screen_write_carriagereturn(struct screen_write_ctx *); +void screen_write_kcursormode(struct screen_write_ctx *, int); +void screen_write_kkeypadmode(struct screen_write_ctx *, int); +void screen_write_clearendofscreen(struct screen_write_ctx *); +void screen_write_clearstartofscreen(struct screen_write_ctx *); +void screen_write_clearscreen(struct screen_write_ctx *); +void screen_write_cell(struct screen_write_ctx *, + const struct grid_cell *, const struct utf8_data *); +void screen_write_setselection(struct screen_write_ctx *, u_char *, u_int); +void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int); + +/* screen-redraw.c */ +void screen_redraw_screen(struct client *, int, int); +void screen_redraw_pane(struct client *, struct window_pane *); + +/* screen.c */ +void screen_init(struct screen *, u_int, u_int, u_int); +void screen_reinit(struct screen *); +void screen_free(struct screen *); +void screen_reset_tabs(struct screen *); +void screen_set_cursor_style(struct screen *, u_int); +void screen_set_cursor_colour(struct screen *, const char *); +void screen_set_title(struct screen *, const char *); +void screen_resize(struct screen *, u_int, u_int); +void screen_set_selection(struct screen *, + u_int, u_int, u_int, u_int, u_int, struct grid_cell *); +void screen_clear_selection(struct screen *); +int screen_check_selection(struct screen *, u_int, u_int); + +/* window.c */ +extern struct windows windows; +extern struct window_pane_tree all_window_panes; +int winlink_cmp(struct winlink *, struct winlink *); +RB_PROTOTYPE(winlinks, winlink, entry, winlink_cmp); +int window_pane_cmp(struct window_pane *, struct window_pane *); +RB_PROTOTYPE(window_pane_tree, window_pane, tree_entry, window_pane_cmp); +struct winlink *winlink_find_by_index(struct winlinks *, int); +struct winlink *winlink_find_by_window(struct winlinks *, struct window *); +int winlink_next_index(struct winlinks *, int); +u_int winlink_count(struct winlinks *); +struct winlink *winlink_add(struct winlinks *, int); +void winlink_set_window(struct winlink *, struct window *); +void winlink_remove(struct winlinks *, struct winlink *); +struct winlink *winlink_next(struct winlink *); +struct winlink *winlink_previous(struct winlink *); +struct winlink *winlink_next_by_number(struct winlink *, struct session *, + int); +struct winlink *winlink_previous_by_number(struct winlink *, struct session *, + int); +void winlink_stack_push(struct winlink_stack *, struct winlink *); +void winlink_stack_remove(struct winlink_stack *, struct winlink *); +int window_index(struct window *, u_int *); +struct window *window_create1(u_int, u_int); +struct window *window_create(const char *, const char *, const char *, + const char *, struct environ *, struct termios *, + u_int, u_int, u_int, char **); +void window_destroy(struct window *); +struct window_pane *window_get_active_at(struct window *, u_int, u_int); +void window_set_active_at(struct window *, u_int, u_int); +struct window_pane *window_find_string(struct window *, const char *); +void window_set_active_pane(struct window *, struct window_pane *); +struct window_pane *window_add_pane(struct window *, u_int); +void window_resize(struct window *, u_int, u_int); +void window_remove_pane(struct window *, struct window_pane *); +struct window_pane *window_pane_at_index(struct window *, u_int); +struct window_pane *window_pane_next_by_number(struct window *, + struct window_pane *, u_int); +struct window_pane *window_pane_previous_by_number(struct window *, + struct window_pane *, u_int); +u_int window_pane_index(struct window *, struct window_pane *); +u_int window_count_panes(struct window *); +void window_destroy_panes(struct window *); +struct window_pane *window_pane_find_by_id(u_int); +struct window_pane *window_pane_create(struct window *, u_int, u_int, u_int); +void window_pane_destroy(struct window_pane *); +int window_pane_spawn(struct window_pane *, const char *, + const char *, const char *, struct environ *, + struct termios *, char **); +void window_pane_resize(struct window_pane *, u_int, u_int); +void window_pane_alternate_on( + struct window_pane *, struct grid_cell *); +void window_pane_alternate_off( + struct window_pane *, struct grid_cell *); +int window_pane_set_mode( + struct window_pane *, const struct window_mode *); +void window_pane_reset_mode(struct window_pane *); +void window_pane_key(struct window_pane *, struct session *, int); +void window_pane_mouse(struct window_pane *, + struct session *, struct mouse_event *); +int window_pane_visible(struct window_pane *); +char *window_pane_search( + struct window_pane *, const char *, u_int *); +char *window_printable_flags(struct session *, struct winlink *); + +struct window_pane *window_pane_find_up(struct window_pane *); +struct window_pane *window_pane_find_down(struct window_pane *); +struct window_pane *window_pane_find_left(struct window_pane *); +struct window_pane *window_pane_find_right(struct window_pane *); + +/* layout.c */ +u_int layout_count_cells(struct layout_cell *); +struct layout_cell *layout_create_cell(struct layout_cell *); +void layout_free_cell(struct layout_cell *); +void layout_print_cell(struct layout_cell *, const char *, u_int); +void layout_destroy_cell(struct layout_cell *, struct layout_cell **); +void layout_set_size( + struct layout_cell *, u_int, u_int, u_int, u_int); +void layout_make_leaf( + struct layout_cell *, struct window_pane *); +void layout_make_node(struct layout_cell *, enum layout_type); +void layout_fix_offsets(struct layout_cell *); +void layout_fix_panes(struct window *, u_int, u_int); +u_int layout_resize_check(struct layout_cell *, enum layout_type); +void layout_resize_adjust( + struct layout_cell *, enum layout_type, int); +void layout_init(struct window *); +void layout_free(struct window *); +void layout_resize(struct window *, u_int, u_int); +void layout_resize_pane( + struct window_pane *, enum layout_type, int); +void layout_resize_pane_mouse( + struct client *c, struct mouse_event *mouse); +void layout_assign_pane(struct layout_cell *, struct window_pane *); +struct layout_cell *layout_split_pane( + struct window_pane *, enum layout_type, int); +void layout_close_pane(struct window_pane *); + +/* layout-custom.c */ +char *layout_dump(struct window *); +int layout_parse(struct window *, const char *); + +/* layout-set.c */ +const char *layout_set_name(u_int); +int layout_set_lookup(const char *); +u_int layout_set_select(struct window *, u_int); +u_int layout_set_next(struct window *); +u_int layout_set_previous(struct window *); +void layout_set_active_changed(struct window *); + +/* window-clock.c */ +extern const struct window_mode window_clock_mode; + +/* window-copy.c */ +extern const struct window_mode window_copy_mode; +void window_copy_init_from_pane(struct window_pane *); +void window_copy_init_for_output(struct window_pane *); +void window_copy_add(struct window_pane *, const char *, ...); +void window_copy_vadd(struct window_pane *, const char *, va_list); +void window_copy_pageup(struct window_pane *); + +/* window-choose.c */ +extern const struct window_mode window_choose_mode; +void window_choose_vadd( + struct window_pane *, int, const char *, va_list); +void printflike3 window_choose_add( + struct window_pane *, int, const char *, ...); +void window_choose_ready(struct window_pane *, + u_int, void (*)(void *, int), void (*)(void *), void *); + +/* names.c */ +void queue_window_name(struct window *); +char *default_window_name(struct window *); + +/* signal.c */ +void set_signals(void(*)(int, short, void *)); +void clear_signals(int); + +/* session.c */ +extern struct sessions sessions; +extern struct sessions dead_sessions; +extern struct session_groups session_groups; +int session_cmp(struct session *, struct session *); +RB_PROTOTYPE(sessions, session, entry, session_cmp); +int session_alive(struct session *); +struct session *session_find(const char *); +struct session *session_find_by_index(u_int); +struct session *session_create(const char *, const char *, const char *, + struct environ *, struct termios *, int, u_int, u_int, + char **); +void session_destroy(struct session *); +int session_check_name(const char *); +void session_update_activity(struct session *); +struct session *session_next_session(struct session *); +struct session *session_previous_session(struct session *); +struct winlink *session_new(struct session *, + const char *, const char *, const char *, int, char **); +struct winlink *session_attach( + struct session *, struct window *, int, char **); +int session_detach(struct session *, struct winlink *); +struct winlink* session_has(struct session *, struct window *); +int session_next(struct session *, int); +int session_previous(struct session *, int); +int session_select(struct session *, int); +int session_last(struct session *); +struct session_group *session_group_find(struct session *); +u_int session_group_index(struct session_group *); +void session_group_add(struct session *, struct session *); +void session_group_remove(struct session *); +void session_group_synchronize_to(struct session *); +void session_group_synchronize_from(struct session *); +void session_group_synchronize1(struct session *, struct session *); + +/* utf8.c */ +void utf8_build(void); +int utf8_open(struct utf8_data *, u_char); +int utf8_append(struct utf8_data *, u_char); +u_int utf8_combine(const struct utf8_data *); +u_int utf8_split2(u_int, u_char *); + +/* osdep-*.c */ +char *osdep_get_name(int, char *); +struct event_base *osdep_event_init(void); + +/* log.c */ +void log_open_tty(int); +void log_open_file(int, const char *); +void log_close(void); +void printflike1 log_warn(const char *, ...); +void printflike1 log_warnx(const char *, ...); +void printflike1 log_info(const char *, ...); +void printflike1 log_debug(const char *, ...); +void printflike1 log_debug2(const char *, ...); +__dead void printflike1 log_fatal(const char *, ...); +__dead void printflike1 log_fatalx(const char *, ...); + +/* xmalloc.c */ +char *xstrdup(const char *); +void *xcalloc(size_t, size_t); +void *xmalloc(size_t); +void *xrealloc(void *, size_t, size_t); +void xfree(void *); +int printflike2 xasprintf(char **, const char *, ...); +int xvasprintf(char **, const char *, va_list); +int printflike3 xsnprintf(char *, size_t, const char *, ...); +int xvsnprintf(char *, size_t, const char *, va_list); + +/* utmp.c */ +struct window_utmp *utmp_create(const char *); +void utmp_destroy(struct window_utmp *); + +#endif /* TMUX_H */ diff --git a/external/bsd/tmux/dist/tty-acs.c b/external/bsd/tmux/dist/tty-acs.c new file mode 100644 index 000000000..48e9940e0 --- /dev/null +++ b/external/bsd/tmux/dist/tty-acs.c @@ -0,0 +1,97 @@ +/* $Id: tty-acs.c,v 1.3 2011/08/17 18:48:36 jmmv Exp $ */ + +/* + * Copyright (c) 2010 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +int tty_acs_cmp(const void *, const void *); + +/* Table mapping ACS entries to UTF-8. */ +struct tty_acs_entry { + u_char key; + const char *string; +}; +const struct tty_acs_entry tty_acs_table[] = { + { '+', "\342\206\222" }, + { ',', "\342\206\220" }, + { '-', "\342\206\221" }, + { '.', "\342\206\223" }, + { '0', "\342\226\256" }, + { '`', "\342\227\206" }, + { 'a', "\342\226\222" }, + { 'f', "\302\260" }, + { 'g', "\302\261" }, + { 'h', "\342\226\222" }, + { 'i', "\342\230\203" }, + { 'j', "\342\224\230" }, + { 'k', "\342\224\220" }, + { 'l', "\342\224\214" }, + { 'm', "\342\224\224" }, + { 'n', "\342\224\274" }, + { 'o', "\342\216\272" }, + { 'p', "\342\216\273" }, + { 'q', "\342\224\200" }, + { 'r', "\342\216\274" }, + { 's', "\342\216\275" }, + { 't', "\342\224\234" }, + { 'u', "\342\224\244" }, + { 'v', "\342\224\264" }, + { 'w', "\342\224\254" }, + { 'x', "\342\224\202" }, + { 'y', "\342\211\244" }, + { 'z', "\342\211\245" }, + { '{', "\317\200" }, + { '|', "\342\211\240" }, + { '}', "\302\243" }, + { '~', "\302\267" } +}; + +int +tty_acs_cmp(const void *key, const void *value) +{ + const struct tty_acs_entry *entry = value; + u_char ch; + + ch = *(const u_char *) key; + return (ch - entry->key); +} + +/* Retrieve ACS to output as a string. */ +const char * +tty_acs_get(struct tty *tty, u_char ch) +{ + struct tty_acs_entry *entry; + + /* If not a UTF-8 terminal, use the ACS set. */ + if (!(tty->flags & TTY_UTF8)) { + if (tty->term->acs[ch][0] == '\0') + return (NULL); + return (&tty->term->acs[ch][0]); + } + + /* Otherwise look up the UTF-8 translation. */ + entry = bsearch(&ch, + tty_acs_table, nitems(tty_acs_table), sizeof tty_acs_table[0], + tty_acs_cmp); + if (entry == NULL) + return (NULL); + return (entry->string); +} diff --git a/external/bsd/tmux/dist/tty-keys.c b/external/bsd/tmux/dist/tty-keys.c new file mode 100644 index 000000000..90101fe89 --- /dev/null +++ b/external/bsd/tmux/dist/tty-keys.c @@ -0,0 +1,657 @@ +/* $Id: tty-keys.c,v 1.4 2011/08/17 19:28:36 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include +#include +#include + +#include "tmux.h" + +/* + * Handle keys input from the outside terminal. tty_keys[] is a base table of + * supported keys which are looked up in terminfo(5) and translated into a + * ternary tree (a binary tree of binary trees). + */ + +void tty_keys_add1(struct tty_key **, const char *, int); +void tty_keys_add(struct tty *, const char *, int); +void tty_keys_free1(struct tty_key *); +struct tty_key *tty_keys_find1( + struct tty_key *, const char *, size_t, size_t *); +struct tty_key *tty_keys_find(struct tty *, const char *, size_t, size_t *); +void tty_keys_callback(int, short, void *); +int tty_keys_mouse(struct tty *, + const char *, size_t, size_t *, struct mouse_event *); + +struct tty_key_ent { + enum tty_code_code code; + const char *string; + + int key; + int flags; +#define TTYKEY_RAW 0x1 +}; + +/* + * Default key tables. Those flagged with TTYKEY_RAW are inserted directly, + * otherwise they are looked up in terminfo(5). + */ +const struct tty_key_ent tty_keys[] = { + /* + * Numeric keypad. Just use the vt100 escape sequences here and always + * put the terminal into keypad_xmit mode. Translation of numbers + * mode/applications mode is done in input-keys.c. + */ + { 0, "\033Oo", KEYC_KP_SLASH, TTYKEY_RAW }, + { 0, "\033Oj", KEYC_KP_STAR, TTYKEY_RAW }, + { 0, "\033Om", KEYC_KP_MINUS, TTYKEY_RAW }, + { 0, "\033Ow", KEYC_KP_SEVEN, TTYKEY_RAW }, + { 0, "\033Ox", KEYC_KP_EIGHT, TTYKEY_RAW }, + { 0, "\033Oy", KEYC_KP_NINE, TTYKEY_RAW }, + { 0, "\033Ok", KEYC_KP_PLUS, TTYKEY_RAW }, + { 0, "\033Ot", KEYC_KP_FOUR, TTYKEY_RAW }, + { 0, "\033Ou", KEYC_KP_FIVE, TTYKEY_RAW }, + { 0, "\033Ov", KEYC_KP_SIX, TTYKEY_RAW }, + { 0, "\033Oq", KEYC_KP_ONE, TTYKEY_RAW }, + { 0, "\033Or", KEYC_KP_TWO, TTYKEY_RAW }, + { 0, "\033Os", KEYC_KP_THREE, TTYKEY_RAW }, + { 0, "\033OM", KEYC_KP_ENTER, TTYKEY_RAW }, + { 0, "\033Op", KEYC_KP_ZERO, TTYKEY_RAW }, + { 0, "\033On", KEYC_KP_PERIOD, TTYKEY_RAW }, + + /* Arrow keys. */ + { 0, "\033OA", KEYC_UP, TTYKEY_RAW }, + { 0, "\033OB", KEYC_DOWN, TTYKEY_RAW }, + { 0, "\033OC", KEYC_RIGHT, TTYKEY_RAW }, + { 0, "\033OD", KEYC_LEFT, TTYKEY_RAW }, + + { 0, "\033[A", KEYC_UP, TTYKEY_RAW }, + { 0, "\033[B", KEYC_DOWN, TTYKEY_RAW }, + { 0, "\033[C", KEYC_RIGHT, TTYKEY_RAW }, + { 0, "\033[D", KEYC_LEFT, TTYKEY_RAW }, + + /* rxvt-style arrow + modifier keys. */ + { 0, "\033Oa", KEYC_UP|KEYC_CTRL, TTYKEY_RAW }, + { 0, "\033Ob", KEYC_DOWN|KEYC_CTRL, TTYKEY_RAW }, + { 0, "\033Oc", KEYC_RIGHT|KEYC_CTRL, TTYKEY_RAW }, + { 0, "\033Od", KEYC_LEFT|KEYC_CTRL, TTYKEY_RAW }, + + { 0, "\033[a", KEYC_UP|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[b", KEYC_DOWN|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[c", KEYC_RIGHT|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[d", KEYC_LEFT|KEYC_SHIFT, TTYKEY_RAW }, + + /* + * rxvt-style function + modifier keys: + * Ctrl = ^, Shift = $, Ctrl+Shift = @ + */ + { 0, "\033[11^", KEYC_F1|KEYC_CTRL, TTYKEY_RAW }, + { 0, "\033[12^", KEYC_F2|KEYC_CTRL, TTYKEY_RAW }, + { 0, "\033[13^", KEYC_F3|KEYC_CTRL, TTYKEY_RAW }, + { 0, "\033[14^", KEYC_F4|KEYC_CTRL, TTYKEY_RAW }, + { 0, "\033[15^", KEYC_F5|KEYC_CTRL, TTYKEY_RAW }, + { 0, "\033[17^", KEYC_F6|KEYC_CTRL, TTYKEY_RAW }, + { 0, "\033[18^", KEYC_F7|KEYC_CTRL, TTYKEY_RAW }, + { 0, "\033[19^", KEYC_F8|KEYC_CTRL, TTYKEY_RAW }, + { 0, "\033[20^", KEYC_F9|KEYC_CTRL, TTYKEY_RAW }, + { 0, "\033[21^", KEYC_F10|KEYC_CTRL, TTYKEY_RAW }, + { 0, "\033[23^", KEYC_F11|KEYC_CTRL, TTYKEY_RAW }, + { 0, "\033[24^", KEYC_F12|KEYC_CTRL, TTYKEY_RAW }, + { 0, "\033[25^", KEYC_F13|KEYC_CTRL, TTYKEY_RAW }, + { 0, "\033[26^", KEYC_F14|KEYC_CTRL, TTYKEY_RAW }, + { 0, "\033[28^", KEYC_F15|KEYC_CTRL, TTYKEY_RAW }, + { 0, "\033[29^", KEYC_F16|KEYC_CTRL, TTYKEY_RAW }, + { 0, "\033[31^", KEYC_F17|KEYC_CTRL, TTYKEY_RAW }, + { 0, "\033[32^", KEYC_F18|KEYC_CTRL, TTYKEY_RAW }, + { 0, "\033[33^", KEYC_F19|KEYC_CTRL, TTYKEY_RAW }, + { 0, "\033[34^", KEYC_F20|KEYC_CTRL, TTYKEY_RAW }, + { 0, "\033[2^", KEYC_IC|KEYC_CTRL, TTYKEY_RAW }, + { 0, "\033[3^", KEYC_DC|KEYC_CTRL, TTYKEY_RAW }, + { 0, "\033[7^", KEYC_HOME|KEYC_CTRL, TTYKEY_RAW }, + { 0, "\033[8^", KEYC_END|KEYC_CTRL, TTYKEY_RAW }, + { 0, "\033[6^", KEYC_NPAGE|KEYC_CTRL, TTYKEY_RAW }, + { 0, "\033[5^", KEYC_PPAGE|KEYC_CTRL, TTYKEY_RAW }, + + { 0, "\033[11$", KEYC_F1|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[12$", KEYC_F2|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[13$", KEYC_F3|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[14$", KEYC_F4|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[15$", KEYC_F5|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[17$", KEYC_F6|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[18$", KEYC_F7|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[19$", KEYC_F8|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[20$", KEYC_F9|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[21$", KEYC_F10|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[23$", KEYC_F11|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[24$", KEYC_F12|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[25$", KEYC_F13|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[26$", KEYC_F14|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[28$", KEYC_F15|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[29$", KEYC_F16|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[31$", KEYC_F17|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[32$", KEYC_F18|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[33$", KEYC_F19|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[34$", KEYC_F20|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[2$", KEYC_IC|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[3$", KEYC_DC|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[7$", KEYC_HOME|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[8$", KEYC_END|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[6$", KEYC_NPAGE|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[5$", KEYC_PPAGE|KEYC_SHIFT, TTYKEY_RAW }, + + { 0, "\033[11@", KEYC_F1|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[12@", KEYC_F2|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[13@", KEYC_F3|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[14@", KEYC_F4|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[15@", KEYC_F5|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[17@", KEYC_F6|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[18@", KEYC_F7|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[19@", KEYC_F8|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[20@", KEYC_F9|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[21@", KEYC_F10|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[23@", KEYC_F11|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[24@", KEYC_F12|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[25@", KEYC_F13|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[26@", KEYC_F14|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[28@", KEYC_F15|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[29@", KEYC_F16|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[31@", KEYC_F17|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[32@", KEYC_F18|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[33@", KEYC_F19|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[34@", KEYC_F20|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[2@", KEYC_IC|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[3@", KEYC_DC|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[7@", KEYC_HOME|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[8@", KEYC_END|KEYC_CTRL|KEYC_SHIFT, TTYKEY_RAW }, + { 0, "\033[6@", KEYC_NPAGE|KEYC_CTRL|KEYC_SHIFT,TTYKEY_RAW }, + { 0, "\033[5@", KEYC_PPAGE|KEYC_CTRL|KEYC_SHIFT,TTYKEY_RAW }, + + /* terminfo lookups below this line so they can override raw keys. */ + + /* Function keys. */ + { TTYC_KF1, NULL, KEYC_F1, 0 }, + { TTYC_KF2, NULL, KEYC_F2, 0 }, + { TTYC_KF3, NULL, KEYC_F3, 0 }, + { TTYC_KF4, NULL, KEYC_F4, 0 }, + { TTYC_KF5, NULL, KEYC_F5, 0 }, + { TTYC_KF6, NULL, KEYC_F6, 0 }, + { TTYC_KF7, NULL, KEYC_F7, 0 }, + { TTYC_KF8, NULL, KEYC_F8, 0 }, + { TTYC_KF9, NULL, KEYC_F9, 0 }, + { TTYC_KF10, NULL, KEYC_F10, 0 }, + { TTYC_KF11, NULL, KEYC_F11, 0 }, + { TTYC_KF12, NULL, KEYC_F12, 0 }, + { TTYC_KF13, NULL, KEYC_F13, 0 }, + { TTYC_KF14, NULL, KEYC_F14, 0 }, + { TTYC_KF15, NULL, KEYC_F15, 0 }, + { TTYC_KF16, NULL, KEYC_F16, 0 }, + { TTYC_KF17, NULL, KEYC_F17, 0 }, + { TTYC_KF18, NULL, KEYC_F18, 0 }, + { TTYC_KF19, NULL, KEYC_F19, 0 }, + { TTYC_KF20, NULL, KEYC_F20, 0 }, + { TTYC_KICH1, NULL, KEYC_IC, 0 }, + { TTYC_KDCH1, NULL, KEYC_DC, 0 }, + { TTYC_KHOME, NULL, KEYC_HOME, 0 }, + { TTYC_KEND, NULL, KEYC_END, 0 }, + { TTYC_KNP, NULL, KEYC_NPAGE, 0 }, + { TTYC_KPP, NULL, KEYC_PPAGE, 0 }, + { TTYC_KCBT, NULL, KEYC_BTAB, 0 }, + + /* Arrow keys from terminfo. */ + { TTYC_KCUU1, NULL, KEYC_UP, 0 }, + { TTYC_KCUD1, NULL, KEYC_DOWN, 0 }, + { TTYC_KCUB1, NULL, KEYC_LEFT, 0 }, + { TTYC_KCUF1, NULL, KEYC_RIGHT, 0 }, + + /* Key and modifier capabilities. */ + { TTYC_KDC2, NULL, KEYC_DC|KEYC_SHIFT, 0 }, + { TTYC_KDC3, NULL, KEYC_DC|KEYC_ESCAPE, 0 }, + { TTYC_KDC4, NULL, KEYC_DC|KEYC_SHIFT|KEYC_ESCAPE, 0 }, + { TTYC_KDC5, NULL, KEYC_DC|KEYC_CTRL, 0 }, + { TTYC_KDC6, NULL, KEYC_DC|KEYC_SHIFT|KEYC_CTRL, 0 }, + { TTYC_KDC7, NULL, KEYC_DC|KEYC_ESCAPE|KEYC_CTRL, 0 }, + { TTYC_KDN2, NULL, KEYC_DOWN|KEYC_SHIFT, 0 }, + { TTYC_KDN3, NULL, KEYC_DOWN|KEYC_ESCAPE, 0 }, + { TTYC_KDN4, NULL, KEYC_DOWN|KEYC_SHIFT|KEYC_ESCAPE, 0 }, + { TTYC_KDN5, NULL, KEYC_DOWN|KEYC_CTRL, 0 }, + { TTYC_KDN6, NULL, KEYC_DOWN|KEYC_SHIFT|KEYC_CTRL, 0 }, + { TTYC_KDN7, NULL, KEYC_DOWN|KEYC_ESCAPE|KEYC_CTRL, 0 }, + { TTYC_KEND2, NULL, KEYC_END|KEYC_SHIFT, 0 }, + { TTYC_KEND3, NULL, KEYC_END|KEYC_ESCAPE, 0 }, + { TTYC_KEND4, NULL, KEYC_END|KEYC_SHIFT|KEYC_ESCAPE, 0 }, + { TTYC_KEND5, NULL, KEYC_END|KEYC_CTRL, 0 }, + { TTYC_KEND6, NULL, KEYC_END|KEYC_SHIFT|KEYC_CTRL, 0 }, + { TTYC_KEND7, NULL, KEYC_END|KEYC_ESCAPE|KEYC_CTRL, 0 }, + { TTYC_KHOM2, NULL, KEYC_HOME|KEYC_SHIFT, 0 }, + { TTYC_KHOM3, NULL, KEYC_HOME|KEYC_ESCAPE, 0 }, + { TTYC_KHOM4, NULL, KEYC_HOME|KEYC_SHIFT|KEYC_ESCAPE, 0 }, + { TTYC_KHOM5, NULL, KEYC_HOME|KEYC_CTRL, 0 }, + { TTYC_KHOM6, NULL, KEYC_HOME|KEYC_SHIFT|KEYC_CTRL, 0 }, + { TTYC_KHOM7, NULL, KEYC_HOME|KEYC_ESCAPE|KEYC_CTRL, 0 }, + { TTYC_KIC2, NULL, KEYC_IC|KEYC_SHIFT, 0 }, + { TTYC_KIC3, NULL, KEYC_IC|KEYC_ESCAPE, 0 }, + { TTYC_KIC4, NULL, KEYC_IC|KEYC_SHIFT|KEYC_ESCAPE, 0 }, + { TTYC_KIC5, NULL, KEYC_IC|KEYC_CTRL, 0 }, + { TTYC_KIC6, NULL, KEYC_IC|KEYC_SHIFT|KEYC_CTRL, 0 }, + { TTYC_KIC7, NULL, KEYC_IC|KEYC_ESCAPE|KEYC_CTRL, 0 }, + { TTYC_KLFT2, NULL, KEYC_LEFT|KEYC_SHIFT, 0 }, + { TTYC_KLFT3, NULL, KEYC_LEFT|KEYC_ESCAPE, 0 }, + { TTYC_KLFT4, NULL, KEYC_LEFT|KEYC_SHIFT|KEYC_ESCAPE, 0 }, + { TTYC_KLFT5, NULL, KEYC_LEFT|KEYC_CTRL, 0 }, + { TTYC_KLFT6, NULL, KEYC_LEFT|KEYC_SHIFT|KEYC_CTRL, 0 }, + { TTYC_KLFT7, NULL, KEYC_LEFT|KEYC_ESCAPE|KEYC_CTRL, 0 }, + { TTYC_KNXT2, NULL, KEYC_NPAGE|KEYC_SHIFT, 0 }, + { TTYC_KNXT3, NULL, KEYC_NPAGE|KEYC_ESCAPE, 0 }, + { TTYC_KNXT4, NULL, KEYC_NPAGE|KEYC_SHIFT|KEYC_ESCAPE, 0 }, + { TTYC_KNXT5, NULL, KEYC_NPAGE|KEYC_CTRL, 0 }, + { TTYC_KNXT6, NULL, KEYC_NPAGE|KEYC_SHIFT|KEYC_CTRL, 0 }, + { TTYC_KNXT7, NULL, KEYC_NPAGE|KEYC_ESCAPE|KEYC_CTRL, 0 }, + { TTYC_KPRV2, NULL, KEYC_PPAGE|KEYC_SHIFT, 0 }, + { TTYC_KPRV3, NULL, KEYC_PPAGE|KEYC_ESCAPE, 0 }, + { TTYC_KPRV4, NULL, KEYC_PPAGE|KEYC_SHIFT|KEYC_ESCAPE, 0 }, + { TTYC_KPRV5, NULL, KEYC_PPAGE|KEYC_CTRL, 0 }, + { TTYC_KPRV6, NULL, KEYC_PPAGE|KEYC_SHIFT|KEYC_CTRL, 0 }, + { TTYC_KPRV7, NULL, KEYC_PPAGE|KEYC_ESCAPE|KEYC_CTRL, 0 }, + { TTYC_KRIT2, NULL, KEYC_RIGHT|KEYC_SHIFT, 0 }, + { TTYC_KRIT3, NULL, KEYC_RIGHT|KEYC_ESCAPE, 0 }, + { TTYC_KRIT4, NULL, KEYC_RIGHT|KEYC_SHIFT|KEYC_ESCAPE, 0 }, + { TTYC_KRIT5, NULL, KEYC_RIGHT|KEYC_CTRL, 0 }, + { TTYC_KRIT6, NULL, KEYC_RIGHT|KEYC_SHIFT|KEYC_CTRL, 0 }, + { TTYC_KRIT7, NULL, KEYC_RIGHT|KEYC_ESCAPE|KEYC_CTRL, 0 }, + { TTYC_KUP2, NULL, KEYC_UP|KEYC_SHIFT, 0 }, + { TTYC_KUP3, NULL, KEYC_UP|KEYC_ESCAPE, 0 }, + { TTYC_KUP4, NULL, KEYC_UP|KEYC_SHIFT|KEYC_ESCAPE, 0 }, + { TTYC_KUP5, NULL, KEYC_UP|KEYC_CTRL, 0 }, + { TTYC_KUP6, NULL, KEYC_UP|KEYC_SHIFT|KEYC_CTRL, 0 }, + { TTYC_KUP7, NULL, KEYC_UP|KEYC_ESCAPE|KEYC_CTRL, 0 }, +}; + +void +tty_keys_add(struct tty *tty, const char *s, int key) +{ + struct tty_key *tk; + size_t size; + const char *keystr; + + keystr = key_string_lookup_key(key); + if ((tk = tty_keys_find(tty, s, strlen(s), &size)) == NULL) { + log_debug("new key %s: 0x%x (%s)", s, key, keystr); + tty_keys_add1(&tty->key_tree, s, key); + } else { + log_debug("replacing key %s: 0x%x (%s)", s, key, keystr); + tk->key = key; + } +} + +/* Add next node to the tree. */ +void +tty_keys_add1(struct tty_key **tkp, const char *s, int key) +{ + struct tty_key *tk; + + /* Allocate a tree entry if there isn't one already. */ + tk = *tkp; + if (tk == NULL) { + tk = *tkp = xcalloc(1, sizeof *tk); + tk->ch = *s; + tk->key = KEYC_NONE; + } + + /* Find the next entry. */ + if (*s == tk->ch) { + /* Move forward in string. */ + s++; + + /* If this is the end of the string, no more is necessary. */ + if (*s == '\0') { + tk->key = key; + return; + } + + /* Use the child tree for the next character. */ + tkp = &tk->next; + } else { + if (*s < tk->ch) + tkp = &tk->left; + else if (*s > tk->ch) + tkp = &tk->right; + } + + /* And recurse to add it. */ + tty_keys_add1(tkp, s, key); +} + +/* Initialise a key tree from the table. */ +void +tty_keys_init(struct tty *tty) +{ + const struct tty_key_ent *tke; + u_int i; + const char *s; + + tty->key_tree = NULL; + for (i = 0; i < nitems(tty_keys); i++) { + tke = &tty_keys[i]; + + if (tke->flags & TTYKEY_RAW) + s = tke->string; + else { + if (!tty_term_has(tty->term, tke->code)) + continue; + s = tty_term_string(tty->term, tke->code); + } + if (s[0] != '\033' || s[1] == '\0') + continue; + + tty_keys_add(tty, s + 1, tke->key); + } +} + +/* Free the entire key tree. */ +void +tty_keys_free(struct tty *tty) +{ + tty_keys_free1(tty->key_tree); +} + +/* Free a single key. */ +void +tty_keys_free1(struct tty_key *tk) +{ + if (tk->next != NULL) + tty_keys_free1(tk->next); + if (tk->left != NULL) + tty_keys_free1(tk->left); + if (tk->right != NULL) + tty_keys_free1(tk->right); + xfree(tk); +} + +/* Lookup a key in the tree. */ +struct tty_key * +tty_keys_find(struct tty *tty, const char *buf, size_t len, size_t *size) +{ + *size = 0; + return (tty_keys_find1(tty->key_tree, buf, len, size)); +} + +/* Find the next node. */ +struct tty_key * +tty_keys_find1(struct tty_key *tk, const char *buf, size_t len, size_t *size) +{ + /* If the node is NULL, this is the end of the tree. No match. */ + if (tk == NULL) + return (NULL); + + /* Pick the next in the sequence. */ + if (tk->ch == *buf) { + /* Move forward in the string. */ + buf++; len--; + (*size)++; + + /* At the end of the string, return the current node. */ + if (len == 0 || (tk->next == NULL && tk->key != KEYC_NONE)) + return (tk); + + /* Move into the next tree for the following character. */ + tk = tk->next; + } else { + if (*buf < tk->ch) + tk = tk->left; + else if (*buf > tk->ch) + tk = tk->right; + } + + /* Move to the next in the tree. */ + return (tty_keys_find1(tk, buf, len, size)); +} + +/* + * Process at least one key in the buffer and invoke tty->key_callback. Return + * 0 if there are no further keys, or 1 if there could be more in the buffer. + */ +int +tty_keys_next(struct tty *tty) +{ + struct tty_key *tk; + struct timeval tv; + struct mouse_event mouse; + const u_char *buf; + size_t len, size; + cc_t bspace; + int key, delay; + + buf = EVBUFFER_DATA(tty->event->input); + len = EVBUFFER_LENGTH(tty->event->input); + if (len == 0) + return (0); + log_debug("keys are %zu (%.*s)", len, (int) len, buf); + + /* If a normal key, return it. */ + if (*buf != '\033') { + key = *buf; + evbuffer_drain(tty->event->input, 1); + + /* + * Check for backspace key using termios VERASE - the terminfo + * kbs entry is extremely unreliable, so cannot be safely + * used. termios should have a better idea. + */ + bspace = tty->tio.c_cc[VERASE]; + if (bspace != _POSIX_VDISABLE && key == bspace) + key = KEYC_BSPACE; + goto handle_key; + } + + /* Is this a mouse key press? */ + switch (tty_keys_mouse(tty, (const char *)buf, len, &size, &mouse)) { + case 0: /* yes */ + evbuffer_drain(tty->event->input, size); + key = KEYC_MOUSE; + goto handle_key; + case -1: /* no, or not valid */ + break; + case 1: /* partial */ + goto partial_key; + } + + /* Try to parse a key with an xterm-style modifier. */ + switch (xterm_keys_find((const char *)buf, len, &size, &key)) { + case 0: /* found */ + evbuffer_drain(tty->event->input, size); + goto handle_key; + case -1: /* not found */ + break; + case 1: + goto partial_key; + } + + /* Look for matching key string and return if found. */ + tk = tty_keys_find(tty, (const char *)buf + 1, len - 1, &size); + if (tk != NULL) { + key = tk->key; + goto found_key; + } + + /* Skip the escape. */ + buf++; + len--; + + /* Is there a normal key following? */ + if (len != 0 && *buf != '\033') { + key = *buf | KEYC_ESCAPE; + evbuffer_drain(tty->event->input, 2); + goto handle_key; + } + + /* Or a key string? */ + if (len > 1) { + tk = tty_keys_find(tty, (const char *)buf + 1, len - 1, &size); + if (tk != NULL) { + key = tk->key | KEYC_ESCAPE; + size++; /* include escape */ + goto found_key; + } + } + + /* Escape and then nothing useful - fall through. */ + +partial_key: + /* + * Escape but no key string. If have already seen an escape, then the + * timer must have expired, so give up waiting and send the escape. + */ + if (tty->flags & TTY_ESCAPE) { + evbuffer_drain(tty->event->input, 1); + key = '\033'; + goto handle_key; + } + + /* Fall through to start the timer. */ + +start_timer: + /* Start the timer and wait for expiry or more data. */ + delay = options_get_number(&global_options, "escape-time"); + tv.tv_sec = delay / 1000; + tv.tv_usec = (delay % 1000) * 1000L; + + evtimer_del(&tty->key_timer); + evtimer_set(&tty->key_timer, tty_keys_callback, tty); + evtimer_add(&tty->key_timer, &tv); + + tty->flags |= TTY_ESCAPE; + return (0); + +found_key: + if (tk->next != NULL) { + /* Partial key. Start the timer if not already expired. */ + if (!(tty->flags & TTY_ESCAPE)) + goto start_timer; + + /* Otherwise, if no key, send the escape alone. */ + if (tk->key == KEYC_NONE) + goto partial_key; + + /* Or fall through to send the partial key found. */ + } + evbuffer_drain(tty->event->input, size + 1); + + goto handle_key; + +handle_key: + evtimer_del(&tty->key_timer); + + tty->key_callback(key, &mouse, tty->key_data); + + tty->flags &= ~TTY_ESCAPE; + return (1); +} + +/* Key timer callback. */ +/* ARGSUSED */ +void +tty_keys_callback(unused int fd, unused short events, void *data) +{ + struct tty *tty = data; + + if (!(tty->flags & TTY_ESCAPE)) + return; + + while (tty_keys_next(tty)) + ; +} + +/* + * Handle mouse key input. Returns 0 for success, -1 for failure, 1 for partial + * (probably a mouse sequence but need more data). + */ +int +tty_keys_mouse(struct tty *tty, + const char *buf, size_t len, size_t *size, struct mouse_event *m) +{ + struct utf8_data utf8data; + u_int i, value; + + /* + * Standard mouse sequences are \033[M followed by three characters + * indicating buttons, X and Y, all based at 32 with 1,1 top-left. + * + * UTF-8 mouse sequences are similar but the three are expressed as + * UTF-8 characters. + */ + + *size = 0; + + /* First three bytes are always \033[M. */ + if (buf[0] != '\033') + return (-1); + if (len == 1) + return (1); + if (buf[1] != '[') + return (-1); + if (len == 2) + return (1); + if (buf[2] != 'M') + return (-1); + if (len == 3) + return (1); + + /* Read the three inputs. */ + *size = 3; + for (i = 0; i < 3; i++) { + if (len < *size) + return (1); + + if (tty->mode & MODE_MOUSE_UTF8) { + if (utf8_open(&utf8data, buf[*size])) { + if (utf8data.size != 2) + return (-1); + (*size)++; + if (len < *size) + return (1); + utf8_append(&utf8data, buf[*size]); + value = utf8_combine(&utf8data); + } else + value = (unsigned char)buf[*size]; + (*size)++; + } else { + value = (unsigned char)buf[*size]; + (*size)++; + } + + if (i == 0) + m->b = value; + else if (i == 1) + m->x = value; + else + m->y = value; + } + log_debug("mouse input: %.*s", (int) *size, buf); + + /* Check and return the mouse input. */ + if (m->b < 32 || m->x < 33 || m->y < 33) + return (-1); + m->b -= 32; + m->x -= 33; + m->y -= 33; + log_debug("mouse position: x=%u y=%u b=%u", m->x, m->y, m->b); + return (0); +} diff --git a/external/bsd/tmux/dist/tty-term.c b/external/bsd/tmux/dist/tty-term.c new file mode 100644 index 000000000..e5f789de9 --- /dev/null +++ b/external/bsd/tmux/dist/tty-term.c @@ -0,0 +1,535 @@ +/* $Id: tty-term.c,v 1.4 2011/08/17 19:28:36 jmmv Exp $ */ + +/* + * Copyright (c) 2008 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#ifdef HAVE_CURSES_H +#include +#else +#include +#endif +#include +#include +#include +#include + +#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~."; + for (; acs[0] != '\0' && acs[1] != '\0'; acs += 2) + term->acs[(u_char) acs[0]][0] = acs[1]; + + /* On terminals with xterm titles (XT), fill in tsl and fsl. */ + if (tty_term_flag(term, TTYC_XT) && + !tty_term_has(term, TTYC_TSL) && + !tty_term_has(term, TTYC_FSL)) { + code = &term->codes[TTYC_TSL]; + code->value.string = xstrdup("\033]0;"); + code->type = TTYCODE_STRING; + code = &term->codes[TTYC_FSL]; + code->value.string = xstrdup("\007"); + code->type = TTYCODE_STRING; + } + + return (term); + +error: + tty_term_free(term); + return (NULL); +} + +void +tty_term_free(struct tty_term *term) +{ + u_int i; + + if (--term->references != 0) + return; + + LIST_REMOVE(term, entry); + + for (i = 0; i < NTTYCODE; i++) { + if (term->codes[i].type == TTYCODE_STRING) + xfree(term->codes[i].value.string); + } + xfree(term->name); + xfree(term); +} + +int +tty_term_has(struct tty_term *term, enum tty_code_code code) +{ + return (term->codes[code].type != TTYCODE_NONE); +} + +const char * +tty_term_string(struct tty_term *term, enum tty_code_code code) +{ + if (!tty_term_has(term, code)) + return (""); + if (term->codes[code].type != TTYCODE_STRING) + log_fatalx("not a string: %d", code); + return (term->codes[code].value.string); +} + +/* No vtparm. Fucking curses. */ +const char * +tty_term_string1(struct tty_term *term, enum tty_code_code code, int a) +{ + return (tparm(tty_term_string(term, code), a, 0, 0, 0, 0, 0, 0, 0, 0)); +} + +const char * +tty_term_string2(struct tty_term *term, enum tty_code_code code, int a, int b) +{ + return (tparm(tty_term_string(term, code), a, b, 0, 0, 0, 0, 0, 0, 0)); +} + +const char * +tty_term_ptr1(struct tty_term *term, enum tty_code_code code, const void *a) +{ + return (tparm(tty_term_string(term, code), (long)a, 0, 0, 0, 0, 0, 0, 0, 0)); +} + +const char * +tty_term_ptr2(struct tty_term *term, enum tty_code_code code, const void *a, const void *b) +{ + return (tparm(tty_term_string(term, code), (long)a, (long)b, 0, 0, 0, 0, 0, 0, 0)); +} + +int +tty_term_number(struct tty_term *term, enum tty_code_code code) +{ + if (!tty_term_has(term, code)) + return (0); + if (term->codes[code].type != TTYCODE_NUMBER) + log_fatalx("not a number: %d", code); + return (term->codes[code].value.number); +} + +int +tty_term_flag(struct tty_term *term, enum tty_code_code code) +{ + if (!tty_term_has(term, code)) + return (0); + if (term->codes[code].type != TTYCODE_FLAG) + log_fatalx("not a flag: %d", code); + return (term->codes[code].value.flag); +} diff --git a/external/bsd/tmux/dist/tty.c b/external/bsd/tmux/dist/tty.c new file mode 100644 index 000000000..885daaf06 --- /dev/null +++ b/external/bsd/tmux/dist/tty.c @@ -0,0 +1,1548 @@ +/* $Id: tty.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "tmux.h" + +void tty_read_callback(struct bufferevent *, void *); +void tty_error_callback(struct bufferevent *, short, void *); + +int tty_try_256(struct tty *, u_char, const char *); +int tty_try_88(struct tty *, u_char, const char *); + +void tty_colours(struct tty *, const struct grid_cell *); +void tty_check_fg(struct tty *, struct grid_cell *); +void tty_check_bg(struct tty *, struct grid_cell *); +void tty_colours_fg(struct tty *, const struct grid_cell *); +void tty_colours_bg(struct tty *, const struct grid_cell *); + +void tty_redraw_region(struct tty *, const struct tty_ctx *); +void tty_emulate_repeat( + struct tty *, enum tty_code_code, enum tty_code_code, u_int); +void tty_cell(struct tty *, + const struct grid_cell *, const struct grid_utf8 *); + +#define tty_use_acs(tty) \ + (tty_term_has(tty->term, TTYC_ACSC) && !((tty)->flags & TTY_UTF8)) + +void +tty_init(struct tty *tty, int fd, char *term) +{ + char *path; + + memset(tty, 0, sizeof *tty); + tty->log_fd = -1; + + if (term == NULL || *term == '\0') + tty->termname = xstrdup("unknown"); + else + tty->termname = xstrdup(term); + tty->fd = fd; + + if ((path = ttyname(fd)) == NULL) + fatalx("ttyname failed"); + tty->path = xstrdup(path); + tty->cstyle = 0; + tty->ccolour = xstrdup(""); + + tty->flags = 0; + tty->term_flags = 0; +} + +int +tty_resize(struct tty *tty) +{ + struct winsize ws; + u_int sx, sy; + + if (ioctl(tty->fd, TIOCGWINSZ, &ws) != -1) { + sx = ws.ws_col; + if (sx == 0) + sx = 80; + sy = ws.ws_row; + if (sy == 0) + sy = 24; + } else { + sx = 80; + sy = 24; + } + if (sx == tty->sx && sy == tty->sy) + return (0); + tty->sx = sx; + tty->sy = sy; + + tty->cx = UINT_MAX; + tty->cy = UINT_MAX; + + tty->rupper = UINT_MAX; + tty->rlower = UINT_MAX; + + /* + * If the terminal has been started, reset the actual scroll region and + * cursor position, as this may not have happened. + */ + if (tty->flags & TTY_STARTED) { + tty_cursor(tty, 0, 0); + tty_region(tty, 0, tty->sy - 1); + } + + return (1); +} + +int +tty_open(struct tty *tty, const char *overrides, char **cause) +{ + char out[64]; + int fd; + + if (debug_level > 3) { + xsnprintf(out, sizeof out, "tmux-out-%ld.log", (long) getpid()); + fd = open(out, O_WRONLY|O_CREAT|O_TRUNC, 0644); + if (fd != -1 && fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) + fatal("fcntl failed"); + tty->log_fd = fd; + } + + tty->term = tty_term_find(tty->termname, tty->fd, overrides, cause); + if (tty->term == NULL) { + tty_close(tty); + return (-1); + } + tty->flags |= TTY_OPENED; + + tty->flags &= ~(TTY_NOCURSOR|TTY_FREEZE|TTY_ESCAPE); + + tty->event = bufferevent_new( + tty->fd, tty_read_callback, NULL, tty_error_callback, tty); + + tty_start_tty(tty); + + tty_keys_init(tty); + + return (0); +} + +/* ARGSUSED */ +void +tty_read_callback(unused struct bufferevent *bufev, void *data) +{ + struct tty *tty = data; + + while (tty_keys_next(tty)) + ; +} + +/* ARGSUSED */ +void +tty_error_callback( + unused struct bufferevent *bufev, unused short what, unused void *data) +{ +} + +void +tty_start_tty(struct tty *tty) +{ + struct termios tio; + + if (tty->fd == -1 || tcgetattr(tty->fd, &tty->tio) != 0) + return; + + setblocking(tty->fd, 0); + + bufferevent_enable(tty->event, EV_READ|EV_WRITE); + + memcpy(&tio, &tty->tio, sizeof tio); + tio.c_iflag &= ~(IXON|IXOFF|ICRNL|INLCR|IGNCR|IMAXBEL|ISTRIP); + tio.c_iflag |= IGNBRK; + tio.c_oflag &= ~(OPOST|ONLCR|OCRNL|ONLRET); + tio.c_lflag &= ~(IEXTEN|ICANON|ECHO|ECHOE|ECHONL|ECHOCTL| + ECHOPRT|ECHOKE|ECHOCTL|ISIG); + tio.c_cc[VMIN] = 1; + tio.c_cc[VTIME] = 0; + if (tcsetattr(tty->fd, TCSANOW, &tio) == 0) + tcflush(tty->fd, TCIOFLUSH); + + tty_putcode(tty, TTYC_SMCUP); + + tty_putcode(tty, TTYC_SGR0); + memcpy(&tty->cell, &grid_default_cell, sizeof tty->cell); + + tty_putcode(tty, TTYC_RMKX); + if (tty_use_acs(tty)) + tty_putcode(tty, TTYC_ENACS); + tty_putcode(tty, TTYC_CLEAR); + + tty_putcode(tty, TTYC_CNORM); + if (tty_term_has(tty->term, TTYC_KMOUS)) + tty_puts(tty, "\033[?1000l"); + + tty->cx = UINT_MAX; + tty->cy = UINT_MAX; + + tty->rlower = UINT_MAX; + tty->rupper = UINT_MAX; + + tty->mode = MODE_CURSOR; + + tty->flags |= TTY_STARTED; + + tty_force_cursor_colour(tty, ""); +} + +void +tty_stop_tty(struct tty *tty) +{ + struct winsize ws; + + if (!(tty->flags & TTY_STARTED)) + return; + tty->flags &= ~TTY_STARTED; + + bufferevent_disable(tty->event, EV_READ|EV_WRITE); + + /* + * Be flexible about error handling and try not kill the server just + * because the fd is invalid. Things like ssh -t can easily leave us + * with a dead tty. + */ + if (ioctl(tty->fd, TIOCGWINSZ, &ws) == -1) + return; + if (tcsetattr(tty->fd, TCSANOW, &tty->tio) == -1) + return; + + setblocking(tty->fd, 1); + + tty_raw(tty, tty_term_string2(tty->term, TTYC_CSR, 0, ws.ws_row - 1)); + if (tty_use_acs(tty)) + tty_raw(tty, tty_term_string(tty->term, TTYC_RMACS)); + tty_raw(tty, tty_term_string(tty->term, TTYC_SGR0)); + tty_raw(tty, tty_term_string(tty->term, TTYC_RMKX)); + tty_raw(tty, tty_term_string(tty->term, TTYC_CLEAR)); + if (tty_term_has(tty->term, TTYC_CS1) && tty->cstyle != 0) { + if (tty_term_has(tty->term, TTYC_CSR1)) + tty_raw(tty, tty_term_string(tty->term, TTYC_CSR1)); + else + tty_raw(tty, tty_term_string1(tty->term, TTYC_CS1, 0)); + } + tty_raw(tty, tty_term_string(tty->term, TTYC_CR)); + + tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM)); + if (tty_term_has(tty->term, TTYC_KMOUS)) + tty_raw(tty, "\033[?1000l"); + + tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP)); +} + +void +tty_close(struct tty *tty) +{ + if (tty->log_fd != -1) { + close(tty->log_fd); + tty->log_fd = -1; + } + + evtimer_del(&tty->key_timer); + tty_stop_tty(tty); + + if (tty->flags & TTY_OPENED) { + bufferevent_free(tty->event); + + tty_term_free(tty->term); + tty_keys_free(tty); + + tty->flags &= ~TTY_OPENED; + } + + if (tty->fd != -1) { + close(tty->fd); + tty->fd = -1; + } +} + +void +tty_free(struct tty *tty) +{ + tty_close(tty); + + xfree(tty->ccolour); + if (tty->path != NULL) + xfree(tty->path); + if (tty->termname != NULL) + xfree(tty->termname); +} + +void +tty_raw(struct tty *tty, const char *s) +{ + write(tty->fd, s, strlen(s)); +} + +void +tty_putcode(struct tty *tty, enum tty_code_code code) +{ + tty_puts(tty, tty_term_string(tty->term, code)); +} + +void +tty_putcode1(struct tty *tty, enum tty_code_code code, int a) +{ + if (a < 0) + return; + tty_puts(tty, tty_term_string1(tty->term, code, a)); +} + +void +tty_putcode2(struct tty *tty, enum tty_code_code code, int a, int b) +{ + if (a < 0 || b < 0) + return; + tty_puts(tty, tty_term_string2(tty->term, code, a, b)); +} + +void +tty_putcode_ptr1(struct tty *tty, enum tty_code_code code, const void *a) +{ + if (a != NULL) + tty_puts(tty, tty_term_ptr1(tty->term, code, a)); +} + +void +tty_putcode_ptr2(struct tty *tty, enum tty_code_code code, const void *a, const void *b) +{ + if (a != NULL && b != NULL) + tty_puts(tty, tty_term_ptr2(tty->term, code, a, b)); +} + +void +tty_puts(struct tty *tty, const char *s) +{ + if (*s == '\0') + return; + bufferevent_write(tty->event, s, strlen(s)); + + if (tty->log_fd != -1) + write(tty->log_fd, s, strlen(s)); +} + +void +tty_putc(struct tty *tty, u_char ch) +{ + const char *acs; + u_int sx; + + if (tty->cell.attr & GRID_ATTR_CHARSET) { + acs = tty_acs_get(tty, ch); + if (acs != NULL) + bufferevent_write(tty->event, acs, strlen(acs)); + else + bufferevent_write(tty->event, &ch, 1); + } else + bufferevent_write(tty->event, &ch, 1); + + if (ch >= 0x20 && ch != 0x7f) { + sx = tty->sx; + if (tty->term->flags & TERM_EARLYWRAP) + sx--; + + if (tty->cx >= sx) { + tty->cx = 1; + if (tty->cy != tty->rlower) + tty->cy++; + } else + tty->cx++; + } + + if (tty->log_fd != -1) + write(tty->log_fd, &ch, 1); +} + +void +tty_pututf8(struct tty *tty, const struct grid_utf8 *gu) +{ + size_t size; + + size = grid_utf8_size(gu); + bufferevent_write(tty->event, gu->data, size); + if (tty->log_fd != -1) + write(tty->log_fd, gu->data, size); + tty->cx += gu->width; +} + +void +tty_set_title(struct tty *tty, const char *title) +{ + if (!tty_term_has(tty->term, TTYC_TSL) || + !tty_term_has(tty->term, TTYC_FSL)) + return; + + tty_putcode(tty, TTYC_TSL); + tty_puts(tty, title); + tty_putcode(tty, TTYC_FSL); +} + +void +tty_force_cursor_colour(struct tty *tty, const char *ccolour) +{ + if (*ccolour == '\0') + tty_putcode(tty, TTYC_CR); + else + tty_putcode_ptr1(tty, TTYC_CC, ccolour); + xfree(tty->ccolour); + tty->ccolour = xstrdup(ccolour); +} + +void +tty_update_mode(struct tty *tty, int mode, struct screen *s) +{ + int changed; + + if (strcmp(s->ccolour, tty->ccolour)) + tty_force_cursor_colour(tty, s->ccolour); + + if (tty->flags & TTY_NOCURSOR) + mode &= ~MODE_CURSOR; + + changed = mode ^ tty->mode; + if (changed & MODE_CURSOR) { + if (mode & MODE_CURSOR) + tty_putcode(tty, TTYC_CNORM); + else + tty_putcode(tty, TTYC_CIVIS); + } + if (tty->cstyle != s->cstyle) { + if (tty_term_has(tty->term, TTYC_CS1)) { + if (s->cstyle == 0 && + tty_term_has(tty->term, TTYC_CSR1)) + tty_putcode(tty, TTYC_CSR1); + else + tty_putcode1(tty, TTYC_CS1, s->cstyle); + } + tty->cstyle = s->cstyle; + } + if (changed & ALL_MOUSE_MODES) { + if (mode & ALL_MOUSE_MODES) { + if (mode & MODE_MOUSE_UTF8) + tty_puts(tty, "\033[?1005h"); + if (mode & MODE_MOUSE_ANY) + tty_puts(tty, "\033[?1003h"); + else if (mode & MODE_MOUSE_BUTTON) + tty_puts(tty, "\033[?1002h"); + else if (mode & MODE_MOUSE_STANDARD) + tty_puts(tty, "\033[?1000h"); + } else { + if (tty->mode & MODE_MOUSE_ANY) + tty_puts(tty, "\033[?1003l"); + else if (tty->mode & MODE_MOUSE_BUTTON) + tty_puts(tty, "\033[?1002l"); + else if (tty->mode & MODE_MOUSE_STANDARD) + tty_puts(tty, "\033[?1000l"); + if (tty->mode & MODE_MOUSE_UTF8) + tty_puts(tty, "\033[?1005l"); + } + } + if (changed & MODE_KKEYPAD) { + if (mode & MODE_KKEYPAD) + tty_putcode(tty, TTYC_SMKX); + else + tty_putcode(tty, TTYC_RMKX); + } + tty->mode = mode; +} + +void +tty_emulate_repeat( + struct tty *tty, enum tty_code_code code, enum tty_code_code code1, u_int n) +{ + if (tty_term_has(tty->term, code)) + tty_putcode1(tty, code, n); + else { + while (n-- > 0) + tty_putcode(tty, code1); + } +} + +/* + * Redraw scroll region using data from screen (already updated). Used when + * CSR not supported, or window is a pane that doesn't take up the full + * width of the terminal. + */ +void +tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx) +{ + struct window_pane *wp = ctx->wp; + struct screen *s = wp->screen; + u_int i; + + /* + * If region is >= 50% of the screen, just schedule a window redraw. In + * most cases, this is likely to be followed by some more scrolling - + * without this, the entire pane ends up being redrawn many times which + * can be much more data. + */ + if (ctx->orlower - ctx->orupper >= screen_size_y(s) / 2) { + wp->flags |= PANE_REDRAW; + return; + } + + if (ctx->ocy < ctx->orupper || ctx->ocy > ctx->orlower) { + for (i = ctx->ocy; i < screen_size_y(s); i++) + tty_draw_line(tty, s, i, wp->xoff, wp->yoff); + } else { + for (i = ctx->orupper; i <= ctx->orlower; i++) + tty_draw_line(tty, s, i, wp->xoff, wp->yoff); + } +} + +void +tty_draw_line(struct tty *tty, struct screen *s, u_int py, u_int ox, u_int oy) +{ + const struct grid_cell *gc; + struct grid_line *gl; + struct grid_cell tmpgc; + const struct grid_utf8 *gu; + u_int i, sx; + + tty_update_mode(tty, tty->mode & ~MODE_CURSOR, s); + + sx = screen_size_x(s); + if (sx > s->grid->linedata[s->grid->hsize + py].cellsize) + sx = s->grid->linedata[s->grid->hsize + py].cellsize; + if (sx > tty->sx) + sx = tty->sx; + + /* + * Don't move the cursor to the start permission if it will wrap there + * itself. + */ + gl = NULL; + if (py != 0) + gl = &s->grid->linedata[s->grid->hsize + py - 1]; + if (oy + py == 0 || gl == NULL || !(gl->flags & GRID_LINE_WRAPPED) || + tty->cx < tty->sx || ox != 0 || + (oy + py != tty->cy + 1 && tty->cy != s->rlower + oy)) + tty_cursor(tty, ox, oy + py); + + for (i = 0; i < sx; i++) { + gc = grid_view_peek_cell(s->grid, i, py); + + gu = NULL; + if (gc->flags & GRID_FLAG_UTF8) + gu = grid_view_peek_utf8(s->grid, i, py); + + if (screen_check_selection(s, i, py)) { + memcpy(&tmpgc, &s->sel.cell, sizeof tmpgc); + tmpgc.data = gc->data; + tmpgc.flags = gc->flags & + ~(GRID_FLAG_FG256|GRID_FLAG_BG256); + tmpgc.flags |= s->sel.cell.flags & + (GRID_FLAG_FG256|GRID_FLAG_BG256); + tty_cell(tty, &tmpgc, gu); + } else + tty_cell(tty, gc, gu); + } + + if (sx >= tty->sx) { + tty_update_mode(tty, tty->mode, s); + return; + } + tty_reset(tty); + + tty_cursor(tty, ox + sx, oy + py); + if (screen_size_x(s) >= tty->sx && tty_term_has(tty->term, TTYC_EL)) + tty_putcode(tty, TTYC_EL); + else { + for (i = sx; i < screen_size_x(s); i++) + tty_putc(tty, ' '); + } + tty_update_mode(tty, tty->mode, s); +} + +void +tty_write(void (*cmdfn)( + struct tty *, const struct tty_ctx *), const struct tty_ctx *ctx) +{ + struct window_pane *wp = ctx->wp; + struct client *c; + u_int i; + + /* wp can be NULL if updating the screen but not the terminal. */ + if (wp == NULL) + return; + + if (wp->window->flags & WINDOW_REDRAW || wp->flags & PANE_REDRAW) + return; + if (!window_pane_visible(wp)) + return; + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session == NULL) + continue; + if (c->flags & CLIENT_SUSPENDED) + continue; + + if (c->session->curw->window == wp->window) { + if (c->tty.term == NULL) + continue; + if (c->tty.flags & (TTY_FREEZE|TTY_BACKOFF)) + continue; + cmdfn(&c->tty, ctx); + } + } +} + +void +tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx) +{ + struct window_pane *wp = ctx->wp; + struct screen *s = wp->screen; + u_int i; + + if (wp->xoff != 0 || screen_size_x(s) < tty->sx) { + tty_draw_line(tty, wp->screen, ctx->ocy, wp->xoff, wp->yoff); + return; + } + + tty_reset(tty); + + tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); + + if (tty_term_has(tty->term, TTYC_ICH) || + tty_term_has(tty->term, TTYC_ICH1)) + tty_emulate_repeat(tty, TTYC_ICH, TTYC_ICH1, ctx->num); + else if (tty_term_has(tty->term, TTYC_SMIR) && + tty_term_has(tty->term, TTYC_RMIR)) { + tty_putcode(tty, TTYC_SMIR); + for (i = 0; i < ctx->num; i++) + tty_putc(tty, ' '); + tty_putcode(tty, TTYC_RMIR); + } else + tty_draw_line(tty, wp->screen, ctx->ocy, wp->xoff, wp->yoff); +} + +void +tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx) +{ + struct window_pane *wp = ctx->wp; + struct screen *s = wp->screen; + + if (wp->xoff != 0 || screen_size_x(s) < tty->sx || + (!tty_term_has(tty->term, TTYC_DCH) && + !tty_term_has(tty->term, TTYC_DCH1))) { + tty_draw_line(tty, wp->screen, ctx->ocy, wp->xoff, wp->yoff); + return; + } + + tty_reset(tty); + + tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); + + if (tty_term_has(tty->term, TTYC_DCH) || + tty_term_has(tty->term, TTYC_DCH1)) + tty_emulate_repeat(tty, TTYC_DCH, TTYC_DCH1, ctx->num); +} + +void +tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx) +{ + struct window_pane *wp = ctx->wp; + struct screen *s = wp->screen; + + if (wp->xoff != 0 || screen_size_x(s) < tty->sx || + !tty_term_has(tty->term, TTYC_CSR) || + !tty_term_has(tty->term, TTYC_IL1)) { + tty_redraw_region(tty, ctx); + return; + } + + tty_reset(tty); + + tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); + tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); + + tty_emulate_repeat(tty, TTYC_IL, TTYC_IL1, ctx->num); +} + +void +tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx) +{ + struct window_pane *wp = ctx->wp; + struct screen *s = wp->screen; + + if (wp->xoff != 0 || screen_size_x(s) < tty->sx || + !tty_term_has(tty->term, TTYC_CSR) || + !tty_term_has(tty->term, TTYC_DL1)) { + tty_redraw_region(tty, ctx); + return; + } + + tty_reset(tty); + + tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); + tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); + + tty_emulate_repeat(tty, TTYC_DL, TTYC_DL1, ctx->num); +} + +void +tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx) +{ + struct window_pane *wp = ctx->wp; + struct screen *s = wp->screen; + u_int i; + + tty_reset(tty); + + tty_cursor_pane(tty, ctx, 0, ctx->ocy); + + if (wp->xoff == 0 && screen_size_x(s) >= tty->sx && + tty_term_has(tty->term, TTYC_EL)) { + tty_putcode(tty, TTYC_EL); + } else { + for (i = 0; i < screen_size_x(s); i++) + tty_putc(tty, ' '); + } +} + +void +tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx) +{ + struct window_pane *wp = ctx->wp; + struct screen *s = wp->screen; + u_int i; + + tty_reset(tty); + + tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); + + if (wp->xoff == 0 && screen_size_x(s) >= tty->sx && + tty_term_has(tty->term, TTYC_EL)) + tty_putcode(tty, TTYC_EL); + else { + for (i = ctx->ocx; i < screen_size_x(s); i++) + tty_putc(tty, ' '); + } +} + +void +tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx) +{ + struct window_pane *wp = ctx->wp; + u_int i; + + tty_reset(tty); + + if (wp->xoff == 0 && tty_term_has(tty->term, TTYC_EL1)) { + tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); + tty_putcode(tty, TTYC_EL1); + } else { + tty_cursor_pane(tty, ctx, 0, ctx->ocy); + for (i = 0; i < ctx->ocx + 1; i++) + tty_putc(tty, ' '); + } +} + +void +tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx) +{ + struct window_pane *wp = ctx->wp; + struct screen *s = wp->screen; + + if (ctx->ocy != ctx->orupper) + return; + + if (wp->xoff != 0 || screen_size_x(s) < tty->sx || + !tty_term_has(tty->term, TTYC_CSR) || + !tty_term_has(tty->term, TTYC_RI)) { + tty_redraw_region(tty, ctx); + return; + } + + tty_reset(tty); + + tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); + tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper); + + tty_putcode(tty, TTYC_RI); +} + +void +tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) +{ + struct window_pane *wp = ctx->wp; + struct screen *s = wp->screen; + + if (ctx->ocy != ctx->orlower) + return; + + if (wp->xoff != 0 || screen_size_x(s) < tty->sx || + !tty_term_has(tty->term, TTYC_CSR)) { + tty_redraw_region(tty, ctx); + return; + } + + /* + * If this line wrapped naturally (ctx->num is nonzero), don't do + * anything - the cursor can just be moved to the last cell and wrap + * naturally. + */ + if (ctx->num && !(tty->term->flags & TERM_EARLYWRAP)) + return; + + tty_reset(tty); + + tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); + tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); + + tty_putc(tty, '\n'); +} + +void +tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx) +{ + struct window_pane *wp = ctx->wp; + struct screen *s = wp->screen; + u_int i, j; + + tty_reset(tty); + + tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); + tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); + + if (wp->xoff == 0 && screen_size_x(s) >= tty->sx && + tty_term_has(tty->term, TTYC_EL)) { + tty_putcode(tty, TTYC_EL); + if (ctx->ocy != screen_size_y(s) - 1) { + tty_cursor_pane(tty, ctx, 0, ctx->ocy + 1); + for (i = ctx->ocy + 1; i < screen_size_y(s); i++) { + tty_putcode(tty, TTYC_EL); + if (i == screen_size_y(s) - 1) + continue; + tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1); + tty->cy++; + } + } + } else { + for (i = ctx->ocx; i < screen_size_x(s); i++) + tty_putc(tty, ' '); + for (j = ctx->ocy + 1; j < screen_size_y(s); j++) { + tty_cursor_pane(tty, ctx, 0, j); + for (i = 0; i < screen_size_x(s); i++) + tty_putc(tty, ' '); + } + } +} + +void +tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx) +{ + struct window_pane *wp = ctx->wp; + struct screen *s = wp->screen; + u_int i, j; + + tty_reset(tty); + + tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); + tty_cursor_pane(tty, ctx, 0, 0); + + if (wp->xoff == 0 && screen_size_x(s) >= tty->sx && + tty_term_has(tty->term, TTYC_EL)) { + for (i = 0; i < ctx->ocy; i++) { + tty_putcode(tty, TTYC_EL); + tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1); + tty->cy++; + } + } else { + for (j = 0; j < ctx->ocy; j++) { + tty_cursor_pane(tty, ctx, 0, j); + for (i = 0; i < screen_size_x(s); i++) + tty_putc(tty, ' '); + } + } + for (i = 0; i <= ctx->ocx; i++) + tty_putc(tty, ' '); +} + +void +tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx) +{ + struct window_pane *wp = ctx->wp; + struct screen *s = wp->screen; + u_int i, j; + + tty_reset(tty); + + tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); + tty_cursor_pane(tty, ctx, 0, 0); + + if (wp->xoff == 0 && screen_size_x(s) >= tty->sx && + tty_term_has(tty->term, TTYC_EL)) { + for (i = 0; i < screen_size_y(s); i++) { + tty_putcode(tty, TTYC_EL); + if (i != screen_size_y(s) - 1) { + tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1); + tty->cy++; + } + } + } else { + for (j = 0; j < screen_size_y(s); j++) { + tty_cursor_pane(tty, ctx, 0, j); + for (i = 0; i < screen_size_x(s); i++) + tty_putc(tty, ' '); + } + } +} + +void +tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx) +{ + struct window_pane *wp = ctx->wp; + struct screen *s = wp->screen; + u_int i, j; + + tty_reset(tty); + + tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); + + for (j = 0; j < screen_size_y(s); j++) { + tty_cursor_pane(tty, ctx, 0, j); + for (i = 0; i < screen_size_x(s); i++) + tty_putc(tty, 'E'); + } +} + +void +tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) +{ + struct window_pane *wp = ctx->wp; + struct screen *s = wp->screen; + u_int cx; + u_int width; + const struct grid_cell *gc = ctx->cell; + const struct grid_utf8 *gu = ctx->utf8; + + if (gc->flags & GRID_FLAG_UTF8) + width = gu->width; + else + width = 1; + + tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); + + /* Is the cursor in the very last position? */ + if (ctx->ocx > wp->sx - width) { + if (wp->xoff != 0 || wp->sx != tty->sx) { + /* + * The pane doesn't fill the entire line, the linefeed + * will already have happened, so just move the cursor. + */ + tty_cursor_pane(tty, ctx, 0, ctx->ocy + 1); + } else if (tty->cx < tty->sx) { + /* + * The cursor isn't in the last position already, so + * move as far left as possible and redraw the last + * cell to move into the last position. + */ + cx = screen_size_x(s) - width; + tty_cursor_pane(tty, ctx, cx, ctx->ocy); + tty_cell(tty, &ctx->last_cell, &ctx->last_utf8); + } + } else + tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); + + tty_cell(tty, ctx->cell, ctx->utf8); +} + +void +tty_cmd_utf8character(struct tty *tty, const struct tty_ctx *ctx) +{ + struct window_pane *wp = ctx->wp; + + /* + * Cannot rely on not being a partial character, so just redraw the + * whole line. + */ + tty_draw_line(tty, wp->screen, ctx->ocy, wp->xoff, wp->yoff); +} + +void +tty_cmd_setselection(struct tty *tty, const struct tty_ctx *ctx) +{ + char *buf; + size_t off; + + if (!tty_term_has(tty->term, TTYC_MS)) + return; + + off = 4 * ((ctx->num + 2) / 3) + 1; /* storage for base64 */ + buf = xmalloc(off); + + b64_ntop(ctx->ptr, ctx->num, buf, off); + tty_putcode_ptr2(tty, TTYC_MS, "", buf); + + xfree(buf); +} + +void +tty_cmd_rawstring(struct tty *tty, const struct tty_ctx *ctx) +{ + u_int i; + u_char *str = ctx->ptr; + + for (i = 0; i < ctx->num; i++) + tty_putc(tty, str[i]); +} + +void +tty_cell( + struct tty *tty, const struct grid_cell *gc, const struct grid_utf8 *gu) +{ + u_int i; + + /* Skip last character if terminal is stupid. */ + if (tty->term->flags & TERM_EARLYWRAP && + tty->cy == tty->sy - 1 && tty->cx == tty->sx - 1) + return; + + /* If this is a padding character, do nothing. */ + if (gc->flags & GRID_FLAG_PADDING) + return; + + /* Set the attributes. */ + tty_attributes(tty, gc); + + /* If not UTF-8, write directly. */ + if (!(gc->flags & GRID_FLAG_UTF8)) { + if (gc->data < 0x20 || gc->data == 0x7f) + return; + tty_putc(tty, gc->data); + return; + } + + /* If the terminal doesn't support UTF-8, write underscores. */ + if (!(tty->flags & TTY_UTF8)) { + for (i = 0; i < gu->width; i++) + tty_putc(tty, '_'); + return; + } + + /* Otherwise, write UTF-8. */ + tty_pututf8(tty, gu); +} + +void +tty_reset(struct tty *tty) +{ + struct grid_cell *gc = &tty->cell; + + if (memcmp(gc, &grid_default_cell, sizeof *gc) == 0) + return; + + if ((gc->attr & GRID_ATTR_CHARSET) && tty_use_acs(tty)) + tty_putcode(tty, TTYC_RMACS); + tty_putcode(tty, TTYC_SGR0); + memcpy(gc, &grid_default_cell, sizeof *gc); +} + +/* Set region inside pane. */ +void +tty_region_pane( + struct tty *tty, const struct tty_ctx *ctx, u_int rupper, u_int rlower) +{ + struct window_pane *wp = ctx->wp; + + tty_region(tty, wp->yoff + rupper, wp->yoff + rlower); +} + +/* Set region at absolute position. */ +void +tty_region(struct tty *tty, u_int rupper, u_int rlower) +{ + if (tty->rlower == rlower && tty->rupper == rupper) + return; + if (!tty_term_has(tty->term, TTYC_CSR)) + return; + + tty->rupper = rupper; + tty->rlower = rlower; + + /* + * Some terminals (such as PuTTY) do not correctly reset the cursor to + * 0,0 if it is beyond the last column (they do not reset their wrap + * flag so further output causes a line feed). As a workaround, do an + * explicit move to 0 first. + */ + if (tty->cx >= tty->sx) + tty_cursor(tty, 0, tty->cy); + + tty_putcode2(tty, TTYC_CSR, tty->rupper, tty->rlower); + tty_cursor(tty, 0, 0); +} + +/* Move cursor inside pane. */ +void +tty_cursor_pane(struct tty *tty, const struct tty_ctx *ctx, u_int cx, u_int cy) +{ + struct window_pane *wp = ctx->wp; + + tty_cursor(tty, wp->xoff + cx, wp->yoff + cy); +} + +/* Move cursor to absolute position. */ +void +tty_cursor(struct tty *tty, u_int cx, u_int cy) +{ + struct tty_term *term = tty->term; + u_int thisx, thisy; + int change; + + if (cx > tty->sx - 1) + cx = tty->sx - 1; + + thisx = tty->cx; + thisy = tty->cy; + + /* No change. */ + if (cx == thisx && cy == thisy) + return; + + /* Very end of the line, just use absolute movement. */ + if (thisx > tty->sx - 1) + goto absolute; + + /* Move to home position (0, 0). */ + if (cx == 0 && cy == 0 && tty_term_has(term, TTYC_HOME)) { + tty_putcode(tty, TTYC_HOME); + goto out; + } + + /* Zero on the next line. */ + if (cx == 0 && cy == thisy + 1 && thisy != tty->rlower) { + tty_putc(tty, '\r'); + tty_putc(tty, '\n'); + goto out; + } + + /* Moving column or row. */ + if (cy == thisy) { + /* + * Moving column only, row staying the same. + */ + + /* To left edge. */ + if (cx == 0) { + tty_putc(tty, '\r'); + goto out; + } + + /* One to the left. */ + if (cx == thisx - 1 && tty_term_has(term, TTYC_CUB1)) { + tty_putcode(tty, TTYC_CUB1); + goto out; + } + + /* One to the right. */ + if (cx == thisx + 1 && tty_term_has(term, TTYC_CUF1)) { + tty_putcode(tty, TTYC_CUF1); + goto out; + } + + /* Calculate difference. */ + change = thisx - cx; /* +ve left, -ve right */ + + /* + * Use HPA if change is larger than absolute, otherwise move + * the cursor with CUB/CUF. + */ + if ((u_int) abs(change) > cx && tty_term_has(term, TTYC_HPA)) { + tty_putcode1(tty, TTYC_HPA, cx); + goto out; + } else if (change > 0 && tty_term_has(term, TTYC_CUB)) { + tty_putcode1(tty, TTYC_CUB, change); + goto out; + } else if (change < 0 && tty_term_has(term, TTYC_CUF)) { + tty_putcode1(tty, TTYC_CUF, -change); + goto out; + } + } else if (cx == thisx) { + /* + * Moving row only, column staying the same. + */ + + /* One above. */ + if (thisy != tty->rupper && + cy == thisy - 1 && tty_term_has(term, TTYC_CUU1)) { + tty_putcode(tty, TTYC_CUU1); + goto out; + } + + /* One below. */ + if (thisy != tty->rlower && + cy == thisy + 1 && tty_term_has(term, TTYC_CUD1)) { + tty_putcode(tty, TTYC_CUD1); + goto out; + } + + /* Calculate difference. */ + change = thisy - cy; /* +ve up, -ve down */ + + /* + * Try to use VPA if change is larger than absolute or if this + * change would cross the scroll region, otherwise use CUU/CUD. + */ + if ((u_int) abs(change) > cy || + (change < 0 && cy - change > tty->rlower) || + (change > 0 && cy - change < tty->rupper)) { + if (tty_term_has(term, TTYC_VPA)) { + tty_putcode1(tty, TTYC_VPA, cy); + goto out; + } + } else if (change > 0 && tty_term_has(term, TTYC_CUU)) { + tty_putcode1(tty, TTYC_CUU, change); + goto out; + } else if (change < 0 && tty_term_has(term, TTYC_CUD)) { + tty_putcode1(tty, TTYC_CUD, -change); + goto out; + } + } + +absolute: + /* Absolute movement. */ + tty_putcode2(tty, TTYC_CUP, cy, cx); + +out: + tty->cx = cx; + tty->cy = cy; +} + +void +tty_attributes(struct tty *tty, const struct grid_cell *gc) +{ + struct grid_cell *tc = &tty->cell, gc2; + u_char changed; + + memcpy(&gc2, gc, sizeof gc2); + + /* + * If no setab, try to use the reverse attribute as a best-effort for a + * non-default background. This is a bit of a hack but it doesn't do + * any serious harm and makes a couple of applications happier. + */ + if (!tty_term_has(tty->term, TTYC_SETAB)) { + if (gc2.attr & GRID_ATTR_REVERSE) { + if (gc2.fg != 7 && gc2.fg != 8) + gc2.attr &= ~GRID_ATTR_REVERSE; + } else { + if (gc2.bg != 0 && gc2.bg != 8) + gc2.attr |= GRID_ATTR_REVERSE; + } + } + + /* Fix up the colours if necessary. */ + tty_check_fg(tty, &gc2); + tty_check_bg(tty, &gc2); + + /* If any bits are being cleared, reset everything. */ + if (tc->attr & ~gc2.attr) + tty_reset(tty); + + /* + * Set the colours. This may call tty_reset() (so it comes next) and + * may add to (NOT remove) the desired attributes by changing new_attr. + */ + tty_colours(tty, &gc2); + + /* Filter out attribute bits already set. */ + changed = gc2.attr & ~tc->attr; + tc->attr = gc2.attr; + + /* Set the attributes. */ + if (changed & GRID_ATTR_BRIGHT) + tty_putcode(tty, TTYC_BOLD); + if (changed & GRID_ATTR_DIM) + tty_putcode(tty, TTYC_DIM); + if (changed & GRID_ATTR_ITALICS) + { + if (tty_term_has(tty->term, TTYC_SITM)) + tty_putcode(tty, TTYC_SITM); + else + tty_putcode(tty, TTYC_SMSO); + } + if (changed & GRID_ATTR_UNDERSCORE) + tty_putcode(tty, TTYC_SMUL); + if (changed & GRID_ATTR_BLINK) + tty_putcode(tty, TTYC_BLINK); + if (changed & GRID_ATTR_REVERSE) { + if (tty_term_has(tty->term, TTYC_REV)) + tty_putcode(tty, TTYC_REV); + else if (tty_term_has(tty->term, TTYC_SMSO)) + tty_putcode(tty, TTYC_SMSO); + } + if (changed & GRID_ATTR_HIDDEN) + tty_putcode(tty, TTYC_INVIS); + if ((changed & GRID_ATTR_CHARSET) && tty_use_acs(tty)) + tty_putcode(tty, TTYC_SMACS); +} + +void +tty_colours(struct tty *tty, const struct grid_cell *gc) +{ + struct grid_cell *tc = &tty->cell; + u_char fg = gc->fg, bg = gc->bg, flags = gc->flags; + int have_ax, fg_default, bg_default; + + /* No changes? Nothing is necessary. */ + if (fg == tc->fg && bg == tc->bg && + ((flags ^ tc->flags) & (GRID_FLAG_FG256|GRID_FLAG_BG256)) == 0) + return; + + /* + * Is either the default colour? This is handled specially because the + * best solution might be to reset both colours to default, in which + * case if only one is default need to fall onward to set the other + * colour. + */ + fg_default = (fg == 8 && !(flags & GRID_FLAG_FG256)); + bg_default = (bg == 8 && !(flags & GRID_FLAG_BG256)); + if (fg_default || bg_default) { + /* + * If don't have AX but do have op, send sgr0 (op can't + * actually be used because it is sometimes the same as sgr0 + * and sometimes isn't). This resets both colours to default. + * + * Otherwise, try to set the default colour only as needed. + */ + have_ax = tty_term_has(tty->term, TTYC_AX); + if (!have_ax && tty_term_has(tty->term, TTYC_OP)) + tty_reset(tty); + else { + if (fg_default && + (tc->fg != 8 || tc->flags & GRID_FLAG_FG256)) { + if (have_ax) + tty_puts(tty, "\033[39m"); + else if (tc->fg != 7 || + tc->flags & GRID_FLAG_FG256) + tty_putcode1(tty, TTYC_SETAF, 7); + tc->fg = 8; + tc->flags &= ~GRID_FLAG_FG256; + } + if (bg_default && + (tc->bg != 8 || tc->flags & GRID_FLAG_BG256)) { + if (have_ax) + tty_puts(tty, "\033[49m"); + else if (tc->bg != 0 || + tc->flags & GRID_FLAG_BG256) + tty_putcode1(tty, TTYC_SETAB, 0); + tc->bg = 8; + tc->flags &= ~GRID_FLAG_BG256; + } + } + } + + /* Set the foreground colour. */ + if (!fg_default && (fg != tc->fg || + ((flags & GRID_FLAG_FG256) != (tc->flags & GRID_FLAG_FG256)))) + tty_colours_fg(tty, gc); + + /* + * Set the background colour. This must come after the foreground as + * tty_colour_fg() can call tty_reset(). + */ + if (!bg_default && (bg != tc->bg || + ((flags & GRID_FLAG_BG256) != (tc->flags & GRID_FLAG_BG256)))) + tty_colours_bg(tty, gc); +} + +void +tty_check_fg(struct tty *tty, struct grid_cell *gc) +{ + u_int colours; + + /* Is this a 256-colour colour? */ + if (gc->flags & GRID_FLAG_FG256) { + /* And not a 256 colour mode? */ + if (!(tty->term->flags & TERM_88COLOURS) && + !(tty->term_flags & TERM_88COLOURS) && + !(tty->term->flags & TERM_256COLOURS) && + !(tty->term_flags & TERM_256COLOURS)) { + gc->fg = colour_256to16(gc->fg); + if (gc->fg & 8) { + gc->fg &= 7; + gc->attr |= GRID_ATTR_BRIGHT; + } else + gc->attr &= ~GRID_ATTR_BRIGHT; + gc->flags &= ~GRID_FLAG_FG256; + } + return; + } + + /* Is this an aixterm colour? */ + colours = tty_term_number(tty->term, TTYC_COLORS); + if (gc->fg >= 90 && gc->fg <= 97 && colours < 16) { + gc->fg -= 90; + gc->attr |= GRID_ATTR_BRIGHT; + } +} + +void +tty_check_bg(struct tty *tty, struct grid_cell *gc) +{ + u_int colours; + + /* Is this a 256-colour colour? */ + if (gc->flags & GRID_FLAG_BG256) { + /* + * And not a 256 colour mode? Translate to 16-colour + * palette. Bold background doesn't exist portably, so just + * discard the bold bit if set. + */ + if (!(tty->term->flags & TERM_88COLOURS) && + !(tty->term_flags & TERM_88COLOURS) && + !(tty->term->flags & TERM_256COLOURS) && + !(tty->term_flags & TERM_256COLOURS)) { + gc->bg = colour_256to16(gc->bg); + if (gc->bg & 8) + gc->bg &= 7; + gc->attr &= ~GRID_ATTR_BRIGHT; + gc->flags &= ~GRID_FLAG_BG256; + } + return; + } + + /* Is this an aixterm colour? */ + colours = tty_term_number(tty->term, TTYC_COLORS); + if (gc->bg >= 100 && gc->bg <= 107 && colours < 16) { + gc->bg -= 90; + gc->attr |= GRID_ATTR_BRIGHT; + } +} + +void +tty_colours_fg(struct tty *tty, const struct grid_cell *gc) +{ + struct grid_cell *tc = &tty->cell; + u_char fg = gc->fg; + char s[32]; + + /* Is this a 256-colour colour? */ + if (gc->flags & GRID_FLAG_FG256) { + /* Try as 256 colours or translating to 88. */ + if (tty_try_256(tty, fg, "38") == 0) + goto save_fg; + if (tty_try_88(tty, fg, "38") == 0) + goto save_fg; + /* Else already handled by tty_check_fg. */ + return; + } + + /* Is this an aixterm bright colour? */ + if (fg >= 90 && fg <= 97) { + xsnprintf(s, sizeof s, "\033[%dm", fg); + tty_puts(tty, s); + goto save_fg; + } + + /* Otherwise set the foreground colour. */ + tty_putcode1(tty, TTYC_SETAF, fg); + +save_fg: + /* Save the new values in the terminal current cell. */ + tc->fg = fg; + tc->flags &= ~GRID_FLAG_FG256; + tc->flags |= gc->flags & GRID_FLAG_FG256; +} + +void +tty_colours_bg(struct tty *tty, const struct grid_cell *gc) +{ + struct grid_cell *tc = &tty->cell; + u_char bg = gc->bg; + char s[32]; + + /* Is this a 256-colour colour? */ + if (gc->flags & GRID_FLAG_BG256) { + /* Try as 256 colours or translating to 88. */ + if (tty_try_256(tty, bg, "48") == 0) + goto save_bg; + if (tty_try_88(tty, bg, "48") == 0) + goto save_bg; + /* Else already handled by tty_check_bg. */ + return; + } + + /* Is this an aixterm bright colour? */ + if (bg >= 100 && bg <= 107) { + /* 16 colour terminals or above only. */ + if (tty_term_number(tty->term, TTYC_COLORS) >= 16) { + xsnprintf(s, sizeof s, "\033[%dm", bg); + tty_puts(tty, s); + goto save_bg; + } + bg -= 100; + /* no such thing as a bold background */ + } + + /* Otherwise set the background colour. */ + tty_putcode1(tty, TTYC_SETAB, bg); + +save_bg: + /* Save the new values in the terminal current cell. */ + tc->bg = bg; + tc->flags &= ~GRID_FLAG_BG256; + tc->flags |= gc->flags & GRID_FLAG_BG256; +} + +int +tty_try_256(struct tty *tty, u_char colour, const char *type) +{ + char s[32]; + + if (!(tty->term->flags & TERM_256COLOURS) && + !(tty->term_flags & TERM_256COLOURS)) + return (-1); + + xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour); + tty_puts(tty, s); + return (0); +} + +int +tty_try_88(struct tty *tty, u_char colour, const char *type) +{ + char s[32]; + + if (!(tty->term->flags & TERM_88COLOURS) && + !(tty->term_flags & TERM_88COLOURS)) + return (-1); + colour = colour_256to88(colour); + + xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour); + tty_puts(tty, s); + return (0); +} diff --git a/external/bsd/tmux/dist/utf8.c b/external/bsd/tmux/dist/utf8.c new file mode 100644 index 000000000..5f13299cd --- /dev/null +++ b/external/bsd/tmux/dist/utf8.c @@ -0,0 +1,353 @@ +/* $Id: utf8.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv Exp $ */ + +/* + * Copyright (c) 2008 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +struct utf8_width_entry { + u_int first; + u_int last; + + int width; + + struct utf8_width_entry *left; + struct utf8_width_entry *right; +}; + +/* Random order. Not optimal but it'll do for now... */ +struct utf8_width_entry utf8_width_table[] = { + { 0x00951, 0x00954, 0, NULL, NULL }, + { 0x00ccc, 0x00ccd, 0, NULL, NULL }, + { 0x0fff9, 0x0fffb, 0, NULL, NULL }, + { 0x20000, 0x2fffd, 2, NULL, NULL }, + { 0x00ebb, 0x00ebc, 0, NULL, NULL }, + { 0x01932, 0x01932, 0, NULL, NULL }, + { 0x0070f, 0x0070f, 0, NULL, NULL }, + { 0x00a70, 0x00a71, 0, NULL, NULL }, + { 0x02329, 0x02329, 2, NULL, NULL }, + { 0x00acd, 0x00acd, 0, NULL, NULL }, + { 0x00ac7, 0x00ac8, 0, NULL, NULL }, + { 0x00a3c, 0x00a3c, 0, NULL, NULL }, + { 0x009cd, 0x009cd, 0, NULL, NULL }, + { 0x00591, 0x005bd, 0, NULL, NULL }, + { 0x01058, 0x01059, 0, NULL, NULL }, + { 0x0ffe0, 0x0ffe6, 2, NULL, NULL }, + { 0x01100, 0x0115f, 2, NULL, NULL }, + { 0x0fe20, 0x0fe23, 0, NULL, NULL }, + { 0x0302a, 0x0302f, 0, NULL, NULL }, + { 0x01772, 0x01773, 0, NULL, NULL }, + { 0x005bf, 0x005bf, 0, NULL, NULL }, + { 0x006ea, 0x006ed, 0, NULL, NULL }, + { 0x00bc0, 0x00bc0, 0, NULL, NULL }, + { 0x00962, 0x00963, 0, NULL, NULL }, + { 0x01732, 0x01734, 0, NULL, NULL }, + { 0x00d41, 0x00d43, 0, NULL, NULL }, + { 0x01b42, 0x01b42, 0, NULL, NULL }, + { 0x00a41, 0x00a42, 0, NULL, NULL }, + { 0x00eb4, 0x00eb9, 0, NULL, NULL }, + { 0x00b01, 0x00b01, 0, NULL, NULL }, + { 0x00e34, 0x00e3a, 0, NULL, NULL }, + { 0x03040, 0x03098, 2, NULL, NULL }, + { 0x0093c, 0x0093c, 0, NULL, NULL }, + { 0x00c4a, 0x00c4d, 0, NULL, NULL }, + { 0x01032, 0x01032, 0, NULL, NULL }, + { 0x00f37, 0x00f37, 0, NULL, NULL }, + { 0x00901, 0x00902, 0, NULL, NULL }, + { 0x00cbf, 0x00cbf, 0, NULL, NULL }, + { 0x0a806, 0x0a806, 0, NULL, NULL }, + { 0x00dd2, 0x00dd4, 0, NULL, NULL }, + { 0x00f71, 0x00f7e, 0, NULL, NULL }, + { 0x01752, 0x01753, 0, NULL, NULL }, + { 0x1d242, 0x1d244, 0, NULL, NULL }, + { 0x005c1, 0x005c2, 0, NULL, NULL }, + { 0x0309b, 0x0a4cf, 2, NULL, NULL }, + { 0xe0100, 0xe01ef, 0, NULL, NULL }, + { 0x017dd, 0x017dd, 0, NULL, NULL }, + { 0x00600, 0x00603, 0, NULL, NULL }, + { 0x009e2, 0x009e3, 0, NULL, NULL }, + { 0x00cc6, 0x00cc6, 0, NULL, NULL }, + { 0x0a80b, 0x0a80b, 0, NULL, NULL }, + { 0x01712, 0x01714, 0, NULL, NULL }, + { 0x00b3c, 0x00b3c, 0, NULL, NULL }, + { 0x01b00, 0x01b03, 0, NULL, NULL }, + { 0x007eb, 0x007f3, 0, NULL, NULL }, + { 0xe0001, 0xe0001, 0, NULL, NULL }, + { 0x1d185, 0x1d18b, 0, NULL, NULL }, + { 0x0feff, 0x0feff, 0, NULL, NULL }, + { 0x01b36, 0x01b3a, 0, NULL, NULL }, + { 0x01920, 0x01922, 0, NULL, NULL }, + { 0x00670, 0x00670, 0, NULL, NULL }, + { 0x00f90, 0x00f97, 0, NULL, NULL }, + { 0x01927, 0x01928, 0, NULL, NULL }, + { 0x0200b, 0x0200f, 0, NULL, NULL }, + { 0x0ff00, 0x0ff60, 2, NULL, NULL }, + { 0x0f900, 0x0faff, 2, NULL, NULL }, + { 0x0fb1e, 0x0fb1e, 0, NULL, NULL }, + { 0x00cbc, 0x00cbc, 0, NULL, NULL }, + { 0x00eb1, 0x00eb1, 0, NULL, NULL }, + { 0x10a38, 0x10a3a, 0, NULL, NULL }, + { 0x007a6, 0x007b0, 0, NULL, NULL }, + { 0x00f80, 0x00f84, 0, NULL, NULL }, + { 0x005c4, 0x005c5, 0, NULL, NULL }, + { 0x0ac00, 0x0d7a3, 2, NULL, NULL }, + { 0x017c9, 0x017d3, 0, NULL, NULL }, + { 0x00d4d, 0x00d4d, 0, NULL, NULL }, + { 0x1d167, 0x1d169, 0, NULL, NULL }, + { 0x01036, 0x01037, 0, NULL, NULL }, + { 0xe0020, 0xe007f, 0, NULL, NULL }, + { 0x00f35, 0x00f35, 0, NULL, NULL }, + { 0x017b4, 0x017b5, 0, NULL, NULL }, + { 0x0206a, 0x0206f, 0, NULL, NULL }, + { 0x00c46, 0x00c48, 0, NULL, NULL }, + { 0x01939, 0x0193b, 0, NULL, NULL }, + { 0x01dc0, 0x01dca, 0, NULL, NULL }, + { 0x10a0c, 0x10a0f, 0, NULL, NULL }, + { 0x0102d, 0x01030, 0, NULL, NULL }, + { 0x017c6, 0x017c6, 0, NULL, NULL }, + { 0x00ec8, 0x00ecd, 0, NULL, NULL }, + { 0x00b41, 0x00b43, 0, NULL, NULL }, + { 0x017b7, 0x017bd, 0, NULL, NULL }, + { 0x1d173, 0x1d182, 0, NULL, NULL }, + { 0x00a47, 0x00a48, 0, NULL, NULL }, + { 0x0232a, 0x0232a, 2, NULL, NULL }, + { 0x01b3c, 0x01b3c, 0, NULL, NULL }, + { 0x10a01, 0x10a03, 0, NULL, NULL }, + { 0x00ae2, 0x00ae3, 0, NULL, NULL }, + { 0x00483, 0x00486, 0, NULL, NULL }, + { 0x0135f, 0x0135f, 0, NULL, NULL }, + { 0x01a17, 0x01a18, 0, NULL, NULL }, + { 0x006e7, 0x006e8, 0, NULL, NULL }, + { 0x03099, 0x0309a, 0, NULL, NULL }, + { 0x00b4d, 0x00b4d, 0, NULL, NULL }, + { 0x00ce2, 0x00ce3, 0, NULL, NULL }, + { 0x00bcd, 0x00bcd, 0, NULL, NULL }, + { 0x00610, 0x00615, 0, NULL, NULL }, + { 0x00f99, 0x00fbc, 0, NULL, NULL }, + { 0x009c1, 0x009c4, 0, NULL, NULL }, + { 0x00730, 0x0074a, 0, NULL, NULL }, + { 0x00300, 0x0036f, 0, NULL, NULL }, + { 0x03030, 0x0303e, 2, NULL, NULL }, + { 0x01b34, 0x01b34, 0, NULL, NULL }, + { 0x1d1aa, 0x1d1ad, 0, NULL, NULL }, + { 0x00dca, 0x00dca, 0, NULL, NULL }, + { 0x006d6, 0x006e4, 0, NULL, NULL }, + { 0x00f86, 0x00f87, 0, NULL, NULL }, + { 0x00b3f, 0x00b3f, 0, NULL, NULL }, + { 0x0fe30, 0x0fe6f, 2, NULL, NULL }, + { 0x01039, 0x01039, 0, NULL, NULL }, + { 0x0094d, 0x0094d, 0, NULL, NULL }, + { 0x00c55, 0x00c56, 0, NULL, NULL }, + { 0x00488, 0x00489, 0, NULL, NULL }, + { 0x00e47, 0x00e4e, 0, NULL, NULL }, + { 0x00a81, 0x00a82, 0, NULL, NULL }, + { 0x00ac1, 0x00ac5, 0, NULL, NULL }, + { 0x0202a, 0x0202e, 0, NULL, NULL }, + { 0x00dd6, 0x00dd6, 0, NULL, NULL }, + { 0x018a9, 0x018a9, 0, NULL, NULL }, + { 0x0064b, 0x0065e, 0, NULL, NULL }, + { 0x00abc, 0x00abc, 0, NULL, NULL }, + { 0x00b82, 0x00b82, 0, NULL, NULL }, + { 0x00f39, 0x00f39, 0, NULL, NULL }, + { 0x020d0, 0x020ef, 0, NULL, NULL }, + { 0x01dfe, 0x01dff, 0, NULL, NULL }, + { 0x30000, 0x3fffd, 2, NULL, NULL }, + { 0x00711, 0x00711, 0, NULL, NULL }, + { 0x0fe00, 0x0fe0f, 0, NULL, NULL }, + { 0x01160, 0x011ff, 0, NULL, NULL }, + { 0x0180b, 0x0180d, 0, NULL, NULL }, + { 0x10a3f, 0x10a3f, 0, NULL, NULL }, + { 0x00981, 0x00981, 0, NULL, NULL }, + { 0x0a825, 0x0a826, 0, NULL, NULL }, + { 0x00941, 0x00948, 0, NULL, NULL }, + { 0x01b6b, 0x01b73, 0, NULL, NULL }, + { 0x00e31, 0x00e31, 0, NULL, NULL }, + { 0x0fe10, 0x0fe19, 2, NULL, NULL }, + { 0x00a01, 0x00a02, 0, NULL, NULL }, + { 0x00a4b, 0x00a4d, 0, NULL, NULL }, + { 0x00f18, 0x00f19, 0, NULL, NULL }, + { 0x00fc6, 0x00fc6, 0, NULL, NULL }, + { 0x02e80, 0x03029, 2, NULL, NULL }, + { 0x00b56, 0x00b56, 0, NULL, NULL }, + { 0x009bc, 0x009bc, 0, NULL, NULL }, + { 0x005c7, 0x005c7, 0, NULL, NULL }, + { 0x02060, 0x02063, 0, NULL, NULL }, + { 0x00c3e, 0x00c40, 0, NULL, NULL }, + { 0x10a05, 0x10a06, 0, NULL, NULL }, +}; + +struct utf8_width_entry *utf8_width_root = NULL; + +int utf8_overlap(struct utf8_width_entry *, struct utf8_width_entry *); +u_int utf8_combine(const struct utf8_data *); +u_int utf8_width(const struct utf8_data *); + +/* + * Open UTF-8 sequence. + * + * 11000010-11011111 C2-DF start of 2-byte sequence + * 11100000-11101111 E0-EF start of 3-byte sequence + * 11110000-11110100 F0-F4 start of 4-byte sequence + * + * Returns 1 if more UTF-8 to come, 0 if not UTF-8. + */ +int +utf8_open(struct utf8_data *utf8data, u_char ch) +{ + memset(utf8data, 0, sizeof *utf8data); + if (ch >= 0xc2 && ch <= 0xdf) + utf8data->size = 2; + else if (ch >= 0xe0 && ch <= 0xef) + utf8data->size = 3; + else if (ch >= 0xf0 && ch <= 0xf4) + utf8data->size = 4; + else + return (0); + utf8_append(utf8data, ch); + return (1); +} + +/* + * Append character to UTF-8, closing if finished. + * + * Returns 1 if more UTF-8 data to come, 0 if finished. + */ +int +utf8_append(struct utf8_data *utf8data, u_char ch) +{ + if (utf8data->have >= utf8data->size) + fatalx("UTF-8 character overflow"); + if (utf8data->size > sizeof utf8data->data) + fatalx("UTF-8 character size too large"); + + utf8data->data[utf8data->have++] = ch; + if (utf8data->have != utf8data->size) + return (1); + + utf8data->width = utf8_width(utf8data); + return (0); +} + +/* Check if two width tree entries overlap. */ +int +utf8_overlap( + struct utf8_width_entry *item1, struct utf8_width_entry *item2) +{ + if (item1->first >= item2->first && item1->first <= item2->last) + return (1); + if (item1->last >= item2->first && item1->last <= item2->last) + return (1); + if (item2->first >= item1->first && item2->first <= item1->last) + return (1); + if (item2->last >= item1->first && item2->last <= item1->last) + return (1); + return (0); +} + +/* Build UTF-8 width tree. */ +void +utf8_build(void) +{ + struct utf8_width_entry **ptr, *item, *node; + u_int i, j; + + for (i = 0; i < nitems(utf8_width_table); i++) { + item = &utf8_width_table[i]; + + for (j = 0; j < nitems(utf8_width_table); j++) { + if (i != j && utf8_overlap(item, &utf8_width_table[j])) + log_fatalx("utf8 overlap: %u %u", i, j); + } + + ptr = &utf8_width_root; + while (*ptr != NULL) { + node = *ptr; + if (item->last < node->first) + ptr = &(node->left); + else if (item->first > node->last) + ptr = &(node->right); + } + *ptr = item; + } +} + +/* Combine UTF-8 into 32-bit Unicode. */ +u_int +utf8_combine(const struct utf8_data *utf8data) +{ + u_int value; + + value = 0xff; + switch (utf8data->size) { + case 1: + value = utf8data->data[0]; + break; + case 2: + value = utf8data->data[1] & 0x3f; + value |= (utf8data->data[0] & 0x1f) << 6; + break; + case 3: + value = utf8data->data[2] & 0x3f; + value |= (utf8data->data[1] & 0x3f) << 6; + value |= (utf8data->data[0] & 0x0f) << 12; + break; + case 4: + value = utf8data->data[3] & 0x3f; + value |= (utf8data->data[2] & 0x3f) << 6; + value |= (utf8data->data[1] & 0x3f) << 12; + value |= (utf8data->data[0] & 0x3f) << 18; + break; + } + return (value); +} + +/* Split a two-byte UTF-8 character. */ +u_int +utf8_split2(u_int uc, u_char *ptr) +{ + if (uc > 0x7f) { + ptr[0] = (uc >> 6) | 0xc0; + ptr[1] = (uc & 0x3f) | 0x80; + return (2); + } + ptr[0] = uc; + return (1); +} + +/* Lookup width of UTF-8 data in tree. */ +u_int +utf8_width(const struct utf8_data *utf8data) +{ + struct utf8_width_entry *item; + u_int value; + + value = utf8_combine(utf8data); + + item = utf8_width_root; + while (item != NULL) { + if (value < item->first) + item = item->left; + else if (value > item->last) + item = item->right; + else + return (item->width); + } + return (1); +} diff --git a/external/bsd/tmux/dist/utmp.c b/external/bsd/tmux/dist/utmp.c new file mode 100644 index 000000000..94a0b62a0 --- /dev/null +++ b/external/bsd/tmux/dist/utmp.c @@ -0,0 +1,167 @@ +/* $NetBSD: utmp.c,v 1.1 2011/09/17 01:50:08 christos Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include +__RCSID("$NetBSD: utmp.c,v 1.1 2011/09/17 01:50:08 christos Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef SUPPORT_UTMP +#include +#endif +#ifdef SUPPORT_UTMPX +#include +#endif + +#include "tmux.h" + +struct window_utmp { +#ifdef SUPPORT_UTMP + struct utmp ut; +#endif +#ifdef SUPPORT_UTMPX + struct utmpx utx; +#endif +}; + +#ifdef SUPPORT_UTMPX +static void +login_utmpx(struct utmpx *utmpx, const char *username, const char *hostname, + const char *tty, const struct timeval *now) +{ + const char *t; + + (void)memset(utmpx, 0, sizeof(*utmpx)); + utmpx->ut_tv = *now; + (void)strncpy(utmpx->ut_name, username, sizeof(utmpx->ut_name)); + if (hostname) + (void)strncpy(utmpx->ut_host, hostname, sizeof(utmpx->ut_host)); + (void)strncpy(utmpx->ut_line, tty, sizeof(utmpx->ut_line)); + utmpx->ut_type = USER_PROCESS; + utmpx->ut_pid = getpid(); + t = tty + strlen(tty); + if ((size_t)(t - tty) >= sizeof(utmpx->ut_id)) { + (void)strncpy(utmpx->ut_id, t - sizeof(utmpx->ut_id), + sizeof(utmpx->ut_id)); + } else { + (void)strncpy(utmpx->ut_id, tty, sizeof(utmpx->ut_id)); + } + (void)pututxline(utmpx); + endutxent(); +} + +static void +logout_utmpx(struct utmpx *utmpx, const struct timeval *now) +{ + utmpx->ut_type = DEAD_PROCESS; + utmpx->ut_tv = *now; + utmpx->ut_pid = 0; + (void)pututxline(utmpx); + endutxent(); +} +#endif + +#ifdef SUPPORT_UTMP +static void +login_utmp(struct utmp *utmp, const char *username, const char *hostname, + const char *tty, const struct timeval *now) +{ + (void)memset(utmp, 0, sizeof(*utmp)); + utmp->ut_time = now->tv_sec; + (void)strncpy(utmp->ut_name, username, sizeof(utmp->ut_name)); + if (hostname) + (void)strncpy(utmp->ut_host, hostname, sizeof(utmp->ut_host)); + (void)strncpy(utmp->ut_line, tty, sizeof(utmp->ut_line)); + login(utmp); +} + +static void +logout_utmp(struct utmp *utmp, const struct timeval *now) +{ + logout(utmp->ut_line); +} +#endif + +struct window_utmp * +utmp_create(const char *tty) +{ + struct window_utmp *wu; + struct timeval tv; + char username[LOGIN_NAME_MAX]; + + if (getlogin_r(username, sizeof(username)) == -1) + return NULL; + + if ((wu = malloc(sizeof(*wu))) == NULL) + return NULL; + + tty += sizeof(_PATH_DEV) - 1; + + (void)gettimeofday(&tv, NULL); +#ifdef SUPPORT_UTMPX + login_utmpx(&wu->utx, username, "tmux", tty, &tv); +#endif +#ifdef SUPPORT_UTMP + login_utmp(&wu->ut, username, "tmux", tty, &tv); +#endif + return wu; +} + +void +utmp_destroy(struct window_utmp *wu) +{ + struct timeval tv; + + if (wu == NULL) + return; + + (void)gettimeofday(&tv, NULL); +#ifdef SUPPORT_UTMPX + logout_utmpx(&wu->utx, &tv); +#endif +#ifdef SUPPORT_UTMP + logout_utmp(&wu->ut, &tv); +#endif +} diff --git a/external/bsd/tmux/dist/window-choose.c b/external/bsd/tmux/dist/window-choose.c new file mode 100644 index 000000000..f55ae6bf5 --- /dev/null +++ b/external/bsd/tmux/dist/window-choose.c @@ -0,0 +1,457 @@ +/* $Id: window-choose.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +struct screen *window_choose_init(struct window_pane *); +void window_choose_free(struct window_pane *); +void window_choose_resize(struct window_pane *, u_int, u_int); +void window_choose_key(struct window_pane *, struct session *, int); +void window_choose_mouse( + struct window_pane *, struct session *, struct mouse_event *); + +void window_choose_redraw_screen(struct window_pane *); +void window_choose_write_line( + struct window_pane *, struct screen_write_ctx *, u_int); + +void window_choose_scroll_up(struct window_pane *); +void window_choose_scroll_down(struct window_pane *); + +const struct window_mode window_choose_mode = { + window_choose_init, + window_choose_free, + window_choose_resize, + window_choose_key, + window_choose_mouse, + NULL, +}; + +struct window_choose_mode_item { + char *name; + int idx; +}; + +struct window_choose_mode_data { + struct screen screen; + + struct mode_key_data mdata; + + ARRAY_DECL(, struct window_choose_mode_item) list; + u_int top; + u_int selected; + + void (*callbackfn)(void *, int); + void (*freefn)(void *); + void *data; +}; + +int window_choose_key_index(struct window_choose_mode_data *, u_int); +int window_choose_index_key(struct window_choose_mode_data *, int); + +void +window_choose_vadd(struct window_pane *wp, int idx, const char *fmt, va_list ap) +{ + struct window_choose_mode_data *data = wp->modedata; + struct window_choose_mode_item *item; + + ARRAY_EXPAND(&data->list, 1); + item = &ARRAY_LAST(&data->list); + xvasprintf(&item->name, fmt, ap); + item->idx = idx; +} + +void printflike3 +window_choose_add(struct window_pane *wp, int idx, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + window_choose_vadd(wp, idx, fmt, ap); + va_end(ap); +} + +void +window_choose_ready(struct window_pane *wp, u_int cur, + void (*callbackfn)(void *, int), void (*freefn)(void *), void *cdata) +{ + struct window_choose_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + + data->selected = cur; + if (data->selected > screen_size_y(s) - 1) + data->top = ARRAY_LENGTH(&data->list) - screen_size_y(s); + + data->callbackfn = callbackfn; + data->freefn = freefn; + data->data = cdata; + + window_choose_redraw_screen(wp); +} + +struct screen * +window_choose_init(struct window_pane *wp) +{ + struct window_choose_mode_data *data; + struct screen *s; + int keys; + + wp->modedata = data = xmalloc(sizeof *data); + + data->callbackfn = NULL; + data->freefn = NULL; + data->data = NULL; + + ARRAY_INIT(&data->list); + data->top = 0; + + s = &data->screen; + screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); + s->mode &= ~MODE_CURSOR; + if (options_get_number(&wp->window->options, "mode-mouse")) + s->mode |= MODE_MOUSE_STANDARD; + + keys = options_get_number(&wp->window->options, "mode-keys"); + if (keys == MODEKEY_EMACS) + mode_key_init(&data->mdata, &mode_key_tree_emacs_choice); + else + mode_key_init(&data->mdata, &mode_key_tree_vi_choice); + + return (s); +} + +void +window_choose_free(struct window_pane *wp) +{ + struct window_choose_mode_data *data = wp->modedata; + u_int i; + + if (data->freefn != NULL && data->data != NULL) + data->freefn(data->data); + + for (i = 0; i < ARRAY_LENGTH(&data->list); i++) + xfree(ARRAY_ITEM(&data->list, i).name); + ARRAY_FREE(&data->list); + + screen_free(&data->screen); + xfree(data); +} + +void +window_choose_resize(struct window_pane *wp, u_int sx, u_int sy) +{ + struct window_choose_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + + data->top = 0; + if (data->selected > sy - 1) + data->top = data->selected - (sy - 1); + + screen_resize(s, sx, sy); + window_choose_redraw_screen(wp); +} + +/* ARGSUSED */ +void +window_choose_key(struct window_pane *wp, unused struct session *sess, int key) +{ + struct window_choose_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct screen_write_ctx ctx; + struct window_choose_mode_item *item; + u_int items; + int idx; + + items = ARRAY_LENGTH(&data->list); + + switch (mode_key_lookup(&data->mdata, key)) { + case MODEKEYCHOICE_CANCEL: + data->callbackfn(data->data, -1); + window_pane_reset_mode(wp); + break; + case MODEKEYCHOICE_CHOOSE: + item = &ARRAY_ITEM(&data->list, data->selected); + data->callbackfn(data->data, item->idx); + window_pane_reset_mode(wp); + break; + case MODEKEYCHOICE_UP: + if (items == 0) + break; + if (data->selected == 0) { + data->selected = items - 1; + if (data->selected > screen_size_y(s) - 1) + data->top = items - screen_size_y(s); + window_choose_redraw_screen(wp); + break; + } + data->selected--; + if (data->selected < data->top) + window_choose_scroll_up(wp); + else { + screen_write_start(&ctx, wp, NULL); + window_choose_write_line( + wp, &ctx, data->selected - data->top); + window_choose_write_line( + wp, &ctx, data->selected + 1 - data->top); + screen_write_stop(&ctx); + } + break; + case MODEKEYCHOICE_DOWN: + if (items == 0) + break; + if (data->selected == items - 1) { + data->selected = 0; + data->top = 0; + window_choose_redraw_screen(wp); + break; + } + data->selected++; + + if (data->selected < data->top + screen_size_y(s)) { + screen_write_start(&ctx, wp, NULL); + window_choose_write_line( + wp, &ctx, data->selected - data->top); + window_choose_write_line( + wp, &ctx, data->selected - 1 - data->top); + screen_write_stop(&ctx); + } else + window_choose_scroll_down(wp); + break; + case MODEKEYCHOICE_SCROLLUP: + if (items == 0 || data->top == 0) + break; + if (data->selected == data->top + screen_size_y(s) - 1) { + data->selected--; + window_choose_scroll_up(wp); + screen_write_start(&ctx, wp, NULL); + window_choose_write_line( + wp, &ctx, screen_size_y(s) - 1); + screen_write_stop(&ctx); + } else + window_choose_scroll_up(wp); + break; + case MODEKEYCHOICE_SCROLLDOWN: + if (items == 0 || + data->top + screen_size_y(&data->screen) >= items) + break; + if (data->selected == data->top) { + data->selected++; + window_choose_scroll_down(wp); + screen_write_start(&ctx, wp, NULL); + window_choose_write_line(wp, &ctx, 0); + screen_write_stop(&ctx); + } else + window_choose_scroll_down(wp); + break; + case MODEKEYCHOICE_PAGEUP: + if (data->selected < screen_size_y(s)) { + data->selected = 0; + data->top = 0; + } else { + data->selected -= screen_size_y(s); + if (data->top < screen_size_y(s)) + data->top = 0; + else + data->top -= screen_size_y(s); + } + window_choose_redraw_screen(wp); + break; + case MODEKEYCHOICE_PAGEDOWN: + data->selected += screen_size_y(s); + if (data->selected > items - 1) + data->selected = items - 1; + data->top += screen_size_y(s); + if (screen_size_y(s) < items) { + if (data->top + screen_size_y(s) > items) + data->top = items - screen_size_y(s); + } else + data->top = 0; + if (data->selected < data->top) + data->top = data->selected; + window_choose_redraw_screen(wp); + break; + default: + idx = window_choose_index_key(data, key); + if (idx < 0 || (u_int) idx >= ARRAY_LENGTH(&data->list)) + break; + data->selected = idx; + + item = &ARRAY_ITEM(&data->list, data->selected); + data->callbackfn(data->data, item->idx); + window_pane_reset_mode(wp); + break; + } +} + +/* ARGSUSED */ +void +window_choose_mouse( + struct window_pane *wp, unused struct session *sess, struct mouse_event *m) +{ + struct window_choose_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct window_choose_mode_item *item; + u_int idx; + + if ((m->b & 3) == 3) + return; + if (m->x >= screen_size_x(s)) + return; + if (m->y >= screen_size_y(s)) + return; + + idx = data->top + m->y; + if (idx >= ARRAY_LENGTH(&data->list)) + return; + data->selected = idx; + + item = &ARRAY_ITEM(&data->list, data->selected); + data->callbackfn(data->data, item->idx); + window_pane_reset_mode(wp); +} + +void +window_choose_write_line( + struct window_pane *wp, struct screen_write_ctx *ctx, u_int py) +{ + struct window_choose_mode_data *data = wp->modedata; + struct window_choose_mode_item *item; + struct options *oo = &wp->window->options; + struct screen *s = &data->screen; + struct grid_cell gc; + int utf8flag, key; + + if (data->callbackfn == NULL) + fatalx("called before callback assigned"); + + utf8flag = options_get_number(&wp->window->options, "utf8"); + memcpy(&gc, &grid_default_cell, sizeof gc); + if (data->selected == data->top + py) { + colour_set_fg(&gc, options_get_number(oo, "mode-fg")); + colour_set_bg(&gc, options_get_number(oo, "mode-bg")); + gc.attr |= options_get_number(oo, "mode-attr"); + } + + screen_write_cursormove(ctx, 0, py); + if (data->top + py < ARRAY_LENGTH(&data->list)) { + item = &ARRAY_ITEM(&data->list, data->top + py); + key = window_choose_key_index(data, data->top + py); + if (key != -1) { + screen_write_nputs(ctx, screen_size_x(s) - 1, + &gc, utf8flag, "(%c) %s", key, item->name); + } else { + screen_write_nputs(ctx, screen_size_x(s) - 1, + &gc, utf8flag, " %s", item->name); + } + + } + while (s->cx < screen_size_x(s)) + screen_write_putc(ctx, &gc, ' '); +} + +int +window_choose_key_index(struct window_choose_mode_data *data, u_int idx) +{ + static const char keys[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + const char *ptr; + int mkey; + + for (ptr = keys; *ptr != '\0'; ptr++) { + mkey = mode_key_lookup(&data->mdata, *ptr); + if (mkey != MODEKEY_NONE && mkey != MODEKEY_OTHER) + continue; + if (idx-- == 0) + return (*ptr); + } + return (-1); +} + +int +window_choose_index_key(struct window_choose_mode_data *data, int key) +{ + static const char keys[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + const char *ptr; + int mkey; + u_int idx = 0; + + for (ptr = keys; *ptr != '\0'; ptr++) { + mkey = mode_key_lookup(&data->mdata, *ptr); + if (mkey != MODEKEY_NONE && mkey != MODEKEY_OTHER) + continue; + if (key == *ptr) + return (idx); + idx++; + } + return (-1); +} + +void +window_choose_redraw_screen(struct window_pane *wp) +{ + struct window_choose_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct screen_write_ctx ctx; + u_int i; + + screen_write_start(&ctx, wp, NULL); + for (i = 0; i < screen_size_y(s); i++) + window_choose_write_line(wp, &ctx, i); + screen_write_stop(&ctx); +} + +void +window_choose_scroll_up(struct window_pane *wp) +{ + struct window_choose_mode_data *data = wp->modedata; + struct screen_write_ctx ctx; + + if (data->top == 0) + return; + data->top--; + + screen_write_start(&ctx, wp, NULL); + screen_write_cursormove(&ctx, 0, 0); + screen_write_insertline(&ctx, 1); + window_choose_write_line(wp, &ctx, 0); + if (screen_size_y(&data->screen) > 1) + window_choose_write_line(wp, &ctx, 1); + screen_write_stop(&ctx); +} + +void +window_choose_scroll_down(struct window_pane *wp) +{ + struct window_choose_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct screen_write_ctx ctx; + + if (data->top >= ARRAY_LENGTH(&data->list)) + return; + data->top++; + + screen_write_start(&ctx, wp, NULL); + screen_write_cursormove(&ctx, 0, 0); + screen_write_deleteline(&ctx, 1); + window_choose_write_line(wp, &ctx, screen_size_y(s) - 1); + if (screen_size_y(&data->screen) > 1) + window_choose_write_line(wp, &ctx, screen_size_y(s) - 2); + screen_write_stop(&ctx); +} diff --git a/external/bsd/tmux/dist/window-clock.c b/external/bsd/tmux/dist/window-clock.c new file mode 100644 index 000000000..7f9725c54 --- /dev/null +++ b/external/bsd/tmux/dist/window-clock.c @@ -0,0 +1,124 @@ +/* $Id: window-clock.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +struct screen *window_clock_init(struct window_pane *); +void window_clock_free(struct window_pane *); +void window_clock_resize(struct window_pane *, u_int, u_int); +void window_clock_key(struct window_pane *, struct session *, int); +void window_clock_timer(struct window_pane *); + +void window_clock_draw_screen(struct window_pane *); + +const struct window_mode window_clock_mode = { + window_clock_init, + window_clock_free, + window_clock_resize, + window_clock_key, + NULL, + window_clock_timer, +}; + +struct window_clock_mode_data { + struct screen screen; + time_t tim; +}; + +struct screen * +window_clock_init(struct window_pane *wp) +{ + struct window_clock_mode_data *data; + struct screen *s; + + wp->modedata = data = xmalloc(sizeof *data); + data->tim = time(NULL); + + s = &data->screen; + screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); + s->mode &= ~MODE_CURSOR; + + window_clock_draw_screen(wp); + + return (s); +} + +void +window_clock_free(struct window_pane *wp) +{ + struct window_clock_mode_data *data = wp->modedata; + + screen_free(&data->screen); + xfree(data); +} + +void +window_clock_resize(struct window_pane *wp, u_int sx, u_int sy) +{ + struct window_clock_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + + screen_resize(s, sx, sy); + window_clock_draw_screen(wp); +} + +/* ARGSUSED */ +void +window_clock_key( + struct window_pane *wp, unused struct session *sess, unused int key) +{ + window_pane_reset_mode(wp); +} + +void +window_clock_timer(struct window_pane *wp) +{ + struct window_clock_mode_data *data = wp->modedata; + struct tm now, then; + time_t t; + + t = time(NULL); + gmtime_r(&t, &now); + gmtime_r(&data->tim, &then); + if (now.tm_min == then.tm_min) + return; + data->tim = t; + + window_clock_draw_screen(wp); + server_redraw_window(wp->window); +} + +void +window_clock_draw_screen(struct window_pane *wp) +{ + struct window_clock_mode_data *data = wp->modedata; + struct screen_write_ctx ctx; + int colour, style; + + colour = options_get_number(&wp->window->options, "clock-mode-colour"); + style = options_get_number(&wp->window->options, "clock-mode-style"); + + screen_write_start(&ctx, NULL, &data->screen); + clock_draw(&ctx, colour, style); + screen_write_stop(&ctx); +} diff --git a/external/bsd/tmux/dist/window-copy.c b/external/bsd/tmux/dist/window-copy.c new file mode 100644 index 000000000..86a00340c --- /dev/null +++ b/external/bsd/tmux/dist/window-copy.c @@ -0,0 +1,1910 @@ +/* $Id: window-copy.c,v 1.2 2011/08/17 19:28:36 jmmv Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +struct screen *window_copy_init(struct window_pane *); +void window_copy_free(struct window_pane *); +void window_copy_resize(struct window_pane *, u_int, u_int); +void window_copy_key(struct window_pane *, struct session *, int); +int window_copy_key_input(struct window_pane *, int); +int window_copy_key_numeric_prefix(struct window_pane *, int); +void window_copy_mouse( + struct window_pane *, struct session *, struct mouse_event *); + +void window_copy_redraw_lines(struct window_pane *, u_int, u_int); +void window_copy_redraw_screen(struct window_pane *); +void window_copy_write_line( + struct window_pane *, struct screen_write_ctx *, u_int); +void window_copy_write_lines( + struct window_pane *, struct screen_write_ctx *, u_int, u_int); + +void window_copy_scroll_to(struct window_pane *, u_int, u_int); +int window_copy_search_compare( + struct grid *, u_int, u_int, struct grid *, u_int); +int window_copy_search_lr( + struct grid *, struct grid *, u_int *, u_int, u_int, u_int); +int window_copy_search_rl( + struct grid *, struct grid *, u_int *, u_int, u_int, u_int); +void window_copy_search_up(struct window_pane *, const char *); +void window_copy_search_down(struct window_pane *, const char *); +void window_copy_goto_line(struct window_pane *, const char *); +void window_copy_update_cursor(struct window_pane *, u_int, u_int); +void window_copy_start_selection(struct window_pane *); +int window_copy_update_selection(struct window_pane *); +void window_copy_copy_selection(struct window_pane *); +void window_copy_clear_selection(struct window_pane *); +void window_copy_copy_line( + struct window_pane *, char **, size_t *, u_int, u_int, u_int); +int window_copy_in_set(struct window_pane *, u_int, u_int, const char *); +u_int window_copy_find_length(struct window_pane *, u_int); +void window_copy_cursor_start_of_line(struct window_pane *); +void window_copy_cursor_back_to_indentation(struct window_pane *); +void window_copy_cursor_end_of_line(struct window_pane *); +void window_copy_cursor_left(struct window_pane *); +void window_copy_cursor_right(struct window_pane *); +void window_copy_cursor_up(struct window_pane *, int); +void window_copy_cursor_down(struct window_pane *, int); +void window_copy_cursor_jump(struct window_pane *); +void window_copy_cursor_jump_back(struct window_pane *); +void window_copy_cursor_next_word(struct window_pane *, const char *); +void window_copy_cursor_next_word_end(struct window_pane *, const char *); +void window_copy_cursor_previous_word(struct window_pane *, const char *); +void window_copy_scroll_up(struct window_pane *, u_int); +void window_copy_scroll_down(struct window_pane *, u_int); +void window_copy_rectangle_toggle(struct window_pane *); + +const struct window_mode window_copy_mode = { + window_copy_init, + window_copy_free, + window_copy_resize, + window_copy_key, + window_copy_mouse, + NULL, +}; + +enum window_copy_input_type { + WINDOW_COPY_OFF, + WINDOW_COPY_NUMERICPREFIX, + WINDOW_COPY_SEARCHUP, + WINDOW_COPY_SEARCHDOWN, + WINDOW_COPY_JUMPFORWARD, + WINDOW_COPY_JUMPBACK, + WINDOW_COPY_GOTOLINE, +}; + +/* + * Copy-mode's visible screen (the "screen" field) is filled from one of + * two sources: the original contents of the pane (used when we + * actually enter via the "copy-mode" command, to copy the contents of + * the current pane), or else a series of lines containing the output + * from an output-writing tmux command (such as any of the "show-*" or + * "list-*" commands). + * + * In either case, the full content of the copy-mode grid is pointed at + * by the "backing" field, and is copied into "screen" as needed (that + * is, when scrolling occurs). When copy-mode is backed by a pane, + * backing points directly at that pane's screen structure (&wp->base); + * when backed by a list of output-lines from a command, it points at + * a newly-allocated screen structure (which is deallocated when the + * mode ends). + */ +struct window_copy_mode_data { + struct screen screen; + + struct screen *backing; + int backing_written; /* backing display has started */ + + struct mode_key_data mdata; + + u_int oy; + + u_int selx; + u_int sely; + + u_int rectflag; /* are we in rectangle copy mode? */ + + u_int cx; + u_int cy; + + u_int lastcx; /* position in last line with content */ + u_int lastsx; /* size of last line with content */ + + enum window_copy_input_type inputtype; + const char *inputprompt; + char *inputstr; + + u_int numprefix; + + enum window_copy_input_type searchtype; + char *searchstr; + + enum window_copy_input_type jumptype; + char jumpchar; +}; + +struct screen * +window_copy_init(struct window_pane *wp) +{ + struct window_copy_mode_data *data; + struct screen *s; + int keys; + + wp->modedata = data = xmalloc(sizeof *data); + data->oy = 0; + data->cx = 0; + data->cy = 0; + + data->lastcx = 0; + data->lastsx = 0; + + data->backing_written = 0; + + data->rectflag = 0; + + data->inputtype = WINDOW_COPY_OFF; + data->inputprompt = NULL; + data->inputstr = xstrdup(""); + data->numprefix = 0; + + data->searchtype = WINDOW_COPY_OFF; + data->searchstr = NULL; + + if (wp->fd != -1) + bufferevent_disable(wp->event, EV_READ|EV_WRITE); + + data->jumptype = WINDOW_COPY_OFF; + data->jumpchar = '\0'; + + s = &data->screen; + screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); + if (options_get_number(&wp->window->options, "mode-mouse")) + s->mode |= MODE_MOUSE_STANDARD; + + keys = options_get_number(&wp->window->options, "mode-keys"); + if (keys == MODEKEY_EMACS) + mode_key_init(&data->mdata, &mode_key_tree_emacs_copy); + else + mode_key_init(&data->mdata, &mode_key_tree_vi_copy); + + data->backing = NULL; + + return (s); +} + +void +window_copy_init_from_pane(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct screen_write_ctx ctx; + u_int i; + + if (wp->mode != &window_copy_mode) + fatalx("not in copy mode"); + + data->backing = &wp->base; + data->cx = data->backing->cx; + data->cy = data->backing->cy; + + s->cx = data->cx; + s->cy = data->cy; + + screen_write_start(&ctx, NULL, s); + for (i = 0; i < screen_size_y(s); i++) + window_copy_write_line(wp, &ctx, i); + screen_write_cursormove(&ctx, data->cx, data->cy); + screen_write_stop(&ctx); +} + +void +window_copy_init_for_output(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + + data->backing = xmalloc(sizeof *data->backing); + screen_init(data->backing, screen_size_x(&wp->base), + screen_size_y(&wp->base), UINT_MAX); + data->backing->mode &= ~MODE_WRAP; +} + +void +window_copy_free(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + + if (wp->fd != -1) + bufferevent_enable(wp->event, EV_READ|EV_WRITE); + + if (data->searchstr != NULL) + xfree(data->searchstr); + xfree(data->inputstr); + + if (data->backing != &wp->base) { + screen_free(data->backing); + xfree(data->backing); + } + screen_free(&data->screen); + + xfree(data); +} + +void +window_copy_add(struct window_pane *wp, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + window_copy_vadd(wp, fmt, ap); + va_end(ap); +} + +void +window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *backing = data->backing; + struct screen_write_ctx back_ctx, ctx; + struct grid_cell gc; + int utf8flag; + u_int old_hsize; + + if (backing == &wp->base) + return; + + utf8flag = options_get_number(&wp->window->options, "utf8"); + memcpy(&gc, &grid_default_cell, sizeof gc); + + old_hsize = screen_hsize(data->backing); + screen_write_start(&back_ctx, NULL, backing); + if (data->backing_written) { + /* + * On the second or later line, do a CRLF before writing + * (so it's on a new line). + */ + screen_write_carriagereturn(&back_ctx); + screen_write_linefeed(&back_ctx, 0); + } else + data->backing_written = 1; + screen_write_vnputs(&back_ctx, 0, &gc, utf8flag, fmt, ap); + screen_write_stop(&back_ctx); + + data->oy += screen_hsize(data->backing) - old_hsize; + + screen_write_start(&ctx, wp, &data->screen); + + /* + * If the history has changed, draw the top line. + * (If there's any history at all, it has changed.) + */ + if (screen_hsize(data->backing)) + window_copy_redraw_lines(wp, 0, 1); + + /* Write the line, if it's visible. */ + if (backing->cy + data->oy < screen_size_y(backing)) + window_copy_redraw_lines(wp, backing->cy, 1); + + screen_write_stop(&ctx); +} + +void +window_copy_pageup(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + u_int n; + + n = 1; + if (screen_size_y(s) > 2) + n = screen_size_y(s) - 2; + if (data->oy + n > screen_hsize(data->backing)) + data->oy = screen_hsize(data->backing); + else + data->oy += n; + window_copy_update_selection(wp); + window_copy_redraw_screen(wp); +} + +void +window_copy_resize(struct window_pane *wp, u_int sx, u_int sy) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct screen_write_ctx ctx; + + screen_resize(s, sx, sy); + if (data->backing != &wp->base) + screen_resize(data->backing, sx, sy); + + if (data->cy > sy - 1) + data->cy = sy - 1; + if (data->cx > sx) + data->cx = sx; + if (data->oy > screen_hsize(data->backing)) + data->oy = screen_hsize(data->backing); + + window_copy_clear_selection(wp); + + screen_write_start(&ctx, NULL, s); + window_copy_write_lines(wp, &ctx, 0, screen_size_y(s) - 1); + screen_write_stop(&ctx); + + window_copy_redraw_screen(wp); +} + +void +window_copy_key(struct window_pane *wp, struct session *sess, int key) +{ + const char *word_separators; + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + u_int n, np; + int keys; + enum mode_key_cmd cmd; + + np = data->numprefix; + if (np == 0) + np = 1; + + if (data->inputtype == WINDOW_COPY_JUMPFORWARD || + data->inputtype == WINDOW_COPY_JUMPBACK) { + /* Ignore keys with modifiers. */ + if ((key & KEYC_MASK_MOD) == 0) { + data->jumpchar = key; + if (data->inputtype == WINDOW_COPY_JUMPFORWARD) { + for (; np != 0; np--) + window_copy_cursor_jump(wp); + } else { + for (; np != 0; np--) + window_copy_cursor_jump_back(wp); + } + } + data->jumptype = data->inputtype; + data->inputtype = WINDOW_COPY_OFF; + window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); + return; + } else if (data->inputtype == WINDOW_COPY_NUMERICPREFIX) { + if (window_copy_key_numeric_prefix(wp, key) == 0) + return; + data->inputtype = WINDOW_COPY_OFF; + window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); + } else if (data->inputtype != WINDOW_COPY_OFF) { + if (window_copy_key_input(wp, key) != 0) + goto input_off; + return; + } + + cmd = mode_key_lookup(&data->mdata, key); + switch (cmd) { + case MODEKEYCOPY_CANCEL: + window_pane_reset_mode(wp); + return; + case MODEKEYCOPY_LEFT: + for (; np != 0; np--) + window_copy_cursor_left(wp); + break; + case MODEKEYCOPY_RIGHT: + for (; np != 0; np--) + window_copy_cursor_right(wp); + break; + case MODEKEYCOPY_UP: + for (; np != 0; np--) + window_copy_cursor_up(wp, 0); + break; + case MODEKEYCOPY_DOWN: + for (; np != 0; np--) + window_copy_cursor_down(wp, 0); + break; + case MODEKEYCOPY_SCROLLUP: + for (; np != 0; np--) + window_copy_cursor_up(wp, 1); + break; + case MODEKEYCOPY_SCROLLDOWN: + for (; np != 0; np--) + window_copy_cursor_down(wp, 1); + break; + case MODEKEYCOPY_PREVIOUSPAGE: + for (; np != 0; np--) + window_copy_pageup(wp); + break; + case MODEKEYCOPY_NEXTPAGE: + n = 1; + if (screen_size_y(s) > 2) + n = screen_size_y(s) - 2; + for (; np != 0; np--) { + if (data->oy < n) + data->oy = 0; + else + data->oy -= n; + } + window_copy_update_selection(wp); + window_copy_redraw_screen(wp); + break; + case MODEKEYCOPY_HALFPAGEUP: + n = screen_size_y(s) / 2; + for (; np != 0; np--) { + if (data->oy + n > screen_hsize(data->backing)) + data->oy = screen_hsize(data->backing); + else + data->oy += n; + } + window_copy_update_selection(wp); + window_copy_redraw_screen(wp); + break; + case MODEKEYCOPY_HALFPAGEDOWN: + n = screen_size_y(s) / 2; + for (; np != 0; np--) { + if (data->oy < n) + data->oy = 0; + else + data->oy -= n; + } + window_copy_update_selection(wp); + window_copy_redraw_screen(wp); + break; + case MODEKEYCOPY_TOPLINE: + data->cx = 0; + data->cy = 0; + window_copy_update_selection(wp); + window_copy_redraw_screen(wp); + break; + case MODEKEYCOPY_MIDDLELINE: + data->cx = 0; + data->cy = (screen_size_y(s) - 1) / 2; + window_copy_update_selection(wp); + window_copy_redraw_screen(wp); + break; + case MODEKEYCOPY_BOTTOMLINE: + data->cx = 0; + data->cy = screen_size_y(s) - 1; + window_copy_update_selection(wp); + window_copy_redraw_screen(wp); + break; + case MODEKEYCOPY_HISTORYTOP: + data->cx = 0; + data->cy = 0; + data->oy = screen_hsize(data->backing); + window_copy_update_selection(wp); + window_copy_redraw_screen(wp); + break; + case MODEKEYCOPY_HISTORYBOTTOM: + data->cx = 0; + data->cy = screen_size_y(s) - 1; + data->oy = 0; + window_copy_update_selection(wp); + window_copy_redraw_screen(wp); + break; + case MODEKEYCOPY_STARTSELECTION: + window_copy_start_selection(wp); + window_copy_redraw_screen(wp); + break; + case MODEKEYCOPY_COPYLINE: + case MODEKEYCOPY_SELECTLINE: + window_copy_cursor_start_of_line(wp); + /* FALLTHROUGH */ + case MODEKEYCOPY_COPYENDOFLINE: + window_copy_start_selection(wp); + for (; np > 1; np--) + window_copy_cursor_down(wp, 0); + window_copy_cursor_end_of_line(wp); + window_copy_redraw_screen(wp); + + /* If a copy command then copy the selection and exit. */ + if (sess != NULL && + (cmd == MODEKEYCOPY_COPYLINE || + cmd == MODEKEYCOPY_COPYENDOFLINE)) { + window_copy_copy_selection(wp); + window_pane_reset_mode(wp); + return; + } + break; + case MODEKEYCOPY_CLEARSELECTION: + window_copy_clear_selection(wp); + window_copy_redraw_screen(wp); + break; + case MODEKEYCOPY_COPYSELECTION: + if (sess != NULL) { + window_copy_copy_selection(wp); + window_pane_reset_mode(wp); + return; + } + break; + case MODEKEYCOPY_STARTOFLINE: + window_copy_cursor_start_of_line(wp); + break; + case MODEKEYCOPY_BACKTOINDENTATION: + window_copy_cursor_back_to_indentation(wp); + break; + case MODEKEYCOPY_ENDOFLINE: + window_copy_cursor_end_of_line(wp); + break; + case MODEKEYCOPY_NEXTSPACE: + for (; np != 0; np--) + window_copy_cursor_next_word(wp, " "); + break; + case MODEKEYCOPY_NEXTSPACEEND: + for (; np != 0; np--) + window_copy_cursor_next_word_end(wp, " "); + break; + case MODEKEYCOPY_NEXTWORD: + word_separators = + options_get_string(&wp->window->options, "word-separators"); + for (; np != 0; np--) + window_copy_cursor_next_word(wp, word_separators); + break; + case MODEKEYCOPY_NEXTWORDEND: + word_separators = + options_get_string(&wp->window->options, "word-separators"); + for (; np != 0; np--) + window_copy_cursor_next_word_end(wp, word_separators); + break; + case MODEKEYCOPY_PREVIOUSSPACE: + for (; np != 0; np--) + window_copy_cursor_previous_word(wp, " "); + break; + case MODEKEYCOPY_PREVIOUSWORD: + word_separators = + options_get_string(&wp->window->options, "word-separators"); + for (; np != 0; np--) + window_copy_cursor_previous_word(wp, word_separators); + break; + case MODEKEYCOPY_JUMP: + data->inputtype = WINDOW_COPY_JUMPFORWARD; + data->inputprompt = "Jump Forward"; + *data->inputstr = '\0'; + window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); + return; /* skip numprefix reset */ + case MODEKEYCOPY_JUMPAGAIN: + if (data->jumptype == WINDOW_COPY_JUMPFORWARD) { + for (; np != 0; np--) + window_copy_cursor_jump(wp); + } else if (data->jumptype == WINDOW_COPY_JUMPBACK) { + for (; np != 0; np--) + window_copy_cursor_jump_back(wp); + } + break; + case MODEKEYCOPY_JUMPREVERSE: + if (data->jumptype == WINDOW_COPY_JUMPFORWARD) { + for (; np != 0; np--) + window_copy_cursor_jump_back(wp); + } else if (data->jumptype == WINDOW_COPY_JUMPBACK) { + for (; np != 0; np--) + window_copy_cursor_jump(wp); + } + break; + case MODEKEYCOPY_JUMPBACK: + data->inputtype = WINDOW_COPY_JUMPBACK; + data->inputprompt = "Jump Back"; + *data->inputstr = '\0'; + window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); + return; /* skip numprefix reset */ + case MODEKEYCOPY_SEARCHUP: + data->inputtype = WINDOW_COPY_SEARCHUP; + data->inputprompt = "Search Up"; + goto input_on; + case MODEKEYCOPY_SEARCHDOWN: + data->inputtype = WINDOW_COPY_SEARCHDOWN; + data->inputprompt = "Search Down"; + goto input_on; + case MODEKEYCOPY_SEARCHAGAIN: + case MODEKEYCOPY_SEARCHREVERSE: + switch (data->searchtype) { + case WINDOW_COPY_OFF: + case WINDOW_COPY_GOTOLINE: + case WINDOW_COPY_JUMPFORWARD: + case WINDOW_COPY_JUMPBACK: + case WINDOW_COPY_NUMERICPREFIX: + break; + case WINDOW_COPY_SEARCHUP: + if (cmd == MODEKEYCOPY_SEARCHAGAIN) { + for (; np != 0; np--) { + window_copy_search_up( + wp, data->searchstr); + } + } else { + for (; np != 0; np--) { + window_copy_search_down( + wp, data->searchstr); + } + } + break; + case WINDOW_COPY_SEARCHDOWN: + if (cmd == MODEKEYCOPY_SEARCHAGAIN) { + for (; np != 0; np--) { + window_copy_search_down( + wp, data->searchstr); + } + } else { + for (; np != 0; np--) { + window_copy_search_up( + wp, data->searchstr); + } + } + break; + } + break; + case MODEKEYCOPY_GOTOLINE: + data->inputtype = WINDOW_COPY_GOTOLINE; + data->inputprompt = "Goto Line"; + *data->inputstr = '\0'; + goto input_on; + case MODEKEYCOPY_STARTNUMBERPREFIX: + key &= KEYC_MASK_KEY; + if (key >= '0' && key <= '9') { + data->inputtype = WINDOW_COPY_NUMERICPREFIX; + data->numprefix = 0; + window_copy_key_numeric_prefix(wp, key); + return; + } + break; + case MODEKEYCOPY_RECTANGLETOGGLE: + window_copy_rectangle_toggle(wp); + break; + default: + break; + } + + data->numprefix = 0; + return; + +input_on: + keys = options_get_number(&wp->window->options, "mode-keys"); + if (keys == MODEKEY_EMACS) + mode_key_init(&data->mdata, &mode_key_tree_emacs_edit); + else + mode_key_init(&data->mdata, &mode_key_tree_vi_edit); + + window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); + return; + +input_off: + keys = options_get_number(&wp->window->options, "mode-keys"); + if (keys == MODEKEY_EMACS) + mode_key_init(&data->mdata, &mode_key_tree_emacs_copy); + else + mode_key_init(&data->mdata, &mode_key_tree_vi_copy); + + data->inputtype = WINDOW_COPY_OFF; + data->inputprompt = NULL; + + window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); +} + +int +window_copy_key_input(struct window_pane *wp, int key) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + size_t inputlen; + u_int np; + + switch (mode_key_lookup(&data->mdata, key)) { + case MODEKEYEDIT_CANCEL: + data->numprefix = 0; + return (-1); + case MODEKEYEDIT_BACKSPACE: + inputlen = strlen(data->inputstr); + if (inputlen > 0) + data->inputstr[inputlen - 1] = '\0'; + break; + case MODEKEYEDIT_DELETELINE: + *data->inputstr = '\0'; + break; + case MODEKEYEDIT_ENTER: + np = data->numprefix; + if (np == 0) + np = 1; + + switch (data->inputtype) { + case WINDOW_COPY_OFF: + case WINDOW_COPY_JUMPFORWARD: + case WINDOW_COPY_JUMPBACK: + case WINDOW_COPY_NUMERICPREFIX: + break; + case WINDOW_COPY_SEARCHUP: + for (; np != 0; np--) + window_copy_search_up(wp, data->inputstr); + data->searchtype = data->inputtype; + data->searchstr = xstrdup(data->inputstr); + break; + case WINDOW_COPY_SEARCHDOWN: + for (; np != 0; np--) + window_copy_search_down(wp, data->inputstr); + data->searchtype = data->inputtype; + data->searchstr = xstrdup(data->inputstr); + break; + case WINDOW_COPY_GOTOLINE: + window_copy_goto_line(wp, data->inputstr); + *data->inputstr = '\0'; + break; + } + data->numprefix = 0; + return (1); + case MODEKEY_OTHER: + if (key < 32 || key > 126) + break; + inputlen = strlen(data->inputstr) + 2; + + data->inputstr = xrealloc(data->inputstr, 1, inputlen); + data->inputstr[inputlen - 2] = key; + data->inputstr[inputlen - 1] = '\0'; + break; + default: + break; + } + + window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); + return (0); +} + +int +window_copy_key_numeric_prefix(struct window_pane *wp, int key) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + + key &= KEYC_MASK_KEY; + if (key < '0' || key > '9') + return 1; + + if (data->numprefix >= 100) /* no more than three digits */ + return 0; + data->numprefix = data->numprefix * 10 + key - '0'; + + window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); + return 0; +} + +/* ARGSUSED */ +void +window_copy_mouse( + struct window_pane *wp, struct session *sess, struct mouse_event *m) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + u_int i, old_cy; + + if (m->x >= screen_size_x(s)) + return; + if (m->y >= screen_size_y(s)) + return; + + /* If mouse wheel (buttons 4 and 5), scroll. */ + if ((m->b & MOUSE_45)) { + if ((m->b & MOUSE_BUTTON) == MOUSE_1) { + for (i = 0; i < 5; i++) + window_copy_cursor_up(wp, 0); + } else if ((m->b & MOUSE_BUTTON) == MOUSE_2) { + old_cy = data->cy; + for (i = 0; i < 5; i++) + window_copy_cursor_down(wp, 0); + if (old_cy == data->cy) + goto reset_mode; + } + return; + } + + /* + * If already reading motion, move the cursor while buttons are still + * pressed, or stop the selection on their release. + */ + if (s->mode & MODE_MOUSE_BUTTON) { + if ((m->b & MOUSE_BUTTON) != MOUSE_UP) { + window_copy_update_cursor(wp, m->x, m->y); + if (window_copy_update_selection(wp)) + window_copy_redraw_screen(wp); + return; + } + goto reset_mode; + } + + /* Otherwise if other buttons pressed, start selection and motion. */ + if ((m->b & MOUSE_BUTTON) != MOUSE_UP) { + s->mode &= ~MODE_MOUSE_STANDARD; + s->mode |= MODE_MOUSE_BUTTON; + + window_copy_update_cursor(wp, m->x, m->y); + window_copy_start_selection(wp); + window_copy_redraw_screen(wp); + } + + return; + +reset_mode: + s->mode &= ~MODE_MOUSE_BUTTON; + s->mode |= MODE_MOUSE_STANDARD; + if (sess != NULL) { + window_copy_copy_selection(wp); + window_pane_reset_mode(wp); + } +} + +void +window_copy_scroll_to(struct window_pane *wp, u_int px, u_int py) +{ + struct window_copy_mode_data *data = wp->modedata; + struct grid *gd = data->backing->grid; + u_int offset, gap; + + data->cx = px; + + gap = gd->sy / 4; + if (py < gd->sy) { + offset = 0; + data->cy = py; + } else if (py > gd->hsize + gd->sy - gap) { + offset = gd->hsize; + data->cy = py - gd->hsize; + } else { + offset = py + gap - gd->sy; + data->cy = py - offset; + } + data->oy = gd->hsize - offset; + + window_copy_update_selection(wp); + window_copy_redraw_screen(wp); +} + +int +window_copy_search_compare( + struct grid *gd, u_int px, u_int py, struct grid *sgd, u_int spx) +{ + const struct grid_cell *gc, *sgc; + const struct grid_utf8 *gu, *sgu; + + gc = grid_peek_cell(gd, px, py); + sgc = grid_peek_cell(sgd, spx, 0); + + if ((gc->flags & GRID_FLAG_UTF8) != (sgc->flags & GRID_FLAG_UTF8)) + return (0); + + if (gc->flags & GRID_FLAG_UTF8) { + gu = grid_peek_utf8(gd, px, py); + sgu = grid_peek_utf8(sgd, spx, 0); + if (grid_utf8_compare(gu, sgu)) + return (1); + } else { + if (gc->data == sgc->data) + return (1); + } + return (0); +} + +int +window_copy_search_lr(struct grid *gd, + struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last) +{ + u_int ax, bx, px; + + for (ax = first; ax < last; ax++) { + if (ax + sgd->sx >= gd->sx) + break; + for (bx = 0; bx < sgd->sx; bx++) { + px = ax + bx; + if (!window_copy_search_compare(gd, px, py, sgd, bx)) + break; + } + if (bx == sgd->sx) { + *ppx = ax; + return (1); + } + } + return (0); +} + +int +window_copy_search_rl(struct grid *gd, + struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last) +{ + u_int ax, bx, px; + + for (ax = last + 1; ax > first; ax--) { + if (gd->sx - (ax - 1) < sgd->sx) + continue; + for (bx = 0; bx < sgd->sx; bx++) { + px = ax - 1 + bx; + if (!window_copy_search_compare(gd, px, py, sgd, bx)) + break; + } + if (bx == sgd->sx) { + *ppx = ax - 1; + return (1); + } + } + return (0); +} + +void +window_copy_search_up(struct window_pane *wp, const char *searchstr) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = data->backing, ss; + struct screen_write_ctx ctx; + struct grid *gd = s->grid, *sgd; + struct grid_cell gc; + size_t searchlen; + u_int i, last, fx, fy, px; + int utf8flag, n, wrapped; + + if (*searchstr == '\0') + return; + utf8flag = options_get_number(&wp->window->options, "utf8"); + searchlen = screen_write_strlen(utf8flag, "%s", searchstr); + + screen_init(&ss, searchlen, 1, 0); + screen_write_start(&ctx, NULL, &ss); + memcpy(&gc, &grid_default_cell, sizeof gc); + screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr); + screen_write_stop(&ctx); + + fx = data->cx; + fy = gd->hsize - data->oy + data->cy; + + if (fx == 0) { + if (fy == 0) + return; + fx = gd->sx - 1; + fy--; + } else + fx--; + n = wrapped = 0; + +retry: + sgd = ss.grid; + for (i = fy + 1; i > 0; i--) { + last = screen_size_x(s); + if (i == fy + 1) + last = fx; + n = window_copy_search_rl(gd, sgd, &px, i - 1, 0, last); + if (n) { + window_copy_scroll_to(wp, px, i - 1); + break; + } + } + if (!n && !wrapped) { + fx = gd->sx - 1; + fy = gd->hsize + gd->sy - 1; + wrapped = 1; + goto retry; + } + + screen_free(&ss); +} + +void +window_copy_search_down(struct window_pane *wp, const char *searchstr) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = data->backing, ss; + struct screen_write_ctx ctx; + struct grid *gd = s->grid, *sgd; + struct grid_cell gc; + size_t searchlen; + u_int i, first, fx, fy, px; + int utf8flag, n, wrapped; + + if (*searchstr == '\0') + return; + utf8flag = options_get_number(&wp->window->options, "utf8"); + searchlen = screen_write_strlen(utf8flag, "%s", searchstr); + + screen_init(&ss, searchlen, 1, 0); + screen_write_start(&ctx, NULL, &ss); + memcpy(&gc, &grid_default_cell, sizeof gc); + screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr); + screen_write_stop(&ctx); + + fx = data->cx; + fy = gd->hsize - data->oy + data->cy; + + if (fx == gd->sx - 1) { + if (fy == gd->hsize + gd->sy) + return; + fx = 0; + fy++; + } else + fx++; + n = wrapped = 0; + +retry: + sgd = ss.grid; + for (i = fy + 1; i < gd->hsize + gd->sy; i++) { + first = 0; + if (i == fy + 1) + first = fx; + n = window_copy_search_lr(gd, sgd, &px, i - 1, first, gd->sx); + if (n) { + window_copy_scroll_to(wp, px, i - 1); + break; + } + } + if (!n && !wrapped) { + fx = 0; + fy = 0; + wrapped = 1; + goto retry; + } + + screen_free(&ss); +} + +void +window_copy_goto_line(struct window_pane *wp, const char *linestr) +{ + struct window_copy_mode_data *data = wp->modedata; + const char *errstr; + u_int lineno; + + lineno = strtonum(linestr, 0, screen_hsize(data->backing), &errstr); + if (errstr != NULL) + return; + + data->oy = lineno; + window_copy_update_selection(wp); + window_copy_redraw_screen(wp); +} + +void +window_copy_write_line( + struct window_pane *wp, struct screen_write_ctx *ctx, u_int py) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct options *oo = &wp->window->options; + struct grid_cell gc; + char hdr[32]; + size_t last, xoff = 0, size = 0; + + memcpy(&gc, &grid_default_cell, sizeof gc); + colour_set_fg(&gc, options_get_number(oo, "mode-fg")); + colour_set_bg(&gc, options_get_number(oo, "mode-bg")); + gc.attr |= options_get_number(oo, "mode-attr"); + + last = screen_size_y(s) - 1; + if (py == 0) { + size = xsnprintf(hdr, sizeof hdr, + "[%u/%u]", data->oy, screen_hsize(data->backing)); + if (size > screen_size_x(s)) + size = screen_size_x(s); + screen_write_cursormove(ctx, screen_size_x(s) - size, 0); + screen_write_puts(ctx, &gc, "%s", hdr); + } else if (py == last && data->inputtype != WINDOW_COPY_OFF) { + if (data->inputtype == WINDOW_COPY_NUMERICPREFIX) { + xoff = size = xsnprintf(hdr, sizeof hdr, + "Repeat: %u", data->numprefix); + } else { + xoff = size = xsnprintf(hdr, sizeof hdr, + "%s: %s", data->inputprompt, data->inputstr); + } + screen_write_cursormove(ctx, 0, last); + screen_write_puts(ctx, &gc, "%s", hdr); + } else + size = 0; + + screen_write_cursormove(ctx, xoff, py); + screen_write_copy(ctx, data->backing, xoff, + (screen_hsize(data->backing) - data->oy) + py, + screen_size_x(s) - size, 1); + + if (py == data->cy && data->cx == screen_size_x(s)) { + memcpy(&gc, &grid_default_cell, sizeof gc); + screen_write_cursormove(ctx, screen_size_x(s) - 1, py); + screen_write_putc(ctx, &gc, '$'); + } +} + +void +window_copy_write_lines( + struct window_pane *wp, struct screen_write_ctx *ctx, u_int py, u_int ny) +{ + u_int yy; + + for (yy = py; yy < py + ny; yy++) + window_copy_write_line(wp, ctx, py); +} + +void +window_copy_redraw_lines(struct window_pane *wp, u_int py, u_int ny) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen_write_ctx ctx; + u_int i; + + screen_write_start(&ctx, wp, NULL); + for (i = py; i < py + ny; i++) + window_copy_write_line(wp, &ctx, i); + screen_write_cursormove(&ctx, data->cx, data->cy); + screen_write_stop(&ctx); +} + +void +window_copy_redraw_screen(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + + window_copy_redraw_lines(wp, 0, screen_size_y(&data->screen)); +} + +void +window_copy_update_cursor(struct window_pane *wp, u_int cx, u_int cy) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct screen_write_ctx ctx; + u_int old_cx, old_cy; + + old_cx = data->cx; old_cy = data->cy; + data->cx = cx; data->cy = cy; + if (old_cx == screen_size_x(s)) + window_copy_redraw_lines(wp, old_cy, 1); + if (data->cx == screen_size_x(s)) + window_copy_redraw_lines(wp, data->cy, 1); + else { + screen_write_start(&ctx, wp, NULL); + screen_write_cursormove(&ctx, data->cx, data->cy); + screen_write_stop(&ctx); + } +} + +void +window_copy_start_selection(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + + data->selx = data->cx; + data->sely = screen_hsize(data->backing) + data->cy - data->oy; + + s->sel.flag = 1; + window_copy_update_selection(wp); +} + +int +window_copy_update_selection(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct options *oo = &wp->window->options; + struct grid_cell gc; + u_int sx, sy, ty, cy; + + if (!s->sel.flag) + return (0); + + /* Set colours. */ + memcpy(&gc, &grid_default_cell, sizeof gc); + colour_set_fg(&gc, options_get_number(oo, "mode-fg")); + colour_set_bg(&gc, options_get_number(oo, "mode-bg")); + gc.attr |= options_get_number(oo, "mode-attr"); + + /* Find top of screen. */ + ty = screen_hsize(data->backing) - data->oy; + + /* Adjust the selection. */ + sx = data->selx; + sy = data->sely; + if (sy < ty) { /* above screen */ + if (!data->rectflag) + sx = 0; + sy = 0; + } else if (sy > ty + screen_size_y(s) - 1) { /* below screen */ + if (!data->rectflag) + sx = screen_size_x(s) - 1; + sy = screen_size_y(s) - 1; + } else + sy -= ty; + sy = screen_hsize(s) + sy; + + screen_set_selection(s, + sx, sy, data->cx, screen_hsize(s) + data->cy, data->rectflag, &gc); + + if (data->rectflag) { + /* + * Can't rely on the caller to redraw the right lines for + * rectangle selection - find the highest line and the number + * of lines, and redraw just past that in both directions + */ + cy = data->cy; + if (sy < cy) + window_copy_redraw_lines(wp, sy, cy - sy + 1); + else + window_copy_redraw_lines(wp, cy, sy - cy + 1); + } + + return (1); +} + +void +window_copy_copy_selection(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + char *buf; + size_t off; + u_int i, xx, yy, sx, sy, ex, ey, limit; + u_int firstsx, lastex, restex, restsx; + int keys; + + if (!s->sel.flag) + return; + + buf = xmalloc(1); + off = 0; + + *buf = '\0'; + + /* + * The selection extends from selx,sely to (adjusted) cx,cy on + * the base screen. + */ + + /* Find start and end. */ + xx = data->cx; + yy = screen_hsize(data->backing) + data->cy - data->oy; + if (yy < data->sely || (yy == data->sely && xx < data->selx)) { + sx = xx; sy = yy; + ex = data->selx; ey = data->sely; + } else { + sx = data->selx; sy = data->sely; + ex = xx; ey = yy; + } + + /* Trim ex to end of line. */ + xx = window_copy_find_length(wp, ey); + if (ex > xx) + ex = xx; + + /* + * Deal with rectangle-copy if necessary; four situations: start of + * first line (firstsx), end of last line (lastex), start (restsx) and + * end (restex) of all other lines. + */ + xx = screen_size_x(s); + + /* + * Behave according to mode-keys. If it is emacs, copy like emacs, + * keeping the top-left-most character, and dropping the + * bottom-right-most, regardless of copy direction. If it is vi, also + * keep bottom-right-most character. + */ + keys = options_get_number(&wp->window->options, "mode-keys"); + if (data->rectflag) { + /* + * Need to ignore the column with the cursor in it, which for + * rectangular copy means knowing which side the cursor is on. + */ + if (data->selx < data->cx) { + /* Selection start is on the left. */ + if (keys == MODEKEY_EMACS) { + lastex = data->cx; + restex = data->cx; + } + else { + lastex = data->cx + 1; + restex = data->cx + 1; + } + firstsx = data->selx; + restsx = data->selx; + } else { + /* Cursor is on the left. */ + lastex = data->selx + 1; + restex = data->selx + 1; + firstsx = data->cx; + restsx = data->cx; + } + } else { + if (keys == MODEKEY_EMACS) + lastex = ex; + else + lastex = ex + 1; + restex = xx; + firstsx = sx; + restsx = 0; + } + + /* Copy the lines. */ + if (sy == ey) + window_copy_copy_line(wp, &buf, &off, sy, firstsx, lastex); + else { + window_copy_copy_line(wp, &buf, &off, sy, firstsx, restex); + if (ey - sy > 1) { + for (i = sy + 1; i < ey; i++) { + window_copy_copy_line( + wp, &buf, &off, i, restsx, restex); + } + } + window_copy_copy_line(wp, &buf, &off, ey, restsx, lastex); + } + + /* Don't bother if no data. */ + if (off == 0) { + xfree(buf); + return; + } + off--; /* remove final \n */ + + if (options_get_number(&global_options, "set-clipboard")) + screen_write_setselection(&wp->ictx.ctx, (u_char *)buf, off); + + /* Add the buffer to the stack. */ + limit = options_get_number(&global_options, "buffer-limit"); + paste_add(&global_buffers, buf, off, limit); +} + +void +window_copy_copy_line(struct window_pane *wp, + char **buf, size_t *off, u_int sy, u_int sx, u_int ex) +{ + struct window_copy_mode_data *data = wp->modedata; + struct grid *gd = data->backing->grid; + const struct grid_cell *gc; + const struct grid_utf8 *gu; + struct grid_line *gl; + u_int i, xx, wrapped = 0; + size_t size; + + if (sx > ex) + return; + + /* + * Work out if the line was wrapped at the screen edge and all of it is + * on screen. + */ + gl = &gd->linedata[sy]; + if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx) + wrapped = 1; + + /* If the line was wrapped, don't strip spaces (use the full length). */ + if (wrapped) + xx = gl->cellsize; + else + xx = window_copy_find_length(wp, sy); + if (ex > xx) + ex = xx; + if (sx > xx) + sx = xx; + + if (sx < ex) { + for (i = sx; i < ex; i++) { + gc = grid_peek_cell(gd, i, sy); + if (gc->flags & GRID_FLAG_PADDING) + continue; + if (!(gc->flags & GRID_FLAG_UTF8)) { + *buf = xrealloc(*buf, 1, (*off) + 1); + (*buf)[(*off)++] = gc->data; + } else { + gu = grid_peek_utf8(gd, i, sy); + size = grid_utf8_size(gu); + *buf = xrealloc(*buf, 1, (*off) + size); + *off += grid_utf8_copy(gu, *buf + *off, size); + } + } + } + + /* Only add a newline if the line wasn't wrapped. */ + if (!wrapped || ex != xx) { + *buf = xrealloc(*buf, 1, (*off) + 1); + (*buf)[(*off)++] = '\n'; + } +} + +void +window_copy_clear_selection(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + u_int px, py; + + screen_clear_selection(&data->screen); + + py = screen_hsize(data->backing) + data->cy - data->oy; + px = window_copy_find_length(wp, py); + if (data->cx > px) + window_copy_update_cursor(wp, px, data->cy); +} + +int +window_copy_in_set(struct window_pane *wp, u_int px, u_int py, const char *set) +{ + struct window_copy_mode_data *data = wp->modedata; + const struct grid_cell *gc; + + gc = grid_peek_cell(data->backing->grid, px, py); + if (gc->flags & (GRID_FLAG_PADDING|GRID_FLAG_UTF8)) + return (0); + if (gc->data == 0x00 || gc->data == 0x7f) + return (0); + return (strchr(set, gc->data) != NULL); +} + +u_int +window_copy_find_length(struct window_pane *wp, u_int py) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = data->backing; + const struct grid_cell *gc; + u_int px; + + /* + * If the pane has been resized, its grid can contain old overlong + * lines. grid_peek_cell does not allow accessing cells beyond the + * width of the grid, and screen_write_copy treats them as spaces, so + * ignore them here too. + */ + px = s->grid->linedata[py].cellsize; + if (px > screen_size_x(s)) + px = screen_size_x(s); + while (px > 0) { + gc = grid_peek_cell(s->grid, px - 1, py); + if (gc->flags & GRID_FLAG_UTF8) + break; + if (gc->data != ' ') + break; + px--; + } + return (px); +} + +void +window_copy_cursor_start_of_line(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *back_s = data->backing; + struct grid *gd = back_s->grid; + u_int py; + + if (data->cx == 0) { + py = screen_hsize(back_s) + data->cy - data->oy; + while (py > 0 && gd->linedata[py-1].flags & GRID_LINE_WRAPPED) { + window_copy_cursor_up(wp, 0); + py = screen_hsize(back_s) + data->cy - data->oy; + } + } + window_copy_update_cursor(wp, 0, data->cy); + if (window_copy_update_selection(wp)) + window_copy_redraw_lines(wp, data->cy, 1); +} + +void +window_copy_cursor_back_to_indentation(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + u_int px, py, xx; + const struct grid_cell *gc; + + px = 0; + py = screen_hsize(data->backing) + data->cy - data->oy; + xx = window_copy_find_length(wp, py); + + while (px < xx) { + gc = grid_peek_cell(data->backing->grid, px, py); + if (gc->flags & GRID_FLAG_UTF8) + break; + if (gc->data != ' ') + break; + px++; + } + + window_copy_update_cursor(wp, px, data->cy); + if (window_copy_update_selection(wp)) + window_copy_redraw_lines(wp, data->cy, 1); +} + +void +window_copy_cursor_end_of_line(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *back_s = data->backing; + struct grid *gd = back_s->grid; + u_int px, py; + + py = screen_hsize(back_s) + data->cy - data->oy; + px = window_copy_find_length(wp, py); + + if (data->cx == px) { + if (data->screen.sel.flag && data->rectflag) + px = screen_size_x(back_s); + if (gd->linedata[py].flags & GRID_LINE_WRAPPED) { + while (py < gd->sy + gd->hsize && + gd->linedata[py].flags & GRID_LINE_WRAPPED) { + window_copy_cursor_down(wp, 0); + py = screen_hsize(back_s) + + data->cy - data->oy; + } + px = window_copy_find_length(wp, py); + } + } + window_copy_update_cursor(wp, px, data->cy); + + if (window_copy_update_selection(wp)) + window_copy_redraw_lines(wp, data->cy, 1); +} + +void +window_copy_cursor_left(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + + if (data->cx == 0) { + window_copy_cursor_up(wp, 0); + window_copy_cursor_end_of_line(wp); + } else { + window_copy_update_cursor(wp, data->cx - 1, data->cy); + if (window_copy_update_selection(wp)) + window_copy_redraw_lines(wp, data->cy, 1); + } +} + +void +window_copy_cursor_right(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + u_int px, py; + + if (data->screen.sel.flag && data->rectflag) + px = screen_size_x(&data->screen); + else { + py = screen_hsize(data->backing) + data->cy - data->oy; + px = window_copy_find_length(wp, py); + } + + if (data->cx >= px) { + window_copy_cursor_start_of_line(wp); + window_copy_cursor_down(wp, 0); + } else { + window_copy_update_cursor(wp, data->cx + 1, data->cy); + if (window_copy_update_selection(wp)) + window_copy_redraw_lines(wp, data->cy, 1); + } +} + +void +window_copy_cursor_up(struct window_pane *wp, int scroll_only) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + u_int ox, oy, px, py; + + oy = screen_hsize(data->backing) + data->cy - data->oy; + ox = window_copy_find_length(wp, oy); + if (ox != 0) { + data->lastcx = data->cx; + data->lastsx = ox; + } + + data->cx = data->lastcx; + if (scroll_only || data->cy == 0) { + window_copy_scroll_down(wp, 1); + if (scroll_only) { + if (data->cy == screen_size_y(s) - 1) + window_copy_redraw_lines(wp, data->cy, 1); + else + window_copy_redraw_lines(wp, data->cy, 2); + } + } else { + window_copy_update_cursor(wp, data->cx, data->cy - 1); + if (window_copy_update_selection(wp)) { + if (data->cy == screen_size_y(s) - 1) + window_copy_redraw_lines(wp, data->cy, 1); + else + window_copy_redraw_lines(wp, data->cy, 2); + } + } + + if (!data->screen.sel.flag || !data->rectflag) { + py = screen_hsize(data->backing) + data->cy - data->oy; + px = window_copy_find_length(wp, py); + if ((data->cx >= data->lastsx && data->cx != px) || + data->cx > px) + window_copy_cursor_end_of_line(wp); + } +} + +void +window_copy_cursor_down(struct window_pane *wp, int scroll_only) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + u_int ox, oy, px, py; + + oy = screen_hsize(data->backing) + data->cy - data->oy; + ox = window_copy_find_length(wp, oy); + if (ox != 0) { + data->lastcx = data->cx; + data->lastsx = ox; + } + + data->cx = data->lastcx; + if (scroll_only || data->cy == screen_size_y(s) - 1) { + window_copy_scroll_up(wp, 1); + if (scroll_only && data->cy > 0) + window_copy_redraw_lines(wp, data->cy - 1, 2); + } else { + window_copy_update_cursor(wp, data->cx, data->cy + 1); + if (window_copy_update_selection(wp)) + window_copy_redraw_lines(wp, data->cy - 1, 2); + } + + if (!data->screen.sel.flag || !data->rectflag) { + py = screen_hsize(data->backing) + data->cy - data->oy; + px = window_copy_find_length(wp, py); + if ((data->cx >= data->lastsx && data->cx != px) || + data->cx > px) + window_copy_cursor_end_of_line(wp); + } +} + +void +window_copy_cursor_jump(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *back_s = data->backing; + const struct grid_cell *gc; + u_int px, py, xx; + + px = data->cx + 1; + py = screen_hsize(back_s) + data->cy - data->oy; + xx = window_copy_find_length(wp, py); + + while (px < xx) { + gc = grid_peek_cell(back_s->grid, px, py); + if ((gc->flags & (GRID_FLAG_PADDING|GRID_FLAG_UTF8)) == 0 + && gc->data == data->jumpchar) { + + window_copy_update_cursor(wp, px, data->cy); + if (window_copy_update_selection(wp)) + window_copy_redraw_lines(wp, data->cy, 1); + return; + } + px++; + } +} + +void +window_copy_cursor_jump_back(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *back_s = data->backing; + const struct grid_cell *gc; + u_int px, py; + + px = data->cx; + py = screen_hsize(back_s) + data->cy - data->oy; + + if (px > 0) + px--; + + for (;;) { + gc = grid_peek_cell(back_s->grid, px, py); + if ((gc->flags & (GRID_FLAG_PADDING|GRID_FLAG_UTF8)) == 0 + && gc->data == data->jumpchar) { + + window_copy_update_cursor(wp, px, data->cy); + if (window_copy_update_selection(wp)) + window_copy_redraw_lines(wp, data->cy, 1); + return; + } + if (px == 0) + break; + px--; + } +} + +void +window_copy_cursor_next_word(struct window_pane *wp, const char *separators) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *back_s = data->backing; + u_int px, py, xx, yy; + int expected = 0; + + px = data->cx; + py = screen_hsize(back_s) + data->cy - data->oy; + xx = window_copy_find_length(wp, py); + yy = screen_hsize(back_s) + screen_size_y(back_s) - 1; + + /* + * First skip past any nonword characters and then any word characters. + * + * expected is initially set to 0 for the former and then 1 for the + * latter. + */ + do { + while (px > xx || + window_copy_in_set(wp, px, py, separators) == expected) { + /* Move down if we're past the end of the line. */ + if (px > xx) { + if (py == yy) + return; + window_copy_cursor_down(wp, 0); + px = 0; + + py = screen_hsize(back_s) + data->cy - data->oy; + xx = window_copy_find_length(wp, py); + } else + px++; + } + expected = !expected; + } while (expected == 1); + + window_copy_update_cursor(wp, px, data->cy); + if (window_copy_update_selection(wp)) + window_copy_redraw_lines(wp, data->cy, 1); +} + +void +window_copy_cursor_next_word_end(struct window_pane *wp, const char *separators) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *back_s = data->backing; + u_int px, py, xx, yy; + int expected = 1; + + px = data->cx; + py = screen_hsize(back_s) + data->cy - data->oy; + xx = window_copy_find_length(wp, py); + yy = screen_hsize(back_s) + screen_size_y(back_s) - 1; + + /* + * First skip past any word characters, then any nonword characters. + * + * expected is initially set to 1 for the former and then 0 for the + * latter. + */ + do { + while (px > xx || + window_copy_in_set(wp, px, py, separators) == expected) { + /* Move down if we're past the end of the line. */ + if (px > xx) { + if (py == yy) + return; + window_copy_cursor_down(wp, 0); + px = 0; + + py = screen_hsize(back_s) + data->cy - data->oy; + xx = window_copy_find_length(wp, py); + } else + px++; + } + expected = !expected; + } while (expected == 0); + + window_copy_update_cursor(wp, px, data->cy); + if (window_copy_update_selection(wp)) + window_copy_redraw_lines(wp, data->cy, 1); +} + +/* Move to the previous place where a word begins. */ +void +window_copy_cursor_previous_word(struct window_pane *wp, const char *separators) +{ + struct window_copy_mode_data *data = wp->modedata; + u_int px, py; + + px = data->cx; + py = screen_hsize(data->backing) + data->cy - data->oy; + + /* Move back to the previous word character. */ + for (;;) { + if (px > 0) { + px--; + if (!window_copy_in_set(wp, px, py, separators)) + break; + } else { + if (data->cy == 0 && + (screen_hsize(data->backing) == 0 || + data->oy >= screen_hsize(data->backing) - 1)) + goto out; + window_copy_cursor_up(wp, 0); + + py = screen_hsize(data->backing) + data->cy - data->oy; + px = window_copy_find_length(wp, py); + } + } + + /* Move back to the beginning of this word. */ + while (px > 0 && !window_copy_in_set(wp, px - 1, py, separators)) + px--; + +out: + window_copy_update_cursor(wp, px, data->cy); + if (window_copy_update_selection(wp)) + window_copy_redraw_lines(wp, data->cy, 1); +} + +void +window_copy_scroll_up(struct window_pane *wp, u_int ny) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct screen_write_ctx ctx; + + if (data->oy < ny) + ny = data->oy; + if (ny == 0) + return; + data->oy -= ny; + + screen_write_start(&ctx, wp, NULL); + screen_write_cursormove(&ctx, 0, 0); + screen_write_deleteline(&ctx, ny); + window_copy_write_lines(wp, &ctx, screen_size_y(s) - ny, ny); + window_copy_write_line(wp, &ctx, 0); + if (screen_size_y(s) > 1) + window_copy_write_line(wp, &ctx, 1); + if (screen_size_y(s) > 3) + window_copy_write_line(wp, &ctx, screen_size_y(s) - 2); + if (s->sel.flag && screen_size_y(s) > ny) { + window_copy_update_selection(wp); + window_copy_write_line(wp, &ctx, screen_size_y(s) - ny - 1); + } + screen_write_cursormove(&ctx, data->cx, data->cy); + window_copy_update_selection(wp); + screen_write_stop(&ctx); +} + +void +window_copy_scroll_down(struct window_pane *wp, u_int ny) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + struct screen_write_ctx ctx; + + if (ny > screen_hsize(data->backing)) + return; + + if (data->oy > screen_hsize(data->backing) - ny) + ny = screen_hsize(data->backing) - data->oy; + if (ny == 0) + return; + data->oy += ny; + + screen_write_start(&ctx, wp, NULL); + screen_write_cursormove(&ctx, 0, 0); + screen_write_insertline(&ctx, ny); + window_copy_write_lines(wp, &ctx, 0, ny); + if (s->sel.flag && screen_size_y(s) > ny) { + window_copy_update_selection(wp); + window_copy_write_line(wp, &ctx, ny); + } else if (ny == 1) /* nuke position */ + window_copy_write_line(wp, &ctx, 1); + screen_write_cursormove(&ctx, data->cx, data->cy); + window_copy_update_selection(wp); + screen_write_stop(&ctx); +} + +void +window_copy_rectangle_toggle(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + u_int px, py; + + data->rectflag = !data->rectflag; + + py = screen_hsize(data->backing) + data->cy - data->oy; + px = window_copy_find_length(wp, py); + if (data->cx > px) + window_copy_update_cursor(wp, px, data->cy); + + window_copy_update_selection(wp); + window_copy_redraw_screen(wp); +} diff --git a/external/bsd/tmux/dist/window.c b/external/bsd/tmux/dist/window.c new file mode 100644 index 000000000..36b4f86cb --- /dev/null +++ b/external/bsd/tmux/dist/window.c @@ -0,0 +1,1092 @@ +/* $Id: window.c,v 1.4 2011/09/17 01:50:09 christos Exp $ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tmux.h" + +/* + * Each window is attached to a number of panes, each of which is a pty. This + * file contains code to handle them. + * + * A pane has two buffers attached, these are filled and emptied by the main + * server poll loop. Output data is received from pty's in screen format, + * translated and returned as a series of escape sequences and strings via + * input_parse (in input.c). Input data is received as key codes and written + * directly via input_key. + * + * Each pane also has a "virtual" screen (screen.c) which contains the current + * state and is redisplayed when the window is reattached to a client. + * + * Windows are stored directly on a global array and wrapped in any number of + * winlink structs to be linked onto local session RB trees. A reference count + * is maintained and a window removed from the global list and destroyed when + * it reaches zero. + */ + +/* Global window list. */ +struct windows windows; + +/* Global panes tree. */ +struct window_pane_tree all_window_panes; +u_int next_window_pane; + +void window_pane_read_callback(struct bufferevent *, void *); +void window_pane_error_callback(struct bufferevent *, short, void *); + +RB_GENERATE(winlinks, winlink, entry, winlink_cmp); + +int +winlink_cmp(struct winlink *wl1, struct winlink *wl2) +{ + return (wl1->idx - wl2->idx); +} + +RB_GENERATE(window_pane_tree, window_pane, tree_entry, window_pane_cmp); + +int +window_pane_cmp(struct window_pane *wp1, struct window_pane *wp2) +{ + return (wp1->id - wp2->id); +} + +struct winlink * +winlink_find_by_window(struct winlinks *wwl, struct window *w) +{ + struct winlink *wl; + + RB_FOREACH(wl, winlinks, wwl) { + if (wl->window == w) + return (wl); + } + + return (NULL); +} + +struct winlink * +winlink_find_by_index(struct winlinks *wwl, int idx) +{ + struct winlink wl; + + if (idx < 0) + fatalx("bad index"); + + wl.idx = idx; + return (RB_FIND(winlinks, wwl, &wl)); +} + +int +winlink_next_index(struct winlinks *wwl, int idx) +{ + int i; + + i = idx; + do { + if (winlink_find_by_index(wwl, i) == NULL) + return (i); + if (i == INT_MAX) + i = 0; + else + i++; + } while (i != idx); + return (-1); +} + +u_int +winlink_count(struct winlinks *wwl) +{ + struct winlink *wl; + u_int n; + + n = 0; + RB_FOREACH(wl, winlinks, wwl) + n++; + + return (n); +} + +struct winlink * +winlink_add(struct winlinks *wwl, int idx) +{ + struct winlink *wl; + + if (idx < 0) { + if ((idx = winlink_next_index(wwl, -idx - 1)) == -1) + return (NULL); + } else if (winlink_find_by_index(wwl, idx) != NULL) + return (NULL); + + wl = xcalloc(1, sizeof *wl); + wl->idx = idx; + RB_INSERT(winlinks, wwl, wl); + + return (wl); +} + +void +winlink_set_window(struct winlink *wl, struct window *w) +{ + wl->window = w; + w->references++; +} + +void +winlink_remove(struct winlinks *wwl, struct winlink *wl) +{ + struct window *w = wl->window; + + RB_REMOVE(winlinks, wwl, wl); + if (wl->status_text != NULL) + xfree(wl->status_text); + xfree(wl); + + if (w != NULL) { + if (w->references == 0) + fatal("bad reference count"); + w->references--; + if (w->references == 0) + window_destroy(w); + } +} + +struct winlink * +winlink_next(struct winlink *wl) +{ + return (RB_NEXT(winlinks, wwl, wl)); +} + +struct winlink * +winlink_previous(struct winlink *wl) +{ + return (RB_PREV(winlinks, wwl, wl)); +} + +struct winlink * +winlink_next_by_number(struct winlink *wl, struct session *s, int n) +{ + for (; n > 0; n--) { + if ((wl = RB_NEXT(winlinks, wwl, wl)) == NULL) + wl = RB_MIN(winlinks, &s->windows); + } + + return (wl); +} + +struct winlink * +winlink_previous_by_number(struct winlink *wl, struct session *s, int n) +{ + for (; n > 0; n--) { + if ((wl = RB_PREV(winlinks, wwl, wl)) == NULL) + wl = RB_MAX(winlinks, &s->windows); + } + + return (wl); +} + +void +winlink_stack_push(struct winlink_stack *stack, struct winlink *wl) +{ + if (wl == NULL) + return; + + winlink_stack_remove(stack, wl); + TAILQ_INSERT_HEAD(stack, wl, sentry); +} + +void +winlink_stack_remove(struct winlink_stack *stack, struct winlink *wl) +{ + struct winlink *wl2; + + if (wl == NULL) + return; + + TAILQ_FOREACH(wl2, stack, sentry) { + if (wl2 == wl) { + TAILQ_REMOVE(stack, wl, sentry); + return; + } + } +} + +int +window_index(struct window *s, u_int *i) +{ + for (*i = 0; *i < ARRAY_LENGTH(&windows); (*i)++) { + if (s == ARRAY_ITEM(&windows, *i)) + return (0); + } + return (-1); +} + +struct window * +window_create1(u_int sx, u_int sy) +{ + struct window *w; + u_int i; + + w = xcalloc(1, sizeof *w); + w->name = NULL; + w->flags = 0; + + TAILQ_INIT(&w->panes); + w->active = NULL; + + w->lastlayout = -1; + w->layout_root = NULL; + + w->sx = sx; + w->sy = sy; + + queue_window_name(w); + + options_init(&w->options, &global_w_options); + + for (i = 0; i < ARRAY_LENGTH(&windows); i++) { + if (ARRAY_ITEM(&windows, i) == NULL) { + ARRAY_SET(&windows, i, w); + break; + } + } + if (i == ARRAY_LENGTH(&windows)) + ARRAY_ADD(&windows, w); + w->references = 0; + + return (w); +} + +struct window * +window_create(const char *name, const char *cmd, const char *shell, + const char *cwd, struct environ *env, struct termios *tio, + u_int sx, u_int sy, u_int hlimit,char **cause) +{ + struct window *w; + struct window_pane *wp; + + w = window_create1(sx, sy); + wp = window_add_pane(w, hlimit); + layout_init(w); + if (window_pane_spawn(wp, cmd, shell, cwd, env, tio, cause) != 0) { + window_destroy(w); + return (NULL); + } + w->active = TAILQ_FIRST(&w->panes); + if (name != NULL) { + w->name = xstrdup(name); + options_set_number(&w->options, "automatic-rename", 0); + } else + w->name = default_window_name(w); + return (w); +} + +void +window_destroy(struct window *w) +{ + u_int i; + + if (window_index(w, &i) != 0) + fatalx("index not found"); + ARRAY_SET(&windows, i, NULL); + while (!ARRAY_EMPTY(&windows) && ARRAY_LAST(&windows) == NULL) + ARRAY_TRUNC(&windows, 1); + + if (w->layout_root != NULL) + layout_free(w); + + evtimer_del(&w->name_timer); + + options_free(&w->options); + + window_destroy_panes(w); + + if (w->name != NULL) + xfree(w->name); + xfree(w); +} + +void +window_resize(struct window *w, u_int sx, u_int sy) +{ + w->sx = sx; + w->sy = sy; +} + +void +window_set_active_pane(struct window *w, struct window_pane *wp) +{ + if (wp == w->active) + return; + w->last = w->active; + w->active = wp; + while (!window_pane_visible(w->active)) { + w->active = TAILQ_PREV(w->active, window_panes, entry); + if (w->active == NULL) + w->active = TAILQ_LAST(&w->panes, window_panes); + if (w->active == wp) + return; + } +} + +struct window_pane * +window_get_active_at(struct window *w, u_int x, u_int y) +{ + struct window_pane *wp; + + TAILQ_FOREACH(wp, &w->panes, entry) { + if (!window_pane_visible(wp)) + continue; + if (x < wp->xoff || x > wp->xoff + wp->sx) + continue; + if (y < wp->yoff || y > wp->yoff + wp->sy) + continue; + return (wp); + } + return (NULL); +} + +void +window_set_active_at(struct window *w, u_int x, u_int y) +{ + struct window_pane *wp; + + wp = window_get_active_at(w, x, y); + if (wp != NULL && wp != w->active) + window_set_active_pane(w, wp); +} + +struct window_pane * +window_find_string(struct window *w, const char *s) +{ + u_int x, y; + + x = w->sx / 2; + y = w->sy / 2; + + if (strcasecmp(s, "top") == 0) + y = 0; + else if (strcasecmp(s, "bottom") == 0) + y = w->sy - 1; + else if (strcasecmp(s, "left") == 0) + x = 0; + else if (strcasecmp(s, "right") == 0) + x = w->sx - 1; + else if (strcasecmp(s, "top-left") == 0) { + x = 0; + y = 0; + } else if (strcasecmp(s, "top-right") == 0) { + x = w->sx - 1; + y = 0; + } else if (strcasecmp(s, "bottom-left") == 0) { + x = 0; + y = w->sy - 1; + } else if (strcasecmp(s, "bottom-right") == 0) { + x = w->sx - 1; + y = w->sy - 1; + } else + return (NULL); + + return (window_get_active_at(w, x, y)); +} + +struct window_pane * +window_add_pane(struct window *w, u_int hlimit) +{ + struct window_pane *wp; + + wp = window_pane_create(w, w->sx, w->sy, hlimit); + if (TAILQ_EMPTY(&w->panes)) + TAILQ_INSERT_HEAD(&w->panes, wp, entry); + else + TAILQ_INSERT_AFTER(&w->panes, w->active, wp, entry); + return (wp); +} + +void +window_remove_pane(struct window *w, struct window_pane *wp) +{ + if (wp == w->active) { + w->active = w->last; + w->last = NULL; + if (w->active == NULL) { + w->active = TAILQ_PREV(wp, window_panes, entry); + if (w->active == NULL) + w->active = TAILQ_NEXT(wp, entry); + } + } else if (wp == w->last) + w->last = NULL; + + TAILQ_REMOVE(&w->panes, wp, entry); + window_pane_destroy(wp); +} + +struct window_pane * +window_pane_at_index(struct window *w, u_int idx) +{ + struct window_pane *wp; + u_int n; + + n = 0; + TAILQ_FOREACH(wp, &w->panes, entry) { + if (n == idx) + return (wp); + n++; + } + return (NULL); +} + +struct window_pane * +window_pane_next_by_number(struct window *w, struct window_pane *wp, u_int n) +{ + for (; n > 0; n--) { + if ((wp = TAILQ_NEXT(wp, entry)) == NULL) + wp = TAILQ_FIRST(&w->panes); + } + + return (wp); +} + +struct window_pane * +window_pane_previous_by_number(struct window *w, struct window_pane *wp, + u_int n) +{ + for (; n > 0; n--) { + if ((wp = TAILQ_PREV(wp, window_panes, entry)) == NULL) + wp = TAILQ_LAST(&w->panes, window_panes); + } + + return (wp); +} + +u_int +window_pane_index(struct window *w, struct window_pane *wp) +{ + struct window_pane *wq; + u_int n; + + n = 0; + TAILQ_FOREACH(wq, &w->panes, entry) { + if (wp == wq) + break; + n++; + } + return (n); +} + +u_int +window_count_panes(struct window *w) +{ + struct window_pane *wp; + u_int n; + + n = 0; + TAILQ_FOREACH(wp, &w->panes, entry) + n++; + return (n); +} + +void +window_destroy_panes(struct window *w) +{ + struct window_pane *wp; + + while (!TAILQ_EMPTY(&w->panes)) { + wp = TAILQ_FIRST(&w->panes); + TAILQ_REMOVE(&w->panes, wp, entry); + window_pane_destroy(wp); + } +} + +/* Return list of printable window flag symbols. No flags is just a space. */ +char * +window_printable_flags(struct session *s, struct winlink *wl) +{ + char flags[BUFSIZ]; + int pos; + + pos = 0; + if (wl->flags & WINLINK_ACTIVITY) + flags[pos++] = '#'; + if (wl->flags & WINLINK_BELL) + flags[pos++] = '!'; + if (wl->flags & WINLINK_CONTENT) + flags[pos++] = '+'; + if (wl->flags & WINLINK_SILENCE) + flags[pos++] = '~'; + if (wl == s->curw) + flags[pos++] = '*'; + if (wl == TAILQ_FIRST(&s->lastw)) + flags[pos++] = '-'; + if (pos == 0) + flags[pos++] = ' '; + flags[pos] = '\0'; + return (xstrdup(flags)); +} + +/* Find pane in global tree by id. */ +struct window_pane * +window_pane_find_by_id(u_int id) +{ + struct window_pane wp; + + wp.id = id; + return (RB_FIND(window_pane_tree, &all_window_panes, &wp)); +} + +struct window_pane * +window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) +{ + struct window_pane *wp; + + wp = xcalloc(1, sizeof *wp); + wp->window = w; + + wp->id = next_window_pane++; + RB_INSERT(window_pane_tree, &all_window_panes, wp); + + wp->cmd = NULL; + wp->shell = NULL; + wp->cwd = NULL; + + wp->fd = -1; + wp->event = NULL; + + wp->mode = NULL; + + wp->layout_cell = NULL; + + wp->xoff = 0; + wp->yoff = 0; + + wp->sx = sx; + wp->sy = sy; + + wp->pipe_fd = -1; + wp->pipe_off = 0; + wp->pipe_event = NULL; + + wp->saved_grid = NULL; + + screen_init(&wp->base, sx, sy, hlimit); + wp->screen = &wp->base; + + input_init(wp); + + return (wp); +} + +void +window_pane_destroy(struct window_pane *wp) +{ + window_pane_reset_mode(wp); + + if (wp->fd != -1) { + close(wp->fd); + bufferevent_free(wp->event); + } + + input_free(wp); + + screen_free(&wp->base); + if (wp->saved_grid != NULL) + grid_destroy(wp->saved_grid); + + if (wp->pipe_fd != -1) { + close(wp->pipe_fd); + bufferevent_free(wp->pipe_event); + } + + RB_REMOVE(window_pane_tree, &all_window_panes, wp); + + if (wp->cwd != NULL) + xfree(wp->cwd); + if (wp->shell != NULL) + xfree(wp->shell); + if (wp->cmd != NULL) + xfree(wp->cmd); + utmp_destroy(wp->utmp); + xfree(wp); +} + +int +window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell, + const char *cwd, struct environ *env, struct termios *tio, char **cause) +{ + struct winsize ws; + char *argv0, paneid[16]; + const char *ptr; + struct termios tio2; + + if (wp->fd != -1) { + close(wp->fd); + bufferevent_free(wp->event); + } + if (cmd != NULL) { + if (wp->cmd != NULL) + xfree(wp->cmd); + wp->cmd = xstrdup(cmd); + } + if (shell != NULL) { + if (wp->shell != NULL) + xfree(wp->shell); + wp->shell = xstrdup(shell); + } + if (cwd != NULL) { + if (wp->cwd != NULL) + xfree(wp->cwd); + wp->cwd = xstrdup(cwd); + } + + memset(&ws, 0, sizeof ws); + ws.ws_col = screen_size_x(&wp->base); + ws.ws_row = screen_size_y(&wp->base); + + switch (wp->pid = forkpty(&wp->fd, wp->tty, NULL, &ws)) { + case -1: + wp->fd = -1; + xasprintf(cause, "%s: %s", cmd, strerror(errno)); + return (-1); + case 0: + if (chdir(wp->cwd) != 0) + chdir("/"); + + if (tcgetattr(STDIN_FILENO, &tio2) != 0) + fatal("tcgetattr failed"); + if (tio != NULL) + memcpy(tio2.c_cc, tio->c_cc, sizeof tio2.c_cc); + tio2.c_cc[VERASE] = '\177'; + if (tcsetattr(STDIN_FILENO, TCSANOW, &tio2) != 0) + fatal("tcgetattr failed"); + + closefrom(STDERR_FILENO + 1); + + xsnprintf(paneid, sizeof paneid, "%%%u", wp->id); + environ_set(env, "TMUX_PANE", paneid); + environ_push(env); + + clear_signals(1); + log_close(); + + if (*wp->cmd != '\0') { + /* Set SHELL but only if it is currently not useful. */ + shell = getenv("SHELL"); + if (shell == NULL || *shell == '\0' || areshell(shell)) + setenv("SHELL", wp->shell, 1); + + execl(_PATH_BSHELL, "sh", "-c", wp->cmd, (char *) NULL); + fatal("execl failed"); + } + + /* No command; fork a login shell. */ + ptr = strrchr(wp->shell, '/'); + if (ptr != NULL && *(ptr + 1) != '\0') + xasprintf(&argv0, "-%s", ptr + 1); + else + xasprintf(&argv0, "-%s", wp->shell); + setenv("SHELL", wp->shell, 1); + execl(wp->shell, argv0, (char *) NULL); + fatal("execl failed"); + } + + setblocking(wp->fd, 0); + + wp->event = bufferevent_new(wp->fd, + window_pane_read_callback, NULL, window_pane_error_callback, wp); + bufferevent_enable(wp->event, EV_READ|EV_WRITE); + + wp->utmp = utmp_create(wp->tty); + + return (0); +} + +/* ARGSUSED */ +void +window_pane_read_callback(unused struct bufferevent *bufev, void *data) +{ + struct window_pane *wp = data; + char *new_data; + size_t new_size; + + new_size = EVBUFFER_LENGTH(wp->event->input) - wp->pipe_off; + if (wp->pipe_fd != -1 && new_size > 0) { + new_data = (char *)EVBUFFER_DATA(wp->event->input); + bufferevent_write(wp->pipe_event, new_data, new_size); + } + + input_parse(wp); + + wp->pipe_off = EVBUFFER_LENGTH(wp->event->input); + + /* + * If we get here, we're not outputting anymore, so set the silence + * flag on the window. + */ + wp->window->flags |= WINDOW_SILENCE; + if (gettimeofday(&wp->window->silence_timer, NULL) != 0) + fatal("gettimeofday failed."); +} + +/* ARGSUSED */ +void +window_pane_error_callback( + unused struct bufferevent *bufev, unused short what, void *data) +{ + struct window_pane *wp = data; + + server_destroy_pane(wp); +} + +void +window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) +{ + struct winsize ws; + + if (sx == wp->sx && sy == wp->sy) + return; + wp->sx = sx; + wp->sy = sy; + + memset(&ws, 0, sizeof ws); + ws.ws_col = sx; + ws.ws_row = sy; + + screen_resize(&wp->base, sx, sy); + if (wp->mode != NULL) + wp->mode->resize(wp, sx, sy); + + if (wp->fd != -1 && ioctl(wp->fd, TIOCSWINSZ, &ws) == -1) +#ifdef __sun + /* + * Some versions of Solaris apparently can return an error when + * resizing; don't know why this happens, can't reproduce on + * other platforms and ignoring it doesn't seem to cause any + * issues. + */ + if (errno != EINVAL) +#endif + fatal("ioctl failed"); +} + +/* + * Enter alternative screen mode. A copy of the visible screen is saved and the + * history is not updated + */ +void +window_pane_alternate_on(struct window_pane *wp, struct grid_cell *gc) +{ + struct screen *s = &wp->base; + u_int sx, sy; + + if (wp->saved_grid != NULL) + return; + if (!options_get_number(&wp->window->options, "alternate-screen")) + return; + sx = screen_size_x(s); + sy = screen_size_y(s); + + wp->saved_grid = grid_create(sx, sy, 0); + grid_duplicate_lines(wp->saved_grid, 0, s->grid, screen_hsize(s), sy); + wp->saved_cx = s->cx; + wp->saved_cy = s->cy; + memcpy(&wp->saved_cell, gc, sizeof wp->saved_cell); + + grid_view_clear(s->grid, 0, 0, sx, sy); + + wp->base.grid->flags &= ~GRID_HISTORY; + + wp->flags |= PANE_REDRAW; +} + +/* Exit alternate screen mode and restore the copied grid. */ +void +window_pane_alternate_off(struct window_pane *wp, struct grid_cell *gc) +{ + struct screen *s = &wp->base; + u_int sx, sy; + + if (wp->saved_grid == NULL) + return; + if (!options_get_number(&wp->window->options, "alternate-screen")) + return; + sx = screen_size_x(s); + sy = screen_size_y(s); + + /* + * If the current size is bigger, temporarily resize to the old size + * before copying back. + */ + if (sy > wp->saved_grid->sy) + screen_resize(s, sx, wp->saved_grid->sy); + + /* Restore the grid, cursor position and cell. */ + grid_duplicate_lines(s->grid, screen_hsize(s), wp->saved_grid, 0, sy); + s->cx = wp->saved_cx; + if (s->cx > screen_size_x(s) - 1) + s->cx = screen_size_x(s) - 1; + s->cy = wp->saved_cy; + if (s->cy > screen_size_y(s) - 1) + s->cy = screen_size_y(s) - 1; + memcpy(gc, &wp->saved_cell, sizeof *gc); + + /* + * Turn history back on (so resize can use it) and then resize back to + * the current size. + */ + wp->base.grid->flags |= GRID_HISTORY; + if (sy > wp->saved_grid->sy) + screen_resize(s, sx, sy); + + grid_destroy(wp->saved_grid); + wp->saved_grid = NULL; + + wp->flags |= PANE_REDRAW; +} + +int +window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode) +{ + struct screen *s; + + if (wp->mode != NULL) + return (1); + wp->mode = mode; + + if ((s = wp->mode->init(wp)) != NULL) + wp->screen = s; + wp->flags |= PANE_REDRAW; + return (0); +} + +void +window_pane_reset_mode(struct window_pane *wp) +{ + if (wp->mode == NULL) + return; + + wp->mode->free(wp); + wp->mode = NULL; + + wp->screen = &wp->base; + wp->flags |= PANE_REDRAW; +} + +void +window_pane_key(struct window_pane *wp, struct session *sess, int key) +{ + struct window_pane *wp2; + + if (!window_pane_visible(wp)) + return; + + if (wp->mode != NULL) { + if (wp->mode->key != NULL) + wp->mode->key(wp, sess, key); + return; + } + + if (wp->fd == -1) + return; + input_key(wp, key); + if (options_get_number(&wp->window->options, "synchronize-panes")) { + TAILQ_FOREACH(wp2, &wp->window->panes, entry) { + if (wp2 == wp || wp2->mode != NULL) + continue; + if (wp2->fd != -1 && window_pane_visible(wp2)) + input_key(wp2, key); + } + } +} + +void +window_pane_mouse( + struct window_pane *wp, struct session *sess, struct mouse_event *m) +{ + if (!window_pane_visible(wp)) + return; + + if (m->x < wp->xoff || m->x >= wp->xoff + wp->sx) + return; + if (m->y < wp->yoff || m->y >= wp->yoff + wp->sy) + return; + m->x -= wp->xoff; + m->y -= wp->yoff; + + if (wp->mode != NULL) { + if (wp->mode->mouse != NULL && + options_get_number(&wp->window->options, "mode-mouse")) + wp->mode->mouse(wp, sess, m); + } else if (wp->fd != -1) + input_mouse(wp, m); +} + +int +window_pane_visible(struct window_pane *wp) +{ + struct window *w = wp->window; + + if (wp->xoff >= w->sx || wp->yoff >= w->sy) + return (0); + if (wp->xoff + wp->sx > w->sx || wp->yoff + wp->sy > w->sy) + return (0); + return (1); +} + +char * +window_pane_search(struct window_pane *wp, const char *searchstr, u_int *lineno) +{ + struct screen *s = &wp->base; + char *newsearchstr, *line, *msg; + u_int i; + + msg = NULL; + xasprintf(&newsearchstr, "*%s*", searchstr); + + for (i = 0; i < screen_size_y(s); i++) { + line = grid_view_string_cells(s->grid, 0, i, screen_size_x(s)); + if (fnmatch(newsearchstr, line, 0) == 0) { + msg = line; + if (lineno != NULL) + *lineno = i; + break; + } + xfree(line); + } + + xfree(newsearchstr); + return (msg); +} + +/* Find the pane directly above another. */ +struct window_pane * +window_pane_find_up(struct window_pane *wp) +{ + struct window_pane *wp2; + u_int left, top; + + if (wp == NULL || !window_pane_visible(wp)) + return (NULL); + + top = wp->yoff; + if (top == 0) + top = wp->window->sy + 1; + left = wp->xoff; + + TAILQ_FOREACH(wp2, &wp->window->panes, entry) { + if (!window_pane_visible(wp2)) + continue; + if (wp2->yoff + wp2->sy + 1 != top) + continue; + if (left >= wp2->xoff && left <= wp2->xoff + wp2->sx) + return (wp2); + } + return (NULL); +} + +/* Find the pane directly below another. */ +struct window_pane * +window_pane_find_down(struct window_pane *wp) +{ + struct window_pane *wp2; + u_int left, bottom; + + if (wp == NULL || !window_pane_visible(wp)) + return (NULL); + + bottom = wp->yoff + wp->sy + 1; + if (bottom >= wp->window->sy) + bottom = 0; + left = wp->xoff; + + TAILQ_FOREACH(wp2, &wp->window->panes, entry) { + if (!window_pane_visible(wp2)) + continue; + if (wp2->yoff != bottom) + continue; + if (left >= wp2->xoff && left <= wp2->xoff + wp2->sx) + return (wp2); + } + return (NULL); +} + +/* + * Find the pane directly to the left of another, adjacent to the left side and + * containing the top edge. + */ +struct window_pane * +window_pane_find_left(struct window_pane *wp) +{ + struct window_pane *wp2; + u_int left, top; + + if (wp == NULL || !window_pane_visible(wp)) + return (NULL); + + left = wp->xoff; + if (left == 0) + left = wp->window->sx + 1; + top = wp->yoff; + + TAILQ_FOREACH(wp2, &wp->window->panes, entry) { + if (!window_pane_visible(wp2)) + continue; + if (wp2->xoff + wp2->sx + 1 != left) + continue; + if (top >= wp2->yoff && top <= wp2->yoff + wp2->sy) + return (wp2); + } + return (NULL); +} + +/* + * Find the pane directly to the right of another, that is adjacent to the + * right edge and including the top edge. + */ +struct window_pane * +window_pane_find_right(struct window_pane *wp) +{ + struct window_pane *wp2; + u_int right, top; + + if (wp == NULL || !window_pane_visible(wp)) + return (NULL); + + right = wp->xoff + wp->sx + 1; + if (right >= wp->window->sx) + right = 0; + top = wp->yoff; + + TAILQ_FOREACH(wp2, &wp->window->panes, entry) { + if (!window_pane_visible(wp2)) + continue; + if (wp2->xoff != right) + continue; + if (top >= wp2->yoff && top <= wp2->yoff + wp2->sy) + return (wp2); + } + return (NULL); +} diff --git a/external/bsd/tmux/dist/xmalloc.c b/external/bsd/tmux/dist/xmalloc.c new file mode 100644 index 000000000..fae5ebd2b --- /dev/null +++ b/external/bsd/tmux/dist/xmalloc.c @@ -0,0 +1,144 @@ +/* $Id: xmalloc.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv Exp $ */ + +/* + * Copyright (c) 2004 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include +#include + +#include "tmux.h" + +char * +xstrdup(const char *s) +{ + char *ptr; + size_t len; + + len = strlen(s) + 1; + ptr = xmalloc(len); + + strlcpy(ptr, s, len); + return (ptr); +} + +void * +xcalloc(size_t nmemb, size_t size) +{ + void *ptr; + + if (size == 0 || nmemb == 0) + fatalx("zero size"); + if (SIZE_MAX / nmemb < size) + fatalx("nmemb * size > SIZE_MAX"); + if ((ptr = calloc(nmemb, size)) == NULL) + fatal("xcalloc failed"); + + return (ptr); +} + +void * +xmalloc(size_t size) +{ + void *ptr; + + if (size == 0) + fatalx("zero size"); + if ((ptr = malloc(size)) == NULL) + fatal("xmalloc failed"); + + return (ptr); +} + +void * +xrealloc(void *oldptr, size_t nmemb, size_t size) +{ + size_t newsize = nmemb * size; + void *newptr; + + if (newsize == 0) + fatalx("zero size"); + if (SIZE_MAX / nmemb < size) + fatalx("nmemb * size > SIZE_MAX"); + if ((newptr = realloc(oldptr, newsize)) == NULL) + fatal("xrealloc failed"); + + return (newptr); +} + +void +xfree(void *ptr) +{ + if (ptr == NULL) + fatalx("null pointer"); + free(ptr); +} + +int printflike2 +xasprintf(char **ret, const char *fmt, ...) +{ + va_list ap; + int i; + + va_start(ap, fmt); + i = xvasprintf(ret, fmt, ap); + va_end(ap); + + return (i); +} + +int +xvasprintf(char **ret, const char *fmt, va_list ap) +{ + int i; + + i = vasprintf(ret, fmt, ap); + if (i < 0 || *ret == NULL) + fatal("xvasprintf failed"); + + return (i); +} + +int printflike3 +xsnprintf(char *buf, size_t len, const char *fmt, ...) +{ + va_list ap; + int i; + + va_start(ap, fmt); + i = xvsnprintf(buf, len, fmt, ap); + va_end(ap); + + return (i); +} + +int +xvsnprintf(char *buf, size_t len, const char *fmt, va_list ap) +{ + int i; + + if (len > INT_MAX) + fatalx("len > INT_MAX"); + + i = vsnprintf(buf, len, fmt, ap); + if (i < 0) + fatal("vsnprintf failed"); + + return (i); +} diff --git a/external/bsd/tmux/dist/xterm-keys.c b/external/bsd/tmux/dist/xterm-keys.c new file mode 100644 index 000000000..f42a776f5 --- /dev/null +++ b/external/bsd/tmux/dist/xterm-keys.c @@ -0,0 +1,203 @@ +/* $Id: xterm-keys.c,v 1.1.1.2 2011/08/17 18:40:05 jmmv Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include + +#include "tmux.h" + +/* + * xterm-style function keys append one of the following values before the last + * character: + * + * 2 Shift + * 3 Alt + * 4 Shift + Alt + * 5 Ctrl + * 6 Shift + Ctrl + * 7 Alt + Ctrl + * 8 Shift + Alt + Ctrl + * + * Rather than parsing them, just match against a table. + * + * There are three forms for F1-F4 (\\033O_P and \\033O1;_P and \\033[1;_P). + * We accept any but always output the latter (it comes first in the table). + */ + +int xterm_keys_match(const char *, const char *, size_t); +int xterm_keys_modifiers(const char *, const char *, size_t); + +struct xterm_keys_entry { + int key; + const char *template; +}; + +const struct xterm_keys_entry xterm_keys_table[] = { + { KEYC_F1, "\033[1;_P" }, + { KEYC_F1, "\033O1;_P" }, + { KEYC_F1, "\033O_P" }, + { KEYC_F2, "\033[1;_Q" }, + { KEYC_F2, "\033O1;_Q" }, + { KEYC_F2, "\033O_Q" }, + { KEYC_F3, "\033[1;_R" }, + { KEYC_F3, "\033O1;_R" }, + { KEYC_F3, "\033O_R" }, + { KEYC_F4, "\033[1;_S" }, + { KEYC_F4, "\033O1;_S" }, + { KEYC_F4, "\033O_S" }, + { KEYC_F5, "\033[15;_~" }, + { KEYC_F6, "\033[17;_~" }, + { KEYC_F7, "\033[18;_~" }, + { KEYC_F8, "\033[19;_~" }, + { KEYC_F9, "\033[20;_~" }, + { KEYC_F10, "\033[21;_~" }, + { KEYC_F11, "\033[23;_~" }, + { KEYC_F12, "\033[24;_~" }, + { KEYC_F13, "\033[25;_~" }, + { KEYC_F14, "\033[26;_~" }, + { KEYC_F15, "\033[28;_~" }, + { KEYC_F16, "\033[29;_~" }, + { KEYC_F17, "\033[31;_~" }, + { KEYC_F18, "\033[32;_~" }, + { KEYC_F19, "\033[33;_~" }, + { KEYC_F20, "\033[34;_~" }, + { KEYC_UP, "\033[1;_A" }, + { KEYC_DOWN, "\033[1;_B" }, + { KEYC_RIGHT, "\033[1;_C" }, + { KEYC_LEFT, "\033[1;_D" }, + { KEYC_HOME, "\033[1;_H" }, + { KEYC_END, "\033[1;_F" }, + { KEYC_PPAGE, "\033[5;_~" }, + { KEYC_NPAGE, "\033[6;_~" }, + { KEYC_IC, "\033[2;_~" }, + { KEYC_DC, "\033[3;_~" }, +}; + +/* + * Match key against buffer, treating _ as a wildcard. Return -1 for no match, + * 0 for match, 1 if the end of the buffer is reached (need more data). + */ +int +xterm_keys_match(const char *template, const char *buf, size_t len) +{ + size_t pos; + + if (len == 0) + return (0); + + pos = 0; + do { + if (*template != '_' && buf[pos] != *template) + return (-1); + } while (pos++ != len && *++template != '\0'); + + if (*template != '\0') /* partial */ + return (1); + + return (0); +} + +/* Find modifiers based on template. */ +int +xterm_keys_modifiers(const char *template, const char *buf, size_t len) +{ + size_t idx; + int param, modifiers; + + idx = strcspn(template, "_"); + if (idx >= len) + return (0); + param = buf[idx] - '1'; + + modifiers = 0; + if (param & 1) + modifiers |= KEYC_SHIFT; + if (param & 2) + modifiers |= KEYC_ESCAPE; + if (param & 4) + modifiers |= KEYC_CTRL; + if (param & 8) + modifiers |= KEYC_ESCAPE; + return (modifiers); +} + +/* + * Lookup key from a buffer against the table. Returns 0 for found (and the + * key), -1 for not found, 1 for partial match. + */ +int +xterm_keys_find(const char *buf, size_t len, size_t *size, int *key) +{ + const struct xterm_keys_entry *entry; + u_int i; + + for (i = 0; i < nitems(xterm_keys_table); i++) { + entry = &xterm_keys_table[i]; + switch (xterm_keys_match(entry->template, buf, len)) { + case 0: + *size = strlen(entry->template); + *key = entry->key; + *key |= xterm_keys_modifiers(entry->template, buf, len); + return (0); + case 1: + return (1); + } + } + return (-1); +} + +/* Lookup a key number from the table. */ +char * +xterm_keys_lookup(int key) +{ + const struct xterm_keys_entry *entry; + u_int i; + int modifiers; + char *out; + + modifiers = 1; + if (key & KEYC_SHIFT) + modifiers += 1; + if (key & KEYC_ESCAPE) + modifiers += 2; + if (key & KEYC_CTRL) + modifiers += 4; + + /* + * If the key has no modifiers, return NULL and let it fall through to + * the normal lookup. + */ + if (modifiers == 1) + return (NULL); + + /* Otherwise, find the key in the table. */ + key &= ~(KEYC_SHIFT|KEYC_ESCAPE|KEYC_CTRL); + for (i = 0; i < nitems(xterm_keys_table); i++) { + entry = &xterm_keys_table[i]; + if (key == entry->key) + break; + } + if (i == nitems(xterm_keys_table)) + return (NULL); + + /* Copy the template and replace the modifier. */ + out = xstrdup(entry->template); + out[strcspn(out, "_")] = '0' + modifiers; + return (out); +} diff --git a/external/bsd/tmux/prepare-import.sh b/external/bsd/tmux/prepare-import.sh new file mode 100755 index 000000000..17cf0ff5d --- /dev/null +++ b/external/bsd/tmux/prepare-import.sh @@ -0,0 +1,105 @@ +#! /bin/sh +# $NetBSD: prepare-import.sh,v 1.2 2011/08/17 18:37:59 jmmv Exp $ +# +# Use this script to recreate the 'dist' subdirectory from a newly released +# distfile. The script takes care of unpacking the distfile, removing any +# files that are not relevant to NetBSD and checking if there are any new +# files in the new release that need to be addressed. +# +# See the README file for general instructions. +# + +set -e + +ProgName=${0##*/} + +CLEAN_PATTERNS= +CLEAN_PATTERNS="${CLEAN_PATTERNS} [A-Z]*" +CLEAN_PATTERNS="${CLEAN_PATTERNS} aclocal.m4" +CLEAN_PATTERNS="${CLEAN_PATTERNS} etc" +CLEAN_PATTERNS="${CLEAN_PATTERNS} configure*" + +err() { + echo "${ProgName}:" "${@}" 1>&2 + exit 1 +} + +log() { + echo "${ProgName}:" "${@}" +} + +backup_dist() { + if [ -d dist.old ]; then + log "Removing dist; dist.old exists" + rm -rf dist + else + log "Backing up dist as dist.old" + mv dist dist.old + fi +} + +extract_distfile() { + local distfile="${1}"; shift + local distname="${1}"; shift + + log "Extracting ${distfile}" + tar -xzf "${distfile}" + [ -d "${distname}" ] || err "Distfile did not create ${distname}" + log "Renaming ${distname} to dist" + mv "${distname}" dist +} + +get_distname() { + local distfile="${1}"; shift + basename "${distfile}" | sed -e 's,\.tar.*,,' +} + +cleanup_dist() { + log "Removing unnecessary files from dist" + ( cd dist && rm -rf ${CLEAN_PATTERNS} ) +} + +diff_dirs() { + local old_dir="${1}"; shift + local new_dir="${1}"; shift + + local old_list=$(mktemp -t tmux-import.XXXXXX) + local new_list=$(mktemp -t tmux-import.XXXXXX) + local diff=$(mktemp -t tmux-import.XXXXXX) + trap "rm -f '${old_list}' '${new_list}' '${diff}'; exit 1" \ + HUP INT QUIT TERM + + ( cd "${old_dir}" && find . | sort >>"${old_list}" ) + ( cd "${new_dir}" && find . | sort >>"${new_list}" ) + + diff -u "${old_list}" "${new_list}" | grep '^+\.' >>"${diff}" || true + if [ -s "${diff}" ]; then + log "New files found" + diff -u "${old_list}" "${new_list}" | grep '^+\.' + log "Check if any files have to be cleaned up and update" \ + "the prepare-import.sh script accordingly" + else + log "No new files; all good!" + fi + + rm -f "${old_list}" "${new_list}" "${diff}" +} + +main() { + [ ${#} -eq 1 ] || err "Must provide a distfile name" + local distfile="${1}"; shift + + [ -f Makefile -a -f prepare-import.sh ] || \ + err "Must be run from the src/external/bsd/tmux subdirectory" + + local distname="$(get_distname ${distfile})" + + backup_dist + extract_distfile "${distfile}" "${distname}" + cleanup_dist + diff_dirs dist.old dist + log "Don't forget to update the -D flags in usr.bin/tmux/Makefile" \ + "and to update the version in doc/3RDPARTY" +} + +main "${@}" diff --git a/external/bsd/tmux/share/Makefile b/external/bsd/tmux/share/Makefile new file mode 100644 index 000000000..bff5f2803 --- /dev/null +++ b/external/bsd/tmux/share/Makefile @@ -0,0 +1,5 @@ +# $NetBSD: Makefile,v 1.1 2011/03/10 09:18:00 jmmv Exp $ + +SUBDIR= examples + +.include diff --git a/external/bsd/tmux/share/examples/Makefile b/external/bsd/tmux/share/examples/Makefile new file mode 100644 index 000000000..4ad92662e --- /dev/null +++ b/external/bsd/tmux/share/examples/Makefile @@ -0,0 +1,5 @@ +# $NetBSD: Makefile,v 1.1 2011/03/10 09:18:00 jmmv Exp $ + +SUBDIR= tmux + +.include diff --git a/external/bsd/tmux/share/examples/tmux/Makefile b/external/bsd/tmux/share/examples/tmux/Makefile new file mode 100644 index 000000000..2176cb974 --- /dev/null +++ b/external/bsd/tmux/share/examples/tmux/Makefile @@ -0,0 +1,14 @@ +# $NetBSD: Makefile,v 1.1 2011/03/10 09:18:00 jmmv Exp $ + +.include + +.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 diff --git a/external/bsd/tmux/usr.bin/Makefile b/external/bsd/tmux/usr.bin/Makefile new file mode 100644 index 000000000..4ad92662e --- /dev/null +++ b/external/bsd/tmux/usr.bin/Makefile @@ -0,0 +1,5 @@ +# $NetBSD: Makefile,v 1.1 2011/03/10 09:18:00 jmmv Exp $ + +SUBDIR= tmux + +.include diff --git a/external/bsd/tmux/usr.bin/tmux/Makefile b/external/bsd/tmux/usr.bin/tmux/Makefile new file mode 100644 index 000000000..d73f0d9a7 --- /dev/null +++ b/external/bsd/tmux/usr.bin/tmux/Makefile @@ -0,0 +1,221 @@ +# $NetBSD: Makefile,v 1.10 2013/11/26 01:27:20 christos Exp $ + +.include + +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