tmux-3.0a/compat/asprintf.c100644 001750 001750 00000002661 13537640375 0011513/* * 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. */ #include #include #include #include #include #include "compat.h" #include "xmalloc.h" int asprintf(char **ret, const char *fmt, ...) { va_list ap; int n; va_start(ap, fmt); n = vasprintf(ret, fmt, ap); va_end(ap); return (n); } int vasprintf(char **ret, const char *fmt, va_list ap) { int n; va_list ap2; va_copy(ap2, ap); if ((n = vsnprintf(NULL, 0, fmt, ap)) < 0) goto error; *ret = xmalloc(n + 1); if ((n = vsnprintf(*ret, n + 1, fmt, ap2)) < 0) { free(*ret); goto error; } va_end(ap2); return (n); error: va_end(ap2); *ret = NULL; return (-1); } tmux-3.0a/compat/base64.c100644 001750 001750 00000024247 13466230030 0010735/* $OpenBSD: base64.c,v 1.8 2015/01/16 16:48:51 deraadt Exp $ */ /* * Copyright (c) 1996 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM 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. */ /* * Portions Copyright (c) 1995 by International Business Machines, Inc. * * International Business Machines, Inc. (hereinafter called IBM) grants * permission under its copyrights to use, copy, modify, and distribute this * Software with or without fee, provided that the above copyright notice and * all paragraphs of this notice appear in all copies, and that the name of IBM * not be used in connection with the marketing of any product incorporating * the Software or modifications thereof, without specific, written prior * permission. * * To the extent it has a right to do so, IBM grants an immunity from suit * under its patents, if any, for the use, sale or manufacture of products to * the extent that such products are used for performing Domain Name System * dynamic updates in TCP/IP networks by means of the Software. No immunity is * granted for any product per se or for any other function of any product. * * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. */ #include #include #include #include #include #include #include #include #include #include static const char Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static const char Pad64 = '='; /* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) The following encoding technique is taken from RFC 1521 by Borenstein and Freed. It is reproduced here in a slightly edited form for convenience. A 65-character subset of US-ASCII is used, enabling 6 bits to be represented per printable character. (The extra 65th character, "=", is used to signify a special processing function.) The encoding process represents 24-bit groups of input bits as output strings of 4 encoded characters. Proceeding from left to right, a 24-bit input group is formed by concatenating 3 8-bit input groups. These 24 bits are then treated as 4 concatenated 6-bit groups, each of which is translated into a single digit in the base64 alphabet. Each 6-bit group is used as an index into an array of 64 printable characters. The character referenced by the index is placed in the output string. Table 1: The Base64 Alphabet Value Encoding Value Encoding Value Encoding Value Encoding 0 A 17 R 34 i 51 z 1 B 18 S 35 j 52 0 2 C 19 T 36 k 53 1 3 D 20 U 37 l 54 2 4 E 21 V 38 m 55 3 5 F 22 W 39 n 56 4 6 G 23 X 40 o 57 5 7 H 24 Y 41 p 58 6 8 I 25 Z 42 q 59 7 9 J 26 a 43 r 60 8 10 K 27 b 44 s 61 9 11 L 28 c 45 t 62 + 12 M 29 d 46 u 63 / 13 N 30 e 47 v 14 O 31 f 48 w (pad) = 15 P 32 g 49 x 16 Q 33 h 50 y Special processing is performed if fewer than 24 bits are available at the end of the data being encoded. A full encoding quantum is always completed at the end of a quantity. When fewer than 24 input bits are available in an input group, zero bits are added (on the right) to form an integral number of 6-bit groups. Padding at the end of the data is performed using the '=' character. Since all base64 input is an integral number of octets, only the ------------------------------------------------- following cases can arise: (1) the final quantum of encoding input is an integral multiple of 24 bits; here, the final unit of encoded output will be an integral multiple of 4 characters with no "=" padding, (2) the final quantum of encoding input is exactly 8 bits; here, the final unit of encoded output will be two characters followed by two "=" padding characters, or (3) the final quantum of encoding input is exactly 16 bits; here, the final unit of encoded output will be three characters followed by one "=" padding character. */ int b64_ntop(src, srclength, target, targsize) u_char const *src; size_t srclength; char *target; size_t targsize; { size_t datalength = 0; u_char input[3]; u_char output[4]; int i; while (2 < srclength) { input[0] = *src++; input[1] = *src++; input[2] = *src++; srclength -= 3; output[0] = input[0] >> 2; output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); output[3] = input[2] & 0x3f; if (datalength + 4 > targsize) return (-1); target[datalength++] = Base64[output[0]]; target[datalength++] = Base64[output[1]]; target[datalength++] = Base64[output[2]]; target[datalength++] = Base64[output[3]]; } /* Now we worry about padding. */ if (0 != srclength) { /* Get what's left. */ input[0] = input[1] = input[2] = '\0'; for (i = 0; i < srclength; i++) input[i] = *src++; output[0] = input[0] >> 2; output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); if (datalength + 4 > targsize) return (-1); target[datalength++] = Base64[output[0]]; target[datalength++] = Base64[output[1]]; if (srclength == 1) target[datalength++] = Pad64; else target[datalength++] = Base64[output[2]]; target[datalength++] = Pad64; } if (datalength >= targsize) return (-1); target[datalength] = '\0'; /* Returned value doesn't count \0. */ return (datalength); } /* skips all whitespace anywhere. converts characters, four at a time, starting at (or after) src from base - 64 numbers into three 8 bit bytes in the target area. it returns the number of data bytes stored at the target, or -1 on error. */ int b64_pton(src, target, targsize) char const *src; u_char *target; size_t targsize; { int tarindex, state, ch; u_char nextbyte; char *pos; state = 0; tarindex = 0; while ((ch = (unsigned char)*src++) != '\0') { if (isspace(ch)) /* Skip whitespace anywhere. */ continue; if (ch == Pad64) break; pos = strchr(Base64, ch); if (pos == 0) /* A non-base64 character. */ return (-1); switch (state) { case 0: if (target) { if (tarindex >= targsize) return (-1); target[tarindex] = (pos - Base64) << 2; } state = 1; break; case 1: if (target) { if (tarindex >= targsize) return (-1); target[tarindex] |= (pos - Base64) >> 4; nextbyte = ((pos - Base64) & 0x0f) << 4; if (tarindex + 1 < targsize) target[tarindex+1] = nextbyte; else if (nextbyte) return (-1); } tarindex++; state = 2; break; case 2: if (target) { if (tarindex >= targsize) return (-1); target[tarindex] |= (pos - Base64) >> 2; nextbyte = ((pos - Base64) & 0x03) << 6; if (tarindex + 1 < targsize) target[tarindex+1] = nextbyte; else if (nextbyte) return (-1); } tarindex++; state = 3; break; case 3: if (target) { if (tarindex >= targsize) return (-1); target[tarindex] |= (pos - Base64); } tarindex++; state = 0; break; } } /* * We are done decoding Base-64 chars. Let's see if we ended * on a byte boundary, and/or with erroneous trailing characters. */ if (ch == Pad64) { /* We got a pad char. */ ch = (unsigned char)*src++; /* Skip it, get next. */ switch (state) { case 0: /* Invalid = in first position */ case 1: /* Invalid = in second position */ return (-1); case 2: /* Valid, means one byte of info */ /* Skip any number of spaces. */ for (; ch != '\0'; ch = (unsigned char)*src++) if (!isspace(ch)) break; /* Make sure there is another trailing = sign. */ if (ch != Pad64) return (-1); ch = (unsigned char)*src++; /* Skip the = */ /* Fall through to "single trailing =" case. */ /* FALLTHROUGH */ case 3: /* Valid, means two bytes of info */ /* * We know this char is an =. Is there anything but * whitespace after it? */ for (; ch != '\0'; ch = (unsigned char)*src++) if (!isspace(ch)) return (-1); /* * Now make sure for cases 2 and 3 that the "extra" * bits that slopped past the last full byte were * zeros. If we don't check them, they become a * subliminal channel. */ if (target && tarindex < targsize && target[tarindex] != 0) return (-1); } } else { /* * We ended by seeing the end of the string. Make sure we * have no partial bytes lying around. */ if (state != 0) return (-1); } return (tarindex); } tmux-3.0a/compat/cfmakeraw.c100644 001750 001750 00000002242 13466230030 0011600/* * Copyright (c) 2013 Dagobert Michelsen * Copyright (c) 2013 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 "compat.h" void cfmakeraw(struct termios *tio) { tio->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); tio->c_oflag &= ~OPOST; tio->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); tio->c_cflag &= ~(CSIZE|PARENB); tio->c_cflag |= CS8; } tmux-3.0a/compat/closefrom.c100644 001750 001750 00000005530 13466230030 0011634/* * Copyright (c) 2004-2005 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. 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. */ #ifndef HAVE_CLOSEFROM #include #include #include #include #ifdef HAVE_FCNTL_H # include #endif #include #include #include #include #include #ifdef HAVE_DIRENT_H # include # define NAMLEN(dirent) strlen((dirent)->d_name) #else # define dirent direct # define NAMLEN(dirent) (dirent)->d_namlen # ifdef HAVE_SYS_NDIR_H # include # endif # ifdef HAVE_SYS_DIR_H # include # endif # ifdef HAVE_NDIR_H # include # endif #endif #include "compat.h" #ifndef OPEN_MAX # define OPEN_MAX 256 #endif #if 0 __unused static const char rcsid[] = "$Sudo: closefrom.c,v 1.11 2006/08/17 15:26:54 millert Exp $"; #endif /* lint */ /* * Close all file descriptors greater than or equal to lowfd. */ #ifdef HAVE_FCNTL_CLOSEM void closefrom(int lowfd) { (void) fcntl(lowfd, F_CLOSEM, 0); } #else void closefrom(int lowfd) { long fd, maxfd; #if defined(HAVE_DIRFD) && defined(HAVE_PROC_PID) char fdpath[PATH_MAX], *endp; struct dirent *dent; DIR *dirp; int len; /* Check for a /proc/$$/fd directory. */ len = snprintf(fdpath, sizeof(fdpath), "/proc/%ld/fd", (long)getpid()); if (len > 0 && (size_t)len <= sizeof(fdpath) && (dirp = opendir(fdpath))) { while ((dent = readdir(dirp)) != NULL) { fd = strtol(dent->d_name, &endp, 10); if (dent->d_name != endp && *endp == '\0' && fd >= 0 && fd < INT_MAX && fd >= lowfd && fd != dirfd(dirp)) (void) close((int) fd); } (void) closedir(dirp); } else #endif { /* * Fall back on sysconf() or getdtablesize(). We avoid checking * resource limits since it is possible to open a file descriptor * and then drop the rlimit such that it is below the open fd. */ #ifdef HAVE_SYSCONF maxfd = sysconf(_SC_OPEN_MAX); #else maxfd = getdtablesize(); #endif /* HAVE_SYSCONF */ if (maxfd < 0) maxfd = OPEN_MAX; for (fd = lowfd; fd < maxfd; fd++) (void) close((int) fd); } } #endif /* !HAVE_FCNTL_CLOSEM */ #endif /* HAVE_CLOSEFROM */ tmux-3.0a/compat/daemon-darwin.c100644 001750 001750 00000005767 13466230030 0012404/* * Copyright (c) 2017 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. */ /* * Copyright (c) 2011-2013, Chris Johnsen * All rights reserved. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 * COPYRIGHT HOLDER 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 #include #include #include void daemon_darwin(void); #ifdef __MAC_10_10 extern kern_return_t bootstrap_look_up_per_user(mach_port_t, const char *, uid_t, mach_port_t *); extern kern_return_t bootstrap_get_root(mach_port_t, mach_port_t *); void daemon_darwin(void) { mach_port_t root = MACH_PORT_NULL; mach_port_t s = MACH_PORT_NULL; uid_t uid; uid = getuid(); if (bootstrap_get_root(bootstrap_port, &root) == KERN_SUCCESS && bootstrap_look_up_per_user(root, NULL, uid, &s) == KERN_SUCCESS && task_set_bootstrap_port(mach_task_self(), s) == KERN_SUCCESS && mach_port_deallocate(mach_task_self(), bootstrap_port) == KERN_SUCCESS) bootstrap_port = s; } #else void daemon_darwin(void) { } #endif tmux-3.0a/compat/daemon.c100644 001750 001750 00000004362 13466230030 0011110/* $OpenBSD: daemon.c,v 1.6 2005/08/08 08:05:33 espie Exp $ */ /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * 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. Neither the name of the University 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 REGENTS 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 REGENTS 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 #include #include #include #include "compat.h" #ifdef __APPLE__ extern void daemon_darwin(void); #endif int daemon(int nochdir, int noclose) { int fd; switch (fork()) { case -1: return (-1); case 0: break; default: _exit(0); } if (setsid() == -1) return (-1); if (!nochdir) (void)chdir("/"); if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { (void)dup2(fd, STDIN_FILENO); (void)dup2(fd, STDOUT_FILENO); (void)dup2(fd, STDERR_FILENO); if (fd > 2) (void)close (fd); } #ifdef __APPLE__ daemon_darwin(); #endif return (0); } tmux-3.0a/compat/err.c100644 001750 001750 00000003644 13466230030 0010437/* * Copyright (c) 2017 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 "compat.h" void err(int eval, const char *fmt, ...) { va_list ap; int saved_errno = errno; fprintf(stderr, "%s: ", getprogname()); va_start(ap, fmt); if (fmt != NULL) { vfprintf(stderr, fmt, ap); fprintf(stderr, ": "); } va_end(ap); fprintf(stderr, "%s\n", strerror(saved_errno)); exit(eval); } void errx(int eval, const char *fmt, ...) { va_list ap; fprintf(stderr, "%s: ", getprogname()); va_start(ap, fmt); if (fmt != NULL) vfprintf(stderr, fmt, ap); va_end(ap); putc('\n', stderr); exit(eval); } void warn(const char *fmt, ...) { va_list ap; int saved_errno = errno; fprintf(stderr, "%s: ", getprogname()); va_start(ap, fmt); if (fmt != NULL) { vfprintf(stderr, fmt, ap); fprintf(stderr, ": "); } va_end(ap); fprintf(stderr, "%s\n", strerror(saved_errno)); } void warnx(const char *fmt, ...) { va_list ap; fprintf(stderr, "%s: ", getprogname()); va_start(ap, fmt); if (fmt != NULL) vfprintf(stderr, fmt, ap); va_end(ap); putc('\n', stderr); } tmux-3.0a/compat/explicit_bzero.c100644 001750 001750 00000000362 13466230030 0012663/* $OpenBSD: explicit_bzero.c,v 1.4 2015/08/31 02:53:57 guenther Exp $ */ /* * Public domain. * Written by Matthew Dempsky. */ #include #include "compat.h" void explicit_bzero(void *buf, size_t len) { memset(buf, 0, len); } tmux-3.0a/compat/fdforkpty.c100644 001750 001750 00000002062 13466230030 0011650/* * Copyright (c) 2017 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 "compat.h" int getptmfd(void) { return (INT_MAX); } pid_t fdforkpty(__unused int ptmfd, int *master, char *name, struct termios *tio, struct winsize *ws) { return (forkpty(master, name, tio, ws)); } tmux-3.0a/compat/fgetln.c100644 001750 001750 00000002731 13466230030 0011122/* * Copyright (c) 2015 Joerg Jung * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. 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. */ /* * portable fgetln() version, NOT reentrant */ #include #include #include #include "compat.h" char * fgetln(FILE *fp, size_t *len) { static char *buf = NULL; static size_t bufsz = 0; size_t r = 0; char *p; int c, e; if (!fp || !len) { errno = EINVAL; return NULL; } if (!buf) { if (!(buf = calloc(1, BUFSIZ))) return NULL; bufsz = BUFSIZ; } while ((c = getc(fp)) != EOF) { buf[r++] = c; if (r == bufsz) { if (!(p = reallocarray(buf, 2, bufsz))) { e = errno; free(buf); errno = e; buf = NULL, bufsz = 0; return NULL; } buf = p, bufsz = 2 * bufsz; } if (c == '\n') break; } return (*len = r) ? buf : NULL; } tmux-3.0a/compat/freezero.c100644 001750 001750 00000001751 13466230030 0011465/* * Copyright (c) 2017 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 "compat.h" void freezero(void *ptr, size_t size) { if (ptr != NULL) { memset(ptr, 0, size); free(ptr); } } tmux-3.0a/compat/getdtablecount.c100644 001750 001750 00000002417 13466230030 0012650/* * Copyright (c) 2017 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 "compat.h" void fatal(const char *, ...); void fatalx(const char *, ...); #ifdef HAVE_PROC_PID int getdtablecount(void) { char path[PATH_MAX]; glob_t g; int n = 0; if (snprintf(path, sizeof path, "/proc/%ld/fd/*", (long)getpid()) < 0) fatal("snprintf overflow"); if (glob(path, 0, NULL, &g) == 0) n = g.gl_pathc; globfree(&g); return (n); } #else int getdtablecount(void) { return (0); } #endif tmux-3.0a/compat/getopt.c100644 001750 001750 00000007161 13466230030 0011147/* * Copyright (c) 1987, 1993, 1994 * The Regents of the University of California. All rights reserved. * * 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. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ /* OPENBSD ORIGINAL: lib/libc/stdlib/getopt.c */ #include "compat.h" #include #include #include int BSDopterr = 1, /* if error message should be printed */ BSDoptind = 1, /* index into parent argv vector */ BSDoptopt, /* character checked for validity */ BSDoptreset; /* reset getopt */ char *BSDoptarg; /* argument associated with option */ #define BADCH (int)'?' #define BADARG (int)':' #define EMSG "" /* * getopt -- * Parse argc/argv argument vector. */ int BSDgetopt(int nargc, char *const *nargv, const char *ostr) { static const char *place = EMSG; /* option letter processing */ char *oli; /* option letter list index */ if (ostr == NULL) return (-1); if (BSDoptreset || !*place) { /* update scanning pointer */ BSDoptreset = 0; if (BSDoptind >= nargc || *(place = nargv[BSDoptind]) != '-') { place = EMSG; return (-1); } if (place[1] && *++place == '-') { /* found "--" */ if (place[1]) return (BADCH); ++BSDoptind; place = EMSG; return (-1); } } /* option letter okay? */ if ((BSDoptopt = (int)*place++) == (int)':' || !(oli = strchr(ostr, BSDoptopt))) { /* * if the user didn't specify '-' as an option, * assume it means -1. */ if (BSDoptopt == (int)'-') return (-1); if (!*place) ++BSDoptind; if (BSDopterr && *ostr != ':') (void)fprintf(stderr, "%s: unknown option -- %c\n", getprogname(), BSDoptopt); return (BADCH); } if (*++oli != ':') { /* don't need argument */ BSDoptarg = NULL; if (!*place) ++BSDoptind; } else { /* need an argument */ if (*place) /* no white space */ BSDoptarg = (char *)place; else if (nargc <= ++BSDoptind) { /* no arg */ place = EMSG; if (*ostr == ':') return (BADARG); if (BSDopterr) (void)fprintf(stderr, "%s: option requires an argument -- %c\n", getprogname(), BSDoptopt); return (BADCH); } else /* white space */ BSDoptarg = nargv[BSDoptind]; place = EMSG; ++BSDoptind; } return (BSDoptopt); /* dump back option letter */ } tmux-3.0a/compat/getprogname.c100644 001750 001750 00000002237 13466230030 0012154/* * Copyright (c) 2016 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 "compat.h" #if defined(HAVE_PROGRAM_INVOCATION_SHORT_NAME) const char * getprogname(void) { return (program_invocation_short_name); } #elif defined(HAVE___PROGNAME) const char * getprogname(void) { extern char *__progname; return (__progname); } #else const char * getprogname(void) { return ("tmux"); } #endif tmux-3.0a/compat/imsg-buffer.c100644 001750 001750 00000013443 13466230030 0012053/* $OpenBSD: imsg-buffer.c,v 1.11 2017/12/14 09:27:44 kettenis 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 #include "compat.h" #include "imsg.h" static int ibuf_realloc(struct ibuf *, size_t); static void ibuf_enqueue(struct msgbuf *, struct ibuf *); static 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); } static 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 = ERANGE; return (-1); } b = recallocarray(buf->buf, buf->size, buf->wpos + len, 1); 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; memset(&iov, 0, 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++; } again: if ((n = writev(msgbuf->fd, iov, i)) == -1) { if (errno == EINTR) goto again; if (errno == ENOBUFS) errno = EAGAIN; return (-1); } if (n == 0) { /* connection closed */ errno = 0; return (0); } msgbuf_drain(msgbuf, n); return (1); } void ibuf_free(struct ibuf *buf) { if (buf == NULL) return; freezero(buf->buf, buf->size); 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; memset(&iov, 0, sizeof(iov)); memset(&msg, 0, sizeof(msg)); memset(&cmsgbuf, 0, sizeof(cmsgbuf)); 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 = sizeof(cmsgbuf.buf); 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; } again: if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) { if (errno == EINTR) goto again; if (errno == ENOBUFS) errno = EAGAIN; return (-1); } if (n == 0) { /* connection closed */ errno = 0; return (0); } /* * 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 (1); } static void ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf) { TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry); msgbuf->queued++; } static 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); } tmux-3.0a/compat/imsg.c100644 001750 001750 00000014253 13466230030 0010604/* $OpenBSD: imsg.c,v 1.16 2017/12/14 09:27:44 kettenis 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 "compat.h" #include "imsg.h" int imsg_fd_overhead = 0; static int imsg_get_fd(struct imsgbuf *); void imsg_init(struct imsgbuf *ibuf, int fd) { msgbuf_init(&ibuf->w); memset(&ibuf->r, 0, 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) * 1)]; } cmsgbuf; struct iovec iov; ssize_t n = -1; int fd; struct imsg_fd *ifd; memset(&msg, 0, sizeof(msg)); memset(&cmsgbuf, 0, sizeof(cmsgbuf)); 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 = sizeof(cmsgbuf.buf); if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL) return (-1); again: if (getdtablecount() + imsg_fd_overhead + (int)((CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int)) >= getdtablesize()) { errno = EAGAIN; free(ifd); return (-1); } if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) { if (errno == EINTR) goto again; goto fail; } 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) { int i; int j; /* * We only accept one file descriptor. Due to C * padding rules, our control buffer might contain * more than one fd, and we must close them. */ j = ((char *)cmsg + cmsg->cmsg_len - (char *)CMSG_DATA(cmsg)) / sizeof(int); for (i = 0; i < j; i++) { fd = ((int *)CMSG_DATA(cmsg))[i]; if (ifd != NULL) { ifd->fd = fd; TAILQ_INSERT_TAIL(&ibuf->fds, ifd, entry); ifd = NULL; } else close(fd); } } /* we do not handle other ctl data level */ } fail: free(ifd); 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 (datalen == 0) imsg->data = NULL; else if ((imsg->data = malloc(datalen)) == NULL) 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, uint32_t type, uint32_t peerid, pid_t pid, int fd, const void *data, uint16_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, uint32_t type, uint32_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, uint32_t type, uint32_t peerid, pid_t pid, uint16_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, const void *data, uint16_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 = (uint16_t)msg->wpos; ibuf_close(&ibuf->w, msg); } void imsg_free(struct imsg *imsg) { freezero(imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE); } static 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); } tmux-3.0a/compat/memmem.c100644 001750 001750 00000004427 13466230030 0011124/* $OpenBSD: memmem.c,v 1.4 2015/08/31 02:53:57 guenther Exp $ */ /*- * Copyright (c) 2005 Pascal Gloor * * 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. The name of the author may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 #include "compat.h" /* * Find the first occurrence of the byte string s in byte string l. */ void * memmem(const void *l, size_t l_len, const void *s, size_t s_len) { const char *cur, *last; const char *cl = l; const char *cs = s; /* a zero length needle should just return the haystack */ if (s_len == 0) return (void *)cl; /* "s" must be smaller or equal to "l" */ if (l_len < s_len) return NULL; /* special case where s_len == 1 */ if (s_len == 1) return memchr(l, *cs, l_len); /* the last position where its possible to find "s" in "l" */ last = cl + l_len - s_len; for (cur = cl; cur <= last; cur++) if (cur[0] == cs[0] && memcmp(cur, cs, s_len) == 0) return (void *)cur; return NULL; } tmux-3.0a/compat/reallocarray.c100644 001750 001750 00000002550 13466230030 0012322/* $OpenBSD: reallocarray.c,v 1.3 2015/09/13 08:31:47 guenther Exp $ */ /* * Copyright (c) 2008 Otto Moerbeek * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. 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 "compat.h" /* * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW */ #define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) void * reallocarray(void *optr, size_t nmemb, size_t size) { if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && nmemb > 0 && SIZE_MAX / nmemb < size) { errno = ENOMEM; return NULL; } return realloc(optr, size * nmemb); } tmux-3.0a/compat/recallocarray.c100644 001750 001750 00000004351 13466230030 0012466/* $OpenBSD: recallocarray.c,v 1.1 2017/03/06 18:44:21 otto Exp $ */ /* * Copyright (c) 2008, 2017 Otto Moerbeek * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. 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 "compat.h" /* * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW */ #define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) void * recallocarray(void *ptr, size_t oldnmemb, size_t newnmemb, size_t size) { size_t oldsize, newsize; void *newptr; if (ptr == NULL) return calloc(newnmemb, size); if ((newnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && newnmemb > 0 && SIZE_MAX / newnmemb < size) { errno = ENOMEM; return NULL; } newsize = newnmemb * size; if ((oldnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && oldnmemb > 0 && SIZE_MAX / oldnmemb < size) { errno = EINVAL; return NULL; } oldsize = oldnmemb * size; /* * Don't bother too much if we're shrinking just a bit, * we do not shrink for series of small steps, oh well. */ if (newsize <= oldsize) { size_t d = oldsize - newsize; if (d < oldsize / 2 && d < (size_t)getpagesize()) { memset((char *)ptr + newsize, 0, d); return ptr; } } newptr = malloc(newsize); if (newptr == NULL) return NULL; if (newsize > oldsize) { memcpy(newptr, ptr, oldsize); memset((char *)newptr + oldsize, 0, newsize - oldsize); } else memcpy(newptr, ptr, newsize); explicit_bzero(ptr, oldsize); free(ptr); return newptr; } tmux-3.0a/compat/setenv.c100644 001750 001750 00000002646 13466230030 0011154/* * Copyright (c) 2010 Dagobert Michelsen * 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 "compat.h" int setenv(const char *name, const char *value, __unused int overwrite) { char *newval; xasprintf(&newval, "%s=%s", name, value); return (putenv(newval)); } int unsetenv(const char *name) { char **envptr; int namelen; namelen = strlen(name); for (envptr = environ; *envptr != NULL; envptr++) { if (strncmp(name, *envptr, namelen) == 0 && ((*envptr)[namelen] == '=' || (*envptr)[namelen] == '\0')) break; } for (; *envptr != NULL; envptr++) *envptr = *(envptr + 1); return (0); } tmux-3.0a/compat/setproctitle.c100644 001750 001750 00000002604 13466230030 0012363/* * Copyright (c) 2016 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 "compat.h" #if defined(HAVE_PRCTL) && defined(HAVE_PR_SET_NAME) #include void setproctitle(const char *fmt, ...) { char title[16], name[16], *cp; va_list ap; int used; va_start(ap, fmt); vsnprintf(title, sizeof title, fmt, ap); va_end(ap); used = snprintf(name, sizeof name, "%s: %s", getprogname(), title); if (used >= (int)sizeof name) { cp = strrchr(name, ' '); if (cp != NULL) *cp = '\0'; } prctl(PR_SET_NAME, name); } #else void setproctitle(__unused const char *fmt, ...) { } #endif tmux-3.0a/compat/strcasestr.c100644 001750 001750 00000004373 13466230030 0012044/* $OpenBSD: strcasestr.c,v 1.3 2006/03/31 05:34:55 deraadt Exp $ */ /* $NetBSD: strcasestr.c,v 1.2 2005/02/09 21:35:47 kleink Exp $ */ /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Chris Torek. * * 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. Neither the name of the University 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 REGENTS 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 REGENTS 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 #include #include "compat.h" /* * Find the first occurrence of find in s, ignore case. */ char * strcasestr(const char *s, const char *find) { char c, sc; size_t len; if ((c = *find++) != 0) { c = (char)tolower((unsigned char)c); len = strlen(find); do { do { if ((sc = *s++) == 0) return (NULL); } while ((char)tolower((unsigned char)sc) != c); } while (strncasecmp(s, find, len) != 0); s--; } return ((char *)s); } tmux-3.0a/compat/strlcat.c100644 001750 001750 00000003250 13466230030 0011314/* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */ /* * Copyright (c) 1998 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. 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 "compat.h" /* * Appends src to string dst of size siz (unlike strncat, siz is the * full size of dst, not space left). At most siz-1 characters * will be copied. Always NUL terminates (unless siz <= strlen(dst)). * Returns strlen(src) + MIN(siz, strlen(initial dst)). * If retval >= siz, truncation occurred. */ size_t strlcat(char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; size_t dlen; /* Find the end of dst and adjust bytes left but don't go past end */ while (n-- != 0 && *d != '\0') d++; dlen = d - dst; n = siz - dlen; if (n == 0) return(dlen + strlen(s)); while (*s != '\0') { if (n != 1) { *d++ = *s; n--; } s++; } *d = '\0'; return(dlen + (s - src)); /* count does not include NUL */ } tmux-3.0a/compat/strlcpy.c100644 001750 001750 00000003071 13466230030 0011341/* $OpenBSD: strlcpy.c,v 1.10 2005/08/08 08:05:37 espie Exp $ */ /* * Copyright (c) 1998 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. 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 "compat.h" /* * Copy src to string dst of size siz. At most siz-1 characters * will be copied. Always NUL terminates (unless siz == 0). * Returns strlen(src); if retval >= siz, truncation occurred. */ size_t strlcpy(char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; /* Copy as many bytes as will fit */ if (n != 0 && --n != 0) { do { if ((*d++ = *s++) == 0) break; } while (--n != 0); } /* Not enough room in dst, add NUL and traverse rest of src */ if (n == 0) { if (siz != 0) *d = '\0'; /* NUL-terminate dst */ while (*s++) ; } return(s - src - 1); /* count does not include NUL */ } tmux-3.0a/compat/strndup.c100644 001750 001750 00000002257 13466230030 0011345/* $OpenBSD: strndup.c,v 1.2 2015/08/31 02:53:57 guenther Exp $ */ /* * Copyright (c) 2010 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. 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 "compat.h" char * strndup(const char *str, size_t maxlen) { char *copy; size_t len; len = strnlen(str, maxlen); copy = malloc(len + 1); if (copy != NULL) { (void)memcpy(copy, str, len); copy[len] = '\0'; } return copy; } tmux-3.0a/compat/strnlen.c100644 001750 001750 00000002111 13466230030 0011320/* $OpenBSD: strnlen.c,v 1.8 2016/10/16 17:37:39 dtucker Exp $ */ /* * Copyright (c) 2010 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. 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 "compat.h" size_t strnlen(const char *str, size_t maxlen) { const char *cp; for (cp = str; maxlen != 0 && *cp != '\0'; cp++, maxlen--) ; return (size_t)(cp - str); } tmux-3.0a/compat/strsep.c100644 001750 001750 00000004750 13466230030 0011166/* $OpenBSD: strsep.c,v 1.6 2005/08/08 08:05:37 espie Exp $ */ /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * 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. Neither the name of the University 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 REGENTS 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 REGENTS 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 #include #include "compat.h" /* * Get next token from string *stringp, where tokens are possibly-empty * strings separated by characters from delim. * * Writes NULs into the string at *stringp to end tokens. * delim need not remain constant from call to call. * On return, *stringp points past the last NUL written (if there might * be further tokens), or is NULL (if there are definitely no more tokens). * * If *stringp is NULL, strsep returns NULL. */ char * strsep(char **stringp, const char *delim) { char *s; const char *spanp; int c, sc; char *tok; if ((s = *stringp) == NULL) return (NULL); for (tok = s;;) { c = *s++; spanp = delim; do { if ((sc = *spanp++) == c) { if (c == 0) s = NULL; else s[-1] = 0; *stringp = s; return (tok); } } while (sc != 0); } /* NOTREACHED */ } tmux-3.0a/compat/strtonum.c100644 001750 001750 00000003376 13466230030 0011544/* $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 "compat.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); } tmux-3.0a/compat/unvis.c100644 001750 001750 00000014025 13466230030 0011006/* $OpenBSD: unvis.c,v 1.12 2005/08/08 08:05:34 espie Exp $ */ /*- * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * 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. Neither the name of the University 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 REGENTS 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 REGENTS 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 #include #include "compat.h" /* * decode driven by state machine */ #define S_GROUND 0 /* haven't seen escape char */ #define S_START 1 /* start decoding special sequence */ #define S_META 2 /* metachar started (M) */ #define S_META1 3 /* metachar more, regular char (-) */ #define S_CTRL 4 /* control char started (^) */ #define S_OCTAL2 5 /* octal digit 2 */ #define S_OCTAL3 6 /* octal digit 3 */ #define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7') /* * unvis - decode characters previously encoded by vis */ int unvis(char *cp, char c, int *astate, int flag) { if (flag & UNVIS_END) { if (*astate == S_OCTAL2 || *astate == S_OCTAL3) { *astate = S_GROUND; return (UNVIS_VALID); } return (*astate == S_GROUND ? UNVIS_NOCHAR : UNVIS_SYNBAD); } switch (*astate) { case S_GROUND: *cp = 0; if (c == '\\') { *astate = S_START; return (0); } *cp = c; return (UNVIS_VALID); case S_START: switch(c) { case '\\': *cp = c; *astate = S_GROUND; return (UNVIS_VALID); case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': *cp = (c - '0'); *astate = S_OCTAL2; return (0); case 'M': *cp = (char) 0200; *astate = S_META; return (0); case '^': *astate = S_CTRL; return (0); case 'n': *cp = '\n'; *astate = S_GROUND; return (UNVIS_VALID); case 'r': *cp = '\r'; *astate = S_GROUND; return (UNVIS_VALID); case 'b': *cp = '\b'; *astate = S_GROUND; return (UNVIS_VALID); case 'a': *cp = '\007'; *astate = S_GROUND; return (UNVIS_VALID); case 'v': *cp = '\v'; *astate = S_GROUND; return (UNVIS_VALID); case 't': *cp = '\t'; *astate = S_GROUND; return (UNVIS_VALID); case 'f': *cp = '\f'; *astate = S_GROUND; return (UNVIS_VALID); case 's': *cp = ' '; *astate = S_GROUND; return (UNVIS_VALID); case 'E': *cp = '\033'; *astate = S_GROUND; return (UNVIS_VALID); case '\n': /* * hidden newline */ *astate = S_GROUND; return (UNVIS_NOCHAR); case '$': /* * hidden marker */ *astate = S_GROUND; return (UNVIS_NOCHAR); } *astate = S_GROUND; return (UNVIS_SYNBAD); case S_META: if (c == '-') *astate = S_META1; else if (c == '^') *astate = S_CTRL; else { *astate = S_GROUND; return (UNVIS_SYNBAD); } return (0); case S_META1: *astate = S_GROUND; *cp |= c; return (UNVIS_VALID); case S_CTRL: if (c == '?') *cp |= 0177; else *cp |= c & 037; *astate = S_GROUND; return (UNVIS_VALID); case S_OCTAL2: /* second possible octal digit */ if (isoctal(c)) { /* * yes - and maybe a third */ *cp = (*cp << 3) + (c - '0'); *astate = S_OCTAL3; return (0); } /* * no - done with current sequence, push back passed char */ *astate = S_GROUND; return (UNVIS_VALIDPUSH); case S_OCTAL3: /* third possible octal digit */ *astate = S_GROUND; if (isoctal(c)) { *cp = (*cp << 3) + (c - '0'); return (UNVIS_VALID); } /* * we were done, push back passed char */ return (UNVIS_VALIDPUSH); default: /* * decoder in unknown state - (probably uninitialized) */ *astate = S_GROUND; return (UNVIS_SYNBAD); } } /* * strunvis - decode src into dst * * Number of chars decoded into dst is returned, -1 on error. * Dst is null terminated. */ int strunvis(char *dst, const char *src) { char c; char *start = dst; int state = 0; while ((c = *src++)) { again: switch (unvis(dst, c, &state, 0)) { case UNVIS_VALID: dst++; break; case UNVIS_VALIDPUSH: dst++; goto again; case 0: case UNVIS_NOCHAR: break; default: *dst = '\0'; return (-1); } } if (unvis(dst, c, &state, UNVIS_END) == UNVIS_VALID) dst++; *dst = '\0'; return (dst - start); } ssize_t strnunvis(char *dst, const char *src, size_t sz) { char c, p; char *start = dst, *end = dst + sz - 1; int state = 0; if (sz > 0) *end = '\0'; while ((c = *src++)) { again: switch (unvis(&p, c, &state, 0)) { case UNVIS_VALID: if (dst < end) *dst = p; dst++; break; case UNVIS_VALIDPUSH: if (dst < end) *dst = p; dst++; goto again; case 0: case UNVIS_NOCHAR: break; default: if (dst <= end) *dst = '\0'; return (-1); } } if (unvis(&p, c, &state, UNVIS_END) == UNVIS_VALID) { if (dst < end) *dst = p; dst++; } if (dst <= end) *dst = '\0'; return (dst - start); } tmux-3.0a/compat/vis.c100644 001750 001750 00000013467 13466230030 0010454/* $OpenBSD: vis.c,v 1.24 2015/07/20 01:52:28 millert Exp $ */ /*- * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * 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. Neither the name of the University 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 REGENTS 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 REGENTS 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 #include #include #include #include #include #include "compat.h" #define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7') #define isvisible(c,flag) \ (((c) == '\\' || (flag & VIS_ALL) == 0) && \ (((u_int)(c) <= UCHAR_MAX && isascii((u_char)(c)) && \ (((c) != '*' && (c) != '?' && (c) != '[' && (c) != '#') || \ (flag & VIS_GLOB) == 0) && isgraph((u_char)(c))) || \ ((flag & VIS_SP) == 0 && (c) == ' ') || \ ((flag & VIS_TAB) == 0 && (c) == '\t') || \ ((flag & VIS_NL) == 0 && (c) == '\n') || \ ((flag & VIS_SAFE) && ((c) == '\b' || \ (c) == '\007' || (c) == '\r' || \ isgraph((u_char)(c)))))) /* * vis - visually encode characters */ char * vis(char *dst, int c, int flag, int nextc) { if (isvisible(c, flag)) { if ((c == '"' && (flag & VIS_DQ) != 0) || (c == '\\' && (flag & VIS_NOSLASH) == 0)) *dst++ = '\\'; *dst++ = c; *dst = '\0'; return (dst); } if (flag & VIS_CSTYLE) { switch(c) { case '\n': *dst++ = '\\'; *dst++ = 'n'; goto done; case '\r': *dst++ = '\\'; *dst++ = 'r'; goto done; case '\b': *dst++ = '\\'; *dst++ = 'b'; goto done; case '\a': *dst++ = '\\'; *dst++ = 'a'; goto done; case '\v': *dst++ = '\\'; *dst++ = 'v'; goto done; case '\t': *dst++ = '\\'; *dst++ = 't'; goto done; case '\f': *dst++ = '\\'; *dst++ = 'f'; goto done; case ' ': *dst++ = '\\'; *dst++ = 's'; goto done; case '\0': *dst++ = '\\'; *dst++ = '0'; if (isoctal(nextc)) { *dst++ = '0'; *dst++ = '0'; } goto done; } } if (((c & 0177) == ' ') || (flag & VIS_OCTAL) || ((flag & VIS_GLOB) && (c == '*' || c == '?' || c == '[' || c == '#'))) { *dst++ = '\\'; *dst++ = ((u_char)c >> 6 & 07) + '0'; *dst++ = ((u_char)c >> 3 & 07) + '0'; *dst++ = ((u_char)c & 07) + '0'; goto done; } if ((flag & VIS_NOSLASH) == 0) *dst++ = '\\'; if (c & 0200) { c &= 0177; *dst++ = 'M'; } if (iscntrl((u_char)c)) { *dst++ = '^'; if (c == 0177) *dst++ = '?'; else *dst++ = c + '@'; } else { *dst++ = '-'; *dst++ = c; } done: *dst = '\0'; return (dst); } /* * strvis, strnvis, strvisx - visually encode characters from src into dst * * Dst must be 4 times the size of src to account for possible * expansion. The length of dst, not including the trailing NULL, * is returned. * * Strnvis will write no more than siz-1 bytes (and will NULL terminate). * The number of bytes needed to fully encode the string is returned. * * Strvisx encodes exactly len bytes from src into dst. * This is useful for encoding a block of data. */ int strvis(char *dst, const char *src, int flag) { char c; char *start; for (start = dst; (c = *src);) dst = vis(dst, c, flag, *++src); *dst = '\0'; return (dst - start); } int strnvis(char *dst, const char *src, size_t siz, int flag) { char *start, *end; char tbuf[5]; int c, i; i = 0; for (start = dst, end = start + siz - 1; (c = *src) && dst < end; ) { if (isvisible(c, flag)) { if ((c == '"' && (flag & VIS_DQ) != 0) || (c == '\\' && (flag & VIS_NOSLASH) == 0)) { /* need space for the extra '\\' */ if (dst + 1 >= end) { i = 2; break; } *dst++ = '\\'; } i = 1; *dst++ = c; src++; } else { i = vis(tbuf, c, flag, *++src) - tbuf; if (dst + i <= end) { memcpy(dst, tbuf, i); dst += i; } else { src--; break; } } } if (siz > 0) *dst = '\0'; if (dst + i > end) { /* adjust return value for truncation */ while ((c = *src)) dst += vis(tbuf, c, flag, *++src) - tbuf; } return (dst - start); } int stravis(char **outp, const char *src, int flag) { char *buf; int len, serrno; buf = calloc(4, strlen(src) + 1); if (buf == NULL) return -1; len = strvis(buf, src, flag); serrno = errno; *outp = realloc(buf, len + 1); if (*outp == NULL) { *outp = buf; errno = serrno; } return (len); } int strvisx(char *dst, const char *src, size_t len, int flag) { char c; char *start; for (start = dst; len > 1; len--) { c = *src; dst = vis(dst, c, flag, *++src); } if (len) dst = vis(dst, *src, flag, '\0'); *dst = '\0'; return (dst - start); } tmux-3.0a/compat/bitstring.h100644 001750 001750 00000010336 13466230030 0011655/* $OpenBSD: bitstring.h,v 1.5 2003/06/02 19:34:12 millert Exp $ */ /* $NetBSD: bitstring.h,v 1.5 1997/05/14 15:49:55 pk Exp $ */ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Paul Vixie. * * 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. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)bitstring.h 8.1 (Berkeley) 7/19/93 */ #ifndef _BITSTRING_H_ #define _BITSTRING_H_ /* modified for SV/AT and bitstring bugfix by M.R.Murphy, 11oct91 * bitstr_size changed gratuitously, but shorter * bit_alloc spelling error fixed * the following were efficient, but didn't work, they've been made to * work, but are no longer as efficient :-) * bit_nclear, bit_nset, bit_ffc, bit_ffs */ typedef unsigned char bitstr_t; /* internal macros */ /* byte of the bitstring bit is in */ #define _bit_byte(bit) \ ((bit) >> 3) /* mask for the bit within its byte */ #define _bit_mask(bit) \ (1 << ((bit)&0x7)) /* external macros */ /* bytes in a bitstring of nbits bits */ #define bitstr_size(nbits) \ (((nbits) + 7) >> 3) /* allocate a bitstring */ #define bit_alloc(nbits) \ (bitstr_t *)calloc((size_t)bitstr_size(nbits), sizeof(bitstr_t)) /* allocate a bitstring on the stack */ #define bit_decl(name, nbits) \ ((name)[bitstr_size(nbits)]) /* is bit N of bitstring name set? */ #define bit_test(name, bit) \ ((name)[_bit_byte(bit)] & _bit_mask(bit)) /* set bit N of bitstring name */ #define bit_set(name, bit) \ ((name)[_bit_byte(bit)] |= _bit_mask(bit)) /* clear bit N of bitstring name */ #define bit_clear(name, bit) \ ((name)[_bit_byte(bit)] &= ~_bit_mask(bit)) /* clear bits start ... stop in bitstring */ #define bit_nclear(name, start, stop) do { \ register bitstr_t *_name = name; \ register int _start = start, _stop = stop; \ while (_start <= _stop) { \ bit_clear(_name, _start); \ _start++; \ } \ } while(0) /* set bits start ... stop in bitstring */ #define bit_nset(name, start, stop) do { \ register bitstr_t *_name = name; \ register int _start = start, _stop = stop; \ while (_start <= _stop) { \ bit_set(_name, _start); \ _start++; \ } \ } while(0) /* find first bit clear in name */ #define bit_ffc(name, nbits, value) do { \ register bitstr_t *_name = name; \ register int _bit, _nbits = nbits, _value = -1; \ for (_bit = 0; _bit < _nbits; ++_bit) \ if (!bit_test(_name, _bit)) { \ _value = _bit; \ break; \ } \ *(value) = _value; \ } while(0) /* find first bit set in name */ #define bit_ffs(name, nbits, value) do { \ register bitstr_t *_name = name; \ register int _bit, _nbits = nbits, _value = -1; \ for (_bit = 0; _bit < _nbits; ++_bit) \ if (bit_test(_name, _bit)) { \ _value = _bit; \ break; \ } \ *(value) = _value; \ } while(0) #endif /* !_BITSTRING_H_ */ tmux-3.0a/compat/forkpty-aix.c100644 001750 001750 00000005043 13466230030 0012117/* * 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 "compat.h" void fatal(const char *, ...); void fatalx(const char *, ...); pid_t forkpty(int *master, __unused char *name, struct termios *tio, struct winsize *ws) { int slave = -1, fd, pipe_fd[2]; char *path, dummy; pid_t pid; if (pipe(pipe_fd) == -1) return (-1); if ((*master = open("/dev/ptc", O_RDWR|O_NOCTTY)) == -1) goto out; if ((path = ttyname(*master)) == NULL) goto out; if (name != NULL) strlcpy(name, path, TTY_NAME_MAX); if ((slave = open(path, O_RDWR|O_NOCTTY)) == -1) goto out; switch (pid = fork()) { case -1: goto out; case 0: close(*master); close(pipe_fd[1]); while (read(pipe_fd[0], &dummy, 1) == -1) { if (errno != EINTR) break; } close(pipe_fd[0]); fd = open(_PATH_TTY, O_RDWR|O_NOCTTY); if (fd >= 0) { ioctl(fd, TIOCNOTTY, NULL); close(fd); } if (setsid() < 0) fatal("setsid"); fd = open(_PATH_TTY, O_RDWR|O_NOCTTY); if (fd >= 0) fatalx("open succeeded (failed to disconnect)"); fd = open(path, O_RDWR); if (fd < 0) fatal("open failed"); close(fd); fd = open("/dev/tty", O_WRONLY); if (fd < 0) fatal("open failed"); close(fd); if (tio != NULL && tcsetattr(slave, TCSAFLUSH, tio) == -1) fatal("tcsetattr failed"); if (ioctl(slave, TIOCSWINSZ, ws) == -1) fatal("ioctl failed"); dup2(slave, 0); dup2(slave, 1); dup2(slave, 2); if (slave > 2) close(slave); return (0); } close(slave); close(pipe_fd[0]); close(pipe_fd[1]); return (pid); out: if (*master != -1) close(*master); if (slave != -1) close(slave); close(pipe_fd[0]); close(pipe_fd[1]); return (-1); } tmux-3.0a/compat/forkpty-hpux.c100644 001750 001750 00000004216 13466230030 0012323/* * 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 "compat.h" void fatal(const char *, ...); void fatalx(const char *, ...); pid_t forkpty(int *master, char *name, struct termios *tio, struct winsize *ws) { int slave = -1; char *path; pid_t pid; if ((*master = open("/dev/ptmx", O_RDWR|O_NOCTTY)) == -1) return (-1); if (grantpt(*master) != 0) goto out; if (unlockpt(*master) != 0) goto out; if ((path = ptsname(*master)) == NULL) goto out; if (name != NULL) strlcpy(name, path, TTY_NAME_MAX); if ((slave = open(path, O_RDWR|O_NOCTTY)) == -1) goto out; switch (pid = fork()) { case -1: goto out; case 0: close(*master); setsid(); #ifdef TIOCSCTTY if (ioctl(slave, TIOCSCTTY, NULL) == -1) fatal("ioctl failed"); #endif if (ioctl(slave, I_PUSH, "ptem") == -1) fatal("ioctl failed"); if (ioctl(slave, I_PUSH, "ldterm") == -1) fatal("ioctl failed"); if (tio != NULL && tcsetattr(slave, TCSAFLUSH, tio) == -1) fatal("tcsetattr failed"); if (ioctl(slave, TIOCSWINSZ, ws) == -1) fatal("ioctl failed"); dup2(slave, 0); dup2(slave, 1); dup2(slave, 2); if (slave > 2) close(slave); return (0); } close(slave); return (pid); out: if (*master != -1) close(*master); if (slave != -1) close(slave); return (-1); } tmux-3.0a/compat/forkpty-sunos.c100644 001750 001750 00000004270 13466230030 0012506/* * 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 #include "compat.h" void fatal(const char *, ...); void fatalx(const char *, ...); pid_t forkpty(int *master, char *name, struct termios *tio, struct winsize *ws) { int slave = -1; char *path; pid_t pid; if ((*master = open("/dev/ptmx", O_RDWR|O_NOCTTY)) == -1) return (-1); if (grantpt(*master) != 0) goto out; if (unlockpt(*master) != 0) goto out; if ((path = ptsname(*master)) == NULL) goto out; if (name != NULL) strlcpy(name, path, TTY_NAME_MAX); if ((slave = open(path, O_RDWR|O_NOCTTY)) == -1) goto out; switch (pid = fork()) { case -1: goto out; case 0: close(*master); setsid(); #ifdef TIOCSCTTY if (ioctl(slave, TIOCSCTTY, NULL) == -1) fatal("ioctl failed"); #endif if (ioctl(slave, I_PUSH, "ptem") == -1) fatal("ioctl failed"); if (ioctl(slave, I_PUSH, "ldterm") == -1) fatal("ioctl failed"); if (tio != NULL && tcsetattr(slave, TCSAFLUSH, tio) == -1) fatal("tcsetattr failed"); if (ioctl(slave, TIOCSWINSZ, ws) == -1) fatal("ioctl failed"); dup2(slave, 0); dup2(slave, 1); dup2(slave, 2); if (slave > 2) close(slave); return (0); } close(slave); return (pid); out: if (*master != -1) close(*master); if (slave != -1) close(slave); return (-1); } tmux-3.0a/compat/imsg.h100644 001750 001750 00000005753 13466230030 0010616/* $OpenBSD: imsg.h,v 1.4 2017/03/24 09:34:12 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. */ #ifndef _IMSG_H_ #define _IMSG_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; uint32_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 { uint32_t type; uint16_t len; uint16_t flags; uint32_t peerid; uint32_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 *, uint32_t, uint32_t, pid_t, int, const void *, uint16_t); int imsg_composev(struct imsgbuf *, uint32_t, uint32_t, pid_t, int, const struct iovec *, int); struct ibuf *imsg_create(struct imsgbuf *, uint32_t, uint32_t, pid_t, uint16_t); int imsg_add(struct ibuf *, const void *, uint16_t); void imsg_close(struct imsgbuf *, struct ibuf *); void imsg_free(struct imsg *); int imsg_flush(struct imsgbuf *); void imsg_clear(struct imsgbuf *); #endif tmux-3.0a/compat/queue.h100644 001750 001750 00000043621 13466230030 0010777/* $OpenBSD: queue.h,v 1.44 2016/09/09 20:31:46 millert Exp $ */ /* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ /* * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * 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. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)queue.h 8.5 (Berkeley) 8/20/94 */ #ifndef _SYS_QUEUE_H_ #define _SYS_QUEUE_H_ /* * This file defines five types of data structures: singly-linked lists, * lists, simple queues, tail queues and XOR simple queues. * * * A singly-linked list is headed by a single forward pointer. The elements * are singly linked for minimum space and pointer manipulation overhead at * the expense of O(n) removal for arbitrary elements. New elements can be * added to the list after an existing element or at the head of the list. * Elements being removed from the head of the list should use the explicit * macro for this purpose for optimum efficiency. A singly-linked list may * only be traversed in the forward direction. Singly-linked lists are ideal * for applications with large datasets and few or no removals or for * implementing a LIFO queue. * * A list is headed by a single forward pointer (or an array of forward * pointers for a hash table header). The elements are doubly linked * so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before * or after an existing element or at the head of the list. A list * may only be traversed in the forward direction. * * A simple queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are singly * linked to save space, so elements can only be removed from the * head of the list. New elements can be added to the list before or after * an existing element, at the head of the list, or at the end of the * list. A simple queue may only be traversed in the forward direction. * * A tail queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are doubly * linked so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before or * after an existing element, at the head of the list, or at the end of * the list. A tail queue may be traversed in either direction. * * An XOR simple queue is used in the same way as a regular simple queue. * The difference is that the head structure also includes a "cookie" that * is XOR'd with the queue pointer (first, last or next) to generate the * real pointer value. * * For details on the use of these macros, see the queue(3) manual page. */ #if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC)) #define _Q_INVALIDATE(a) (a) = ((void *)-1) #else #define _Q_INVALIDATE(a) #endif /* * Singly-linked List definitions. */ #define SLIST_HEAD(name, type) \ struct name { \ struct type *slh_first; /* first element */ \ } #define SLIST_HEAD_INITIALIZER(head) \ { NULL } #define SLIST_ENTRY(type) \ struct { \ struct type *sle_next; /* next element */ \ } /* * Singly-linked List access methods. */ #define SLIST_FIRST(head) ((head)->slh_first) #define SLIST_END(head) NULL #define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) #define SLIST_NEXT(elm, field) ((elm)->field.sle_next) #define SLIST_FOREACH(var, head, field) \ for((var) = SLIST_FIRST(head); \ (var) != SLIST_END(head); \ (var) = SLIST_NEXT(var, field)) #define SLIST_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = SLIST_FIRST(head); \ (var) && ((tvar) = SLIST_NEXT(var, field), 1); \ (var) = (tvar)) /* * Singly-linked List functions. */ #define SLIST_INIT(head) { \ SLIST_FIRST(head) = SLIST_END(head); \ } #define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ (elm)->field.sle_next = (slistelm)->field.sle_next; \ (slistelm)->field.sle_next = (elm); \ } while (0) #define SLIST_INSERT_HEAD(head, elm, field) do { \ (elm)->field.sle_next = (head)->slh_first; \ (head)->slh_first = (elm); \ } while (0) #define SLIST_REMOVE_AFTER(elm, field) do { \ (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ } while (0) #define SLIST_REMOVE_HEAD(head, field) do { \ (head)->slh_first = (head)->slh_first->field.sle_next; \ } while (0) #define SLIST_REMOVE(head, elm, type, field) do { \ if ((head)->slh_first == (elm)) { \ SLIST_REMOVE_HEAD((head), field); \ } else { \ struct type *curelm = (head)->slh_first; \ \ while (curelm->field.sle_next != (elm)) \ curelm = curelm->field.sle_next; \ curelm->field.sle_next = \ curelm->field.sle_next->field.sle_next; \ } \ _Q_INVALIDATE((elm)->field.sle_next); \ } while (0) /* * List definitions. */ #define LIST_HEAD(name, type) \ struct name { \ struct type *lh_first; /* first element */ \ } #define LIST_HEAD_INITIALIZER(head) \ { NULL } #define LIST_ENTRY(type) \ struct { \ struct type *le_next; /* next element */ \ struct type **le_prev; /* address of previous next element */ \ } /* * List access methods. */ #define LIST_FIRST(head) ((head)->lh_first) #define LIST_END(head) NULL #define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) #define LIST_NEXT(elm, field) ((elm)->field.le_next) #define LIST_FOREACH(var, head, field) \ for((var) = LIST_FIRST(head); \ (var)!= LIST_END(head); \ (var) = LIST_NEXT(var, field)) #define LIST_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = LIST_FIRST(head); \ (var) && ((tvar) = LIST_NEXT(var, field), 1); \ (var) = (tvar)) /* * List functions. */ #define LIST_INIT(head) do { \ LIST_FIRST(head) = LIST_END(head); \ } while (0) #define LIST_INSERT_AFTER(listelm, elm, field) do { \ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ (listelm)->field.le_next->field.le_prev = \ &(elm)->field.le_next; \ (listelm)->field.le_next = (elm); \ (elm)->field.le_prev = &(listelm)->field.le_next; \ } while (0) #define LIST_INSERT_BEFORE(listelm, elm, field) do { \ (elm)->field.le_prev = (listelm)->field.le_prev; \ (elm)->field.le_next = (listelm); \ *(listelm)->field.le_prev = (elm); \ (listelm)->field.le_prev = &(elm)->field.le_next; \ } while (0) #define LIST_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.le_next = (head)->lh_first) != NULL) \ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ (head)->lh_first = (elm); \ (elm)->field.le_prev = &(head)->lh_first; \ } while (0) #define LIST_REMOVE(elm, field) do { \ if ((elm)->field.le_next != NULL) \ (elm)->field.le_next->field.le_prev = \ (elm)->field.le_prev; \ *(elm)->field.le_prev = (elm)->field.le_next; \ _Q_INVALIDATE((elm)->field.le_prev); \ _Q_INVALIDATE((elm)->field.le_next); \ } while (0) #define LIST_REPLACE(elm, elm2, field) do { \ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ (elm2)->field.le_next->field.le_prev = \ &(elm2)->field.le_next; \ (elm2)->field.le_prev = (elm)->field.le_prev; \ *(elm2)->field.le_prev = (elm2); \ _Q_INVALIDATE((elm)->field.le_prev); \ _Q_INVALIDATE((elm)->field.le_next); \ } while (0) /* * Simple queue definitions. */ #define SIMPLEQ_HEAD(name, type) \ struct name { \ struct type *sqh_first; /* first element */ \ struct type **sqh_last; /* addr of last next element */ \ } #define SIMPLEQ_HEAD_INITIALIZER(head) \ { NULL, &(head).sqh_first } #define SIMPLEQ_ENTRY(type) \ struct { \ struct type *sqe_next; /* next element */ \ } /* * Simple queue access methods. */ #define SIMPLEQ_FIRST(head) ((head)->sqh_first) #define SIMPLEQ_END(head) NULL #define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) #define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) #define SIMPLEQ_FOREACH(var, head, field) \ for((var) = SIMPLEQ_FIRST(head); \ (var) != SIMPLEQ_END(head); \ (var) = SIMPLEQ_NEXT(var, field)) #define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = SIMPLEQ_FIRST(head); \ (var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \ (var) = (tvar)) /* * Simple queue functions. */ #define SIMPLEQ_INIT(head) do { \ (head)->sqh_first = NULL; \ (head)->sqh_last = &(head)->sqh_first; \ } while (0) #define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ (head)->sqh_last = &(elm)->field.sqe_next; \ (head)->sqh_first = (elm); \ } while (0) #define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.sqe_next = NULL; \ *(head)->sqh_last = (elm); \ (head)->sqh_last = &(elm)->field.sqe_next; \ } while (0) #define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ (head)->sqh_last = &(elm)->field.sqe_next; \ (listelm)->field.sqe_next = (elm); \ } while (0) #define SIMPLEQ_REMOVE_HEAD(head, field) do { \ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ (head)->sqh_last = &(head)->sqh_first; \ } while (0) #define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \ == NULL) \ (head)->sqh_last = &(elm)->field.sqe_next; \ } while (0) #define SIMPLEQ_CONCAT(head1, head2) do { \ if (!SIMPLEQ_EMPTY((head2))) { \ *(head1)->sqh_last = (head2)->sqh_first; \ (head1)->sqh_last = (head2)->sqh_last; \ SIMPLEQ_INIT((head2)); \ } \ } while (0) /* * XOR Simple queue definitions. */ #define XSIMPLEQ_HEAD(name, type) \ struct name { \ struct type *sqx_first; /* first element */ \ struct type **sqx_last; /* addr of last next element */ \ unsigned long sqx_cookie; \ } #define XSIMPLEQ_ENTRY(type) \ struct { \ struct type *sqx_next; /* next element */ \ } /* * XOR Simple queue access methods. */ #define XSIMPLEQ_XOR(head, ptr) ((__typeof(ptr))((head)->sqx_cookie ^ \ (unsigned long)(ptr))) #define XSIMPLEQ_FIRST(head) XSIMPLEQ_XOR(head, ((head)->sqx_first)) #define XSIMPLEQ_END(head) NULL #define XSIMPLEQ_EMPTY(head) (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head)) #define XSIMPLEQ_NEXT(head, elm, field) XSIMPLEQ_XOR(head, ((elm)->field.sqx_next)) #define XSIMPLEQ_FOREACH(var, head, field) \ for ((var) = XSIMPLEQ_FIRST(head); \ (var) != XSIMPLEQ_END(head); \ (var) = XSIMPLEQ_NEXT(head, var, field)) #define XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = XSIMPLEQ_FIRST(head); \ (var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1); \ (var) = (tvar)) /* * XOR Simple queue functions. */ #define XSIMPLEQ_INIT(head) do { \ arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \ (head)->sqx_first = XSIMPLEQ_XOR(head, NULL); \ (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ } while (0) #define XSIMPLEQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.sqx_next = (head)->sqx_first) == \ XSIMPLEQ_XOR(head, NULL)) \ (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ (head)->sqx_first = XSIMPLEQ_XOR(head, (elm)); \ } while (0) #define XSIMPLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL); \ *(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \ (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ } while (0) #define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.sqx_next = (listelm)->field.sqx_next) == \ XSIMPLEQ_XOR(head, NULL)) \ (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ (listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm)); \ } while (0) #define XSIMPLEQ_REMOVE_HEAD(head, field) do { \ if (((head)->sqx_first = XSIMPLEQ_XOR(head, \ (head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \ (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ } while (0) #define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head, \ (elm)->field.sqx_next)->field.sqx_next) \ == XSIMPLEQ_XOR(head, NULL)) \ (head)->sqx_last = \ XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ } while (0) /* * Tail queue definitions. */ #define TAILQ_HEAD(name, type) \ struct name { \ struct type *tqh_first; /* first element */ \ struct type **tqh_last; /* addr of last next element */ \ } #define TAILQ_HEAD_INITIALIZER(head) \ { NULL, &(head).tqh_first } #define TAILQ_ENTRY(type) \ struct { \ struct type *tqe_next; /* next element */ \ struct type **tqe_prev; /* address of previous next element */ \ } /* * Tail queue access methods. */ #define TAILQ_FIRST(head) ((head)->tqh_first) #define TAILQ_END(head) NULL #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) #define TAILQ_LAST(head, headname) \ (*(((struct headname *)((head)->tqh_last))->tqh_last)) /* XXX */ #define TAILQ_PREV(elm, headname, field) \ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) #define TAILQ_EMPTY(head) \ (TAILQ_FIRST(head) == TAILQ_END(head)) #define TAILQ_FOREACH(var, head, field) \ for((var) = TAILQ_FIRST(head); \ (var) != TAILQ_END(head); \ (var) = TAILQ_NEXT(var, field)) #define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = TAILQ_FIRST(head); \ (var) != TAILQ_END(head) && \ ((tvar) = TAILQ_NEXT(var, field), 1); \ (var) = (tvar)) #define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ for((var) = TAILQ_LAST(head, headname); \ (var) != TAILQ_END(head); \ (var) = TAILQ_PREV(var, headname, field)) #define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ for ((var) = TAILQ_LAST(head, headname); \ (var) != TAILQ_END(head) && \ ((tvar) = TAILQ_PREV(var, headname, field), 1); \ (var) = (tvar)) /* * Tail queue functions. */ #define TAILQ_INIT(head) do { \ (head)->tqh_first = NULL; \ (head)->tqh_last = &(head)->tqh_first; \ } while (0) #define TAILQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ (head)->tqh_first->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (head)->tqh_first = (elm); \ (elm)->field.tqe_prev = &(head)->tqh_first; \ } while (0) #define TAILQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.tqe_next = NULL; \ (elm)->field.tqe_prev = (head)->tqh_last; \ *(head)->tqh_last = (elm); \ (head)->tqh_last = &(elm)->field.tqe_next; \ } while (0) #define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ (elm)->field.tqe_next->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (listelm)->field.tqe_next = (elm); \ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ } while (0) #define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ (elm)->field.tqe_next = (listelm); \ *(listelm)->field.tqe_prev = (elm); \ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ } while (0) #define TAILQ_REMOVE(head, elm, field) do { \ if (((elm)->field.tqe_next) != NULL) \ (elm)->field.tqe_next->field.tqe_prev = \ (elm)->field.tqe_prev; \ else \ (head)->tqh_last = (elm)->field.tqe_prev; \ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ _Q_INVALIDATE((elm)->field.tqe_prev); \ _Q_INVALIDATE((elm)->field.tqe_next); \ } while (0) #define TAILQ_REPLACE(head, elm, elm2, field) do { \ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ (elm2)->field.tqe_next->field.tqe_prev = \ &(elm2)->field.tqe_next; \ else \ (head)->tqh_last = &(elm2)->field.tqe_next; \ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ *(elm2)->field.tqe_prev = (elm2); \ _Q_INVALIDATE((elm)->field.tqe_prev); \ _Q_INVALIDATE((elm)->field.tqe_next); \ } while (0) #define TAILQ_CONCAT(head1, head2, field) do { \ if (!TAILQ_EMPTY(head2)) { \ *(head1)->tqh_last = (head2)->tqh_first; \ (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ (head1)->tqh_last = (head2)->tqh_last; \ TAILQ_INIT((head2)); \ } \ } while (0) #endif /* !_SYS_QUEUE_H_ */ tmux-3.0a/compat/tree.h100644 001750 001750 00000061064 13466230030 0010613/* $OpenBSD: tree.h,v 1.13 2011/07/09 00:19:45 pirofti Exp $ */ /* * Copyright 2002 Niels Provos * All rights reserved. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. */ #ifndef _SYS_TREE_H_ #define _SYS_TREE_H_ /* * This file defines data structures for different types of trees: * splay trees and red-black trees. * * A splay tree is a self-organizing data structure. Every operation * on the tree causes a splay to happen. The splay moves the requested * node to the root of the tree and partly rebalances it. * * This has the benefit that request locality causes faster lookups as * the requested nodes move to the top of the tree. On the other hand, * every lookup causes memory writes. * * The Balance Theorem bounds the total access time for m operations * and n inserts on an initially empty tree as O((m + n)lg n). The * amortized cost for a sequence of m accesses to a splay tree is O(lg n); * * A red-black tree is a binary search tree with the node color as an * extra attribute. It fulfills a set of conditions: * - every search path from the root to a leaf consists of the * same number of black nodes, * - each red node (except for the root) has a black parent, * - each leaf node is black. * * Every operation on a red-black tree is bounded as O(lg n). * The maximum height of a red-black tree is 2lg (n+1). */ #define SPLAY_HEAD(name, type) \ struct name { \ struct type *sph_root; /* root of the tree */ \ } #define SPLAY_INITIALIZER(root) \ { NULL } #define SPLAY_INIT(root) do { \ (root)->sph_root = NULL; \ } while (0) #define SPLAY_ENTRY(type) \ struct { \ struct type *spe_left; /* left element */ \ struct type *spe_right; /* right element */ \ } #define SPLAY_LEFT(elm, field) (elm)->field.spe_left #define SPLAY_RIGHT(elm, field) (elm)->field.spe_right #define SPLAY_ROOT(head) (head)->sph_root #define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) /* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ #define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ (head)->sph_root = tmp; \ } while (0) #define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ SPLAY_LEFT(tmp, field) = (head)->sph_root; \ (head)->sph_root = tmp; \ } while (0) #define SPLAY_LINKLEFT(head, tmp, field) do { \ SPLAY_LEFT(tmp, field) = (head)->sph_root; \ tmp = (head)->sph_root; \ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ } while (0) #define SPLAY_LINKRIGHT(head, tmp, field) do { \ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ tmp = (head)->sph_root; \ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ } while (0) #define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ } while (0) /* Generates prototypes and inline functions */ #define SPLAY_PROTOTYPE(name, type, field, cmp) \ void name##_SPLAY(struct name *, struct type *); \ void name##_SPLAY_MINMAX(struct name *, int); \ struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ \ /* Finds the node with the same key as elm */ \ static __inline struct type * \ name##_SPLAY_FIND(struct name *head, struct type *elm) \ { \ if (SPLAY_EMPTY(head)) \ return(NULL); \ name##_SPLAY(head, elm); \ if ((cmp)(elm, (head)->sph_root) == 0) \ return (head->sph_root); \ return (NULL); \ } \ \ static __inline struct type * \ name##_SPLAY_NEXT(struct name *head, struct type *elm) \ { \ name##_SPLAY(head, elm); \ if (SPLAY_RIGHT(elm, field) != NULL) { \ elm = SPLAY_RIGHT(elm, field); \ while (SPLAY_LEFT(elm, field) != NULL) { \ elm = SPLAY_LEFT(elm, field); \ } \ } else \ elm = NULL; \ return (elm); \ } \ \ static __inline struct type * \ name##_SPLAY_MIN_MAX(struct name *head, int val) \ { \ name##_SPLAY_MINMAX(head, val); \ return (SPLAY_ROOT(head)); \ } /* Main splay operation. * Moves node close to the key of elm to top */ #define SPLAY_GENERATE(name, type, field, cmp) \ struct type * \ name##_SPLAY_INSERT(struct name *head, struct type *elm) \ { \ if (SPLAY_EMPTY(head)) { \ SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ } else { \ int __comp; \ name##_SPLAY(head, elm); \ __comp = (cmp)(elm, (head)->sph_root); \ if(__comp < 0) { \ SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ SPLAY_RIGHT(elm, field) = (head)->sph_root; \ SPLAY_LEFT((head)->sph_root, field) = NULL; \ } else if (__comp > 0) { \ SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ SPLAY_LEFT(elm, field) = (head)->sph_root; \ SPLAY_RIGHT((head)->sph_root, field) = NULL; \ } else \ return ((head)->sph_root); \ } \ (head)->sph_root = (elm); \ return (NULL); \ } \ \ struct type * \ name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ { \ struct type *__tmp; \ if (SPLAY_EMPTY(head)) \ return (NULL); \ name##_SPLAY(head, elm); \ if ((cmp)(elm, (head)->sph_root) == 0) { \ if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ } else { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ name##_SPLAY(head, elm); \ SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ } \ return (elm); \ } \ return (NULL); \ } \ \ void \ name##_SPLAY(struct name *head, struct type *elm) \ { \ struct type __node, *__left, *__right, *__tmp; \ int __comp; \ \ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ __left = __right = &__node; \ \ while ((__comp = (cmp)(elm, (head)->sph_root))) { \ if (__comp < 0) { \ __tmp = SPLAY_LEFT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if ((cmp)(elm, __tmp) < 0){ \ SPLAY_ROTATE_RIGHT(head, __tmp, field); \ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKLEFT(head, __right, field); \ } else if (__comp > 0) { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if ((cmp)(elm, __tmp) > 0){ \ SPLAY_ROTATE_LEFT(head, __tmp, field); \ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKRIGHT(head, __left, field); \ } \ } \ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ } \ \ /* Splay with either the minimum or the maximum element \ * Used to find minimum or maximum element in tree. \ */ \ void name##_SPLAY_MINMAX(struct name *head, int __comp) \ { \ struct type __node, *__left, *__right, *__tmp; \ \ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ __left = __right = &__node; \ \ while (1) { \ if (__comp < 0) { \ __tmp = SPLAY_LEFT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if (__comp < 0){ \ SPLAY_ROTATE_RIGHT(head, __tmp, field); \ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKLEFT(head, __right, field); \ } else if (__comp > 0) { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if (__comp > 0) { \ SPLAY_ROTATE_LEFT(head, __tmp, field); \ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKRIGHT(head, __left, field); \ } \ } \ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ } #define SPLAY_NEGINF -1 #define SPLAY_INF 1 #define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) #define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) #define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) #define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) #define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) #define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) #define SPLAY_FOREACH(x, name, head) \ for ((x) = SPLAY_MIN(name, head); \ (x) != NULL; \ (x) = SPLAY_NEXT(name, head, x)) /* Macros that define a red-black tree */ #define RB_HEAD(name, type) \ struct name { \ struct type *rbh_root; /* root of the tree */ \ } #define RB_INITIALIZER(root) \ { NULL } #define RB_INIT(root) do { \ (root)->rbh_root = NULL; \ } while (0) #define RB_BLACK 0 #define RB_RED 1 #define RB_ENTRY(type) \ struct { \ struct type *rbe_left; /* left element */ \ struct type *rbe_right; /* right element */ \ struct type *rbe_parent; /* parent element */ \ int rbe_color; /* node color */ \ } #define RB_LEFT(elm, field) (elm)->field.rbe_left #define RB_RIGHT(elm, field) (elm)->field.rbe_right #define RB_PARENT(elm, field) (elm)->field.rbe_parent #define RB_COLOR(elm, field) (elm)->field.rbe_color #define RB_ROOT(head) (head)->rbh_root #define RB_EMPTY(head) (RB_ROOT(head) == NULL) #define RB_SET(elm, parent, field) do { \ RB_PARENT(elm, field) = parent; \ RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ RB_COLOR(elm, field) = RB_RED; \ } while (0) #define RB_SET_BLACKRED(black, red, field) do { \ RB_COLOR(black, field) = RB_BLACK; \ RB_COLOR(red, field) = RB_RED; \ } while (0) #ifndef RB_AUGMENT #define RB_AUGMENT(x) do {} while (0) #endif #define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ (tmp) = RB_RIGHT(elm, field); \ if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ } \ RB_AUGMENT(elm); \ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ else \ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ } else \ (head)->rbh_root = (tmp); \ RB_LEFT(tmp, field) = (elm); \ RB_PARENT(elm, field) = (tmp); \ RB_AUGMENT(tmp); \ if ((RB_PARENT(tmp, field))) \ RB_AUGMENT(RB_PARENT(tmp, field)); \ } while (0) #define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ (tmp) = RB_LEFT(elm, field); \ if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ } \ RB_AUGMENT(elm); \ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ else \ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ } else \ (head)->rbh_root = (tmp); \ RB_RIGHT(tmp, field) = (elm); \ RB_PARENT(elm, field) = (tmp); \ RB_AUGMENT(tmp); \ if ((RB_PARENT(tmp, field))) \ RB_AUGMENT(RB_PARENT(tmp, field)); \ } while (0) /* Generates prototypes and inline functions */ #define RB_PROTOTYPE(name, type, field, cmp) \ RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) #define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) #define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ attr struct type *name##_RB_INSERT(struct name *, struct type *); \ attr struct type *name##_RB_FIND(struct name *, struct type *); \ attr struct type *name##_RB_NFIND(struct name *, struct type *); \ attr struct type *name##_RB_NEXT(struct type *); \ attr struct type *name##_RB_PREV(struct type *); \ attr struct type *name##_RB_MINMAX(struct name *, int); \ \ /* Main rb operation. * Moves node close to the key of elm to top */ #define RB_GENERATE(name, type, field, cmp) \ RB_GENERATE_INTERNAL(name, type, field, cmp,) #define RB_GENERATE_STATIC(name, type, field, cmp) \ RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) #define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ attr void \ name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ { \ struct type *parent, *gparent, *tmp; \ while ((parent = RB_PARENT(elm, field)) && \ RB_COLOR(parent, field) == RB_RED) { \ gparent = RB_PARENT(parent, field); \ if (parent == RB_LEFT(gparent, field)) { \ tmp = RB_RIGHT(gparent, field); \ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ RB_COLOR(tmp, field) = RB_BLACK; \ RB_SET_BLACKRED(parent, gparent, field);\ elm = gparent; \ continue; \ } \ if (RB_RIGHT(parent, field) == elm) { \ RB_ROTATE_LEFT(head, parent, tmp, field);\ tmp = parent; \ parent = elm; \ elm = tmp; \ } \ RB_SET_BLACKRED(parent, gparent, field); \ RB_ROTATE_RIGHT(head, gparent, tmp, field); \ } else { \ tmp = RB_LEFT(gparent, field); \ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ RB_COLOR(tmp, field) = RB_BLACK; \ RB_SET_BLACKRED(parent, gparent, field);\ elm = gparent; \ continue; \ } \ if (RB_LEFT(parent, field) == elm) { \ RB_ROTATE_RIGHT(head, parent, tmp, field);\ tmp = parent; \ parent = elm; \ elm = tmp; \ } \ RB_SET_BLACKRED(parent, gparent, field); \ RB_ROTATE_LEFT(head, gparent, tmp, field); \ } \ } \ RB_COLOR(head->rbh_root, field) = RB_BLACK; \ } \ \ attr void \ name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ { \ struct type *tmp; \ while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ elm != RB_ROOT(head)) { \ if (RB_LEFT(parent, field) == elm) { \ tmp = RB_RIGHT(parent, field); \ if (RB_COLOR(tmp, field) == RB_RED) { \ RB_SET_BLACKRED(tmp, parent, field); \ RB_ROTATE_LEFT(head, parent, tmp, field);\ tmp = RB_RIGHT(parent, field); \ } \ if ((RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ RB_COLOR(tmp, field) = RB_RED; \ elm = parent; \ parent = RB_PARENT(elm, field); \ } else { \ if (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ struct type *oleft; \ if ((oleft = RB_LEFT(tmp, field)))\ RB_COLOR(oleft, field) = RB_BLACK;\ RB_COLOR(tmp, field) = RB_RED; \ RB_ROTATE_RIGHT(head, tmp, oleft, field);\ tmp = RB_RIGHT(parent, field); \ } \ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ RB_COLOR(parent, field) = RB_BLACK; \ if (RB_RIGHT(tmp, field)) \ RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ RB_ROTATE_LEFT(head, parent, tmp, field);\ elm = RB_ROOT(head); \ break; \ } \ } else { \ tmp = RB_LEFT(parent, field); \ if (RB_COLOR(tmp, field) == RB_RED) { \ RB_SET_BLACKRED(tmp, parent, field); \ RB_ROTATE_RIGHT(head, parent, tmp, field);\ tmp = RB_LEFT(parent, field); \ } \ if ((RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ RB_COLOR(tmp, field) = RB_RED; \ elm = parent; \ parent = RB_PARENT(elm, field); \ } else { \ if (RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ struct type *oright; \ if ((oright = RB_RIGHT(tmp, field)))\ RB_COLOR(oright, field) = RB_BLACK;\ RB_COLOR(tmp, field) = RB_RED; \ RB_ROTATE_LEFT(head, tmp, oright, field);\ tmp = RB_LEFT(parent, field); \ } \ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ RB_COLOR(parent, field) = RB_BLACK; \ if (RB_LEFT(tmp, field)) \ RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ RB_ROTATE_RIGHT(head, parent, tmp, field);\ elm = RB_ROOT(head); \ break; \ } \ } \ } \ if (elm) \ RB_COLOR(elm, field) = RB_BLACK; \ } \ \ attr struct type * \ name##_RB_REMOVE(struct name *head, struct type *elm) \ { \ struct type *child, *parent, *old = elm; \ int color; \ if (RB_LEFT(elm, field) == NULL) \ child = RB_RIGHT(elm, field); \ else if (RB_RIGHT(elm, field) == NULL) \ child = RB_LEFT(elm, field); \ else { \ struct type *left; \ elm = RB_RIGHT(elm, field); \ while ((left = RB_LEFT(elm, field))) \ elm = left; \ child = RB_RIGHT(elm, field); \ parent = RB_PARENT(elm, field); \ color = RB_COLOR(elm, field); \ if (child) \ RB_PARENT(child, field) = parent; \ if (parent) { \ if (RB_LEFT(parent, field) == elm) \ RB_LEFT(parent, field) = child; \ else \ RB_RIGHT(parent, field) = child; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = child; \ if (RB_PARENT(elm, field) == old) \ parent = elm; \ (elm)->field = (old)->field; \ if (RB_PARENT(old, field)) { \ if (RB_LEFT(RB_PARENT(old, field), field) == old)\ RB_LEFT(RB_PARENT(old, field), field) = elm;\ else \ RB_RIGHT(RB_PARENT(old, field), field) = elm;\ RB_AUGMENT(RB_PARENT(old, field)); \ } else \ RB_ROOT(head) = elm; \ RB_PARENT(RB_LEFT(old, field), field) = elm; \ if (RB_RIGHT(old, field)) \ RB_PARENT(RB_RIGHT(old, field), field) = elm; \ if (parent) { \ left = parent; \ do { \ RB_AUGMENT(left); \ } while ((left = RB_PARENT(left, field))); \ } \ goto color; \ } \ parent = RB_PARENT(elm, field); \ color = RB_COLOR(elm, field); \ if (child) \ RB_PARENT(child, field) = parent; \ if (parent) { \ if (RB_LEFT(parent, field) == elm) \ RB_LEFT(parent, field) = child; \ else \ RB_RIGHT(parent, field) = child; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = child; \ color: \ if (color == RB_BLACK) \ name##_RB_REMOVE_COLOR(head, parent, child); \ return (old); \ } \ \ /* Inserts a node into the RB tree */ \ attr struct type * \ name##_RB_INSERT(struct name *head, struct type *elm) \ { \ struct type *tmp; \ struct type *parent = NULL; \ int comp = 0; \ tmp = RB_ROOT(head); \ while (tmp) { \ parent = tmp; \ comp = (cmp)(elm, parent); \ if (comp < 0) \ tmp = RB_LEFT(tmp, field); \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ RB_SET(elm, parent, field); \ if (parent != NULL) { \ if (comp < 0) \ RB_LEFT(parent, field) = elm; \ else \ RB_RIGHT(parent, field) = elm; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = elm; \ name##_RB_INSERT_COLOR(head, elm); \ return (NULL); \ } \ \ /* Finds the node with the same key as elm */ \ attr struct type * \ name##_RB_FIND(struct name *head, struct type *elm) \ { \ struct type *tmp = RB_ROOT(head); \ int comp; \ while (tmp) { \ comp = cmp(elm, tmp); \ if (comp < 0) \ tmp = RB_LEFT(tmp, field); \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ return (NULL); \ } \ \ /* Finds the first node greater than or equal to the search key */ \ attr struct type * \ name##_RB_NFIND(struct name *head, struct type *elm) \ { \ struct type *tmp = RB_ROOT(head); \ struct type *res = NULL; \ int comp; \ while (tmp) { \ comp = cmp(elm, tmp); \ if (comp < 0) { \ res = tmp; \ tmp = RB_LEFT(tmp, field); \ } \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ return (res); \ } \ \ /* ARGSUSED */ \ attr struct type * \ name##_RB_NEXT(struct type *elm) \ { \ if (RB_RIGHT(elm, field)) { \ elm = RB_RIGHT(elm, field); \ while (RB_LEFT(elm, field)) \ elm = RB_LEFT(elm, field); \ } else { \ if (RB_PARENT(elm, field) && \ (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ elm = RB_PARENT(elm, field); \ else { \ while (RB_PARENT(elm, field) && \ (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ elm = RB_PARENT(elm, field); \ elm = RB_PARENT(elm, field); \ } \ } \ return (elm); \ } \ \ /* ARGSUSED */ \ attr struct type * \ name##_RB_PREV(struct type *elm) \ { \ if (RB_LEFT(elm, field)) { \ elm = RB_LEFT(elm, field); \ while (RB_RIGHT(elm, field)) \ elm = RB_RIGHT(elm, field); \ } else { \ if (RB_PARENT(elm, field) && \ (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ elm = RB_PARENT(elm, field); \ else { \ while (RB_PARENT(elm, field) && \ (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ elm = RB_PARENT(elm, field); \ elm = RB_PARENT(elm, field); \ } \ } \ return (elm); \ } \ \ attr struct type * \ name##_RB_MINMAX(struct name *head, int val) \ { \ struct type *tmp = RB_ROOT(head); \ struct type *parent = NULL; \ while (tmp) { \ parent = tmp; \ if (val < 0) \ tmp = RB_LEFT(tmp, field); \ else \ tmp = RB_RIGHT(tmp, field); \ } \ return (parent); \ } #define RB_NEGINF -1 #define RB_INF 1 #define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) #define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) #define RB_FIND(name, x, y) name##_RB_FIND(x, y) #define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) #define RB_NEXT(name, x, y) name##_RB_NEXT(y) #define RB_PREV(name, x, y) name##_RB_PREV(y) #define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) #define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) #define RB_FOREACH(x, name, head) \ for ((x) = RB_MIN(name, head); \ (x) != NULL; \ (x) = name##_RB_NEXT(x)) #define RB_FOREACH_SAFE(x, name, head, y) \ for ((x) = RB_MIN(name, head); \ ((x) != NULL) && ((y) = name##_RB_NEXT(x), 1); \ (x) = (y)) #define RB_FOREACH_REVERSE(x, name, head) \ for ((x) = RB_MAX(name, head); \ (x) != NULL; \ (x) = name##_RB_PREV(x)) #define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ for ((x) = RB_MAX(name, head); \ ((x) != NULL) && ((y) = name##_RB_PREV(x), 1); \ (x) = (y)) #endif /* _SYS_TREE_H_ */ tmux-3.0a/compat/utf8proc.c100644 001750 001750 00000003166 13466230030 0011420/* * Copyright (c) 2016 Joshua Rubin * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN 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 "compat.h" int utf8proc_wcwidth(wchar_t wc) { int cat; cat = utf8proc_category(wc); if (cat == UTF8PROC_CATEGORY_CO) { /* * The private use category is where powerline and similar * codepoints are stored, they have "ambiguous" width - use 1. */ return (1); } return (utf8proc_charwidth(wc)); } int utf8proc_mbtowc(wchar_t *pwc, const char *s, size_t n) { utf8proc_ssize_t slen; if (s == NULL) return (0); /* * *pwc == -1 indicates invalid codepoint * slen < 0 indicates an error */ slen = utf8proc_iterate(s, n, pwc); if (*pwc == (wchar_t)-1 || slen < 0) return (-1); return (slen); } int utf8proc_wctomb(char *s, wchar_t wc) { if (s == NULL) return (0); if (!utf8proc_codepoint_valid(wc)) return (-1); return (utf8proc_encode_char(wc, s)); } tmux-3.0a/compat/vis.h100644 001750 001750 00000006355 13466230030 0010457/* $OpenBSD: vis.h,v 1.15 2015/07/20 01:52:27 millert Exp $ */ /* $NetBSD: vis.h,v 1.4 1994/10/26 00:56:41 cgd Exp $ */ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * 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. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)vis.h 5.9 (Berkeley) 4/3/91 */ #ifndef _VIS_H_ #define _VIS_H_ /* * to select alternate encoding format */ #define VIS_OCTAL 0x01 /* use octal \ddd format */ #define VIS_CSTYLE 0x02 /* use \[nrft0..] where appropriate */ /* * to alter set of characters encoded (default is to encode all * non-graphic except space, tab, and newline). */ #define VIS_SP 0x04 /* also encode space */ #define VIS_TAB 0x08 /* also encode tab */ #define VIS_NL 0x10 /* also encode newline */ #define VIS_WHITE (VIS_SP | VIS_TAB | VIS_NL) #define VIS_SAFE 0x20 /* only encode "unsafe" characters */ #define VIS_DQ 0x200 /* backslash-escape double quotes */ #define VIS_ALL 0x400 /* encode all characters */ /* * other */ #define VIS_NOSLASH 0x40 /* inhibit printing '\' */ #define VIS_GLOB 0x100 /* encode glob(3) magics and '#' */ /* * unvis return codes */ #define UNVIS_VALID 1 /* character valid */ #define UNVIS_VALIDPUSH 2 /* character valid, push back passed char */ #define UNVIS_NOCHAR 3 /* valid sequence, no character produced */ #define UNVIS_SYNBAD -1 /* unrecognized escape sequence */ #define UNVIS_ERROR -2 /* decoder in unknown state (unrecoverable) */ /* * unvis flags */ #define UNVIS_END 1 /* no more characters */ char *vis(char *, int, int, int); int strvis(char *, const char *, int); int stravis(char **, const char *, int); int strnvis(char *, const char *, size_t, int); int strvisx(char *, const char *, size_t, int); int strunvis(char *, const char *); int unvis(char *, char, int *, int); ssize_t strnunvis(char *, const char *, size_t); #endif /* !_VIS_H_ */ tmux-3.0a/etc/compile100755 001750 001750 00000016326 13570677631 0010374#! /bin/sh # Wrapper for compilers which do not understand '-c -o'. scriptversion=2016-01-11.22; # UTC # Copyright (C) 1999-2017 Free Software Foundation, Inc. # Written by Tom Tromey . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . nl=' ' # We need space, tab and new line, in precisely that order. Quoting is # there to prevent tools from complaining about whitespace usage. IFS=" "" $nl" file_conv= # func_file_conv build_file lazy # Convert a $build file to $host form and store it in $file # Currently only supports Windows hosts. If the determined conversion # type is listed in (the comma separated) LAZY, no conversion will # take place. func_file_conv () { file=$1 case $file in / | /[!/]*) # absolute file, and not a UNC file if test -z "$file_conv"; then # lazily determine how to convert abs files case `uname -s` in MINGW*) file_conv=mingw ;; CYGWIN*) file_conv=cygwin ;; *) file_conv=wine ;; esac fi case $file_conv/,$2, in *,$file_conv,*) ;; mingw/*) file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` ;; cygwin/*) file=`cygpath -m "$file" || echo "$file"` ;; wine/*) file=`winepath -w "$file" || echo "$file"` ;; esac ;; esac } # func_cl_dashL linkdir # Make cl look for libraries in LINKDIR func_cl_dashL () { func_file_conv "$1" if test -z "$lib_path"; then lib_path=$file else lib_path="$lib_path;$file" fi linker_opts="$linker_opts -LIBPATH:$file" } # func_cl_dashl library # Do a library search-path lookup for cl func_cl_dashl () { lib=$1 found=no save_IFS=$IFS IFS=';' for dir in $lib_path $LIB do IFS=$save_IFS if $shared && test -f "$dir/$lib.dll.lib"; then found=yes lib=$dir/$lib.dll.lib break fi if test -f "$dir/$lib.lib"; then found=yes lib=$dir/$lib.lib break fi if test -f "$dir/lib$lib.a"; then found=yes lib=$dir/lib$lib.a break fi done IFS=$save_IFS if test "$found" != yes; then lib=$lib.lib fi } # func_cl_wrapper cl arg... # Adjust compile command to suit cl func_cl_wrapper () { # Assume a capable shell lib_path= shared=: linker_opts= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. eat=1 case $2 in *.o | *.[oO][bB][jJ]) func_file_conv "$2" set x "$@" -Fo"$file" shift ;; *) func_file_conv "$2" set x "$@" -Fe"$file" shift ;; esac ;; -I) eat=1 func_file_conv "$2" mingw set x "$@" -I"$file" shift ;; -I*) func_file_conv "${1#-I}" mingw set x "$@" -I"$file" shift ;; -l) eat=1 func_cl_dashl "$2" set x "$@" "$lib" shift ;; -l*) func_cl_dashl "${1#-l}" set x "$@" "$lib" shift ;; -L) eat=1 func_cl_dashL "$2" ;; -L*) func_cl_dashL "${1#-L}" ;; -static) shared=false ;; -Wl,*) arg=${1#-Wl,} save_ifs="$IFS"; IFS=',' for flag in $arg; do IFS="$save_ifs" linker_opts="$linker_opts $flag" done IFS="$save_ifs" ;; -Xlinker) eat=1 linker_opts="$linker_opts $2" ;; -*) set x "$@" "$1" shift ;; *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) func_file_conv "$1" set x "$@" -Tp"$file" shift ;; *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) func_file_conv "$1" mingw set x "$@" "$file" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -n "$linker_opts"; then linker_opts="-link$linker_opts" fi exec "$@" $linker_opts exit 1 } eat= case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: compile [--help] [--version] PROGRAM [ARGS] Wrapper for compilers which do not understand '-c -o'. Remove '-o dest.o' from ARGS, run PROGRAM with the remaining arguments, and rename the output as expected. If you are trying to build a whole package this is not the right script to run: please start by reading the file 'INSTALL'. Report bugs to . EOF exit $? ;; -v | --v*) echo "compile $scriptversion" exit $? ;; cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \ icl | *[/\\]icl | icl.exe | *[/\\]icl.exe ) func_cl_wrapper "$@" # Doesn't return... ;; esac ofile= cfile= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. # So we strip '-o arg' only if arg is an object. eat=1 case $2 in *.o | *.obj) ofile=$2 ;; *) set x "$@" -o "$2" shift ;; esac ;; *.c) cfile=$1 set x "$@" "$1" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -z "$ofile" || test -z "$cfile"; then # If no '-o' option was seen then we might have been invoked from a # pattern rule where we don't need one. That is ok -- this is a # normal compilation that the losing compiler can handle. If no # '.c' file was seen then we are probably linking. That is also # ok. exec "$@" fi # Name of file we expect compiler to create. cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` # Create the lock directory. # Note: use '[/\\:.-]' here to ensure that we don't use the same name # that we are using for the .o file. Also, base the name on the expected # object file name, since that is what matters with a parallel build. lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d while true; do if mkdir "$lockdir" >/dev/null 2>&1; then break fi sleep 1 done # FIXME: race condition here if user kills between mkdir and trap. trap "rmdir '$lockdir'; exit 1" 1 2 15 # Run the compile. "$@" ret=$? if test -f "$cofile"; then test "$cofile" = "$ofile" || mv "$cofile" "$ofile" elif test -f "${cofile}bj"; then test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" fi rmdir "$lockdir" exit $ret # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: tmux-3.0a/etc/config.guess100755 001750 001750 00000126343 13570677631 0011337#! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2017 Free Software Foundation, Inc. timestamp='2017-05-27' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # # Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess # # Please send patches to . me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright 1992-2017 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi trap 'exit 1' 1 2 15 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. set_cc_for_build=' trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; : ${TMPDIR=/tmp} ; { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; case $CC_FOR_BUILD,$HOST_CC,$CC in ,,) echo "int x;" > $dummy.c ; for c in cc gcc c89 c99 ; do if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found ; fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac ; set_cc_for_build= ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if (test -f /.attbin/uname) >/dev/null 2>&1 ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown case "${UNAME_SYSTEM}" in Linux|GNU|GNU/*) # If the system lacks a compiler, then just pick glibc. # We could probably try harder. LIBC=gnu eval $set_cc_for_build cat <<-EOF > $dummy.c #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #else LIBC=gnu #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` ;; esac # Note: order is significant - the case branches are not exclusive. case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ /sbin/$sysctl 2>/dev/null || \ /usr/sbin/$sysctl 2>/dev/null || \ echo unknown)` case "${UNAME_MACHINE_ARCH}" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; earmv*) arch=`echo ${UNAME_MACHINE_ARCH} | sed -e 's,^e\(armv[0-9]\).*$,\1,'` endian=`echo ${UNAME_MACHINE_ARCH} | sed -ne 's,^.*\(eb\)$,\1,p'` machine=${arch}${endian}-unknown ;; *) machine=${UNAME_MACHINE_ARCH}-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently (or will in the future) and ABI. case "${UNAME_MACHINE_ARCH}" in earm*) os=netbsdelf ;; arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # Determine ABI tags. case "${UNAME_MACHINE_ARCH}" in earm*) expr='s/^earmv[0-9]/-eabi/;s/eb$//' abi=`echo ${UNAME_MACHINE_ARCH} | sed -e "$expr"` ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "${UNAME_VERSION}" in Debian*) release='-gnu' ;; *) release=`echo ${UNAME_RELEASE} | sed -e 's/[-_].*//' | cut -d. -f1,2` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "${machine}-${os}${release}${abi}" exit ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} exit ;; *:LibertyBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` echo ${UNAME_MACHINE_ARCH}-unknown-libertybsd${UNAME_RELEASE} exit ;; *:ekkoBSD:*:*) echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} exit ;; *:SolidBSD:*:*) echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} exit ;; macppc:MirBSD:*:*) echo powerpc-unknown-mirbsd${UNAME_RELEASE} exit ;; *:MirBSD:*:*) echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} exit ;; *:Sortix:*:*) echo ${UNAME_MACHINE}-unknown-sortix exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE=alpha ;; "EV4.5 (21064)") UNAME_MACHINE=alpha ;; "LCA4 (21066/21068)") UNAME_MACHINE=alpha ;; "EV5 (21164)") UNAME_MACHINE=alphaev5 ;; "EV5.6 (21164A)") UNAME_MACHINE=alphaev56 ;; "EV5.6 (21164PC)") UNAME_MACHINE=alphapca56 ;; "EV5.7 (21164PC)") UNAME_MACHINE=alphapca57 ;; "EV6 (21264)") UNAME_MACHINE=alphaev6 ;; "EV6.7 (21264A)") UNAME_MACHINE=alphaev67 ;; "EV6.8CB (21264C)") UNAME_MACHINE=alphaev68 ;; "EV6.8AL (21264B)") UNAME_MACHINE=alphaev68 ;; "EV6.8CX (21264D)") UNAME_MACHINE=alphaev68 ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE=alphaev69 ;; "EV7 (21364)") UNAME_MACHINE=alphaev7 ;; "EV7.9 (21364A)") UNAME_MACHINE=alphaev79 ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 exit $exitcode ;; Alpha\ *:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # Should we change UNAME_MACHINE based on the output of uname instead # of the specific Alpha model? echo alpha-pc-interix exit ;; 21064:Windows_NT:50:3) echo alpha-dec-winnt3.5 exit ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix${UNAME_RELEASE} exit ;; arm*:riscos:*:*|arm*:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) echo i386-pc-auroraux${UNAME_RELEASE} exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval $set_cc_for_build SUN_ARCH=i386 # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if [ "$CC_FOR_BUILD" != no_compiler_found ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH=x86_64 fi fi echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos${UNAME_RELEASE} exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x${UNAME_RELEASE}" = x && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos${UNAME_RELEASE} ;; sun4) echo sparc-sun-sunos${UNAME_RELEASE} ;; esac exit ;; aushp:SunOS:*:*) echo sparc-auspex-sunos${UNAME_RELEASE} exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint${UNAME_RELEASE} exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint${UNAME_RELEASE} exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint${UNAME_RELEASE} exit ;; m68k:machten:*:*) echo m68k-apple-machten${UNAME_RELEASE} exit ;; powerpc:machten:*:*) echo powerpc-apple-machten${UNAME_RELEASE} exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix${UNAME_RELEASE} exit ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix${UNAME_RELEASE} exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix${UNAME_RELEASE} exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`$dummy $dummyarg` && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos${UNAME_RELEASE} exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] then if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ [ ${TARGET_BINARY_INTERFACE}x = x ] then echo m88k-dg-dgux${UNAME_RELEASE} else echo m88k-dg-dguxbcs${UNAME_RELEASE} fi else echo i586-dg-dgux${UNAME_RELEASE} fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` then echo "$SYSTEM_NAME" else echo rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/lslpp ] ; then IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${IBM_ARCH}-ibm-aix${IBM_REV} exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; ibmrt:4.4BSD:*|romp-ibm:BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` case "${UNAME_MACHINE}" in 9000/31? ) HP_ARCH=m68000 ;; 9000/[34]?? ) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "${sc_cpu_version}" in 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "${sc_kernel_bits}" in 32) HP_ARCH=hppa2.0n ;; 64) HP_ARCH=hppa2.0w ;; '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 esac ;; esac fi if [ "${HP_ARCH}" = "" ]; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS="" $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ ${HP_ARCH} = hppa2.0w ] then eval $set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH=hppa2.0w else HP_ARCH=hppa64 fi fi echo ${HP_ARCH}-hp-hpux${HPUX_REV} exit ;; ia64:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux${HPUX_REV} exit ;; 3050*:HI-UX:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo ${UNAME_MACHINE}-unknown-osf1mk else echo ${UNAME_MACHINE}-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} exit ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi${UNAME_RELEASE} exit ;; *:BSD/OS:*:*) echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} exit ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case ${UNAME_PROCESSOR} in amd64) UNAME_PROCESSOR=x86_64 ;; i386) UNAME_PROCESSOR=i586 ;; esac echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit ;; i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin exit ;; *:MINGW64*:*) echo ${UNAME_MACHINE}-pc-mingw64 exit ;; *:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit ;; *:MSYS*:*) echo ${UNAME_MACHINE}-pc-msys exit ;; i*:windows32*:*) # uname -m includes "-pc" on this system. echo ${UNAME_MACHINE}-mingw32 exit ;; i*:PW*:*) echo ${UNAME_MACHINE}-pc-pw32 exit ;; *:Interix*:*) case ${UNAME_MACHINE} in x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; authenticamd | genuineintel | EM64T) echo x86_64-unknown-interix${UNAME_RELEASE} exit ;; IA64) echo ia64-unknown-interix${UNAME_RELEASE} exit ;; esac ;; [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) echo i${UNAME_MACHINE}-pc-mks exit ;; 8664:Windows_NT:*) echo x86_64-pc-mks exit ;; i*:Windows_NT*:* | Pentium*:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we # UNAME_MACHINE based on the output of uname instead of i386? echo i586-pc-interix exit ;; i*:UWIN*:*) echo ${UNAME_MACHINE}-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-unknown-cygwin exit ;; p*:CYGWIN*:*) echo powerpcle-unknown-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; *:GNU:*:*) # the GNU system echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} exit ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit ;; aarch64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC=gnulibc1 ; fi echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arc:Linux:*:* | arceb:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arm*:Linux:*:*) eval $set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then echo ${UNAME_MACHINE}-unknown-linux-${LIBC} else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi else echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf fi fi exit ;; avr32*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; cris:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; crisv32:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; e2k:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; frv:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; hexagon:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:Linux:*:*) echo ${UNAME_MACHINE}-pc-linux-${LIBC} exit ;; ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; k1om:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m32r*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m68*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; mips:Linux:*:* | mips64:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU #undef ${UNAME_MACHINE} #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=${UNAME_MACHINE} #else CPU= #endif #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } ;; mips64el:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; openrisc*:Linux:*:*) echo or1k-unknown-linux-${LIBC} exit ;; or32:Linux:*:* | or1k*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; padre:Linux:*:*) echo sparc-unknown-linux-${LIBC} exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-${LIBC} exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; *) echo hppa-unknown-linux-${LIBC} ;; esac exit ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-${LIBC} exit ;; ppc:Linux:*:*) echo powerpc-unknown-linux-${LIBC} exit ;; ppc64le:Linux:*:*) echo powerpc64le-unknown-linux-${LIBC} exit ;; ppcle:Linux:*:*) echo powerpcle-unknown-linux-${LIBC} exit ;; riscv32:Linux:*:* | riscv64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux-${LIBC} exit ;; sh64*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sh*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; tile*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; vax:Linux:*:*) echo ${UNAME_MACHINE}-dec-linux-${LIBC} exit ;; x86_64:Linux:*:*) echo ${UNAME_MACHINE}-pc-linux-${LIBC} exit ;; xtensa*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo ${UNAME_MACHINE}-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) echo ${UNAME_MACHINE}-unknown-stop exit ;; i*86:atheos:*:*) echo ${UNAME_MACHINE}-unknown-atheos exit ;; i*86:syllable:*:*) echo ${UNAME_MACHINE}-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-unknown-lynxos${UNAME_RELEASE} exit ;; i*86:*DOS:*:*) echo ${UNAME_MACHINE}-pc-msdosdjgpp exit ;; i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} else echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} fi exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo ${UNAME_MACHINE}-pc-sco$UNAME_REL else echo ${UNAME_MACHINE}-pc-sysv32 fi exit ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configure will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit ;; paragon:*:*:*) echo i860-intel-osf1 exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos${UNAME_RELEASE} exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos${UNAME_RELEASE} exit ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos${UNAME_RELEASE} exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-unknown-lynxos${UNAME_RELEASE} exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv${UNAME_RELEASE} exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo ${UNAME_MACHINE}-sni-sysv4 else echo ns32k-sni-sysv fi exit ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo ${UNAME_MACHINE}-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) echo m68k-apple-aux${UNAME_RELEASE} exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv${UNAME_RELEASE} else echo mips-unknown-sysv${UNAME_RELEASE} fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; x86_64:Haiku:*:*) echo x86_64-unknown-haiku exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux${UNAME_RELEASE} exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux${UNAME_RELEASE} exit ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux${UNAME_RELEASE} exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux${UNAME_RELEASE} exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux${UNAME_RELEASE} exit ;; SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux${UNAME_RELEASE} exit ;; SX-ACE:SUPER-UX:*:*) echo sxace-nec-superux${UNAME_RELEASE} exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody${UNAME_RELEASE} exit ;; *:Rhapsody:*:*) echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown eval $set_cc_for_build if test "$UNAME_PROCESSOR" = unknown ; then UNAME_PROCESSOR=powerpc fi if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then if [ "$CC_FOR_BUILD" != no_compiler_found ]; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_PPC >/dev/null then UNAME_PROCESSOR=powerpc fi fi elif test "$UNAME_PROCESSOR" = i386 ; then # Avoid executing cc on OS X 10.9, as it ships with a stub # that puts up a graphical alert prompting to install # developer tools. Any system running Mac OS X 10.7 or # later (Darwin 11 and later) is required to have a 64-bit # processor. This is not true of the ARM version of Darwin # that Apple uses in portable devices. UNAME_PROCESSOR=x86_64 fi echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = x86; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; NEO-*:NONSTOP_KERNEL:*:*) echo neo-tandem-nsk${UNAME_RELEASE} exit ;; NSE-*:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk${UNAME_RELEASE} exit ;; NSR-*:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk${UNAME_RELEASE} exit ;; NSX-*:NONSTOP_KERNEL:*:*) echo nsx-tandem-nsk${UNAME_RELEASE} exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "$cputype" = 386; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo ${UNAME_MACHINE}-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit ;; *:ITS:*:*) echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux${UNAME_RELEASE} exit ;; *:DragonFly:*:*) echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "${UNAME_MACHINE}" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit ;; i*86:skyos:*:*) echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE} | sed -e 's/ .*$//'` exit ;; i*86:rdos:*:*) echo ${UNAME_MACHINE}-pc-rdos exit ;; i*86:AROS:*:*) echo ${UNAME_MACHINE}-pc-aros exit ;; x86_64:VMkernel:*:*) echo ${UNAME_MACHINE}-unknown-esx exit ;; amd64:Isilon\ OneFS:*:*) echo x86_64-unknown-onefs exit ;; esac cat >&2 </dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = ${UNAME_MACHINE} UNAME_RELEASE = ${UNAME_RELEASE} UNAME_SYSTEM = ${UNAME_SYSTEM} UNAME_VERSION = ${UNAME_VERSION} EOF exit 1 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: tmux-3.0a/etc/config.sub100755 001750 001750 00000107243 13570677632 0011001#! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2017 Free Software Foundation, Inc. timestamp='2017-04-02' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # Please send patches to . # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS Canonicalize a configuration name. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright 1992-2017 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" exit 1 ;; *local*) # First pass through any local machine types. echo $1 exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). # Here we must recognize all the valid KERNEL-OS combinations. maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \ kopensolaris*-gnu* | cloudabi*-eabi* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` ;; android-linux) os=-linux-android basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown ;; *) basic_machine=`echo $1 | sed 's/-[^-]*$//'` if [ $basic_machine != $1 ] then os=`echo $1 | sed 's/.*-/-/'` else os=; fi ;; esac ### Let's recognize common machines as not being operating systems so ### that things like config.sub decstation-3100 work. We also ### recognize some manufacturers as not being operating systems, so we ### can provide default operating systems below. case $os in -sun*os*) # Prevent following clause from handling this invalid input. ;; -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ -apple | -axis | -knuth | -cray | -microblaze*) os= basic_machine=$1 ;; -bluegene*) os=-cnk ;; -sim | -cisco | -oki | -wec | -winbond) os= basic_machine=$1 ;; -scout) ;; -wrs) os=-vxworks basic_machine=$1 ;; -chorusos*) os=-chorusos basic_machine=$1 ;; -chorusrdb) os=-chorusrdb basic_machine=$1 ;; -hiux*) os=-hiuxwe2 ;; -sco6) os=-sco5v6 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5) os=-sco3.2v5 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco4) os=-sco3.2v4 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2.[4-9]*) os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2v[4-9]*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5v6*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco*) os=-sco3.2v2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -udk*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -isc) os=-isc2.2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -clix*) basic_machine=clipper-intergraph ;; -isc*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -lynx*178) os=-lynxos178 ;; -lynx*5) os=-lynxos5 ;; -lynx*) os=-lynxos ;; -ptx*) basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` ;; -windowsnt*) os=`echo $os | sed -e 's/windowsnt/winnt/'` ;; -psos*) os=-psos ;; -mint | -mint[0-9]*) basic_machine=m68k-atari os=-mint ;; esac # Decode aliases for certain CPU-COMPANY combinations. case $basic_machine in # Recognize the basic CPU types without company name. # Some are omitted here because they have special meanings below. 1750a | 580 \ | a29k \ | aarch64 | aarch64_be \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ | arc | arceb \ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ | avr | avr32 \ | ba \ | be32 | be64 \ | bfin \ | c4x | c8051 | clipper \ | d10v | d30v | dlx | dsp16xx \ | e2k | epiphany \ | fido | fr30 | frv | ft32 \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ | i370 | i860 | i960 | ia16 | ia64 \ | ip2k | iq2000 \ | k1om \ | le32 | le64 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ | mips64octeon | mips64octeonel \ | mips64orion | mips64orionel \ | mips64r5900 | mips64r5900el \ | mips64vr | mips64vrel \ | mips64vr4100 | mips64vr4100el \ | mips64vr4300 | mips64vr4300el \ | mips64vr5000 | mips64vr5000el \ | mips64vr5900 | mips64vr5900el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ | mipsisa32r6 | mipsisa32r6el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ | mipsisa64r6 | mipsisa64r6el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ | mipsr5900 | mipsr5900el \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ | moxie \ | mt \ | msp430 \ | nds32 | nds32le | nds32be \ | nios | nios2 | nios2eb | nios2el \ | ns16k | ns32k \ | open8 | or1k | or1knd | or32 \ | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ | pru \ | pyramid \ | riscv32 | riscv64 \ | rl78 | rx \ | score \ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ | spu \ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ | ubicom32 \ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ | visium \ | wasm32 \ | we32k \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) basic_machine=$basic_machine-unknown ;; c54x) basic_machine=tic54x-unknown ;; c55x) basic_machine=tic55x-unknown ;; c6x) basic_machine=tic6x-unknown ;; leon|leon[3-9]) basic_machine=sparc-$basic_machine ;; m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) basic_machine=$basic_machine-unknown os=-none ;; m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) ;; ms1) basic_machine=mt-unknown ;; strongarm | thumb | xscale) basic_machine=arm-unknown ;; xgate) basic_machine=$basic_machine-unknown os=-none ;; xscaleeb) basic_machine=armeb-unknown ;; xscaleel) basic_machine=armel-unknown ;; # We use `pc' rather than `unknown' # because (1) that's what they normally are, and # (2) the word "unknown" tends to confuse beginning users. i*86 | x86_64) basic_machine=$basic_machine-pc ;; # Object if more than one company name word. *-*-*) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; # Recognize the basic CPU types with company name. 580-* \ | a29k-* \ | aarch64-* | aarch64_be-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ | ba-* \ | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ | c8051-* | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | e2k-* | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | hexagon-* \ | i*86-* | i860-* | i960-* | ia16-* | ia64-* \ | ip2k-* | iq2000-* \ | k1om-* \ | le32-* | le64-* \ | lm32-* \ | m32c-* | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ | microblaze-* | microblazeel-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ | mips64octeon-* | mips64octeonel-* \ | mips64orion-* | mips64orionel-* \ | mips64r5900-* | mips64r5900el-* \ | mips64vr-* | mips64vrel-* \ | mips64vr4100-* | mips64vr4100el-* \ | mips64vr4300-* | mips64vr4300el-* \ | mips64vr5000-* | mips64vr5000el-* \ | mips64vr5900-* | mips64vr5900el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ | mipsisa32r6-* | mipsisa32r6el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ | mipsisa64r6-* | mipsisa64r6el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ | mipsr5900-* | mipsr5900el-* \ | mipstx39-* | mipstx39el-* \ | mmix-* \ | mt-* \ | msp430-* \ | nds32-* | nds32le-* | nds32be-* \ | nios-* | nios2-* | nios2eb-* | nios2el-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | open8-* \ | or1k*-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ | pru-* \ | pyramid-* \ | riscv32-* | riscv64-* \ | rl78-* | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ | sparclite-* \ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \ | tahoe-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tile*-* \ | tron-* \ | ubicom32-* \ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ | vax-* \ | visium-* \ | wasm32-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* \ | xstormy16-* | xtensa*-* \ | ymp-* \ | z8k-* | z80-*) ;; # Recognize the basic CPU types without company name, with glob match. xtensa*) basic_machine=$basic_machine-unknown ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 386bsd) basic_machine=i386-unknown os=-bsd ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) basic_machine=m68000-att ;; 3b*) basic_machine=we32k-att ;; a29khif) basic_machine=a29k-amd os=-udi ;; abacus) basic_machine=abacus-unknown ;; adobe68k) basic_machine=m68010-adobe os=-scout ;; alliant | fx80) basic_machine=fx80-alliant ;; altos | altos3068) basic_machine=m68k-altos ;; am29k) basic_machine=a29k-none os=-bsd ;; amd64) basic_machine=x86_64-pc ;; amd64-*) basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; amdahl) basic_machine=580-amdahl os=-sysv ;; amiga | amiga-*) basic_machine=m68k-unknown ;; amigaos | amigados) basic_machine=m68k-unknown os=-amigaos ;; amigaunix | amix) basic_machine=m68k-unknown os=-sysv4 ;; apollo68) basic_machine=m68k-apollo os=-sysv ;; apollo68bsd) basic_machine=m68k-apollo os=-bsd ;; aros) basic_machine=i386-pc os=-aros ;; asmjs) basic_machine=asmjs-unknown ;; aux) basic_machine=m68k-apple os=-aux ;; balance) basic_machine=ns32k-sequent os=-dynix ;; blackfin) basic_machine=bfin-unknown os=-linux ;; blackfin-*) basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; bluegene*) basic_machine=powerpc-ibm os=-cnk ;; c54x-*) basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c55x-*) basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c6x-*) basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c90) basic_machine=c90-cray os=-unicos ;; cegcc) basic_machine=arm-unknown os=-cegcc ;; convex-c1) basic_machine=c1-convex os=-bsd ;; convex-c2) basic_machine=c2-convex os=-bsd ;; convex-c32) basic_machine=c32-convex os=-bsd ;; convex-c34) basic_machine=c34-convex os=-bsd ;; convex-c38) basic_machine=c38-convex os=-bsd ;; cray | j90) basic_machine=j90-cray os=-unicos ;; craynv) basic_machine=craynv-cray os=-unicosmp ;; cr16 | cr16-*) basic_machine=cr16-unknown os=-elf ;; crds | unos) basic_machine=m68k-crds ;; crisv32 | crisv32-* | etraxfs*) basic_machine=crisv32-axis ;; cris | cris-* | etrax*) basic_machine=cris-axis ;; crx) basic_machine=crx-unknown os=-elf ;; da30 | da30-*) basic_machine=m68k-da30 ;; decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) basic_machine=mips-dec ;; decsystem10* | dec10*) basic_machine=pdp10-dec os=-tops10 ;; decsystem20* | dec20*) basic_machine=pdp10-dec os=-tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) basic_machine=m68k-motorola ;; delta88) basic_machine=m88k-motorola os=-sysv3 ;; dicos) basic_machine=i686-pc os=-dicos ;; djgpp) basic_machine=i586-pc os=-msdosdjgpp ;; dpx20 | dpx20-*) basic_machine=rs6000-bull os=-bosx ;; dpx2* | dpx2*-bull) basic_machine=m68k-bull os=-sysv3 ;; e500v[12]) basic_machine=powerpc-unknown os=$os"spe" ;; e500v[12]-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` os=$os"spe" ;; ebmon29k) basic_machine=a29k-amd os=-ebmon ;; elxsi) basic_machine=elxsi-elxsi os=-bsd ;; encore | umax | mmax) basic_machine=ns32k-encore ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson os=-ose ;; fx2800) basic_machine=i860-alliant ;; genix) basic_machine=ns32k-ns ;; gmicro) basic_machine=tron-gmicro os=-sysv ;; go32) basic_machine=i386-pc os=-go32 ;; h3050r* | hiux*) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; h8300hms) basic_machine=h8300-hitachi os=-hms ;; h8300xray) basic_machine=h8300-hitachi os=-xray ;; h8500hms) basic_machine=h8500-hitachi os=-hms ;; harris) basic_machine=m88k-harris os=-sysv3 ;; hp300-*) basic_machine=m68k-hp ;; hp300bsd) basic_machine=m68k-hp os=-bsd ;; hp300hpux) basic_machine=m68k-hp os=-hpux ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) basic_machine=m68000-hp ;; hp9k3[2-9][0-9]) basic_machine=m68k-hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) basic_machine=hppa1.1-hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) basic_machine=hppa1.1-hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) basic_machine=hppa1.0-hp ;; hppa-next) os=-nextstep3 ;; hppaosf) basic_machine=hppa1.1-hp os=-osf ;; hppro) basic_machine=hppa1.1-hp os=-proelf ;; i370-ibm* | ibm*) basic_machine=i370-ibm ;; i*86v32) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv32 ;; i*86v4*) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv4 ;; i*86v) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv ;; i*86sol2) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-solaris2 ;; i386mach) basic_machine=i386-mach os=-mach ;; i386-vsta | vsta) basic_machine=i386-unknown os=-vsta ;; iris | iris4d) basic_machine=mips-sgi case $os in -irix*) ;; *) os=-irix4 ;; esac ;; isi68 | isi) basic_machine=m68k-isi os=-sysv ;; leon-*|leon[3-9]-*) basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'` ;; m68knommu) basic_machine=m68k-unknown os=-linux ;; m68knommu-*) basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; m88k-omron*) basic_machine=m88k-omron ;; magnum | m3230) basic_machine=mips-mips os=-sysv ;; merlin) basic_machine=ns32k-utek os=-sysv ;; microblaze*) basic_machine=microblaze-xilinx ;; mingw64) basic_machine=x86_64-pc os=-mingw64 ;; mingw32) basic_machine=i686-pc os=-mingw32 ;; mingw32ce) basic_machine=arm-unknown os=-mingw32ce ;; miniframe) basic_machine=m68000-convergent ;; *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) basic_machine=m68k-atari os=-mint ;; mips3*-*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` ;; mips3*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown ;; monitor) basic_machine=m68k-rom68k os=-coff ;; morphos) basic_machine=powerpc-unknown os=-morphos ;; moxiebox) basic_machine=moxie-unknown os=-moxiebox ;; msdos) basic_machine=i386-pc os=-msdos ;; ms1-*) basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` ;; msys) basic_machine=i686-pc os=-msys ;; mvs) basic_machine=i370-ibm os=-mvs ;; nacl) basic_machine=le32-unknown os=-nacl ;; ncr3000) basic_machine=i486-ncr os=-sysv4 ;; netbsd386) basic_machine=i386-unknown os=-netbsd ;; netwinder) basic_machine=armv4l-rebel os=-linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony os=-newsos ;; news1000) basic_machine=m68030-sony os=-newsos ;; news-3600 | risc-news) basic_machine=mips-sony os=-newsos ;; necv70) basic_machine=v70-nec os=-sysv ;; next | m*-next ) basic_machine=m68k-next case $os in -nextstep* ) ;; -ns2*) os=-nextstep2 ;; *) os=-nextstep3 ;; esac ;; nh3000) basic_machine=m68k-harris os=-cxux ;; nh[45]000) basic_machine=m88k-harris os=-cxux ;; nindy960) basic_machine=i960-intel os=-nindy ;; mon960) basic_machine=i960-intel os=-mon960 ;; nonstopux) basic_machine=mips-compaq os=-nonstopux ;; np1) basic_machine=np1-gould ;; neo-tandem) basic_machine=neo-tandem ;; nse-tandem) basic_machine=nse-tandem ;; nsr-tandem) basic_machine=nsr-tandem ;; nsx-tandem) basic_machine=nsx-tandem ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki os=-proelf ;; openrisc | openrisc-*) basic_machine=or32-unknown ;; os400) basic_machine=powerpc-ibm os=-os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson os=-ose ;; os68k) basic_machine=m68k-none os=-os68k ;; pa-hitachi) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; paragon) basic_machine=i860-intel os=-osf ;; parisc) basic_machine=hppa-unknown os=-linux ;; parisc-*) basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; pbd) basic_machine=sparc-tti ;; pbb) basic_machine=m68k-tti ;; pc532 | pc532-*) basic_machine=ns32k-pc532 ;; pc98) basic_machine=i386-pc ;; pc98-*) basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium | p5 | k5 | k6 | nexgen | viac3) basic_machine=i586-pc ;; pentiumpro | p6 | 6x86 | athlon | athlon_*) basic_machine=i686-pc ;; pentiumii | pentium2 | pentiumiii | pentium3) basic_machine=i686-pc ;; pentium4) basic_machine=i786-pc ;; pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumpro-* | p6-* | 6x86-* | athlon-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium4-*) basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pn) basic_machine=pn-gould ;; power) basic_machine=power-ibm ;; ppc | ppcbe) basic_machine=powerpc-unknown ;; ppc-* | ppcbe-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppcle | powerpclittle) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64) basic_machine=powerpc64-unknown ;; ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64le | powerpc64little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ps2) basic_machine=i386-ibm ;; pw32) basic_machine=i586-unknown os=-pw32 ;; rdos | rdos64) basic_machine=x86_64-pc os=-rdos ;; rdos32) basic_machine=i386-pc os=-rdos ;; rom68k) basic_machine=m68k-rom68k os=-coff ;; rm[46]00) basic_machine=mips-siemens ;; rtpc | rtpc-*) basic_machine=romp-ibm ;; s390 | s390-*) basic_machine=s390-ibm ;; s390x | s390x-*) basic_machine=s390x-ibm ;; sa29200) basic_machine=a29k-amd os=-udi ;; sb1) basic_machine=mipsisa64sb1-unknown ;; sb1el) basic_machine=mipsisa64sb1el-unknown ;; sde) basic_machine=mipsisa32-sde os=-elf ;; sei) basic_machine=mips-sei os=-seiux ;; sequent) basic_machine=i386-sequent ;; sh) basic_machine=sh-hitachi os=-hms ;; sh5el) basic_machine=sh5le-unknown ;; sh64) basic_machine=sh64-unknown ;; sparclite-wrs | simso-wrs) basic_machine=sparclite-wrs os=-vxworks ;; sps7) basic_machine=m68k-bull os=-sysv2 ;; spur) basic_machine=spur-unknown ;; st2000) basic_machine=m68k-tandem ;; stratus) basic_machine=i860-stratus os=-sysv4 ;; strongarm-* | thumb-*) basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` ;; sun2) basic_machine=m68000-sun ;; sun2os3) basic_machine=m68000-sun os=-sunos3 ;; sun2os4) basic_machine=m68000-sun os=-sunos4 ;; sun3os3) basic_machine=m68k-sun os=-sunos3 ;; sun3os4) basic_machine=m68k-sun os=-sunos4 ;; sun4os3) basic_machine=sparc-sun os=-sunos3 ;; sun4os4) basic_machine=sparc-sun os=-sunos4 ;; sun4sol2) basic_machine=sparc-sun os=-solaris2 ;; sun3 | sun3-*) basic_machine=m68k-sun ;; sun4) basic_machine=sparc-sun ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun ;; sv1) basic_machine=sv1-cray os=-unicos ;; symmetry) basic_machine=i386-sequent os=-dynix ;; t3e) basic_machine=alphaev5-cray os=-unicos ;; t90) basic_machine=t90-cray os=-unicos ;; tile*) basic_machine=$basic_machine-unknown os=-linux-gnu ;; tx39) basic_machine=mipstx39-unknown ;; tx39el) basic_machine=mipstx39el-unknown ;; toad1) basic_machine=pdp10-xkl os=-tops20 ;; tower | tower-32) basic_machine=m68k-ncr ;; tpf) basic_machine=s390x-ibm os=-tpf ;; udi29k) basic_machine=a29k-amd os=-udi ;; ultra3) basic_machine=a29k-nyu os=-sym1 ;; v810 | necv810) basic_machine=v810-nec os=-none ;; vaxv) basic_machine=vax-dec os=-sysv ;; vms) basic_machine=vax-dec os=-vms ;; vpp*|vx|vx-*) basic_machine=f301-fujitsu ;; vxworks960) basic_machine=i960-wrs os=-vxworks ;; vxworks68) basic_machine=m68k-wrs os=-vxworks ;; vxworks29k) basic_machine=a29k-wrs os=-vxworks ;; wasm32) basic_machine=wasm32-unknown ;; w65*) basic_machine=w65-wdc os=-none ;; w89k-*) basic_machine=hppa1.1-winbond os=-proelf ;; xbox) basic_machine=i686-pc os=-mingw32 ;; xps | xps100) basic_machine=xps100-honeywell ;; xscale-* | xscalee[bl]-*) basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` ;; ymp) basic_machine=ymp-cray os=-unicos ;; z8k-*-coff) basic_machine=z8k-unknown os=-sim ;; z80-*-coff) basic_machine=z80-unknown os=-sim ;; none) basic_machine=none-none os=-none ;; # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) basic_machine=hppa1.1-winbond ;; op50n) basic_machine=hppa1.1-oki ;; op60c) basic_machine=hppa1.1-oki ;; romp) basic_machine=romp-ibm ;; mmix) basic_machine=mmix-knuth ;; rs6000) basic_machine=rs6000-ibm ;; vax) basic_machine=vax-dec ;; pdp10) # there are many clones, so DEC is not a safe bet basic_machine=pdp10-unknown ;; pdp11) basic_machine=pdp11-dec ;; we32k) basic_machine=we32k-att ;; sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) basic_machine=sparc-sun ;; cydra) basic_machine=cydra-cydrome ;; orion) basic_machine=orion-highlevel ;; orion105) basic_machine=clipper-highlevel ;; mac | mpw | mac-mpw) basic_machine=m68k-apple ;; pmac | pmac-mpw) basic_machine=powerpc-apple ;; *-unknown) # Make sure to match an already-canonicalized machine name. ;; *) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; esac # Here we canonicalize certain aliases for manufacturers. case $basic_machine in *-digital*) basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` ;; *-commodore*) basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if [ x"$os" != x"" ] then case $os in # First match some system type aliases # that might get confused with valid system types. # -solaris* is a basic system type, with this one exception. -auroraux) os=-auroraux ;; -solaris1 | -solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; -solaris) os=-solaris2 ;; -svr4*) os=-sysv4 ;; -unixware*) os=-sysv4.2uw ;; -gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; # First accept the basic system types. # The portable systems comes first. # Each alternative MUST END IN A *, to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ | -sym* | -kopensolaris* | -plan9* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* | -aros* | -cloudabi* | -sortix* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | -chorusos* | -chorusrdb* | -cegcc* | -glidix* \ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ | -linux-newlib* | -linux-musl* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \ | -onefs* | -tirtos* | -phoenix* | -fuchsia* | -redox*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) case $basic_machine in x86-* | i*86-*) ;; *) os=-nto$os ;; esac ;; -nto-qnx*) ;; -nto*) os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) ;; -mac*) os=`echo $os | sed -e 's|mac|macos|'` ;; -linux-dietlibc) os=-linux-dietlibc ;; -linux*) os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; -sunos5*) os=`echo $os | sed -e 's|sunos5|solaris2|'` ;; -sunos6*) os=`echo $os | sed -e 's|sunos6|solaris3|'` ;; -opened*) os=-openedition ;; -os400*) os=-os400 ;; -wince*) os=-wince ;; -osfrose*) os=-osfrose ;; -osf*) os=-osf ;; -utek*) os=-bsd ;; -dynix*) os=-bsd ;; -acis*) os=-aos ;; -atheos*) os=-atheos ;; -syllable*) os=-syllable ;; -386bsd) os=-bsd ;; -ctix* | -uts*) os=-sysv ;; -nova*) os=-rtmk-nova ;; -ns2 ) os=-nextstep2 ;; -nsk*) os=-nsk ;; # Preserve the version number of sinix5. -sinix5.*) os=`echo $os | sed -e 's|sinix|sysv|'` ;; -sinix*) os=-sysv4 ;; -tpf*) os=-tpf ;; -triton*) os=-sysv3 ;; -oss*) os=-sysv3 ;; -svr4) os=-sysv4 ;; -svr3) os=-sysv3 ;; -sysvr4) os=-sysv4 ;; # This must come after -sysvr4. -sysv*) ;; -ose*) os=-ose ;; -es1800*) os=-ose ;; -xenix) os=-xenix ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) os=-mint ;; -aros*) os=-aros ;; -zvmoe) os=-zvmoe ;; -dicos*) os=-dicos ;; -nacl*) ;; -ios) ;; -none) ;; *) # Get rid of the `-' at the beginning of $os. os=`echo $os | sed 's/[^-]*-//'` echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 exit 1 ;; esac else # Here we handle the default operating systems that come with various machines. # The value should be what the vendor currently ships out the door with their # machine or put another way, the most popular os provided with the machine. # Note that if you're going to try to match "-MANUFACTURER" here (say, # "-sun"), then you have to tell the case statement up towards the top # that MANUFACTURER isn't an operating system. Otherwise, code above # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. case $basic_machine in score-*) os=-elf ;; spu-*) os=-elf ;; *-acorn) os=-riscix1.2 ;; arm*-rebel) os=-linux ;; arm*-semi) os=-aout ;; c4x-* | tic4x-*) os=-coff ;; c8051-*) os=-elf ;; hexagon-*) os=-elf ;; tic54x-*) os=-coff ;; tic55x-*) os=-coff ;; tic6x-*) os=-coff ;; # This must come before the *-dec entry. pdp10-*) os=-tops20 ;; pdp11-*) os=-none ;; *-dec | vax-*) os=-ultrix4.2 ;; m68*-apollo) os=-domain ;; i386-sun) os=-sunos4.0.2 ;; m68000-sun) os=-sunos3 ;; m68*-cisco) os=-aout ;; mep-*) os=-elf ;; mips*-cisco) os=-elf ;; mips*-*) os=-elf ;; or32-*) os=-coff ;; *-tti) # must be before sparc entry or we get the wrong os. os=-sysv3 ;; sparc-* | *-sun) os=-sunos4.1.1 ;; pru-*) os=-elf ;; *-be) os=-beos ;; *-haiku) os=-haiku ;; *-ibm) os=-aix ;; *-knuth) os=-mmixware ;; *-wec) os=-proelf ;; *-winbond) os=-proelf ;; *-oki) os=-proelf ;; *-hp) os=-hpux ;; *-hitachi) os=-hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) os=-sysv ;; *-cbm) os=-amigaos ;; *-dg) os=-dgux ;; *-dolphin) os=-sysv3 ;; m68k-ccur) os=-rtu ;; m88k-omron*) os=-luna ;; *-next ) os=-nextstep ;; *-sequent) os=-ptx ;; *-crds) os=-unos ;; *-ns) os=-genix ;; i370-*) os=-mvs ;; *-next) os=-nextstep3 ;; *-gould) os=-sysv ;; *-highlevel) os=-bsd ;; *-encore) os=-bsd ;; *-sgi) os=-irix ;; *-siemens) os=-sysv4 ;; *-masscomp) os=-rtu ;; f30[01]-fujitsu | f700-fujitsu) os=-uxpv ;; *-rom68k) os=-coff ;; *-*bug) os=-coff ;; *-apple) os=-macos ;; *-atari*) os=-mint ;; *) os=-none ;; esac fi # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. vendor=unknown case $basic_machine in *-unknown) case $os in -riscix*) vendor=acorn ;; -sunos*) vendor=sun ;; -cnk*|-aix*) vendor=ibm ;; -beos*) vendor=be ;; -hpux*) vendor=hp ;; -mpeix*) vendor=hp ;; -hiux*) vendor=hitachi ;; -unos*) vendor=crds ;; -dgux*) vendor=dg ;; -luna*) vendor=omron ;; -genix*) vendor=ns ;; -mvs* | -opened*) vendor=ibm ;; -os400*) vendor=ibm ;; -ptx*) vendor=sequent ;; -tpf*) vendor=ibm ;; -vxsim* | -vxworks* | -windiss*) vendor=wrs ;; -aux*) vendor=apple ;; -hms*) vendor=hitachi ;; -mpw* | -macos*) vendor=apple ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) vendor=atari ;; -vos*) vendor=stratus ;; esac basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` ;; esac echo $basic_machine$os exit # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: tmux-3.0a/etc/depcomp100755 001750 001750 00000056017 13570677633 0010376#! /bin/sh # depcomp - compile a program generating dependencies as side-effects scriptversion=2016-01-11.22; # UTC # Copyright (C) 1999-2017 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Alexandre Oliva . case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: depcomp [--help] [--version] PROGRAM [ARGS] Run PROGRAMS ARGS to compile a file, generating dependencies as side-effects. Environment variables: depmode Dependency tracking mode. source Source file read by 'PROGRAMS ARGS'. object Object file output by 'PROGRAMS ARGS'. DEPDIR directory where to store dependencies. depfile Dependency file to output. tmpdepfile Temporary file to use when outputting dependencies. libtool Whether libtool is used (yes/no). Report bugs to . EOF exit $? ;; -v | --v*) echo "depcomp $scriptversion" exit $? ;; esac # Get the directory component of the given path, and save it in the # global variables '$dir'. Note that this directory component will # be either empty or ending with a '/' character. This is deliberate. set_dir_from () { case $1 in */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; *) dir=;; esac } # Get the suffix-stripped basename of the given path, and save it the # global variable '$base'. set_base_from () { base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` } # If no dependency file was actually created by the compiler invocation, # we still have to create a dummy depfile, to avoid errors with the # Makefile "include basename.Plo" scheme. make_dummy_depfile () { echo "#dummy" > "$depfile" } # Factor out some common post-processing of the generated depfile. # Requires the auxiliary global variable '$tmpdepfile' to be set. aix_post_process_depfile () { # If the compiler actually managed to produce a dependency file, # post-process it. if test -f "$tmpdepfile"; then # Each line is of the form 'foo.o: dependency.h'. # Do two passes, one to just change these to # $object: dependency.h # and one to simply output # dependency.h: # which is needed to avoid the deleted-header problem. { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" } > "$depfile" rm -f "$tmpdepfile" else make_dummy_depfile fi } # A tabulation character. tab=' ' # A newline character. nl=' ' # Character ranges might be problematic outside the C locale. # These definitions help. upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ lower=abcdefghijklmnopqrstuvwxyz digits=0123456789 alpha=${upper}${lower} if test -z "$depmode" || test -z "$source" || test -z "$object"; then echo "depcomp: Variables source, object and depmode must be set" 1>&2 exit 1 fi # Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. depfile=${depfile-`echo "$object" | sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} rm -f "$tmpdepfile" # Avoid interferences from the environment. gccflag= dashmflag= # Some modes work just like other modes, but use different flags. We # parameterize here, but still list the modes in the big case below, # to make depend.m4 easier to write. Note that we *cannot* use a case # here, because this file can only contain one case statement. if test "$depmode" = hp; then # HP compiler uses -M and no extra arg. gccflag=-M depmode=gcc fi if test "$depmode" = dashXmstdout; then # This is just like dashmstdout with a different argument. dashmflag=-xM depmode=dashmstdout fi cygpath_u="cygpath -u -f -" if test "$depmode" = msvcmsys; then # This is just like msvisualcpp but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvisualcpp fi if test "$depmode" = msvc7msys; then # This is just like msvc7 but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvc7 fi if test "$depmode" = xlc; then # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. gccflag=-qmakedep=gcc,-MF depmode=gcc fi case "$depmode" in gcc3) ## gcc 3 implements dependency tracking that does exactly what ## we want. Yay! Note: for some reason libtool 1.4 doesn't like ## it if -MD -MP comes after the -MF stuff. Hmm. ## Unfortunately, FreeBSD c89 acceptance of flags depends upon ## the command line argument order; so add the flags where they ## appear in depend2.am. Note that the slowdown incurred here ## affects only configure: in makefiles, %FASTDEP% shortcuts this. for arg do case $arg in -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; *) set fnord "$@" "$arg" ;; esac shift # fnord shift # $arg done "$@" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi mv "$tmpdepfile" "$depfile" ;; gcc) ## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. ## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. ## (see the conditional assignment to $gccflag above). ## There are various ways to get dependency output from gcc. Here's ## why we pick this rather obscure method: ## - Don't want to use -MD because we'd like the dependencies to end ## up in a subdir. Having to rename by hand is ugly. ## (We might end up doing this anyway to support other compilers.) ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like ## -MM, not -M (despite what the docs say). Also, it might not be ## supported by the other compilers which use the 'gcc' depmode. ## - Using -M directly means running the compiler twice (even worse ## than renaming). if test -z "$gccflag"; then gccflag=-MD, fi "$@" -Wp,"$gccflag$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The second -e expression handles DOS-style file names with drive # letters. sed -e 's/^[^:]*: / /' \ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" ## This next piece of magic avoids the "deleted header file" problem. ## The problem is that when a header file which appears in a .P file ## is deleted, the dependency causes make to die (because there is ## typically no way to rebuild the header). We avoid this by adding ## dummy dependencies for each header file. Too bad gcc doesn't do ## this for us directly. ## Some versions of gcc put a space before the ':'. On the theory ## that the space means something, we add a space to the output as ## well. hp depmode also adds that space, but also prefixes the VPATH ## to the object. Take care to not repeat it in the output. ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; sgi) if test "$libtool" = yes; then "$@" "-Wp,-MDupdate,$tmpdepfile" else "$@" -MDupdate "$tmpdepfile" fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files echo "$object : \\" > "$depfile" # Clip off the initial element (the dependent). Don't try to be # clever and replace this with sed code, as IRIX sed won't handle # lines with more than a fixed number of characters (4096 in # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; # the IRIX cc adds comments like '#:fec' to the end of the # dependency line. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ | tr "$nl" ' ' >> "$depfile" echo >> "$depfile" # The second pass generates a dummy entry for each header file. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" ;; xlc) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; aix) # The C for AIX Compiler uses -M and outputs the dependencies # in a .u file. In older versions, this file always lives in the # current directory. Also, the AIX compiler puts '$object:' at the # start of each line; $object doesn't have directory information. # Version 6 uses the directory in both cases. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.u tmpdepfile2=$base.u tmpdepfile3=$dir.libs/$base.u "$@" -Wc,-M else tmpdepfile1=$dir$base.u tmpdepfile2=$dir$base.u tmpdepfile3=$dir$base.u "$@" -M fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done aix_post_process_depfile ;; tcc) # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 # FIXME: That version still under development at the moment of writing. # Make that this statement remains true also for stable, released # versions. # It will wrap lines (doesn't matter whether long or short) with a # trailing '\', as in: # # foo.o : \ # foo.c \ # foo.h \ # # It will put a trailing '\' even on the last line, and will use leading # spaces rather than leading tabs (at least since its commit 0394caf7 # "Emit spaces for -MD"). "$@" -MD -MF "$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. # We have to change lines of the first kind to '$object: \'. sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" # And for each line of the second kind, we have to emit a 'dep.h:' # dummy dependency, to avoid the deleted-header problem. sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" rm -f "$tmpdepfile" ;; ## The order of this option in the case statement is important, since the ## shell code in configure will try each of these formats in the order ## listed in this file. A plain '-MD' option would be understood by many ## compilers, so we must ensure this comes after the gcc and icc options. pgcc) # Portland's C compiler understands '-MD'. # Will always output deps to 'file.d' where file is the root name of the # source file under compilation, even if file resides in a subdirectory. # The object file name does not affect the name of the '.d' file. # pgcc 10.2 will output # foo.o: sub/foo.c sub/foo.h # and will wrap long lines using '\' : # foo.o: sub/foo.c ... \ # sub/foo.h ... \ # ... set_dir_from "$object" # Use the source, not the object, to determine the base name, since # that's sadly what pgcc will do too. set_base_from "$source" tmpdepfile=$base.d # For projects that build the same source file twice into different object # files, the pgcc approach of using the *source* file root name can cause # problems in parallel builds. Use a locking strategy to avoid stomping on # the same $tmpdepfile. lockdir=$base.d-lock trap " echo '$0: caught signal, cleaning up...' >&2 rmdir '$lockdir' exit 1 " 1 2 13 15 numtries=100 i=$numtries while test $i -gt 0; do # mkdir is a portable test-and-set. if mkdir "$lockdir" 2>/dev/null; then # This process acquired the lock. "$@" -MD stat=$? # Release the lock. rmdir "$lockdir" break else # If the lock is being held by a different process, wait # until the winning process is done or we timeout. while test -d "$lockdir" && test $i -gt 0; do sleep 1 i=`expr $i - 1` done fi i=`expr $i - 1` done trap - 1 2 13 15 if test $i -le 0; then echo "$0: failed to acquire lock after $numtries attempts" >&2 echo "$0: check lockdir '$lockdir'" >&2 exit 1 fi if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each line is of the form `foo.o: dependent.h', # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. # Do two passes, one to just change these to # `$object: dependent.h' and one to simply `dependent.h:'. sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this invocation # correctly. Breaking it into two sed invocations is a workaround. sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp2) # The "hp" stanza above does not work with aCC (C++) and HP's ia64 # compilers, which have integrated preprocessors. The correct option # to use with these is +Maked; it writes dependencies to a file named # 'foo.d', which lands next to the object file, wherever that # happens to be. # Much of this is similar to the tru64 case; see comments there. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.d tmpdepfile2=$dir.libs/$base.d "$@" -Wc,+Maked else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d "$@" +Maked fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" do test -f "$tmpdepfile" && break done if test -f "$tmpdepfile"; then sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" # Add 'dependent.h:' lines. sed -ne '2,${ s/^ *// s/ \\*$// s/$/:/ p }' "$tmpdepfile" >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" "$tmpdepfile2" ;; tru64) # The Tru64 compiler uses -MD to generate dependencies as a side # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put # dependencies in 'foo.d' instead, so we check for that too. # Subdirectories are respected. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then # Libtool generates 2 separate objects for the 2 libraries. These # two compilations output dependencies in $dir.libs/$base.o.d and # in $dir$base.o.d. We have to check for both files, because # one of the two compilations can be disabled. We should prefer # $dir$base.o.d over $dir.libs/$base.o.d because the latter is # automatically cleaned when .libs/ is deleted, while ignoring # the former would cause a distcleancheck panic. tmpdepfile1=$dir$base.o.d # libtool 1.5 tmpdepfile2=$dir.libs/$base.o.d # Likewise. tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 "$@" -Wc,-MD else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d tmpdepfile3=$dir$base.d "$@" -MD fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done # Same post-processing that is required for AIX mode. aix_post_process_depfile ;; msvc7) if test "$libtool" = yes; then showIncludes=-Wc,-showIncludes else showIncludes=-showIncludes fi "$@" $showIncludes > "$tmpdepfile" stat=$? grep -v '^Note: including file: ' "$tmpdepfile" if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The first sed program below extracts the file names and escapes # backslashes for cygpath. The second sed program outputs the file # name when reading, but also accumulates all include files in the # hold buffer in order to output them again at the end. This only # works with sed implementations that can handle large buffers. sed < "$tmpdepfile" -n ' /^Note: including file: *\(.*\)/ { s//\1/ s/\\/\\\\/g p }' | $cygpath_u | sort -u | sed -n ' s/ /\\ /g s/\(.*\)/'"$tab"'\1 \\/p s/.\(.*\) \\/\1:/ H $ { s/.*/'"$tab"'/ G p }' >> "$depfile" echo >> "$depfile" # make sure the fragment doesn't end with a backslash rm -f "$tmpdepfile" ;; msvc7msys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; #nosideeffect) # This comment above is used by automake to tell side-effect # dependency tracking mechanisms from slower ones. dashmstdout) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout, regardless of -o. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done test -z "$dashmflag" && dashmflag=-M # Require at least two characters before searching for ':' # in the target name. This is to cope with DOS-style filenames: # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. "$@" $dashmflag | sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" rm -f "$depfile" cat < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this sed invocation # correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; dashXmstdout) # This case only exists to satisfy depend.m4. It is never actually # run, as this mode is specially recognized in the preamble. exit 1 ;; makedepend) "$@" || exit $? # Remove any Libtool call if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # X makedepend shift cleared=no eat=no for arg do case $cleared in no) set ""; shift cleared=yes ;; esac if test $eat = yes; then eat=no continue fi case "$arg" in -D*|-I*) set fnord "$@" "$arg"; shift ;; # Strip any option that makedepend may not understand. Remove # the object too, otherwise makedepend will parse it as a source file. -arch) eat=yes ;; -*|$object) ;; *) set fnord "$@" "$arg"; shift ;; esac done obj_suffix=`echo "$object" | sed 's/^.*\././'` touch "$tmpdepfile" ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" rm -f "$depfile" # makedepend may prepend the VPATH from the source file name to the object. # No need to regex-escape $object, excess matching of '.' is harmless. sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process the last invocation # correctly. Breaking it into two sed invocations is a workaround. sed '1,2d' "$tmpdepfile" \ | tr ' ' "$nl" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" "$tmpdepfile".bak ;; cpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done "$@" -E \ | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ | sed '$ s: \\$::' > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" cat < "$tmpdepfile" >> "$depfile" sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; msvisualcpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi IFS=" " for arg do case "$arg" in -o) shift ;; $object) shift ;; "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") set fnord "$@" shift shift ;; *) set fnord "$@" "$arg" shift shift ;; esac done "$@" -E 2>/dev/null | sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" echo "$tab" >> "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" rm -f "$tmpdepfile" ;; msvcmsys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; none) exec "$@" ;; *) echo "Unknown depmode $depmode" 1>&2 exit 1 ;; esac exit 0 # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: tmux-3.0a/etc/install-sh100755 001750 001750 00000034524 13570677632 0011023#!/bin/sh # install - install a program, script, or datafile scriptversion=2016-01-11.22; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # 'make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. tab=' ' nl=' ' IFS=" $tab$nl" # Set DOITPROG to "echo" to test this script. doit=${DOITPROG-} doit_exec=${doit:-exec} # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_mkdir= # Desired mode of installed file. mode=0755 chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false is_target_a_directory=possibly usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve the last data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -s $stripprog installed files. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -s) stripcmd=$stripprog;; -t) is_target_a_directory=always dst_arg=$2 # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac shift;; -T) is_target_a_directory=never;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done # We allow the use of options -d and -T together, by making -d # take the precedence; this is for compatibility with GNU install. if test -n "$dir_arg"; then if test -n "$dst_arg"; then echo "$0: target directory not allowed when installing a directory." >&2 exit 1 fi fi if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call 'install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then if test $# -gt 1 || test "$is_target_a_directory" = always; then if test ! -d "$dst_arg"; then echo "$0: $dst_arg: Is not a directory." >&2 exit 1 fi fi fi if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names problematic for 'test' and other utilities. case $src in -* | [=\(\)!]) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # If destination is a directory, append the input filename; won't work # if double slashes aren't ignored. if test -d "$dst"; then if test "$is_target_a_directory" = never; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dst=$dstdir/`basename "$src"` dstdir_status=0 else dstdir=`dirname "$dst"` test -d "$dstdir" dstdir_status=$? fi fi obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # Create intermediate dirs using mode 755 as modified by the umask. # This is like FreeBSD 'install' as of 1997-10-28. umask=`umask` case $stripcmd.$umask in # Optimize common cases. *[2367][2367]) mkdir_umask=$umask;; .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; *[0-7]) mkdir_umask=`expr $umask + 22 \ - $umask % 100 % 40 + $umask % 20 \ - $umask % 10 % 4 + $umask % 2 `;; *) mkdir_umask=$umask,go-w;; esac # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false case $umask in *[123567][0-7][0-7]) # POSIX mkdir -p sets u+wx bits regardless of umask, which # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 if (umask $mkdir_umask && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. ls_ld_tmpdir=`ls -ld "$tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/d" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null fi trap '' 0;; esac;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; [-=\(\)!]*) prefix='./';; *) prefix='';; esac oIFS=$IFS IFS=/ set -f set fnord $dstdir shift set +f IFS=$oIFS prefixes= for d do test X"$d" = X && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=$dstdir/_inst.$$_ rmtmp=$dstdir/_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: tmux-3.0a/etc/missing100755 001750 001750 00000015331 13570677632 0010411#! /bin/sh # Common wrapper for a few potentially missing GNU programs. scriptversion=2016-01-11.22; # UTC # Copyright (C) 1996-2017 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard , 1996. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. if test $# -eq 0; then echo 1>&2 "Try '$0 --help' for more information" exit 1 fi case $1 in --is-lightweight) # Used by our autoconf macros to check whether the available missing # script is modern enough. exit 0 ;; --run) # Back-compat with the calling convention used by older automake. shift ;; -h|--h|--he|--hel|--help) echo "\ $0 [OPTION]... PROGRAM [ARGUMENT]... Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due to PROGRAM being missing or too old. Options: -h, --help display this help and exit -v, --version output version information and exit Supported PROGRAM values: aclocal autoconf autoheader autom4te automake makeinfo bison yacc flex lex help2man Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and 'g' are ignored when checking the name. Send bug reports to ." exit $? ;; -v|--v|--ve|--ver|--vers|--versi|--versio|--version) echo "missing $scriptversion (GNU Automake)" exit $? ;; -*) echo 1>&2 "$0: unknown '$1' option" echo 1>&2 "Try '$0 --help' for more information" exit 1 ;; esac # Run the given program, remember its exit status. "$@"; st=$? # If it succeeded, we are done. test $st -eq 0 && exit 0 # Also exit now if we it failed (or wasn't found), and '--version' was # passed; such an option is passed most likely to detect whether the # program is present and works. case $2 in --version|--help) exit $st;; esac # Exit code 63 means version mismatch. This often happens when the user # tries to use an ancient version of a tool on a file that requires a # minimum version. if test $st -eq 63; then msg="probably too old" elif test $st -eq 127; then # Program was missing. msg="missing on your system" else # Program was found and executed, but failed. Give up. exit $st fi perl_URL=http://www.perl.org/ flex_URL=http://flex.sourceforge.net/ gnu_software_URL=http://www.gnu.org/software program_details () { case $1 in aclocal|automake) echo "The '$1' program is part of the GNU Automake package:" echo "<$gnu_software_URL/automake>" echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/autoconf>" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; autoconf|autom4te|autoheader) echo "The '$1' program is part of the GNU Autoconf package:" echo "<$gnu_software_URL/autoconf/>" echo "It also requires GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; esac } give_advice () { # Normalize program name to check for. normalized_program=`echo "$1" | sed ' s/^gnu-//; t s/^gnu//; t s/^g//; t'` printf '%s\n' "'$1' is $msg." configure_deps="'configure.ac' or m4 files included by 'configure.ac'" case $normalized_program in autoconf*) echo "You should only need it if you modified 'configure.ac'," echo "or m4 files included by it." program_details 'autoconf' ;; autoheader*) echo "You should only need it if you modified 'acconfig.h' or" echo "$configure_deps." program_details 'autoheader' ;; automake*) echo "You should only need it if you modified 'Makefile.am' or" echo "$configure_deps." program_details 'automake' ;; aclocal*) echo "You should only need it if you modified 'acinclude.m4' or" echo "$configure_deps." program_details 'aclocal' ;; autom4te*) echo "You might have modified some maintainer files that require" echo "the 'autom4te' program to be rebuilt." program_details 'autom4te' ;; bison*|yacc*) echo "You should only need it if you modified a '.y' file." echo "You may want to install the GNU Bison package:" echo "<$gnu_software_URL/bison/>" ;; lex*|flex*) echo "You should only need it if you modified a '.l' file." echo "You may want to install the Fast Lexical Analyzer package:" echo "<$flex_URL>" ;; help2man*) echo "You should only need it if you modified a dependency" \ "of a man page." echo "You may want to install the GNU Help2man package:" echo "<$gnu_software_URL/help2man/>" ;; makeinfo*) echo "You should only need it if you modified a '.texi' file, or" echo "any other file indirectly affecting the aspect of the manual." echo "You might want to install the Texinfo package:" echo "<$gnu_software_URL/texinfo/>" echo "The spurious makeinfo call might also be the consequence of" echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" echo "want to install GNU make:" echo "<$gnu_software_URL/make/>" ;; *) echo "You might have modified some files without having the proper" echo "tools for further handling them. Check the 'README' file, it" echo "often tells you about the needed prerequisites for installing" echo "this package. You may also peek at any GNU archive site, in" echo "case some other package contains this missing '$1' program." ;; esac } give_advice "$1" | sed -e '1s/^/WARNING: /' \ -e '2,$s/^/ /' >&2 # Propagate the correct exit status (expected to be 127 for a program # not found, 63 for a program that failed due to version mismatch). exit $st # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: tmux-3.0a/etc/ylwrap100755 001750 001750 00000015313 13570677633 0010257#! /bin/sh # ylwrap - wrapper for lex/yacc invocations. scriptversion=2016-01-11.22; # UTC # Copyright (C) 1996-2017 Free Software Foundation, Inc. # # Written by Tom Tromey . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . get_dirname () { case $1 in */*|*\\*) printf '%s\n' "$1" | sed -e 's|\([\\/]\)[^\\/]*$|\1|';; # Otherwise, we want the empty string (not "."). esac } # guard FILE # ---------- # The CPP macro used to guard inclusion of FILE. guard () { printf '%s\n' "$1" \ | sed \ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \ -e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g' \ -e 's/__*/_/g' } # quote_for_sed [STRING] # ---------------------- # Return STRING (or stdin) quoted to be used as a sed pattern. quote_for_sed () { case $# in 0) cat;; 1) printf '%s\n' "$1";; esac \ | sed -e 's|[][\\.*]|\\&|g' } case "$1" in '') echo "$0: No files given. Try '$0 --help' for more information." 1>&2 exit 1 ;; --basedir) basedir=$2 shift 2 ;; -h|--h*) cat <<\EOF Usage: ylwrap [--help|--version] INPUT [OUTPUT DESIRED]... -- PROGRAM [ARGS]... Wrapper for lex/yacc invocations, renaming files as desired. INPUT is the input file OUTPUT is one file PROG generates DESIRED is the file we actually want instead of OUTPUT PROGRAM is program to run ARGS are passed to PROG Any number of OUTPUT,DESIRED pairs may be used. Report bugs to . EOF exit $? ;; -v|--v*) echo "ylwrap $scriptversion" exit $? ;; esac # The input. input=$1 shift # We'll later need for a correct munging of "#line" directives. input_sub_rx=`get_dirname "$input" | quote_for_sed` case $input in [\\/]* | ?:[\\/]*) # Absolute path; do nothing. ;; *) # Relative path. Make it absolute. input=`pwd`/$input ;; esac input_rx=`get_dirname "$input" | quote_for_sed` # Since DOS filename conventions don't allow two dots, # the DOS version of Bison writes out y_tab.c instead of y.tab.c # and y_tab.h instead of y.tab.h. Test to see if this is the case. y_tab_nodot=false if test -f y_tab.c || test -f y_tab.h; then y_tab_nodot=true fi # The parser itself, the first file, is the destination of the .y.c # rule in the Makefile. parser=$1 # A sed program to s/FROM/TO/g for all the FROM/TO so that, for # instance, we rename #include "y.tab.h" into #include "parse.h" # during the conversion from y.tab.c to parse.c. sed_fix_filenames= # Also rename header guards, as Bison 2.7 for instance uses its header # guard in its implementation file. sed_fix_header_guards= while test $# -ne 0; do if test x"$1" = x"--"; then shift break fi from=$1 # Handle y_tab.c and y_tab.h output by DOS if $y_tab_nodot; then case $from in "y.tab.c") from=y_tab.c;; "y.tab.h") from=y_tab.h;; esac fi shift to=$1 shift sed_fix_filenames="${sed_fix_filenames}s|"`quote_for_sed "$from"`"|$to|g;" sed_fix_header_guards="${sed_fix_header_guards}s|"`guard "$from"`"|"`guard "$to"`"|g;" done # The program to run. prog=$1 shift # Make any relative path in $prog absolute. case $prog in [\\/]* | ?:[\\/]*) ;; *[\\/]*) prog=`pwd`/$prog ;; esac dirname=ylwrap$$ do_exit="cd '`pwd`' && rm -rf $dirname > /dev/null 2>&1;"' (exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 mkdir $dirname || exit 1 cd $dirname case $# in 0) "$prog" "$input" ;; *) "$prog" "$@" "$input" ;; esac ret=$? if test $ret -eq 0; then for from in * do to=`printf '%s\n' "$from" | sed "$sed_fix_filenames"` if test -f "$from"; then # If $2 is an absolute path name, then just use that, # otherwise prepend '../'. case $to in [\\/]* | ?:[\\/]*) target=$to;; *) target=../$to;; esac # Do not overwrite unchanged header files to avoid useless # recompilations. Always update the parser itself: it is the # destination of the .y.c rule in the Makefile. Divert the # output of all other files to a temporary file so we can # compare them to existing versions. if test $from != $parser; then realtarget=$target target=tmp-`printf '%s\n' "$target" | sed 's|.*[\\/]||g'` fi # Munge "#line" or "#" directives. Don't let the resulting # debug information point at an absolute srcdir. Use the real # output file name, not yy.lex.c for instance. Adjust the # include guards too. sed -e "/^#/!b" \ -e "s|$input_rx|$input_sub_rx|" \ -e "$sed_fix_filenames" \ -e "$sed_fix_header_guards" \ "$from" >"$target" || ret=$? # Check whether files must be updated. if test "$from" != "$parser"; then if test -f "$realtarget" && cmp -s "$realtarget" "$target"; then echo "$to is unchanged" rm -f "$target" else echo "updating $to" mv -f "$target" "$realtarget" fi fi else # A missing file is only an error for the parser. This is a # blatant hack to let us support using "yacc -d". If -d is not # specified, don't fail when the header file is "missing". if test "$from" = "$parser"; then ret=1 fi fi done fi # Remove the directory. cd .. rm -rf $dirname exit $ret # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: tmux-3.0a/Makefile.am100644 001750 001750 00000010056 13570677043 0010266# Makefile.am # Obvious program stuff. bin_PROGRAMS = tmux CLEANFILES = tmux.1.mdoc tmux.1.man cmd-parse.c # Distribution tarball options. EXTRA_DIST = \ CHANGES README README.ja COPYING example_tmux.conf compat/*.[ch] \ osdep-*.c mdoc2man.awk tmux.1 # Preprocessor flags. AM_CPPFLAGS += @XOPEN_DEFINES@ -DTMUX_CONF="\"$(sysconfdir)/tmux.conf\"" # Additional object files. LDADD = $(LIBOBJS) # Set flags for gcc. if IS_GCC AM_CFLAGS += -std=gnu99 -O2 if IS_DEBUG AM_CFLAGS += -g AM_CFLAGS += -Wno-long-long -Wall -W -Wformat=2 AM_CFLAGS += -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations AM_CFLAGS += -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare AM_CFLAGS += -Wundef -Wbad-function-cast -Winline -Wcast-align AM_CFLAGS += -Wdeclaration-after-statement -Wno-pointer-sign -Wno-attributes AM_CFLAGS += -Wno-unused-result AM_CPPFLAGS += -DDEBUG endif AM_CPPFLAGS += -iquote. endif # Set flags for Solaris. if IS_SUNOS if IS_GCC AM_CPPFLAGS += -D_XPG6 else AM_CPPFLAGS += -D_XPG4_2 endif endif # Set flags for Sun CC. if IS_SUNCC AM_CFLAGS += -erroff=E_EMPTY_DECLARATION endif # Set _LINUX_SOURCE_COMPAT for AIX for malloc(0). if IS_AIX AM_CPPFLAGS += -D_LINUX_SOURCE_COMPAT=1 endif # Set flags for NetBSD. if IS_NETBSD AM_CPPFLAGS += -D_OPENBSD_SOURCE endif # List of sources. dist_tmux_SOURCES = \ alerts.c \ arguments.c \ attributes.c \ cfg.c \ client.c \ cmd-attach-session.c \ cmd-bind-key.c \ cmd-break-pane.c \ cmd-capture-pane.c \ cmd-choose-tree.c \ cmd-command-prompt.c \ cmd-confirm-before.c \ cmd-copy-mode.c \ cmd-detach-client.c \ cmd-display-menu.c \ cmd-display-message.c \ cmd-display-panes.c \ cmd-find-window.c \ cmd-find.c \ cmd-if-shell.c \ cmd-join-pane.c \ cmd-kill-pane.c \ cmd-kill-server.c \ cmd-kill-session.c \ cmd-kill-window.c \ cmd-list-buffers.c \ cmd-list-clients.c \ cmd-list-keys.c \ cmd-list-panes.c \ cmd-list-sessions.c \ cmd-list-windows.c \ cmd-load-buffer.c \ cmd-lock-server.c \ cmd-move-window.c \ cmd-new-session.c \ cmd-new-window.c \ cmd-parse.y \ cmd-paste-buffer.c \ cmd-pipe-pane.c \ cmd-queue.c \ cmd-refresh-client.c \ cmd-rename-session.c \ cmd-rename-window.c \ cmd-resize-pane.c \ cmd-resize-window.c \ cmd-respawn-pane.c \ cmd-respawn-window.c \ cmd-rotate-window.c \ cmd-run-shell.c \ cmd-save-buffer.c \ cmd-select-layout.c \ cmd-select-pane.c \ cmd-select-window.c \ cmd-send-keys.c \ cmd-set-buffer.c \ cmd-set-environment.c \ cmd-set-option.c \ cmd-show-environment.c \ cmd-show-messages.c \ cmd-show-options.c \ cmd-source-file.c \ cmd-split-window.c \ cmd-swap-pane.c \ cmd-swap-window.c \ cmd-switch-client.c \ cmd-unbind-key.c \ cmd-wait-for.c \ cmd.c \ colour.c \ compat.h \ control-notify.c \ control.c \ environ.c \ format.c \ format-draw.c \ grid-view.c \ grid.c \ input-keys.c \ input.c \ job.c \ key-bindings.c \ key-string.c \ layout-custom.c \ layout-set.c \ layout.c \ log.c \ menu.c \ mode-tree.c \ names.c \ notify.c \ options-table.c \ options.c \ paste.c \ proc.c \ regsub.c \ resize.c \ screen-redraw.c \ screen-write.c \ screen.c \ server-client.c \ server-fn.c \ server.c \ session.c \ spawn.c \ status.c \ style.c \ tmux.c \ tmux.h \ tty-acs.c \ tty-keys.c \ tty-term.c \ tty.c \ utf8.c \ window-buffer.c \ window-client.c \ window-clock.c \ window-copy.c \ window-tree.c \ window.c \ xmalloc.c \ xmalloc.h \ xterm-keys.c nodist_tmux_SOURCES = osdep-@PLATFORM@.c # Add compat file for forkpty. if NEED_FORKPTY nodist_tmux_SOURCES += compat/forkpty-@PLATFORM@.c endif # Add compat file for utf8proc. if HAVE_UTF8PROC nodist_tmux_SOURCES += compat/utf8proc.c endif # Install tmux.1 in the right format. install-exec-hook: if test x@MANFORMAT@ = xmdoc; then \ sed -e "s|@SYSCONFDIR@|$(sysconfdir)|g" $(srcdir)/tmux.1 \ >$(srcdir)/tmux.1.mdoc; \ else \ sed -e "s|@SYSCONFDIR@|$(sysconfdir)|g" $(srcdir)/tmux.1| \ $(AWK) -f $(srcdir)/mdoc2man.awk >$(srcdir)/tmux.1.man; \ fi $(mkdir_p) $(DESTDIR)$(mandir)/man1 $(INSTALL_DATA) $(srcdir)/tmux.1.@MANFORMAT@ \ $(DESTDIR)$(mandir)/man1/tmux.1 tmux-3.0a/configure100755 001750 001750 00000727401 13570677647 0010164#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69 for tmux 3.0a. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 as_fn_exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, $0: including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='tmux' PACKAGE_TARNAME='tmux' PACKAGE_VERSION='3.0a' PACKAGE_STRING='tmux 3.0a' PACKAGE_BUGREPORT='' PACKAGE_URL='' ac_config_libobj_dir=compat # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_subst_vars='am__EXEEXT_FALSE am__EXEEXT_TRUE LTLIBOBJS AM_LDFLAGS AM_CFLAGS AM_CPPFLAGS IS_UNKNOWN_FALSE IS_UNKNOWN_TRUE IS_HPUX_FALSE IS_HPUX_TRUE IS_SUNOS_FALSE IS_SUNOS_TRUE IS_OPENBSD_FALSE IS_OPENBSD_TRUE IS_NETBSD_FALSE IS_NETBSD_TRUE IS_FREEBSD_FALSE IS_FREEBSD_TRUE IS_LINUX_FALSE IS_LINUX_TRUE IS_DRAGONFLY_FALSE IS_DRAGONFLY_TRUE IS_DARWIN_FALSE IS_DARWIN_TRUE IS_AIX_FALSE IS_AIX_TRUE PLATFORM MANFORMAT NEED_FORKPTY_FALSE NEED_FORKPTY_TRUE XOPEN_DEFINES HAVE_UTF8PROC_FALSE HAVE_UTF8PROC_TRUE LIBNCURSES_LIBS LIBNCURSES_CFLAGS LIBTINFO_LIBS LIBTINFO_CFLAGS LIBEVENT_LIBS LIBEVENT_CFLAGS LIBOBJS IS_SUNCC_FALSE IS_SUNCC_TRUE IS_GCC_FALSE IS_GCC_TRUE IS_DEBUG_FALSE IS_DEBUG_TRUE PKG_CONFIG_LIBDIR PKG_CONFIG_PATH PKG_CONFIG YFLAGS YACC EGREP GREP CPP am__fastdepCC_FALSE am__fastdepCC_TRUE CCDEPMODE am__nodep AMDEPBACKSLASH AMDEP_FALSE AMDEP_TRUE am__quote am__include DEPDIR OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC host_os host_vendor host_cpu host build_os build_vendor build_cpu build AM_BACKSLASH AM_DEFAULT_VERBOSITY AM_DEFAULT_V AM_V am__untar am__tar AMTAR am__leading_dot SET_MAKE AWK mkdir_p MKDIR_P INSTALL_STRIP_PROGRAM STRIP install_sh MAKEINFO AUTOHEADER AUTOMAKE AUTOCONF ACLOCAL VERSION PACKAGE CYGPATH_W am__isrc INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking enable_silent_rules enable_dependency_tracking enable_debug enable_static enable_utempter enable_utf8proc ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS CPP YACC YFLAGS PKG_CONFIG PKG_CONFIG_PATH PKG_CONFIG_LIBDIR LIBEVENT_CFLAGS LIBEVENT_LIBS LIBTINFO_CFLAGS LIBTINFO_LIBS LIBNCURSES_CFLAGS LIBNCURSES_LIBS' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures tmux 3.0a to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/tmux] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF Program names: --program-prefix=PREFIX prepend PREFIX to installed program names --program-suffix=SUFFIX append SUFFIX to installed program names --program-transform-name=PROGRAM run sed PROGRAM on installed program names System types: --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of tmux 3.0a:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-silent-rules less verbose build output (undo: "make V=1") --disable-silent-rules verbose build output (undo: "make V=0") --enable-dependency-tracking do not reject slow dependency extractors --disable-dependency-tracking speeds up one-time build --enable-debug enable debug build flags --enable-static create a static build --enable-utempter use utempter if it is installed --enable-utf8proc use utf8proc if it is installed Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor YACC The `Yet Another Compiler Compiler' implementation to use. Defaults to the first program found out of: `bison -y', `byacc', `yacc'. YFLAGS The list of arguments that will be passed by default to $YACC. This script will default YFLAGS to the empty string to avoid a default value of `-d' given by some make applications. PKG_CONFIG path to pkg-config utility PKG_CONFIG_PATH directories to add to pkg-config's search path PKG_CONFIG_LIBDIR path overriding pkg-config's built-in search path LIBEVENT_CFLAGS C compiler flags for LIBEVENT, overriding pkg-config LIBEVENT_LIBS linker flags for LIBEVENT, overriding pkg-config LIBTINFO_CFLAGS C compiler flags for LIBTINFO, overriding pkg-config LIBTINFO_LIBS linker flags for LIBTINFO, overriding pkg-config LIBNCURSES_CFLAGS C compiler flags for LIBNCURSES, overriding pkg-config LIBNCURSES_LIBS linker flags for LIBNCURSES, overriding pkg-config Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to the package provider. _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF tmux configure 3.0a generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_cpp # ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists, giving a warning if it cannot be compiled using # the include files in INCLUDES and setting the cache variable VAR # accordingly. ac_fn_c_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if eval \${$3+:} false; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } else # Is the header compilable? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 $as_echo_n "checking $2 usability... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_header_compiler=yes else ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 $as_echo_n "checking $2 presence... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <$2> _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : ac_header_preproc=yes else ac_header_preproc=no fi rm -f conftest.err conftest.i conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( yes:no: ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; no:yes:* ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_mongrel # ac_fn_c_try_run LINENO # ---------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes # that executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then : ac_retval=0 else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main () { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func # ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES # --------------------------------------------- # Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR # accordingly. ac_fn_c_check_decl () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack as_decl_name=`echo $2|sed 's/ *(.*//'` as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 $as_echo_n "checking whether $as_decl_name is declared... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { #ifndef $as_decl_name #ifdef __cplusplus (void) $as_decl_use; #else (void) $as_decl_name; #endif #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_decl cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by tmux $as_me 3.0a, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_aux_dir= for ac_dir in etc "$srcdir"/etc; do if test -f "$ac_dir/install-sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f "$ac_dir/install.sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f "$ac_dir/shtool"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then as_fn_error $? "cannot find install-sh, install.sh, or shtool in etc \"$srcdir\"/etc" "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. am__api_version='1.15' # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. # Reject install programs that cannot install multiple files. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 $as_echo_n "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if ${ac_cv_path_install+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. # Account for people who put trailing slashes in PATH elements. case $as_dir/ in #(( ./ | .// | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir fi if test "${ac_cv_path_install+set}" = set; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 $as_echo "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 $as_echo_n "checking whether build environment is sane... " >&6; } # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[\\\"\#\$\&\'\`$am_lf]*) as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; esac case $srcdir in *[\\\"\#\$\&\'\`$am_lf\ \ ]*) as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$*" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$*" != "X $srcdir/configure conftest.file" \ && test "$*" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". as_fn_error $? "ls -t appears to fail. Make sure there is not a broken alias in your environment" "$LINENO" 5 fi if test "$2" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$2" = conftest.file ) then # Ok. : else as_fn_error $? "newly created file is older than distributed files! Check your system clock" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi rm -f conftest.file test "$program_prefix" != NONE && program_transform_name="s&^&$program_prefix&;$program_transform_name" # Use a double $ so make ignores it. test "$program_suffix" != NONE && program_transform_name="s&\$&$program_suffix&;$program_transform_name" # Double any \ or $. # By default was `s,x,x', remove it if useless. ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; *) MISSING="\${SHELL} $am_aux_dir/missing" ;; esac fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 $as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} fi if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. if test "$cross_compiling" != no; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 $as_echo "$STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_STRIP="strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 $as_echo "$ac_ct_STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 $as_echo_n "checking for a thread-safe mkdir -p... " >&6; } if test -z "$MKDIR_P"; then if ${ac_cv_path_mkdir+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in mkdir gmkdir; do for ac_exec_ext in '' $ac_executable_extensions; do as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( 'mkdir (GNU coreutils) '* | \ 'mkdir (coreutils) '* | \ 'mkdir (fileutils) '4.1*) ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext break 3;; esac done done done IFS=$as_save_IFS fi test -d ./--version && rmdir ./--version if test "${ac_cv_path_mkdir+set}" = set; then MKDIR_P="$ac_cv_path_mkdir -p" else # As a last resort, use the slow shell script. Don't cache a # value for MKDIR_P within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. MKDIR_P="$ac_install_sh -d" fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 $as_echo "$MKDIR_P" >&6; } for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_AWK+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AWK"; then ac_cv_prog_AWK="$AWK" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AWK="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AWK=$ac_cv_prog_AWK if test -n "$AWK"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 $as_echo "$AWK" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$AWK" && break done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 $as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : $as_echo_n "(cached) " >&6 else cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } SET_MAKE= else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null # Check whether --enable-silent-rules was given. if test "${enable_silent_rules+set}" = set; then : enableval=$enable_silent_rules; fi case $enable_silent_rules in # ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=1;; esac am_make=${MAKE-make} { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 $as_echo_n "checking whether $am_make supports nested variables... " >&6; } if ${am_cv_make_support_nested_variables+:} false; then : $as_echo_n "(cached) " >&6 else if $as_echo 'TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 $as_echo "$am_cv_make_support_nested_variables" >&6; } if test $am_cv_make_support_nested_variables = yes; then AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AM_BACKSLASH='\' if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." am__isrc=' -I$(srcdir)' # test to see if srcdir already configured if test -f $srcdir/config.status; then as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi # Define the identity of the package. PACKAGE='tmux' VERSION='3.0a' cat >>confdefs.h <<_ACEOF #define PACKAGE "$PACKAGE" _ACEOF cat >>confdefs.h <<_ACEOF #define VERSION "$VERSION" _ACEOF # Some tools Automake needs. ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # mkdir_p='$(MKDIR_P)' # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. # Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AMTAR='$${TAR-tar}' # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar pax cpio none' am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 fi fi # Make sure we can run config.sub. $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 $as_echo_n "checking build system type... " >&6; } if ${ac_cv_build+:} false; then : $as_echo_n "(cached) " >&6 else ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` test "x$ac_build_alias" = x && as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 $as_echo "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; *) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; esac build=$ac_cv_build ac_save_IFS=$IFS; IFS='-' set x $ac_cv_build shift build_cpu=$1 build_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: build_os=$* IFS=$ac_save_IFS case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 $as_echo_n "checking host system type... " >&6; } if ${ac_cv_host+:} false; then : $as_echo_n "(cached) " >&6 else if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 $as_echo "$ac_cv_host" >&6; } case $ac_cv_host in *-*-*) ;; *) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; esac host=$ac_cv_host ac_save_IFS=$IFS; IFS='-' set x $ac_cv_host shift host_cpu=$1 host_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: host_os=$* IFS=$ac_save_IFS case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac # When CFLAGS isn't set at this stage and gcc is detected by the macro below, # autoconf will automatically use CFLAGS="-O2 -g". Prevent that by using an # empty default. : ${CFLAGS=""} # Save user CPPFLAGS, CFLAGS and LDFLAGS. We need to change them because # AC_CHECK_HEADER doesn't give us any other way to update the include # paths. But for Makefile.am we want to use AM_CPPFLAGS and friends. SAVED_CFLAGS="$CFLAGS" SAVED_CPPFLAGS="$CPPFLAGS" SAVED_LDFLAGS="$LDFLAGS" # Set up the compiler in two different ways and say yes we may want to install. ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 $as_echo_n "checking whether the C compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 $as_echo_n "checking for C compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 $as_echo_n "checking whether $CC understands -c and -o together... " >&6; } if ${am_cv_prog_cc_c_o+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 $as_echo "$am_cv_prog_cc_c_o" >&6; } if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu DEPDIR="${am__leading_dot}deps" ac_config_commands="$ac_config_commands depfiles" am_make=${MAKE-make} cat > confinc << 'END' am__doit: @echo this is the am__doit target .PHONY: am__doit END # If we don't find an include directive, just comment out the code. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 $as_echo_n "checking for style of include used by $am_make... " >&6; } am__include="#" am__quote= _am_result=none # First try GNU make style include. echo "include confinc" > confmf # Ignore all kinds of additional output from 'make'. case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=include am__quote= _am_result=GNU ;; esac # Now try BSD make style include. if test "$am__include" = "#"; then echo '.include "confinc"' > confmf case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=.include am__quote="\"" _am_result=BSD ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 $as_echo "$_am_result" >&6; } rm -f confinc confmf # Check whether --enable-dependency-tracking was given. if test "${enable_dependency_tracking+set}" = set; then : enableval=$enable_dependency_tracking; fi if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi if test "x$enable_dependency_tracking" != xno; then AMDEP_TRUE= AMDEP_FALSE='#' else AMDEP_TRUE='#' AMDEP_FALSE= fi depcc="$CC" am_compiler_list= { $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 $as_echo_n "checking dependency style of $depcc... " >&6; } if ${am_cv_CC_dependencies_compiler_type+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_CC_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi am__universal=false case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_CC_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_CC_dependencies_compiler_type=none fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 $as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type if test "x$enable_dependency_tracking" != xno \ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then am__fastdepCC_TRUE= am__fastdepCC_FALSE='#' else am__fastdepCC_TRUE='#' am__fastdepCC_FALSE= fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C99" >&5 $as_echo_n "checking for $CC option to accept ISO C99... " >&6; } if ${ac_cv_prog_cc_c99+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c99=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include #include // Check varargs macros. These examples are taken from C99 6.10.3.5. #define debug(...) fprintf (stderr, __VA_ARGS__) #define showlist(...) puts (#__VA_ARGS__) #define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) static void test_varargs_macros (void) { int x = 1234; int y = 5678; debug ("Flag"); debug ("X = %d\n", x); showlist (The first, second, and third items.); report (x>y, "x is %d but y is %d", x, y); } // Check long long types. #define BIG64 18446744073709551615ull #define BIG32 4294967295ul #define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) #if !BIG_OK your preprocessor is broken; #endif #if BIG_OK #else your preprocessor is broken; #endif static long long int bignum = -9223372036854775807LL; static unsigned long long int ubignum = BIG64; struct incomplete_array { int datasize; double data[]; }; struct named_init { int number; const wchar_t *name; double average; }; typedef const char *ccp; static inline int test_restrict (ccp restrict text) { // See if C++-style comments work. // Iterate through items via the restricted pointer. // Also check for declarations in for loops. for (unsigned int i = 0; *(text+i) != '\0'; ++i) continue; return 0; } // Check varargs and va_copy. static void test_varargs (const char *format, ...) { va_list args; va_start (args, format); va_list args_copy; va_copy (args_copy, args); const char *str; int number; float fnumber; while (*format) { switch (*format++) { case 's': // string str = va_arg (args_copy, const char *); break; case 'd': // int number = va_arg (args_copy, int); break; case 'f': // float fnumber = va_arg (args_copy, double); break; default: break; } } va_end (args_copy); va_end (args); } int main () { // Check bool. _Bool success = false; // Check restrict. if (test_restrict ("String literal") == 0) success = true; char *restrict newvar = "Another string"; // Check varargs. test_varargs ("s, d' f .", "string", 65, 34.234); test_varargs_macros (); // Check flexible array members. struct incomplete_array *ia = malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); ia->datasize = 10; for (int i = 0; i < ia->datasize; ++i) ia->data[i] = i * 1.234; // Check named initializers. struct named_init ni = { .number = 34, .name = L"Test wide string", .average = 543.34343, }; ni.number = 58; int dynamic_array[ni.number]; dynamic_array[ni.number - 1] = 543; // work around unused variable warnings return (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == 'x' || dynamic_array[ni.number - 1] != 543); ; return 0; } _ACEOF for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -D_STDC_C99= -qlanglvl=extc99 do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c99=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c99" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c99" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c99" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 $as_echo "$ac_cv_prog_cc_c99" >&6; } ;; esac if test "x$ac_cv_prog_cc_c99" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 $as_echo_n "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if ${ac_cv_prog_CPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 $as_echo "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } if ${ac_cv_path_GREP+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 $as_echo "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } if ${ac_cv_path_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 $as_echo "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" for ac_prog in 'bison -y' byacc do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_YACC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$YACC"; then ac_cv_prog_YACC="$YACC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_YACC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi YACC=$ac_cv_prog_YACC if test -n "$YACC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $YACC" >&5 $as_echo "$YACC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$YACC" && break done test -n "$YACC" || YACC="yacc" if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi PKG_CONFIG=$ac_cv_path_PKG_CONFIG if test -n "$PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 $as_echo "$PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_path_PKG_CONFIG"; then ac_pt_PKG_CONFIG=$PKG_CONFIG # Extract the first word of "pkg-config", so it can be a program name with args. set dummy pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $ac_pt_PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG if test -n "$ac_pt_PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 $as_echo "$ac_pt_PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_pt_PKG_CONFIG" = x; then PKG_CONFIG="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac PKG_CONFIG=$ac_pt_PKG_CONFIG fi else PKG_CONFIG="$ac_cv_path_PKG_CONFIG" fi fi if test -n "$PKG_CONFIG"; then _pkg_min_version=0.9.0 { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 $as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } PKG_CONFIG="" fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done ac_fn_c_check_header_mongrel "$LINENO" "minix/config.h" "ac_cv_header_minix_config_h" "$ac_includes_default" if test "x$ac_cv_header_minix_config_h" = xyes; then : MINIX=yes else MINIX= fi if test "$MINIX" = yes; then $as_echo "#define _POSIX_SOURCE 1" >>confdefs.h $as_echo "#define _POSIX_1_SOURCE 2" >>confdefs.h $as_echo "#define _MINIX 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5 $as_echo_n "checking whether it is safe to define __EXTENSIONS__... " >&6; } if ${ac_cv_safe_to_define___extensions__+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ # define __EXTENSIONS__ 1 $ac_includes_default int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_safe_to_define___extensions__=yes else ac_cv_safe_to_define___extensions__=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5 $as_echo "$ac_cv_safe_to_define___extensions__" >&6; } test $ac_cv_safe_to_define___extensions__ = yes && $as_echo "#define __EXTENSIONS__ 1" >>confdefs.h $as_echo "#define _ALL_SOURCE 1" >>confdefs.h $as_echo "#define _GNU_SOURCE 1" >>confdefs.h $as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h $as_echo "#define _TANDEM_SOURCE 1" >>confdefs.h # Default tmux.conf goes in /etc not ${prefix}/etc. test "$sysconfdir" = '${prefix}/etc' && sysconfdir=/etc # Is this --enable-debug? case "x$VERSION" in xnext*) enable_debug=yes;; esac # Check whether --enable-debug was given. if test "${enable_debug+set}" = set; then : enableval=$enable_debug; fi if test "x$enable_debug" = xyes; then IS_DEBUG_TRUE= IS_DEBUG_FALSE='#' else IS_DEBUG_TRUE='#' IS_DEBUG_FALSE= fi # Is this a static build? # Check whether --enable-static was given. if test "${enable_static+set}" = set; then : enableval=$enable_static; fi if test "x$enable_static" = xyes; then test "x$PKG_CONFIG" != x && PKG_CONFIG="$PKG_CONFIG --static" AM_LDFLAGS="-static $AM_LDFLAGS" LDFLAGS="$AM_LDFLAGS $SAVED_LDFLAGS" fi # Is this gcc? if test "x$GCC" = xyes; then IS_GCC_TRUE= IS_GCC_FALSE='#' else IS_GCC_TRUE='#' IS_GCC_FALSE= fi # Is this Sun CC? cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __SUNPRO_C yes #endif _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "yes" >/dev/null 2>&1; then : found_suncc=yes else found_suncc=no fi rm -f conftest* if test "x$found_suncc" = xyes; then IS_SUNCC_TRUE= IS_SUNCC_FALSE='#' else IS_SUNCC_TRUE='#' IS_SUNCC_FALSE= fi # Check for various headers. Alternatives included from compat.h. for ac_header in \ bitstring.h \ dirent.h \ fcntl.h \ inttypes.h \ libutil.h \ ndir.h \ paths.h \ pty.h \ stdint.h \ sys/dir.h \ sys/ndir.h \ sys/tree.h \ util.h \ do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done # Look for library needed for flock. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing flock" >&5 $as_echo_n "checking for library containing flock... " >&6; } if ${ac_cv_search_flock+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char flock (); int main () { return flock (); ; return 0; } _ACEOF for ac_lib in '' bsd; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_flock=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_flock+:} false; then : break fi done if ${ac_cv_search_flock+:} false; then : else ac_cv_search_flock=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_flock" >&5 $as_echo "$ac_cv_search_flock" >&6; } ac_res=$ac_cv_search_flock if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi # Check for functions that are replaced or omitted. for ac_func in \ dirfd \ flock \ prctl \ sysconf \ do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done # Check for functions with a compatibility implementation. ac_fn_c_check_func "$LINENO" "asprintf" "ac_cv_func_asprintf" if test "x$ac_cv_func_asprintf" = xyes; then : $as_echo "#define HAVE_ASPRINTF 1" >>confdefs.h else case " $LIBOBJS " in *" asprintf.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS asprintf.$ac_objext" ;; esac fi ac_fn_c_check_func "$LINENO" "cfmakeraw" "ac_cv_func_cfmakeraw" if test "x$ac_cv_func_cfmakeraw" = xyes; then : $as_echo "#define HAVE_CFMAKERAW 1" >>confdefs.h else case " $LIBOBJS " in *" cfmakeraw.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS cfmakeraw.$ac_objext" ;; esac fi ac_fn_c_check_func "$LINENO" "closefrom" "ac_cv_func_closefrom" if test "x$ac_cv_func_closefrom" = xyes; then : $as_echo "#define HAVE_CLOSEFROM 1" >>confdefs.h else case " $LIBOBJS " in *" closefrom.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS closefrom.$ac_objext" ;; esac fi ac_fn_c_check_func "$LINENO" "explicit_bzero" "ac_cv_func_explicit_bzero" if test "x$ac_cv_func_explicit_bzero" = xyes; then : $as_echo "#define HAVE_EXPLICIT_BZERO 1" >>confdefs.h else case " $LIBOBJS " in *" explicit_bzero.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS explicit_bzero.$ac_objext" ;; esac fi ac_fn_c_check_func "$LINENO" "fgetln" "ac_cv_func_fgetln" if test "x$ac_cv_func_fgetln" = xyes; then : $as_echo "#define HAVE_FGETLN 1" >>confdefs.h else case " $LIBOBJS " in *" fgetln.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS fgetln.$ac_objext" ;; esac fi ac_fn_c_check_func "$LINENO" "freezero" "ac_cv_func_freezero" if test "x$ac_cv_func_freezero" = xyes; then : $as_echo "#define HAVE_FREEZERO 1" >>confdefs.h else case " $LIBOBJS " in *" freezero.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS freezero.$ac_objext" ;; esac fi ac_fn_c_check_func "$LINENO" "getdtablecount" "ac_cv_func_getdtablecount" if test "x$ac_cv_func_getdtablecount" = xyes; then : $as_echo "#define HAVE_GETDTABLECOUNT 1" >>confdefs.h else case " $LIBOBJS " in *" getdtablecount.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS getdtablecount.$ac_objext" ;; esac fi ac_fn_c_check_func "$LINENO" "getprogname" "ac_cv_func_getprogname" if test "x$ac_cv_func_getprogname" = xyes; then : $as_echo "#define HAVE_GETPROGNAME 1" >>confdefs.h else case " $LIBOBJS " in *" getprogname.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS getprogname.$ac_objext" ;; esac fi ac_fn_c_check_func "$LINENO" "memmem" "ac_cv_func_memmem" if test "x$ac_cv_func_memmem" = xyes; then : $as_echo "#define HAVE_MEMMEM 1" >>confdefs.h else case " $LIBOBJS " in *" memmem.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS memmem.$ac_objext" ;; esac fi ac_fn_c_check_func "$LINENO" "recallocarray" "ac_cv_func_recallocarray" if test "x$ac_cv_func_recallocarray" = xyes; then : $as_echo "#define HAVE_RECALLOCARRAY 1" >>confdefs.h else case " $LIBOBJS " in *" recallocarray.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS recallocarray.$ac_objext" ;; esac fi ac_fn_c_check_func "$LINENO" "reallocarray" "ac_cv_func_reallocarray" if test "x$ac_cv_func_reallocarray" = xyes; then : $as_echo "#define HAVE_REALLOCARRAY 1" >>confdefs.h else case " $LIBOBJS " in *" reallocarray.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS reallocarray.$ac_objext" ;; esac fi ac_fn_c_check_func "$LINENO" "setenv" "ac_cv_func_setenv" if test "x$ac_cv_func_setenv" = xyes; then : $as_echo "#define HAVE_SETENV 1" >>confdefs.h else case " $LIBOBJS " in *" setenv.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS setenv.$ac_objext" ;; esac fi ac_fn_c_check_func "$LINENO" "setproctitle" "ac_cv_func_setproctitle" if test "x$ac_cv_func_setproctitle" = xyes; then : $as_echo "#define HAVE_SETPROCTITLE 1" >>confdefs.h else case " $LIBOBJS " in *" setproctitle.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS setproctitle.$ac_objext" ;; esac fi ac_fn_c_check_func "$LINENO" "strcasestr" "ac_cv_func_strcasestr" if test "x$ac_cv_func_strcasestr" = xyes; then : $as_echo "#define HAVE_STRCASESTR 1" >>confdefs.h else case " $LIBOBJS " in *" strcasestr.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS strcasestr.$ac_objext" ;; esac fi ac_fn_c_check_func "$LINENO" "strlcat" "ac_cv_func_strlcat" if test "x$ac_cv_func_strlcat" = xyes; then : $as_echo "#define HAVE_STRLCAT 1" >>confdefs.h else case " $LIBOBJS " in *" strlcat.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS strlcat.$ac_objext" ;; esac fi ac_fn_c_check_func "$LINENO" "strlcpy" "ac_cv_func_strlcpy" if test "x$ac_cv_func_strlcpy" = xyes; then : $as_echo "#define HAVE_STRLCPY 1" >>confdefs.h else case " $LIBOBJS " in *" strlcpy.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS strlcpy.$ac_objext" ;; esac fi ac_fn_c_check_func "$LINENO" "strndup" "ac_cv_func_strndup" if test "x$ac_cv_func_strndup" = xyes; then : $as_echo "#define HAVE_STRNDUP 1" >>confdefs.h else case " $LIBOBJS " in *" strndup.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS strndup.$ac_objext" ;; esac fi ac_fn_c_check_func "$LINENO" "strsep" "ac_cv_func_strsep" if test "x$ac_cv_func_strsep" = xyes; then : $as_echo "#define HAVE_STRSEP 1" >>confdefs.h else case " $LIBOBJS " in *" strsep.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS strsep.$ac_objext" ;; esac fi ac_fn_c_check_func "$LINENO" "strtonum" "ac_cv_func_strtonum" if test "x$ac_cv_func_strtonum" = xyes; then : $as_echo "#define HAVE_STRTONUM 1" >>confdefs.h else case " $LIBOBJS " in *" strtonum.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS strtonum.$ac_objext" ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working strnlen" >&5 $as_echo_n "checking for working strnlen... " >&6; } if ${ac_cv_func_strnlen_working+:} false; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : # Guess no on AIX systems, yes otherwise. case "$host_os" in aix*) ac_cv_func_strnlen_working=no;; *) ac_cv_func_strnlen_working=yes;; esac else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main () { #define S "foobar" #define S_LEN (sizeof S - 1) /* At least one implementation is buggy: that of AIX 4.3 would give strnlen (S, 1) == 3. */ int i; for (i = 0; i < S_LEN + 1; ++i) { int expected = i <= S_LEN ? i : S_LEN; if (strnlen (S, i) != expected) return 1; } return 0; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : ac_cv_func_strnlen_working=yes else ac_cv_func_strnlen_working=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_strnlen_working" >&5 $as_echo "$ac_cv_func_strnlen_working" >&6; } test $ac_cv_func_strnlen_working = no && case " $LIBOBJS " in *" strnlen.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS strnlen.$ac_objext" ;; esac # Look for clock_gettime. Must come before event_init. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5 $as_echo_n "checking for library containing clock_gettime... " >&6; } if ${ac_cv_search_clock_gettime+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char clock_gettime (); int main () { return clock_gettime (); ; return 0; } _ACEOF for ac_lib in '' rt; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_clock_gettime=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_clock_gettime+:} false; then : break fi done if ${ac_cv_search_clock_gettime+:} false; then : else ac_cv_search_clock_gettime=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5 $as_echo "$ac_cv_search_clock_gettime" >&6; } ac_res=$ac_cv_search_clock_gettime if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi # Look for libevent. pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBEVENT" >&5 $as_echo_n "checking for LIBEVENT... " >&6; } if test -n "$LIBEVENT_CFLAGS"; then pkg_cv_LIBEVENT_CFLAGS="$LIBEVENT_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libevent\""; } >&5 ($PKG_CONFIG --exists --print-errors "libevent") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBEVENT_CFLAGS=`$PKG_CONFIG --cflags "libevent" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$LIBEVENT_LIBS"; then pkg_cv_LIBEVENT_LIBS="$LIBEVENT_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libevent\""; } >&5 ($PKG_CONFIG --exists --print-errors "libevent") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBEVENT_LIBS=`$PKG_CONFIG --libs "libevent" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then LIBEVENT_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libevent" 2>&1` else LIBEVENT_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libevent" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$LIBEVENT_PKG_ERRORS" >&5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing event_init" >&5 $as_echo_n "checking for library containing event_init... " >&6; } if ${ac_cv_search_event_init+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char event_init (); int main () { return event_init (); ; return 0; } _ACEOF for ac_lib in '' event event-1.4 event2; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_event_init=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_event_init+:} false; then : break fi done if ${ac_cv_search_event_init+:} false; then : else ac_cv_search_event_init=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_event_init" >&5 $as_echo "$ac_cv_search_event_init" >&6; } ac_res=$ac_cv_search_event_init if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" found_libevent=yes else found_libevent=no fi elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing event_init" >&5 $as_echo_n "checking for library containing event_init... " >&6; } if ${ac_cv_search_event_init+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char event_init (); int main () { return event_init (); ; return 0; } _ACEOF for ac_lib in '' event event-1.4 event2; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_event_init=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_event_init+:} false; then : break fi done if ${ac_cv_search_event_init+:} false; then : else ac_cv_search_event_init=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_event_init" >&5 $as_echo "$ac_cv_search_event_init" >&6; } ac_res=$ac_cv_search_event_init if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" found_libevent=yes else found_libevent=no fi else LIBEVENT_CFLAGS=$pkg_cv_LIBEVENT_CFLAGS LIBEVENT_LIBS=$pkg_cv_LIBEVENT_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } AM_CFLAGS="$LIBEVENT_CFLAGS $AM_CFLAGS" CFLAGS="$AM_CFLAGS $SAVED_CFLAGS" LIBS="$LIBEVENT_LIBS $LIBS" found_libevent=yes fi ac_fn_c_check_header_mongrel "$LINENO" "event.h" "ac_cv_header_event_h" "$ac_includes_default" if test "x$ac_cv_header_event_h" = xyes; then : else found_libevent=no fi if test "x$found_libevent" = xno; then as_fn_error $? "\"libevent not found\"" "$LINENO" 5 fi # Look for ncurses. pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBTINFO" >&5 $as_echo_n "checking for LIBTINFO... " >&6; } if test -n "$LIBTINFO_CFLAGS"; then pkg_cv_LIBTINFO_CFLAGS="$LIBTINFO_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"tinfo\""; } >&5 ($PKG_CONFIG --exists --print-errors "tinfo") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBTINFO_CFLAGS=`$PKG_CONFIG --cflags "tinfo" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$LIBTINFO_LIBS"; then pkg_cv_LIBTINFO_LIBS="$LIBTINFO_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"tinfo\""; } >&5 ($PKG_CONFIG --exists --print-errors "tinfo") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBTINFO_LIBS=`$PKG_CONFIG --libs "tinfo" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then LIBTINFO_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "tinfo" 2>&1` else LIBTINFO_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "tinfo" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$LIBTINFO_PKG_ERRORS" >&5 found_ncurses=no elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } found_ncurses=no else LIBTINFO_CFLAGS=$pkg_cv_LIBTINFO_CFLAGS LIBTINFO_LIBS=$pkg_cv_LIBTINFO_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } found_ncurses=yes fi if test "x$found_ncurses" = xno; then pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBNCURSES" >&5 $as_echo_n "checking for LIBNCURSES... " >&6; } if test -n "$LIBNCURSES_CFLAGS"; then pkg_cv_LIBNCURSES_CFLAGS="$LIBNCURSES_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"ncurses\""; } >&5 ($PKG_CONFIG --exists --print-errors "ncurses") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBNCURSES_CFLAGS=`$PKG_CONFIG --cflags "ncurses" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$LIBNCURSES_LIBS"; then pkg_cv_LIBNCURSES_LIBS="$LIBNCURSES_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"ncurses\""; } >&5 ($PKG_CONFIG --exists --print-errors "ncurses") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBNCURSES_LIBS=`$PKG_CONFIG --libs "ncurses" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then LIBNCURSES_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "ncurses" 2>&1` else LIBNCURSES_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "ncurses" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$LIBNCURSES_PKG_ERRORS" >&5 found_ncurses=no elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } found_ncurses=no else LIBNCURSES_CFLAGS=$pkg_cv_LIBNCURSES_CFLAGS LIBNCURSES_LIBS=$pkg_cv_LIBNCURSES_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } found_ncurses=yes fi fi if test "x$found_ncurses" = xno; then pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBNCURSES" >&5 $as_echo_n "checking for LIBNCURSES... " >&6; } if test -n "$LIBNCURSES_CFLAGS"; then pkg_cv_LIBNCURSES_CFLAGS="$LIBNCURSES_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"ncursesw\""; } >&5 ($PKG_CONFIG --exists --print-errors "ncursesw") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBNCURSES_CFLAGS=`$PKG_CONFIG --cflags "ncursesw" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test -n "$LIBNCURSES_LIBS"; then pkg_cv_LIBNCURSES_LIBS="$LIBNCURSES_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"ncursesw\""; } >&5 ($PKG_CONFIG --exists --print-errors "ncursesw") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBNCURSES_LIBS=`$PKG_CONFIG --libs "ncursesw" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi else pkg_failed=untried fi if test $pkg_failed = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then LIBNCURSES_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "ncursesw" 2>&1` else LIBNCURSES_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "ncursesw" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$LIBNCURSES_PKG_ERRORS" >&5 found_ncurses=no elif test $pkg_failed = untried; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } found_ncurses=no else LIBNCURSES_CFLAGS=$pkg_cv_LIBNCURSES_CFLAGS LIBNCURSES_LIBS=$pkg_cv_LIBNCURSES_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } found_ncurses=yes fi fi if test "x$found_ncurses" = xyes; then AM_CFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $AM_CFLAGS" CFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $CFLAGS" LIBS="$LIBNCURSES_LIBS $LIBTINFO_LIBS $LIBS" else # pkg-config didn't work, try ncurses. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for setupterm in -ltinfo" >&5 $as_echo_n "checking for setupterm in -ltinfo... " >&6; } if ${ac_cv_lib_tinfo_setupterm+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ltinfo $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char setupterm (); int main () { return setupterm (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_tinfo_setupterm=yes else ac_cv_lib_tinfo_setupterm=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_tinfo_setupterm" >&5 $as_echo "$ac_cv_lib_tinfo_setupterm" >&6; } if test "x$ac_cv_lib_tinfo_setupterm" = xyes; then : found_ncurses=yes else found_ncurses=no fi if test "x$found_ncurses" = xno; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for setupterm in -lncurses" >&5 $as_echo_n "checking for setupterm in -lncurses... " >&6; } if ${ac_cv_lib_ncurses_setupterm+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lncurses $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char setupterm (); int main () { return setupterm (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_ncurses_setupterm=yes else ac_cv_lib_ncurses_setupterm=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ncurses_setupterm" >&5 $as_echo "$ac_cv_lib_ncurses_setupterm" >&6; } if test "x$ac_cv_lib_ncurses_setupterm" = xyes; then : found_ncurses=yes else found_ncurses=no fi fi if test "x$found_ncurses" = xyes; then ac_fn_c_check_header_mongrel "$LINENO" "ncurses.h" "ac_cv_header_ncurses_h" "$ac_includes_default" if test "x$ac_cv_header_ncurses_h" = xyes; then : LIBS="$LIBS -lncurses" else found_ncurses=no fi fi fi if test "x$found_ncurses" = xyes; then $as_echo "#define HAVE_NCURSES_H 1" >>confdefs.h else # No ncurses, try curses. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for setupterm in -lcurses" >&5 $as_echo_n "checking for setupterm in -lcurses... " >&6; } if ${ac_cv_lib_curses_setupterm+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lcurses $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char setupterm (); int main () { return setupterm (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_curses_setupterm=yes else ac_cv_lib_curses_setupterm=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curses_setupterm" >&5 $as_echo "$ac_cv_lib_curses_setupterm" >&6; } if test "x$ac_cv_lib_curses_setupterm" = xyes; then : found_curses=yes else found_curses=no fi ac_fn_c_check_header_mongrel "$LINENO" "curses.h" "ac_cv_header_curses_h" "$ac_includes_default" if test "x$ac_cv_header_curses_h" = xyes; then : else found_curses=no fi if test "x$found_curses" = xyes; then LIBS="$LIBS -lcurses" $as_echo "#define HAVE_CURSES_H 1" >>confdefs.h else as_fn_error $? "\"curses not found\"" "$LINENO" 5 fi fi # Look for utempter. # Check whether --enable-utempter was given. if test "${enable_utempter+set}" = set; then : enableval=$enable_utempter; fi if test "x$enable_utempter" = xyes; then ac_fn_c_check_header_mongrel "$LINENO" "utempter.h" "ac_cv_header_utempter_h" "$ac_includes_default" if test "x$ac_cv_header_utempter_h" = xyes; then : enable_utempter=yes else enable_utempter=no fi if test "x$enable_utempter" = xyes; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing utempter_add_record" >&5 $as_echo_n "checking for library containing utempter_add_record... " >&6; } if ${ac_cv_search_utempter_add_record+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char utempter_add_record (); int main () { return utempter_add_record (); ; return 0; } _ACEOF for ac_lib in '' utempter; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_utempter_add_record=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_utempter_add_record+:} false; then : break fi done if ${ac_cv_search_utempter_add_record+:} false; then : else ac_cv_search_utempter_add_record=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_utempter_add_record" >&5 $as_echo "$ac_cv_search_utempter_add_record" >&6; } ac_res=$ac_cv_search_utempter_add_record if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" enable_utempter=yes else enable_utempter=no fi fi if test "x$enable_utempter" = xyes; then $as_echo "#define HAVE_UTEMPTER 1" >>confdefs.h else as_fn_error $? "\"utempter not found\"" "$LINENO" 5 fi fi # Look for utf8proc. # Check whether --enable-utf8proc was given. if test "${enable_utf8proc+set}" = set; then : enableval=$enable_utf8proc; fi if test "x$enable_utf8proc" = xyes; then ac_fn_c_check_header_mongrel "$LINENO" "utf8proc.h" "ac_cv_header_utf8proc_h" "$ac_includes_default" if test "x$ac_cv_header_utf8proc_h" = xyes; then : enable_utf8proc=yes else enable_utf8proc=no fi if test "x$enable_utf8proc" = xyes; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing utf8proc_charwidth" >&5 $as_echo_n "checking for library containing utf8proc_charwidth... " >&6; } if ${ac_cv_search_utf8proc_charwidth+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char utf8proc_charwidth (); int main () { return utf8proc_charwidth (); ; return 0; } _ACEOF for ac_lib in '' utf8proc; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_utf8proc_charwidth=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_utf8proc_charwidth+:} false; then : break fi done if ${ac_cv_search_utf8proc_charwidth+:} false; then : else ac_cv_search_utf8proc_charwidth=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_utf8proc_charwidth" >&5 $as_echo "$ac_cv_search_utf8proc_charwidth" >&6; } ac_res=$ac_cv_search_utf8proc_charwidth if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" enable_utf8proc=yes else enable_utf8proc=no fi fi if test "x$enable_utf8proc" = xyes; then $as_echo "#define HAVE_UTF8PROC 1" >>confdefs.h else as_fn_error $? "\"utf8proc not found\"" "$LINENO" 5 fi fi if test "x$enable_utf8proc" = xyes; then HAVE_UTF8PROC_TRUE= HAVE_UTF8PROC_FALSE='#' else HAVE_UTF8PROC_TRUE='#' HAVE_UTF8PROC_FALSE= fi # Check for b64_ntop. If we have b64_ntop, we assume b64_pton as well. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for b64_ntop" >&5 $as_echo_n "checking for b64_ntop... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main () { b64_ntop(NULL, 0, NULL, 0); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : found_b64_ntop=yes else found_b64_ntop=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test "x$found_b64_ntop" = xno; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for b64_ntop with -lresolv" >&5 $as_echo_n "checking for b64_ntop with -lresolv... " >&6; } OLD_LIBS="$LIBS" LIBS="$LIBS -lresolv" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main () { b64_ntop(NULL, 0, NULL, 0); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : found_b64_ntop=yes else found_b64_ntop=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test "x$found_b64_ntop" = xno; then LIBS="$OLD_LIBS" { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test "x$found_b64_ntop" = xyes; then $as_echo "#define HAVE_B64_NTOP 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else case " $LIBOBJS " in *" base64.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS base64.$ac_objext" ;; esac fi # Look for networking libraries. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing inet_ntoa" >&5 $as_echo_n "checking for library containing inet_ntoa... " >&6; } if ${ac_cv_search_inet_ntoa+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char inet_ntoa (); int main () { return inet_ntoa (); ; return 0; } _ACEOF for ac_lib in '' nsl; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_inet_ntoa=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_inet_ntoa+:} false; then : break fi done if ${ac_cv_search_inet_ntoa+:} false; then : else ac_cv_search_inet_ntoa=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_inet_ntoa" >&5 $as_echo "$ac_cv_search_inet_ntoa" >&6; } ac_res=$ac_cv_search_inet_ntoa if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing socket" >&5 $as_echo_n "checking for library containing socket... " >&6; } if ${ac_cv_search_socket+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char socket (); int main () { return socket (); ; return 0; } _ACEOF for ac_lib in '' socket; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_socket=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_socket+:} false; then : break fi done if ${ac_cv_search_socket+:} false; then : else ac_cv_search_socket=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_socket" >&5 $as_echo "$ac_cv_search_socket" >&6; } ac_res=$ac_cv_search_socket if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for socket in -lxnet" >&5 $as_echo_n "checking for socket in -lxnet... " >&6; } if ${ac_cv_lib_xnet_socket+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lxnet $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char socket (); int main () { return socket (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_xnet_socket=yes else ac_cv_lib_xnet_socket=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_xnet_socket" >&5 $as_echo "$ac_cv_lib_xnet_socket" >&6; } if test "x$ac_cv_lib_xnet_socket" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBXNET 1 _ACEOF LIBS="-lxnet $LIBS" fi # Check for CMSG_DATA. Some platforms require _XOPEN_SOURCE_EXTENDED (for # example see xopen_networking(7) on HP-UX). XOPEN_DEFINES= { $as_echo "$as_me:${as_lineno-$LINENO}: checking for CMSG_DATA" >&5 $as_echo_n "checking for CMSG_DATA... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #ifdef CMSG_DATA yes #endif _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "yes" >/dev/null 2>&1; then : found_cmsg_data=yes else found_cmsg_data=no fi rm -f conftest* { $as_echo "$as_me:${as_lineno-$LINENO}: result: $found_cmsg_data" >&5 $as_echo "$found_cmsg_data" >&6; } if test "x$found_cmsg_data" = xno; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking if CMSG_DATA needs _XOPEN_SOURCE_EXTENDED" >&5 $as_echo_n "checking if CMSG_DATA needs _XOPEN_SOURCE_EXTENDED... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _XOPEN_SOURCE 1 #define _XOPEN_SOURCE_EXTENDED 1 #include #ifdef CMSG_DATA yes #endif _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "yes" >/dev/null 2>&1; then : found_cmsg_data=yes else found_cmsg_data=no fi rm -f conftest* { $as_echo "$as_me:${as_lineno-$LINENO}: result: $found_cmsg_data" >&5 $as_echo "$found_cmsg_data" >&6; } if test "x$found_cmsg_data" = xyes; then XOPEN_DEFINES="-D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED" else as_fn_error $? "\"CMSG_DATA not found\"" "$LINENO" 5 fi fi # Look for err and friends in err.h. ac_fn_c_check_func "$LINENO" "err" "ac_cv_func_err" if test "x$ac_cv_func_err" = xyes; then : found_err_h=yes else found_err_h=no fi ac_fn_c_check_func "$LINENO" "errx" "ac_cv_func_errx" if test "x$ac_cv_func_errx" = xyes; then : else found_err_h=no fi ac_fn_c_check_func "$LINENO" "warn" "ac_cv_func_warn" if test "x$ac_cv_func_warn" = xyes; then : else found_err_h=no fi ac_fn_c_check_func "$LINENO" "warnx" "ac_cv_func_warnx" if test "x$ac_cv_func_warnx" = xyes; then : else found_err_h=no fi if test "x$found_err_h" = xyes; then ac_fn_c_check_header_mongrel "$LINENO" "err.h" "ac_cv_header_err_h" "$ac_includes_default" if test "x$ac_cv_header_err_h" = xyes; then : else found_err_h=no fi else case " $LIBOBJS " in *" err.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS err.$ac_objext" ;; esac fi # Look for imsg_init in libutil. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing imsg_init" >&5 $as_echo_n "checking for library containing imsg_init... " >&6; } if ${ac_cv_search_imsg_init+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char imsg_init (); int main () { return imsg_init (); ; return 0; } _ACEOF for ac_lib in '' util; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_imsg_init=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_imsg_init+:} false; then : break fi done if ${ac_cv_search_imsg_init+:} false; then : else ac_cv_search_imsg_init=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_imsg_init" >&5 $as_echo "$ac_cv_search_imsg_init" >&6; } ac_res=$ac_cv_search_imsg_init if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" found_imsg_init=yes else found_imsg_init=no fi if test "x$found_imsg_init" = xyes; then $as_echo "#define HAVE_IMSG 1" >>confdefs.h else case " $LIBOBJS " in *" imsg.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS imsg.$ac_objext" ;; esac case " $LIBOBJS " in *" imsg-buffer.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS imsg-buffer.$ac_objext" ;; esac fi # Look for daemon, compat/daemon.c used if missing. Solaris 10 has it in # libresolv, but no declaration anywhere, so check for declaration as well as # function. ac_fn_c_check_func "$LINENO" "daemon" "ac_cv_func_daemon" if test "x$ac_cv_func_daemon" = xyes; then : found_daemon=yes else found_daemon=no fi ac_fn_c_check_decl "$LINENO" "daemon" "ac_cv_have_decl_daemon" " #include #include " if test "x$ac_cv_have_decl_daemon" = xyes; then : else found_daemon=no fi if test "x$found_daemon" = xyes; then $as_echo "#define HAVE_DAEMON 1" >>confdefs.h else case " $LIBOBJS " in *" daemon.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS daemon.$ac_objext" ;; esac fi # Look for stravis, compat/{vis,unvis}.c used if missing. ac_fn_c_check_func "$LINENO" "stravis" "ac_cv_func_stravis" if test "x$ac_cv_func_stravis" = xyes; then : found_stravis=yes else found_stravis=no fi if test "x$found_stravis" = xyes; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking if strnvis is broken" >&5 $as_echo_n "checking if strnvis is broken... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "strnvis\(char \*, const char \*, size_t, int\)" >/dev/null 2>&1; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } else found_stravis=no fi rm -f conftest* if test "x$found_stravis" = xno; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi fi if test "x$found_stravis" = xyes; then ac_fn_c_check_decl "$LINENO" "VIS_DQ" "ac_cv_have_decl_VIS_DQ" " #include #include " if test "x$ac_cv_have_decl_VIS_DQ" = xyes; then : else found_stravis=no fi fi if test "x$found_stravis" = xyes; then $as_echo "#define HAVE_VIS 1" >>confdefs.h else case " $LIBOBJS " in *" vis.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS vis.$ac_objext" ;; esac case " $LIBOBJS " in *" unvis.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS unvis.$ac_objext" ;; esac fi # Look for getopt. glibc's getopt does not enforce argument order and the ways # of making it do so are stupid, so just use our own instead. ac_fn_c_check_func "$LINENO" "getopt" "ac_cv_func_getopt" if test "x$ac_cv_func_getopt" = xyes; then : found_getopt=yes else found_getopt=no fi if test "x$found_getopt" != xno; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking if getopt is suitable" >&5 $as_echo_n "checking if getopt is suitable... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #ifdef __GLIBC__ yes #endif _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "yes" >/dev/null 2>&1; then : found_getopt=no { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi rm -f conftest* fi if test "x$found_getopt" != xno; then ac_fn_c_check_decl "$LINENO" "optarg" "ac_cv_have_decl_optarg" " #include " if test "x$ac_cv_have_decl_optarg" = xyes; then : ac_have_decl=1 else ac_have_decl=0 fi cat >>confdefs.h <<_ACEOF #define HAVE_DECL_OPTARG $ac_have_decl _ACEOF if test $ac_have_decl = 1; then : else found_getopt=no fi ac_fn_c_check_decl "$LINENO" "optind" "ac_cv_have_decl_optind" " #include " if test "x$ac_cv_have_decl_optind" = xyes; then : ac_have_decl=1 else ac_have_decl=0 fi cat >>confdefs.h <<_ACEOF #define HAVE_DECL_OPTIND $ac_have_decl _ACEOF if test $ac_have_decl = 1; then : else found_getopt=no fi ac_fn_c_check_decl "$LINENO" "optreset" "ac_cv_have_decl_optreset" " #include " if test "x$ac_cv_have_decl_optreset" = xyes; then : ac_have_decl=1 else ac_have_decl=0 fi cat >>confdefs.h <<_ACEOF #define HAVE_DECL_OPTRESET $ac_have_decl _ACEOF if test $ac_have_decl = 1; then : else found_getopt=no fi fi if test "x$found_getopt" != xno; then $as_echo "#define HAVE_GETOPT 1" >>confdefs.h else case " $LIBOBJS " in *" getopt.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS getopt.$ac_objext" ;; esac fi # Look for fdforkpty and forkpty in libutil. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing fdforkpty" >&5 $as_echo_n "checking for library containing fdforkpty... " >&6; } if ${ac_cv_search_fdforkpty+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char fdforkpty (); int main () { return fdforkpty (); ; return 0; } _ACEOF for ac_lib in '' util; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_fdforkpty=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_fdforkpty+:} false; then : break fi done if ${ac_cv_search_fdforkpty+:} false; then : else ac_cv_search_fdforkpty=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_fdforkpty" >&5 $as_echo "$ac_cv_search_fdforkpty" >&6; } ac_res=$ac_cv_search_fdforkpty if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" found_fdforkpty=yes else found_fdforkpty=no fi if test "x$found_fdforkpty" = xyes; then $as_echo "#define HAVE_FDFORKPTY 1" >>confdefs.h else case " $LIBOBJS " in *" fdforkpty.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS fdforkpty.$ac_objext" ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing forkpty" >&5 $as_echo_n "checking for library containing forkpty... " >&6; } if ${ac_cv_search_forkpty+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char forkpty (); int main () { return forkpty (); ; return 0; } _ACEOF for ac_lib in '' util; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_forkpty=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_forkpty+:} false; then : break fi done if ${ac_cv_search_forkpty+:} false; then : else ac_cv_search_forkpty=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_forkpty" >&5 $as_echo "$ac_cv_search_forkpty" >&6; } ac_res=$ac_cv_search_forkpty if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" found_forkpty=yes else found_forkpty=no fi if test "x$found_forkpty" = xyes; then $as_echo "#define HAVE_FORKPTY 1" >>confdefs.h fi if test "x$found_forkpty" = xno; then NEED_FORKPTY_TRUE= NEED_FORKPTY_FALSE='#' else NEED_FORKPTY_TRUE='#' NEED_FORKPTY_FALSE= fi # Look for kinfo_getfile in libutil. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing kinfo_getfile" >&5 $as_echo_n "checking for library containing kinfo_getfile... " >&6; } if ${ac_cv_search_kinfo_getfile+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char kinfo_getfile (); int main () { return kinfo_getfile (); ; return 0; } _ACEOF for ac_lib in '' util util-freebsd; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_kinfo_getfile=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_kinfo_getfile+:} false; then : break fi done if ${ac_cv_search_kinfo_getfile+:} false; then : else ac_cv_search_kinfo_getfile=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_kinfo_getfile" >&5 $as_echo "$ac_cv_search_kinfo_getfile" >&6; } ac_res=$ac_cv_search_kinfo_getfile if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi # Look for a suitable queue.h. ac_fn_c_check_decl "$LINENO" "TAILQ_CONCAT" "ac_cv_have_decl_TAILQ_CONCAT" "#include " if test "x$ac_cv_have_decl_TAILQ_CONCAT" = xyes; then : found_queue_h=yes else found_queue_h=no fi ac_fn_c_check_decl "$LINENO" "TAILQ_PREV" "ac_cv_have_decl_TAILQ_PREV" "#include " if test "x$ac_cv_have_decl_TAILQ_PREV" = xyes; then : found_queue_h=yes else found_queue_h=no fi ac_fn_c_check_decl "$LINENO" "TAILQ_REPLACE" "ac_cv_have_decl_TAILQ_REPLACE" "#include " if test "x$ac_cv_have_decl_TAILQ_REPLACE" = xyes; then : else found_queue_h=no fi if test "x$found_queue_h" = xyes; then $as_echo "#define HAVE_QUEUE_H 1" >>confdefs.h fi # Look for __progname. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __progname" >&5 $as_echo_n "checking for __progname... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include extern char *__progname; int main(void) { const char *cp = __progname; printf("%s\n", cp); exit(0); } _ACEOF if ac_fn_c_try_link "$LINENO"; then : $as_echo "#define HAVE___PROGNAME 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext # Look for program_invocation_short_name. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for program_invocation_short_name" >&5 $as_echo_n "checking for program_invocation_short_name... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main(void) { const char *cp = program_invocation_short_name; printf("%s\n", cp); exit(0); } _ACEOF if ac_fn_c_try_link "$LINENO"; then : $as_echo "#define HAVE_PROGRAM_INVOCATION_SHORT_NAME 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext # Look for prctl(PR_SET_NAME). ac_fn_c_check_decl "$LINENO" "PR_SET_NAME" "ac_cv_have_decl_PR_SET_NAME" "#include " if test "x$ac_cv_have_decl_PR_SET_NAME" = xyes; then : $as_echo "#define HAVE_PR_SET_NAME 1" >>confdefs.h fi # Look for fcntl(F_CLOSEM). ac_fn_c_check_decl "$LINENO" "F_CLOSEM" "ac_cv_have_decl_F_CLOSEM" "#include " if test "x$ac_cv_have_decl_F_CLOSEM" = xyes; then : $as_echo "#define HAVE_FCNTL_CLOSEM 1" >>confdefs.h fi # Look for /proc/$$. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for /proc/\$\$" >&5 $as_echo_n "checking for /proc/\$\$... " >&6; } if test -d /proc/$$; then $as_echo "#define HAVE_PROC_PID 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi # Man page defaults to mdoc. MANFORMAT=mdoc # Figure out the platform. { $as_echo "$as_me:${as_lineno-$LINENO}: checking platform" >&5 $as_echo_n "checking platform... " >&6; } case "$host_os" in *aix*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: aix" >&5 $as_echo "aix" >&6; } PLATFORM=aix ;; *darwin*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: darwin" >&5 $as_echo "darwin" >&6; } PLATFORM=darwin # # OS X CMSG_FIRSTHDR is broken, so redefine it with a working # one. daemon works but has some stupid side effects, so use # our internal version which has a workaround. # $as_echo "#define BROKEN_CMSG_FIRSTHDR 1" >>confdefs.h case " $LIBOBJS " in *" daemon.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS daemon.$ac_objext" ;; esac case " $LIBOBJS " in *" daemon-darwin.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS daemon-darwin.$ac_objext" ;; esac ;; *dragonfly*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: dragonfly" >&5 $as_echo "dragonfly" >&6; } PLATFORM=dragonfly ;; *linux*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: linux" >&5 $as_echo "linux" >&6; } PLATFORM=linux ;; *freebsd*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: freebsd" >&5 $as_echo "freebsd" >&6; } PLATFORM=freebsd ;; *netbsd*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: netbsd" >&5 $as_echo "netbsd" >&6; } PLATFORM=netbsd ;; *openbsd*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: openbsd" >&5 $as_echo "openbsd" >&6; } PLATFORM=openbsd ;; *sunos*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: sunos" >&5 $as_echo "sunos" >&6; } PLATFORM=sunos ;; *solaris*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: sunos" >&5 $as_echo "sunos" >&6; } PLATFORM=sunos case `/usr/bin/nroff --version 2>&1` in *GNU*) # Solaris 11.4 and later use GNU groff. MANFORMAT=mdoc ;; *) # Solaris 2.0 to 11.3 use AT&T nroff. MANFORMAT=man ;; esac ;; *hpux*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: hpux" >&5 $as_echo "hpux" >&6; } PLATFORM=hpux ;; *cygwin*|*msys*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: cygwin" >&5 $as_echo "cygwin" >&6; } PLATFORM=cygwin ;; *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unknown" >&5 $as_echo "unknown" >&6; } PLATFORM=unknown ;; esac if test "x$PLATFORM" = xaix; then IS_AIX_TRUE= IS_AIX_FALSE='#' else IS_AIX_TRUE='#' IS_AIX_FALSE= fi if test "x$PLATFORM" = xdarwin; then IS_DARWIN_TRUE= IS_DARWIN_FALSE='#' else IS_DARWIN_TRUE='#' IS_DARWIN_FALSE= fi if test "x$PLATFORM" = xdragonfly; then IS_DRAGONFLY_TRUE= IS_DRAGONFLY_FALSE='#' else IS_DRAGONFLY_TRUE='#' IS_DRAGONFLY_FALSE= fi if test "x$PLATFORM" = xlinux; then IS_LINUX_TRUE= IS_LINUX_FALSE='#' else IS_LINUX_TRUE='#' IS_LINUX_FALSE= fi if test "x$PLATFORM" = xfreebsd; then IS_FREEBSD_TRUE= IS_FREEBSD_FALSE='#' else IS_FREEBSD_TRUE='#' IS_FREEBSD_FALSE= fi if test "x$PLATFORM" = xnetbsd; then IS_NETBSD_TRUE= IS_NETBSD_FALSE='#' else IS_NETBSD_TRUE='#' IS_NETBSD_FALSE= fi if test "x$PLATFORM" = xopenbsd; then IS_OPENBSD_TRUE= IS_OPENBSD_FALSE='#' else IS_OPENBSD_TRUE='#' IS_OPENBSD_FALSE= fi if test "x$PLATFORM" = xsunos; then IS_SUNOS_TRUE= IS_SUNOS_FALSE='#' else IS_SUNOS_TRUE='#' IS_SUNOS_FALSE= fi if test "x$PLATFORM" = xhpux; then IS_HPUX_TRUE= IS_HPUX_FALSE='#' else IS_HPUX_TRUE='#' IS_HPUX_FALSE= fi if test "x$PLATFORM" = xunknown; then IS_UNKNOWN_TRUE= IS_UNKNOWN_FALSE='#' else IS_UNKNOWN_TRUE='#' IS_UNKNOWN_FALSE= fi # Save our CFLAGS/CPPFLAGS/LDFLAGS for the Makefile and restore the old user # variables. CPPFLAGS="$SAVED_CPPFLAGS" CFLAGS="$SAVED_CFLAGS" LDFLAGS="$SAVED_LDFLAGS" # autoconf should create a Makefile. ac_config_files="$ac_config_files Makefile" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' # Transform confdefs.h into DEFS. # Protect against shell expansion while executing Makefile rules. # Protect against Makefile macro expansion. # # If the first sed substitution is executed (which looks for macros that # take arguments), then branch to the quote section. Otherwise, # look for a macro that doesn't take arguments. ac_script=' :mline /\\$/{ N s,\\\n,, b mline } t clear :clear s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g t quote s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g t quote b any :quote s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g s/\[/\\&/g s/\]/\\&/g s/\$/$$/g H :any ${ g s/^\n// s/\n/ /g p } ' DEFS=`sed -n "$ac_script" confdefs.h` ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs { $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 $as_echo_n "checking that generated files are newer than configure... " >&6; } if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 $as_echo "done" >&6; } if test -n "$EXEEXT"; then am__EXEEXT_TRUE= am__EXEEXT_FALSE='#' else am__EXEEXT_TRUE='#' am__EXEEXT_FALSE= fi if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then as_fn_error $? "conditional \"AMDEP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${IS_DEBUG_TRUE}" && test -z "${IS_DEBUG_FALSE}"; then as_fn_error $? "conditional \"IS_DEBUG\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${IS_GCC_TRUE}" && test -z "${IS_GCC_FALSE}"; then as_fn_error $? "conditional \"IS_GCC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${IS_SUNCC_TRUE}" && test -z "${IS_SUNCC_FALSE}"; then as_fn_error $? "conditional \"IS_SUNCC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_UTF8PROC_TRUE}" && test -z "${HAVE_UTF8PROC_FALSE}"; then as_fn_error $? "conditional \"HAVE_UTF8PROC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${NEED_FORKPTY_TRUE}" && test -z "${NEED_FORKPTY_FALSE}"; then as_fn_error $? "conditional \"NEED_FORKPTY\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${IS_AIX_TRUE}" && test -z "${IS_AIX_FALSE}"; then as_fn_error $? "conditional \"IS_AIX\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${IS_DARWIN_TRUE}" && test -z "${IS_DARWIN_FALSE}"; then as_fn_error $? "conditional \"IS_DARWIN\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${IS_DRAGONFLY_TRUE}" && test -z "${IS_DRAGONFLY_FALSE}"; then as_fn_error $? "conditional \"IS_DRAGONFLY\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${IS_LINUX_TRUE}" && test -z "${IS_LINUX_FALSE}"; then as_fn_error $? "conditional \"IS_LINUX\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${IS_FREEBSD_TRUE}" && test -z "${IS_FREEBSD_FALSE}"; then as_fn_error $? "conditional \"IS_FREEBSD\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${IS_NETBSD_TRUE}" && test -z "${IS_NETBSD_FALSE}"; then as_fn_error $? "conditional \"IS_NETBSD\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${IS_OPENBSD_TRUE}" && test -z "${IS_OPENBSD_FALSE}"; then as_fn_error $? "conditional \"IS_OPENBSD\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${IS_SUNOS_TRUE}" && test -z "${IS_SUNOS_FALSE}"; then as_fn_error $? "conditional \"IS_SUNOS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${IS_HPUX_TRUE}" && test -z "${IS_HPUX_FALSE}"; then as_fn_error $? "conditional \"IS_HPUX\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${IS_UNKNOWN_TRUE}" && test -z "${IS_UNKNOWN_FALSE}"; then as_fn_error $? "conditional \"IS_UNKNOWN\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by tmux $as_me 3.0a, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_commands="$ac_config_commands" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE Configuration files: $config_files Configuration commands: $config_commands Report bugs to the package provider." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ tmux config.status 3.0a configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' MKDIR_P='$MKDIR_P' AWK='$AWK' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --he | --h | --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # # INIT-COMMANDS # AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" eval set X " :F $CONFIG_FILES :C $CONFIG_COMMANDS" shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac ac_MKDIR_P=$MKDIR_P case $MKDIR_P in [\\/$]* | ?:[\\/]* ) ;; */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t s&@MKDIR_P@&$ac_MKDIR_P&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 $as_echo "$as_me: executing $ac_file commands" >&6;} ;; esac case $ac_file$ac_mode in "depfiles":C) test x"$AMDEP_TRUE" != x"" || { # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. case $CONFIG_FILES in *\'*) eval set x "$CONFIG_FILES" ;; *) set x $CONFIG_FILES ;; esac shift for mf do # Strip MF so we end up with the name of the file. mf=`echo "$mf" | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile or not. # We used to match only the files named 'Makefile.in', but # some people rename them; so instead we look at the file content. # Grep'ing the first line is not enough: some people post-process # each Makefile.in and add a new line on top of each file to say so. # Grep'ing the whole file is not good either: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then dirpart=`$as_dirname -- "$mf" || $as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$mf" : 'X\(//\)[^/]' \| \ X"$mf" : 'X\(//\)$' \| \ X"$mf" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$mf" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` else continue fi # Extract the definition of DEPDIR, am__include, and am__quote # from the Makefile without running 'make'. DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` test -z "$DEPDIR" && continue am__include=`sed -n 's/^am__include = //p' < "$mf"` test -z "$am__include" && continue am__quote=`sed -n 's/^am__quote = //p' < "$mf"` # Find all dependency output files, they are included files with # $(DEPDIR) in their names. We invoke sed twice because it is the # simplest approach to changing $(DEPDIR) to its actual value in the # expansion. for file in `sed -n " s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do # Make sure the directory exists. test -f "$dirpart/$file" && continue fdir=`$as_dirname -- "$file" || $as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$file" : 'X\(//\)[^/]' \| \ X"$file" : 'X\(//\)$' \| \ X"$file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir=$dirpart/$fdir; as_fn_mkdir_p # echo "creating $dirpart/$file" echo '# dummy' > "$dirpart/$file" done done } ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi tmux-3.0a/configure.ac100644 001750 001750 00000032336 13570677043 0010525# configure.ac AC_INIT([tmux], 3.0a) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) AC_CONFIG_LIBOBJ_DIR(compat) AM_INIT_AUTOMAKE([foreign subdir-objects]) AC_CANONICAL_HOST # When CFLAGS isn't set at this stage and gcc is detected by the macro below, # autoconf will automatically use CFLAGS="-O2 -g". Prevent that by using an # empty default. : ${CFLAGS=""} # Save user CPPFLAGS, CFLAGS and LDFLAGS. We need to change them because # AC_CHECK_HEADER doesn't give us any other way to update the include # paths. But for Makefile.am we want to use AM_CPPFLAGS and friends. SAVED_CFLAGS="$CFLAGS" SAVED_CPPFLAGS="$CPPFLAGS" SAVED_LDFLAGS="$LDFLAGS" # Set up the compiler in two different ways and say yes we may want to install. AC_PROG_CC AM_PROG_CC_C_O AC_PROG_CC_C99 AC_PROG_CPP AC_PROG_EGREP AC_PROG_INSTALL AC_PROG_YACC PKG_PROG_PKG_CONFIG AC_USE_SYSTEM_EXTENSIONS # Default tmux.conf goes in /etc not ${prefix}/etc. test "$sysconfdir" = '${prefix}/etc' && sysconfdir=/etc # Is this --enable-debug? case "x$VERSION" in xnext*) enable_debug=yes;; esac AC_ARG_ENABLE( debug, AC_HELP_STRING(--enable-debug, enable debug build flags), ) AM_CONDITIONAL(IS_DEBUG, test "x$enable_debug" = xyes) # Is this a static build? AC_ARG_ENABLE( static, AC_HELP_STRING(--enable-static, create a static build) ) if test "x$enable_static" = xyes; then test "x$PKG_CONFIG" != x && PKG_CONFIG="$PKG_CONFIG --static" AM_LDFLAGS="-static $AM_LDFLAGS" LDFLAGS="$AM_LDFLAGS $SAVED_LDFLAGS" fi # Is this gcc? AM_CONDITIONAL(IS_GCC, test "x$GCC" = xyes) # Is this Sun CC? AC_EGREP_CPP( yes, [ #ifdef __SUNPRO_C yes #endif ], found_suncc=yes, found_suncc=no ) AM_CONDITIONAL(IS_SUNCC, test "x$found_suncc" = xyes) # Check for various headers. Alternatives included from compat.h. AC_CHECK_HEADERS([ \ bitstring.h \ dirent.h \ fcntl.h \ inttypes.h \ libutil.h \ ndir.h \ paths.h \ pty.h \ stdint.h \ sys/dir.h \ sys/ndir.h \ sys/tree.h \ util.h \ ]) # Look for library needed for flock. AC_SEARCH_LIBS(flock, bsd) # Check for functions that are replaced or omitted. AC_CHECK_FUNCS([ \ dirfd \ flock \ prctl \ sysconf \ ]) # Check for functions with a compatibility implementation. AC_REPLACE_FUNCS([ \ asprintf \ cfmakeraw \ closefrom \ explicit_bzero \ fgetln \ freezero \ getdtablecount \ getprogname \ memmem \ recallocarray \ reallocarray \ setenv \ setproctitle \ strcasestr \ strlcat \ strlcpy \ strndup \ strsep \ strtonum \ ]) AC_FUNC_STRNLEN # Look for clock_gettime. Must come before event_init. AC_SEARCH_LIBS(clock_gettime, rt) # Look for libevent. PKG_CHECK_MODULES( LIBEVENT, libevent, [ AM_CFLAGS="$LIBEVENT_CFLAGS $AM_CFLAGS" CFLAGS="$AM_CFLAGS $SAVED_CFLAGS" LIBS="$LIBEVENT_LIBS $LIBS" found_libevent=yes ], [ AC_SEARCH_LIBS( event_init, [event event-1.4 event2], found_libevent=yes, found_libevent=no ) ] ) AC_CHECK_HEADER( event.h, , found_libevent=no ) if test "x$found_libevent" = xno; then AC_MSG_ERROR("libevent not found") fi # Look for ncurses. PKG_CHECK_MODULES( LIBTINFO, tinfo, found_ncurses=yes, found_ncurses=no ) if test "x$found_ncurses" = xno; then PKG_CHECK_MODULES( LIBNCURSES, ncurses, found_ncurses=yes, found_ncurses=no ) fi if test "x$found_ncurses" = xno; then PKG_CHECK_MODULES( LIBNCURSES, ncursesw, found_ncurses=yes, found_ncurses=no ) fi if test "x$found_ncurses" = xyes; then AM_CFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $AM_CFLAGS" CFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $CFLAGS" LIBS="$LIBNCURSES_LIBS $LIBTINFO_LIBS $LIBS" else # pkg-config didn't work, try ncurses. AC_CHECK_LIB( tinfo, setupterm, found_ncurses=yes, found_ncurses=no ) if test "x$found_ncurses" = xno; then AC_CHECK_LIB( ncurses, setupterm, found_ncurses=yes, found_ncurses=no ) fi if test "x$found_ncurses" = xyes; then AC_CHECK_HEADER( ncurses.h, LIBS="$LIBS -lncurses", found_ncurses=no) fi fi if test "x$found_ncurses" = xyes; then AC_DEFINE(HAVE_NCURSES_H) else # No ncurses, try curses. AC_CHECK_LIB( curses, setupterm, found_curses=yes, found_curses=no ) AC_CHECK_HEADER( curses.h, , found_curses=no) if test "x$found_curses" = xyes; then LIBS="$LIBS -lcurses" AC_DEFINE(HAVE_CURSES_H) else AC_MSG_ERROR("curses not found") fi fi # Look for utempter. AC_ARG_ENABLE( utempter, AC_HELP_STRING(--enable-utempter, use utempter if it is installed) ) if test "x$enable_utempter" = xyes; then AC_CHECK_HEADER(utempter.h, enable_utempter=yes, enable_utempter=no) if test "x$enable_utempter" = xyes; then AC_SEARCH_LIBS( utempter_add_record, utempter, enable_utempter=yes, enable_utempter=no ) fi if test "x$enable_utempter" = xyes; then AC_DEFINE(HAVE_UTEMPTER) else AC_MSG_ERROR("utempter not found") fi fi # Look for utf8proc. AC_ARG_ENABLE( utf8proc, AC_HELP_STRING(--enable-utf8proc, use utf8proc if it is installed) ) if test "x$enable_utf8proc" = xyes; then AC_CHECK_HEADER(utf8proc.h, enable_utf8proc=yes, enable_utf8proc=no) if test "x$enable_utf8proc" = xyes; then AC_SEARCH_LIBS( utf8proc_charwidth, utf8proc, enable_utf8proc=yes, enable_utf8proc=no ) fi if test "x$enable_utf8proc" = xyes; then AC_DEFINE(HAVE_UTF8PROC) else AC_MSG_ERROR("utf8proc not found") fi fi AM_CONDITIONAL(HAVE_UTF8PROC, [test "x$enable_utf8proc" = xyes]) # Check for b64_ntop. If we have b64_ntop, we assume b64_pton as well. AC_MSG_CHECKING(for b64_ntop) AC_TRY_LINK( [ #include #include #include ], [b64_ntop(NULL, 0, NULL, 0);], found_b64_ntop=yes, found_b64_ntop=no ) if test "x$found_b64_ntop" = xno; then AC_MSG_RESULT(no) AC_MSG_CHECKING(for b64_ntop with -lresolv) OLD_LIBS="$LIBS" LIBS="$LIBS -lresolv" AC_TRY_LINK( [ #include #include #include ], [b64_ntop(NULL, 0, NULL, 0);], found_b64_ntop=yes, found_b64_ntop=no ) if test "x$found_b64_ntop" = xno; then LIBS="$OLD_LIBS" AC_MSG_RESULT(no) fi fi if test "x$found_b64_ntop" = xyes; then AC_DEFINE(HAVE_B64_NTOP) AC_MSG_RESULT(yes) else AC_LIBOBJ(base64) fi # Look for networking libraries. AC_SEARCH_LIBS(inet_ntoa, nsl) AC_SEARCH_LIBS(socket, socket) AC_CHECK_LIB(xnet, socket) # Check for CMSG_DATA. Some platforms require _XOPEN_SOURCE_EXTENDED (for # example see xopen_networking(7) on HP-UX). XOPEN_DEFINES= AC_MSG_CHECKING(for CMSG_DATA) AC_EGREP_CPP( yes, [ #include #ifdef CMSG_DATA yes #endif ], found_cmsg_data=yes, found_cmsg_data=no ) AC_MSG_RESULT($found_cmsg_data) if test "x$found_cmsg_data" = xno; then AC_MSG_CHECKING(if CMSG_DATA needs _XOPEN_SOURCE_EXTENDED) AC_EGREP_CPP( yes, [ #define _XOPEN_SOURCE 1 #define _XOPEN_SOURCE_EXTENDED 1 #include #ifdef CMSG_DATA yes #endif ], found_cmsg_data=yes, found_cmsg_data=no ) AC_MSG_RESULT($found_cmsg_data) if test "x$found_cmsg_data" = xyes; then XOPEN_DEFINES="-D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED" else AC_MSG_ERROR("CMSG_DATA not found") fi fi AC_SUBST(XOPEN_DEFINES) # Look for err and friends in err.h. AC_CHECK_FUNC(err, found_err_h=yes, found_err_h=no) AC_CHECK_FUNC(errx, , found_err_h=no) AC_CHECK_FUNC(warn, , found_err_h=no) AC_CHECK_FUNC(warnx, , found_err_h=no) if test "x$found_err_h" = xyes; then AC_CHECK_HEADER(err.h, , found_err_h=no) else AC_LIBOBJ(err) fi # Look for imsg_init in libutil. AC_SEARCH_LIBS(imsg_init, util, found_imsg_init=yes, found_imsg_init=no) if test "x$found_imsg_init" = xyes; then AC_DEFINE(HAVE_IMSG) else AC_LIBOBJ(imsg) AC_LIBOBJ(imsg-buffer) fi # Look for daemon, compat/daemon.c used if missing. Solaris 10 has it in # libresolv, but no declaration anywhere, so check for declaration as well as # function. AC_CHECK_FUNC(daemon, found_daemon=yes, found_daemon=no) AC_CHECK_DECL( daemon, , found_daemon=no, [ #include #include ] ) if test "x$found_daemon" = xyes; then AC_DEFINE(HAVE_DAEMON) else AC_LIBOBJ(daemon) fi # Look for stravis, compat/{vis,unvis}.c used if missing. AC_CHECK_FUNC(stravis, found_stravis=yes, found_stravis=no) if test "x$found_stravis" = xyes; then AC_MSG_CHECKING(if strnvis is broken) AC_EGREP_HEADER([strnvis\(char \*, const char \*, size_t, int\)], vis.h, AC_MSG_RESULT(no), [found_stravis=no]) if test "x$found_stravis" = xno; then AC_MSG_RESULT(yes) fi fi if test "x$found_stravis" = xyes; then AC_CHECK_DECL( VIS_DQ, , found_stravis=no, [ #include #include ] ) fi if test "x$found_stravis" = xyes; then AC_DEFINE(HAVE_VIS) else AC_LIBOBJ(vis) AC_LIBOBJ(unvis) fi # Look for getopt. glibc's getopt does not enforce argument order and the ways # of making it do so are stupid, so just use our own instead. AC_CHECK_FUNC(getopt, found_getopt=yes, found_getopt=no) if test "x$found_getopt" != xno; then AC_MSG_CHECKING(if getopt is suitable) AC_EGREP_CPP( yes, [ #include #ifdef __GLIBC__ yes #endif ], [ found_getopt=no AC_MSG_RESULT(no) ], AC_MSG_RESULT(yes)) fi if test "x$found_getopt" != xno; then AC_CHECK_DECLS( [optarg, optind, optreset], , found_getopt=no, [ #include ]) fi if test "x$found_getopt" != xno; then AC_DEFINE(HAVE_GETOPT) else AC_LIBOBJ(getopt) fi # Look for fdforkpty and forkpty in libutil. AC_SEARCH_LIBS(fdforkpty, util, found_fdforkpty=yes, found_fdforkpty=no) if test "x$found_fdforkpty" = xyes; then AC_DEFINE(HAVE_FDFORKPTY) else AC_LIBOBJ(fdforkpty) fi AC_SEARCH_LIBS(forkpty, util, found_forkpty=yes, found_forkpty=no) if test "x$found_forkpty" = xyes; then AC_DEFINE(HAVE_FORKPTY) fi AM_CONDITIONAL(NEED_FORKPTY, test "x$found_forkpty" = xno) # Look for kinfo_getfile in libutil. AC_SEARCH_LIBS(kinfo_getfile, [util util-freebsd]) # Look for a suitable queue.h. AC_CHECK_DECL( TAILQ_CONCAT, found_queue_h=yes, found_queue_h=no, [#include ] ) AC_CHECK_DECL( TAILQ_PREV, found_queue_h=yes, found_queue_h=no, [#include ] ) AC_CHECK_DECL( TAILQ_REPLACE, , found_queue_h=no, [#include ] ) if test "x$found_queue_h" = xyes; then AC_DEFINE(HAVE_QUEUE_H) fi # Look for __progname. AC_MSG_CHECKING(for __progname) AC_LINK_IFELSE([AC_LANG_SOURCE( [ #include #include extern char *__progname; int main(void) { const char *cp = __progname; printf("%s\n", cp); exit(0); } ])], [AC_DEFINE(HAVE___PROGNAME) AC_MSG_RESULT(yes)], AC_MSG_RESULT(no) ) # Look for program_invocation_short_name. AC_MSG_CHECKING(for program_invocation_short_name) AC_LINK_IFELSE([AC_LANG_SOURCE( [ #include #include #include int main(void) { const char *cp = program_invocation_short_name; printf("%s\n", cp); exit(0); } ])], [AC_DEFINE(HAVE_PROGRAM_INVOCATION_SHORT_NAME) AC_MSG_RESULT(yes)], AC_MSG_RESULT(no) ) # Look for prctl(PR_SET_NAME). AC_CHECK_DECL( PR_SET_NAME, AC_DEFINE(HAVE_PR_SET_NAME), , [#include ] ) # Look for fcntl(F_CLOSEM). AC_CHECK_DECL( F_CLOSEM, AC_DEFINE(HAVE_FCNTL_CLOSEM), , [#include ] ) # Look for /proc/$$. AC_MSG_CHECKING(for /proc/\$\$) if test -d /proc/$$; then AC_DEFINE(HAVE_PROC_PID) AC_MSG_RESULT(yes) else AC_MSG_RESULT(no) fi # Man page defaults to mdoc. MANFORMAT=mdoc AC_SUBST(MANFORMAT) # Figure out the platform. AC_MSG_CHECKING(platform) case "$host_os" in *aix*) AC_MSG_RESULT(aix) PLATFORM=aix ;; *darwin*) AC_MSG_RESULT(darwin) PLATFORM=darwin # # OS X CMSG_FIRSTHDR is broken, so redefine it with a working # one. daemon works but has some stupid side effects, so use # our internal version which has a workaround. # AC_DEFINE(BROKEN_CMSG_FIRSTHDR) AC_LIBOBJ(daemon) AC_LIBOBJ(daemon-darwin) ;; *dragonfly*) AC_MSG_RESULT(dragonfly) PLATFORM=dragonfly ;; *linux*) AC_MSG_RESULT(linux) PLATFORM=linux ;; *freebsd*) AC_MSG_RESULT(freebsd) PLATFORM=freebsd ;; *netbsd*) AC_MSG_RESULT(netbsd) PLATFORM=netbsd ;; *openbsd*) AC_MSG_RESULT(openbsd) PLATFORM=openbsd ;; *sunos*) AC_MSG_RESULT(sunos) PLATFORM=sunos ;; *solaris*) AC_MSG_RESULT(sunos) PLATFORM=sunos case `/usr/bin/nroff --version 2>&1` in *GNU*) # Solaris 11.4 and later use GNU groff. MANFORMAT=mdoc ;; *) # Solaris 2.0 to 11.3 use AT&T nroff. MANFORMAT=man ;; esac ;; *hpux*) AC_MSG_RESULT(hpux) PLATFORM=hpux ;; *cygwin*|*msys*) AC_MSG_RESULT(cygwin) PLATFORM=cygwin ;; *) AC_MSG_RESULT(unknown) PLATFORM=unknown ;; esac AC_SUBST(PLATFORM) AM_CONDITIONAL(IS_AIX, test "x$PLATFORM" = xaix) AM_CONDITIONAL(IS_DARWIN, test "x$PLATFORM" = xdarwin) AM_CONDITIONAL(IS_DRAGONFLY, test "x$PLATFORM" = xdragonfly) AM_CONDITIONAL(IS_LINUX, test "x$PLATFORM" = xlinux) AM_CONDITIONAL(IS_FREEBSD, test "x$PLATFORM" = xfreebsd) AM_CONDITIONAL(IS_NETBSD, test "x$PLATFORM" = xnetbsd) AM_CONDITIONAL(IS_OPENBSD, test "x$PLATFORM" = xopenbsd) AM_CONDITIONAL(IS_SUNOS, test "x$PLATFORM" = xsunos) AM_CONDITIONAL(IS_HPUX, test "x$PLATFORM" = xhpux) AM_CONDITIONAL(IS_UNKNOWN, test "x$PLATFORM" = xunknown) # Save our CFLAGS/CPPFLAGS/LDFLAGS for the Makefile and restore the old user # variables. AC_SUBST(AM_CPPFLAGS) CPPFLAGS="$SAVED_CPPFLAGS" AC_SUBST(AM_CFLAGS) CFLAGS="$SAVED_CFLAGS" AC_SUBST(AM_LDFLAGS) LDFLAGS="$SAVED_LDFLAGS" # autoconf should create a Makefile. AC_OUTPUT(Makefile) tmux-3.0a/aclocal.m4100644 001750 001750 00000146255 13570677622 0010110# generated automatically by aclocal 1.15.1 -*- Autoconf -*- # Copyright (C) 1996-2017 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],, [m4_warning([this file was generated for autoconf 2.69. You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically 'autoreconf'.])]) dnl pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- dnl serial 11 (pkg-config-0.29.1) dnl dnl Copyright © 2004 Scott James Remnant . dnl Copyright © 2012-2015 Dan Nicholson dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by dnl the Free Software Foundation; either version 2 of the License, or dnl (at your option) any later version. dnl dnl This program is distributed in the hope that it will be useful, but dnl WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU dnl General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl along with this program; if not, write to the Free Software dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA dnl 02111-1307, USA. dnl dnl As a special exception to the GNU General Public License, if you dnl distribute this file as part of a program that contains a dnl configuration script generated by Autoconf, you may include it under dnl the same distribution terms that you use for the rest of that dnl program. dnl PKG_PREREQ(MIN-VERSION) dnl ----------------------- dnl Since: 0.29 dnl dnl Verify that the version of the pkg-config macros are at least dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's dnl installed version of pkg-config, this checks the developer's version dnl of pkg.m4 when generating configure. dnl dnl To ensure that this macro is defined, also add: dnl m4_ifndef([PKG_PREREQ], dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])]) dnl dnl See the "Since" comment for each macro you use to see what version dnl of the macros you require. m4_defun([PKG_PREREQ], [m4_define([PKG_MACROS_VERSION], [0.29.1]) m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) ])dnl PKG_PREREQ dnl PKG_PROG_PKG_CONFIG([MIN-VERSION]) dnl ---------------------------------- dnl Since: 0.16 dnl dnl Search for the pkg-config tool and set the PKG_CONFIG variable to dnl first found in the path. Checks that the version of pkg-config found dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is dnl used since that's the first version where most current features of dnl pkg-config existed. AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])dnl PKG_PROG_PKG_CONFIG dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------------------------------- dnl Since: 0.18 dnl dnl Check to see whether a particular set of modules exists. Similar to dnl PKG_CHECK_MODULES(), but does not set variables or print errors. dnl dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) dnl only at the first occurence in configure.ac, so if the first place dnl it's called might be skipped (such as if it is within an "if", you dnl have to call PKG_CHECK_EXISTS manually AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_default([$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) dnl --------------------------------------------- dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting dnl pkg_failed based on the result. m4_define([_PKG_CONFIG], [if test -n "$$1"; then pkg_cv_[]$1="$$1" elif test -n "$PKG_CONFIG"; then PKG_CHECK_EXISTS([$3], [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes ], [pkg_failed=yes]) else pkg_failed=untried fi[]dnl ])dnl _PKG_CONFIG dnl _PKG_SHORT_ERRORS_SUPPORTED dnl --------------------------- dnl Internal check to see if pkg-config supports short errors. AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ])dnl _PKG_SHORT_ERRORS_SUPPORTED dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl -------------------------------------------------------------- dnl Since: 0.4.0 dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES might not happen, you should be sure to include an dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no AC_MSG_CHECKING([for $1]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then AC_MSG_RESULT([no]) _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` else $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD m4_default([$4], [AC_MSG_ERROR( [Package requirements ($2) were not met: $$1_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. _PKG_TEXT])[]dnl ]) elif test $pkg_failed = untried; then AC_MSG_RESULT([no]) m4_default([$4], [AC_MSG_FAILURE( [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. _PKG_TEXT To get pkg-config, see .])[]dnl ]) else $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) $3 fi[]dnl ])dnl PKG_CHECK_MODULES dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl --------------------------------------------------------------------- dnl Since: 0.29 dnl dnl Checks for existence of MODULES and gathers its build flags with dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags dnl and VARIABLE-PREFIX_LIBS from --libs. dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to dnl include an explicit call to PKG_PROG_PKG_CONFIG in your dnl configure.ac. AC_DEFUN([PKG_CHECK_MODULES_STATIC], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl _save_PKG_CONFIG=$PKG_CONFIG PKG_CONFIG="$PKG_CONFIG --static" PKG_CHECK_MODULES($@) PKG_CONFIG=$_save_PKG_CONFIG[]dnl ])dnl PKG_CHECK_MODULES_STATIC dnl PKG_INSTALLDIR([DIRECTORY]) dnl ------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable pkgconfigdir as the location where a module dnl should install pkg-config .pc files. By default the directory is dnl $libdir/pkgconfig, but the default can be changed by passing dnl DIRECTORY. The user can override through the --with-pkgconfigdir dnl parameter. AC_DEFUN([PKG_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([pkgconfigdir], [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, [with_pkgconfigdir=]pkg_default) AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_INSTALLDIR dnl PKG_NOARCH_INSTALLDIR([DIRECTORY]) dnl -------------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable noarch_pkgconfigdir as the location where a dnl module should install arch-independent pkg-config .pc files. By dnl default the directory is $datadir/pkgconfig, but the default can be dnl changed by passing DIRECTORY. The user can override through the dnl --with-noarch-pkgconfigdir parameter. AC_DEFUN([PKG_NOARCH_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([noarch-pkgconfigdir], [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, [with_noarch_pkgconfigdir=]pkg_default) AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_NOARCH_INSTALLDIR dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------- dnl Since: 0.28 dnl dnl Retrieves the value of the pkg-config variable for the given module. AC_DEFUN([PKG_CHECK_VAR], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl _PKG_CONFIG([$1], [variable="][$3]["], [$2]) AS_VAR_COPY([$1], [pkg_cv_][$1]) AS_VAR_IF([$1], [""], [$5], [$4])dnl ])dnl PKG_CHECK_VAR # Copyright (C) 2002-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_AUTOMAKE_VERSION(VERSION) # ---------------------------- # Automake X.Y traces this macro to ensure aclocal.m4 has been # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.15' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. m4_if([$1], [1.15.1], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) # _AM_AUTOCONF_VERSION(VERSION) # ----------------------------- # aclocal traces this macro to find the Autoconf version. # This is a private macro too. Using m4_define simplifies # the logic in aclocal, which can simply ignore this definition. m4_define([_AM_AUTOCONF_VERSION], []) # AM_SET_CURRENT_AUTOMAKE_VERSION # ------------------------------- # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], [AM_AUTOMAKE_VERSION([1.15.1])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- # Copyright (C) 2001-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets # $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to # '$srcdir', '$srcdir/..', or '$srcdir/../..'. # # Of course, Automake must honor this variable whenever it calls a # tool from the auxiliary directory. The problem is that $srcdir (and # therefore $ac_aux_dir as well) can be either absolute or relative, # depending on how configure is run. This is pretty annoying, since # it makes $ac_aux_dir quite unusable in subdirectories: in the top # source directory, any form will work fine, but in subdirectories a # relative path needs to be adjusted first. # # $ac_aux_dir/missing # fails when called from a subdirectory if $ac_aux_dir is relative # $top_srcdir/$ac_aux_dir/missing # fails if $ac_aux_dir is absolute, # fails when called from a subdirectory in a VPATH build with # a relative $ac_aux_dir # # The reason of the latter failure is that $top_srcdir and $ac_aux_dir # are both prefixed by $srcdir. In an in-source build this is usually # harmless because $srcdir is '.', but things will broke when you # start a VPATH build or use an absolute $srcdir. # # So we could use something similar to $top_srcdir/$ac_aux_dir/missing, # iff we strip the leading $srcdir from $ac_aux_dir. That would be: # am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` # and then we would define $MISSING as # MISSING="\${SHELL} $am_aux_dir/missing" # This will work as long as MISSING is not called from configure, because # unfortunately $(top_srcdir) has no meaning in configure. # However there are other variables, like CC, which are often used in # configure, and could therefore not use this "fixed" $ac_aux_dir. # # Another solution, used here, is to always expand $ac_aux_dir to an # absolute PATH. The drawback is that using absolute paths prevent a # configured tree to be moved without reconfiguration. AC_DEFUN([AM_AUX_DIR_EXPAND], [AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` ]) # AM_CONDITIONAL -*- Autoconf -*- # Copyright (C) 1997-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_CONDITIONAL(NAME, SHELL-CONDITION) # ------------------------------------- # Define a conditional. AC_DEFUN([AM_CONDITIONAL], [AC_PREREQ([2.52])dnl m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl AC_SUBST([$1_TRUE])dnl AC_SUBST([$1_FALSE])dnl _AM_SUBST_NOTMAKE([$1_TRUE])dnl _AM_SUBST_NOTMAKE([$1_FALSE])dnl m4_define([_AM_COND_VALUE_$1], [$2])dnl if $2; then $1_TRUE= $1_FALSE='#' else $1_TRUE='#' $1_FALSE= fi AC_CONFIG_COMMANDS_PRE( [if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then AC_MSG_ERROR([[conditional "$1" was never defined. Usually this means the macro was only invoked conditionally.]]) fi])]) # Copyright (C) 1999-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be # written in clear, in which case automake, when reading aclocal.m4, # will think it sees a *use*, and therefore will trigger all it's # C support machinery. Also note that it means that autoscan, seeing # CC etc. in the Makefile, will ask for an AC_PROG_CC use... # _AM_DEPENDENCIES(NAME) # ---------------------- # See how the compiler implements dependency checking. # NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". # We try a few techniques and use that to set a single cache variable. # # We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was # modified to invoke _AM_DEPENDENCIES(CC); we would have a circular # dependency, and given that the user is not expected to run this macro, # just rely on AC_PROG_CC. AC_DEFUN([_AM_DEPENDENCIES], [AC_REQUIRE([AM_SET_DEPDIR])dnl AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl AC_REQUIRE([AM_MAKE_INCLUDE])dnl AC_REQUIRE([AM_DEP_TRACK])dnl m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], [$1], [CXX], [depcc="$CXX" am_compiler_list=], [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], [$1], [UPC], [depcc="$UPC" am_compiler_list=], [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], [depcc="$$1" am_compiler_list=]) AC_CACHE_CHECK([dependency style of $depcc], [am_cv_$1_dependencies_compiler_type], [if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_$1_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` fi am__universal=false m4_case([$1], [CC], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac], [CXX], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac]) for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_$1_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_$1_dependencies_compiler_type=none fi ]) AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) AM_CONDITIONAL([am__fastdep$1], [ test "x$enable_dependency_tracking" != xno \ && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) ]) # AM_SET_DEPDIR # ------------- # Choose a directory name for dependency files. # This macro is AC_REQUIREd in _AM_DEPENDENCIES. AC_DEFUN([AM_SET_DEPDIR], [AC_REQUIRE([AM_SET_LEADING_DOT])dnl AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl ]) # AM_DEP_TRACK # ------------ AC_DEFUN([AM_DEP_TRACK], [AC_ARG_ENABLE([dependency-tracking], [dnl AS_HELP_STRING( [--enable-dependency-tracking], [do not reject slow dependency extractors]) AS_HELP_STRING( [--disable-dependency-tracking], [speeds up one-time build])]) if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) AC_SUBST([AMDEPBACKSLASH])dnl _AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl AC_SUBST([am__nodep])dnl _AM_SUBST_NOTMAKE([am__nodep])dnl ]) # Generate code to set up dependency tracking. -*- Autoconf -*- # Copyright (C) 1999-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_OUTPUT_DEPENDENCY_COMMANDS # ------------------------------ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], [{ # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. case $CONFIG_FILES in *\'*) eval set x "$CONFIG_FILES" ;; *) set x $CONFIG_FILES ;; esac shift for mf do # Strip MF so we end up with the name of the file. mf=`echo "$mf" | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile or not. # We used to match only the files named 'Makefile.in', but # some people rename them; so instead we look at the file content. # Grep'ing the first line is not enough: some people post-process # each Makefile.in and add a new line on top of each file to say so. # Grep'ing the whole file is not good either: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then dirpart=`AS_DIRNAME("$mf")` else continue fi # Extract the definition of DEPDIR, am__include, and am__quote # from the Makefile without running 'make'. DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` test -z "$DEPDIR" && continue am__include=`sed -n 's/^am__include = //p' < "$mf"` test -z "$am__include" && continue am__quote=`sed -n 's/^am__quote = //p' < "$mf"` # Find all dependency output files, they are included files with # $(DEPDIR) in their names. We invoke sed twice because it is the # simplest approach to changing $(DEPDIR) to its actual value in the # expansion. for file in `sed -n " s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do # Make sure the directory exists. test -f "$dirpart/$file" && continue fdir=`AS_DIRNAME(["$file"])` AS_MKDIR_P([$dirpart/$fdir]) # echo "creating $dirpart/$file" echo '# dummy' > "$dirpart/$file" done done } ])# _AM_OUTPUT_DEPENDENCY_COMMANDS # AM_OUTPUT_DEPENDENCY_COMMANDS # ----------------------------- # This macro should only be invoked once -- use via AC_REQUIRE. # # This code is only required when automatic dependency tracking # is enabled. FIXME. This creates each '.P' file that we will # need in order to bootstrap the dependency handling code. AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles], [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) ]) # Do all the work for Automake. -*- Autoconf -*- # Copyright (C) 1996-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This macro actually does too much. Some checks are only needed if # your package does certain things. But this isn't really a big deal. dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC]) [_AM_PROG_CC_C_O ]) # AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) # AM_INIT_AUTOMAKE([OPTIONS]) # ----------------------------------------------- # The call with PACKAGE and VERSION arguments is the old style # call (pre autoconf-2.50), which is being phased out. PACKAGE # and VERSION should now be passed to AC_INIT and removed from # the call to AM_INIT_AUTOMAKE. # We support both call styles for the transition. After # the next Automake release, Autoconf can make the AC_INIT # arguments mandatory, and then we can depend on a new Autoconf # release and drop the old call support. AC_DEFUN([AM_INIT_AUTOMAKE], [AC_PREREQ([2.65])dnl dnl Autoconf wants to disallow AM_ names. We explicitly allow dnl the ones we care about. m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl AC_REQUIRE([AC_PROG_INSTALL])dnl if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl # test to see if srcdir already configured if test -f $srcdir/config.status; then AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi AC_SUBST([CYGPATH_W]) # Define the identity of the package. dnl Distinguish between old-style and new-style calls. m4_ifval([$2], [AC_DIAGNOSE([obsolete], [$0: two- and three-arguments forms are deprecated.]) m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl AC_SUBST([PACKAGE], [$1])dnl AC_SUBST([VERSION], [$2])], [_AM_SET_OPTIONS([$1])dnl dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. m4_if( m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), [ok:ok],, [m4_fatal([AC_INIT should be called with package and version arguments])])dnl AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl _AM_IF_OPTION([no-define],, [AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl # Some tools Automake needs. AC_REQUIRE([AM_SANITY_CHECK])dnl AC_REQUIRE([AC_ARG_PROGRAM])dnl AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) AM_MISSING_PROG([AUTOCONF], [autoconf]) AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) AM_MISSING_PROG([AUTOHEADER], [autoheader]) AM_MISSING_PROG([MAKEINFO], [makeinfo]) AC_REQUIRE([AM_PROG_INSTALL_SH])dnl AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl AC_REQUIRE([AC_PROG_MKDIR_P])dnl # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # AC_SUBST([mkdir_p], ['$(MKDIR_P)']) # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AM_SET_LEADING_DOT])dnl _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], [_AM_PROG_TAR([v7])])]) _AM_IF_OPTION([no-dependencies],, [AC_PROVIDE_IFELSE([AC_PROG_CC], [_AM_DEPENDENCIES([CC])], [m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_CXX], [_AM_DEPENDENCIES([CXX])], [m4_define([AC_PROG_CXX], m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJC], [_AM_DEPENDENCIES([OBJC])], [m4_define([AC_PROG_OBJC], m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], [_AM_DEPENDENCIES([OBJCXX])], [m4_define([AC_PROG_OBJCXX], m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl ]) AC_REQUIRE([AM_SILENT_RULES])dnl dnl The testsuite driver may need to know about EXEEXT, so add the dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. AC_CONFIG_COMMANDS_PRE(dnl [m4_provide_if([_AM_COMPILER_EXEEXT], [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) fi fi dnl The trailing newline in this macro's definition is deliberate, for dnl backward compatibility and to allow trailing 'dnl'-style comments dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841. ]) dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further dnl mangled by Autoconf and run in a shell conditional statement. m4_define([_AC_COMPILER_EXEEXT], m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) # When config.status generates a header, we must update the stamp-h file. # This file resides in the same directory as the config header # that is generated. The stamp files are numbered to have different names. # Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the # loop where config.status creates the headers, so we can generate # our stamp files there. AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], [# Compute $1's index in $config_headers. _am_arg=$1 _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) # Copyright (C) 2001-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_SH # ------------------ # Define $install_sh. AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi AC_SUBST([install_sh])]) # Copyright (C) 2003-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # Check whether the underlying file-system supports filenames # with a leading dot. For instance MS-DOS doesn't. AC_DEFUN([AM_SET_LEADING_DOT], [rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null AC_SUBST([am__leading_dot])]) # Check to see how 'make' treats includes. -*- Autoconf -*- # Copyright (C) 2001-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MAKE_INCLUDE() # ----------------- # Check to see how make treats includes. AC_DEFUN([AM_MAKE_INCLUDE], [am_make=${MAKE-make} cat > confinc << 'END' am__doit: @echo this is the am__doit target .PHONY: am__doit END # If we don't find an include directive, just comment out the code. AC_MSG_CHECKING([for style of include used by $am_make]) am__include="#" am__quote= _am_result=none # First try GNU make style include. echo "include confinc" > confmf # Ignore all kinds of additional output from 'make'. case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=include am__quote= _am_result=GNU ;; esac # Now try BSD make style include. if test "$am__include" = "#"; then echo '.include "confinc"' > confmf case `$am_make -s -f confmf 2> /dev/null` in #( *the\ am__doit\ target*) am__include=.include am__quote="\"" _am_result=BSD ;; esac fi AC_SUBST([am__include]) AC_SUBST([am__quote]) AC_MSG_RESULT([$_am_result]) rm -f confinc confmf ]) # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- # Copyright (C) 1997-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MISSING_PROG(NAME, PROGRAM) # ------------------------------ AC_DEFUN([AM_MISSING_PROG], [AC_REQUIRE([AM_MISSING_HAS_RUN]) $1=${$1-"${am_missing_run}$2"} AC_SUBST($1)]) # AM_MISSING_HAS_RUN # ------------------ # Define MISSING if not defined so far and test if it is modern enough. # If it is, set am_missing_run to use it, otherwise, to nothing. AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([missing])dnl if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; *) MISSING="\${SHELL} $am_aux_dir/missing" ;; esac fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= AC_MSG_WARN(['missing' script is too old or missing]) fi ]) # Helper functions for option handling. -*- Autoconf -*- # Copyright (C) 2001-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_MANGLE_OPTION(NAME) # ----------------------- AC_DEFUN([_AM_MANGLE_OPTION], [[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) # _AM_SET_OPTION(NAME) # -------------------- # Set option NAME. Presently that only means defining a flag for this option. AC_DEFUN([_AM_SET_OPTION], [m4_define(_AM_MANGLE_OPTION([$1]), [1])]) # _AM_SET_OPTIONS(OPTIONS) # ------------------------ # OPTIONS is a space-separated list of Automake options. AC_DEFUN([_AM_SET_OPTIONS], [m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) # _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) # ------------------------------------------- # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) # Copyright (C) 1999-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_CC_C_O # --------------- # Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC # to automatically call this. AC_DEFUN([_AM_PROG_CC_C_O], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([compile])dnl AC_LANG_PUSH([C])dnl AC_CACHE_CHECK( [whether $CC understands -c and -o together], [am_cv_prog_cc_c_o], [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i]) if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi AC_LANG_POP([C])]) # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) # Copyright (C) 2001-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_RUN_LOG(COMMAND) # ------------------- # Run COMMAND, save the exit status in ac_status, and log it. # (This has been adapted from Autoconf's _AC_RUN_LOG macro.) AC_DEFUN([AM_RUN_LOG], [{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD (exit $ac_status); }]) # Check to make sure that the build environment is sane. -*- Autoconf -*- # Copyright (C) 1996-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SANITY_CHECK # --------------- AC_DEFUN([AM_SANITY_CHECK], [AC_MSG_CHECKING([whether build environment is sane]) # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[[\\\"\#\$\&\'\`$am_lf]]*) AC_MSG_ERROR([unsafe absolute working directory name]);; esac case $srcdir in *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$[*]" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$[*]" != "X $srcdir/configure conftest.file" \ && test "$[*]" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken alias in your environment]) fi if test "$[2]" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$[2]" = conftest.file ) then # Ok. : else AC_MSG_ERROR([newly created file is older than distributed files! Check your system clock]) fi AC_MSG_RESULT([yes]) # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi AC_CONFIG_COMMANDS_PRE( [AC_MSG_CHECKING([that generated files are newer than configure]) if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi AC_MSG_RESULT([done])]) rm -f conftest.file ]) # Copyright (C) 2009-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SILENT_RULES([DEFAULT]) # -------------------------- # Enable less verbose build rules; with the default set to DEFAULT # ("yes" being less verbose, "no" or empty being verbose). AC_DEFUN([AM_SILENT_RULES], [AC_ARG_ENABLE([silent-rules], [dnl AS_HELP_STRING( [--enable-silent-rules], [less verbose build output (undo: "make V=1")]) AS_HELP_STRING( [--disable-silent-rules], [verbose build output (undo: "make V=0")])dnl ]) case $enable_silent_rules in @%:@ ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; esac dnl dnl A few 'make' implementations (e.g., NonStop OS and NextStep) dnl do not support nested variable expansions. dnl See automake bug#9928 and bug#10237. am_make=${MAKE-make} AC_CACHE_CHECK([whether $am_make supports nested variables], [am_cv_make_support_nested_variables], [if AS_ECHO([['TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi]) if test $am_cv_make_support_nested_variables = yes; then dnl Using '$V' instead of '$(V)' breaks IRIX make. AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AC_SUBST([AM_V])dnl AM_SUBST_NOTMAKE([AM_V])dnl AC_SUBST([AM_DEFAULT_V])dnl AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl AC_SUBST([AM_DEFAULT_VERBOSITY])dnl AM_BACKSLASH='\' AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) # Copyright (C) 2001-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_STRIP # --------------------- # One issue with vendor 'install' (even GNU) is that you can't # specify the program used to strip binaries. This is especially # annoying in cross-compiling environments, where the build's strip # is unlikely to handle the host's binaries. # Fortunately install-sh will honor a STRIPPROG variable, so we # always use install-sh in "make install-strip", and initialize # STRIPPROG with the value of the STRIP variable (set by the user). AC_DEFUN([AM_PROG_INSTALL_STRIP], [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. if test "$cross_compiling" != no; then AC_CHECK_TOOL([STRIP], [strip], :) fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) # Copyright (C) 2006-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_SUBST_NOTMAKE(VARIABLE) # --------------------------- # Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. # This macro is traced by Automake. AC_DEFUN([_AM_SUBST_NOTMAKE]) # AM_SUBST_NOTMAKE(VARIABLE) # -------------------------- # Public sister of _AM_SUBST_NOTMAKE. AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- # Copyright (C) 2004-2017 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_TAR(FORMAT) # -------------------- # Check how to create a tarball in format FORMAT. # FORMAT should be one of 'v7', 'ustar', or 'pax'. # # Substitute a variable $(am__tar) that is a command # writing to stdout a FORMAT-tarball containing the directory # $tardir. # tardir=directory && $(am__tar) > result.tar # # Substitute a variable $(am__untar) that extract such # a tarball read from stdin. # $(am__untar) < result.tar # AC_DEFUN([_AM_PROG_TAR], [# Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AC_SUBST([AMTAR], ['$${TAR-tar}']) # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' m4_if([$1], [v7], [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], [m4_case([$1], [ustar], [# The POSIX 1988 'ustar' format is defined with fixed-size fields. # There is notably a 21 bits limit for the UID and the GID. In fact, # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 # and bug#13588). am_max_uid=2097151 # 2^21 - 1 am_max_gid=$am_max_uid # The $UID and $GID variables are not portable, so we need to resort # to the POSIX-mandated id(1) utility. Errors in the 'id' calls # below are definitely unexpected, so allow the users to see them # (that is, avoid stderr redirection). am_uid=`id -u || echo unknown` am_gid=`id -g || echo unknown` AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) if test $am_uid -le $am_max_uid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) if test $am_gid -le $am_max_gid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi], [pax], [], [m4_fatal([Unknown tar format])]) AC_MSG_CHECKING([how to create a $1 tar archive]) # Go ahead even if we have the value already cached. We do so because we # need to set the values for the 'am__tar' and 'am__untar' variables. _am_tools=${am_cv_prog_tar_$1-$_am_tools} for _am_tool in $_am_tools; do case $_am_tool in gnutar) for _am_tar in tar gnutar gtar; do AM_RUN_LOG([$_am_tar --version]) && break done am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' am__untar="$_am_tar -xf -" ;; plaintar) # Must skip GNU tar: if it does not support --format= it doesn't create # ustar tarball either. (tar --version) >/dev/null 2>&1 && continue am__tar='tar chf - "$$tardir"' am__tar_='tar chf - "$tardir"' am__untar='tar xf -' ;; pax) am__tar='pax -L -x $1 -w "$$tardir"' am__tar_='pax -L -x $1 -w "$tardir"' am__untar='pax -r' ;; cpio) am__tar='find "$$tardir" -print | cpio -o -H $1 -L' am__tar_='find "$tardir" -print | cpio -o -H $1 -L' am__untar='cpio -i -H $1 -d' ;; none) am__tar=false am__tar_=false am__untar=false ;; esac # If the value was cached, stop now. We just wanted to have am__tar # and am__untar set. test -n "${am_cv_prog_tar_$1}" && break # tar/untar a dummy directory, and stop if the command works. rm -rf conftest.dir mkdir conftest.dir echo GrepMe > conftest.dir/file AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) rm -rf conftest.dir if test -s conftest.tar; then AM_RUN_LOG([$am__untar /dev/null 2>&1 && break fi done rm -rf conftest.dir AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) AC_MSG_RESULT([$am_cv_prog_tar_$1])]) AC_SUBST([am__tar]) AC_SUBST([am__untar]) ]) # _AM_PROG_TAR tmux-3.0a/Makefile.in100644 001750 001750 00000131501 13570677633 0010303# Makefile.in generated by automake 1.15.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2017 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # Makefile.am VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ bin_PROGRAMS = tmux$(EXEEXT) # Set flags for gcc. @IS_GCC_TRUE@am__append_1 = -std=gnu99 -O2 @IS_DEBUG_TRUE@@IS_GCC_TRUE@am__append_2 = -g -Wno-long-long -Wall -W \ @IS_DEBUG_TRUE@@IS_GCC_TRUE@ -Wformat=2 -Wmissing-prototypes \ @IS_DEBUG_TRUE@@IS_GCC_TRUE@ -Wstrict-prototypes \ @IS_DEBUG_TRUE@@IS_GCC_TRUE@ -Wmissing-declarations \ @IS_DEBUG_TRUE@@IS_GCC_TRUE@ -Wwrite-strings -Wshadow \ @IS_DEBUG_TRUE@@IS_GCC_TRUE@ -Wpointer-arith -Wsign-compare \ @IS_DEBUG_TRUE@@IS_GCC_TRUE@ -Wundef -Wbad-function-cast \ @IS_DEBUG_TRUE@@IS_GCC_TRUE@ -Winline -Wcast-align \ @IS_DEBUG_TRUE@@IS_GCC_TRUE@ -Wdeclaration-after-statement \ @IS_DEBUG_TRUE@@IS_GCC_TRUE@ -Wno-pointer-sign -Wno-attributes \ @IS_DEBUG_TRUE@@IS_GCC_TRUE@ -Wno-unused-result @IS_DEBUG_TRUE@@IS_GCC_TRUE@am__append_3 = -DDEBUG @IS_GCC_TRUE@am__append_4 = -iquote. # Set flags for Solaris. @IS_GCC_TRUE@@IS_SUNOS_TRUE@am__append_5 = -D_XPG6 @IS_GCC_FALSE@@IS_SUNOS_TRUE@am__append_6 = -D_XPG4_2 # Set flags for Sun CC. @IS_SUNCC_TRUE@am__append_7 = -erroff=E_EMPTY_DECLARATION # Set _LINUX_SOURCE_COMPAT for AIX for malloc(0). @IS_AIX_TRUE@am__append_8 = -D_LINUX_SOURCE_COMPAT=1 # Set flags for NetBSD. @IS_NETBSD_TRUE@am__append_9 = -D_OPENBSD_SOURCE # Add compat file for forkpty. @NEED_FORKPTY_TRUE@am__append_10 = compat/forkpty-@PLATFORM@.c # Add compat file for utf8proc. @HAVE_UTF8PROC_TRUE@am__append_11 = compat/utf8proc.c subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \ $(am__configure_deps) $(am__DIST_COMMON) am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ configure.lineno config.status.lineno mkinstalldirs = $(install_sh) -d CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(bindir)" PROGRAMS = $(bin_PROGRAMS) LIBOBJDIR = compat/ dist_tmux_OBJECTS = alerts.$(OBJEXT) arguments.$(OBJEXT) \ attributes.$(OBJEXT) cfg.$(OBJEXT) client.$(OBJEXT) \ cmd-attach-session.$(OBJEXT) cmd-bind-key.$(OBJEXT) \ cmd-break-pane.$(OBJEXT) cmd-capture-pane.$(OBJEXT) \ cmd-choose-tree.$(OBJEXT) cmd-command-prompt.$(OBJEXT) \ cmd-confirm-before.$(OBJEXT) cmd-copy-mode.$(OBJEXT) \ cmd-detach-client.$(OBJEXT) cmd-display-menu.$(OBJEXT) \ cmd-display-message.$(OBJEXT) cmd-display-panes.$(OBJEXT) \ cmd-find-window.$(OBJEXT) cmd-find.$(OBJEXT) \ cmd-if-shell.$(OBJEXT) cmd-join-pane.$(OBJEXT) \ cmd-kill-pane.$(OBJEXT) cmd-kill-server.$(OBJEXT) \ cmd-kill-session.$(OBJEXT) cmd-kill-window.$(OBJEXT) \ cmd-list-buffers.$(OBJEXT) cmd-list-clients.$(OBJEXT) \ cmd-list-keys.$(OBJEXT) cmd-list-panes.$(OBJEXT) \ cmd-list-sessions.$(OBJEXT) cmd-list-windows.$(OBJEXT) \ cmd-load-buffer.$(OBJEXT) cmd-lock-server.$(OBJEXT) \ cmd-move-window.$(OBJEXT) cmd-new-session.$(OBJEXT) \ cmd-new-window.$(OBJEXT) cmd-parse.$(OBJEXT) \ cmd-paste-buffer.$(OBJEXT) cmd-pipe-pane.$(OBJEXT) \ cmd-queue.$(OBJEXT) cmd-refresh-client.$(OBJEXT) \ cmd-rename-session.$(OBJEXT) cmd-rename-window.$(OBJEXT) \ cmd-resize-pane.$(OBJEXT) cmd-resize-window.$(OBJEXT) \ cmd-respawn-pane.$(OBJEXT) cmd-respawn-window.$(OBJEXT) \ cmd-rotate-window.$(OBJEXT) cmd-run-shell.$(OBJEXT) \ cmd-save-buffer.$(OBJEXT) cmd-select-layout.$(OBJEXT) \ cmd-select-pane.$(OBJEXT) cmd-select-window.$(OBJEXT) \ cmd-send-keys.$(OBJEXT) cmd-set-buffer.$(OBJEXT) \ cmd-set-environment.$(OBJEXT) cmd-set-option.$(OBJEXT) \ cmd-show-environment.$(OBJEXT) cmd-show-messages.$(OBJEXT) \ cmd-show-options.$(OBJEXT) cmd-source-file.$(OBJEXT) \ cmd-split-window.$(OBJEXT) cmd-swap-pane.$(OBJEXT) \ cmd-swap-window.$(OBJEXT) cmd-switch-client.$(OBJEXT) \ cmd-unbind-key.$(OBJEXT) cmd-wait-for.$(OBJEXT) cmd.$(OBJEXT) \ colour.$(OBJEXT) control-notify.$(OBJEXT) control.$(OBJEXT) \ environ.$(OBJEXT) format.$(OBJEXT) format-draw.$(OBJEXT) \ grid-view.$(OBJEXT) grid.$(OBJEXT) input-keys.$(OBJEXT) \ input.$(OBJEXT) job.$(OBJEXT) key-bindings.$(OBJEXT) \ key-string.$(OBJEXT) layout-custom.$(OBJEXT) \ layout-set.$(OBJEXT) layout.$(OBJEXT) log.$(OBJEXT) \ menu.$(OBJEXT) mode-tree.$(OBJEXT) names.$(OBJEXT) \ notify.$(OBJEXT) options-table.$(OBJEXT) options.$(OBJEXT) \ paste.$(OBJEXT) proc.$(OBJEXT) regsub.$(OBJEXT) \ resize.$(OBJEXT) screen-redraw.$(OBJEXT) \ screen-write.$(OBJEXT) screen.$(OBJEXT) \ server-client.$(OBJEXT) server-fn.$(OBJEXT) server.$(OBJEXT) \ session.$(OBJEXT) spawn.$(OBJEXT) status.$(OBJEXT) \ style.$(OBJEXT) tmux.$(OBJEXT) tty-acs.$(OBJEXT) \ tty-keys.$(OBJEXT) tty-term.$(OBJEXT) tty.$(OBJEXT) \ utf8.$(OBJEXT) window-buffer.$(OBJEXT) window-client.$(OBJEXT) \ window-clock.$(OBJEXT) window-copy.$(OBJEXT) \ window-tree.$(OBJEXT) window.$(OBJEXT) xmalloc.$(OBJEXT) \ xterm-keys.$(OBJEXT) am__dirstamp = $(am__leading_dot)dirstamp @NEED_FORKPTY_TRUE@am__objects_1 = \ @NEED_FORKPTY_TRUE@ compat/forkpty-@PLATFORM@.$(OBJEXT) @HAVE_UTF8PROC_TRUE@am__objects_2 = compat/utf8proc.$(OBJEXT) nodist_tmux_OBJECTS = osdep-@PLATFORM@.$(OBJEXT) $(am__objects_1) \ $(am__objects_2) tmux_OBJECTS = $(dist_tmux_OBJECTS) $(nodist_tmux_OBJECTS) tmux_LDADD = $(LDADD) tmux_DEPENDENCIES = $(LIBOBJS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ depcomp = $(SHELL) $(top_srcdir)/etc/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = am__yacc_c2h = sed -e s/cc$$/hh/ -e s/cpp$$/hpp/ -e s/cxx$$/hxx/ \ -e s/c++$$/h++/ -e s/c$$/h/ YACCCOMPILE = $(YACC) $(AM_YFLAGS) $(YFLAGS) AM_V_YACC = $(am__v_YACC_@AM_V@) am__v_YACC_ = $(am__v_YACC_@AM_DEFAULT_V@) am__v_YACC_0 = @echo " YACC " $@; am__v_YACC_1 = YLWRAP = $(top_srcdir)/etc/ylwrap SOURCES = $(dist_tmux_SOURCES) $(nodist_tmux_SOURCES) DIST_SOURCES = $(dist_tmux_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags CSCOPE = cscope AM_RECURSIVE_TARGETS = cscope am__DIST_COMMON = $(srcdir)/Makefile.in \ $(top_srcdir)/compat/asprintf.c $(top_srcdir)/compat/base64.c \ $(top_srcdir)/compat/cfmakeraw.c \ $(top_srcdir)/compat/closefrom.c \ $(top_srcdir)/compat/daemon-darwin.c \ $(top_srcdir)/compat/daemon.c $(top_srcdir)/compat/err.c \ $(top_srcdir)/compat/explicit_bzero.c \ $(top_srcdir)/compat/fdforkpty.c $(top_srcdir)/compat/fgetln.c \ $(top_srcdir)/compat/freezero.c \ $(top_srcdir)/compat/getdtablecount.c \ $(top_srcdir)/compat/getopt.c \ $(top_srcdir)/compat/getprogname.c \ $(top_srcdir)/compat/imsg-buffer.c $(top_srcdir)/compat/imsg.c \ $(top_srcdir)/compat/memmem.c \ $(top_srcdir)/compat/reallocarray.c \ $(top_srcdir)/compat/recallocarray.c \ $(top_srcdir)/compat/setenv.c \ $(top_srcdir)/compat/setproctitle.c \ $(top_srcdir)/compat/strcasestr.c \ $(top_srcdir)/compat/strlcat.c $(top_srcdir)/compat/strlcpy.c \ $(top_srcdir)/compat/strndup.c $(top_srcdir)/compat/strnlen.c \ $(top_srcdir)/compat/strsep.c $(top_srcdir)/compat/strtonum.c \ $(top_srcdir)/compat/unvis.c $(top_srcdir)/compat/vis.c \ $(top_srcdir)/etc/compile $(top_srcdir)/etc/config.guess \ $(top_srcdir)/etc/config.sub $(top_srcdir)/etc/depcomp \ $(top_srcdir)/etc/install-sh $(top_srcdir)/etc/missing \ $(top_srcdir)/etc/ylwrap COPYING README cmd-parse.c \ etc/compile etc/config.guess etc/config.sub etc/depcomp \ etc/install-sh etc/missing etc/ylwrap DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) am__remove_distdir = \ if test -d "$(distdir)"; then \ find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ && rm -rf "$(distdir)" \ || { sleep 5 && rm -rf "$(distdir)"; }; \ else :; fi am__post_remove_distdir = $(am__remove_distdir) DIST_ARCHIVES = $(distdir).tar.gz GZIP_ENV = --best DIST_TARGETS = dist-gzip distuninstallcheck_listfiles = find . -type f -print am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' distcleancheck_listfiles = find . -type f -print ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_CFLAGS = @AM_CFLAGS@ $(am__append_1) $(am__append_2) \ $(am__append_7) # Preprocessor flags. AM_CPPFLAGS = @AM_CPPFLAGS@ @XOPEN_DEFINES@ \ -DTMUX_CONF="\"$(sysconfdir)/tmux.conf\"" $(am__append_3) \ $(am__append_4) $(am__append_5) $(am__append_6) \ $(am__append_8) $(am__append_9) AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AM_LDFLAGS = @AM_LDFLAGS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LDFLAGS = @LDFLAGS@ LIBEVENT_CFLAGS = @LIBEVENT_CFLAGS@ LIBEVENT_LIBS = @LIBEVENT_LIBS@ LIBNCURSES_CFLAGS = @LIBNCURSES_CFLAGS@ LIBNCURSES_LIBS = @LIBNCURSES_LIBS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTINFO_CFLAGS = @LIBTINFO_CFLAGS@ LIBTINFO_LIBS = @LIBTINFO_LIBS@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MANFORMAT = @MANFORMAT@ MKDIR_P = @MKDIR_P@ OBJEXT = @OBJEXT@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PLATFORM = @PLATFORM@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ XOPEN_DEFINES = @XOPEN_DEFINES@ YACC = @YACC@ YFLAGS = @YFLAGS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_CC = @ac_ct_CC@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ CLEANFILES = tmux.1.mdoc tmux.1.man cmd-parse.c # Distribution tarball options. EXTRA_DIST = \ CHANGES README README.ja COPYING example_tmux.conf compat/*.[ch] \ osdep-*.c mdoc2man.awk tmux.1 # Additional object files. LDADD = $(LIBOBJS) # List of sources. dist_tmux_SOURCES = \ alerts.c \ arguments.c \ attributes.c \ cfg.c \ client.c \ cmd-attach-session.c \ cmd-bind-key.c \ cmd-break-pane.c \ cmd-capture-pane.c \ cmd-choose-tree.c \ cmd-command-prompt.c \ cmd-confirm-before.c \ cmd-copy-mode.c \ cmd-detach-client.c \ cmd-display-menu.c \ cmd-display-message.c \ cmd-display-panes.c \ cmd-find-window.c \ cmd-find.c \ cmd-if-shell.c \ cmd-join-pane.c \ cmd-kill-pane.c \ cmd-kill-server.c \ cmd-kill-session.c \ cmd-kill-window.c \ cmd-list-buffers.c \ cmd-list-clients.c \ cmd-list-keys.c \ cmd-list-panes.c \ cmd-list-sessions.c \ cmd-list-windows.c \ cmd-load-buffer.c \ cmd-lock-server.c \ cmd-move-window.c \ cmd-new-session.c \ cmd-new-window.c \ cmd-parse.y \ cmd-paste-buffer.c \ cmd-pipe-pane.c \ cmd-queue.c \ cmd-refresh-client.c \ cmd-rename-session.c \ cmd-rename-window.c \ cmd-resize-pane.c \ cmd-resize-window.c \ cmd-respawn-pane.c \ cmd-respawn-window.c \ cmd-rotate-window.c \ cmd-run-shell.c \ cmd-save-buffer.c \ cmd-select-layout.c \ cmd-select-pane.c \ cmd-select-window.c \ cmd-send-keys.c \ cmd-set-buffer.c \ cmd-set-environment.c \ cmd-set-option.c \ cmd-show-environment.c \ cmd-show-messages.c \ cmd-show-options.c \ cmd-source-file.c \ cmd-split-window.c \ cmd-swap-pane.c \ cmd-swap-window.c \ cmd-switch-client.c \ cmd-unbind-key.c \ cmd-wait-for.c \ cmd.c \ colour.c \ compat.h \ control-notify.c \ control.c \ environ.c \ format.c \ format-draw.c \ grid-view.c \ grid.c \ input-keys.c \ input.c \ job.c \ key-bindings.c \ key-string.c \ layout-custom.c \ layout-set.c \ layout.c \ log.c \ menu.c \ mode-tree.c \ names.c \ notify.c \ options-table.c \ options.c \ paste.c \ proc.c \ regsub.c \ resize.c \ screen-redraw.c \ screen-write.c \ screen.c \ server-client.c \ server-fn.c \ server.c \ session.c \ spawn.c \ status.c \ style.c \ tmux.c \ tmux.h \ tty-acs.c \ tty-keys.c \ tty-term.c \ tty.c \ utf8.c \ window-buffer.c \ window-client.c \ window-clock.c \ window-copy.c \ window-tree.c \ window.c \ xmalloc.c \ xmalloc.h \ xterm-keys.c nodist_tmux_SOURCES = osdep-@PLATFORM@.c $(am__append_10) \ $(am__append_11) all: all-am .SUFFIXES: .SUFFIXES: .c .o .obj .y am--refresh: Makefile @: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \ $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ echo ' $(SHELL) ./config.status'; \ $(SHELL) ./config.status;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) $(SHELL) ./config.status --recheck $(top_srcdir)/configure: $(am__configure_deps) $(am__cd) $(srcdir) && $(AUTOCONF) $(ACLOCAL_M4): $(am__aclocal_m4_deps) $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) $(am__aclocal_m4_deps): install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-binPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(bindir)" && rm -f $$files clean-binPROGRAMS: -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) compat/$(am__dirstamp): @$(MKDIR_P) compat @: > compat/$(am__dirstamp) compat/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) compat/$(DEPDIR) @: > compat/$(DEPDIR)/$(am__dirstamp) compat/forkpty-@PLATFORM@.$(OBJEXT): compat/$(am__dirstamp) \ compat/$(DEPDIR)/$(am__dirstamp) compat/utf8proc.$(OBJEXT): compat/$(am__dirstamp) \ compat/$(DEPDIR)/$(am__dirstamp) tmux$(EXEEXT): $(tmux_OBJECTS) $(tmux_DEPENDENCIES) $(EXTRA_tmux_DEPENDENCIES) @rm -f tmux$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tmux_OBJECTS) $(tmux_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) -rm -f compat/*.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alerts.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/arguments.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/attributes.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cfg.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-attach-session.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-bind-key.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-break-pane.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-capture-pane.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-choose-tree.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-command-prompt.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-confirm-before.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-copy-mode.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-detach-client.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-display-menu.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-display-message.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-display-panes.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-find-window.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-find.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-if-shell.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-join-pane.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-kill-pane.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-kill-server.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-kill-session.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-kill-window.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-list-buffers.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-list-clients.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-list-keys.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-list-panes.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-list-sessions.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-list-windows.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-load-buffer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-lock-server.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-move-window.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-new-session.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-new-window.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-parse.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-paste-buffer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-pipe-pane.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-queue.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-refresh-client.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-rename-session.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-rename-window.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-resize-pane.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-resize-window.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-respawn-pane.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-respawn-window.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-rotate-window.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-run-shell.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-save-buffer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-select-layout.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-select-pane.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-select-window.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-send-keys.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-set-buffer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-set-environment.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-set-option.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-show-environment.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-show-messages.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-show-options.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-source-file.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-split-window.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-swap-pane.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-swap-window.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-switch-client.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-unbind-key.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd-wait-for.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/colour.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/control-notify.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/control.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/environ.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/format-draw.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/format.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/grid-view.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/grid.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/input-keys.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/input.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/job.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/key-bindings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/key-string.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/layout-custom.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/layout-set.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/layout.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/menu.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mode-tree.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/names.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/notify.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/options-table.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/options.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/osdep-@PLATFORM@.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/paste.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regsub.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/resize.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/screen-redraw.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/screen-write.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/screen.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server-client.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server-fn.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/session.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/spawn.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/status.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/style.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tmux.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tty-acs.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tty-keys.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tty-term.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tty.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utf8.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/window-buffer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/window-client.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/window-clock.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/window-copy.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/window-tree.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/window.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xmalloc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xterm-keys.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/asprintf.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/base64.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/cfmakeraw.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/closefrom.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/daemon-darwin.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/daemon.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/err.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/explicit_bzero.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/fdforkpty.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/fgetln.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/forkpty-@PLATFORM@.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/freezero.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/getdtablecount.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/getopt.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/getprogname.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/imsg-buffer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/imsg.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/memmem.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/reallocarray.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/recallocarray.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/setenv.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/setproctitle.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/strcasestr.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/strlcat.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/strlcpy.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/strndup.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/strnlen.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/strsep.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/strtonum.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/unvis.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/utf8proc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/vis.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .y.c: $(AM_V_YACC)$(am__skipyacc) $(SHELL) $(YLWRAP) $< y.tab.c $@ y.tab.h `echo $@ | $(am__yacc_c2h)` y.output $*.output -- $(YACCCOMPILE) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscope: cscope.files test ! -s cscope.files \ || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) clean-cscope: -rm -f cscope.files cscope.files: clean-cscope cscopelist cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -rm -f cscope.out cscope.in.out cscope.po.out cscope.files distdir: $(DISTFILES) $(am__remove_distdir) test -d "$(distdir)" || mkdir "$(distdir)" @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done -test -n "$(am__skip_mode_fix)" \ || find "$(distdir)" -type d ! -perm -755 \ -exec chmod u+rwx,go+rx {} \; -o \ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ || chmod -R a+r "$(distdir)" dist-gzip: distdir tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz $(am__post_remove_distdir) dist-bzip2: distdir tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 $(am__post_remove_distdir) dist-lzip: distdir tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz $(am__post_remove_distdir) dist-xz: distdir tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz $(am__post_remove_distdir) dist-tarZ: distdir @echo WARNING: "Support for distribution archives compressed with" \ "legacy program 'compress' is deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z $(am__post_remove_distdir) dist-shar: distdir @echo WARNING: "Support for shar distribution archives is" \ "deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz $(am__post_remove_distdir) dist-zip: distdir -rm -f $(distdir).zip zip -rq $(distdir).zip $(distdir) $(am__post_remove_distdir) dist dist-all: $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' $(am__post_remove_distdir) # This target untars the dist file and tries a VPATH configuration. Then # it guarantees that the distribution is self-contained by making another # tarfile. distcheck: dist case '$(DIST_ARCHIVES)' in \ *.tar.gz*) \ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).tar.gz | $(am__untar) ;;\ *.tar.bz2*) \ bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ *.tar.lz*) \ lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ *.tar.xz*) \ xz -dc $(distdir).tar.xz | $(am__untar) ;;\ *.tar.Z*) \ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ *.shar.gz*) \ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\ *.zip*) \ unzip $(distdir).zip ;;\ esac chmod -R a-w $(distdir) chmod u+w $(distdir) mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst chmod a-w $(distdir) test -d $(distdir)/_build || exit 0; \ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ && am__cwd=`pwd` \ && $(am__cd) $(distdir)/_build/sub \ && ../../configure \ $(AM_DISTCHECK_CONFIGURE_FLAGS) \ $(DISTCHECK_CONFIGURE_FLAGS) \ --srcdir=../.. --prefix="$$dc_install_base" \ && $(MAKE) $(AM_MAKEFLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) dvi \ && $(MAKE) $(AM_MAKEFLAGS) check \ && $(MAKE) $(AM_MAKEFLAGS) install \ && $(MAKE) $(AM_MAKEFLAGS) installcheck \ && $(MAKE) $(AM_MAKEFLAGS) uninstall \ && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ distuninstallcheck \ && chmod -R a-w "$$dc_install_base" \ && ({ \ (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ } || { rm -rf "$$dc_destdir"; exit 1; }) \ && rm -rf "$$dc_destdir" \ && $(MAKE) $(AM_MAKEFLAGS) dist \ && rm -rf $(DIST_ARCHIVES) \ && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ && cd "$$am__cwd" \ || exit 1 $(am__post_remove_distdir) @(echo "$(distdir) archives ready for distribution: "; \ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' distuninstallcheck: @test -n '$(distuninstallcheck_dir)' || { \ echo 'ERROR: trying to run $@ with an empty' \ '$$(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ $(am__cd) '$(distuninstallcheck_dir)' || { \ echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left after uninstall:" ; \ if test -n "$(DESTDIR)"; then \ echo " (check DESTDIR support)"; \ fi ; \ $(distuninstallcheck_listfiles) ; \ exit 1; } >&2 distcleancheck: distclean @if test '$(srcdir)' = . ; then \ echo "ERROR: distcleancheck can only run from a VPATH build" ; \ exit 1 ; \ fi @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left in build directory after distclean:" ; \ $(distcleancheck_listfiles) ; \ exit 1; } >&2 check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) installdirs: for dir in "$(DESTDIR)$(bindir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: -test -z "$(LIBOBJS)" || rm -f $(LIBOBJS) clean-generic: -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -rm -f compat/$(DEPDIR)/$(am__dirstamp) -rm -f compat/$(am__dirstamp) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -rm -f cmd-parse.c clean: clean-am clean-am: clean-binPROGRAMS clean-generic mostlyclean-am distclean: distclean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf ./$(DEPDIR) compat/$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-binPROGRAMS @$(NORMAL_INSTALL) $(MAKE) $(AM_MAKEFLAGS) install-exec-hook install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf $(top_srcdir)/autom4te.cache -rm -rf ./$(DEPDIR) compat/$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-binPROGRAMS .MAKE: install-am install-exec-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am am--refresh check check-am clean \ clean-binPROGRAMS clean-cscope clean-generic cscope \ cscopelist-am ctags ctags-am dist dist-all dist-bzip2 \ dist-gzip dist-lzip dist-shar dist-tarZ dist-xz dist-zip \ distcheck distclean distclean-compile distclean-generic \ distclean-tags distcleancheck distdir distuninstallcheck dvi \ dvi-am html html-am info info-am install install-am \ install-binPROGRAMS install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-exec-hook \ install-html install-html-am install-info install-info-am \ install-man install-pdf install-pdf-am install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \ ps ps-am tags tags-am uninstall uninstall-am \ uninstall-binPROGRAMS .PRECIOUS: Makefile # Install tmux.1 in the right format. install-exec-hook: if test x@MANFORMAT@ = xmdoc; then \ sed -e "s|@SYSCONFDIR@|$(sysconfdir)|g" $(srcdir)/tmux.1 \ >$(srcdir)/tmux.1.mdoc; \ else \ sed -e "s|@SYSCONFDIR@|$(sysconfdir)|g" $(srcdir)/tmux.1| \ $(AWK) -f $(srcdir)/mdoc2man.awk >$(srcdir)/tmux.1.man; \ fi $(mkdir_p) $(DESTDIR)$(mandir)/man1 $(INSTALL_DATA) $(srcdir)/tmux.1.@MANFORMAT@ \ $(DESTDIR)$(mandir)/man1/tmux.1 # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: tmux-3.0a/COPYING100644 001750 001750 00000001701 13504653145 0007254THIS IS FOR INFORMATION ONLY, CODE IS UNDER THE LICENCE AT THE TOP OF ITS FILE. The README, CHANGES, FAQ and TODO files are licensed under the ISC license. All other files have a license and copyright notice at their start, typically: Copyright (c) Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. tmux-3.0a/README100644 001750 001750 00000003600 13517540563 0007104Welcome to tmux! tmux is a terminal multiplexer: it enables a number of terminals to be created, accessed, and controlled from a single screen. tmux may be detached from a screen and continue running in the background, then later reattached. This release runs on OpenBSD, FreeBSD, NetBSD, Linux, OS X and Solaris. * Dependencies tmux depends on libevent 2.x, available from: https://github.com/libevent/libevent/releases/latest It also depends on ncurses, available from: https://invisible-mirror.net/archives/ncurses/ * Installation To build and install tmux from a release tarball, use: $ ./configure && make $ sudo make install tmux can use the utempter library to update utmp(5), if it is installed - run configure with --enable-utempter to enable this. To get and build the latest from version control - note that this requires autoconf, automake and pkg-config: $ git clone https://github.com/tmux/tmux.git $ cd tmux $ sh autogen.sh $ ./configure && make * Contributing Bug reports, feature suggestions and especially code contributions are most welcome. Please send by email to: tmux-users@googlegroups.com Or open a GitHub issue or pull request. * Documentation For documentation on using tmux, see the tmux.1 manpage. View it from the source tree with: $ nroff -mdoc tmux.1|less A small example configuration is in example_tmux.conf. A bash(1) completion file is at: https://github.com/imomaliev/tmux-bash-completion For debugging, run tmux with -v and -vv to generate server and client log files in the current directory. * Support The tmux mailing list for general discussion and bug reports is: https://groups.google.com/forum/#!forum/tmux-users Subscribe by sending an email to: tmux-users+subscribe@googlegroups.com * License This file and the CHANGES files are licensed under the ISC license. All other files have a license and copyright notice at their start. tmux-3.0a/cmd-parse.c100644 001750 001750 00000136614 13570677750 0010267#include #include #define YYBYACC 1 #define YYMAJOR 1 #define YYMINOR 9 #define YYLEX yylex() #define YYEMPTY -1 #define yyclearin (yychar=(YYEMPTY)) #define yyerrok (yyerrflag=0) #define YYRECOVERING() (yyerrflag!=0) #define YYPREFIX "yy" #line 20 "cmd-parse.y" #include #include #include #include #include #include #include #include "tmux.h" static int yylex(void); static int yyparse(void); static int printflike(1,2) yyerror(const char *, ...); static char *yylex_token(int); static char *yylex_format(void); struct cmd_parse_scope { int flag; TAILQ_ENTRY (cmd_parse_scope) entry; }; struct cmd_parse_command { char *name; u_int line; int argc; char **argv; TAILQ_ENTRY(cmd_parse_command) entry; }; TAILQ_HEAD(cmd_parse_commands, cmd_parse_command); struct cmd_parse_state { FILE *f; const char *buf; size_t len; size_t off; int condition; int eol; int eof; struct cmd_parse_input *input; u_int escapes; char *error; struct cmd_parse_commands *commands; struct cmd_parse_scope *scope; TAILQ_HEAD(, cmd_parse_scope) stack; }; static struct cmd_parse_state parse_state; static char *cmd_parse_get_error(const char *, u_int, const char *); static void cmd_parse_free_command(struct cmd_parse_command *); static struct cmd_parse_commands *cmd_parse_new_commands(void); static void cmd_parse_free_commands(struct cmd_parse_commands *); static void cmd_parse_print_commands(struct cmd_parse_input *, u_int, struct cmd_list *); #line 85 "cmd-parse.y" #ifndef YYSTYPE_DEFINED #define YYSTYPE_DEFINED typedef union { char *token; struct { int argc; char **argv; } arguments; int flag; struct { int flag; struct cmd_parse_commands *commands; } elif; struct cmd_parse_commands *commands; struct cmd_parse_command *command; } YYSTYPE; #endif /* YYSTYPE_DEFINED */ #line 96 "cmd-parse.c" #define ERROR 257 #define IF 258 #define ELSE 259 #define ELIF 260 #define ENDIF 261 #define FORMAT 262 #define TOKEN 263 #define EQUALS 264 #define YYERRCODE 256 const short yylhs[] = { -1, 0, 0, 9, 9, 10, 10, 10, 3, 3, 2, 15, 15, 5, 16, 6, 17, 12, 12, 12, 12, 7, 7, 11, 11, 11, 11, 11, 14, 14, 13, 13, 13, 13, 8, 8, 4, 4, 1, 1, }; const short yylen[] = { 2, 0, 1, 2, 3, 1, 1, 1, 1, 1, 1, 0, 1, 2, 1, 2, 1, 4, 7, 5, 8, 3, 4, 1, 2, 3, 3, 1, 2, 3, 3, 5, 4, 6, 2, 3, 1, 2, 1, 1, }; const short yydefred[] = { 0, 0, 12, 0, 0, 0, 0, 0, 5, 27, 23, 0, 8, 9, 13, 10, 0, 0, 0, 0, 0, 3, 0, 0, 0, 14, 0, 16, 0, 0, 0, 30, 4, 25, 26, 38, 39, 0, 29, 0, 0, 0, 17, 15, 0, 0, 32, 0, 37, 0, 0, 19, 0, 35, 0, 31, 0, 0, 0, 33, 22, 0, 18, 20, }; const short yydgoto[] = { 3, 37, 14, 15, 38, 4, 28, 40, 29, 5, 6, 7, 8, 9, 10, 11, 30, 31, }; const short yysindex[] = { -214, -241, 0, 0, -10, -214, 3, -41, 0, 0, 0, -232, 0, 0, 0, 0, -214, -214, -56, -232, 25, 0, -214, -201, -234, 0, -241, 0, -214, -184, -214, 0, 0, 0, 0, 0, 0, -201, 0, 29, -184, 39, 0, 0, -52, -214, 0, -55, 0, -214, 49, 0, -214, 0, -55, 0, -206, -214, -185, 0, 0, -185, 0, 0,}; const short yyrindex[] = { 1, 0, 0, 0, -195, 2, 0, 64, 0, 0, 0, 70, 0, 0, 0, 0, -2, -195, 0, 0, 0, 0, -4, 7, -2, 0, 0, 0, -195, 0, -195, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, -178, -195, 0, 0, 0, -2, 0, 0, -2, 0, 0, 0, -1, -2, -2, 0, 0, -2, 0, 0,}; const short yygindex[] = { 0, 0, 58, 0, 50, 6, -9, 30, 44, -11, 9, 12, 0, 67, 68, 15, 32, 24, }; #define YYTABLESIZE 271 const short yytable[] = { 16, 1, 2, 22, 22, 24, 24, 22, 11, 11, 17, 11, 11, 21, 20, 39, 18, 28, 22, 19, 36, 12, 13, 17, 1, 25, 26, 27, 17, 18, 2, 23, 19, 20, 17, 32, 17, 19, 56, 49, 44, 58, 47, 19, 1, 19, 61, 39, 42, 52, 2, 17, 1, 46, 26, 24, 41, 54, 2, 57, 19, 45, 35, 36, 51, 20, 28, 20, 11, 36, 20, 55, 50, 1, 7, 25, 27, 27, 59, 2, 6, 34, 62, 34, 43, 63, 60, 48, 53, 33, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 26, 27, 27, 0, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 24, 24, 24, 21, 11, 21, 11, 11, 0, 11, 11, 28, 28, 28, 36, 36, 36, }; const short yycheck[] = { 10, 0, 0, 59, 59, 16, 10, 59, 10, 10, 4, 10, 10, 10, 5, 24, 4, 10, 59, 4, 10, 262, 263, 17, 258, 259, 260, 261, 22, 17, 264, 263, 17, 24, 28, 10, 30, 22, 49, 10, 28, 52, 30, 28, 258, 30, 57, 56, 24, 10, 264, 45, 258, 29, 260, 59, 24, 45, 264, 10, 45, 29, 263, 264, 40, 56, 59, 58, 263, 59, 61, 47, 40, 258, 10, 259, 261, 261, 54, 264, 10, 259, 58, 261, 26, 61, 56, 37, 44, 22, 22, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 259, 260, 261, 261, -1, 260, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 258, -1, -1, -1, -1, -1, 264, 259, 260, 261, 259, 263, 261, 263, 263, -1, 263, 263, 259, 260, 261, 259, 260, 261, }; #define YYFINAL 3 #ifndef YYDEBUG #define YYDEBUG 0 #endif #define YYMAXTOKEN 264 #if YYDEBUG const char * const yyname[] = { "end-of-file",0,0,0,0,0,0,0,0,0,"'\\n'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"';'",0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"ERROR","IF","ELSE", "ELIF","ENDIF","FORMAT","TOKEN","EQUALS", }; const char * const yyrule[] = {"$accept : lines", "lines :", "lines : statements", "statements : statement '\\n'", "statements : statements statement '\\n'", "statement : condition", "statement : assignment", "statement : commands", "format : FORMAT", "format : TOKEN", "expanded : format", "assignment :", "assignment : EQUALS", "if_open : IF expanded", "if_else : ELSE", "if_elif : ELIF expanded", "if_close : ENDIF", "condition : if_open '\\n' statements if_close", "condition : if_open '\\n' statements if_else '\\n' statements if_close", "condition : if_open '\\n' statements elif if_close", "condition : if_open '\\n' statements elif if_else '\\n' statements if_close", "elif : if_elif '\\n' statements", "elif : if_elif '\\n' statements elif", "commands : command", "commands : commands ';'", "commands : commands ';' condition1", "commands : commands ';' command", "commands : condition1", "command : assignment TOKEN", "command : assignment TOKEN arguments", "condition1 : if_open commands if_close", "condition1 : if_open commands if_else commands if_close", "condition1 : if_open commands elif1 if_close", "condition1 : if_open commands elif1 if_else commands if_close", "elif1 : if_elif commands", "elif1 : if_elif commands elif1", "arguments : argument", "arguments : argument arguments", "argument : TOKEN", "argument : EQUALS", }; #endif #ifdef YYSTACKSIZE #undef YYMAXDEPTH #define YYMAXDEPTH YYSTACKSIZE #else #ifdef YYMAXDEPTH #define YYSTACKSIZE YYMAXDEPTH #else #define YYSTACKSIZE 10000 #define YYMAXDEPTH 10000 #endif #endif #define YYINITSTACKSIZE 200 /* LINTUSED */ int yydebug; int yynerrs; int yyerrflag; int yychar; short *yyssp; YYSTYPE *yyvsp; YYSTYPE yyval; YYSTYPE yylval; short *yyss; short *yysslim; YYSTYPE *yyvs; unsigned int yystacksize; int yyparse(void); #line 499 "cmd-parse.y" static char * cmd_parse_get_error(const char *file, u_int line, const char *error) { char *s; if (file == NULL) s = xstrdup(error); else xasprintf (&s, "%s:%u: %s", file, line, error); return (s); } static void cmd_parse_print_commands(struct cmd_parse_input *pi, u_int line, struct cmd_list *cmdlist) { char *s; if (pi->item != NULL && (pi->flags & CMD_PARSE_VERBOSE)) { s = cmd_list_print(cmdlist, 0); if (pi->file != NULL) cmdq_print(pi->item, "%s:%u: %s", pi->file, line, s); else cmdq_print(pi->item, "%u: %s", line, s); free(s); } } static void cmd_parse_free_command(struct cmd_parse_command *cmd) { free(cmd->name); cmd_free_argv(cmd->argc, cmd->argv); free(cmd); } static struct cmd_parse_commands * cmd_parse_new_commands(void) { struct cmd_parse_commands *cmds; cmds = xmalloc(sizeof *cmds); TAILQ_INIT (cmds); return (cmds); } static void cmd_parse_free_commands(struct cmd_parse_commands *cmds) { struct cmd_parse_command *cmd, *cmd1; TAILQ_FOREACH_SAFE(cmd, cmds, entry, cmd1) { TAILQ_REMOVE(cmds, cmd, entry); cmd_parse_free_command(cmd); } free(cmds); } static struct cmd_parse_commands * cmd_parse_run_parser(char **cause) { struct cmd_parse_state *ps = &parse_state; struct cmd_parse_scope *scope, *scope1; int retval; ps->commands = NULL; TAILQ_INIT(&ps->stack); retval = yyparse(); TAILQ_FOREACH_SAFE(scope, &ps->stack, entry, scope1) { TAILQ_REMOVE(&ps->stack, scope, entry); free(scope); } if (retval != 0) { *cause = ps->error; return (NULL); } if (ps->commands == NULL) return (cmd_parse_new_commands()); return (ps->commands); } static struct cmd_parse_commands * cmd_parse_do_file(FILE *f, struct cmd_parse_input *pi, char **cause) { struct cmd_parse_state *ps = &parse_state; memset(ps, 0, sizeof *ps); ps->input = pi; ps->f = f; return (cmd_parse_run_parser(cause)); } static struct cmd_parse_commands * cmd_parse_do_buffer(const char *buf, size_t len, struct cmd_parse_input *pi, char **cause) { struct cmd_parse_state *ps = &parse_state; memset(ps, 0, sizeof *ps); ps->input = pi; ps->buf = buf; ps->len = len; return (cmd_parse_run_parser(cause)); } static struct cmd_parse_result * cmd_parse_build_commands(struct cmd_parse_commands *cmds, struct cmd_parse_input *pi) { static struct cmd_parse_result pr; struct cmd_parse_commands *cmds2; struct cmd_parse_command *cmd, *cmd2, *next, *next2, *after; u_int line = UINT_MAX; int i; struct cmd_list *cmdlist = NULL, *result; struct cmd *add; char *alias, *cause, *s; /* Check for an empty list. */ if (TAILQ_EMPTY(cmds)) { cmd_parse_free_commands(cmds); pr.status = CMD_PARSE_EMPTY; return (&pr); } /* * Walk the commands and expand any aliases. Each alias is parsed * individually to a new command list, any trailing arguments appended * to the last command, and all commands inserted into the original * command list. */ TAILQ_FOREACH_SAFE(cmd, cmds, entry, next) { alias = cmd_get_alias(cmd->name); if (alias == NULL) continue; line = cmd->line; log_debug("%s: %u %s = %s", __func__, line, cmd->name, alias); pi->line = line; cmds2 = cmd_parse_do_buffer(alias, strlen(alias), pi, &cause); free(alias); if (cmds2 == NULL) { pr.status = CMD_PARSE_ERROR; pr.error = cause; goto out; } cmd2 = TAILQ_LAST(cmds2, cmd_parse_commands); if (cmd2 == NULL) { TAILQ_REMOVE(cmds, cmd, entry); cmd_parse_free_command(cmd); continue; } for (i = 0; i < cmd->argc; i++) cmd_append_argv(&cmd2->argc, &cmd2->argv, cmd->argv[i]); after = cmd; TAILQ_FOREACH_SAFE(cmd2, cmds2, entry, next2) { cmd2->line = line; TAILQ_REMOVE(cmds2, cmd2, entry); TAILQ_INSERT_AFTER(cmds, after, cmd2, entry); after = cmd2; } cmd_parse_free_commands(cmds2); TAILQ_REMOVE(cmds, cmd, entry); cmd_parse_free_command(cmd); } /* * Parse each command into a command list. Create a new command list * for each line so they get a new group (so the queue knows which ones * to remove if a command fails when executed). */ result = cmd_list_new(); TAILQ_FOREACH(cmd, cmds, entry) { log_debug("%s: %u %s", __func__, cmd->line, cmd->name); cmd_log_argv(cmd->argc, cmd->argv, __func__); if (cmdlist == NULL || cmd->line != line) { if (cmdlist != NULL) { cmd_parse_print_commands(pi, line, cmdlist); cmd_list_move(result, cmdlist); cmd_list_free(cmdlist); } cmdlist = cmd_list_new(); } line = cmd->line; cmd_prepend_argv(&cmd->argc, &cmd->argv, cmd->name); add = cmd_parse(cmd->argc, cmd->argv, pi->file, line, &cause); if (add == NULL) { cmd_list_free(result); pr.status = CMD_PARSE_ERROR; pr.error = cmd_parse_get_error(pi->file, line, cause); free(cause); goto out; } cmd_list_append(cmdlist, add); } if (cmdlist != NULL) { cmd_parse_print_commands(pi, line, cmdlist); cmd_list_move(result, cmdlist); cmd_list_free(cmdlist); } s = cmd_list_print(result, 0); log_debug("%s: %s", __func__, s); free(s); pr.status = CMD_PARSE_SUCCESS; pr.cmdlist = result; out: cmd_parse_free_commands(cmds); return (&pr); } struct cmd_parse_result * cmd_parse_from_file(FILE *f, struct cmd_parse_input *pi) { static struct cmd_parse_result pr; struct cmd_parse_input input; struct cmd_parse_commands *cmds; char *cause; if (pi == NULL) { memset(&input, 0, sizeof input); pi = &input; } memset(&pr, 0, sizeof pr); cmds = cmd_parse_do_file(f, pi, &cause); if (cmds == NULL) { pr.status = CMD_PARSE_ERROR; pr.error = cause; return (&pr); } return (cmd_parse_build_commands(cmds, pi)); } struct cmd_parse_result * cmd_parse_from_string(const char *s, struct cmd_parse_input *pi) { static struct cmd_parse_result pr; struct cmd_parse_input input; struct cmd_parse_commands *cmds; char *cause; if (pi == NULL) { memset(&input, 0, sizeof input); pi = &input; } memset(&pr, 0, sizeof pr); if (*s == '\0') { pr.status = CMD_PARSE_EMPTY; pr.cmdlist = NULL; pr.error = NULL; return (&pr); } cmds = cmd_parse_do_buffer(s, strlen(s), pi, &cause); if (cmds == NULL) { pr.status = CMD_PARSE_ERROR; pr.error = cause; return (&pr); } return (cmd_parse_build_commands(cmds, pi)); } struct cmd_parse_result * cmd_parse_from_arguments(int argc, char **argv, struct cmd_parse_input *pi) { struct cmd_parse_input input; struct cmd_parse_commands *cmds; struct cmd_parse_command *cmd; char **copy, **new_argv; size_t size; int i, last, new_argc; /* * The commands are already split up into arguments, so just separate * into a set of commands by ';'. */ if (pi == NULL) { memset(&input, 0, sizeof input); pi = &input; } cmd_log_argv(argc, argv, "%s", __func__); cmds = cmd_parse_new_commands(); copy = cmd_copy_argv(argc, argv); last = 0; for (i = 0; i < argc; i++) { size = strlen(copy[i]); if (size == 0 || copy[i][size - 1] != ';') continue; copy[i][--size] = '\0'; if (size > 0 && copy[i][size - 1] == '\\') { copy[i][size - 1] = ';'; continue; } new_argc = i - last; new_argv = copy + last; if (size != 0) new_argc++; if (new_argc != 0) { cmd_log_argv(new_argc, new_argv, "%s: at %u", __func__, i); cmd = xcalloc(1, sizeof *cmd); cmd->name = xstrdup(new_argv[0]); cmd->line = pi->line; cmd->argc = new_argc - 1; cmd->argv = cmd_copy_argv(new_argc - 1, new_argv + 1); TAILQ_INSERT_TAIL(cmds, cmd, entry); } last = i + 1; } if (last != argc) { new_argv = copy + last; new_argc = argc - last; if (new_argc != 0) { cmd_log_argv(new_argc, new_argv, "%s: at %u", __func__, last); cmd = xcalloc(1, sizeof *cmd); cmd->name = xstrdup(new_argv[0]); cmd->line = pi->line; cmd->argc = new_argc - 1; cmd->argv = cmd_copy_argv(new_argc - 1, new_argv + 1); TAILQ_INSERT_TAIL(cmds, cmd, entry); } } cmd_free_argv(argc, copy); return (cmd_parse_build_commands(cmds, pi)); } static int printflike(1, 2) yyerror(const char *fmt, ...) { struct cmd_parse_state *ps = &parse_state; struct cmd_parse_input *pi = ps->input; va_list ap; char *error; if (ps->error != NULL) return (0); va_start(ap, fmt); xvasprintf(&error, fmt, ap); va_end(ap); ps->error = cmd_parse_get_error(pi->file, pi->line, error); free(error); return (0); } static int yylex_is_var(char ch, int first) { if (ch == '=') return (0); if (first && isdigit((u_char)ch)) return (0); return (isalnum((u_char)ch) || ch == '_'); } static void yylex_append(char **buf, size_t *len, const char *add, size_t addlen) { if (addlen > SIZE_MAX - 1 || *len > SIZE_MAX - 1 - addlen) fatalx("buffer is too big"); *buf = xrealloc(*buf, (*len) + 1 + addlen); memcpy((*buf) + *len, add, addlen); (*len) += addlen; } static void yylex_append1(char **buf, size_t *len, char add) { yylex_append(buf, len, &add, 1); } static int yylex_getc1(void) { struct cmd_parse_state *ps = &parse_state; int ch; if (ps->f != NULL) ch = getc(ps->f); else { if (ps->off == ps->len) ch = EOF; else ch = ps->buf[ps->off++]; } return (ch); } static void yylex_ungetc(int ch) { struct cmd_parse_state *ps = &parse_state; if (ps->f != NULL) ungetc(ch, ps->f); else if (ps->off > 0 && ch != EOF) ps->off--; } static int yylex_getc(void) { struct cmd_parse_state *ps = &parse_state; int ch; if (ps->escapes != 0) { ps->escapes--; return ('\\'); } for (;;) { ch = yylex_getc1(); if (ch == '\\') { ps->escapes++; continue; } if (ch == '\n' && (ps->escapes % 2) == 1) { ps->input->line++; ps->escapes--; continue; } if (ps->escapes != 0) { yylex_ungetc(ch); ps->escapes--; return ('\\'); } return (ch); } } static char * yylex_get_word(int ch) { char *buf; size_t len; len = 0; buf = xmalloc(1); do yylex_append1(&buf, &len, ch); while ((ch = yylex_getc()) != EOF && strchr(" \t\n", ch) == NULL); yylex_ungetc(ch); buf[len] = '\0'; log_debug("%s: %s", __func__, buf); return (buf); } static int yylex(void) { struct cmd_parse_state *ps = &parse_state; char *token, *cp; int ch, next, condition; if (ps->eol) ps->input->line++; ps->eol = 0; condition = ps->condition; ps->condition = 0; for (;;) { ch = yylex_getc(); if (ch == EOF) { /* * Ensure every file or string is terminated by a * newline. This keeps the parser simpler and avoids * having to add a newline to each string. */ if (ps->eof) break; ps->eof = 1; return ('\n'); } if (ch == ' ' || ch == '\t') { /* * Ignore whitespace. */ continue; } if (ch == '\n') { /* * End of line. Update the line number. */ ps->eol = 1; return ('\n'); } if (ch == ';') { /* * A semicolon is itself. */ return (';'); } if (ch == '#') { /* * #{ after a condition opens a format; anything else * is a comment, ignore up to the end of the line. */ next = yylex_getc(); if (condition && next == '{') { yylval.token = yylex_format(); if (yylval.token == NULL) return (ERROR); return (FORMAT); } while (next != '\n' && next != EOF) next = yylex_getc(); if (next == '\n') { ps->input->line++; return ('\n'); } continue; } if (ch == '%') { /* * % is a condition unless it is all % or all numbers, * then it is a token. */ yylval.token = yylex_get_word('%'); for (cp = yylval.token; *cp != '\0'; cp++) { if (*cp != '%' && !isdigit((u_char)*cp)) break; } if (*cp == '\0') return (TOKEN); ps->condition = 1; if (strcmp(yylval.token, "%if") == 0) { free(yylval.token); return (IF); } if (strcmp(yylval.token, "%else") == 0) { free(yylval.token); return (ELSE); } if (strcmp(yylval.token, "%elif") == 0) { free(yylval.token); return (ELIF); } if (strcmp(yylval.token, "%endif") == 0) { free(yylval.token); return (ENDIF); } free(yylval.token); return (ERROR); } /* * Otherwise this is a token. */ token = yylex_token(ch); if (token == NULL) return (ERROR); yylval.token = token; if (strchr(token, '=') != NULL && yylex_is_var(*token, 1)) { for (cp = token + 1; *cp != '='; cp++) { if (!yylex_is_var(*cp, 0)) break; } if (*cp == '=') return (EQUALS); } return (TOKEN); } return (0); } static char * yylex_format(void) { char *buf; size_t len; int ch, brackets = 1; len = 0; buf = xmalloc(1); yylex_append(&buf, &len, "#{", 2); for (;;) { if ((ch = yylex_getc()) == EOF || ch == '\n') goto error; if (ch == '#') { if ((ch = yylex_getc()) == EOF || ch == '\n') goto error; if (ch == '{') brackets++; yylex_append1(&buf, &len, '#'); } else if (ch == '}') { if (brackets != 0 && --brackets == 0) { yylex_append1(&buf, &len, ch); break; } } yylex_append1(&buf, &len, ch); } if (brackets != 0) goto error; buf[len] = '\0'; log_debug("%s: %s", __func__, buf); return (buf); error: free(buf); return (NULL); } static int yylex_token_escape(char **buf, size_t *len) { int ch, type, o2, o3; u_int size, i, tmp; char s[9]; struct utf8_data ud; ch = yylex_getc(); if (ch >= '4' && ch <= '7') { yyerror("invalid octal escape"); return (0); } if (ch >= '0' && ch <= '3') { o2 = yylex_getc(); if (o2 >= '0' && o2 <= '7') { o3 = yylex_getc(); if (o3 >= '0' && o3 <= '7') { ch = 64 * (ch - '0') + 8 * (o2 - '0') + (o3 - '0'); yylex_append1(buf, len, ch); return (1); } } yyerror("invalid octal escape"); return (0); } switch (ch) { case EOF: return (0); case 'a': ch = '\a'; break; case 'b': ch = '\b'; break; case 'e': ch = '\033'; break; case 'f': ch = '\f'; break; case 's': ch = ' '; break; case 'v': ch = '\v'; break; case 'r': ch = '\r'; break; case 'n': ch = '\n'; break; case 't': ch = '\t'; break; case 'u': type = 'u'; size = 4; goto unicode; case 'U': type = 'U'; size = 8; goto unicode; } yylex_append1(buf, len, ch); return (1); unicode: for (i = 0; i < size; i++) { ch = yylex_getc(); if (ch == EOF || ch == '\n') return (0); if (!isxdigit((u_char)ch)) { yyerror("invalid \\%c argument", type); return (0); } s[i] = ch; } s[i] = '\0'; if ((size == 4 && sscanf(s, "%4x", &tmp) != 1) || (size == 8 && sscanf(s, "%8x", &tmp) != 1)) { yyerror("invalid \\%c argument", type); return (0); } if (utf8_split(tmp, &ud) != UTF8_DONE) { yyerror("invalid \\%c argument", type); return (0); } yylex_append(buf, len, ud.data, ud.size); return (1); } static int yylex_token_variable(char **buf, size_t *len) { struct environ_entry *envent; int ch, brackets = 0; char name[1024]; size_t namelen = 0; const char *value; ch = yylex_getc(); if (ch == EOF) return (0); if (ch == '{') brackets = 1; else { if (!yylex_is_var(ch, 1)) { yylex_append1(buf, len, '$'); yylex_ungetc(ch); return (1); } name[namelen++] = ch; } for (;;) { ch = yylex_getc(); if (brackets && ch == '}') break; if (ch == EOF || !yylex_is_var(ch, 0)) { if (!brackets) { yylex_ungetc(ch); break; } yyerror("invalid environment variable"); return (0); } if (namelen == (sizeof name) - 2) { yyerror("environment variable is too long"); return (0); } name[namelen++] = ch; } name[namelen] = '\0'; envent = environ_find(global_environ, name); if (envent != NULL) { value = envent->value; log_debug("%s: %s -> %s", __func__, name, value); yylex_append(buf, len, value, strlen(value)); } return (1); } static int yylex_token_tilde(char **buf, size_t *len) { struct environ_entry *envent; int ch; char name[1024]; size_t namelen = 0; struct passwd *pw; const char *home = NULL; for (;;) { ch = yylex_getc(); if (ch == EOF || strchr("/ \t\n\"'", ch) != NULL) { yylex_ungetc(ch); break; } if (namelen == (sizeof name) - 2) { yyerror("user name is too long"); return (0); } name[namelen++] = ch; } name[namelen] = '\0'; if (*name == '\0') { 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 { if ((pw = getpwnam(name)) != NULL) home = pw->pw_dir; } if (home == NULL) return (0); log_debug("%s: ~%s -> %s", __func__, name, home); yylex_append(buf, len, home, strlen(home)); return (1); } static int yylex_token_brace(char **buf, size_t *len) { struct cmd_parse_state *ps = &parse_state; int ch, lines = 0, nesting = 1, escape = 0; int quote = '\0', token = 0; /* * Extract a string up to the matching unquoted '}', including newlines * and handling nested braces. * * To detect the final and intermediate braces which affect the nesting * depth, we scan the input as if it was a tmux config file, and ignore * braces which would be considered quoted, escaped, or in a comment. * * We update the token state after every character because '#' begins a * comment only when it begins a token. For simplicity, we treat an * unquoted directive format as comment. * * The result is verbatim copy of the input excluding the final brace. */ for (ch = yylex_getc1(); ch != EOF; ch = yylex_getc1()) { yylex_append1(buf, len, ch); if (ch == '\n') lines++; /* * If the previous character was a backslash (escape is set), * escape anything if unquoted or in double quotes, otherwise * escape only '\n' and '\\'. */ if (escape && (quote == '\0' || quote == '"' || ch == '\n' || ch == '\\')) { escape = 0; if (ch != '\n') token = 1; continue; } /* * The character is not escaped. If it is a backslash, set the * escape flag. */ if (ch == '\\') { escape = 1; continue; } escape = 0; /* A newline always resets to unquoted. */ if (ch == '\n') { quote = token = 0; continue; } if (quote) { /* * Inside quotes or comment. Check if this is the * closing quote. */ if (ch == quote && quote != '#') quote = 0; token = 1; /* token continues regardless */ } else { /* Not inside quotes or comment. */ switch (ch) { case '"': case '\'': case '#': /* Beginning of quote or maybe comment. */ if (ch != '#' || !token) quote = ch; token = 1; break; case ' ': case '\t': case ';': /* Delimiter - token resets. */ token = 0; break; case '{': nesting++; token = 0; /* new commands set - token resets */ break; case '}': nesting--; token = 1; /* same as after quotes */ if (nesting == 0) { (*len)--; /* remove closing } */ ps->input->line += lines; return (1); } break; default: token = 1; break; } } } /* * Update line count after error as reporting the opening line is more * useful than EOF. */ yyerror("unterminated brace string"); ps->input->line += lines; return (0); } static char * yylex_token(int ch) { char *buf; size_t len; enum { START, NONE, DOUBLE_QUOTES, SINGLE_QUOTES } state = NONE, last = START; len = 0; buf = xmalloc(1); for (;;) { /* * EOF or \n are always the end of the token. If inside quotes * they are an error. */ if (ch == EOF || ch == '\n') { if (state != NONE) goto error; break; } /* Whitespace or ; ends a token unless inside quotes. */ if ((ch == ' ' || ch == '\t' || ch == ';') && state == NONE) break; /* * \ ~ and $ are expanded except in single quotes. */ if (ch == '\\' && state != SINGLE_QUOTES) { if (!yylex_token_escape(&buf, &len)) goto error; goto skip; } if (ch == '~' && last != state && state != SINGLE_QUOTES) { if (!yylex_token_tilde(&buf, &len)) goto error; goto skip; } if (ch == '$' && state != SINGLE_QUOTES) { if (!yylex_token_variable(&buf, &len)) goto error; goto skip; } if (ch == '{' && state == NONE) { if (!yylex_token_brace(&buf, &len)) goto error; goto skip; } if (ch == '}' && state == NONE) goto error; /* unmatched (matched ones were handled) */ /* * ' and " starts or end quotes (and is consumed). */ if (ch == '\'') { if (state == NONE) { state = SINGLE_QUOTES; goto next; } if (state == SINGLE_QUOTES) { state = NONE; goto next; } } if (ch == '"') { if (state == NONE) { state = DOUBLE_QUOTES; goto next; } if (state == DOUBLE_QUOTES) { state = NONE; goto next; } } /* * Otherwise add the character to the buffer. */ yylex_append1(&buf, &len, ch); skip: last = state; next: ch = yylex_getc(); } yylex_ungetc(ch); buf[len] = '\0'; log_debug("%s: %s", __func__, buf); return (buf); error: free(buf); return (NULL); } #line 1350 "cmd-parse.c" /* allocate initial stack or double stack size, up to YYMAXDEPTH */ static int yygrowstack(void) { unsigned int newsize; long sslen; short *newss; YYSTYPE *newvs; if ((newsize = yystacksize) == 0) newsize = YYINITSTACKSIZE; else if (newsize >= YYMAXDEPTH) return -1; else if ((newsize *= 2) > YYMAXDEPTH) newsize = YYMAXDEPTH; sslen = yyssp - yyss; #ifdef SIZE_MAX #define YY_SIZE_MAX SIZE_MAX #else #define YY_SIZE_MAX 0xffffffffU #endif if (newsize && YY_SIZE_MAX / newsize < sizeof *newss) goto bail; newss = yyss ? (short *)realloc(yyss, newsize * sizeof *newss) : (short *)malloc(newsize * sizeof *newss); /* overflow check above */ if (newss == NULL) goto bail; yyss = newss; yyssp = newss + sslen; if (newsize && YY_SIZE_MAX / newsize < sizeof *newvs) goto bail; newvs = yyvs ? (YYSTYPE *)realloc(yyvs, newsize * sizeof *newvs) : (YYSTYPE *)malloc(newsize * sizeof *newvs); /* overflow check above */ if (newvs == NULL) goto bail; yyvs = newvs; yyvsp = newvs + sslen; yystacksize = newsize; yysslim = yyss + newsize - 1; return 0; bail: if (yyss) free(yyss); if (yyvs) free(yyvs); yyss = yyssp = NULL; yyvs = yyvsp = NULL; yystacksize = 0; return -1; } #define YYABORT goto yyabort #define YYREJECT goto yyabort #define YYACCEPT goto yyaccept #define YYERROR goto yyerrlab int yyparse(void) { int yym, yyn, yystate; #if YYDEBUG const char *yys; if ((yys = getenv("YYDEBUG"))) { yyn = *yys; if (yyn >= '0' && yyn <= '9') yydebug = yyn - '0'; } #endif /* YYDEBUG */ yynerrs = 0; yyerrflag = 0; yychar = (-1); if (yyss == NULL && yygrowstack()) goto yyoverflow; yyssp = yyss; yyvsp = yyvs; *yyssp = yystate = 0; yyloop: if ((yyn = yydefred[yystate]) != 0) goto yyreduce; if (yychar < 0) { if ((yychar = yylex()) < 0) yychar = 0; #if YYDEBUG if (yydebug) { yys = 0; if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; if (!yys) yys = "illegal-symbol"; printf("%sdebug: state %d, reading %d (%s)\n", YYPREFIX, yystate, yychar, yys); } #endif } if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 && yyn <= YYTABLESIZE && yycheck[yyn] == yychar) { #if YYDEBUG if (yydebug) printf("%sdebug: state %d, shifting to state %d\n", YYPREFIX, yystate, yytable[yyn]); #endif if (yyssp >= yysslim && yygrowstack()) { goto yyoverflow; } *++yyssp = yystate = yytable[yyn]; *++yyvsp = yylval; yychar = (-1); if (yyerrflag > 0) --yyerrflag; goto yyloop; } if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 && yyn <= YYTABLESIZE && yycheck[yyn] == yychar) { yyn = yytable[yyn]; goto yyreduce; } if (yyerrflag) goto yyinrecovery; #if defined(__GNUC__) goto yynewerror; #endif yynewerror: yyerror("syntax error"); #if defined(__GNUC__) goto yyerrlab; #endif yyerrlab: ++yynerrs; yyinrecovery: if (yyerrflag < 3) { yyerrflag = 3; for (;;) { if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 && yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE) { #if YYDEBUG if (yydebug) printf("%sdebug: state %d, error recovery shifting\ to state %d\n", YYPREFIX, *yyssp, yytable[yyn]); #endif if (yyssp >= yysslim && yygrowstack()) { goto yyoverflow; } *++yyssp = yystate = yytable[yyn]; *++yyvsp = yylval; goto yyloop; } else { #if YYDEBUG if (yydebug) printf("%sdebug: error recovery discarding state %d\n", YYPREFIX, *yyssp); #endif if (yyssp <= yyss) goto yyabort; --yyssp; --yyvsp; } } } else { if (yychar == 0) goto yyabort; #if YYDEBUG if (yydebug) { yys = 0; if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; if (!yys) yys = "illegal-symbol"; printf("%sdebug: state %d, error recovery discards token %d (%s)\n", YYPREFIX, yystate, yychar, yys); } #endif yychar = (-1); goto yyloop; } yyreduce: #if YYDEBUG if (yydebug) printf("%sdebug: state %d, reducing by rule %d (%s)\n", YYPREFIX, yystate, yyn, yyrule[yyn]); #endif yym = yylen[yyn]; if (yym) yyval = yyvsp[1-yym]; else memset(&yyval, 0, sizeof yyval); switch (yyn) { case 2: #line 119 "cmd-parse.y" { struct cmd_parse_state *ps = &parse_state; ps->commands = yyvsp[0].commands; } break; case 3: #line 126 "cmd-parse.y" { yyval.commands = yyvsp[-1].commands; } break; case 4: #line 130 "cmd-parse.y" { yyval.commands = yyvsp[-2].commands; TAILQ_CONCAT(yyval.commands, yyvsp[-1].commands, entry); free(yyvsp[-1].commands); } break; case 5: #line 137 "cmd-parse.y" { struct cmd_parse_state *ps = &parse_state; if (ps->scope == NULL || ps->scope->flag) yyval.commands = yyvsp[0].commands; else { yyval.commands = cmd_parse_new_commands(); cmd_parse_free_commands(yyvsp[0].commands); } } break; case 6: #line 148 "cmd-parse.y" { yyval.commands = xmalloc (sizeof *yyval.commands); TAILQ_INIT(yyval.commands); } break; case 7: #line 153 "cmd-parse.y" { struct cmd_parse_state *ps = &parse_state; if (ps->scope == NULL || ps->scope->flag) yyval.commands = yyvsp[0].commands; else { yyval.commands = cmd_parse_new_commands(); cmd_parse_free_commands(yyvsp[0].commands); } } break; case 8: #line 165 "cmd-parse.y" { yyval.token = yyvsp[0].token; } break; case 9: #line 169 "cmd-parse.y" { yyval.token = yyvsp[0].token; } break; case 10: #line 174 "cmd-parse.y" { struct cmd_parse_state *ps = &parse_state; struct cmd_parse_input *pi = ps->input; struct format_tree *ft; struct client *c = pi->c; struct cmd_find_state *fs; int flags = FORMAT_NOJOBS; if (cmd_find_valid_state(&pi->fs)) fs = &pi->fs; else fs = NULL; ft = format_create(NULL, pi->item, FORMAT_NONE, flags); if (fs != NULL) format_defaults(ft, c, fs->s, fs->wl, fs->wp); else format_defaults(ft, c, NULL, NULL, NULL); yyval.token = format_expand(ft, yyvsp[0].token); format_free(ft); free(yyvsp[0].token); } break; case 12: #line 199 "cmd-parse.y" { struct cmd_parse_state *ps = &parse_state; int flags = ps->input->flags; if ((~flags & CMD_PARSE_PARSEONLY) && (ps->scope == NULL || ps->scope->flag)) environ_put(global_environ, yyvsp[0].token); free(yyvsp[0].token); } break; case 13: #line 210 "cmd-parse.y" { struct cmd_parse_state *ps = &parse_state; struct cmd_parse_scope *scope; scope = xmalloc(sizeof *scope); yyval.flag = scope->flag = format_true(yyvsp[0].token); free(yyvsp[0].token); if (ps->scope != NULL) TAILQ_INSERT_HEAD(&ps->stack, ps->scope, entry); ps->scope = scope; } break; case 14: #line 224 "cmd-parse.y" { struct cmd_parse_state *ps = &parse_state; struct cmd_parse_scope *scope; scope = xmalloc(sizeof *scope); scope->flag = !ps->scope->flag; free(ps->scope); ps->scope = scope; } break; case 15: #line 236 "cmd-parse.y" { struct cmd_parse_state *ps = &parse_state; struct cmd_parse_scope *scope; scope = xmalloc(sizeof *scope); yyval.flag = scope->flag = format_true(yyvsp[0].token); free(yyvsp[0].token); free(ps->scope); ps->scope = scope; } break; case 16: #line 249 "cmd-parse.y" { struct cmd_parse_state *ps = &parse_state; free(ps->scope); ps->scope = TAILQ_FIRST(&ps->stack); if (ps->scope != NULL) TAILQ_REMOVE(&ps->stack, ps->scope, entry); } break; case 17: #line 259 "cmd-parse.y" { if (yyvsp[-3].flag) yyval.commands = yyvsp[-1].commands; else { yyval.commands = cmd_parse_new_commands(); cmd_parse_free_commands(yyvsp[-1].commands); } } break; case 18: #line 268 "cmd-parse.y" { if (yyvsp[-6].flag) { yyval.commands = yyvsp[-4].commands; cmd_parse_free_commands(yyvsp[-1].commands); } else { yyval.commands = yyvsp[-1].commands; cmd_parse_free_commands(yyvsp[-4].commands); } } break; case 19: #line 278 "cmd-parse.y" { if (yyvsp[-4].flag) { yyval.commands = yyvsp[-2].commands; cmd_parse_free_commands(yyvsp[-1].elif.commands); } else if (yyvsp[-1].elif.flag) { yyval.commands = yyvsp[-1].elif.commands; cmd_parse_free_commands(yyvsp[-2].commands); } else { yyval.commands = cmd_parse_new_commands(); cmd_parse_free_commands(yyvsp[-2].commands); cmd_parse_free_commands(yyvsp[-1].elif.commands); } } break; case 20: #line 292 "cmd-parse.y" { if (yyvsp[-7].flag) { yyval.commands = yyvsp[-5].commands; cmd_parse_free_commands(yyvsp[-4].elif.commands); cmd_parse_free_commands(yyvsp[-1].commands); } else if (yyvsp[-4].elif.flag) { yyval.commands = yyvsp[-4].elif.commands; cmd_parse_free_commands(yyvsp[-5].commands); cmd_parse_free_commands(yyvsp[-1].commands); } else { yyval.commands = yyvsp[-1].commands; cmd_parse_free_commands(yyvsp[-5].commands); cmd_parse_free_commands(yyvsp[-4].elif.commands); } } break; case 21: #line 309 "cmd-parse.y" { if (yyvsp[-2].flag) { yyval.elif.flag = 1; yyval.elif.commands = yyvsp[0].commands; } else { yyval.elif.flag = 0; yyval.elif.commands = cmd_parse_new_commands(); cmd_parse_free_commands(yyvsp[0].commands); } } break; case 22: #line 320 "cmd-parse.y" { if (yyvsp[-3].flag) { yyval.elif.flag = 1; yyval.elif.commands = yyvsp[-1].commands; cmd_parse_free_commands(yyvsp[0].elif.commands); } else if (yyvsp[0].elif.flag) { yyval.elif.flag = 1; yyval.elif.commands = yyvsp[0].elif.commands; cmd_parse_free_commands(yyvsp[-1].commands); } else { yyval.elif.flag = 0; yyval.elif.commands = cmd_parse_new_commands(); cmd_parse_free_commands(yyvsp[-1].commands); cmd_parse_free_commands(yyvsp[0].elif.commands); } } break; case 23: #line 338 "cmd-parse.y" { struct cmd_parse_state *ps = &parse_state; yyval.commands = cmd_parse_new_commands(); if (ps->scope == NULL || ps->scope->flag) TAILQ_INSERT_TAIL(yyval.commands, yyvsp[0].command, entry); else cmd_parse_free_command(yyvsp[0].command); } break; case 24: #line 348 "cmd-parse.y" { yyval.commands = yyvsp[-1].commands; } break; case 25: #line 352 "cmd-parse.y" { yyval.commands = yyvsp[-2].commands; TAILQ_CONCAT(yyval.commands, yyvsp[0].commands, entry); free(yyvsp[0].commands); } break; case 26: #line 358 "cmd-parse.y" { struct cmd_parse_state *ps = &parse_state; if (ps->scope == NULL || ps->scope->flag) { yyval.commands = yyvsp[-2].commands; TAILQ_INSERT_TAIL(yyval.commands, yyvsp[0].command, entry); } else { yyval.commands = cmd_parse_new_commands(); cmd_parse_free_commands(yyvsp[-2].commands); cmd_parse_free_command(yyvsp[0].command); } } break; case 27: #line 371 "cmd-parse.y" { yyval.commands = yyvsp[0].commands; } break; case 28: #line 376 "cmd-parse.y" { struct cmd_parse_state *ps = &parse_state; yyval.command = xcalloc(1, sizeof *yyval.command); yyval.command->name = yyvsp[0].token; yyval.command->line = ps->input->line; } break; case 29: #line 385 "cmd-parse.y" { struct cmd_parse_state *ps = &parse_state; yyval.command = xcalloc(1, sizeof *yyval.command); yyval.command->name = yyvsp[-1].token; yyval.command->line = ps->input->line; yyval.command->argc = yyvsp[0].arguments.argc; yyval.command->argv = yyvsp[0].arguments.argv; } break; case 30: #line 397 "cmd-parse.y" { if (yyvsp[-2].flag) yyval.commands = yyvsp[-1].commands; else { yyval.commands = cmd_parse_new_commands(); cmd_parse_free_commands(yyvsp[-1].commands); } } break; case 31: #line 406 "cmd-parse.y" { if (yyvsp[-4].flag) { yyval.commands = yyvsp[-3].commands; cmd_parse_free_commands(yyvsp[-1].commands); } else { yyval.commands = yyvsp[-1].commands; cmd_parse_free_commands(yyvsp[-3].commands); } } break; case 32: #line 416 "cmd-parse.y" { if (yyvsp[-3].flag) { yyval.commands = yyvsp[-2].commands; cmd_parse_free_commands(yyvsp[-1].elif.commands); } else if (yyvsp[-1].elif.flag) { yyval.commands = yyvsp[-1].elif.commands; cmd_parse_free_commands(yyvsp[-2].commands); } else { yyval.commands = cmd_parse_new_commands(); cmd_parse_free_commands(yyvsp[-2].commands); cmd_parse_free_commands(yyvsp[-1].elif.commands); } } break; case 33: #line 430 "cmd-parse.y" { if (yyvsp[-5].flag) { yyval.commands = yyvsp[-4].commands; cmd_parse_free_commands(yyvsp[-3].elif.commands); cmd_parse_free_commands(yyvsp[-1].commands); } else if (yyvsp[-3].elif.flag) { yyval.commands = yyvsp[-3].elif.commands; cmd_parse_free_commands(yyvsp[-4].commands); cmd_parse_free_commands(yyvsp[-1].commands); } else { yyval.commands = yyvsp[-1].commands; cmd_parse_free_commands(yyvsp[-4].commands); cmd_parse_free_commands(yyvsp[-3].elif.commands); } } break; case 34: #line 447 "cmd-parse.y" { if (yyvsp[-1].flag) { yyval.elif.flag = 1; yyval.elif.commands = yyvsp[0].commands; } else { yyval.elif.flag = 0; yyval.elif.commands = cmd_parse_new_commands(); cmd_parse_free_commands(yyvsp[0].commands); } } break; case 35: #line 458 "cmd-parse.y" { if (yyvsp[-2].flag) { yyval.elif.flag = 1; yyval.elif.commands = yyvsp[-1].commands; cmd_parse_free_commands(yyvsp[0].elif.commands); } else if (yyvsp[0].elif.flag) { yyval.elif.flag = 1; yyval.elif.commands = yyvsp[0].elif.commands; cmd_parse_free_commands(yyvsp[-1].commands); } else { yyval.elif.flag = 0; yyval.elif.commands = cmd_parse_new_commands(); cmd_parse_free_commands(yyvsp[-1].commands); cmd_parse_free_commands(yyvsp[0].elif.commands); } } break; case 36: #line 476 "cmd-parse.y" { yyval.arguments.argc = 1; yyval.arguments.argv = xreallocarray(NULL, 1, sizeof *yyval.arguments.argv); yyval.arguments.argv[0] = yyvsp[0].token; } break; case 37: #line 483 "cmd-parse.y" { cmd_prepend_argv(&yyvsp[0].arguments.argc, &yyvsp[0].arguments.argv, yyvsp[-1].token); free(yyvsp[-1].token); yyval.arguments = yyvsp[0].arguments; } break; case 38: #line 490 "cmd-parse.y" { yyval.token = yyvsp[0].token; } break; case 39: #line 494 "cmd-parse.y" { yyval.token = yyvsp[0].token; } break; #line 1979 "cmd-parse.c" } yyssp -= yym; yystate = *yyssp; yyvsp -= yym; yym = yylhs[yyn]; if (yystate == 0 && yym == 0) { #if YYDEBUG if (yydebug) printf("%sdebug: after reduction, shifting from state 0 to\ state %d\n", YYPREFIX, YYFINAL); #endif yystate = YYFINAL; *++yyssp = YYFINAL; *++yyvsp = yyval; if (yychar < 0) { if ((yychar = yylex()) < 0) yychar = 0; #if YYDEBUG if (yydebug) { yys = 0; if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; if (!yys) yys = "illegal-symbol"; printf("%sdebug: state %d, reading %d (%s)\n", YYPREFIX, YYFINAL, yychar, yys); } #endif } if (yychar == 0) goto yyaccept; goto yyloop; } if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 && yyn <= YYTABLESIZE && yycheck[yyn] == yystate) yystate = yytable[yyn]; else yystate = yydgoto[yym]; #if YYDEBUG if (yydebug) printf("%sdebug: after reduction, shifting from state %d \ to state %d\n", YYPREFIX, *yyssp, yystate); #endif if (yyssp >= yysslim && yygrowstack()) { goto yyoverflow; } *++yyssp = yystate; *++yyvsp = yyval; goto yyloop; yyoverflow: yyerror("yacc stack overflow"); yyabort: if (yyss) free(yyss); if (yyvs) free(yyvs); yyss = yyssp = NULL; yyvs = yyvsp = NULL; yystacksize = 0; return (1); yyaccept: if (yyss) free(yyss); if (yyvs) free(yyvs); yyss = yyssp = NULL; yyvs = yyvsp = NULL; yystacksize = 0; return (0); } tmux-3.0a/alerts.c100644 001750 001750 00000017242 13462323664 0007671/* $OpenBSD$ */ /* * Copyright (c) 2015 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" static int alerts_fired; static void alerts_timer(int, short, void *); static int alerts_enabled(struct window *, int); static void alerts_callback(int, short, void *); static void alerts_reset(struct window *); static int alerts_action_applies(struct winlink *, const char *); static int alerts_check_all(struct window *); static int alerts_check_bell(struct window *); static int alerts_check_activity(struct window *); static int alerts_check_silence(struct window *); static void alerts_set_message(struct winlink *, const char *, const char *); static TAILQ_HEAD(, window) alerts_list = TAILQ_HEAD_INITIALIZER(alerts_list); static void alerts_timer(__unused int fd, __unused short events, void *arg) { struct window *w = arg; log_debug("@%u alerts timer expired", w->id); alerts_queue(w, WINDOW_SILENCE); } static void alerts_callback(__unused int fd, __unused short events, __unused void *arg) { struct window *w, *w1; int alerts; TAILQ_FOREACH_SAFE(w, &alerts_list, alerts_entry, w1) { alerts = alerts_check_all(w); log_debug("@%u alerts check, alerts %#x", w->id, alerts); w->alerts_queued = 0; TAILQ_REMOVE(&alerts_list, w, alerts_entry); w->flags &= ~WINDOW_ALERTFLAGS; window_remove_ref(w, __func__); } alerts_fired = 0; } static int alerts_action_applies(struct winlink *wl, const char *name) { int action; /* * {bell,activity,silence}-action determines when to alert: none means * nothing happens, current means only do something for the current * window and other means only for windows other than the current. */ action = options_get_number(wl->session->options, name); if (action == ALERT_ANY) return (1); if (action == ALERT_CURRENT) return (wl == wl->session->curw); if (action == ALERT_OTHER) return (wl != wl->session->curw); return (0); } static int alerts_check_all(struct window *w) { int alerts; alerts = alerts_check_bell(w); alerts |= alerts_check_activity(w); alerts |= alerts_check_silence(w); return (alerts); } void alerts_check_session(struct session *s) { struct winlink *wl; RB_FOREACH(wl, winlinks, &s->windows) alerts_check_all(wl->window); } static int alerts_enabled(struct window *w, int flags) { if (flags & WINDOW_BELL) { if (options_get_number(w->options, "monitor-bell")) return (1); } if (flags & WINDOW_ACTIVITY) { if (options_get_number(w->options, "monitor-activity")) return (1); } if (flags & WINDOW_SILENCE) { if (options_get_number(w->options, "monitor-silence") != 0) return (1); } return (0); } void alerts_reset_all(void) { struct window *w; RB_FOREACH(w, windows, &windows) alerts_reset(w); } static void alerts_reset(struct window *w) { struct timeval tv; if (!event_initialized(&w->alerts_timer)) evtimer_set(&w->alerts_timer, alerts_timer, w); w->flags &= ~WINDOW_SILENCE; event_del(&w->alerts_timer); timerclear(&tv); tv.tv_sec = options_get_number(w->options, "monitor-silence"); log_debug("@%u alerts timer reset %u", w->id, (u_int)tv.tv_sec); if (tv.tv_sec != 0) event_add(&w->alerts_timer, &tv); } void alerts_queue(struct window *w, int flags) { alerts_reset(w); if ((w->flags & flags) != flags) { w->flags |= flags; log_debug("@%u alerts flags added %#x", w->id, flags); } if (alerts_enabled(w, flags)) { if (!w->alerts_queued) { w->alerts_queued = 1; TAILQ_INSERT_TAIL(&alerts_list, w, alerts_entry); window_add_ref(w, __func__); } if (!alerts_fired) { log_debug("alerts check queued (by @%u)", w->id); event_once(-1, EV_TIMEOUT, alerts_callback, NULL, NULL); alerts_fired = 1; } } } static int alerts_check_bell(struct window *w) { struct winlink *wl; struct session *s; if (~w->flags & WINDOW_BELL) return (0); if (!options_get_number(w->options, "monitor-bell")) return (0); TAILQ_FOREACH(wl, &w->winlinks, wentry) wl->session->flags &= ~SESSION_ALERTED; TAILQ_FOREACH(wl, &w->winlinks, wentry) { /* * Bells are allowed even if there is an existing bell (so do * not check WINLINK_BELL). */ s = wl->session; if (s->curw != wl) { wl->flags |= WINLINK_BELL; server_status_session(s); } if (!alerts_action_applies(wl, "bell-action")) continue; notify_winlink("alert-bell", wl); if (s->flags & SESSION_ALERTED) continue; s->flags |= SESSION_ALERTED; alerts_set_message(wl, "Bell", "visual-bell"); } return (WINDOW_BELL); } static int alerts_check_activity(struct window *w) { struct winlink *wl; struct session *s; if (~w->flags & WINDOW_ACTIVITY) return (0); if (!options_get_number(w->options, "monitor-activity")) return (0); TAILQ_FOREACH(wl, &w->winlinks, wentry) wl->session->flags &= ~SESSION_ALERTED; TAILQ_FOREACH(wl, &w->winlinks, wentry) { if (wl->flags & WINLINK_ACTIVITY) continue; s = wl->session; if (s->curw != wl) { wl->flags |= WINLINK_ACTIVITY; server_status_session(s); } if (!alerts_action_applies(wl, "activity-action")) continue; notify_winlink("alert-activity", wl); if (s->flags & SESSION_ALERTED) continue; s->flags |= SESSION_ALERTED; alerts_set_message(wl, "Activity", "visual-activity"); } return (WINDOW_ACTIVITY); } static int alerts_check_silence(struct window *w) { struct winlink *wl; struct session *s; if (~w->flags & WINDOW_SILENCE) return (0); if (options_get_number(w->options, "monitor-silence") == 0) return (0); TAILQ_FOREACH(wl, &w->winlinks, wentry) wl->session->flags &= ~SESSION_ALERTED; TAILQ_FOREACH(wl, &w->winlinks, wentry) { if (wl->flags & WINLINK_SILENCE) continue; s = wl->session; if (s->curw != wl) { wl->flags |= WINLINK_SILENCE; server_status_session(s); } if (!alerts_action_applies(wl, "silence-action")) continue; notify_winlink("alert-silence", wl); if (s->flags & SESSION_ALERTED) continue; s->flags |= SESSION_ALERTED; alerts_set_message(wl, "Silence", "visual-silence"); } return (WINDOW_SILENCE); } static void alerts_set_message(struct winlink *wl, const char *type, const char *option) { struct client *c; int visual; /* * We have found an alert (bell, activity or silence), so we need to * pass it on to the user. For each client attached to this session, * decide whether a bell, message or both is needed. * * If visual-{bell,activity,silence} is on, then a message is * substituted for a bell; if it is off, a bell is sent as normal; both * mean both a bell and message is sent. */ visual = options_get_number(wl->session->options, option); TAILQ_FOREACH(c, &clients, entry) { if (c->session != wl->session || c->flags & CLIENT_CONTROL) continue; if (visual == VISUAL_OFF || visual == VISUAL_BOTH) tty_putcode(&c->tty, TTYC_BEL); if (visual == VISUAL_OFF) continue; if (c->session->curw == wl) status_message_set(c, "%s in current window", type); else status_message_set(c, "%s in window %d", type, wl->idx); } } tmux-3.0a/arguments.c100644 001750 001750 00000016455 13517540563 0010411/* $OpenBSD$ */ /* * 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 #include "tmux.h" /* * Manipulate command arguments. */ struct args_value { char *value; TAILQ_ENTRY(args_value) entry; }; TAILQ_HEAD(args_values, args_value); struct args_entry { u_char flag; struct args_values values; u_int count; RB_ENTRY(args_entry) entry; }; static struct args_entry *args_find(struct args *, u_char); static int args_cmp(struct args_entry *, struct args_entry *); RB_GENERATE_STATIC(args_tree, args_entry, entry, args_cmp); /* Arguments tree comparison function. */ static int args_cmp(struct args_entry *a1, struct args_entry *a2) { return (a1->flag - a2->flag); } /* Find a flag in the arguments tree. */ static struct args_entry * args_find(struct args *args, u_char ch) { struct args_entry entry; entry.flag = ch; return (RB_FIND(args_tree, &args->tree, &entry)); } /* Parse an argv and argc into a new argument set. */ struct args * args_parse(const char *template, int argc, char **argv) { struct args *args; int opt; args = xcalloc(1, sizeof *args); optreset = 1; optind = 1; while ((opt = getopt(argc, argv, template)) != -1) { if (opt < 0) continue; if (opt == '?' || strchr(template, opt) == NULL) { args_free(args); return (NULL); } args_set(args, opt, 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) { struct args_entry *entry; struct args_entry *entry1; struct args_value *value; struct args_value *value1; cmd_free_argv(args->argc, args->argv); RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) { RB_REMOVE(args_tree, &args->tree, entry); TAILQ_FOREACH_SAFE(value, &entry->values, entry, value1) { TAILQ_REMOVE(&entry->values, value, entry); free(value->value); free(value); } free(entry); } free(args); } /* Add to string. */ static void printflike(3, 4) args_print_add(char **buf, size_t *len, const char *fmt, ...) { va_list ap; char *s; size_t slen; va_start(ap, fmt); slen = xvasprintf(&s, fmt, ap); va_end(ap); *len += slen; *buf = xrealloc(*buf, *len); strlcat(*buf, s, *len); free(s); } /* Add value to string. */ static void args_print_add_value(char **buf, size_t *len, struct args_entry *entry, struct args_value *value) { char *escaped; if (**buf != '\0') args_print_add(buf, len, " -%c ", entry->flag); else args_print_add(buf, len, "-%c ", entry->flag); escaped = args_escape(value->value); args_print_add(buf, len, "%s", escaped); free(escaped); } /* Add argument to string. */ static void args_print_add_argument(char **buf, size_t *len, const char *argument) { char *escaped; if (**buf != '\0') args_print_add(buf, len, " "); escaped = args_escape(argument); args_print_add(buf, len, "%s", escaped); free(escaped); } /* Print a set of arguments. */ char * args_print(struct args *args) { size_t len; char *buf; int i; u_int j; struct args_entry *entry; struct args_value *value; len = 1; buf = xcalloc(1, len); /* Process the flags first. */ RB_FOREACH(entry, args_tree, &args->tree) { if (!TAILQ_EMPTY(&entry->values)) continue; if (*buf == '\0') args_print_add(&buf, &len, "-"); for (j = 0; j < entry->count; j++) args_print_add(&buf, &len, "%c", entry->flag); } /* Then the flags with arguments. */ RB_FOREACH(entry, args_tree, &args->tree) { TAILQ_FOREACH(value, &entry->values, entry) args_print_add_value(&buf, &len, entry, value); } /* And finally the argument vector. */ for (i = 0; i < args->argc; i++) args_print_add_argument(&buf, &len, args->argv[i]); return (buf); } /* Escape an argument. */ char * args_escape(const char *s) { static const char quoted[] = " #\"';${}"; char *escaped, *result; int flags; if (*s == '\0') return (xstrdup(s)); if (s[0] != ' ' && (strchr(quoted, s[0]) != NULL || s[0] == '~') && s[1] == '\0') { xasprintf(&escaped, "\\%c", s[0]); return (escaped); } flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL; if (s[strcspn(s, quoted)] != '\0') flags |= VIS_DQ; utf8_stravis(&escaped, s, flags); if (flags & VIS_DQ) { if (*escaped == '~') xasprintf(&result, "\"\\%s\"", escaped); else xasprintf(&result, "\"%s\"", escaped); } else { if (*escaped == '~') xasprintf(&result, "\\%s", escaped); else result = xstrdup(escaped); } free(escaped); return (result); } /* Return if an argument is present. */ int args_has(struct args *args, u_char ch) { struct args_entry *entry; entry = args_find(args, ch); if (entry == NULL) return (0); return (entry->count); } /* Set argument value in the arguments tree. */ void args_set(struct args *args, u_char ch, const char *s) { struct args_entry *entry; struct args_value *value; entry = args_find(args, ch); if (entry == NULL) { entry = xcalloc(1, sizeof *entry); entry->flag = ch; entry->count = 1; TAILQ_INIT(&entry->values); RB_INSERT(args_tree, &args->tree, entry); } else entry->count++; if (s != NULL) { value = xcalloc(1, sizeof *value); value->value = xstrdup(s); TAILQ_INSERT_TAIL(&entry->values, value, entry); } } /* Get argument value. Will be NULL if it isn't present. */ const char * args_get(struct args *args, u_char ch) { struct args_entry *entry; if ((entry = args_find(args, ch)) == NULL) return (NULL); return (TAILQ_LAST(&entry->values, args_values)->value); } /* Get first value in argument. */ const char * args_first_value(struct args *args, u_char ch, struct args_value **value) { struct args_entry *entry; if ((entry = args_find(args, ch)) == NULL) return (NULL); *value = TAILQ_FIRST(&entry->values); if (*value == NULL) return (NULL); return ((*value)->value); } /* Get next value in argument. */ const char * args_next_value(struct args_value **value) { if (*value == NULL) return (NULL); *value = TAILQ_NEXT(*value, entry); if (*value == NULL) return (NULL); return ((*value)->value); } /* 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; struct args_entry *entry; struct args_value *value; if ((entry = args_find(args, ch)) == NULL) { *cause = xstrdup("missing"); return (0); } value = TAILQ_LAST(&entry->values, args_values); ll = strtonum(value->value, minval, maxval, &errstr); if (errstr != NULL) { *cause = xstrdup(errstr); return (0); } *cause = NULL; return (ll); } tmux-3.0a/attributes.c100644 001750 001750 00000006160 13504653145 0010557/* $OpenBSD$ */ /* * Copyright (c) 2009 Joshua Elsasser * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" const char * attributes_tostring(int attr) { static char buf[512]; size_t len; if (attr == 0) return ("none"); len = xsnprintf(buf, sizeof buf, "%s%s%s%s%s%s%s%s%s%s%s%s%s", (attr & GRID_ATTR_BRIGHT) ? "bright," : "", (attr & GRID_ATTR_DIM) ? "dim," : "", (attr & GRID_ATTR_UNDERSCORE) ? "underscore," : "", (attr & GRID_ATTR_BLINK)? "blink," : "", (attr & GRID_ATTR_REVERSE) ? "reverse," : "", (attr & GRID_ATTR_HIDDEN) ? "hidden," : "", (attr & GRID_ATTR_ITALICS) ? "italics," : "", (attr & GRID_ATTR_STRIKETHROUGH) ? "strikethrough," : "", (attr & GRID_ATTR_UNDERSCORE_2) ? "double-underscore," : "", (attr & GRID_ATTR_UNDERSCORE_3) ? "curly-underscore," : "", (attr & GRID_ATTR_UNDERSCORE_4) ? "dotted-underscore," : "", (attr & GRID_ATTR_UNDERSCORE_5) ? "dashed-underscore," : "", (attr & GRID_ATTR_OVERLINE) ? "overline," : ""); if (len > 0) buf[len - 1] = '\0'; return (buf); } int attributes_fromstring(const char *str) { const char delimiters[] = " ,|"; int attr; size_t end; u_int i; struct { const char* name; int attr; } table[] = { { "bright", GRID_ATTR_BRIGHT }, { "bold", GRID_ATTR_BRIGHT }, { "dim", GRID_ATTR_DIM }, { "underscore", GRID_ATTR_UNDERSCORE }, { "blink", GRID_ATTR_BLINK }, { "reverse", GRID_ATTR_REVERSE }, { "hidden", GRID_ATTR_HIDDEN }, { "italics", GRID_ATTR_ITALICS }, { "strikethrough", GRID_ATTR_STRIKETHROUGH }, { "double-underscore", GRID_ATTR_UNDERSCORE_2 }, { "curly-underscore", GRID_ATTR_UNDERSCORE_3 }, { "dotted-underscore", GRID_ATTR_UNDERSCORE_4 }, { "dashed-underscore", GRID_ATTR_UNDERSCORE_5 }, { "overline", GRID_ATTR_OVERLINE } }; 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); for (i = 0; i < nitems(table); i++) { if (end != strlen(table[i].name)) continue; if (strncasecmp(str, table[i].name, end) == 0) { attr |= table[i].attr; break; } } if (i == nitems(table)) return (-1); str += end + strspn(str + end, delimiters); } while (*str != '\0'); return (attr); } tmux-3.0a/cfg.c100644 001750 001750 00000011425 13535442333 0007127/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "tmux.h" struct client *cfg_client; static char *cfg_file; int cfg_finished; static char **cfg_causes; static u_int cfg_ncauses; static struct cmdq_item *cfg_item; static enum cmd_retval cfg_client_done(__unused struct cmdq_item *item, __unused void *data) { if (!cfg_finished) return (CMD_RETURN_WAIT); return (CMD_RETURN_NORMAL); } static enum cmd_retval cfg_done(__unused struct cmdq_item *item, __unused void *data) { if (cfg_finished) return (CMD_RETURN_NORMAL); cfg_finished = 1; if (!RB_EMPTY(&sessions)) cfg_show_causes(RB_MIN(sessions, &sessions)); if (cfg_item != NULL) cmdq_continue(cfg_item); status_prompt_load_history(); return (CMD_RETURN_NORMAL); } void set_cfg_file(const char *path) { free(cfg_file); cfg_file = xstrdup(path); } void start_cfg(void) { const char *home; int flags = 0; struct client *c; /* * Configuration files are loaded without a client, so commands are run * in the global queue with item->client NULL. * * However, we must block the initial client (but just the initial * client) so that its command runs after the configuration is loaded. * Because start_cfg() is called so early, we can be sure the client's * command queue is currently empty and our callback will be at the * front - we need to get in before MSG_COMMAND. */ cfg_client = c = TAILQ_FIRST(&clients); if (c != NULL) { cfg_item = cmdq_get_callback(cfg_client_done, NULL); cmdq_append(c, cfg_item); } if (cfg_file == NULL) load_cfg(TMUX_CONF, c, NULL, CMD_PARSE_QUIET, NULL); if (cfg_file == NULL && (home = find_home()) != NULL) { xasprintf(&cfg_file, "%s/.tmux.conf", home); flags = CMD_PARSE_QUIET; } if (cfg_file != NULL) load_cfg(cfg_file, c, NULL, flags, NULL); cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL)); } int load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags, struct cmdq_item **new_item) { FILE *f; struct cmd_parse_input pi; struct cmd_parse_result *pr; struct cmdq_item *new_item0; if (new_item != NULL) *new_item = NULL; log_debug("loading %s", path); if ((f = fopen(path, "rb")) == NULL) { if (errno == ENOENT && (flags & CMD_PARSE_QUIET)) return (0); cfg_add_cause("%s: %s", path, strerror(errno)); return (-1); } memset(&pi, 0, sizeof pi); pi.flags = flags; pi.file = path; pi.line = 1; pi.item = item; pi.c = c; pr = cmd_parse_from_file(f, &pi); fclose(f); if (pr->status == CMD_PARSE_EMPTY) return (0); if (pr->status == CMD_PARSE_ERROR) { cfg_add_cause("%s", pr->error); free(pr->error); return (-1); } if (flags & CMD_PARSE_PARSEONLY) { cmd_list_free(pr->cmdlist); return (0); } new_item0 = cmdq_get_command(pr->cmdlist, NULL, NULL, 0); if (item != NULL) cmdq_insert_after(item, new_item0); else cmdq_append(NULL, new_item0); cmd_list_free(pr->cmdlist); if (new_item != NULL) *new_item = new_item0; return (0); } void cfg_add_cause(const char *fmt, ...) { va_list ap; char *msg; va_start(ap, fmt); xvasprintf(&msg, fmt, ap); va_end(ap); cfg_ncauses++; cfg_causes = xreallocarray(cfg_causes, cfg_ncauses, sizeof *cfg_causes); cfg_causes[cfg_ncauses - 1] = msg; } void cfg_print_causes(struct cmdq_item *item) { u_int i; for (i = 0; i < cfg_ncauses; i++) { cmdq_print(item, "%s", cfg_causes[i]); free(cfg_causes[i]); } free(cfg_causes); cfg_causes = NULL; cfg_ncauses = 0; } void cfg_show_causes(struct session *s) { struct window_pane *wp; struct window_mode_entry *wme; u_int i; if (s == NULL || cfg_ncauses == 0) return; wp = s->curw->window->active; wme = TAILQ_FIRST(&wp->modes); if (wme == NULL || wme->mode != &window_view_mode) window_pane_set_mode(wp, &window_view_mode, NULL, NULL); for (i = 0; i < cfg_ncauses; i++) { window_copy_add(wp, "%s", cfg_causes[i]); free(cfg_causes[i]); } free(cfg_causes); cfg_causes = NULL; cfg_ncauses = 0; } tmux-3.0a/client.c100644 001750 001750 00000044574 13517540563 0007665/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "tmux.h" static struct tmuxproc *client_proc; static struct tmuxpeer *client_peer; static int client_flags; static struct event client_stdin; static enum { CLIENT_EXIT_NONE, CLIENT_EXIT_DETACHED, CLIENT_EXIT_DETACHED_HUP, CLIENT_EXIT_LOST_TTY, CLIENT_EXIT_TERMINATED, CLIENT_EXIT_LOST_SERVER, CLIENT_EXIT_EXITED, CLIENT_EXIT_SERVER_EXITED, } client_exitreason = CLIENT_EXIT_NONE; static int client_exitval; static enum msgtype client_exittype; static const char *client_exitsession; static const char *client_execshell; static const char *client_execcmd; static int client_attached; static __dead void client_exec(const char *,const char *); static int client_get_lock(char *); static int client_connect(struct event_base *, const char *, int); static void client_send_identify(const char *, const char *); static void client_stdin_callback(int, short, void *); static void client_write(int, const char *, size_t); static void client_signal(int); static void client_dispatch(struct imsg *, void *); static void client_dispatch_attached(struct imsg *); static void client_dispatch_wait(struct imsg *); static const char *client_exit_message(void); /* * Get server create lock. If already held then server start is happening in * another client, so block until the lock is released and return -2 to * retry. Return -1 on failure to continue and start the server anyway. */ static int client_get_lock(char *lockfile) { int lockfd; log_debug("lock file is %s", lockfile); if ((lockfd = open(lockfile, O_WRONLY|O_CREAT, 0600)) == -1) { log_debug("open failed: %s", strerror(errno)); return (-1); } if (flock(lockfd, LOCK_EX|LOCK_NB) == -1) { log_debug("flock failed: %s", strerror(errno)); if (errno != EAGAIN) return (lockfd); while (flock(lockfd, LOCK_EX) == -1 && errno == EINTR) /* nothing */; close(lockfd); return (-2); } log_debug("flock succeeded"); return (lockfd); } /* Connect client to server. */ static int client_connect(struct event_base *base, const char *path, int start_server) { struct sockaddr_un sa; size_t size; int fd, lockfd = -1, locked = 0; char *lockfile = NULL; 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); } log_debug("socket is %s", path); retry: if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) return (-1); log_debug("trying connect"); if (connect(fd, (struct sockaddr *)&sa, sizeof sa) == -1) { log_debug("connect failed: %s", strerror(errno)); if (errno != ECONNREFUSED && errno != ENOENT) goto failed; if (!start_server) goto failed; close(fd); if (!locked) { xasprintf(&lockfile, "%s.lock", path); if ((lockfd = client_get_lock(lockfile)) < 0) { log_debug("didn't get lock (%d)", lockfd); free(lockfile); lockfile = NULL; if (lockfd == -2) goto retry; } log_debug("got lock (%d)", lockfd); /* * Always retry at least once, even if we got the lock, * because another client could have taken the lock, * started the server and released the lock between our * connect() and flock(). */ locked = 1; goto retry; } if (lockfd >= 0 && unlink(path) != 0 && errno != ENOENT) { free(lockfile); close(lockfd); return (-1); } fd = server_start(client_proc, base, lockfd, lockfile); } if (locked && lockfd >= 0) { free(lockfile); close(lockfd); } setblocking(fd, 0); return (fd); failed: if (locked) { free(lockfile); close(lockfd); } close(fd); return (-1); } /* Get exit string from reason number. */ const char * client_exit_message(void) { static char msg[256]; switch (client_exitreason) { case CLIENT_EXIT_NONE: break; case CLIENT_EXIT_DETACHED: if (client_exitsession != NULL) { xsnprintf(msg, sizeof msg, "detached " "(from session %s)", client_exitsession); return (msg); } return ("detached"); case CLIENT_EXIT_DETACHED_HUP: if (client_exitsession != NULL) { xsnprintf(msg, sizeof msg, "detached and SIGHUP " "(from session %s)", client_exitsession); return (msg); } return ("detached and SIGHUP"); case CLIENT_EXIT_LOST_TTY: return ("lost tty"); case CLIENT_EXIT_TERMINATED: return ("terminated"); case CLIENT_EXIT_LOST_SERVER: return ("server exited unexpectedly"); case CLIENT_EXIT_EXITED: return ("exited"); case CLIENT_EXIT_SERVER_EXITED: return ("server exited"); } return ("unknown reason"); } /* Client main loop. */ int client_main(struct event_base *base, int argc, char **argv, int flags) { struct cmd_parse_result *pr; struct cmd *cmd; struct msg_command_data *data; int cmdflags, fd, i; const char *ttynam, *cwd; pid_t ppid; enum msgtype msg; struct termios tio, saved_tio; size_t size; /* Ignore SIGCHLD now or daemon() in the server will leave a zombie. */ signal(SIGCHLD, SIG_IGN); /* Save the flags. */ client_flags = flags; /* Set up the initial command. */ cmdflags = 0; if (shell_command != NULL) { msg = MSG_SHELL; cmdflags = CMD_STARTSERVER; } else if (argc == 0) { msg = MSG_COMMAND; cmdflags = CMD_STARTSERVER; } 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. */ pr = cmd_parse_from_arguments(argc, argv, NULL); if (pr->status == CMD_PARSE_SUCCESS) { TAILQ_FOREACH(cmd, &pr->cmdlist->list, qentry) { if (cmd->entry->flags & CMD_STARTSERVER) cmdflags |= CMD_STARTSERVER; } cmd_list_free(pr->cmdlist); } else free(pr->error); } /* Create client process structure (starts logging). */ client_proc = proc_start("client"); proc_set_signals(client_proc, client_signal); /* Initialize the client socket and start the server. */ fd = client_connect(base, socket_path, cmdflags & CMD_STARTSERVER); if (fd == -1) { if (errno == ECONNREFUSED) { fprintf(stderr, "no server running on %s\n", socket_path); } else { fprintf(stderr, "error connecting to %s (%s)\n", socket_path, strerror(errno)); } return (1); } client_peer = proc_add_peer(client_proc, fd, client_dispatch, NULL); /* Save these before pledge(). */ if ((cwd = find_cwd()) == NULL && (cwd = find_home()) == NULL) cwd = "/"; if ((ttynam = ttyname(STDIN_FILENO)) == NULL) ttynam = ""; /* * Drop privileges for client. "proc exec" is needed for -c and for * locking (which uses system(3)). * * "tty" is needed to restore termios(4) and also for some reason -CC * does not work properly without it (input is not recognised). * * "sendfd" is dropped later in client_dispatch_wait(). */ if (pledge("stdio unix sendfd proc exec tty", NULL) != 0) fatal("pledge failed"); /* Free stuff that is not used in the client. */ if (ptm_fd != -1) close(ptm_fd); options_free(global_options); options_free(global_s_options); options_free(global_w_options); environ_free(global_environ); /* Create stdin handler. */ setblocking(STDIN_FILENO, 0); event_set(&client_stdin, STDIN_FILENO, EV_READ|EV_PERSIST, client_stdin_callback, NULL); if (client_flags & CLIENT_CONTROLCONTROL) { if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) { fprintf(stderr, "tcgetattr failed: %s\n", strerror(errno)); return (1); } cfmakeraw(&tio); tio.c_iflag = ICRNL|IXANY; tio.c_oflag = OPOST|ONLCR; #ifdef NOKERNINFO tio.c_lflag = NOKERNINFO; #endif tio.c_cflag = CREAD|CS8|HUPCL; tio.c_cc[VMIN] = 1; tio.c_cc[VTIME] = 0; cfsetispeed(&tio, cfgetispeed(&saved_tio)); cfsetospeed(&tio, cfgetospeed(&saved_tio)); tcsetattr(STDIN_FILENO, TCSANOW, &tio); } /* Send identify messages. */ client_send_identify(ttynam, cwd); /* Send first command. */ if (msg == MSG_COMMAND) { /* How big is the command? */ size = 0; for (i = 0; i < argc; i++) size += strlen(argv[i]) + 1; if (size > MAX_IMSGSIZE - (sizeof *data)) { fprintf(stderr, "command too long\n"); return (1); } data = xmalloc((sizeof *data) + size); /* Prepare command for server. */ data->argc = argc; if (cmd_pack_argv(argc, argv, (char *)(data + 1), size) != 0) { fprintf(stderr, "command too long\n"); free(data); return (1); } size += sizeof *data; /* Send the command. */ if (proc_send(client_peer, msg, -1, data, size) != 0) { fprintf(stderr, "failed to send command\n"); free(data); return (1); } free(data); } else if (msg == MSG_SHELL) proc_send(client_peer, msg, -1, NULL, 0); /* Start main loop. */ proc_loop(client_proc, NULL); /* Run command if user requested exec, instead of exiting. */ if (client_exittype == MSG_EXEC) { if (client_flags & CLIENT_CONTROLCONTROL) tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio); client_exec(client_execshell, client_execcmd); } /* Print the exit message, if any, and exit. */ if (client_attached) { if (client_exitreason != CLIENT_EXIT_NONE) printf("[%s]\n", client_exit_message()); ppid = getppid(); if (client_exittype == MSG_DETACHKILL && ppid > 1) kill(ppid, SIGHUP); } else if (client_flags & CLIENT_CONTROLCONTROL) { if (client_exitreason != CLIENT_EXIT_NONE) printf("%%exit %s\n", client_exit_message()); else printf("%%exit\n"); printf("\033\\"); tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio); } else if (client_exitreason != CLIENT_EXIT_NONE) fprintf(stderr, "%s\n", client_exit_message()); setblocking(STDIN_FILENO, 1); return (client_exitval); } /* Send identify messages to server. */ static void client_send_identify(const char *ttynam, const char *cwd) { const char *s; char **ss; size_t sslen; int fd, flags = client_flags; pid_t pid; proc_send(client_peer, MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags); if ((s = getenv("TERM")) == NULL) s = ""; proc_send(client_peer, MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1); proc_send(client_peer, MSG_IDENTIFY_TTYNAME, -1, ttynam, strlen(ttynam) + 1); proc_send(client_peer, MSG_IDENTIFY_CWD, -1, cwd, strlen(cwd) + 1); if ((fd = dup(STDIN_FILENO)) == -1) fatal("dup failed"); proc_send(client_peer, MSG_IDENTIFY_STDIN, fd, NULL, 0); pid = getpid(); proc_send(client_peer, MSG_IDENTIFY_CLIENTPID, -1, &pid, sizeof pid); for (ss = environ; *ss != NULL; ss++) { sslen = strlen(*ss) + 1; if (sslen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) continue; proc_send(client_peer, MSG_IDENTIFY_ENVIRON, -1, *ss, sslen); } proc_send(client_peer, MSG_IDENTIFY_DONE, -1, NULL, 0); } /* Callback for client stdin read events. */ static void client_stdin_callback(__unused int fd, __unused short events, __unused void *arg) { struct msg_stdin_data data; data.size = read(STDIN_FILENO, data.data, sizeof data.data); if (data.size == -1 && (errno == EINTR || errno == EAGAIN)) return; proc_send(client_peer, MSG_STDIN, -1, &data, sizeof data); if (data.size <= 0) event_del(&client_stdin); } /* Force write to file descriptor. */ static void client_write(int fd, const char *data, size_t size) { ssize_t used; log_debug("%s: %.*s", __func__, (int)size, data); while (size != 0) { used = write(fd, data, size); if (used == -1) { if (errno == EINTR || errno == EAGAIN) continue; break; } data += used; size -= used; } } /* Run command in shell; used for -c. */ static __dead void client_exec(const char *shell, const char *shellcmd) { const char *name, *ptr; char *argv0; log_debug("shell %s, command %s", shell, shellcmd); ptr = strrchr(shell, '/'); if (ptr != NULL && *(ptr + 1) != '\0') name = ptr + 1; else name = shell; if (client_flags & CLIENT_LOGIN) xasprintf(&argv0, "-%s", name); else xasprintf(&argv0, "%s", name); setenv("SHELL", shell, 1); proc_clear_signals(client_proc, 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"); } /* Callback to handle signals in the client. */ static void client_signal(int sig) { struct sigaction sigact; int status; if (sig == SIGCHLD) waitpid(WAIT_ANY, &status, WNOHANG); else if (!client_attached) { if (sig == SIGTERM) proc_exit(client_proc); } else { switch (sig) { case SIGHUP: client_exitreason = CLIENT_EXIT_LOST_TTY; client_exitval = 1; proc_send(client_peer, MSG_EXITING, -1, NULL, 0); break; case SIGTERM: client_exitreason = CLIENT_EXIT_TERMINATED; client_exitval = 1; proc_send(client_peer, MSG_EXITING, -1, NULL, 0); break; case SIGWINCH: proc_send(client_peer, MSG_RESIZE, -1, 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"); proc_send(client_peer, MSG_WAKEUP, -1, NULL, 0); break; } } } /* Callback for client read events. */ static void client_dispatch(struct imsg *imsg, __unused void *arg) { if (imsg == NULL) { client_exitreason = CLIENT_EXIT_LOST_SERVER; client_exitval = 1; proc_exit(client_proc); return; } if (client_attached) client_dispatch_attached(imsg); else client_dispatch_wait(imsg); } /* Dispatch imsgs when in wait state (before MSG_READY). */ static void client_dispatch_wait(struct imsg *imsg) { char *data; ssize_t datalen; struct msg_stdout_data stdoutdata; struct msg_stderr_data stderrdata; int retval; static int pledge_applied; /* * "sendfd" is no longer required once all of the identify messages * have been sent. We know the server won't send us anything until that * point (because we don't ask it to), so we can drop "sendfd" once we * get the first message from the server. */ if (!pledge_applied) { if (pledge("stdio unix proc exec tty", NULL) != 0) fatal("pledge failed"); pledge_applied = 1; }; data = imsg->data; datalen = imsg->hdr.len - IMSG_HEADER_SIZE; switch (imsg->hdr.type) { case MSG_EXIT: case MSG_SHUTDOWN: if (datalen != sizeof retval && datalen != 0) fatalx("bad MSG_EXIT size"); if (datalen == sizeof retval) { memcpy(&retval, data, sizeof retval); client_exitval = retval; } proc_exit(client_proc); break; case MSG_READY: if (datalen != 0) fatalx("bad MSG_READY size"); event_del(&client_stdin); client_attached = 1; proc_send(client_peer, MSG_RESIZE, -1, NULL, 0); break; case MSG_STDIN: if (datalen != 0) fatalx("bad MSG_STDIN size"); event_add(&client_stdin, NULL); break; case MSG_STDOUT: if (datalen != sizeof stdoutdata) fatalx("bad MSG_STDOUT size"); memcpy(&stdoutdata, data, sizeof stdoutdata); client_write(STDOUT_FILENO, stdoutdata.data, stdoutdata.size); break; case MSG_STDERR: if (datalen != sizeof stderrdata) fatalx("bad MSG_STDERR size"); memcpy(&stderrdata, data, sizeof stderrdata); client_write(STDERR_FILENO, stderrdata.data, stderrdata.size); break; case MSG_VERSION: if (datalen != 0) fatalx("bad MSG_VERSION size"); fprintf(stderr, "protocol version mismatch " "(client %d, server %u)\n", PROTOCOL_VERSION, imsg->hdr.peerid & 0xff); client_exitval = 1; proc_exit(client_proc); break; case MSG_SHELL: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_SHELL string"); client_exec(data, shell_command); /* NOTREACHED */ case MSG_DETACH: case MSG_DETACHKILL: proc_send(client_peer, MSG_EXITING, -1, NULL, 0); break; case MSG_EXITED: proc_exit(client_proc); break; } } /* Dispatch imsgs in attached state (after MSG_READY). */ static void client_dispatch_attached(struct imsg *imsg) { struct sigaction sigact; char *data; ssize_t datalen; data = imsg->data; datalen = imsg->hdr.len - IMSG_HEADER_SIZE; switch (imsg->hdr.type) { case MSG_DETACH: case MSG_DETACHKILL: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_DETACH string"); client_exitsession = xstrdup(data); client_exittype = imsg->hdr.type; if (imsg->hdr.type == MSG_DETACHKILL) client_exitreason = CLIENT_EXIT_DETACHED_HUP; else client_exitreason = CLIENT_EXIT_DETACHED; proc_send(client_peer, MSG_EXITING, -1, NULL, 0); break; case MSG_EXEC: if (datalen == 0 || data[datalen - 1] != '\0' || strlen(data) + 1 == (size_t)datalen) fatalx("bad MSG_EXEC string"); client_execcmd = xstrdup(data); client_execshell = xstrdup(data + strlen(data) + 1); client_exittype = imsg->hdr.type; proc_send(client_peer, MSG_EXITING, -1, NULL, 0); break; case MSG_EXIT: if (datalen != 0 && datalen != sizeof (int)) fatalx("bad MSG_EXIT size"); proc_send(client_peer, MSG_EXITING, -1, NULL, 0); client_exitreason = CLIENT_EXIT_EXITED; break; case MSG_EXITED: if (datalen != 0) fatalx("bad MSG_EXITED size"); proc_exit(client_proc); break; case MSG_SHUTDOWN: if (datalen != 0) fatalx("bad MSG_SHUTDOWN size"); proc_send(client_peer, MSG_EXITING, -1, NULL, 0); client_exitreason = CLIENT_EXIT_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 == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_LOCK string"); system(data); proc_send(client_peer, MSG_UNLOCK, -1, NULL, 0); break; } } tmux-3.0a/cmd-attach-session.c100644 001750 001750 00000011517 13570677043 0012067/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "tmux.h" /* * Attach existing session to the current terminal. */ static enum cmd_retval cmd_attach_session_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_attach_session_entry = { .name = "attach-session", .alias = "attach", .args = { "c:dErt:x", 0, 0 }, .usage = "[-dErx] [-c working-directory] " CMD_TARGET_SESSION_USAGE, /* -t is special */ .flags = CMD_STARTSERVER, .exec = cmd_attach_session_exec }; enum cmd_retval cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, int xflag, int rflag, const char *cflag, int Eflag) { struct cmd_find_state *current = &item->shared->current; enum cmd_find_type type; int flags; struct client *c = item->client, *c_loop; struct session *s; struct winlink *wl; struct window_pane *wp; char *cause; enum msgtype msgtype; if (RB_EMPTY(&sessions)) { cmdq_error(item, "no sessions"); return (CMD_RETURN_ERROR); } if (c == NULL) return (CMD_RETURN_NORMAL); if (server_client_check_nested(c)) { cmdq_error(item, "sessions should be nested with care, " "unset $TMUX to force"); return (CMD_RETURN_ERROR); } if (tflag != NULL && tflag[strcspn(tflag, ":.")] != '\0') { type = CMD_FIND_PANE; flags = 0; } else { type = CMD_FIND_SESSION; flags = CMD_FIND_PREFER_UNATTACHED; } if (cmd_find_target(&item->target, item, tflag, type, flags) != 0) return (CMD_RETURN_ERROR); s = item->target.s; wl = item->target.wl; wp = item->target.wp; if (wl != NULL) { if (wp != NULL) window_set_active_pane(wp->window, wp, 1); session_set_current(s, wl); if (wp != NULL) cmd_find_from_winlink_pane(current, wl, wp, 0); else cmd_find_from_winlink(current, wl, 0); } if (cflag != NULL) { free((void *)s->cwd); s->cwd = format_single(item, cflag, c, s, wl, wp); } c->last_session = c->session; if (c->session != NULL) { if (dflag || xflag) { if (xflag) msgtype = MSG_DETACHKILL; else msgtype = MSG_DETACH; TAILQ_FOREACH(c_loop, &clients, entry) { if (c_loop->session != s || c == c_loop) continue; server_client_detach(c_loop, msgtype); } } if (!Eflag) environ_update(s->options, c->environ, s->environ); c->session = s; if (~item->shared->flags & CMDQ_SHARED_REPEAT) server_client_set_key_table(c, NULL); tty_update_client_offset(c); status_timer_start(c); notify_client("client-session-changed", c); session_update_activity(s, NULL); gettimeofday(&s->last_attached_time, NULL); server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; } else { if (server_client_open(c, &cause) != 0) { cmdq_error(item, "open terminal failed: %s", cause); free(cause); return (CMD_RETURN_ERROR); } if (rflag) c->flags |= CLIENT_READONLY; if (dflag || xflag) { if (xflag) msgtype = MSG_DETACHKILL; else msgtype = MSG_DETACH; TAILQ_FOREACH(c_loop, &clients, entry) { if (c_loop->session != s || c == c_loop) continue; server_client_detach(c_loop, msgtype); } } if (!Eflag) environ_update(s->options, c->environ, s->environ); c->session = s; server_client_set_key_table(c, NULL); tty_update_client_offset(c); status_timer_start(c); notify_client("client-session-changed", c); session_update_activity(s, NULL); gettimeofday(&s->last_attached_time, NULL); server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; if (~c->flags & CLIENT_CONTROL) proc_send(c->peer, MSG_READY, -1, NULL, 0); notify_client("client-attached", c); c->flags |= CLIENT_ATTACHED; } recalculate_sizes(); alerts_check_session(s); server_update_socket(); return (CMD_RETURN_NORMAL); } static enum cmd_retval cmd_attach_session_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; return (cmd_attach_session(item, args_get(args, 't'), args_has(args, 'd'), args_has(args, 'x'), args_has(args, 'r'), args_get(args, 'c'), args_has(args, 'E'))); } tmux-3.0a/cmd-bind-key.c100644 001750 001750 00000004423 13504653145 0010634/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Bind a key to a command. */ static enum cmd_retval cmd_bind_key_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_bind_key_entry = { .name = "bind-key", .alias = "bind", .args = { "cnrT:", 2, -1 }, .usage = "[-cnr] [-T key-table] key " "command [arguments]", .flags = CMD_AFTERHOOK, .exec = cmd_bind_key_exec }; static enum cmd_retval cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; key_code key; const char *tablename; struct cmd_parse_result *pr; char **argv = args->argv; int argc = args->argc; key = key_string_lookup_string(argv[0]); if (key == KEYC_NONE || key == KEYC_UNKNOWN) { cmdq_error(item, "unknown key: %s", argv[0]); return (CMD_RETURN_ERROR); } if (args_has(args, 'T')) tablename = args_get(args, 'T'); else if (args_has(args, 'n')) tablename = "root"; else tablename = "prefix"; if (argc == 2) pr = cmd_parse_from_string(argv[1], NULL); else pr = cmd_parse_from_arguments(argc - 1, argv + 1, NULL); switch (pr->status) { case CMD_PARSE_EMPTY: cmdq_error(item, "empty command"); return (CMD_RETURN_ERROR); case CMD_PARSE_ERROR: cmdq_error(item, "%s", pr->error); free(pr->error); return (CMD_RETURN_ERROR); case CMD_PARSE_SUCCESS: break; } key_bindings_add(tablename, key, args_has(args, 'r'), pr->cmdlist); return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-break-pane.c100644 001750 001750 00000006655 13570677043 0011156/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Break pane off into a window. */ #define BREAK_PANE_TEMPLATE "#{session_name}:#{window_index}.#{pane_index}" static enum cmd_retval cmd_break_pane_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_break_pane_entry = { .name = "break-pane", .alias = "breakp", .args = { "dPF:n:s:t:", 0, 0 }, .usage = "[-dP] [-F format] [-n window-name] [-s src-pane] " "[-t dst-window]", .source = { 's', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX }, .flags = 0, .exec = cmd_break_pane_exec }; static enum cmd_retval cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct cmd_find_state *current = &item->shared->current; struct client *c = cmd_find_client(item, NULL, 1); struct winlink *wl = item->source.wl; struct session *src_s = item->source.s; struct session *dst_s = item->target.s; struct window_pane *wp = item->source.wp; struct window *w = wl->window; char *name, *cause; int idx = item->target.idx; const char *template; char *cp; if (idx != -1 && winlink_find_by_index(&dst_s->windows, idx) != NULL) { cmdq_error(item, "index %d already in use", idx); return (CMD_RETURN_ERROR); } if (window_count_panes(w) == 1) { cmdq_error(item, "can't break with only one pane"); return (CMD_RETURN_ERROR); } server_unzoom_window(w); TAILQ_REMOVE(&w->panes, wp, entry); window_lost_pane(w, wp); layout_close_pane(wp); w = wp->window = window_create(w->sx, w->sy); options_set_parent(wp->options, w->options); wp->flags |= PANE_STYLECHANGED; TAILQ_INSERT_HEAD(&w->panes, wp, entry); w->active = wp; if (!args_has(args, 'n')) { name = default_window_name(w); window_set_name(w, name); free(name); } else { window_set_name(w, args_get(args, 'n')); options_set_number(w->options, "automatic-rename", 0); } layout_init(w, wp); wp->flags |= PANE_CHANGED; if (idx == -1) idx = -1 - options_get_number(dst_s->options, "base-index"); wl = session_attach(dst_s, w, idx, &cause); /* can't fail */ if (!args_has(self->args, 'd')) { session_select(dst_s, wl->idx); cmd_find_from_session(current, dst_s, 0); } server_redraw_session(src_s); if (src_s != dst_s) server_redraw_session(dst_s); server_status_session_group(src_s); if (src_s != dst_s) server_status_session_group(dst_s); if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) template = BREAK_PANE_TEMPLATE; cp = format_single(item, template, c, dst_s, wl, wp); cmdq_print(item, "%s", cp); free(cp); } return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-capture-pane.c100644 001750 001750 00000013740 13570677043 0011526/* $OpenBSD$ */ /* * 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 or stdout. */ static enum cmd_retval cmd_capture_pane_exec(struct cmd *, struct cmdq_item *); static char *cmd_capture_pane_append(char *, size_t *, char *, size_t); static char *cmd_capture_pane_pending(struct args *, struct window_pane *, size_t *); static char *cmd_capture_pane_history(struct args *, struct cmdq_item *, struct window_pane *, size_t *); const struct cmd_entry cmd_capture_pane_entry = { .name = "capture-pane", .alias = "capturep", .args = { "ab:CeE:JpPqS:t:", 0, 0 }, .usage = "[-aCeJpPq] " CMD_BUFFER_USAGE " [-E end-line] " "[-S start-line] " CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, .flags = CMD_AFTERHOOK, .exec = cmd_capture_pane_exec }; const struct cmd_entry cmd_clear_history_entry = { .name = "clear-history", .alias = "clearhist", .args = { "t:", 0, 0 }, .usage = CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, .flags = CMD_AFTERHOOK, .exec = cmd_capture_pane_exec }; static char * cmd_capture_pane_append(char *buf, size_t *len, char *line, size_t linelen) { buf = xrealloc(buf, *len + linelen + 1); memcpy(buf + *len, line, linelen); *len += linelen; return (buf); } static char * cmd_capture_pane_pending(struct args *args, struct window_pane *wp, size_t *len) { struct evbuffer *pending; char *buf, *line, tmp[5]; size_t linelen; u_int i; pending = input_pending(wp); if (pending == NULL) return (xstrdup("")); line = EVBUFFER_DATA(pending); linelen = EVBUFFER_LENGTH(pending); buf = xstrdup(""); if (args_has(args, 'C')) { for (i = 0; i < linelen; i++) { if (line[i] >= ' ' && line[i] != '\\') { tmp[0] = line[i]; tmp[1] = '\0'; } else xsnprintf(tmp, sizeof tmp, "\\%03hho", line[i]); buf = cmd_capture_pane_append(buf, len, tmp, strlen(tmp)); } } else buf = cmd_capture_pane_append(buf, len, line, linelen); return (buf); } static char * cmd_capture_pane_history(struct args *args, struct cmdq_item *item, struct window_pane *wp, size_t *len) { struct grid *gd; const struct grid_line *gl; struct grid_cell *gc = NULL; int n, with_codes, escape_c0, join_lines; u_int i, sx, top, bottom, tmp; char *cause, *buf, *line; const char *Sflag, *Eflag; size_t linelen; sx = screen_size_x(&wp->base); if (args_has(args, 'a')) { gd = wp->saved_grid; if (gd == NULL) { if (!args_has(args, 'q')) { cmdq_error(item, "no alternate screen"); return (NULL); } return (xstrdup("")); } } else gd = wp->base.grid; Sflag = args_get(args, 'S'); if (Sflag != NULL && strcmp(Sflag, "-") == 0) top = 0; else { n = args_strtonum(args, 'S', INT_MIN, SHRT_MAX, &cause); if (cause != NULL) { top = gd->hsize; free(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; } Eflag = args_get(args, 'E'); if (Eflag != NULL && strcmp(Eflag, "-") == 0) bottom = gd->hsize + gd->sy - 1; else { n = args_strtonum(args, 'E', INT_MIN, SHRT_MAX, &cause); if (cause != NULL) { bottom = gd->hsize + gd->sy - 1; free(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; } with_codes = args_has(args, 'e'); escape_c0 = args_has(args, 'C'); join_lines = args_has(args, 'J'); buf = NULL; for (i = top; i <= bottom; i++) { line = grid_string_cells(gd, 0, i, sx, &gc, with_codes, escape_c0, !join_lines); linelen = strlen(line); buf = cmd_capture_pane_append(buf, len, line, linelen); gl = grid_peek_line(gd, i); if (!join_lines || !(gl->flags & GRID_LINE_WRAPPED)) buf[(*len)++] = '\n'; free(line); } return (buf); } static enum cmd_retval cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct client *c; struct window_pane *wp = item->target.wp; char *buf, *cause; const char *bufname; size_t len; if (self->entry == &cmd_clear_history_entry) { window_pane_reset_mode_all(wp); grid_clear_history(wp->base.grid); return (CMD_RETURN_NORMAL); } len = 0; if (args_has(args, 'P')) buf = cmd_capture_pane_pending(args, wp, &len); else buf = cmd_capture_pane_history(args, item, wp, &len); if (buf == NULL) return (CMD_RETURN_ERROR); if (args_has(args, 'p')) { c = item->client; if (c == NULL || (c->session != NULL && !(c->flags & CLIENT_CONTROL))) { cmdq_error(item, "can't write to stdout"); free(buf); return (CMD_RETURN_ERROR); } evbuffer_add(c->stdout_data, buf, len); free(buf); if (args_has(args, 'P') && len > 0) evbuffer_add(c->stdout_data, "\n", 1); server_client_push_stdout(c); } else { bufname = NULL; if (args_has(args, 'b')) bufname = args_get(args, 'b'); if (paste_set(buf, len, bufname, &cause) != 0) { cmdq_error(item, "%s", cause); free(cause); free(buf); return (CMD_RETURN_ERROR); } } return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-choose-tree.c100644 001750 001750 00000004755 13570677043 0011365/* $OpenBSD$ */ /* * Copyright (c) 2012 Thomas Adam * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN 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 a mode. */ static enum cmd_retval cmd_choose_tree_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_choose_tree_entry = { .name = "choose-tree", .alias = NULL, .args = { "F:Gf:NO:st:wZ", 0, 1 }, .usage = "[-GNsw] [-F format] [-f filter] [-O sort-order] " CMD_TARGET_PANE_USAGE " [template]", .target = { 't', CMD_FIND_PANE, 0 }, .flags = 0, .exec = cmd_choose_tree_exec }; const struct cmd_entry cmd_choose_client_entry = { .name = "choose-client", .alias = NULL, .args = { "F:f:NO:t:Z", 0, 1 }, .usage = "[-N] [-F format] [-f filter] [-O sort-order] " CMD_TARGET_PANE_USAGE " [template]", .target = { 't', CMD_FIND_PANE, 0 }, .flags = 0, .exec = cmd_choose_tree_exec }; const struct cmd_entry cmd_choose_buffer_entry = { .name = "choose-buffer", .alias = NULL, .args = { "F:f:NO:t:Z", 0, 1 }, .usage = "[-N] [-F format] [-f filter] [-O sort-order] " CMD_TARGET_PANE_USAGE " [template]", .target = { 't', CMD_FIND_PANE, 0 }, .flags = 0, .exec = cmd_choose_tree_exec }; static enum cmd_retval cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct window_pane *wp = item->target.wp; const struct window_mode *mode; if (self->entry == &cmd_choose_buffer_entry) { if (paste_get_top(NULL) == NULL) return (CMD_RETURN_NORMAL); mode = &window_buffer_mode; } else if (self->entry == &cmd_choose_client_entry) { if (server_client_how_many() == 0) return (CMD_RETURN_NORMAL); mode = &window_client_mode; } else mode = &window_tree_mode; window_pane_set_mode(wp, mode, &item->target, args); return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-command-prompt.c100644 001750 001750 00000011505 13506620447 0012067/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include "tmux.h" /* * Prompt for command in client. */ static enum cmd_retval cmd_command_prompt_exec(struct cmd *, struct cmdq_item *); static int cmd_command_prompt_callback(struct client *, void *, const char *, int); static void cmd_command_prompt_free(void *); const struct cmd_entry cmd_command_prompt_entry = { .name = "command-prompt", .alias = NULL, .args = { "1iI:Np:t:", 0, 1 }, .usage = "[-1Ni] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " " "[template]", .flags = 0, .exec = cmd_command_prompt_exec }; struct cmd_command_prompt_cdata { int flags; char *inputs; char *next_input; char *prompts; char *next_prompt; char *template; int idx; }; static enum cmd_retval cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) { 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(item, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); if (c->prompt_string != NULL) return (CMD_RETURN_NORMAL); cdata = xcalloc(1, sizeof *cdata); cdata->inputs = NULL; cdata->next_input = NULL; cdata->prompts = NULL; cdata->next_prompt = NULL; cdata->template = NULL; cdata->idx = 1; 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, ","); } if (args_has(args, '1')) cdata->flags |= PROMPT_SINGLE; else if (args_has(args, 'N')) cdata->flags |= PROMPT_NUMERIC; else if (args_has(args, 'i')) cdata->flags |= PROMPT_INCREMENTAL; status_prompt_set(c, prompt, input, cmd_command_prompt_callback, cmd_command_prompt_free, cdata, cdata->flags); free(prompt); return (CMD_RETURN_NORMAL); } static int cmd_command_prompt_callback(struct client *c, void *data, const char *s, int done) { struct cmd_command_prompt_cdata *cdata = data; struct cmdq_item *new_item; char *new_template, *prompt, *ptr; char *input = NULL; struct cmd_parse_result *pr; if (s == NULL) return (0); if (done && (cdata->flags & PROMPT_INCREMENTAL)) return (0); new_template = cmd_template_replace(cdata->template, s, cdata->idx); if (done) { free(cdata->template); cdata->template = new_template; } /* * Check if there are more prompts; if so, get its respective input * and update the prompt data. */ if (done && (ptr = strsep(&cdata->next_prompt, ",")) != NULL) { xasprintf(&prompt, "%s ", ptr); input = strsep(&cdata->next_input, ","); status_prompt_update(c, prompt, input); free(prompt); cdata->idx++; return (1); } pr = cmd_parse_from_string(new_template, NULL); switch (pr->status) { case CMD_PARSE_EMPTY: new_item = NULL; break; case CMD_PARSE_ERROR: new_item = cmdq_get_error(pr->error); free(pr->error); cmdq_append(c, new_item); break; case CMD_PARSE_SUCCESS: new_item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0); cmd_list_free(pr->cmdlist); cmdq_append(c, new_item); break; } if (!done) free(new_template); if (c->prompt_inputcb != cmd_command_prompt_callback) return (1); return (0); } static void cmd_command_prompt_free(void *data) { struct cmd_command_prompt_cdata *cdata = data; free(cdata->inputs); free(cdata->prompts); free(cdata->template); free(cdata); } tmux-3.0a/cmd-confirm-before.c100644 001750 001750 00000006253 13506620447 0012033/* $OpenBSD$ */ /* * 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" /* * Asks for confirmation before executing a command. */ static enum cmd_retval cmd_confirm_before_exec(struct cmd *, struct cmdq_item *); static int cmd_confirm_before_callback(struct client *, void *, const char *, int); static void cmd_confirm_before_free(void *); const struct cmd_entry cmd_confirm_before_entry = { .name = "confirm-before", .alias = "confirm", .args = { "p:t:", 1, 1 }, .usage = "[-p prompt] " CMD_TARGET_CLIENT_USAGE " command", .flags = 0, .exec = cmd_confirm_before_exec }; struct cmd_confirm_before_data { char *cmd; }; static enum cmd_retval cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct cmd_confirm_before_data *cdata; struct client *c; char *cmd, *copy, *new_prompt, *ptr; const char *prompt; if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); 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); free(copy); } cdata = xmalloc(sizeof *cdata); cdata->cmd = xstrdup(args->argv[0]); status_prompt_set(c, new_prompt, NULL, cmd_confirm_before_callback, cmd_confirm_before_free, cdata, PROMPT_SINGLE); free(new_prompt); return (CMD_RETURN_NORMAL); } static int cmd_confirm_before_callback(struct client *c, void *data, const char *s, __unused int done) { struct cmd_confirm_before_data *cdata = data; struct cmdq_item *new_item; struct cmd_parse_result *pr; if (c->flags & CLIENT_DEAD) return (0); if (s == NULL || *s == '\0') return (0); if (tolower((u_char)s[0]) != 'y' || s[1] != '\0') return (0); pr = cmd_parse_from_string(cdata->cmd, NULL); switch (pr->status) { case CMD_PARSE_EMPTY: new_item = NULL; break; case CMD_PARSE_ERROR: new_item = cmdq_get_error(pr->error); free(pr->error); cmdq_append(c, new_item); break; case CMD_PARSE_SUCCESS: new_item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0); cmd_list_free(pr->cmdlist); cmdq_append(c, new_item); break; } return (0); } static void cmd_confirm_before_free(void *data) { struct cmd_confirm_before_data *cdata = data; free(cdata->cmd); free(cdata); } tmux-3.0a/cmd-copy-mode.c100644 001750 001750 00000004412 13504653145 0011024/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "tmux.h" /* * Enter copy or clock mode. */ static enum cmd_retval cmd_copy_mode_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_copy_mode_entry = { .name = "copy-mode", .alias = NULL, .args = { "Met:u", 0, 0 }, .usage = "[-Mu] " CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, .flags = CMD_AFTERHOOK, .exec = cmd_copy_mode_exec }; const struct cmd_entry cmd_clock_mode_entry = { .name = "clock-mode", .alias = NULL, .args = { "t:", 0, 0 }, .usage = CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, .flags = CMD_AFTERHOOK, .exec = cmd_copy_mode_exec }; static enum cmd_retval cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct cmdq_shared *shared = item->shared; struct client *c = item->client; struct session *s; struct window_pane *wp = item->target.wp; if (args_has(args, 'M')) { if ((wp = cmd_mouse_pane(&shared->mouse, &s, NULL)) == NULL) return (CMD_RETURN_NORMAL); if (c == NULL || c->session != s) return (CMD_RETURN_NORMAL); } if (self->entry == &cmd_clock_mode_entry) { window_pane_set_mode(wp, &window_clock_mode, NULL, NULL); return (CMD_RETURN_NORMAL); } if (!window_pane_set_mode(wp, &window_copy_mode, NULL, args)) { if (args_has(args, 'M')) window_copy_start_drag(c, &shared->mouse); } if (args_has(self->args, 'u')) window_copy_pageup(wp, 0); return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-detach-client.c100644 001750 001750 00000005363 13462323664 0011645/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Detach a client. */ static enum cmd_retval cmd_detach_client_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_detach_client_entry = { .name = "detach-client", .alias = "detach", .args = { "aE:s:t:P", 0, 0 }, .usage = "[-aP] [-E shell-command] " "[-s target-session] " CMD_TARGET_CLIENT_USAGE, .source = { 's', CMD_FIND_SESSION, CMD_FIND_CANFAIL }, .flags = CMD_READONLY, .exec = cmd_detach_client_exec }; const struct cmd_entry cmd_suspend_client_entry = { .name = "suspend-client", .alias = "suspendc", .args = { "t:", 0, 0 }, .usage = CMD_TARGET_CLIENT_USAGE, .flags = 0, .exec = cmd_detach_client_exec }; static enum cmd_retval cmd_detach_client_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct client *c, *cloop; struct session *s; enum msgtype msgtype; const char *cmd = args_get(args, 'E'); if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); if (self->entry == &cmd_suspend_client_entry) { server_client_suspend(c); return (CMD_RETURN_NORMAL); } if (args_has(args, 'P')) msgtype = MSG_DETACHKILL; else msgtype = MSG_DETACH; if (args_has(args, 's')) { s = item->source.s; if (s == NULL) return (CMD_RETURN_NORMAL); TAILQ_FOREACH(cloop, &clients, entry) { if (cloop->session == s) { if (cmd != NULL) server_client_exec(cloop, cmd); else server_client_detach(cloop, msgtype); } } return (CMD_RETURN_STOP); } if (args_has(args, 'a')) { TAILQ_FOREACH(cloop, &clients, entry) { if (cloop->session != NULL && cloop != c) { if (cmd != NULL) server_client_exec(cloop, cmd); else server_client_detach(cloop, msgtype); } } return (CMD_RETURN_NORMAL); } if (cmd != NULL) server_client_exec(c, cmd); else server_client_detach(c, msgtype); return (CMD_RETURN_STOP); } tmux-3.0a/cmd-display-menu.c100644 001750 001750 00000011043 13504653145 0011535/* $OpenBSD$ */ /* * Copyright (c) 2019 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" /* * Display a menu on a client. */ static enum cmd_retval cmd_display_menu_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_display_menu_entry = { .name = "display-menu", .alias = "menu", .args = { "c:t:T:x:y:", 1, -1 }, .usage = "[-c target-client] " CMD_TARGET_PANE_USAGE " [-T title] " "[-x position] [-y position] name key command ...", .target = { 't', CMD_FIND_PANE, 0 }, .flags = CMD_AFTERHOOK, .exec = cmd_display_menu_exec }; static enum cmd_retval cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct client *c; struct session *s = item->target.s; struct winlink *wl = item->target.wl; struct window_pane *wp = item->target.wp; struct cmd_find_state *fs = &item->target; struct menu *menu = NULL; struct style_range *sr; struct menu_item menu_item; const char *xp, *yp, *key; char *title, *name; int at, flags, i; u_int px, py, ox, oy, sx, sy; if ((c = cmd_find_client(item, args_get(args, 'c'), 0)) == NULL) return (CMD_RETURN_ERROR); if (c->overlay_draw != NULL) return (CMD_RETURN_NORMAL); at = status_at_line(c); if (args_has(args, 'T')) title = format_single(NULL, args_get(args, 'T'), c, s, wl, wp); else title = xstrdup(""); menu = menu_create(title); for (i = 0; i != args->argc; /* nothing */) { name = args->argv[i++]; if (*name == '\0') { menu_add_item(menu, NULL, item, c, fs); continue; } if (args->argc - i < 2) { cmdq_error(item, "not enough arguments"); free(title); menu_free(menu); return (CMD_RETURN_ERROR); } key = args->argv[i++]; menu_item.name = name; menu_item.key = key_string_lookup_string(key); menu_item.command = args->argv[i++]; menu_add_item(menu, &menu_item, item, c, fs); } free(title); if (menu == NULL) { cmdq_error(item, "invalid menu arguments"); return (CMD_RETURN_ERROR); } if (menu->count == 0) { menu_free(menu); return (CMD_RETURN_NORMAL); } xp = args_get(args, 'x'); if (xp == NULL) px = 0; else if (strcmp(xp, "R") == 0) px = c->tty.sx - 1; else if (strcmp(xp, "P") == 0) { tty_window_offset(&c->tty, &ox, &oy, &sx, &sy); if (wp->xoff >= ox) px = wp->xoff - ox; else px = 0; } else if (strcmp(xp, "M") == 0 && item->shared->mouse.valid) { if (item->shared->mouse.x > (menu->width + 4) / 2) px = item->shared->mouse.x - (menu->width + 4) / 2; else px = 0; } else if (strcmp(xp, "W") == 0) { if (at == -1) px = 0; else { TAILQ_FOREACH(sr, &c->status.entries[0].ranges, entry) { if (sr->type != STYLE_RANGE_WINDOW) continue; if (sr->argument == (u_int)wl->idx) break; } if (sr != NULL) px = sr->start; else px = 0; } } else px = strtoul(xp, NULL, 10); if (px + menu->width + 4 >= c->tty.sx) px = c->tty.sx - menu->width - 4; yp = args_get(args, 'y'); if (yp == NULL) py = 0; else if (strcmp(yp, "P") == 0) { tty_window_offset(&c->tty, &ox, &oy, &sx, &sy); if (wp->yoff + wp->sy >= oy) py = wp->yoff + wp->sy - oy; else py = 0; } else if (strcmp(yp, "M") == 0 && item->shared->mouse.valid) py = item->shared->mouse.y + menu->count + 2; else if (strcmp(yp, "S") == 0) { if (at == -1) py = c->tty.sy; else if (at == 0) py = status_line_size(c) + menu->count + 2; else py = at; } else py = strtoul(yp, NULL, 10); if (py < menu->count + 2) py = 0; else py -= menu->count + 2; if (py + menu->count + 2 >= c->tty.sy) py = c->tty.sy - menu->count - 2; flags = 0; if (!item->shared->mouse.valid) flags |= MENU_NOMOUSE; if (menu_display(menu, flags, item, px, py, c, fs, NULL, NULL) != 0) return (CMD_RETURN_NORMAL); return (CMD_RETURN_WAIT); } tmux-3.0a/cmd-display-message.c100644 001750 001750 00000006571 13517540563 0012232/* $OpenBSD$ */ /* * 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 "tmux.h" /* * Displays a message in the status line. */ #define DISPLAY_MESSAGE_TEMPLATE \ "[#{session_name}] #{window_index}:" \ "#{window_name}, current pane #{pane_index} " \ "- (%H:%M %d-%b-%y)" static enum cmd_retval cmd_display_message_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_display_message_entry = { .name = "display-message", .alias = "display", .args = { "ac:Ipt:F:v", 0, 1 }, .usage = "[-aIpv] [-c target-client] [-F format] " CMD_TARGET_PANE_USAGE " [message]", .target = { 't', CMD_FIND_PANE, 0 }, .flags = CMD_AFTERHOOK, .exec = cmd_display_message_exec }; static void cmd_display_message_each(const char *key, const char *value, void *arg) { struct cmdq_item *item = arg; cmdq_print(item, "%s=%s", key, value); } static enum cmd_retval cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct client *c, *target_c; struct session *s = item->target.s; struct winlink *wl = item->target.wl; struct window_pane *wp = item->target.wp; const char *template; char *msg, *cause; struct format_tree *ft; int flags; if (args_has(args, 'I')) { if (window_pane_start_input(wp, item, &cause) != 0) { cmdq_error(item, "%s", cause); free(cause); return (CMD_RETURN_ERROR); } return (CMD_RETURN_WAIT); } if (args_has(args, 'F') && args->argc != 0) { cmdq_error(item, "only one of -F or argument must be given"); return (CMD_RETURN_ERROR); } template = args_get(args, 'F'); if (args->argc != 0) template = args->argv[0]; if (template == NULL) template = DISPLAY_MESSAGE_TEMPLATE; /* * -c is intended to be the client where the message should be * displayed if -p is not given. But it makes sense to use it for the * formats too, assuming it matches the session. If it doesn't, use the * best client for the session. */ c = cmd_find_client(item, args_get(args, 'c'), 1); if (c != NULL && c->session == s) target_c = c; else target_c = cmd_find_best_client(s); if (args_has(self->args, 'v')) flags = FORMAT_VERBOSE; else flags = 0; ft = format_create(item->client, item, FORMAT_NONE, flags); format_defaults(ft, target_c, s, wl, wp); if (args_has(args, 'a')) { format_each(ft, cmd_display_message_each, item); return (CMD_RETURN_NORMAL); } msg = format_expand_time(ft, template); if (args_has(self->args, 'p')) cmdq_print(item, "%s", msg); else if (c != NULL) status_message_set(c, "%s", msg); free(msg); format_free(ft); return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-display-panes.c100644 001750 001750 00000015406 13517540563 0011711/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Display panes on a client. */ static enum cmd_retval cmd_display_panes_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_display_panes_entry = { .name = "display-panes", .alias = "displayp", .args = { "bd:t:", 0, 1 }, .usage = "[-b] [-d duration] " CMD_TARGET_CLIENT_USAGE " [template]", .flags = CMD_AFTERHOOK, .exec = cmd_display_panes_exec }; struct cmd_display_panes_data { struct cmdq_item *item; char *command; }; static void cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp) { struct client *c = ctx->c; 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, sx, sy; int colour, active_colour; char buf[16], *ptr; size_t len; if (wp->xoff + wp->sx <= ctx->ox || wp->xoff >= ctx->ox + ctx->sx || wp->yoff + wp->sy <= ctx->oy || wp->yoff >= ctx->oy + ctx->sy) return; if (wp->xoff >= ctx->ox && wp->xoff + wp->sx <= ctx->ox + ctx->sx) { /* All visible. */ xoff = wp->xoff - ctx->ox; sx = wp->sx; } else if (wp->xoff < ctx->ox && wp->xoff + wp->sx > ctx->ox + ctx->sx) { /* Both left and right not visible. */ xoff = 0; sx = ctx->sx; } else if (wp->xoff < ctx->ox) { /* Left not visible. */ xoff = 0; sx = wp->sx - (ctx->ox - wp->xoff); } else { /* Right not visible. */ xoff = wp->xoff - ctx->ox; sx = wp->sx - xoff; } if (wp->yoff >= ctx->oy && wp->yoff + wp->sy <= ctx->oy + ctx->sy) { /* All visible. */ yoff = wp->yoff - ctx->oy; sy = wp->sy; } else if (wp->yoff < ctx->oy && wp->yoff + wp->sy > ctx->oy + ctx->sy) { /* Both top and bottom not visible. */ yoff = 0; sy = ctx->sy; } else if (wp->yoff < ctx->oy) { /* Top not visible. */ yoff = 0; sy = wp->sy - (ctx->oy - wp->yoff); } else { /* Bottom not visible. */ yoff = wp->yoff - ctx->oy; sy = wp->sy - yoff; } if (ctx->statustop) yoff += ctx->statuslines; px = sx / 2; py = sy / 2; if (window_pane_index(wp, &idx) != 0) fatalx("index not found"); len = xsnprintf(buf, sizeof buf, "%u", idx); if (sx < len) return; colour = options_get_number(oo, "display-panes-colour"); active_colour = options_get_number(oo, "display-panes-active-colour"); if (sx < len * 6 || sy < 5) { tty_cursor(tty, xoff + px - len / 2, yoff + py); goto draw_text; } px -= len * 3; py -= 2; memcpy(&gc, &grid_default_cell, sizeof gc); if (w->active == wp) gc.bg = active_colour; else gc.bg = colour; gc.flags |= GRID_FLAG_NOPALETTE; tty_attributes(tty, &gc, wp); 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 (window_clock_table[idx][j][i - px]) tty_putc(tty, ' '); } } px += 6; } len = xsnprintf(buf, sizeof buf, "%ux%u", wp->sx, wp->sy); if (sx < len || sy < 6) return; tty_cursor(tty, xoff + sx - len, yoff); draw_text: memcpy(&gc, &grid_default_cell, sizeof gc); if (w->active == wp) gc.fg = active_colour; else gc.fg = colour; gc.flags |= GRID_FLAG_NOPALETTE; tty_attributes(tty, &gc, wp); tty_puts(tty, buf); tty_cursor(tty, 0, 0); } static void cmd_display_panes_draw(struct client *c, struct screen_redraw_ctx *ctx) { struct window *w = c->session->curw->window; struct window_pane *wp; log_debug("%s: %s @%u", __func__, c->name, w->id); TAILQ_FOREACH(wp, &w->panes, entry) { if (window_pane_visible(wp)) cmd_display_panes_draw_pane(ctx, wp); } } static void cmd_display_panes_free(struct client *c) { struct cmd_display_panes_data *cdata = c->overlay_data; if (cdata->item != NULL) cmdq_continue(cdata->item); free(cdata->command); free(cdata); } static int cmd_display_panes_key(struct client *c, struct key_event *event) { struct cmd_display_panes_data *cdata = c->overlay_data; struct cmdq_item *new_item; char *cmd, *expanded; struct window *w = c->session->curw->window; struct window_pane *wp; struct cmd_parse_result *pr; if (event->key < '0' || event->key > '9') return (-1); wp = window_pane_at_index(w, event->key - '0'); if (wp == NULL) return (1); window_unzoom(w); xasprintf(&expanded, "%%%u", wp->id); cmd = cmd_template_replace(cdata->command, expanded, 1); pr = cmd_parse_from_string(cmd, NULL); switch (pr->status) { case CMD_PARSE_EMPTY: new_item = NULL; break; case CMD_PARSE_ERROR: new_item = cmdq_get_error(pr->error); free(pr->error); cmdq_append(c, new_item); break; case CMD_PARSE_SUCCESS: new_item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0); cmd_list_free(pr->cmdlist); cmdq_append(c, new_item); break; } free(cmd); free(expanded); return (1); } static enum cmd_retval cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct client *c; struct session *s; u_int delay; char *cause; struct cmd_display_panes_data *cdata; if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); s = c->session; if (c->overlay_draw != NULL) return (CMD_RETURN_NORMAL); if (args_has(args, 'd')) { delay = args_strtonum(args, 'd', 0, UINT_MAX, &cause); if (cause != NULL) { cmdq_error(item, "delay %s", cause); free(cause); return (CMD_RETURN_ERROR); } } else delay = options_get_number(s->options, "display-panes-time"); cdata = xmalloc(sizeof *cdata); if (args->argc != 0) cdata->command = xstrdup(args->argv[0]); else cdata->command = xstrdup("select-pane -t '%%'"); if (args_has(args, 'b')) cdata->item = NULL; else cdata->item = item; server_client_set_overlay(c, delay, cmd_display_panes_draw, cmd_display_panes_key, cmd_display_panes_free, cdata); if (args_has(args, 'b')) return (CMD_RETURN_NORMAL); return (CMD_RETURN_WAIT); } tmux-3.0a/cmd-find-window.c100644 001750 001750 00000006447 13517540563 0011372/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Find window containing text. */ static enum cmd_retval cmd_find_window_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_find_window_entry = { .name = "find-window", .alias = "findw", .args = { "CNrt:TZ", 1, 1 }, .usage = "[-CNrTZ] " CMD_TARGET_PANE_USAGE " match-string", .target = { 't', CMD_FIND_PANE, 0 }, .flags = 0, .exec = cmd_find_window_exec }; static enum cmd_retval cmd_find_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args, *new_args; struct window_pane *wp = item->target.wp; const char *s = args->argv[0]; char *filter, *argv = { NULL }; int C, N, T; C = args_has(args, 'C'); N = args_has(args, 'N'); T = args_has(args, 'T'); if (!C && !N && !T) C = N = T = 1; if (!args_has(args, 'r')) { if (C && N && T) { xasprintf(&filter, "#{||:" "#{C:%s},#{||:#{m:*%s*,#{window_name}}," "#{m:*%s*,#{pane_title}}}}", s, s, s); } else if (C && N) { xasprintf(&filter, "#{||:#{C:%s},#{m:*%s*,#{window_name}}}", s, s); } else if (C && T) { xasprintf(&filter, "#{||:#{C:%s},#{m:*%s*,#{pane_title}}}", s, s); } else if (N && T) { xasprintf(&filter, "#{||:#{m:*%s*,#{window_name}}," "#{m:*%s*,#{pane_title}}}", s, s); } else if (C) xasprintf(&filter, "#{C:%s}", s); else if (N) xasprintf(&filter, "#{m:*%s*,#{window_name}}", s); else xasprintf(&filter, "#{m:*%s*,#{pane_title}}", s); } else { if (C && N && T) { xasprintf(&filter, "#{||:" "#{C/r:%s},#{||:#{m/r:%s,#{window_name}}," "#{m/r:%s,#{pane_title}}}}", s, s, s); } else if (C && N) { xasprintf(&filter, "#{||:#{C/r:%s},#{m/r:%s,#{window_name}}}", s, s); } else if (C && T) { xasprintf(&filter, "#{||:#{C/r:%s},#{m/r:%s,#{pane_title}}}", s, s); } else if (N && T) { xasprintf(&filter, "#{||:#{m/r:%s,#{window_name}}," "#{m/r:%s,#{pane_title}}}", s, s); } else if (C) xasprintf(&filter, "#{C/r:%s}", s); else if (N) xasprintf(&filter, "#{m/r:%s,#{window_name}}", s); else xasprintf(&filter, "#{m/r:%s,#{pane_title}}", s); } new_args = args_parse("", 1, &argv); if (args_has(args, 'Z')) args_set(new_args, 'Z', NULL); args_set(new_args, 'f', filter); window_pane_set_mode(wp, &window_tree_mode, &item->target, new_args); args_free(new_args); free(filter); return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-find.c100644 001750 001750 00000075137 13517540563 0010067/* $OpenBSD$ */ /* * Copyright (c) 2015 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" static int cmd_find_session_better(struct session *, struct session *, int); static struct session *cmd_find_best_session(struct session **, u_int, int); static int cmd_find_best_session_with_window(struct cmd_find_state *); static int cmd_find_best_winlink_with_window(struct cmd_find_state *); static const char *cmd_find_map_table(const char *[][2], const char *); static void cmd_find_log_state(const char *, struct cmd_find_state *); static int cmd_find_get_session(struct cmd_find_state *, const char *); static int cmd_find_get_window(struct cmd_find_state *, const char *, int); static int cmd_find_get_window_with_session(struct cmd_find_state *, const char *); static int cmd_find_get_pane(struct cmd_find_state *, const char *, int); static int cmd_find_get_pane_with_session(struct cmd_find_state *, const char *); static int cmd_find_get_pane_with_window(struct cmd_find_state *, const char *); static const char *cmd_find_session_table[][2] = { { NULL, NULL } }; static const char *cmd_find_window_table[][2] = { { "{start}", "^" }, { "{last}", "!" }, { "{end}", "$" }, { "{next}", "+" }, { "{previous}", "-" }, { NULL, NULL } }; static const char *cmd_find_pane_table[][2] = { { "{last}", "!" }, { "{next}", "+" }, { "{previous}", "-" }, { "{top}", "top" }, { "{bottom}", "bottom" }, { "{left}", "left" }, { "{right}", "right" }, { "{top-left}", "top-left" }, { "{top-right}", "top-right" }, { "{bottom-left}", "bottom-left" }, { "{bottom-right}", "bottom-right" }, { "{up-of}", "{up-of}" }, { "{down-of}", "{down-of}" }, { "{left-of}", "{left-of}" }, { "{right-of}", "{right-of}" }, { NULL, NULL } }; /* Find pane containing client if any. */ static struct window_pane * cmd_find_inside_pane(struct client *c) { struct window_pane *wp; struct environ_entry *envent; if (c == NULL) return (NULL); RB_FOREACH(wp, window_pane_tree, &all_window_panes) { if (wp->fd != -1 && strcmp(wp->tty, c->ttyname) == 0) break; } if (wp == NULL) { envent = environ_find(c->environ, "TMUX_PANE"); if (envent != NULL) wp = window_pane_find_by_id_str(envent->value); } if (wp != NULL) log_debug("%s: got pane %%%u (%s)", __func__, wp->id, wp->tty); return (wp); } /* Is this client better? */ static int cmd_find_client_better(struct client *c, struct client *than) { if (than == NULL) return (1); return (timercmp(&c->activity_time, &than->activity_time, >)); } /* Find best client for session. */ struct client * cmd_find_best_client(struct session *s) { struct client *c_loop, *c; if (s->attached == 0) s = NULL; c = NULL; TAILQ_FOREACH(c_loop, &clients, entry) { if (c_loop->session == NULL) continue; if (s != NULL && c_loop->session != s) continue; if (cmd_find_client_better(c_loop, c)) c = c_loop; } return (c); } /* Is this session better? */ static int cmd_find_session_better(struct session *s, struct session *than, int flags) { int attached; if (than == NULL) return (1); if (flags & CMD_FIND_PREFER_UNATTACHED) { attached = (than->attached != 0); if (attached && s->attached == 0) return (1); else if (!attached && s->attached != 0) return (0); } return (timercmp(&s->activity_time, &than->activity_time, >)); } /* Find best session from a list, or all if list is NULL. */ static struct session * cmd_find_best_session(struct session **slist, u_int ssize, int flags) { struct session *s_loop, *s; u_int i; log_debug("%s: %u sessions to try", __func__, ssize); s = NULL; if (slist != NULL) { for (i = 0; i < ssize; i++) { if (cmd_find_session_better(slist[i], s, flags)) s = slist[i]; } } else { RB_FOREACH(s_loop, sessions, &sessions) { if (cmd_find_session_better(s_loop, s, flags)) s = s_loop; } } return (s); } /* Find best session and winlink for window. */ static int cmd_find_best_session_with_window(struct cmd_find_state *fs) { struct session **slist = NULL; u_int ssize; struct session *s; log_debug("%s: window is @%u", __func__, fs->w->id); ssize = 0; RB_FOREACH(s, sessions, &sessions) { if (!session_has(s, fs->w)) continue; slist = xreallocarray(slist, ssize + 1, sizeof *slist); slist[ssize++] = s; } if (ssize == 0) goto fail; fs->s = cmd_find_best_session(slist, ssize, fs->flags); if (fs->s == NULL) goto fail; free(slist); return (cmd_find_best_winlink_with_window(fs)); fail: free(slist); return (-1); } /* * Find the best winlink for a window (the current if it contains the window, * otherwise the first). */ static int cmd_find_best_winlink_with_window(struct cmd_find_state *fs) { struct winlink *wl, *wl_loop; log_debug("%s: window is @%u", __func__, fs->w->id); wl = NULL; if (fs->s->curw != NULL && fs->s->curw->window == fs->w) wl = fs->s->curw; else { RB_FOREACH(wl_loop, winlinks, &fs->s->windows) { if (wl_loop->window == fs->w) { wl = wl_loop; break; } } } if (wl == NULL) return (-1); fs->wl = wl; fs->idx = fs->wl->idx; return (0); } /* Maps string in table. */ static const char * cmd_find_map_table(const char *table[][2], const char *s) { u_int i; for (i = 0; table[i][0] != NULL; i++) { if (strcmp(s, table[i][0]) == 0) return (table[i][1]); } return (s); } /* Find session from string. Fills in s. */ static int cmd_find_get_session(struct cmd_find_state *fs, const char *session) { struct session *s, *s_loop; struct client *c; log_debug("%s: %s", __func__, session); /* Check for session ids starting with $. */ if (*session == '$') { fs->s = session_find_by_id_str(session); if (fs->s == NULL) return (-1); return (0); } /* Look for exactly this session. */ fs->s = session_find(session); if (fs->s != NULL) return (0); /* Look for as a client. */ c = cmd_find_client(NULL, session, 1); if (c != NULL && c->session != NULL) { fs->s = c->session; return (0); } /* Stop now if exact only. */ if (fs->flags & CMD_FIND_EXACT_SESSION) return (-1); /* Otherwise look for prefix. */ s = NULL; RB_FOREACH(s_loop, sessions, &sessions) { if (strncmp(session, s_loop->name, strlen(session)) == 0) { if (s != NULL) return (-1); s = s_loop; } } if (s != NULL) { fs->s = s; return (0); } /* Then as a pattern. */ s = NULL; RB_FOREACH(s_loop, sessions, &sessions) { if (fnmatch(session, s_loop->name, 0) == 0) { if (s != NULL) return (-1); s = s_loop; } } if (s != NULL) { fs->s = s; return (0); } return (-1); } /* Find window from string. Fills in s, wl, w. */ static int cmd_find_get_window(struct cmd_find_state *fs, const char *window, int only) { log_debug("%s: %s", __func__, window); /* Check for window ids starting with @. */ if (*window == '@') { fs->w = window_find_by_id_str(window); if (fs->w == NULL) return (-1); return (cmd_find_best_session_with_window(fs)); } /* Not a window id, so use the current session. */ fs->s = fs->current->s; /* We now only need to find the winlink in this session. */ if (cmd_find_get_window_with_session(fs, window) == 0) return (0); /* Otherwise try as a session itself. */ if (!only && cmd_find_get_session(fs, window) == 0) { fs->wl = fs->s->curw; fs->w = fs->wl->window; if (~fs->flags & CMD_FIND_WINDOW_INDEX) fs->idx = fs->wl->idx; return (0); } return (-1); } /* * Find window from string, assuming it is in given session. Needs s, fills in * wl and w. */ static int cmd_find_get_window_with_session(struct cmd_find_state *fs, const char *window) { struct winlink *wl; const char *errstr; int idx, n, exact; struct session *s; log_debug("%s: %s", __func__, window); exact = (fs->flags & CMD_FIND_EXACT_WINDOW); /* * Start with the current window as the default. So if only an index is * found, the window will be the current. */ fs->wl = fs->s->curw; fs->w = fs->wl->window; /* Check for window ids starting with @. */ if (*window == '@') { fs->w = window_find_by_id_str(window); if (fs->w == NULL || !session_has(fs->s, fs->w)) return (-1); return (cmd_find_best_winlink_with_window(fs)); } /* Try as an offset. */ if (!exact && (window[0] == '+' || window[0] == '-')) { if (window[1] != '\0') n = strtonum(window + 1, 1, INT_MAX, NULL); else n = 1; s = fs->s; if (fs->flags & CMD_FIND_WINDOW_INDEX) { if (window[0] == '+') { if (INT_MAX - s->curw->idx < n) return (-1); fs->idx = s->curw->idx + n; } else { if (n > s->curw->idx) return (-1); fs->idx = s->curw->idx - n; } return (0); } if (window[0] == '+') fs->wl = winlink_next_by_number(s->curw, s, n); else fs->wl = winlink_previous_by_number(s->curw, s, n); if (fs->wl != NULL) { fs->idx = fs->wl->idx; fs->w = fs->wl->window; return (0); } } /* Try special characters. */ if (!exact) { if (strcmp(window, "!") == 0) { fs->wl = TAILQ_FIRST(&fs->s->lastw); if (fs->wl == NULL) return (-1); fs->idx = fs->wl->idx; fs->w = fs->wl->window; return (0); } else if (strcmp(window, "^") == 0) { fs->wl = RB_MIN(winlinks, &fs->s->windows); if (fs->wl == NULL) return (-1); fs->idx = fs->wl->idx; fs->w = fs->wl->window; return (0); } else if (strcmp(window, "$") == 0) { fs->wl = RB_MAX(winlinks, &fs->s->windows); if (fs->wl == NULL) return (-1); fs->idx = fs->wl->idx; fs->w = fs->wl->window; return (0); } } /* First see if this is a valid window index in this session. */ if (window[0] != '+' && window[0] != '-') { idx = strtonum(window, 0, INT_MAX, &errstr); if (errstr == NULL) { fs->wl = winlink_find_by_index(&fs->s->windows, idx); if (fs->wl != NULL) { fs->idx = fs->wl->idx; fs->w = fs->wl->window; return (0); } if (fs->flags & CMD_FIND_WINDOW_INDEX) { fs->idx = idx; return (0); } } } /* Look for exact matches, error if more than one. */ fs->wl = NULL; RB_FOREACH(wl, winlinks, &fs->s->windows) { if (strcmp(window, wl->window->name) == 0) { if (fs->wl != NULL) return (-1); fs->wl = wl; } } if (fs->wl != NULL) { fs->idx = fs->wl->idx; fs->w = fs->wl->window; return (0); } /* Stop now if exact only. */ if (exact) return (-1); /* Try as the start of a window name, error if multiple. */ fs->wl = NULL; RB_FOREACH(wl, winlinks, &fs->s->windows) { if (strncmp(window, wl->window->name, strlen(window)) == 0) { if (fs->wl != NULL) return (-1); fs->wl = wl; } } if (fs->wl != NULL) { fs->idx = fs->wl->idx; fs->w = fs->wl->window; return (0); } /* Now look for pattern matches, again error if multiple. */ fs->wl = NULL; RB_FOREACH(wl, winlinks, &fs->s->windows) { if (fnmatch(window, wl->window->name, 0) == 0) { if (fs->wl != NULL) return (-1); fs->wl = wl; } } if (fs->wl != NULL) { fs->idx = fs->wl->idx; fs->w = fs->wl->window; return (0); } return (-1); } /* Find pane from string. Fills in s, wl, w, wp. */ static int cmd_find_get_pane(struct cmd_find_state *fs, const char *pane, int only) { log_debug("%s: %s", __func__, pane); /* Check for pane ids starting with %. */ if (*pane == '%') { fs->wp = window_pane_find_by_id_str(pane); if (fs->wp == NULL) return (-1); fs->w = fs->wp->window; return (cmd_find_best_session_with_window(fs)); } /* Not a pane id, so try the current session and window. */ fs->s = fs->current->s; fs->wl = fs->current->wl; fs->idx = fs->current->idx; fs->w = fs->current->w; /* We now only need to find the pane in this window. */ if (cmd_find_get_pane_with_window(fs, pane) == 0) return (0); /* Otherwise try as a window itself (this will also try as session). */ if (!only && cmd_find_get_window(fs, pane, 0) == 0) { fs->wp = fs->w->active; return (0); } return (-1); } /* * Find pane from string, assuming it is in given session. Needs s, fills in wl * and w and wp. */ static int cmd_find_get_pane_with_session(struct cmd_find_state *fs, const char *pane) { log_debug("%s: %s", __func__, pane); /* Check for pane ids starting with %. */ if (*pane == '%') { fs->wp = window_pane_find_by_id_str(pane); if (fs->wp == NULL) return (-1); fs->w = fs->wp->window; return (cmd_find_best_winlink_with_window(fs)); } /* Otherwise use the current window. */ fs->wl = fs->s->curw; fs->idx = fs->wl->idx; fs->w = fs->wl->window; /* Now we just need to look up the pane. */ return (cmd_find_get_pane_with_window(fs, pane)); } /* * Find pane from string, assuming it is in the given window. Needs w, fills in * wp. */ static int cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane) { const char *errstr; int idx; struct window_pane *wp; u_int n; log_debug("%s: %s", __func__, pane); /* Check for pane ids starting with %. */ if (*pane == '%') { fs->wp = window_pane_find_by_id_str(pane); if (fs->wp == NULL) return (-1); if (fs->wp->window != fs->w) return (-1); return (0); } /* Try special characters. */ if (strcmp(pane, "!") == 0) { fs->wp = fs->w->last; if (fs->wp == NULL) return (-1); return (0); } else if (strcmp(pane, "{up-of}") == 0) { fs->wp = window_pane_find_up(fs->w->active); if (fs->wp == NULL) return (-1); return (0); } else if (strcmp(pane, "{down-of}") == 0) { fs->wp = window_pane_find_down(fs->w->active); if (fs->wp == NULL) return (-1); return (0); } else if (strcmp(pane, "{left-of}") == 0) { fs->wp = window_pane_find_left(fs->w->active); if (fs->wp == NULL) return (-1); return (0); } else if (strcmp(pane, "{right-of}") == 0) { fs->wp = window_pane_find_right(fs->w->active); if (fs->wp == NULL) return (-1); return (0); } /* Try as an offset. */ if (pane[0] == '+' || pane[0] == '-') { if (pane[1] != '\0') n = strtonum(pane + 1, 1, INT_MAX, NULL); else n = 1; wp = fs->w->active; if (pane[0] == '+') fs->wp = window_pane_next_by_number(fs->w, wp, n); else fs->wp = window_pane_previous_by_number(fs->w, wp, n); if (fs->wp != NULL) return (0); } /* Get pane by index. */ idx = strtonum(pane, 0, INT_MAX, &errstr); if (errstr == NULL) { fs->wp = window_pane_at_index(fs->w, idx); if (fs->wp != NULL) return (0); } /* Try as a description. */ fs->wp = window_find_string(fs->w, pane); if (fs->wp != NULL) return (0); return (-1); } /* Clear state. */ void cmd_find_clear_state(struct cmd_find_state *fs, int flags) { memset(fs, 0, sizeof *fs); fs->flags = flags; fs->idx = -1; } /* Check if state is empty. */ int cmd_find_empty_state(struct cmd_find_state *fs) { if (fs->s == NULL && fs->wl == NULL && fs->w == NULL && fs->wp == NULL) return (1); return (0); } /* Check if a state if valid. */ int cmd_find_valid_state(struct cmd_find_state *fs) { struct winlink *wl; if (fs->s == NULL || fs->wl == NULL || fs->w == NULL || fs->wp == NULL) return (0); if (!session_alive(fs->s)) return (0); RB_FOREACH(wl, winlinks, &fs->s->windows) { if (wl->window == fs->w && wl == fs->wl) break; } if (wl == NULL) return (0); if (fs->w != fs->wl->window) return (0); return (window_has_pane(fs->w, fs->wp)); } /* Copy a state. */ void cmd_find_copy_state(struct cmd_find_state *dst, struct cmd_find_state *src) { dst->s = src->s; dst->wl = src->wl; dst->idx = src->idx; dst->w = src->w; dst->wp = src->wp; } /* Log the result. */ static void cmd_find_log_state(const char *prefix, struct cmd_find_state *fs) { if (fs->s != NULL) log_debug("%s: s=$%u %s", prefix, fs->s->id, fs->s->name); else log_debug("%s: s=none", prefix); if (fs->wl != NULL) { log_debug("%s: wl=%u %d w=@%u %s", prefix, fs->wl->idx, fs->wl->window == fs->w, fs->w->id, fs->w->name); } else log_debug("%s: wl=none", prefix); if (fs->wp != NULL) log_debug("%s: wp=%%%u", prefix, fs->wp->id); else log_debug("%s: wp=none", prefix); if (fs->idx != -1) log_debug("%s: idx=%d", prefix, fs->idx); else log_debug("%s: idx=none", prefix); } /* Find state from a session. */ void cmd_find_from_session(struct cmd_find_state *fs, struct session *s, int flags) { cmd_find_clear_state(fs, flags); fs->s = s; fs->wl = fs->s->curw; fs->w = fs->wl->window; fs->wp = fs->w->active; cmd_find_log_state(__func__, fs); } /* Find state from a winlink. */ void cmd_find_from_winlink(struct cmd_find_state *fs, struct winlink *wl, int flags) { cmd_find_clear_state(fs, flags); fs->s = wl->session; fs->wl = wl; fs->w = wl->window; fs->wp = wl->window->active; cmd_find_log_state(__func__, fs); } /* Find state from a session and window. */ int cmd_find_from_session_window(struct cmd_find_state *fs, struct session *s, struct window *w, int flags) { cmd_find_clear_state(fs, flags); fs->s = s; fs->w = w; if (cmd_find_best_winlink_with_window(fs) != 0) { cmd_find_clear_state(fs, flags); return (-1); } fs->wp = fs->w->active; cmd_find_log_state(__func__, fs); return (0); } /* Find state from a window. */ int cmd_find_from_window(struct cmd_find_state *fs, struct window *w, int flags) { cmd_find_clear_state(fs, flags); fs->w = w; if (cmd_find_best_session_with_window(fs) != 0) { cmd_find_clear_state(fs, flags); return (-1); } if (cmd_find_best_winlink_with_window(fs) != 0) { cmd_find_clear_state(fs, flags); return (-1); } fs->wp = fs->w->active; cmd_find_log_state(__func__, fs); return (0); } /* Find state from a winlink and pane. */ void cmd_find_from_winlink_pane(struct cmd_find_state *fs, struct winlink *wl, struct window_pane *wp, int flags) { cmd_find_clear_state(fs, flags); fs->s = wl->session; fs->wl = wl; fs->idx = fs->wl->idx; fs->w = fs->wl->window; fs->wp = wp; cmd_find_log_state(__func__, fs); } /* Find state from a pane. */ int cmd_find_from_pane(struct cmd_find_state *fs, struct window_pane *wp, int flags) { if (cmd_find_from_window(fs, wp->window, flags) != 0) return (-1); fs->wp = wp; cmd_find_log_state(__func__, fs); return (0); } /* Find state from nothing. */ int cmd_find_from_nothing(struct cmd_find_state *fs, int flags) { cmd_find_clear_state(fs, flags); fs->s = cmd_find_best_session(NULL, 0, flags); if (fs->s == NULL) { cmd_find_clear_state(fs, flags); return (-1); } fs->wl = fs->s->curw; fs->idx = fs->wl->idx; fs->w = fs->wl->window; fs->wp = fs->w->active; cmd_find_log_state(__func__, fs); return (0); } /* Find state from mouse. */ int cmd_find_from_mouse(struct cmd_find_state *fs, struct mouse_event *m, int flags) { cmd_find_clear_state(fs, flags); if (!m->valid) return (-1); fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl); if (fs->wp == NULL) { cmd_find_clear_state(fs, flags); return (-1); } fs->w = fs->wl->window; cmd_find_log_state(__func__, fs); return (0); } /* Find state from client. */ int cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags) { struct window_pane *wp; /* If no client, treat as from nothing. */ if (c == NULL) return (cmd_find_from_nothing(fs, flags)); /* If this is an attached client, all done. */ if (c->session != NULL) { cmd_find_from_session(fs, c->session, flags); return (0); } cmd_find_clear_state(fs, flags); /* * If this is an unattached client running in a pane, we can use that * to limit the list of sessions to those containing that pane. */ wp = cmd_find_inside_pane(c); if (wp == NULL) goto unknown_pane; /* * Don't have a session, or it doesn't have this pane. Try all * sessions. */ fs->w = wp->window; if (cmd_find_best_session_with_window(fs) != 0) { /* * The window may have been destroyed but the pane * still on all_window_panes due to something else * holding a reference. */ goto unknown_pane; } fs->wl = fs->s->curw; fs->w = fs->wl->window; fs->wp = fs->w->active; /* use active pane */ cmd_find_log_state(__func__, fs); return (0); unknown_pane: /* We can't find the pane so need to guess. */ return (cmd_find_from_nothing(fs, flags)); } /* * Split target into pieces and resolve for the given type. Fills in the given * state. Returns 0 on success or -1 on error. */ int cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item, const char *target, enum cmd_find_type type, int flags) { struct mouse_event *m; struct cmd_find_state current; char *colon, *period, *copy = NULL, tmp[256]; const char *session, *window, *pane, *s; int window_only = 0, pane_only = 0; /* Can fail flag implies quiet. */ if (flags & CMD_FIND_CANFAIL) flags |= CMD_FIND_QUIET; /* Log the arguments. */ if (type == CMD_FIND_PANE) s = "pane"; else if (type == CMD_FIND_WINDOW) s = "window"; else if (type == CMD_FIND_SESSION) s = "session"; else s = "unknown"; *tmp = '\0'; if (flags & CMD_FIND_PREFER_UNATTACHED) strlcat(tmp, "PREFER_UNATTACHED,", sizeof tmp); if (flags & CMD_FIND_QUIET) strlcat(tmp, "QUIET,", sizeof tmp); if (flags & CMD_FIND_WINDOW_INDEX) strlcat(tmp, "WINDOW_INDEX,", sizeof tmp); if (flags & CMD_FIND_DEFAULT_MARKED) strlcat(tmp, "DEFAULT_MARKED,", sizeof tmp); if (flags & CMD_FIND_EXACT_SESSION) strlcat(tmp, "EXACT_SESSION,", sizeof tmp); if (flags & CMD_FIND_EXACT_WINDOW) strlcat(tmp, "EXACT_WINDOW,", sizeof tmp); if (flags & CMD_FIND_CANFAIL) strlcat(tmp, "CANFAIL,", sizeof tmp); if (*tmp != '\0') tmp[strlen(tmp) - 1] = '\0'; else strlcat(tmp, "NONE", sizeof tmp); log_debug("%s: target %s, type %s, item %p, flags %s", __func__, target == NULL ? "none" : target, s, item, tmp); /* Clear new state. */ cmd_find_clear_state(fs, flags); /* Find current state. */ if (server_check_marked() && (flags & CMD_FIND_DEFAULT_MARKED)) { fs->current = &marked_pane; log_debug("%s: current is marked pane", __func__); } else if (cmd_find_valid_state(&item->shared->current)) { fs->current = &item->shared->current; log_debug("%s: current is from queue", __func__); } else if (cmd_find_from_client(¤t, item->client, flags) == 0) { fs->current = ¤t; log_debug("%s: current is from client", __func__); } else { if (~flags & CMD_FIND_QUIET) cmdq_error(item, "no current target"); goto error; } if (!cmd_find_valid_state(fs->current)) fatalx("invalid current find state"); /* An empty or NULL target is the current. */ if (target == NULL || *target == '\0') goto current; /* Mouse target is a plain = or {mouse}. */ if (strcmp(target, "=") == 0 || strcmp(target, "{mouse}") == 0) { m = &item->shared->mouse; switch (type) { case CMD_FIND_PANE: fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl); if (fs->wp != NULL) { fs->w = fs->wl->window; break; } /* FALLTHROUGH */ case CMD_FIND_WINDOW: case CMD_FIND_SESSION: fs->wl = cmd_mouse_window(m, &fs->s); if (fs->wl == NULL && fs->s != NULL) fs->wl = fs->s->curw; if (fs->wl != NULL) { fs->w = fs->wl->window; fs->wp = fs->w->active; } break; } if (fs->wp == NULL) { if (~flags & CMD_FIND_QUIET) cmdq_error(item, "no mouse target"); goto error; } goto found; } /* Marked target is a plain ~ or {marked}. */ if (strcmp(target, "~") == 0 || strcmp(target, "{marked}") == 0) { if (!server_check_marked()) { if (~flags & CMD_FIND_QUIET) cmdq_error(item, "no marked target"); goto error; } cmd_find_copy_state(fs, &marked_pane); goto found; } /* Find separators if they exist. */ copy = xstrdup(target); colon = strchr(copy, ':'); if (colon != NULL) *colon++ = '\0'; if (colon == NULL) period = strchr(copy, '.'); else period = strchr(colon, '.'); if (period != NULL) *period++ = '\0'; /* Set session, window and pane parts. */ session = window = pane = NULL; if (colon != NULL && period != NULL) { session = copy; window = colon; window_only = 1; pane = period; pane_only = 1; } else if (colon != NULL && period == NULL) { session = copy; window = colon; window_only = 1; } else if (colon == NULL && period != NULL) { window = copy; pane = period; pane_only = 1; } else { if (*copy == '$') session = copy; else if (*copy == '@') window = copy; else if (*copy == '%') pane = copy; else { switch (type) { case CMD_FIND_SESSION: session = copy; break; case CMD_FIND_WINDOW: window = copy; break; case CMD_FIND_PANE: pane = copy; break; } } } /* Set exact match flags. */ if (session != NULL && *session == '=') { session++; fs->flags |= CMD_FIND_EXACT_SESSION; } if (window != NULL && *window == '=') { window++; fs->flags |= CMD_FIND_EXACT_WINDOW; } /* Empty is the same as NULL. */ if (session != NULL && *session == '\0') session = NULL; if (window != NULL && *window == '\0') window = NULL; if (pane != NULL && *pane == '\0') pane = NULL; /* Map though conversion table. */ if (session != NULL) session = cmd_find_map_table(cmd_find_session_table, session); if (window != NULL) window = cmd_find_map_table(cmd_find_window_table, window); if (pane != NULL) pane = cmd_find_map_table(cmd_find_pane_table, pane); if (session != NULL || window != NULL || pane != NULL) { log_debug("%s: target %s is %s%s%s%s%s%s", __func__, target, session == NULL ? "" : "session ", session == NULL ? "" : session, window == NULL ? "" : "window ", window == NULL ? "" : window, pane == NULL ? "" : "pane ", pane == NULL ? "" : pane); } /* No pane is allowed if want an index. */ if (pane != NULL && (flags & CMD_FIND_WINDOW_INDEX)) { if (~flags & CMD_FIND_QUIET) cmdq_error(item, "can't specify pane here"); goto error; } /* If the session isn't NULL, look it up. */ if (session != NULL) { /* This will fill in session. */ if (cmd_find_get_session(fs, session) != 0) goto no_session; /* If window and pane are NULL, use that session's current. */ if (window == NULL && pane == NULL) { fs->wl = fs->s->curw; fs->idx = -1; fs->w = fs->wl->window; fs->wp = fs->w->active; goto found; } /* If window is present but pane not, find window in session. */ if (window != NULL && pane == NULL) { /* This will fill in winlink and window. */ if (cmd_find_get_window_with_session(fs, window) != 0) goto no_window; if (fs->wl != NULL) /* can be NULL if index only */ fs->wp = fs->wl->window->active; goto found; } /* If pane is present but window not, find pane. */ if (window == NULL && pane != NULL) { /* This will fill in winlink and window and pane. */ if (cmd_find_get_pane_with_session(fs, pane) != 0) goto no_pane; goto found; } /* * If window and pane are present, find both in session. This * will fill in winlink and window. */ if (cmd_find_get_window_with_session(fs, window) != 0) goto no_window; /* This will fill in pane. */ if (cmd_find_get_pane_with_window(fs, pane) != 0) goto no_pane; goto found; } /* No session. If window and pane, try them. */ if (window != NULL && pane != NULL) { /* This will fill in session, winlink and window. */ if (cmd_find_get_window(fs, window, window_only) != 0) goto no_window; /* This will fill in pane. */ if (cmd_find_get_pane_with_window(fs, pane) != 0) goto no_pane; goto found; } /* If just window is present, try it. */ if (window != NULL && pane == NULL) { /* This will fill in session, winlink and window. */ if (cmd_find_get_window(fs, window, window_only) != 0) goto no_window; if (fs->wl != NULL) /* can be NULL if index only */ fs->wp = fs->wl->window->active; goto found; } /* If just pane is present, try it. */ if (window == NULL && pane != NULL) { /* This will fill in session, winlink, window and pane. */ if (cmd_find_get_pane(fs, pane, pane_only) != 0) goto no_pane; goto found; } current: /* Use the current session. */ cmd_find_copy_state(fs, fs->current); if (flags & CMD_FIND_WINDOW_INDEX) fs->idx = -1; goto found; error: fs->current = NULL; log_debug("%s: error", __func__); free(copy); if (flags & CMD_FIND_CANFAIL) return (0); return (-1); found: fs->current = NULL; cmd_find_log_state(__func__, fs); free(copy); return (0); no_session: if (~flags & CMD_FIND_QUIET) cmdq_error(item, "can't find session: %s", session); goto error; no_window: if (~flags & CMD_FIND_QUIET) cmdq_error(item, "can't find window: %s", window); goto error; no_pane: if (~flags & CMD_FIND_QUIET) cmdq_error(item, "can't find pane: %s", pane); goto error; } /* Find the current client. */ static struct client * cmd_find_current_client(struct cmdq_item *item, int quiet) { struct client *c; struct session *s; struct window_pane *wp; struct cmd_find_state fs; if (item->client != NULL && item->client->session != NULL) return (item->client); c = NULL; if ((wp = cmd_find_inside_pane(item->client)) != NULL) { cmd_find_clear_state(&fs, CMD_FIND_QUIET); fs.w = wp->window; if (cmd_find_best_session_with_window(&fs) == 0) c = cmd_find_best_client(fs.s); } else { s = cmd_find_best_session(NULL, 0, CMD_FIND_QUIET); if (s != NULL) c = cmd_find_best_client(s); } if (c == NULL && !quiet) cmdq_error(item, "no current client"); log_debug("%s: no target, return %p", __func__, c); return (c); } /* Find the target client or report an error and return NULL. */ struct client * cmd_find_client(struct cmdq_item *item, const char *target, int quiet) { struct client *c; char *copy; size_t size; /* A NULL argument means the current client. */ if (target == NULL) return (cmd_find_current_client(item, quiet)); copy = xstrdup(target); /* Trim a single trailing colon if any. */ size = strlen(copy); if (size != 0 && copy[size - 1] == ':') copy[size - 1] = '\0'; /* Check name and path of each client. */ TAILQ_FOREACH(c, &clients, entry) { if (c->session == NULL) continue; if (strcmp(copy, c->name) == 0) break; if (*c->ttyname == '\0') continue; if (strcmp(copy, c->ttyname) == 0) break; if (strncmp(c->ttyname, _PATH_DEV, (sizeof _PATH_DEV) - 1) != 0) continue; if (strcmp(copy, c->ttyname + (sizeof _PATH_DEV) - 1) == 0) break; } /* If no client found, report an error. */ if (c == NULL && !quiet) cmdq_error(item, "can't find client: %s", copy); free(copy); log_debug("%s: target %s, return %p", __func__, target, c); return (c); } tmux-3.0a/cmd-if-shell.c100644 001750 001750 00000012656 13517540563 0010647/* $OpenBSD$ */ /* * 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 #include "tmux.h" /* * Executes a tmux command if a shell command returns true or false. */ static enum cmd_retval cmd_if_shell_exec(struct cmd *, struct cmdq_item *); static void cmd_if_shell_callback(struct job *); static void cmd_if_shell_free(void *); const struct cmd_entry cmd_if_shell_entry = { .name = "if-shell", .alias = "if", .args = { "bFt:", 2, 3 }, .usage = "[-bF] " CMD_TARGET_PANE_USAGE " shell-command command " "[command]", .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, .flags = 0, .exec = cmd_if_shell_exec }; struct cmd_if_shell_data { struct cmd_parse_input input; char *cmd_if; char *cmd_else; struct client *client; struct cmdq_item *item; struct mouse_event mouse; }; static enum cmd_retval cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct mouse_event *m = &item->shared->mouse; struct cmd_if_shell_data *cdata; char *shellcmd, *cmd; struct cmdq_item *new_item; struct cmd_find_state *fs = &item->target; struct client *c = cmd_find_client(item, NULL, 1); struct session *s = fs->s; struct winlink *wl = fs->wl; struct window_pane *wp = fs->wp; struct cmd_parse_input pi; struct cmd_parse_result *pr; shellcmd = format_single(item, args->argv[0], c, s, wl, wp); if (args_has(args, 'F')) { if (*shellcmd != '0' && *shellcmd != '\0') cmd = args->argv[1]; else if (args->argc == 3) cmd = args->argv[2]; else cmd = NULL; free(shellcmd); if (cmd == NULL) return (CMD_RETURN_NORMAL); memset(&pi, 0, sizeof pi); if (self->file != NULL) pi.file = self->file; pi.line = self->line; pi.item = item; pi.c = c; cmd_find_copy_state(&pi.fs, fs); pr = cmd_parse_from_string(cmd, &pi); switch (pr->status) { case CMD_PARSE_EMPTY: break; case CMD_PARSE_ERROR: cmdq_error(item, "%s", pr->error); free(pr->error); return (CMD_RETURN_ERROR); case CMD_PARSE_SUCCESS: new_item = cmdq_get_command(pr->cmdlist, fs, m, 0); cmdq_insert_after(item, new_item); cmd_list_free(pr->cmdlist); break; } return (CMD_RETURN_NORMAL); } cdata = xcalloc(1, sizeof *cdata); cdata->cmd_if = xstrdup(args->argv[1]); if (args->argc == 3) cdata->cmd_else = xstrdup(args->argv[2]); else cdata->cmd_else = NULL; memcpy(&cdata->mouse, m, sizeof cdata->mouse); if (!args_has(args, 'b')) cdata->client = item->client; else cdata->client = c; if (cdata->client != NULL) cdata->client->references++; if (!args_has(args, 'b')) cdata->item = item; else cdata->item = NULL; memset(&cdata->input, 0, sizeof cdata->input); if (self->file != NULL) cdata->input.file = xstrdup(self->file); cdata->input.line = self->line; cdata->input.item = cdata->item; cdata->input.c = c; if (cdata->input.c != NULL) cdata->input.c->references++; cmd_find_copy_state(&cdata->input.fs, fs); if (job_run(shellcmd, s, server_client_get_cwd(item->client, s), NULL, cmd_if_shell_callback, cmd_if_shell_free, cdata, 0) == NULL) { cmdq_error(item, "failed to run command: %s", shellcmd); free(shellcmd); free(cdata); return (CMD_RETURN_ERROR); } free(shellcmd); if (args_has(args, 'b')) return (CMD_RETURN_NORMAL); return (CMD_RETURN_WAIT); } static void cmd_if_shell_callback(struct job *job) { struct cmd_if_shell_data *cdata = job_get_data(job); struct client *c = cdata->client; struct mouse_event *m = &cdata->mouse; struct cmdq_item *new_item = NULL; char *cmd; int status; struct cmd_parse_result *pr; status = job_get_status(job); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) cmd = cdata->cmd_else; else cmd = cdata->cmd_if; if (cmd == NULL) goto out; pr = cmd_parse_from_string(cmd, &cdata->input); switch (pr->status) { case CMD_PARSE_EMPTY: break; case CMD_PARSE_ERROR: if (cdata->item != NULL) cmdq_error(cdata->item, "%s", pr->error); free(pr->error); break; case CMD_PARSE_SUCCESS: new_item = cmdq_get_command(pr->cmdlist, NULL, m, 0); cmd_list_free(pr->cmdlist); break; } if (new_item != NULL) { if (cdata->item == NULL) cmdq_append(c, new_item); else cmdq_insert_after(cdata->item, new_item); } out: if (cdata->item != NULL) cmdq_continue(cdata->item); } static void cmd_if_shell_free(void *data) { struct cmd_if_shell_data *cdata = data; if (cdata->client != NULL) server_client_unref(cdata->client); free(cdata->cmd_else); free(cdata->cmd_if); if (cdata->input.c != NULL) server_client_unref(cdata->input.c); free((void *)cdata->input.file); free(cdata); } tmux-3.0a/cmd-join-pane.c100644 001750 001750 00000010670 13570677043 0011021/* $OpenBSD$ */ /* * Copyright (c) 2011 George Nachman * 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 or move a pane into another (like split/swap/kill). */ static enum cmd_retval cmd_join_pane_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_join_pane_entry = { .name = "join-pane", .alias = "joinp", .args = { "bdhvp:l:s:t:", 0, 0 }, .usage = "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, .source = { 's', CMD_FIND_PANE, CMD_FIND_DEFAULT_MARKED }, .target = { 't', CMD_FIND_PANE, 0 }, .flags = 0, .exec = cmd_join_pane_exec }; const struct cmd_entry cmd_move_pane_entry = { .name = "move-pane", .alias = "movep", .args = { "bdhvp:l:s:t:", 0, 0 }, .usage = "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, .source = { 's', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 }, .flags = 0, .exec = cmd_join_pane_exec }; static enum cmd_retval cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct cmd_find_state *current = &item->shared->current; 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; int not_same_window, flags; if (self->entry == &cmd_join_pane_entry) not_same_window = 1; else not_same_window = 0; dst_s = item->target.s; dst_wl = item->target.wl; dst_wp = item->target.wp; dst_w = dst_wl->window; dst_idx = dst_wl->idx; server_unzoom_window(dst_w); src_wl = item->source.wl; src_wp = item->source.wp; src_w = src_wl->window; server_unzoom_window(src_w); if (not_same_window && src_w == dst_w) { cmdq_error(item, "can't join a pane to its own window"); return (CMD_RETURN_ERROR); } if (!not_same_window && src_wp == dst_wp) { cmdq_error(item, "source and target panes must be different"); return (CMD_RETURN_ERROR); } 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) { cmdq_error(item, "size %s", cause); free(cause); return (CMD_RETURN_ERROR); } } else if (args_has(args, 'p')) { percentage = args_strtonum(args, 'p', 0, 100, &cause); if (cause != NULL) { cmdq_error(item, "percentage %s", cause); free(cause); return (CMD_RETURN_ERROR); } if (type == LAYOUT_TOPBOTTOM) size = (dst_wp->sy * percentage) / 100; else size = (dst_wp->sx * percentage) / 100; } if (args_has(args, 'b')) flags = SPAWN_BEFORE; else flags = 0; lc = layout_split_pane(dst_wp, type, size, flags); if (lc == NULL) { cmdq_error(item, "create pane failed: pane too small"); return (CMD_RETURN_ERROR); } layout_close_pane(src_wp); window_lost_pane(src_w, src_wp); TAILQ_REMOVE(&src_w->panes, src_wp, entry); src_wp->window = dst_w; options_set_parent(src_wp->options, dst_w->options); src_wp->flags |= PANE_STYLECHANGED; 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, 1); session_select(dst_s, dst_idx); cmd_find_from_session(current, dst_s, 0); server_redraw_session(dst_s); } else server_status_session(dst_s); if (window_count_panes(src_w) == 0) server_kill_window(src_w); else notify_window("window-layout-changed", src_w); notify_window("window-layout-changed", dst_w); return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-kill-pane.c100644 001750 001750 00000003401 13570677043 0011007/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Kill pane. */ static enum cmd_retval cmd_kill_pane_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_kill_pane_entry = { .name = "kill-pane", .alias = "killp", .args = { "at:", 0, 0 }, .usage = "[-a] " CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, .flags = 0, .exec = cmd_kill_pane_exec }; static enum cmd_retval cmd_kill_pane_exec(struct cmd *self, struct cmdq_item *item) { struct winlink *wl = item->target.wl; struct window_pane *loopwp, *tmpwp, *wp = item->target.wp; if (args_has(self->args, 'a')) { server_unzoom_window(wl->window); TAILQ_FOREACH_SAFE(loopwp, &wl->window->panes, entry, tmpwp) { if (loopwp == wp) continue; layout_close_pane(loopwp); window_remove_pane(wl->window, loopwp); } server_redraw_window(wl->window); return (CMD_RETURN_NORMAL); } server_kill_pane(wp); return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-kill-server.c100644 001750 001750 00000003113 13462323664 0011367/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Kill the server and do nothing else. */ static enum cmd_retval cmd_kill_server_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_kill_server_entry = { .name = "kill-server", .alias = NULL, .args = { "", 0, 0 }, .usage = "", .flags = 0, .exec = cmd_kill_server_exec }; const struct cmd_entry cmd_start_server_entry = { .name = "start-server", .alias = "start", .args = { "", 0, 0 }, .usage = "", .flags = CMD_STARTSERVER, .exec = cmd_kill_server_exec }; static enum cmd_retval cmd_kill_server_exec(struct cmd *self, __unused struct cmdq_item *item) { if (self->entry == &cmd_kill_server_entry) kill(getpid(), SIGTERM); return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-kill-session.c100644 001750 001750 00000004062 13504653146 0011546/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "tmux.h" /* * 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. */ static enum cmd_retval cmd_kill_session_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_kill_session_entry = { .name = "kill-session", .alias = NULL, .args = { "aCt:", 0, 0 }, .usage = "[-aC] " CMD_TARGET_SESSION_USAGE, .target = { 't', CMD_FIND_SESSION, 0 }, .flags = 0, .exec = cmd_kill_session_exec }; static enum cmd_retval cmd_kill_session_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct session *s, *sloop, *stmp; struct winlink *wl; s = item->target.s; if (args_has(args, 'C')) { RB_FOREACH(wl, winlinks, &s->windows) { wl->window->flags &= ~WINDOW_ALERTFLAGS; wl->flags &= ~WINLINK_ALERTFLAGS; } server_redraw_session(s); } else if (args_has(args, 'a')) { RB_FOREACH_SAFE(sloop, sessions, &sessions, stmp) { if (sloop != s) { server_destroy_session(sloop); session_destroy(sloop, 1, __func__); } } } else { server_destroy_session(s); session_destroy(s, 1, __func__); } return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-kill-window.c100644 001750 001750 00000004261 13462323664 0011375/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "tmux.h" /* * Destroy window. */ static enum cmd_retval cmd_kill_window_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_kill_window_entry = { .name = "kill-window", .alias = "killw", .args = { "at:", 0, 0 }, .usage = "[-a] " CMD_TARGET_WINDOW_USAGE, .target = { 't', CMD_FIND_WINDOW, 0 }, .flags = 0, .exec = cmd_kill_window_exec }; const struct cmd_entry cmd_unlink_window_entry = { .name = "unlink-window", .alias = "unlinkw", .args = { "kt:", 0, 0 }, .usage = "[-k] " CMD_TARGET_WINDOW_USAGE, .target = { 't', CMD_FIND_WINDOW, 0 }, .flags = 0, .exec = cmd_kill_window_exec }; static enum cmd_retval cmd_kill_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct winlink *wl = item->target.wl, *wl2, *wl3; struct window *w = wl->window; struct session *s = item->target.s; if (self->entry == &cmd_unlink_window_entry) { if (!args_has(self->args, 'k') && !session_is_linked(s, w)) { cmdq_error(item, "window only linked to one session"); return (CMD_RETURN_ERROR); } server_unlink_window(s, wl); } else { if (args_has(args, 'a')) { RB_FOREACH_SAFE(wl2, winlinks, &s->windows, wl3) { if (wl != wl2) server_kill_window(wl2->window); } } else server_kill_window(wl->window); } recalculate_sizes(); return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-list-buffers.c100644 001750 001750 00000003577 13462323664 0011553/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * List paste buffers. */ #define LIST_BUFFERS_TEMPLATE \ "#{buffer_name}: #{buffer_size} bytes: \"#{buffer_sample}\"" static enum cmd_retval cmd_list_buffers_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_list_buffers_entry = { .name = "list-buffers", .alias = "lsb", .args = { "F:", 0, 0 }, .usage = "[-F format]", .flags = CMD_AFTERHOOK, .exec = cmd_list_buffers_exec }; static enum cmd_retval cmd_list_buffers_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct paste_buffer *pb; struct format_tree *ft; char *line; const char *template; if ((template = args_get(args, 'F')) == NULL) template = LIST_BUFFERS_TEMPLATE; pb = NULL; while ((pb = paste_walk(pb)) != NULL) { ft = format_create(item->client, item, FORMAT_NONE, 0); format_defaults_paste_buffer(ft, pb); line = format_expand(ft, template); cmdq_print(item, "%s", line); free(line); format_free(ft); } return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-list-clients.c100644 001750 001750 00000004426 13462323664 0011552/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" /* * List all clients. */ #define LIST_CLIENTS_TEMPLATE \ "#{client_name}: #{session_name} " \ "[#{client_width}x#{client_height} #{client_termname}]" \ "#{?client_utf8, (utf8),} #{?client_readonly, (ro),}" static enum cmd_retval cmd_list_clients_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_list_clients_entry = { .name = "list-clients", .alias = "lsc", .args = { "F:t:", 0, 0 }, .usage = "[-F format] " CMD_TARGET_SESSION_USAGE, .target = { 't', CMD_FIND_SESSION, 0 }, .flags = CMD_READONLY|CMD_AFTERHOOK, .exec = cmd_list_clients_exec }; static enum cmd_retval cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct client *c; struct session *s; struct format_tree *ft; const char *template; u_int idx; char *line; if (args_has(args, 't')) s = item->target.s; else s = NULL; if ((template = args_get(args, 'F')) == NULL) template = LIST_CLIENTS_TEMPLATE; idx = 0; TAILQ_FOREACH(c, &clients, entry) { if (c->session == NULL || (s != NULL && s != c->session)) continue; ft = format_create(item->client, item, FORMAT_NONE, 0); format_add(ft, "line", "%u", idx); format_defaults(ft, c, NULL, NULL, NULL); line = format_expand(ft, template); cmdq_print(item, "%s", line); free(line); format_free(ft); idx++; } return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-list-keys.c100644 001750 001750 00000012304 13570044333 0011047/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * List key bindings. */ static enum cmd_retval cmd_list_keys_exec(struct cmd *, struct cmdq_item *); static enum cmd_retval cmd_list_keys_commands(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_list_keys_entry = { .name = "list-keys", .alias = "lsk", .args = { "T:", 0, 0 }, .usage = "[-T key-table]", .flags = CMD_STARTSERVER|CMD_AFTERHOOK, .exec = cmd_list_keys_exec }; const struct cmd_entry cmd_list_commands_entry = { .name = "list-commands", .alias = "lscm", .args = { "F:", 0, 0 }, .usage = "[-F format]", .flags = CMD_STARTSERVER|CMD_AFTERHOOK, .exec = cmd_list_keys_exec }; static enum cmd_retval cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct key_table *table; struct key_binding *bd; const char *tablename, *r; char *key, *cp, *tmp; int repeat, width, tablewidth, keywidth; size_t tmpsize, tmpused, cplen; if (self->entry == &cmd_list_commands_entry) return (cmd_list_keys_commands(self, item)); tablename = args_get(args, 'T'); if (tablename != NULL && key_bindings_get_table(tablename, 0) == NULL) { cmdq_error(item, "table %s doesn't exist", tablename); return (CMD_RETURN_ERROR); } repeat = 0; tablewidth = keywidth = 0; table = key_bindings_first_table (); while (table != NULL) { if (tablename != NULL && strcmp(table->name, tablename) != 0) { table = key_bindings_next_table(table); continue; } bd = key_bindings_first(table); while (bd != NULL) { key = args_escape(key_string_lookup_key(bd->key)); if (bd->flags & KEY_BINDING_REPEAT) repeat = 1; width = utf8_cstrwidth(table->name); if (width > tablewidth) tablewidth = width; width = utf8_cstrwidth(key); if (width > keywidth) keywidth = width; free(key); bd = key_bindings_next(table, bd); } table = key_bindings_next_table(table); } tmpsize = 256; tmp = xmalloc(tmpsize); table = key_bindings_first_table (); while (table != NULL) { if (tablename != NULL && strcmp(table->name, tablename) != 0) { table = key_bindings_next_table(table); continue; } bd = key_bindings_first(table); while (bd != NULL) { key = args_escape(key_string_lookup_key(bd->key)); if (!repeat) r = ""; else if (bd->flags & KEY_BINDING_REPEAT) r = "-r "; else r = " "; tmpused = xsnprintf(tmp, tmpsize, "%s-T ", r); cp = utf8_padcstr(table->name, tablewidth); cplen = strlen(cp) + 1; while (tmpused + cplen + 1 >= tmpsize) { tmpsize *= 2; tmp = xrealloc(tmp, tmpsize); } tmpused = strlcat(tmp, cp, tmpsize); tmpused = strlcat(tmp, " ", tmpsize); free(cp); cp = utf8_padcstr(key, keywidth); cplen = strlen(cp) + 1; while (tmpused + cplen + 1 >= tmpsize) { tmpsize *= 2; tmp = xrealloc(tmp, tmpsize); } tmpused = strlcat(tmp, cp, tmpsize); tmpused = strlcat(tmp, " ", tmpsize); free(cp); cp = cmd_list_print(bd->cmdlist, 1); cplen = strlen(cp); while (tmpused + cplen + 1 >= tmpsize) { tmpsize *= 2; tmp = xrealloc(tmp, tmpsize); } strlcat(tmp, cp, tmpsize); free(cp); cmdq_print(item, "bind-key %s", tmp); free(key); bd = key_bindings_next(table, bd); } table = key_bindings_next_table(table); } free(tmp); return (CMD_RETURN_NORMAL); } static enum cmd_retval cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; const struct cmd_entry **entryp; const struct cmd_entry *entry; struct format_tree *ft; const char *template, *s; char *line; if ((template = args_get(args, 'F')) == NULL) { template = "#{command_list_name}" "#{?command_list_alias, (#{command_list_alias}),} " "#{command_list_usage}"; } ft = format_create(item->client, item, FORMAT_NONE, 0); format_defaults(ft, NULL, NULL, NULL, NULL); for (entryp = cmd_table; *entryp != NULL; entryp++) { entry = *entryp; format_add(ft, "command_list_name", "%s", entry->name); if (entry->alias != NULL) s = entry->alias; else s = ""; format_add(ft, "command_list_alias", "%s", s); if (entry->usage != NULL) s = entry->usage; else s = ""; format_add(ft, "command_list_usage", "%s", s); line = format_expand(ft, template); if (*line != '\0') cmdq_print(item, "%s", line); free(line); } format_free(ft); return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-list-panes.c100644 001750 001750 00000007415 13462323664 0011220/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * List panes on given window. */ static enum cmd_retval cmd_list_panes_exec(struct cmd *, struct cmdq_item *); static void cmd_list_panes_server(struct cmd *, struct cmdq_item *); static void cmd_list_panes_session(struct cmd *, struct session *, struct cmdq_item *, int); static void cmd_list_panes_window(struct cmd *, struct session *, struct winlink *, struct cmdq_item *, int); const struct cmd_entry cmd_list_panes_entry = { .name = "list-panes", .alias = "lsp", .args = { "asF:t:", 0, 0 }, .usage = "[-as] [-F format] " CMD_TARGET_WINDOW_USAGE, .target = { 't', CMD_FIND_WINDOW, 0 }, .flags = CMD_AFTERHOOK, .exec = cmd_list_panes_exec }; static enum cmd_retval cmd_list_panes_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct session *s = item->target.s; struct winlink *wl = item->target.wl; if (args_has(args, 'a')) cmd_list_panes_server(self, item); else if (args_has(args, 's')) cmd_list_panes_session(self, s, item, 1); else cmd_list_panes_window(self, s, wl, item, 0); return (CMD_RETURN_NORMAL); } static void cmd_list_panes_server(struct cmd *self, struct cmdq_item *item) { struct session *s; RB_FOREACH(s, sessions, &sessions) cmd_list_panes_session(self, s, item, 2); } static void cmd_list_panes_session(struct cmd *self, struct session *s, struct cmdq_item *item, int type) { struct winlink *wl; RB_FOREACH(wl, winlinks, &s->windows) cmd_list_panes_window(self, s, wl, item, type); } static void cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl, struct cmdq_item *item, int type) { struct args *args = self->args; struct window_pane *wp; u_int n; struct format_tree *ft; const char *template; char *line; template = args_get(args, 'F'); if (template == NULL) { switch (type) { case 0: template = "#{pane_index}: " "[#{pane_width}x#{pane_height}] [history " "#{history_size}/#{history_limit}, " "#{history_bytes} bytes] #{pane_id}" "#{?pane_active, (active),}#{?pane_dead, (dead),}"; break; case 1: template = "#{window_index}.#{pane_index}: " "[#{pane_width}x#{pane_height}] [history " "#{history_size}/#{history_limit}, " "#{history_bytes} bytes] #{pane_id}" "#{?pane_active, (active),}#{?pane_dead, (dead),}"; break; case 2: template = "#{session_name}:#{window_index}." "#{pane_index}: [#{pane_width}x#{pane_height}] " "[history #{history_size}/#{history_limit}, " "#{history_bytes} bytes] #{pane_id}" "#{?pane_active, (active),}#{?pane_dead, (dead),}"; break; } } n = 0; TAILQ_FOREACH(wp, &wl->window->panes, entry) { ft = format_create(item->client, item, FORMAT_NONE, 0); format_add(ft, "line", "%u", n); format_defaults(ft, NULL, s, wl, wp); line = format_expand(ft, template); cmdq_print(item, "%s", line); free(line); format_free(ft); n++; } } tmux-3.0a/cmd-list-sessions.c100644 001750 001750 00000004132 13466241542 0011747/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" /* * List all sessions. */ #define LIST_SESSIONS_TEMPLATE \ "#{session_name}: #{session_windows} windows " \ "(created #{t:session_created})" \ "#{?session_grouped, (group ,}" \ "#{session_group}#{?session_grouped,),}" \ "#{?session_attached, (attached),}" static enum cmd_retval cmd_list_sessions_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_list_sessions_entry = { .name = "list-sessions", .alias = "ls", .args = { "F:", 0, 0 }, .usage = "[-F format]", .flags = CMD_AFTERHOOK, .exec = cmd_list_sessions_exec }; static enum cmd_retval cmd_list_sessions_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct session *s; u_int n; struct format_tree *ft; const char *template; char *line; if ((template = args_get(args, 'F')) == NULL) template = LIST_SESSIONS_TEMPLATE; n = 0; RB_FOREACH(s, sessions, &sessions) { ft = format_create(item->client, item, FORMAT_NONE, 0); format_add(ft, "line", "%u", n); format_defaults(ft, NULL, s, NULL, NULL); line = format_expand(ft, template); cmdq_print(item, "%s", line); free(line); format_free(ft); n++; } return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-list-windows.c100644 001750 001750 00000006134 13462323664 0011601/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * List windows on given session. */ #define LIST_WINDOWS_TEMPLATE \ "#{window_index}: #{window_name}#{window_flags} " \ "(#{window_panes} panes) " \ "[#{window_width}x#{window_height}] " \ "[layout #{window_layout}] #{window_id}" \ "#{?window_active, (active),}"; #define LIST_WINDOWS_WITH_SESSION_TEMPLATE \ "#{session_name}:" \ "#{window_index}: #{window_name}#{window_flags} " \ "(#{window_panes} panes) " \ "[#{window_width}x#{window_height}] " static enum cmd_retval cmd_list_windows_exec(struct cmd *, struct cmdq_item *); static void cmd_list_windows_server(struct cmd *, struct cmdq_item *); static void cmd_list_windows_session(struct cmd *, struct session *, struct cmdq_item *, int); const struct cmd_entry cmd_list_windows_entry = { .name = "list-windows", .alias = "lsw", .args = { "F:at:", 0, 0 }, .usage = "[-a] [-F format] " CMD_TARGET_SESSION_USAGE, .target = { 't', CMD_FIND_SESSION, 0 }, .flags = CMD_AFTERHOOK, .exec = cmd_list_windows_exec }; static enum cmd_retval cmd_list_windows_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; if (args_has(args, 'a')) cmd_list_windows_server(self, item); else cmd_list_windows_session(self, item->target.s, item, 0); return (CMD_RETURN_NORMAL); } static void cmd_list_windows_server(struct cmd *self, struct cmdq_item *item) { struct session *s; RB_FOREACH(s, sessions, &sessions) cmd_list_windows_session(self, s, item, 1); } static void cmd_list_windows_session(struct cmd *self, struct session *s, struct cmdq_item *item, int type) { struct args *args = self->args; struct winlink *wl; u_int n; struct format_tree *ft; const char *template; char *line; template = args_get(args, 'F'); if (template == NULL) { switch (type) { case 0: template = LIST_WINDOWS_TEMPLATE; break; case 1: template = LIST_WINDOWS_WITH_SESSION_TEMPLATE; break; } } n = 0; RB_FOREACH(wl, winlinks, &s->windows) { ft = format_create(item->client, item, FORMAT_NONE, 0); format_add(ft, "line", "%u", n); format_defaults(ft, NULL, s, wl, NULL); line = format_expand(ft, template); cmdq_print(item, "%s", line); free(line); format_free(ft); n++; } } tmux-3.0a/cmd-load-buffer.c100644 001750 001750 00000010373 13517540563 0011324/* $OpenBSD$ */ /* * 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 #include "tmux.h" /* * Loads a paste buffer from a file. */ static enum cmd_retval cmd_load_buffer_exec(struct cmd *, struct cmdq_item *); static void cmd_load_buffer_callback(struct client *, int, void *); const struct cmd_entry cmd_load_buffer_entry = { .name = "load-buffer", .alias = "loadb", .args = { "b:", 1, 1 }, .usage = CMD_BUFFER_USAGE " path", .flags = CMD_AFTERHOOK, .exec = cmd_load_buffer_exec }; struct cmd_load_buffer_data { struct cmdq_item *item; char *bufname; }; static enum cmd_retval cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct cmd_load_buffer_data *cdata; struct client *c = cmd_find_client(item, NULL, 1); struct session *s = item->target.s; struct winlink *wl = item->target.wl; struct window_pane *wp = item->target.wp; FILE *f; const char *bufname; char *pdata = NULL, *new_pdata, *cause; char *path, *file; size_t psize; int ch, error; bufname = NULL; if (args_has(args, 'b')) bufname = args_get(args, 'b'); path = format_single(item, args->argv[0], c, s, wl, wp); if (strcmp(path, "-") == 0) { free(path); c = item->client; cdata = xcalloc(1, sizeof *cdata); cdata->item = item; if (bufname != NULL) cdata->bufname = xstrdup(bufname); error = server_set_stdin_callback(c, cmd_load_buffer_callback, cdata, &cause); if (error != 0) { cmdq_error(item, "-: %s", cause); free(cause); free(cdata); return (CMD_RETURN_ERROR); } return (CMD_RETURN_WAIT); } file = server_client_get_path(item->client, path); free(path); f = fopen(file, "rb"); if (f == NULL) { cmdq_error(item, "%s: %s", file, strerror(errno)); goto error; } 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) { cmdq_error(item, "realloc error: %s", strerror(errno)); goto error; } pdata = new_pdata; pdata[psize++] = ch; } if (ferror(f)) { cmdq_error(item, "%s: read error", file); goto error; } if (pdata != NULL) pdata[psize] = '\0'; fclose(f); free(file); if (paste_set(pdata, psize, bufname, &cause) != 0) { cmdq_error(item, "%s", cause); free(pdata); free(cause); return (CMD_RETURN_ERROR); } return (CMD_RETURN_NORMAL); error: free(pdata); if (f != NULL) fclose(f); free(file); return (CMD_RETURN_ERROR); } static void cmd_load_buffer_callback(struct client *c, int closed, void *data) { struct cmd_load_buffer_data *cdata = data; char *pdata, *cause, *saved; size_t psize; if (!closed) return; c->stdin_callback = NULL; server_client_unref(c); if (c->flags & CLIENT_DEAD) goto out; psize = EVBUFFER_LENGTH(c->stdin_data); if (psize == 0 || (pdata = malloc(psize + 1)) == NULL) goto out; memcpy(pdata, EVBUFFER_DATA(c->stdin_data), psize); pdata[psize] = '\0'; evbuffer_drain(c->stdin_data, psize); if (paste_set(pdata, psize, cdata->bufname, &cause) != 0) { /* No context so can't use server_client_msg_error. */ if (~c->flags & CLIENT_UTF8) { saved = cause; cause = utf8_sanitize(saved); free(saved); } evbuffer_add_printf(c->stderr_data, "%s", cause); server_client_push_stderr(c); free(pdata); free(cause); } out: cmdq_continue(cdata->item); free(cdata->bufname); free(cdata); } tmux-3.0a/cmd-lock-server.c100644 001750 001750 00000004105 13462323664 0011366/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "tmux.h" /* * Lock commands. */ static enum cmd_retval cmd_lock_server_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_lock_server_entry = { .name = "lock-server", .alias = "lock", .args = { "", 0, 0 }, .usage = "", .flags = CMD_AFTERHOOK, .exec = cmd_lock_server_exec }; const struct cmd_entry cmd_lock_session_entry = { .name = "lock-session", .alias = "locks", .args = { "t:", 0, 0 }, .usage = CMD_TARGET_SESSION_USAGE, .target = { 't', CMD_FIND_SESSION, 0 }, .flags = CMD_AFTERHOOK, .exec = cmd_lock_server_exec }; const struct cmd_entry cmd_lock_client_entry = { .name = "lock-client", .alias = "lockc", .args = { "t:", 0, 0 }, .usage = CMD_TARGET_CLIENT_USAGE, .flags = CMD_AFTERHOOK, .exec = cmd_lock_server_exec }; static enum cmd_retval cmd_lock_server_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct client *c; if (self->entry == &cmd_lock_server_entry) server_lock(); else if (self->entry == &cmd_lock_session_entry) server_lock_session(item->target.s); else { if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); server_lock_client(c); } recalculate_sizes(); return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-move-window.c100644 001750 001750 00000006147 13462323664 0011415/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Move a window. */ static enum cmd_retval cmd_move_window_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_move_window_entry = { .name = "move-window", .alias = "movew", .args = { "adkrs:t:", 0, 0 }, .usage = "[-dkr] " CMD_SRCDST_WINDOW_USAGE, .source = { 's', CMD_FIND_WINDOW, 0 }, /* -t is special */ .flags = 0, .exec = cmd_move_window_exec }; const struct cmd_entry cmd_link_window_entry = { .name = "link-window", .alias = "linkw", .args = { "adks:t:", 0, 0 }, .usage = "[-dk] " CMD_SRCDST_WINDOW_USAGE, .source = { 's', CMD_FIND_WINDOW, 0 }, /* -t is special */ .flags = 0, .exec = cmd_move_window_exec }; static enum cmd_retval cmd_move_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; const char *tflag = args_get(args, 't'); struct session *src; struct session *dst; struct winlink *wl; char *cause; int idx, kflag, dflag, sflag; if (args_has(args, 'r')) { if (cmd_find_target(&item->target, item, tflag, CMD_FIND_SESSION, CMD_FIND_QUIET) != 0) return (CMD_RETURN_ERROR); session_renumber_windows(item->target.s); recalculate_sizes(); server_status_session(item->target.s); return (CMD_RETURN_NORMAL); } if (cmd_find_target(&item->target, item, tflag, CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX) != 0) return (CMD_RETURN_ERROR); src = item->source.s; dst = item->target.s; wl = item->source.wl; idx = item->target.idx; kflag = args_has(self->args, 'k'); dflag = args_has(self->args, 'd'); sflag = args_has(self->args, 's'); if (args_has(self->args, 'a')) { if ((idx = winlink_shuffle_up(dst, dst->curw)) == -1) return (CMD_RETURN_ERROR); } if (server_link_window(src, wl, dst, idx, kflag, !dflag, &cause) != 0) { cmdq_error(item, "can't link window: %s", cause); free(cause); return (CMD_RETURN_ERROR); } if (self->entry == &cmd_move_window_entry) server_unlink_window(src, wl); /* * Renumber the winlinks in the src session only, the destination * session already has the correct winlink id to us, either * automatically or specified by -s. */ if (!sflag && options_get_number(src->options, "renumber-windows")) session_renumber_windows(src); recalculate_sizes(); return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-new-session.c100644 001750 001750 00000021736 13570677043 0011420/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include "tmux.h" /* * Create a new session and attach to the current terminal unless -d is given. */ #define NEW_SESSION_TEMPLATE "#{session_name}:" static enum cmd_retval cmd_new_session_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_new_session_entry = { .name = "new-session", .alias = "new", .args = { "Ac:dDEF:n:Ps:t:x:Xy:", 0, -1 }, .usage = "[-AdDEPX] [-c start-directory] [-F format] [-n window-name] " "[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] " "[-y height] [command]", .target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL }, .flags = CMD_STARTSERVER, .exec = cmd_new_session_exec }; const struct cmd_entry cmd_has_session_entry = { .name = "has-session", .alias = "has", .args = { "t:", 0, 0 }, .usage = CMD_TARGET_SESSION_USAGE, .target = { 't', CMD_FIND_SESSION, 0 }, .flags = 0, .exec = cmd_new_session_exec }; static enum cmd_retval cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct client *c = item->client; struct session *s, *as, *groupwith; struct environ *env; struct options *oo; struct termios tio, *tiop; struct session_group *sg; const char *errstr, *template, *group, *prefix, *tmp; char *cause, *cwd = NULL, *cp, *newname = NULL; int detached, already_attached, is_control = 0; u_int sx, sy, dsx, dsy; struct spawn_context sc; enum cmd_retval retval; struct cmd_find_state fs; if (self->entry == &cmd_has_session_entry) { /* * cmd_find_target() will fail if the session cannot be found, * so always return success here. */ return (CMD_RETURN_NORMAL); } if (args_has(args, 't') && (args->argc != 0 || args_has(args, 'n'))) { cmdq_error(item, "command or window name given with target"); return (CMD_RETURN_ERROR); } if (args_has(args, 's')) { newname = format_single(item, args_get(args, 's'), c, NULL, NULL, NULL); if (!session_check_name(newname)) { cmdq_error(item, "bad session name: %s", newname); goto fail; } if ((as = session_find(newname)) != NULL) { if (args_has(args, 'A')) { retval = cmd_attach_session(item, newname, args_has(args, 'D'), args_has(args, 'X'), 0, NULL, args_has(args, 'E')); free(newname); return (retval); } cmdq_error(item, "duplicate session: %s", newname); goto fail; } } /* Is this going to be part of a session group? */ group = args_get(args, 't'); if (group != NULL) { groupwith = item->target.s; if (groupwith == NULL) { if (!session_check_name(group)) { cmdq_error(item, "bad group name: %s", group); goto fail; } sg = session_group_find(group); } else sg = session_group_contains(groupwith); if (sg != NULL) prefix = sg->name; else if (groupwith != NULL) prefix = groupwith->name; else prefix = group; } else { groupwith = NULL; sg = NULL; prefix = NULL; } /* Set -d if no client. */ detached = args_has(args, 'd'); if (c == NULL) detached = 1; else if (c->flags & CLIENT_CONTROL) is_control = 1; /* Is this client already attached? */ already_attached = 0; if (c != NULL && c->session != NULL) already_attached = 1; /* Get the new session working directory. */ if ((tmp = args_get(args, 'c')) != NULL) cwd = format_single(item, tmp, c, NULL, NULL, NULL); else cwd = xstrdup(server_client_get_cwd(c, NULL)); /* * If this is a new client, check for nesting and save the termios * settings (part of which is used for new windows in this session). * * tcgetattr() is used rather than using tty.tio since if the client is * detached, tty_open won't be called. It must be done before opening * the terminal as that calls tcsetattr() to prepare for tmux taking * over. */ if (!detached && !already_attached && c->tty.fd != -1) { if (server_client_check_nested(item->client)) { cmdq_error(item, "sessions should be nested with care, " "unset $TMUX to force"); goto fail; } if (tcgetattr(c->tty.fd, &tio) != 0) fatal("tcgetattr failed"); tiop = &tio; } else tiop = NULL; /* Open the terminal if necessary. */ if (!detached && !already_attached) { if (server_client_open(c, &cause) != 0) { cmdq_error(item, "open terminal failed: %s", cause); free(cause); goto fail; } } /* Get default session size. */ if (args_has(args, 'x')) { tmp = args_get(args, 'x'); if (strcmp(tmp, "-") == 0) { if (c != NULL) dsx = c->tty.sx; else dsx = 80; } else { dsx = strtonum(tmp, 1, USHRT_MAX, &errstr); if (errstr != NULL) { cmdq_error(item, "width %s", errstr); goto fail; } } } if (args_has(args, 'y')) { tmp = args_get(args, 'y'); if (strcmp(tmp, "-") == 0) { if (c != NULL) dsy = c->tty.sy; else dsy = 24; } else { dsy = strtonum(tmp, 1, USHRT_MAX, &errstr); if (errstr != NULL) { cmdq_error(item, "height %s", errstr); goto fail; } } } /* Find new session size. */ if (!detached && !is_control) { sx = c->tty.sx; sy = c->tty.sy; if (sy > 0 && options_get_number(global_s_options, "status")) sy--; } else { tmp = options_get_string(global_s_options, "default-size"); if (sscanf(tmp, "%ux%u", &sx, &sy) != 2) { sx = 80; sy = 24; } if (args_has(args, 'x')) sx = dsx; if (args_has(args, 'y')) sy = dsy; } if (sx == 0) sx = 1; if (sy == 0) sy = 1; /* Create the new session. */ oo = options_create(global_s_options); if (args_has(args, 'x') || args_has(args, 'y')) { if (!args_has(args, 'x')) dsx = sx; if (!args_has(args, 'y')) dsy = sy; options_set_string(oo, "default-size", 0, "%ux%u", dsx, dsy); } env = environ_create(); if (c != NULL && !args_has(args, 'E')) environ_update(global_s_options, c->environ, env); s = session_create(prefix, newname, cwd, env, oo, tiop); /* Spawn the initial window. */ memset(&sc, 0, sizeof sc); sc.item = item; sc.s = s; sc.name = args_get(args, 'n'); sc.argc = args->argc; sc.argv = args->argv; sc.idx = -1; sc.cwd = args_get(args, 'c'); sc.flags = 0; if (spawn_window(&sc, &cause) == NULL) { session_destroy(s, 0, __func__); cmdq_error(item, "create window failed: %s", cause); free(cause); goto fail; } /* * If a target session is given, this is to be part of a session group, * so add it to the group and synchronize. */ if (group != NULL) { if (sg == NULL) { if (groupwith != NULL) { sg = session_group_new(groupwith->name); session_group_add(sg, groupwith); } else sg = session_group_new(group); } session_group_add(sg, s); session_group_synchronize_to(s); session_select(s, RB_MIN(winlinks, &s->windows)->idx); } notify_session("session-created", s); /* * 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 (!already_attached) { if (~c->flags & CLIENT_CONTROL) proc_send(c->peer, MSG_READY, -1, NULL, 0); } else if (c->session != NULL) c->last_session = c->session; c->session = s; if (~item->shared->flags & CMDQ_SHARED_REPEAT) server_client_set_key_table(c, NULL); tty_update_client_offset(c); status_timer_start(c); notify_client("client-session-changed", c); session_update_activity(s, NULL); gettimeofday(&s->last_attached_time, NULL); server_redraw_client(c); } 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) cfg_show_causes(s); /* Print if requested. */ if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) template = NEW_SESSION_TEMPLATE; cp = format_single(item, template, c, s, NULL, NULL); cmdq_print(item, "%s", cp); free(cp); } if (!detached) { c->flags |= CLIENT_ATTACHED; cmd_find_from_session(&item->shared->current, s, 0); } cmd_find_from_session(&fs, s, 0); cmdq_insert_hook(s, item, &fs, "after-new-session"); free(cwd); free(newname); return (CMD_RETURN_NORMAL); fail: free(cwd); free(newname); return (CMD_RETURN_ERROR); } tmux-3.0a/cmd-new-window.c100644 001750 001750 00000006441 13570677043 0011240/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "tmux.h" /* * Create a new window. */ #define NEW_WINDOW_TEMPLATE "#{session_name}:#{window_index}.#{pane_index}" static enum cmd_retval cmd_new_window_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_new_window_entry = { .name = "new-window", .alias = "neww", .args = { "ac:de:F:kn:Pt:", 0, -1 }, .usage = "[-adkP] [-c start-directory] [-e environment] [-F format] " "[-n window-name] " CMD_TARGET_WINDOW_USAGE " [command]", .target = { 't', CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX }, .flags = 0, .exec = cmd_new_window_exec }; static enum cmd_retval cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct cmd_find_state *current = &item->shared->current; struct spawn_context sc; struct client *c = cmd_find_client(item, NULL, 1); struct session *s = item->target.s; struct winlink *wl = item->target.wl; int idx = item->target.idx; struct winlink *new_wl; char *cause = NULL, *cp; const char *template, *add; struct cmd_find_state fs; struct args_value *value; if (args_has(args, 'a') && (idx = winlink_shuffle_up(s, wl)) == -1) { cmdq_error(item, "couldn't get a window index"); return (CMD_RETURN_ERROR); } memset(&sc, 0, sizeof sc); sc.item = item; sc.s = s; sc.name = args_get(args, 'n'); sc.argc = args->argc; sc.argv = args->argv; sc.environ = environ_create(); add = args_first_value(args, 'e', &value); while (add != NULL) { environ_put(sc.environ, add); add = args_next_value(&value); } sc.idx = idx; sc.cwd = args_get(args, 'c'); sc.flags = 0; if (args_has(args, 'd')) sc.flags |= SPAWN_DETACHED; if (args_has(args, 'k')) sc.flags |= SPAWN_KILL; if ((new_wl = spawn_window(&sc, &cause)) == NULL) { cmdq_error(item, "create window failed: %s", cause); free(cause); return (CMD_RETURN_ERROR); } if (!args_has(args, 'd') || new_wl == s->curw) { cmd_find_from_winlink(current, new_wl, 0); server_redraw_session_group(s); } else server_status_session_group(s); if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) template = NEW_WINDOW_TEMPLATE; cp = format_single(item, template, c, s, new_wl, NULL); cmdq_print(item, "%s", cp); free(cp); } cmd_find_from_winlink(&fs, new_wl, 0); cmdq_insert_hook(s, item, &fs, "after-new-window"); environ_free(sc.environ); return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-parse.y100644 001750 001750 00000074613 13570677043 0010310/* $OpenBSD$ */ /* * Copyright (c) 2019 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" static int yylex(void); static int yyparse(void); static int printflike(1,2) yyerror(const char *, ...); static char *yylex_token(int); static char *yylex_format(void); struct cmd_parse_scope { int flag; TAILQ_ENTRY (cmd_parse_scope) entry; }; struct cmd_parse_command { char *name; u_int line; int argc; char **argv; TAILQ_ENTRY(cmd_parse_command) entry; }; TAILQ_HEAD(cmd_parse_commands, cmd_parse_command); struct cmd_parse_state { FILE *f; const char *buf; size_t len; size_t off; int condition; int eol; int eof; struct cmd_parse_input *input; u_int escapes; char *error; struct cmd_parse_commands *commands; struct cmd_parse_scope *scope; TAILQ_HEAD(, cmd_parse_scope) stack; }; static struct cmd_parse_state parse_state; static char *cmd_parse_get_error(const char *, u_int, const char *); static void cmd_parse_free_command(struct cmd_parse_command *); static struct cmd_parse_commands *cmd_parse_new_commands(void); static void cmd_parse_free_commands(struct cmd_parse_commands *); static void cmd_parse_print_commands(struct cmd_parse_input *, u_int, struct cmd_list *); %} %union { char *token; struct { int argc; char **argv; } arguments; int flag; struct { int flag; struct cmd_parse_commands *commands; } elif; struct cmd_parse_commands *commands; struct cmd_parse_command *command; } %token ERROR %token IF %token ELSE %token ELIF %token ENDIF %token FORMAT TOKEN EQUALS %type argument expanded format %type arguments %type if_open if_elif %type elif elif1 %type statements statement commands condition condition1 %type command %% lines : /* empty */ | statements { struct cmd_parse_state *ps = &parse_state; ps->commands = $1; } statements : statement '\n' { $$ = $1; } | statements statement '\n' { $$ = $1; TAILQ_CONCAT($$, $2, entry); free($2); } statement : condition { struct cmd_parse_state *ps = &parse_state; if (ps->scope == NULL || ps->scope->flag) $$ = $1; else { $$ = cmd_parse_new_commands(); cmd_parse_free_commands($1); } } | assignment { $$ = xmalloc (sizeof *$$); TAILQ_INIT($$); } | commands { struct cmd_parse_state *ps = &parse_state; if (ps->scope == NULL || ps->scope->flag) $$ = $1; else { $$ = cmd_parse_new_commands(); cmd_parse_free_commands($1); } } format : FORMAT { $$ = $1; } | TOKEN { $$ = $1; } expanded : format { struct cmd_parse_state *ps = &parse_state; struct cmd_parse_input *pi = ps->input; struct format_tree *ft; struct client *c = pi->c; struct cmd_find_state *fs; int flags = FORMAT_NOJOBS; if (cmd_find_valid_state(&pi->fs)) fs = &pi->fs; else fs = NULL; ft = format_create(NULL, pi->item, FORMAT_NONE, flags); if (fs != NULL) format_defaults(ft, c, fs->s, fs->wl, fs->wp); else format_defaults(ft, c, NULL, NULL, NULL); $$ = format_expand(ft, $1); format_free(ft); free($1); } assignment : /* empty */ | EQUALS { struct cmd_parse_state *ps = &parse_state; int flags = ps->input->flags; if ((~flags & CMD_PARSE_PARSEONLY) && (ps->scope == NULL || ps->scope->flag)) environ_put(global_environ, $1); free($1); } if_open : IF expanded { struct cmd_parse_state *ps = &parse_state; struct cmd_parse_scope *scope; scope = xmalloc(sizeof *scope); $$ = scope->flag = format_true($2); free($2); if (ps->scope != NULL) TAILQ_INSERT_HEAD(&ps->stack, ps->scope, entry); ps->scope = scope; } if_else : ELSE { struct cmd_parse_state *ps = &parse_state; struct cmd_parse_scope *scope; scope = xmalloc(sizeof *scope); scope->flag = !ps->scope->flag; free(ps->scope); ps->scope = scope; } if_elif : ELIF expanded { struct cmd_parse_state *ps = &parse_state; struct cmd_parse_scope *scope; scope = xmalloc(sizeof *scope); $$ = scope->flag = format_true($2); free($2); free(ps->scope); ps->scope = scope; } if_close : ENDIF { struct cmd_parse_state *ps = &parse_state; free(ps->scope); ps->scope = TAILQ_FIRST(&ps->stack); if (ps->scope != NULL) TAILQ_REMOVE(&ps->stack, ps->scope, entry); } condition : if_open '\n' statements if_close { if ($1) $$ = $3; else { $$ = cmd_parse_new_commands(); cmd_parse_free_commands($3); } } | if_open '\n' statements if_else '\n' statements if_close { if ($1) { $$ = $3; cmd_parse_free_commands($6); } else { $$ = $6; cmd_parse_free_commands($3); } } | if_open '\n' statements elif if_close { if ($1) { $$ = $3; cmd_parse_free_commands($4.commands); } else if ($4.flag) { $$ = $4.commands; cmd_parse_free_commands($3); } else { $$ = cmd_parse_new_commands(); cmd_parse_free_commands($3); cmd_parse_free_commands($4.commands); } } | if_open '\n' statements elif if_else '\n' statements if_close { if ($1) { $$ = $3; cmd_parse_free_commands($4.commands); cmd_parse_free_commands($7); } else if ($4.flag) { $$ = $4.commands; cmd_parse_free_commands($3); cmd_parse_free_commands($7); } else { $$ = $7; cmd_parse_free_commands($3); cmd_parse_free_commands($4.commands); } } elif : if_elif '\n' statements { if ($1) { $$.flag = 1; $$.commands = $3; } else { $$.flag = 0; $$.commands = cmd_parse_new_commands(); cmd_parse_free_commands($3); } } | if_elif '\n' statements elif { if ($1) { $$.flag = 1; $$.commands = $3; cmd_parse_free_commands($4.commands); } else if ($4.flag) { $$.flag = 1; $$.commands = $4.commands; cmd_parse_free_commands($3); } else { $$.flag = 0; $$.commands = cmd_parse_new_commands(); cmd_parse_free_commands($3); cmd_parse_free_commands($4.commands); } } commands : command { struct cmd_parse_state *ps = &parse_state; $$ = cmd_parse_new_commands(); if (ps->scope == NULL || ps->scope->flag) TAILQ_INSERT_TAIL($$, $1, entry); else cmd_parse_free_command($1); } | commands ';' { $$ = $1; } | commands ';' condition1 { $$ = $1; TAILQ_CONCAT($$, $3, entry); free($3); } | commands ';' command { struct cmd_parse_state *ps = &parse_state; if (ps->scope == NULL || ps->scope->flag) { $$ = $1; TAILQ_INSERT_TAIL($$, $3, entry); } else { $$ = cmd_parse_new_commands(); cmd_parse_free_commands($1); cmd_parse_free_command($3); } } | condition1 { $$ = $1; } command : assignment TOKEN { struct cmd_parse_state *ps = &parse_state; $$ = xcalloc(1, sizeof *$$); $$->name = $2; $$->line = ps->input->line; } | assignment TOKEN arguments { struct cmd_parse_state *ps = &parse_state; $$ = xcalloc(1, sizeof *$$); $$->name = $2; $$->line = ps->input->line; $$->argc = $3.argc; $$->argv = $3.argv; } condition1 : if_open commands if_close { if ($1) $$ = $2; else { $$ = cmd_parse_new_commands(); cmd_parse_free_commands($2); } } | if_open commands if_else commands if_close { if ($1) { $$ = $2; cmd_parse_free_commands($4); } else { $$ = $4; cmd_parse_free_commands($2); } } | if_open commands elif1 if_close { if ($1) { $$ = $2; cmd_parse_free_commands($3.commands); } else if ($3.flag) { $$ = $3.commands; cmd_parse_free_commands($2); } else { $$ = cmd_parse_new_commands(); cmd_parse_free_commands($2); cmd_parse_free_commands($3.commands); } } | if_open commands elif1 if_else commands if_close { if ($1) { $$ = $2; cmd_parse_free_commands($3.commands); cmd_parse_free_commands($5); } else if ($3.flag) { $$ = $3.commands; cmd_parse_free_commands($2); cmd_parse_free_commands($5); } else { $$ = $5; cmd_parse_free_commands($2); cmd_parse_free_commands($3.commands); } } elif1 : if_elif commands { if ($1) { $$.flag = 1; $$.commands = $2; } else { $$.flag = 0; $$.commands = cmd_parse_new_commands(); cmd_parse_free_commands($2); } } | if_elif commands elif1 { if ($1) { $$.flag = 1; $$.commands = $2; cmd_parse_free_commands($3.commands); } else if ($3.flag) { $$.flag = 1; $$.commands = $3.commands; cmd_parse_free_commands($2); } else { $$.flag = 0; $$.commands = cmd_parse_new_commands(); cmd_parse_free_commands($2); cmd_parse_free_commands($3.commands); } } arguments : argument { $$.argc = 1; $$.argv = xreallocarray(NULL, 1, sizeof *$$.argv); $$.argv[0] = $1; } | argument arguments { cmd_prepend_argv(&$2.argc, &$2.argv, $1); free($1); $$ = $2; } argument : TOKEN { $$ = $1; } | EQUALS { $$ = $1; } %% static char * cmd_parse_get_error(const char *file, u_int line, const char *error) { char *s; if (file == NULL) s = xstrdup(error); else xasprintf (&s, "%s:%u: %s", file, line, error); return (s); } static void cmd_parse_print_commands(struct cmd_parse_input *pi, u_int line, struct cmd_list *cmdlist) { char *s; if (pi->item != NULL && (pi->flags & CMD_PARSE_VERBOSE)) { s = cmd_list_print(cmdlist, 0); if (pi->file != NULL) cmdq_print(pi->item, "%s:%u: %s", pi->file, line, s); else cmdq_print(pi->item, "%u: %s", line, s); free(s); } } static void cmd_parse_free_command(struct cmd_parse_command *cmd) { free(cmd->name); cmd_free_argv(cmd->argc, cmd->argv); free(cmd); } static struct cmd_parse_commands * cmd_parse_new_commands(void) { struct cmd_parse_commands *cmds; cmds = xmalloc(sizeof *cmds); TAILQ_INIT (cmds); return (cmds); } static void cmd_parse_free_commands(struct cmd_parse_commands *cmds) { struct cmd_parse_command *cmd, *cmd1; TAILQ_FOREACH_SAFE(cmd, cmds, entry, cmd1) { TAILQ_REMOVE(cmds, cmd, entry); cmd_parse_free_command(cmd); } free(cmds); } static struct cmd_parse_commands * cmd_parse_run_parser(char **cause) { struct cmd_parse_state *ps = &parse_state; struct cmd_parse_scope *scope, *scope1; int retval; ps->commands = NULL; TAILQ_INIT(&ps->stack); retval = yyparse(); TAILQ_FOREACH_SAFE(scope, &ps->stack, entry, scope1) { TAILQ_REMOVE(&ps->stack, scope, entry); free(scope); } if (retval != 0) { *cause = ps->error; return (NULL); } if (ps->commands == NULL) return (cmd_parse_new_commands()); return (ps->commands); } static struct cmd_parse_commands * cmd_parse_do_file(FILE *f, struct cmd_parse_input *pi, char **cause) { struct cmd_parse_state *ps = &parse_state; memset(ps, 0, sizeof *ps); ps->input = pi; ps->f = f; return (cmd_parse_run_parser(cause)); } static struct cmd_parse_commands * cmd_parse_do_buffer(const char *buf, size_t len, struct cmd_parse_input *pi, char **cause) { struct cmd_parse_state *ps = &parse_state; memset(ps, 0, sizeof *ps); ps->input = pi; ps->buf = buf; ps->len = len; return (cmd_parse_run_parser(cause)); } static struct cmd_parse_result * cmd_parse_build_commands(struct cmd_parse_commands *cmds, struct cmd_parse_input *pi) { static struct cmd_parse_result pr; struct cmd_parse_commands *cmds2; struct cmd_parse_command *cmd, *cmd2, *next, *next2, *after; u_int line = UINT_MAX; int i; struct cmd_list *cmdlist = NULL, *result; struct cmd *add; char *alias, *cause, *s; /* Check for an empty list. */ if (TAILQ_EMPTY(cmds)) { cmd_parse_free_commands(cmds); pr.status = CMD_PARSE_EMPTY; return (&pr); } /* * Walk the commands and expand any aliases. Each alias is parsed * individually to a new command list, any trailing arguments appended * to the last command, and all commands inserted into the original * command list. */ TAILQ_FOREACH_SAFE(cmd, cmds, entry, next) { alias = cmd_get_alias(cmd->name); if (alias == NULL) continue; line = cmd->line; log_debug("%s: %u %s = %s", __func__, line, cmd->name, alias); pi->line = line; cmds2 = cmd_parse_do_buffer(alias, strlen(alias), pi, &cause); free(alias); if (cmds2 == NULL) { pr.status = CMD_PARSE_ERROR; pr.error = cause; goto out; } cmd2 = TAILQ_LAST(cmds2, cmd_parse_commands); if (cmd2 == NULL) { TAILQ_REMOVE(cmds, cmd, entry); cmd_parse_free_command(cmd); continue; } for (i = 0; i < cmd->argc; i++) cmd_append_argv(&cmd2->argc, &cmd2->argv, cmd->argv[i]); after = cmd; TAILQ_FOREACH_SAFE(cmd2, cmds2, entry, next2) { cmd2->line = line; TAILQ_REMOVE(cmds2, cmd2, entry); TAILQ_INSERT_AFTER(cmds, after, cmd2, entry); after = cmd2; } cmd_parse_free_commands(cmds2); TAILQ_REMOVE(cmds, cmd, entry); cmd_parse_free_command(cmd); } /* * Parse each command into a command list. Create a new command list * for each line so they get a new group (so the queue knows which ones * to remove if a command fails when executed). */ result = cmd_list_new(); TAILQ_FOREACH(cmd, cmds, entry) { log_debug("%s: %u %s", __func__, cmd->line, cmd->name); cmd_log_argv(cmd->argc, cmd->argv, __func__); if (cmdlist == NULL || cmd->line != line) { if (cmdlist != NULL) { cmd_parse_print_commands(pi, line, cmdlist); cmd_list_move(result, cmdlist); cmd_list_free(cmdlist); } cmdlist = cmd_list_new(); } line = cmd->line; cmd_prepend_argv(&cmd->argc, &cmd->argv, cmd->name); add = cmd_parse(cmd->argc, cmd->argv, pi->file, line, &cause); if (add == NULL) { cmd_list_free(result); pr.status = CMD_PARSE_ERROR; pr.error = cmd_parse_get_error(pi->file, line, cause); free(cause); goto out; } cmd_list_append(cmdlist, add); } if (cmdlist != NULL) { cmd_parse_print_commands(pi, line, cmdlist); cmd_list_move(result, cmdlist); cmd_list_free(cmdlist); } s = cmd_list_print(result, 0); log_debug("%s: %s", __func__, s); free(s); pr.status = CMD_PARSE_SUCCESS; pr.cmdlist = result; out: cmd_parse_free_commands(cmds); return (&pr); } struct cmd_parse_result * cmd_parse_from_file(FILE *f, struct cmd_parse_input *pi) { static struct cmd_parse_result pr; struct cmd_parse_input input; struct cmd_parse_commands *cmds; char *cause; if (pi == NULL) { memset(&input, 0, sizeof input); pi = &input; } memset(&pr, 0, sizeof pr); cmds = cmd_parse_do_file(f, pi, &cause); if (cmds == NULL) { pr.status = CMD_PARSE_ERROR; pr.error = cause; return (&pr); } return (cmd_parse_build_commands(cmds, pi)); } struct cmd_parse_result * cmd_parse_from_string(const char *s, struct cmd_parse_input *pi) { static struct cmd_parse_result pr; struct cmd_parse_input input; struct cmd_parse_commands *cmds; char *cause; if (pi == NULL) { memset(&input, 0, sizeof input); pi = &input; } memset(&pr, 0, sizeof pr); if (*s == '\0') { pr.status = CMD_PARSE_EMPTY; pr.cmdlist = NULL; pr.error = NULL; return (&pr); } cmds = cmd_parse_do_buffer(s, strlen(s), pi, &cause); if (cmds == NULL) { pr.status = CMD_PARSE_ERROR; pr.error = cause; return (&pr); } return (cmd_parse_build_commands(cmds, pi)); } struct cmd_parse_result * cmd_parse_from_arguments(int argc, char **argv, struct cmd_parse_input *pi) { struct cmd_parse_input input; struct cmd_parse_commands *cmds; struct cmd_parse_command *cmd; char **copy, **new_argv; size_t size; int i, last, new_argc; /* * The commands are already split up into arguments, so just separate * into a set of commands by ';'. */ if (pi == NULL) { memset(&input, 0, sizeof input); pi = &input; } cmd_log_argv(argc, argv, "%s", __func__); cmds = cmd_parse_new_commands(); copy = cmd_copy_argv(argc, argv); last = 0; for (i = 0; i < argc; i++) { size = strlen(copy[i]); if (size == 0 || copy[i][size - 1] != ';') continue; copy[i][--size] = '\0'; if (size > 0 && copy[i][size - 1] == '\\') { copy[i][size - 1] = ';'; continue; } new_argc = i - last; new_argv = copy + last; if (size != 0) new_argc++; if (new_argc != 0) { cmd_log_argv(new_argc, new_argv, "%s: at %u", __func__, i); cmd = xcalloc(1, sizeof *cmd); cmd->name = xstrdup(new_argv[0]); cmd->line = pi->line; cmd->argc = new_argc - 1; cmd->argv = cmd_copy_argv(new_argc - 1, new_argv + 1); TAILQ_INSERT_TAIL(cmds, cmd, entry); } last = i + 1; } if (last != argc) { new_argv = copy + last; new_argc = argc - last; if (new_argc != 0) { cmd_log_argv(new_argc, new_argv, "%s: at %u", __func__, last); cmd = xcalloc(1, sizeof *cmd); cmd->name = xstrdup(new_argv[0]); cmd->line = pi->line; cmd->argc = new_argc - 1; cmd->argv = cmd_copy_argv(new_argc - 1, new_argv + 1); TAILQ_INSERT_TAIL(cmds, cmd, entry); } } cmd_free_argv(argc, copy); return (cmd_parse_build_commands(cmds, pi)); } static int printflike(1, 2) yyerror(const char *fmt, ...) { struct cmd_parse_state *ps = &parse_state; struct cmd_parse_input *pi = ps->input; va_list ap; char *error; if (ps->error != NULL) return (0); va_start(ap, fmt); xvasprintf(&error, fmt, ap); va_end(ap); ps->error = cmd_parse_get_error(pi->file, pi->line, error); free(error); return (0); } static int yylex_is_var(char ch, int first) { if (ch == '=') return (0); if (first && isdigit((u_char)ch)) return (0); return (isalnum((u_char)ch) || ch == '_'); } static void yylex_append(char **buf, size_t *len, const char *add, size_t addlen) { if (addlen > SIZE_MAX - 1 || *len > SIZE_MAX - 1 - addlen) fatalx("buffer is too big"); *buf = xrealloc(*buf, (*len) + 1 + addlen); memcpy((*buf) + *len, add, addlen); (*len) += addlen; } static void yylex_append1(char **buf, size_t *len, char add) { yylex_append(buf, len, &add, 1); } static int yylex_getc1(void) { struct cmd_parse_state *ps = &parse_state; int ch; if (ps->f != NULL) ch = getc(ps->f); else { if (ps->off == ps->len) ch = EOF; else ch = ps->buf[ps->off++]; } return (ch); } static void yylex_ungetc(int ch) { struct cmd_parse_state *ps = &parse_state; if (ps->f != NULL) ungetc(ch, ps->f); else if (ps->off > 0 && ch != EOF) ps->off--; } static int yylex_getc(void) { struct cmd_parse_state *ps = &parse_state; int ch; if (ps->escapes != 0) { ps->escapes--; return ('\\'); } for (;;) { ch = yylex_getc1(); if (ch == '\\') { ps->escapes++; continue; } if (ch == '\n' && (ps->escapes % 2) == 1) { ps->input->line++; ps->escapes--; continue; } if (ps->escapes != 0) { yylex_ungetc(ch); ps->escapes--; return ('\\'); } return (ch); } } static char * yylex_get_word(int ch) { char *buf; size_t len; len = 0; buf = xmalloc(1); do yylex_append1(&buf, &len, ch); while ((ch = yylex_getc()) != EOF && strchr(" \t\n", ch) == NULL); yylex_ungetc(ch); buf[len] = '\0'; log_debug("%s: %s", __func__, buf); return (buf); } static int yylex(void) { struct cmd_parse_state *ps = &parse_state; char *token, *cp; int ch, next, condition; if (ps->eol) ps->input->line++; ps->eol = 0; condition = ps->condition; ps->condition = 0; for (;;) { ch = yylex_getc(); if (ch == EOF) { /* * Ensure every file or string is terminated by a * newline. This keeps the parser simpler and avoids * having to add a newline to each string. */ if (ps->eof) break; ps->eof = 1; return ('\n'); } if (ch == ' ' || ch == '\t') { /* * Ignore whitespace. */ continue; } if (ch == '\n') { /* * End of line. Update the line number. */ ps->eol = 1; return ('\n'); } if (ch == ';') { /* * A semicolon is itself. */ return (';'); } if (ch == '#') { /* * #{ after a condition opens a format; anything else * is a comment, ignore up to the end of the line. */ next = yylex_getc(); if (condition && next == '{') { yylval.token = yylex_format(); if (yylval.token == NULL) return (ERROR); return (FORMAT); } while (next != '\n' && next != EOF) next = yylex_getc(); if (next == '\n') { ps->input->line++; return ('\n'); } continue; } if (ch == '%') { /* * % is a condition unless it is all % or all numbers, * then it is a token. */ yylval.token = yylex_get_word('%'); for (cp = yylval.token; *cp != '\0'; cp++) { if (*cp != '%' && !isdigit((u_char)*cp)) break; } if (*cp == '\0') return (TOKEN); ps->condition = 1; if (strcmp(yylval.token, "%if") == 0) { free(yylval.token); return (IF); } if (strcmp(yylval.token, "%else") == 0) { free(yylval.token); return (ELSE); } if (strcmp(yylval.token, "%elif") == 0) { free(yylval.token); return (ELIF); } if (strcmp(yylval.token, "%endif") == 0) { free(yylval.token); return (ENDIF); } free(yylval.token); return (ERROR); } /* * Otherwise this is a token. */ token = yylex_token(ch); if (token == NULL) return (ERROR); yylval.token = token; if (strchr(token, '=') != NULL && yylex_is_var(*token, 1)) { for (cp = token + 1; *cp != '='; cp++) { if (!yylex_is_var(*cp, 0)) break; } if (*cp == '=') return (EQUALS); } return (TOKEN); } return (0); } static char * yylex_format(void) { char *buf; size_t len; int ch, brackets = 1; len = 0; buf = xmalloc(1); yylex_append(&buf, &len, "#{", 2); for (;;) { if ((ch = yylex_getc()) == EOF || ch == '\n') goto error; if (ch == '#') { if ((ch = yylex_getc()) == EOF || ch == '\n') goto error; if (ch == '{') brackets++; yylex_append1(&buf, &len, '#'); } else if (ch == '}') { if (brackets != 0 && --brackets == 0) { yylex_append1(&buf, &len, ch); break; } } yylex_append1(&buf, &len, ch); } if (brackets != 0) goto error; buf[len] = '\0'; log_debug("%s: %s", __func__, buf); return (buf); error: free(buf); return (NULL); } static int yylex_token_escape(char **buf, size_t *len) { int ch, type, o2, o3; u_int size, i, tmp; char s[9]; struct utf8_data ud; ch = yylex_getc(); if (ch >= '4' && ch <= '7') { yyerror("invalid octal escape"); return (0); } if (ch >= '0' && ch <= '3') { o2 = yylex_getc(); if (o2 >= '0' && o2 <= '7') { o3 = yylex_getc(); if (o3 >= '0' && o3 <= '7') { ch = 64 * (ch - '0') + 8 * (o2 - '0') + (o3 - '0'); yylex_append1(buf, len, ch); return (1); } } yyerror("invalid octal escape"); return (0); } switch (ch) { case EOF: return (0); case 'a': ch = '\a'; break; case 'b': ch = '\b'; break; case 'e': ch = '\033'; break; case 'f': ch = '\f'; break; case 's': ch = ' '; break; case 'v': ch = '\v'; break; case 'r': ch = '\r'; break; case 'n': ch = '\n'; break; case 't': ch = '\t'; break; case 'u': type = 'u'; size = 4; goto unicode; case 'U': type = 'U'; size = 8; goto unicode; } yylex_append1(buf, len, ch); return (1); unicode: for (i = 0; i < size; i++) { ch = yylex_getc(); if (ch == EOF || ch == '\n') return (0); if (!isxdigit((u_char)ch)) { yyerror("invalid \\%c argument", type); return (0); } s[i] = ch; } s[i] = '\0'; if ((size == 4 && sscanf(s, "%4x", &tmp) != 1) || (size == 8 && sscanf(s, "%8x", &tmp) != 1)) { yyerror("invalid \\%c argument", type); return (0); } if (utf8_split(tmp, &ud) != UTF8_DONE) { yyerror("invalid \\%c argument", type); return (0); } yylex_append(buf, len, ud.data, ud.size); return (1); } static int yylex_token_variable(char **buf, size_t *len) { struct environ_entry *envent; int ch, brackets = 0; char name[1024]; size_t namelen = 0; const char *value; ch = yylex_getc(); if (ch == EOF) return (0); if (ch == '{') brackets = 1; else { if (!yylex_is_var(ch, 1)) { yylex_append1(buf, len, '$'); yylex_ungetc(ch); return (1); } name[namelen++] = ch; } for (;;) { ch = yylex_getc(); if (brackets && ch == '}') break; if (ch == EOF || !yylex_is_var(ch, 0)) { if (!brackets) { yylex_ungetc(ch); break; } yyerror("invalid environment variable"); return (0); } if (namelen == (sizeof name) - 2) { yyerror("environment variable is too long"); return (0); } name[namelen++] = ch; } name[namelen] = '\0'; envent = environ_find(global_environ, name); if (envent != NULL) { value = envent->value; log_debug("%s: %s -> %s", __func__, name, value); yylex_append(buf, len, value, strlen(value)); } return (1); } static int yylex_token_tilde(char **buf, size_t *len) { struct environ_entry *envent; int ch; char name[1024]; size_t namelen = 0; struct passwd *pw; const char *home = NULL; for (;;) { ch = yylex_getc(); if (ch == EOF || strchr("/ \t\n\"'", ch) != NULL) { yylex_ungetc(ch); break; } if (namelen == (sizeof name) - 2) { yyerror("user name is too long"); return (0); } name[namelen++] = ch; } name[namelen] = '\0'; if (*name == '\0') { 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 { if ((pw = getpwnam(name)) != NULL) home = pw->pw_dir; } if (home == NULL) return (0); log_debug("%s: ~%s -> %s", __func__, name, home); yylex_append(buf, len, home, strlen(home)); return (1); } static int yylex_token_brace(char **buf, size_t *len) { struct cmd_parse_state *ps = &parse_state; int ch, lines = 0, nesting = 1, escape = 0; int quote = '\0', token = 0; /* * Extract a string up to the matching unquoted '}', including newlines * and handling nested braces. * * To detect the final and intermediate braces which affect the nesting * depth, we scan the input as if it was a tmux config file, and ignore * braces which would be considered quoted, escaped, or in a comment. * * We update the token state after every character because '#' begins a * comment only when it begins a token. For simplicity, we treat an * unquoted directive format as comment. * * The result is verbatim copy of the input excluding the final brace. */ for (ch = yylex_getc1(); ch != EOF; ch = yylex_getc1()) { yylex_append1(buf, len, ch); if (ch == '\n') lines++; /* * If the previous character was a backslash (escape is set), * escape anything if unquoted or in double quotes, otherwise * escape only '\n' and '\\'. */ if (escape && (quote == '\0' || quote == '"' || ch == '\n' || ch == '\\')) { escape = 0; if (ch != '\n') token = 1; continue; } /* * The character is not escaped. If it is a backslash, set the * escape flag. */ if (ch == '\\') { escape = 1; continue; } escape = 0; /* A newline always resets to unquoted. */ if (ch == '\n') { quote = token = 0; continue; } if (quote) { /* * Inside quotes or comment. Check if this is the * closing quote. */ if (ch == quote && quote != '#') quote = 0; token = 1; /* token continues regardless */ } else { /* Not inside quotes or comment. */ switch (ch) { case '"': case '\'': case '#': /* Beginning of quote or maybe comment. */ if (ch != '#' || !token) quote = ch; token = 1; break; case ' ': case '\t': case ';': /* Delimiter - token resets. */ token = 0; break; case '{': nesting++; token = 0; /* new commands set - token resets */ break; case '}': nesting--; token = 1; /* same as after quotes */ if (nesting == 0) { (*len)--; /* remove closing } */ ps->input->line += lines; return (1); } break; default: token = 1; break; } } } /* * Update line count after error as reporting the opening line is more * useful than EOF. */ yyerror("unterminated brace string"); ps->input->line += lines; return (0); } static char * yylex_token(int ch) { char *buf; size_t len; enum { START, NONE, DOUBLE_QUOTES, SINGLE_QUOTES } state = NONE, last = START; len = 0; buf = xmalloc(1); for (;;) { /* * EOF or \n are always the end of the token. If inside quotes * they are an error. */ if (ch == EOF || ch == '\n') { if (state != NONE) goto error; break; } /* Whitespace or ; ends a token unless inside quotes. */ if ((ch == ' ' || ch == '\t' || ch == ';') && state == NONE) break; /* * \ ~ and $ are expanded except in single quotes. */ if (ch == '\\' && state != SINGLE_QUOTES) { if (!yylex_token_escape(&buf, &len)) goto error; goto skip; } if (ch == '~' && last != state && state != SINGLE_QUOTES) { if (!yylex_token_tilde(&buf, &len)) goto error; goto skip; } if (ch == '$' && state != SINGLE_QUOTES) { if (!yylex_token_variable(&buf, &len)) goto error; goto skip; } if (ch == '{' && state == NONE) { if (!yylex_token_brace(&buf, &len)) goto error; goto skip; } if (ch == '}' && state == NONE) goto error; /* unmatched (matched ones were handled) */ /* * ' and " starts or end quotes (and is consumed). */ if (ch == '\'') { if (state == NONE) { state = SINGLE_QUOTES; goto next; } if (state == SINGLE_QUOTES) { state = NONE; goto next; } } if (ch == '"') { if (state == NONE) { state = DOUBLE_QUOTES; goto next; } if (state == DOUBLE_QUOTES) { state = NONE; goto next; } } /* * Otherwise add the character to the buffer. */ yylex_append1(&buf, &len, ch); skip: last = state; next: ch = yylex_getc(); } yylex_ungetc(ch); buf[len] = '\0'; log_debug("%s: %s", __func__, buf); return (buf); error: free(buf); return (NULL); } tmux-3.0a/cmd-paste-buffer.c100644 001750 001750 00000005440 13466230030 0011504/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Paste paste buffer if present. */ static enum cmd_retval cmd_paste_buffer_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_paste_buffer_entry = { .name = "paste-buffer", .alias = "pasteb", .args = { "db:prs:t:", 0, 0 }, .usage = "[-dpr] [-s separator] " CMD_BUFFER_USAGE " " CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, .flags = CMD_AFTERHOOK, .exec = cmd_paste_buffer_exec }; static enum cmd_retval cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct window_pane *wp = item->target.wp; struct paste_buffer *pb; const char *sepstr, *bufname, *bufdata, *bufend, *line; size_t seplen, bufsize; int bracket = args_has(args, 'p'); bufname = NULL; if (args_has(args, 'b')) bufname = args_get(args, 'b'); if (bufname == NULL) pb = paste_get_top(NULL); else { pb = paste_get_name(bufname); if (pb == NULL) { cmdq_error(item, "no buffer %s", bufname); return (CMD_RETURN_ERROR); } } if (pb != NULL && ~wp->flags & PANE_INPUTOFF) { sepstr = args_get(args, 's'); if (sepstr == NULL) { if (args_has(args, 'r')) sepstr = "\n"; else sepstr = "\r"; } seplen = strlen(sepstr); if (bracket && (wp->screen->mode & MODE_BRACKETPASTE)) bufferevent_write(wp->event, "\033[200~", 6); bufdata = paste_buffer_data(pb, &bufsize); bufend = bufdata + bufsize; for (;;) { line = memchr(bufdata, '\n', bufend - bufdata); if (line == NULL) break; bufferevent_write(wp->event, bufdata, line - bufdata); bufferevent_write(wp->event, sepstr, seplen); bufdata = line + 1; } if (bufdata != bufend) bufferevent_write(wp->event, bufdata, bufend - bufdata); if (bracket && (wp->screen->mode & MODE_BRACKETPASTE)) bufferevent_write(wp->event, "\033[201~", 6); } if (pb != NULL && args_has(args, 'd')) paste_free(pb); return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-pipe-pane.c100644 001750 001750 00000013365 13466241542 0011016/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include "tmux.h" /* * Open pipe to redirect pane output. If already open, close first. */ static enum cmd_retval cmd_pipe_pane_exec(struct cmd *, struct cmdq_item *); static void cmd_pipe_pane_read_callback(struct bufferevent *, void *); static void cmd_pipe_pane_write_callback(struct bufferevent *, void *); static void cmd_pipe_pane_error_callback(struct bufferevent *, short, void *); const struct cmd_entry cmd_pipe_pane_entry = { .name = "pipe-pane", .alias = "pipep", .args = { "IOot:", 0, 1 }, .usage = "[-IOo] " CMD_TARGET_PANE_USAGE " [command]", .target = { 't', CMD_FIND_PANE, 0 }, .flags = CMD_AFTERHOOK, .exec = cmd_pipe_pane_exec }; static enum cmd_retval cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct client *c = cmd_find_client(item, NULL, 1); struct window_pane *wp = item->target.wp; struct session *s = item->target.s; struct winlink *wl = item->target.wl; char *cmd; int old_fd, pipe_fd[2], null_fd, in, out; struct format_tree *ft; sigset_t set, oldset; /* 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 (window_pane_destroy_ready(wp)) { server_destroy_pane(wp, 1); return (CMD_RETURN_NORMAL); } } /* If no pipe command, that is enough. */ if (args->argc == 0 || *args->argv[0] == '\0') return (CMD_RETURN_NORMAL); /* * 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 (CMD_RETURN_NORMAL); /* What do we want to do? Neither -I or -O is -O. */ if (args_has(self->args, 'I')) { in = 1; out = args_has(self->args, 'O'); } else { in = 0; out = 1; } /* Open the new pipe. */ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_fd) != 0) { cmdq_error(item, "socketpair error: %s", strerror(errno)); return (CMD_RETURN_ERROR); } /* Expand the command. */ ft = format_create(item->client, item, FORMAT_NONE, 0); format_defaults(ft, c, s, wl, wp); cmd = format_expand_time(ft, args->argv[0]); format_free(ft); /* Fork the child. */ sigfillset(&set); sigprocmask(SIG_BLOCK, &set, &oldset); switch (fork()) { case -1: sigprocmask(SIG_SETMASK, &oldset, NULL); cmdq_error(item, "fork error: %s", strerror(errno)); free(cmd); return (CMD_RETURN_ERROR); case 0: /* Child process. */ proc_clear_signals(server_proc, 1); sigprocmask(SIG_SETMASK, &oldset, NULL); close(pipe_fd[0]); null_fd = open(_PATH_DEVNULL, O_WRONLY, 0); if (out) { if (dup2(pipe_fd[1], STDIN_FILENO) == -1) _exit(1); } else { if (dup2(null_fd, STDIN_FILENO) == -1) _exit(1); } if (in) { if (dup2(pipe_fd[1], STDOUT_FILENO) == -1) _exit(1); if (pipe_fd[1] != STDOUT_FILENO) close(pipe_fd[1]); } else { if (dup2(null_fd, STDOUT_FILENO) == -1) _exit(1); } if (dup2(null_fd, STDERR_FILENO) == -1) _exit(1); closefrom(STDERR_FILENO + 1); execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL); _exit(1); default: /* Parent process. */ sigprocmask(SIG_SETMASK, &oldset, NULL); close(pipe_fd[1]); wp->pipe_fd = pipe_fd[0]; if (wp->fd != -1) wp->pipe_off = EVBUFFER_LENGTH(wp->event->input); else wp->pipe_off = 0; setblocking(wp->pipe_fd, 0); wp->pipe_event = bufferevent_new(wp->pipe_fd, cmd_pipe_pane_read_callback, cmd_pipe_pane_write_callback, cmd_pipe_pane_error_callback, wp); if (wp->pipe_event == NULL) fatalx("out of memory"); if (out) bufferevent_enable(wp->pipe_event, EV_WRITE); if (in) bufferevent_enable(wp->pipe_event, EV_READ); free(cmd); return (CMD_RETURN_NORMAL); } } static void cmd_pipe_pane_read_callback(__unused struct bufferevent *bufev, void *data) { struct window_pane *wp = data; struct evbuffer *evb = wp->pipe_event->input; size_t available; available = EVBUFFER_LENGTH(evb); log_debug("%%%u pipe read %zu", wp->id, available); bufferevent_write(wp->event, EVBUFFER_DATA(evb), available); evbuffer_drain(evb, available); if (window_pane_destroy_ready(wp)) server_destroy_pane(wp, 1); } static void cmd_pipe_pane_write_callback(__unused struct bufferevent *bufev, void *data) { struct window_pane *wp = data; log_debug("%%%u pipe empty", wp->id); if (window_pane_destroy_ready(wp)) server_destroy_pane(wp, 1); } static void cmd_pipe_pane_error_callback(__unused struct bufferevent *bufev, __unused short what, void *data) { struct window_pane *wp = data; log_debug("%%%u pipe error", wp->id); bufferevent_free(wp->pipe_event); close(wp->pipe_fd); wp->pipe_fd = -1; if (window_pane_destroy_ready(wp)) server_destroy_pane(wp, 1); } tmux-3.0a/cmd-queue.c100644 001750 001750 00000030701 13570677043 0010262/* $OpenBSD$ */ /* * Copyright (c) 2013 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" /* Global command queue. */ static struct cmdq_list global_queue = TAILQ_HEAD_INITIALIZER(global_queue); /* Get command queue name. */ static const char * cmdq_name(struct client *c) { static char s[256]; if (c == NULL) return (""); if (c->name != NULL) xsnprintf(s, sizeof s, "<%s>", c->name); else xsnprintf(s, sizeof s, "<%p>", c); return (s); } /* Get command queue from client. */ static struct cmdq_list * cmdq_get(struct client *c) { if (c == NULL) return (&global_queue); return (&c->queue); } /* Append an item. */ void cmdq_append(struct client *c, struct cmdq_item *item) { struct cmdq_list *queue = cmdq_get(c); struct cmdq_item *next; do { next = item->next; item->next = NULL; if (c != NULL) c->references++; item->client = c; item->queue = queue; TAILQ_INSERT_TAIL(queue, item, entry); log_debug("%s %s: %s", __func__, cmdq_name(c), item->name); item = next; } while (item != NULL); } /* Insert an item. */ void cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item) { struct client *c = after->client; struct cmdq_list *queue = after->queue; struct cmdq_item *next; do { next = item->next; item->next = after->next; after->next = item; if (c != NULL) c->references++; item->client = c; item->queue = queue; TAILQ_INSERT_AFTER(queue, after, item, entry); log_debug("%s %s: %s after %s", __func__, cmdq_name(c), item->name, after->name); after = item; item = next; } while (item != NULL); } /* Insert a hook. */ void cmdq_insert_hook(struct session *s, struct cmdq_item *item, struct cmd_find_state *fs, const char *fmt, ...) { struct options *oo; va_list ap; char *name; struct cmdq_item *new_item; struct options_entry *o; struct options_array_item *a; struct cmd_list *cmdlist; if (item->flags & CMDQ_NOHOOKS) return; if (s == NULL) oo = global_s_options; else oo = s->options; va_start(ap, fmt); xvasprintf(&name, fmt, ap); va_end(ap); o = options_get(oo, name); if (o == NULL) { free(name); return; } log_debug("running hook %s (parent %p)", name, item); a = options_array_first(o); while (a != NULL) { cmdlist = options_array_item_value(a)->cmdlist; if (cmdlist == NULL) { a = options_array_next(a); continue; } new_item = cmdq_get_command(cmdlist, fs, NULL, CMDQ_NOHOOKS); cmdq_format(new_item, "hook", "%s", name); if (item != NULL) { cmdq_insert_after(item, new_item); item = new_item; } else cmdq_append(NULL, new_item); a = options_array_next(a); } free(name); } /* Continue processing command queue. */ void cmdq_continue(struct cmdq_item *item) { item->flags &= ~CMDQ_WAITING; } /* Remove an item. */ static void cmdq_remove(struct cmdq_item *item) { if (item->shared != NULL && --item->shared->references == 0) { if (item->shared->formats != NULL) format_free(item->shared->formats); free(item->shared); } if (item->client != NULL) server_client_unref(item->client); if (item->cmdlist != NULL) cmd_list_free(item->cmdlist); TAILQ_REMOVE(item->queue, item, entry); free(item->name); free(item); } /* Remove all subsequent items that match this item's group. */ static void cmdq_remove_group(struct cmdq_item *item) { struct cmdq_item *this, *next; if (item->group == 0) return; this = TAILQ_NEXT(item, entry); while (this != NULL) { next = TAILQ_NEXT(this, entry); if (this->group == item->group) cmdq_remove(this); this = next; } } /* Get a command for the command queue. */ struct cmdq_item * cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current, struct mouse_event *m, int flags) { struct cmdq_item *item, *first = NULL, *last = NULL; struct cmd *cmd; struct cmdq_shared *shared = NULL; u_int group = 0; TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { if (cmd->group != group) { shared = xcalloc(1, sizeof *shared); if (current != NULL) cmd_find_copy_state(&shared->current, current); else cmd_find_clear_state(&shared->current, 0); if (m != NULL) memcpy(&shared->mouse, m, sizeof shared->mouse); group = cmd->group; } item = xcalloc(1, sizeof *item); xasprintf(&item->name, "[%s/%p]", cmd->entry->name, item); item->type = CMDQ_COMMAND; item->group = cmd->group; item->flags = flags; item->shared = shared; item->cmdlist = cmdlist; item->cmd = cmd; log_debug("%s: %s group %u", __func__, item->name, item->group); shared->references++; cmdlist->references++; if (first == NULL) first = item; if (last != NULL) last->next = item; last = item; } return (first); } /* Fill in flag for a command. */ static enum cmd_retval cmdq_find_flag(struct cmdq_item *item, struct cmd_find_state *fs, const struct cmd_entry_flag *flag) { const char *value; if (flag->flag == 0) { cmd_find_clear_state(fs, 0); return (CMD_RETURN_NORMAL); } value = args_get(item->cmd->args, flag->flag); if (cmd_find_target(fs, item, value, flag->type, flag->flags) != 0) { cmd_find_clear_state(fs, 0); return (CMD_RETURN_ERROR); } return (CMD_RETURN_NORMAL); } /* Fire command on command queue. */ static enum cmd_retval cmdq_fire_command(struct cmdq_item *item) { struct client *c = item->client; const char *name = cmdq_name(c); struct cmdq_shared *shared = item->shared; struct cmd *cmd = item->cmd; const struct cmd_entry *entry = cmd->entry; enum cmd_retval retval; struct cmd_find_state *fsp, fs; int flags; char *tmp; if (log_get_level() > 1) { tmp = cmd_print(cmd); log_debug("%s %s: (%u) %s", __func__, name, item->group, tmp); free(tmp); } flags = !!(shared->flags & CMDQ_SHARED_CONTROL); cmdq_guard(item, "begin", flags); if (item->client == NULL) item->client = cmd_find_client(item, NULL, 1); retval = cmdq_find_flag(item, &item->source, &entry->source); if (retval == CMD_RETURN_ERROR) goto out; retval = cmdq_find_flag(item, &item->target, &entry->target); if (retval == CMD_RETURN_ERROR) goto out; retval = entry->exec(cmd, item); if (retval == CMD_RETURN_ERROR) goto out; if (entry->flags & CMD_AFTERHOOK) { if (cmd_find_valid_state(&item->target)) fsp = &item->target; else if (cmd_find_valid_state(&item->shared->current)) fsp = &item->shared->current; else if (cmd_find_from_client(&fs, item->client, 0) == 0) fsp = &fs; else goto out; cmdq_insert_hook(fsp->s, item, fsp, "after-%s", entry->name); } out: item->client = c; if (retval == CMD_RETURN_ERROR) cmdq_guard(item, "error", flags); else cmdq_guard(item, "end", flags); return (retval); } /* Get a callback for the command queue. */ struct cmdq_item * cmdq_get_callback1(const char *name, cmdq_cb cb, void *data) { struct cmdq_item *item; item = xcalloc(1, sizeof *item); xasprintf(&item->name, "[%s/%p]", name, item); item->type = CMDQ_CALLBACK; item->group = 0; item->flags = 0; item->cb = cb; item->data = data; return (item); } /* Generic error callback. */ static enum cmd_retval cmdq_error_callback(struct cmdq_item *item, void *data) { char *error = data; cmdq_error(item, "%s", error); free(error); return (CMD_RETURN_NORMAL); } /* Get an error callback for the command queue. */ struct cmdq_item * cmdq_get_error(const char *error) { return (cmdq_get_callback(cmdq_error_callback, xstrdup(error))); } /* Fire callback on callback queue. */ static enum cmd_retval cmdq_fire_callback(struct cmdq_item *item) { return (item->cb(item, item->data)); } /* Add a format to command queue. */ void cmdq_format(struct cmdq_item *item, const char *key, const char *fmt, ...) { struct cmdq_shared *shared = item->shared; va_list ap; char *value; va_start(ap, fmt); xvasprintf(&value, fmt, ap); va_end(ap); if (shared->formats == NULL) shared->formats = format_create(NULL, NULL, FORMAT_NONE, 0); format_add(shared->formats, key, "%s", value); free(value); } /* Process next item on command queue. */ u_int cmdq_next(struct client *c) { struct cmdq_list *queue = cmdq_get(c); const char *name = cmdq_name(c); struct cmdq_item *item; enum cmd_retval retval; u_int items = 0; static u_int number; if (TAILQ_EMPTY(queue)) { log_debug("%s %s: empty", __func__, name); return (0); } if (TAILQ_FIRST(queue)->flags & CMDQ_WAITING) { log_debug("%s %s: waiting", __func__, name); return (0); } log_debug("%s %s: enter", __func__, name); for (;;) { item = TAILQ_FIRST(queue); if (item == NULL) break; log_debug("%s %s: %s (%d), flags %x", __func__, name, item->name, item->type, item->flags); /* * Any item with the waiting flag set waits until an external * event clears the flag (for example, a job - look at * run-shell). */ if (item->flags & CMDQ_WAITING) goto waiting; /* * Items are only fired once, once the fired flag is set, a * waiting flag can only be cleared by an external event. */ if (~item->flags & CMDQ_FIRED) { item->time = time(NULL); item->number = ++number; switch (item->type) { case CMDQ_COMMAND: retval = cmdq_fire_command(item); /* * If a command returns an error, remove any * subsequent commands in the same group. */ if (retval == CMD_RETURN_ERROR) cmdq_remove_group(item); break; case CMDQ_CALLBACK: retval = cmdq_fire_callback(item); break; default: retval = CMD_RETURN_ERROR; break; } item->flags |= CMDQ_FIRED; if (retval == CMD_RETURN_WAIT) { item->flags |= CMDQ_WAITING; goto waiting; } items++; } cmdq_remove(item); } log_debug("%s %s: exit (empty)", __func__, name); return (items); waiting: log_debug("%s %s: exit (wait)", __func__, name); return (items); } /* Print a guard line. */ void cmdq_guard(struct cmdq_item *item, const char *guard, int flags) { struct client *c = item->client; if (c == NULL || !(c->flags & CLIENT_CONTROL)) return; evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard, (long)item->time, item->number, flags); server_client_push_stdout(c); } /* Show message from command. */ void cmdq_print(struct cmdq_item *item, const char *fmt, ...) { struct client *c = item->client; struct window_pane *wp; struct window_mode_entry *wme; va_list ap; char *tmp, *msg; va_start(ap, fmt); if (c == NULL) /* nothing */; else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { if (~c->flags & CLIENT_UTF8) { xvasprintf(&tmp, fmt, ap); msg = utf8_sanitize(tmp); free(tmp); evbuffer_add(c->stdout_data, msg, strlen(msg)); free(msg); } else evbuffer_add_vprintf(c->stdout_data, fmt, ap); evbuffer_add(c->stdout_data, "\n", 1); server_client_push_stdout(c); } else { wp = c->session->curw->window->active; wme = TAILQ_FIRST(&wp->modes); if (wme == NULL || wme->mode != &window_view_mode) window_pane_set_mode(wp, &window_view_mode, NULL, NULL); window_copy_vadd(wp, fmt, ap); } va_end(ap); } /* Show error from command. */ void cmdq_error(struct cmdq_item *item, const char *fmt, ...) { struct client *c = item->client; struct cmd *cmd = item->cmd; va_list ap; char *msg; size_t msglen; char *tmp; va_start(ap, fmt); msglen = xvasprintf(&msg, fmt, ap); va_end(ap); log_debug("%s: %s", __func__, msg); if (c == NULL) cfg_add_cause("%s:%u: %s", cmd->file, cmd->line, msg); else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { if (~c->flags & CLIENT_UTF8) { tmp = msg; msg = utf8_sanitize(tmp); free(tmp); msglen = strlen(msg); } evbuffer_add(c->stderr_data, msg, msglen); evbuffer_add(c->stderr_data, "\n", 1); server_client_push_stderr(c); c->retval = 1; } else { *msg = toupper((u_char) *msg); status_message_set(c, "%s", msg); } free(msg); } tmux-3.0a/cmd-refresh-client.c100644 001750 001750 00000010300 13570677043 0012041/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Refresh client. */ static enum cmd_retval cmd_refresh_client_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_refresh_client_entry = { .name = "refresh-client", .alias = "refresh", .args = { "cC:DF:lLRSt:U", 0, 1 }, .usage = "[-cDlLRSU] [-C XxY] [-F flags] " CMD_TARGET_CLIENT_USAGE " [adjustment]", .flags = CMD_AFTERHOOK, .exec = cmd_refresh_client_exec }; static enum cmd_retval cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct client *c; struct tty *tty; struct window *w; const char *size, *errstr; char *copy, *next, *s; u_int x, y, adjust; if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); tty = &c->tty; if (args_has(args, 'c') || args_has(args, 'L') || args_has(args, 'R') || args_has(args, 'U') || args_has(args, 'D')) { if (args->argc == 0) adjust = 1; else { adjust = strtonum(args->argv[0], 1, INT_MAX, &errstr); if (errstr != NULL) { cmdq_error(item, "adjustment %s", errstr); return (CMD_RETURN_ERROR); } } if (args_has(args, 'c')) c->pan_window = NULL; else { w = c->session->curw->window; if (c->pan_window != w) { c->pan_window = w; c->pan_ox = tty->oox; c->pan_oy = tty->ooy; } if (args_has(args, 'L')) { if (c->pan_ox > adjust) c->pan_ox -= adjust; else c->pan_ox = 0; } else if (args_has(args, 'R')) { c->pan_ox += adjust; if (c->pan_ox > w->sx - tty->osx) c->pan_ox = w->sx - tty->osx; } else if (args_has(args, 'U')) { if (c->pan_oy > adjust) c->pan_oy -= adjust; else c->pan_oy = 0; } else if (args_has(args, 'D')) { c->pan_oy += adjust; if (c->pan_oy > w->sy - tty->osy) c->pan_oy = w->sy - tty->osy; } } tty_update_client_offset(c); server_redraw_client(c); return (CMD_RETURN_NORMAL); } if (args_has(args, 'l')) { if (c->session != NULL) tty_putcode_ptr2(&c->tty, TTYC_MS, "", "?"); return (CMD_RETURN_NORMAL); } if (args_has(args, 'C') || args_has(args, 'F')) { if (args_has(args, 'C')) { if (!(c->flags & CLIENT_CONTROL)) { cmdq_error(item, "not a control client"); return (CMD_RETURN_ERROR); } size = args_get(args, 'C'); if (sscanf(size, "%u,%u", &x, &y) != 2 && sscanf(size, "%ux%u", &x, &y) != 2) { cmdq_error(item, "bad size argument"); return (CMD_RETURN_ERROR); } if (x < WINDOW_MINIMUM || x > WINDOW_MAXIMUM || y < WINDOW_MINIMUM || y > WINDOW_MAXIMUM) { cmdq_error(item, "size too small or too big"); return (CMD_RETURN_ERROR); } tty_set_size(&c->tty, x, y); c->flags |= CLIENT_SIZECHANGED; recalculate_sizes(); } if (args_has(args, 'F')) { if (!(c->flags & CLIENT_CONTROL)) { cmdq_error(item, "not a control client"); return (CMD_RETURN_ERROR); } s = copy = xstrdup(args_get(args, 'F')); while ((next = strsep(&s, ",")) != NULL) { /* Unknown flags are ignored. */ if (strcmp(next, "no-output") == 0) c->flags |= CLIENT_CONTROL_NOOUTPUT; } free(copy); } return (CMD_RETURN_NORMAL); } if (args_has(args, 'S')) { c->flags |= CLIENT_STATUSFORCE; server_status_client(c); } else { c->flags |= CLIENT_STATUSFORCE; server_redraw_client(c); } return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-rename-session.c100644 001750 001750 00000004217 13462323664 0012066/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Change session name. */ static enum cmd_retval cmd_rename_session_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_rename_session_entry = { .name = "rename-session", .alias = "rename", .args = { "t:", 1, 1 }, .usage = CMD_TARGET_SESSION_USAGE " new-name", .target = { 't', CMD_FIND_SESSION, 0 }, .flags = CMD_AFTERHOOK, .exec = cmd_rename_session_exec }; static enum cmd_retval cmd_rename_session_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct client *c = cmd_find_client(item, NULL, 1); struct session *s = item->target.s; char *newname; newname = format_single(item, args->argv[0], c, s, NULL, NULL); if (strcmp(newname, s->name) == 0) { free(newname); return (CMD_RETURN_NORMAL); } if (!session_check_name(newname)) { cmdq_error(item, "bad session name: %s", newname); free(newname); return (CMD_RETURN_ERROR); } if (session_find(newname) != NULL) { cmdq_error(item, "duplicate session: %s", newname); free(newname); return (CMD_RETURN_ERROR); } RB_REMOVE(sessions, &sessions, s); free(s->name); s->name = newname; RB_INSERT(sessions, &sessions, s); server_status_session(s); notify_session("session-renamed", s); return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-rename-window.c100644 001750 001750 00000003415 13462323664 0011711/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Rename a window. */ static enum cmd_retval cmd_rename_window_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_rename_window_entry = { .name = "rename-window", .alias = "renamew", .args = { "t:", 1, 1 }, .usage = CMD_TARGET_WINDOW_USAGE " new-name", .target = { 't', CMD_FIND_WINDOW, 0 }, .flags = CMD_AFTERHOOK, .exec = cmd_rename_window_exec }; static enum cmd_retval cmd_rename_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct client *c = cmd_find_client(item, NULL, 1); struct session *s = item->target.s; struct winlink *wl = item->target.wl; char *newname; newname = format_single(item, args->argv[0], c, s, wl, NULL); window_set_name(wl->window, newname); options_set_number(wl->window->options, "automatic-rename", 0); server_status_window(wl->window); free(newname); return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-resize-pane.c100644 001750 001750 00000011767 13570677043 0011373/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Increase or decrease pane size. */ static enum cmd_retval cmd_resize_pane_exec(struct cmd *, struct cmdq_item *); static void cmd_resize_pane_mouse_update(struct client *, struct mouse_event *); const struct cmd_entry cmd_resize_pane_entry = { .name = "resize-pane", .alias = "resizep", .args = { "DLMRt:Ux:y:Z", 0, 1 }, .usage = "[-DLMRUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE " " "[adjustment]", .target = { 't', CMD_FIND_PANE, 0 }, .flags = CMD_AFTERHOOK, .exec = cmd_resize_pane_exec }; static enum cmd_retval cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct cmdq_shared *shared = item->shared; struct window_pane *wp = item->target.wp; struct winlink *wl = item->target.wl; struct window *w = wl->window; struct client *c = item->client; struct session *s = item->target.s; const char *errstr; char *cause; u_int adjust; int x, y; if (args_has(args, 'M')) { if (cmd_mouse_window(&shared->mouse, &s) == NULL) return (CMD_RETURN_NORMAL); if (c == NULL || c->session != s) return (CMD_RETURN_NORMAL); c->tty.mouse_drag_update = cmd_resize_pane_mouse_update; cmd_resize_pane_mouse_update(c, &shared->mouse); return (CMD_RETURN_NORMAL); } if (args_has(args, 'Z')) { if (w->flags & WINDOW_ZOOMED) window_unzoom(w); else window_zoom(wp); server_redraw_window(w); server_status_window(w); return (CMD_RETURN_NORMAL); } server_unzoom_window(w); if (args->argc == 0) adjust = 1; else { adjust = strtonum(args->argv[0], 1, INT_MAX, &errstr); if (errstr != NULL) { cmdq_error(item, "adjustment %s", errstr); return (CMD_RETURN_ERROR); } } if (args_has(args, 'x')) { x = args_strtonum(args, 'x', PANE_MINIMUM, INT_MAX, &cause); if (cause != NULL) { cmdq_error(item, "width %s", cause); free(cause); return (CMD_RETURN_ERROR); } layout_resize_pane_to(wp, LAYOUT_LEFTRIGHT, x); } if (args_has(args, 'y')) { y = args_strtonum(args, 'y', PANE_MINIMUM, INT_MAX, &cause); if (cause != NULL) { cmdq_error(item, "height %s", cause); free(cause); return (CMD_RETURN_ERROR); } layout_resize_pane_to(wp, LAYOUT_TOPBOTTOM, y); } if (args_has(args, 'L')) layout_resize_pane(wp, LAYOUT_LEFTRIGHT, -adjust, 1); else if (args_has(args, 'R')) layout_resize_pane(wp, LAYOUT_LEFTRIGHT, adjust, 1); else if (args_has(args, 'U')) layout_resize_pane(wp, LAYOUT_TOPBOTTOM, -adjust, 1); else if (args_has(args, 'D')) layout_resize_pane(wp, LAYOUT_TOPBOTTOM, adjust, 1); server_redraw_window(wl->window); return (CMD_RETURN_NORMAL); } static void cmd_resize_pane_mouse_update(struct client *c, struct mouse_event *m) { struct winlink *wl; struct window *w; u_int y, ly, x, lx; static const int offsets[][2] = { { 0, 0 }, { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 }, }; struct layout_cell *cells[nitems(offsets)], *lc; u_int ncells = 0, i, j, resizes = 0; enum layout_type type; wl = cmd_mouse_window(m, NULL); if (wl == NULL) { c->tty.mouse_drag_update = NULL; return; } w = wl->window; y = m->y + m->oy; x = m->x + m->ox; if (m->statusat == 0 && y >= m->statuslines) y -= m->statuslines; else if (m->statusat > 0 && y >= (u_int)m->statusat) y = m->statusat - 1; ly = m->ly + m->oy; lx = m->lx + m->ox; if (m->statusat == 0 && ly >= m->statuslines) ly -= m->statuslines; else if (m->statusat > 0 && ly >= (u_int)m->statusat) ly = m->statusat - 1; for (i = 0; i < nitems(cells); i++) { lc = layout_search_by_border(w->layout_root, lx + offsets[i][0], ly + offsets[i][1]); if (lc == NULL) continue; for (j = 0; j < ncells; j++) { if (cells[j] == lc) { lc = NULL; break; } } if (lc == NULL) continue; cells[ncells] = lc; ncells++; } if (ncells == 0) return; for (i = 0; i < ncells; i++) { type = cells[i]->parent->type; if (y != ly && type == LAYOUT_TOPBOTTOM) { layout_resize_layout(w, cells[i], type, y - ly, 0); resizes++; } else if (x != lx && type == LAYOUT_LEFTRIGHT) { layout_resize_layout(w, cells[i], type, x - lx, 0); resizes++; } } if (resizes != 0) server_redraw_window(w); } tmux-3.0a/cmd-resize-window.c100644 001750 001750 00000005511 13570677043 0011745/* $OpenBSD$ */ /* * Copyright (c) 2018 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 window size. */ static enum cmd_retval cmd_resize_window_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_resize_window_entry = { .name = "resize-window", .alias = "resizew", .args = { "aADLRt:Ux:y:", 0, 1 }, .usage = "[-aADLRU] [-x width] [-y height] " CMD_TARGET_WINDOW_USAGE " " "[adjustment]", .target = { 't', CMD_FIND_WINDOW, 0 }, .flags = CMD_AFTERHOOK, .exec = cmd_resize_window_exec }; static enum cmd_retval cmd_resize_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct winlink *wl = item->target.wl; struct window *w = wl->window; struct session *s = item->target.s; const char *errstr; char *cause; u_int adjust, sx, sy; if (args->argc == 0) adjust = 1; else { adjust = strtonum(args->argv[0], 1, INT_MAX, &errstr); if (errstr != NULL) { cmdq_error(item, "adjustment %s", errstr); return (CMD_RETURN_ERROR); } } sx = w->sx; sy = w->sy; if (args_has(args, 'x')) { sx = args_strtonum(args, 'x', WINDOW_MINIMUM, WINDOW_MAXIMUM, &cause); if (cause != NULL) { cmdq_error(item, "width %s", cause); free(cause); return (CMD_RETURN_ERROR); } } if (args_has(args, 'y')) { sy = args_strtonum(args, 'y', WINDOW_MINIMUM, WINDOW_MAXIMUM, &cause); if (cause != NULL) { cmdq_error(item, "height %s", cause); free(cause); return (CMD_RETURN_ERROR); } } if (args_has(args, 'L')) { if (sx >= adjust) sx -= adjust; } else if (args_has(args, 'R')) sx += adjust; else if (args_has(args, 'U')) { if (sy >= adjust) sy -= adjust; } else if (args_has(args, 'D')) sy += adjust; if (args_has(args, 'A')) default_window_size(s, w, &sx, &sy, WINDOW_SIZE_LARGEST); else if (args_has(args, 'a')) default_window_size(s, w, &sx, &sy, WINDOW_SIZE_SMALLEST); options_set_number(w->options, "window-size", WINDOW_SIZE_MANUAL); resize_window(w, sx, sy); return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-respawn-pane.c100644 001750 001750 00000004706 13504653146 0011537/* $OpenBSD$ */ /* * 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 #include "tmux.h" /* * Respawn a pane (restart the command). Kill existing if -k given. */ static enum cmd_retval cmd_respawn_pane_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_respawn_pane_entry = { .name = "respawn-pane", .alias = "respawnp", .args = { "c:e:kt:", 0, -1 }, .usage = "[-k] [-c start-directory] [-e environment] " CMD_TARGET_PANE_USAGE " [command]", .target = { 't', CMD_FIND_PANE, 0 }, .flags = 0, .exec = cmd_respawn_pane_exec }; static enum cmd_retval cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct spawn_context sc; struct session *s = item->target.s; struct winlink *wl = item->target.wl; struct window_pane *wp = item->target.wp; char *cause = NULL; const char *add; struct args_value *value; memset(&sc, 0, sizeof sc); sc.item = item; sc.s = s; sc.wl = wl; sc.wp0 = wp; sc.lc = NULL; sc.name = NULL; sc.argc = args->argc; sc.argv = args->argv; sc.environ = environ_create(); add = args_first_value(args, 'e', &value); while (add != NULL) { environ_put(sc.environ, add); add = args_next_value(&value); } sc.idx = -1; sc.cwd = args_get(args, 'c'); sc.flags = SPAWN_RESPAWN; if (args_has(args, 'k')) sc.flags |= SPAWN_KILL; if (spawn_pane(&sc, &cause) == NULL) { cmdq_error(item, "respawn pane failed: %s", cause); free(cause); return (CMD_RETURN_ERROR); } wp->flags |= PANE_REDRAW; server_status_window(wp->window); environ_free(sc.environ); return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-respawn-window.c100644 001750 001750 00000004504 13570677043 0012124/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Respawn a window (restart the command). Kill existing if -k given. */ static enum cmd_retval cmd_respawn_window_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_respawn_window_entry = { .name = "respawn-window", .alias = "respawnw", .args = { "c:e:kt:", 0, -1 }, .usage = "[-k] [-c start-directory] [-e environment] " CMD_TARGET_WINDOW_USAGE " [command]", .target = { 't', CMD_FIND_WINDOW, 0 }, .flags = 0, .exec = cmd_respawn_window_exec }; static enum cmd_retval cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct spawn_context sc; struct session *s = item->target.s; struct winlink *wl = item->target.wl; char *cause = NULL; const char *add; struct args_value *value; memset(&sc, 0, sizeof sc); sc.item = item; sc.s = s; sc.wl = wl; sc.name = NULL; sc.argc = args->argc; sc.argv = args->argv; sc.environ = environ_create(); add = args_first_value(args, 'e', &value); while (add != NULL) { environ_put(sc.environ, add); add = args_next_value(&value); } sc.idx = -1; sc.cwd = args_get(args, 'c'); sc.flags = SPAWN_RESPAWN; if (args_has(args, 'k')) sc.flags |= SPAWN_KILL; if (spawn_window(&sc, &cause) == NULL) { cmdq_error(item, "respawn window failed: %s", cause); free(cause); return (CMD_RETURN_ERROR); } server_redraw_window(wl->window); environ_free(sc.environ); return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-rotate-window.c100644 001750 001750 00000006566 13570677043 0011755/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "tmux.h" /* * Rotate the panes in a window. */ static enum cmd_retval cmd_rotate_window_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_rotate_window_entry = { .name = "rotate-window", .alias = "rotatew", .args = { "Dt:U", 0, 0 }, .usage = "[-DU] " CMD_TARGET_WINDOW_USAGE, .target = { 't', CMD_FIND_WINDOW, 0 }, .flags = 0, .exec = cmd_rotate_window_exec }; static enum cmd_retval cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item) { struct cmd_find_state *current = &item->shared->current; struct winlink *wl = item->target.wl; struct window *w = wl->window; struct window_pane *wp, *wp2; struct layout_cell *lc; u_int sx, sy, xoff, yoff; server_unzoom_window(w); 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, 1); cmd_find_from_winlink_pane(current, wl, wp, 0); 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, 1); cmd_find_from_winlink_pane(current, wl, wp, 0); server_redraw_window(w); } return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-run-shell.c100644 001750 001750 00000010362 13517540563 0011045/* $OpenBSD$ */ /* * 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 #include "tmux.h" /* * Runs a command without a window. */ static enum cmd_retval cmd_run_shell_exec(struct cmd *, struct cmdq_item *); static void cmd_run_shell_callback(struct job *); static void cmd_run_shell_free(void *); static void cmd_run_shell_print(struct job *, const char *); const struct cmd_entry cmd_run_shell_entry = { .name = "run-shell", .alias = "run", .args = { "bt:", 1, 1 }, .usage = "[-b] " CMD_TARGET_PANE_USAGE " shell-command", .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, .flags = 0, .exec = cmd_run_shell_exec }; struct cmd_run_shell_data { char *cmd; struct cmdq_item *item; int wp_id; }; static void cmd_run_shell_print(struct job *job, const char *msg) { struct cmd_run_shell_data *cdata = job_get_data(job); struct window_pane *wp = NULL; struct cmd_find_state fs; struct window_mode_entry *wme; if (cdata->wp_id != -1) wp = window_pane_find_by_id(cdata->wp_id); if (wp == NULL) { if (cdata->item != NULL) { cmdq_print(cdata->item, "%s", msg); return; } if (cmd_find_from_nothing(&fs, 0) != 0) return; wp = fs.wp; if (wp == NULL) return; } wme = TAILQ_FIRST(&wp->modes); if (wme == NULL || wme->mode != &window_view_mode) window_pane_set_mode(wp, &window_view_mode, NULL, NULL); window_copy_add(wp, "%s", msg); } static enum cmd_retval cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct cmd_run_shell_data *cdata; struct client *c = cmd_find_client(item, NULL, 1); struct session *s = item->target.s; struct winlink *wl = item->target.wl; struct window_pane *wp = item->target.wp; cdata = xcalloc(1, sizeof *cdata); cdata->cmd = format_single(item, args->argv[0], c, s, wl, wp); if (args_has(args, 't') && wp != NULL) cdata->wp_id = wp->id; else cdata->wp_id = -1; if (!args_has(args, 'b')) cdata->item = item; if (job_run(cdata->cmd, s, server_client_get_cwd(item->client, s), NULL, cmd_run_shell_callback, cmd_run_shell_free, cdata, 0) == NULL) { cmdq_error(item, "failed to run command: %s", cdata->cmd); free(cdata); return (CMD_RETURN_ERROR); } if (args_has(args, 'b')) return (CMD_RETURN_NORMAL); return (CMD_RETURN_WAIT); } static void cmd_run_shell_callback(struct job *job) { struct cmd_run_shell_data *cdata = job_get_data(job); struct bufferevent *event = job_get_event(job); char *cmd = cdata->cmd, *msg = NULL, *line; size_t size; int retcode, status; do { if ((line = evbuffer_readline(event->input)) != NULL) { cmd_run_shell_print(job, line); free(line); } } while (line != NULL); size = EVBUFFER_LENGTH(event->input); if (size != 0) { line = xmalloc(size + 1); memcpy(line, EVBUFFER_DATA(event->input), size); line[size] = '\0'; cmd_run_shell_print(job, line); free(line); } status = job_get_status(job); if (WIFEXITED(status)) { if ((retcode = WEXITSTATUS(status)) != 0) xasprintf(&msg, "'%s' returned %d", cmd, retcode); } else if (WIFSIGNALED(status)) { retcode = WTERMSIG(status); xasprintf(&msg, "'%s' terminated by signal %d", cmd, retcode); } if (msg != NULL) cmd_run_shell_print(job, msg); free(msg); if (cdata->item != NULL) cmdq_continue(cdata->item); } static void cmd_run_shell_free(void *data) { struct cmd_run_shell_data *cdata = data; free(cdata->cmd); free(cdata); } tmux-3.0a/cmd.c100644 001750 001750 00000041424 13570044333 0007132/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include "tmux.h" 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_tree_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_menu_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_pane_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_resize_window_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_set_buffer_entry; extern const struct cmd_entry cmd_set_environment_entry; extern const struct cmd_entry cmd_set_hook_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_hooks_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; extern const struct cmd_entry cmd_wait_for_entry; 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_tree_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_menu_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_pane_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_resize_window_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_set_buffer_entry, &cmd_set_environment_entry, &cmd_set_hook_entry, &cmd_set_option_entry, &cmd_set_window_option_entry, &cmd_show_buffer_entry, &cmd_show_environment_entry, &cmd_show_hooks_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, &cmd_wait_for_entry, NULL }; static u_int cmd_list_next_group = 1; void printflike(3, 4) cmd_log_argv(int argc, char **argv, const char *fmt, ...) { char *prefix; va_list ap; int i; va_start(ap, fmt); xvasprintf(&prefix, fmt, ap); va_end(ap); for (i = 0; i < argc; i++) log_debug("%s: argv[%d]=%s", prefix, i, argv[i]); free(prefix); } void cmd_prepend_argv(int *argc, char ***argv, char *arg) { char **new_argv; int i; new_argv = xreallocarray(NULL, (*argc) + 1, sizeof *new_argv); new_argv[0] = xstrdup(arg); for (i = 0; i < *argc; i++) new_argv[1 + i] = (*argv)[i]; free(*argv); *argv = new_argv; (*argc)++; } void cmd_append_argv(int *argc, char ***argv, char *arg) { *argv = xreallocarray(*argv, (*argc) + 1, sizeof **argv); (*argv)[(*argc)++] = xstrdup(arg); } int cmd_pack_argv(int argc, char **argv, char *buf, size_t len) { size_t arglen; int i; if (argc == 0) return (0); cmd_log_argv(argc, argv, "%s", __func__); *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; } cmd_log_argv(argc, *argv, "%s", __func__); return (0); } char ** cmd_copy_argv(int argc, char **argv) { char **new_argv; int i; if (argc == 0) return (NULL); new_argv = xcalloc(argc + 1, 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++) free(argv[i]); free(argv); } char * cmd_stringify_argv(int argc, char **argv) { char *buf; int i; size_t len; if (argc == 0) return (xstrdup("")); len = 0; buf = NULL; for (i = 0; i < argc; i++) { len += strlen(argv[i]) + 1; buf = xrealloc(buf, len); if (i == 0) *buf = '\0'; else strlcat(buf, " ", len); strlcat(buf, argv[i], len); } return (buf); } char * cmd_get_alias(const char *name) { struct options_entry *o; struct options_array_item *a; union options_value *ov; size_t wanted, n; const char *equals; o = options_get_only(global_options, "command-alias"); if (o == NULL) return (NULL); wanted = strlen(name); a = options_array_first(o); while (a != NULL) { ov = options_array_item_value(a); equals = strchr(ov->string, '='); if (equals != NULL) { n = equals - ov->string; if (n == wanted && strncmp(name, ov->string, n) == 0) return (xstrdup(equals + 1)); } a = options_array_next(a); } return (NULL); } static const struct cmd_entry * cmd_find(const char *name, char **cause) { const struct cmd_entry **loop, *entry, *found = NULL; int ambiguous; char s[8192]; ambiguous = 0; for (loop = cmd_table; *loop != NULL; loop++) { entry = *loop; if (entry->alias != NULL && strcmp(entry->alias, name) == 0) { ambiguous = 0; found = entry; break; } if (strncmp(entry->name, name, strlen(name)) != 0) continue; if (found != NULL) ambiguous = 1; found = entry; if (strcmp(entry->name, name) == 0) break; } if (ambiguous) goto ambiguous; if (found == NULL) { xasprintf(cause, "unknown command: %s", name); return (NULL); } return (found); ambiguous: *s = '\0'; for (loop = cmd_table; *loop != NULL; loop++) { entry = *loop; if (strncmp(entry->name, name, strlen(name)) != 0) continue; if (strlcat(s, entry->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", name, s); return (NULL); } struct cmd * cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause) { const struct cmd_entry *entry; const char *name; struct cmd *cmd; struct args *args; if (argc == 0) { xasprintf(cause, "no command"); return (NULL); } name = argv[0]; entry = cmd_find(name, cause); if (entry == NULL) return (NULL); cmd_log_argv(argc, argv, "%s: %s", __func__, entry->name); 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; cmd = xcalloc(1, sizeof *cmd); cmd->entry = entry; cmd->args = args; if (file != NULL) cmd->file = xstrdup(file); cmd->line = line; cmd->alias = NULL; cmd->argc = argc; cmd->argv = cmd_copy_argv(argc, argv); return (cmd); usage: if (args != NULL) args_free(args); xasprintf(cause, "usage: %s %s", entry->name, entry->usage); return (NULL); } void cmd_free(struct cmd *cmd) { free(cmd->alias); cmd_free_argv(cmd->argc, cmd->argv); free(cmd->file); args_free(cmd->args); free(cmd); } char * cmd_print(struct cmd *cmd) { char *out, *s; s = args_print(cmd->args); if (*s != '\0') xasprintf(&out, "%s %s", cmd->entry->name, s); else out = xstrdup(cmd->entry->name); free(s); return (out); } struct cmd_list * cmd_list_new(void) { struct cmd_list *cmdlist; cmdlist = xcalloc(1, sizeof *cmdlist); cmdlist->references = 1; cmdlist->group = cmd_list_next_group++; TAILQ_INIT(&cmdlist->list); return (cmdlist); } void cmd_list_append(struct cmd_list *cmdlist, struct cmd *cmd) { cmd->group = cmdlist->group; TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry); } void cmd_list_move(struct cmd_list *cmdlist, struct cmd_list *from) { struct cmd *cmd, *cmd1; TAILQ_FOREACH_SAFE(cmd, &from->list, qentry, cmd1) { TAILQ_REMOVE(&from->list, cmd, qentry); TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry); } cmdlist->group = cmd_list_next_group++; } void cmd_list_free(struct cmd_list *cmdlist) { struct cmd *cmd, *cmd1; if (--cmdlist->references != 0) return; TAILQ_FOREACH_SAFE(cmd, &cmdlist->list, qentry, cmd1) { TAILQ_REMOVE(&cmdlist->list, cmd, qentry); cmd_free(cmd); } free(cmdlist); } char * cmd_list_print(struct cmd_list *cmdlist, int escaped) { struct cmd *cmd; char *buf, *this; size_t len; len = 1; buf = xcalloc(1, len); TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { this = cmd_print(cmd); len += strlen(this) + 4; buf = xrealloc(buf, len); strlcat(buf, this, len); if (TAILQ_NEXT(cmd, qentry) != NULL) { if (escaped) strlcat(buf, " \\; ", len); else strlcat(buf, " ; ", len); } free(this); } return (buf); } /* Adjust current mouse position for a pane. */ int cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp, u_int *yp, int last) { u_int x, y; if (last) { x = m->lx + m->ox; y = m->ly + m->oy; } else { x = m->x + m->ox; y = m->y + m->oy; } log_debug("%s: x=%u, y=%u%s", __func__, x, y, last ? " (last)" : ""); if (m->statusat == 0 && y >= m->statuslines) y -= m->statuslines; if (x < wp->xoff || x >= wp->xoff + wp->sx) return (-1); if (y < wp->yoff || y >= wp->yoff + wp->sy) return (-1); if (xp != NULL) *xp = x - wp->xoff; if (yp != NULL) *yp = y - wp->yoff; return (0); } /* Get current mouse window if any. */ struct winlink * cmd_mouse_window(struct mouse_event *m, struct session **sp) { struct session *s; struct window *w; struct winlink *wl; if (!m->valid) return (NULL); if (m->s == -1 || (s = session_find_by_id(m->s)) == NULL) return (NULL); if (m->w == -1) wl = s->curw; else { if ((w = window_find_by_id(m->w)) == NULL) return (NULL); wl = winlink_find_by_window(&s->windows, w); } if (sp != NULL) *sp = s; return (wl); } /* Get current mouse pane if any. */ struct window_pane * cmd_mouse_pane(struct mouse_event *m, struct session **sp, struct winlink **wlp) { struct winlink *wl; struct window_pane *wp; if ((wl = cmd_mouse_window(m, sp)) == NULL) return (NULL); if ((wp = window_pane_find_by_id(m->wp)) == NULL) return (NULL); if (!window_has_pane(wl->window, wp)) return (NULL); if (wlp != NULL) *wlp = wl; return (wp); } /* Replace the first %% or %idx in template by s. */ char * cmd_template_replace(const char *template, const char *s, int idx) { char ch, *buf; const char *ptr, *cp, quote[] = "\"\\$;"; int replaced, quoted; size_t len; if (strchr(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++; quoted = (*ptr == '%'); if (quoted) ptr++; buf = xrealloc(buf, len + (strlen(s) * 3) + 1); for (cp = s; *cp != '\0'; cp++) { if (quoted && strchr(quote, *cp) != NULL) buf[len++] = '\\'; buf[len++] = *cp; } buf[len] = '\0'; continue; } buf = xrealloc(buf, len + 2); buf[len++] = ch; buf[len] = '\0'; } return (buf); } tmux-3.0a/cmd-save-buffer.c100644 001750 001750 00000007452 13504653146 0011345/* $OpenBSD$ */ /* * 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 #include "tmux.h" /* * Saves a paste buffer to a file. */ static enum cmd_retval cmd_save_buffer_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_save_buffer_entry = { .name = "save-buffer", .alias = "saveb", .args = { "ab:", 1, 1 }, .usage = "[-a] " CMD_BUFFER_USAGE " path", .flags = CMD_AFTERHOOK, .exec = cmd_save_buffer_exec }; const struct cmd_entry cmd_show_buffer_entry = { .name = "show-buffer", .alias = "showb", .args = { "b:", 0, 0 }, .usage = CMD_BUFFER_USAGE, .flags = CMD_AFTERHOOK, .exec = cmd_save_buffer_exec }; static enum cmd_retval cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct client *c = cmd_find_client(item, NULL, 1); struct session *s = item->target.s; struct winlink *wl = item->target.wl; struct window_pane *wp = item->target.wp; struct paste_buffer *pb; const char *bufname, *bufdata, *start, *end, *flags; char *msg, *path, *file; size_t size, used, msglen, bufsize; FILE *f; if (!args_has(args, 'b')) { if ((pb = paste_get_top(NULL)) == NULL) { cmdq_error(item, "no buffers"); return (CMD_RETURN_ERROR); } } else { bufname = args_get(args, 'b'); pb = paste_get_name(bufname); if (pb == NULL) { cmdq_error(item, "no buffer %s", bufname); return (CMD_RETURN_ERROR); } } bufdata = paste_buffer_data(pb, &bufsize); if (self->entry == &cmd_show_buffer_entry) path = xstrdup("-"); else path = format_single(item, args->argv[0], c, s, wl, wp); if (strcmp(path, "-") == 0) { free(path); c = item->client; if (c == NULL) { cmdq_error(item, "can't write to stdout"); return (CMD_RETURN_ERROR); } if (c->session == NULL || (c->flags & CLIENT_CONTROL)) goto do_stdout; goto do_print; } flags = "wb"; if (args_has(self->args, 'a')) flags = "ab"; file = server_client_get_path(item->client, path); free(path); f = fopen(file, flags); if (f == NULL) { cmdq_error(item, "%s: %s", file, strerror(errno)); free(file); return (CMD_RETURN_ERROR); } if (fwrite(bufdata, 1, bufsize, f) != bufsize) { cmdq_error(item, "%s: write error", file); fclose(f); free(file); return (CMD_RETURN_ERROR); } fclose(f); free(file); return (CMD_RETURN_NORMAL); do_stdout: evbuffer_add(c->stdout_data, bufdata, bufsize); server_client_push_stdout(c); return (CMD_RETURN_NORMAL); do_print: if (bufsize > (INT_MAX / 4) - 1) { cmdq_error(item, "buffer too big"); return (CMD_RETURN_ERROR); } msg = NULL; used = 0; while (used != bufsize) { start = bufdata + used; end = memchr(start, '\n', bufsize - used); if (end != NULL) size = end - start; else size = bufsize - used; msglen = size * 4 + 1; msg = xrealloc(msg, msglen); strvisx(msg, start, size, VIS_OCTAL|VIS_TAB); cmdq_print(item, "%s", msg); used += size + (end != NULL); } free(msg); return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-select-layout.c100644 001750 001750 00000006544 13466241542 0011733/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Switch window to selected layout. */ static enum cmd_retval cmd_select_layout_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_select_layout_entry = { .name = "select-layout", .alias = "selectl", .args = { "Enopt:", 0, 1 }, .usage = "[-Enop] " CMD_TARGET_PANE_USAGE " [layout-name]", .target = { 't', CMD_FIND_PANE, 0 }, .flags = CMD_AFTERHOOK, .exec = cmd_select_layout_exec }; const struct cmd_entry cmd_next_layout_entry = { .name = "next-layout", .alias = "nextl", .args = { "t:", 0, 0 }, .usage = CMD_TARGET_WINDOW_USAGE, .target = { 't', CMD_FIND_WINDOW, 0 }, .flags = CMD_AFTERHOOK, .exec = cmd_select_layout_exec }; const struct cmd_entry cmd_previous_layout_entry = { .name = "previous-layout", .alias = "prevl", .args = { "t:", 0, 0 }, .usage = CMD_TARGET_WINDOW_USAGE, .target = { 't', CMD_FIND_WINDOW, 0 }, .flags = CMD_AFTERHOOK, .exec = cmd_select_layout_exec }; static enum cmd_retval cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct winlink *wl = item->target.wl; struct window *w = wl->window; struct window_pane *wp = item->target.wp; const char *layoutname; char *oldlayout; int next, previous, layout; server_unzoom_window(w); next = self->entry == &cmd_next_layout_entry; if (args_has(args, 'n')) next = 1; previous = self->entry == &cmd_previous_layout_entry; if (args_has(args, 'p')) previous = 1; oldlayout = w->old_layout; w->old_layout = layout_dump(w->layout_root); if (next || previous) { if (next) layout_set_next(w); else layout_set_previous(w); goto changed; } if (args_has(args, 'E')) { layout_spread_out(wp); goto changed; } if (!args_has(args, 'o')) { if (args->argc == 0) layout = w->lastlayout; else layout = layout_set_lookup(args->argv[0]); if (layout != -1) { layout_set_select(w, layout); goto changed; } } if (args->argc != 0) layoutname = args->argv[0]; else if (args_has(args, 'o')) layoutname = oldlayout; else layoutname = NULL; if (layoutname != NULL) { if (layout_parse(w, layoutname) == -1) { cmdq_error(item, "can't set layout: %s", layoutname); goto error; } goto changed; } free(oldlayout); return (CMD_RETURN_NORMAL); changed: free(oldlayout); recalculate_sizes(); server_redraw_window(w); notify_window("window-layout-changed", w); return (CMD_RETURN_NORMAL); error: free(w->old_layout); w->old_layout = oldlayout; return (CMD_RETURN_ERROR); } tmux-3.0a/cmd-select-pane.c100644 001750 001750 00000013323 13570677043 0011337/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Select pane. */ static enum cmd_retval cmd_select_pane_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_select_pane_entry = { .name = "select-pane", .alias = "selectp", .args = { "DdegLlMmP:RT:t:U", 0, 0 }, /* -P and -g deprecated */ .usage = "[-DdeLlMmRU] [-T title] " CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, .flags = 0, .exec = cmd_select_pane_exec }; const struct cmd_entry cmd_last_pane_entry = { .name = "last-pane", .alias = "lastp", .args = { "det:", 0, 0 }, .usage = "[-de] " CMD_TARGET_WINDOW_USAGE, .target = { 't', CMD_FIND_WINDOW, 0 }, .flags = 0, .exec = cmd_select_pane_exec }; static void cmd_select_pane_redraw(struct window *w) { struct client *c; /* * Redraw entire window if it is bigger than the client (the * offset may change), otherwise just draw borders. */ TAILQ_FOREACH(c, &clients, entry) { if (c->session == NULL || (c->flags & CLIENT_CONTROL)) continue; if (c->session->curw->window == w && tty_window_bigger(&c->tty)) server_redraw_client(c); else { if (c->session->curw->window == w) c->flags |= CLIENT_REDRAWBORDERS; if (session_has(c->session, w)) c->flags |= CLIENT_REDRAWSTATUS; } } } static enum cmd_retval cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct cmd_find_state *current = &item->shared->current; struct client *c = cmd_find_client(item, NULL, 1); struct winlink *wl = item->target.wl; struct window *w = wl->window; struct session *s = item->target.s; struct window_pane *wp = item->target.wp, *lastwp, *markedwp; char *pane_title; const char *style; struct style *sy; struct options_entry *o; if (self->entry == &cmd_last_pane_entry || args_has(args, 'l')) { lastwp = w->last; if (lastwp == NULL && window_count_panes(w) == 2) { lastwp = TAILQ_PREV(w->active, window_panes, entry); if (lastwp == NULL) lastwp = TAILQ_NEXT(w->active, entry); } if (lastwp == NULL) { cmdq_error(item, "no last pane"); return (CMD_RETURN_ERROR); } if (args_has(self->args, 'e')) lastwp->flags &= ~PANE_INPUTOFF; else if (args_has(self->args, 'd')) lastwp->flags |= PANE_INPUTOFF; else { server_unzoom_window(w); window_redraw_active_switch(w, lastwp); if (window_set_active_pane(w, lastwp, 1)) { cmd_find_from_winlink(current, wl, 0); cmd_select_pane_redraw(w); } } return (CMD_RETURN_NORMAL); } if (args_has(args, 'm') || args_has(args, 'M')) { if (args_has(args, 'm') && !window_pane_visible(wp)) return (CMD_RETURN_NORMAL); lastwp = marked_pane.wp; if (args_has(args, 'M') || server_is_marked(s, wl, wp)) server_clear_marked(); else server_set_marked(s, wl, wp); markedwp = marked_pane.wp; if (lastwp != NULL) { server_redraw_window_borders(lastwp->window); server_status_window(lastwp->window); } if (markedwp != NULL) { server_redraw_window_borders(markedwp->window); server_status_window(markedwp->window); } return (CMD_RETURN_NORMAL); } if (args_has(self->args, 'P') || args_has(self->args, 'g')) { if ((style = args_get(args, 'P')) != NULL) { o = options_set_style(wp->options, "window-style", 0, style); if (o == NULL) { cmdq_error(item, "bad style: %s", style); return (CMD_RETURN_ERROR); } options_set_style(wp->options, "window-active-style", 0, style); wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); } if (args_has(self->args, 'g')) { sy = options_get_style(wp->options, "window-style"); cmdq_print(item, "%s", style_tostring(sy)); } return (CMD_RETURN_NORMAL); } if (args_has(self->args, 'L')) { server_unzoom_window(wp->window); wp = window_pane_find_left(wp); } else if (args_has(self->args, 'R')) { server_unzoom_window(wp->window); wp = window_pane_find_right(wp); } else if (args_has(self->args, 'U')) { server_unzoom_window(wp->window); wp = window_pane_find_up(wp); } else if (args_has(self->args, 'D')) { server_unzoom_window(wp->window); wp = window_pane_find_down(wp); } if (wp == NULL) return (CMD_RETURN_NORMAL); if (args_has(self->args, 'e')) { wp->flags &= ~PANE_INPUTOFF; return (CMD_RETURN_NORMAL); } if (args_has(self->args, 'd')) { wp->flags |= PANE_INPUTOFF; return (CMD_RETURN_NORMAL); } if (args_has(self->args, 'T')) { pane_title = format_single(item, args_get(self->args, 'T'), c, s, wl, wp); screen_set_title(&wp->base, pane_title); server_status_window(wp->window); free(pane_title); return (CMD_RETURN_NORMAL); } if (wp == w->active) return (CMD_RETURN_NORMAL); server_unzoom_window(wp->window); window_redraw_active_switch(w, wp); if (window_set_active_pane(w, wp, 1)) { cmd_find_from_winlink_pane(current, wl, wp, 0); cmdq_insert_hook(s, item, current, "after-select-pane"); cmd_select_pane_redraw(w); } return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-select-window.c100644 001750 001750 00000007332 13504653146 0011721/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Select window by index. */ static enum cmd_retval cmd_select_window_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_select_window_entry = { .name = "select-window", .alias = "selectw", .args = { "lnpTt:", 0, 0 }, .usage = "[-lnpT] " CMD_TARGET_WINDOW_USAGE, .target = { 't', CMD_FIND_WINDOW, 0 }, .flags = 0, .exec = cmd_select_window_exec }; const struct cmd_entry cmd_next_window_entry = { .name = "next-window", .alias = "next", .args = { "at:", 0, 0 }, .usage = "[-a] " CMD_TARGET_SESSION_USAGE, .target = { 't', CMD_FIND_SESSION, 0 }, .flags = 0, .exec = cmd_select_window_exec }; const struct cmd_entry cmd_previous_window_entry = { .name = "previous-window", .alias = "prev", .args = { "at:", 0, 0 }, .usage = "[-a] " CMD_TARGET_SESSION_USAGE, .target = { 't', CMD_FIND_SESSION, 0 }, .flags = 0, .exec = cmd_select_window_exec }; const struct cmd_entry cmd_last_window_entry = { .name = "last-window", .alias = "last", .args = { "t:", 0, 0 }, .usage = CMD_TARGET_SESSION_USAGE, .target = { 't', CMD_FIND_SESSION, 0 }, .flags = 0, .exec = cmd_select_window_exec }; static enum cmd_retval cmd_select_window_exec(struct cmd *self, struct cmdq_item *item) { struct cmd_find_state *current = &item->shared->current; struct winlink *wl = item->target.wl; struct session *s = item->target.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) { activity = args_has(self->args, 'a'); if (next) { if (session_next(s, activity) != 0) { cmdq_error(item, "no next window"); return (CMD_RETURN_ERROR); } } else if (previous) { if (session_previous(s, activity) != 0) { cmdq_error(item, "no previous window"); return (CMD_RETURN_ERROR); } } else { if (session_last(s) != 0) { cmdq_error(item, "no last window"); return (CMD_RETURN_ERROR); } } cmd_find_from_session(current, s, 0); server_redraw_session(s); cmdq_insert_hook(s, item, current, "after-select-window"); } else { /* * If -T and select-window is invoked on same window as * current, switch to previous window. */ if (args_has(self->args, 'T') && wl == s->curw) { if (session_last(s) != 0) { cmdq_error(item, "no last window"); return (-1); } if (current->s == s) cmd_find_from_session(current, s, 0); server_redraw_session(s); } else if (session_select(s, wl->idx) == 0) { cmd_find_from_session(current, s, 0); server_redraw_session(s); } cmdq_insert_hook(s, item, current, "after-select-window"); } recalculate_sizes(); return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-send-keys.c100644 001750 001750 00000012160 13570677043 0011037/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Send keys to client. */ static enum cmd_retval cmd_send_keys_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_send_keys_entry = { .name = "send-keys", .alias = "send", .args = { "HlXRMN:t:", 0, -1 }, .usage = "[-HlXRM] [-N repeat-count] " CMD_TARGET_PANE_USAGE " key ...", .target = { 't', CMD_FIND_PANE, 0 }, .flags = CMD_AFTERHOOK, .exec = cmd_send_keys_exec }; const struct cmd_entry cmd_send_prefix_entry = { .name = "send-prefix", .alias = NULL, .args = { "2t:", 0, 0 }, .usage = "[-2] " CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, .flags = CMD_AFTERHOOK, .exec = cmd_send_keys_exec }; static struct cmdq_item * cmd_send_keys_inject_key(struct client *c, struct cmd_find_state *fs, struct cmdq_item *item, key_code key) { struct window_mode_entry *wme; struct key_table *table; struct key_binding *bd; wme = TAILQ_FIRST(&fs->wp->modes); if (wme == NULL || wme->mode->key_table == NULL) { if (options_get_number(fs->wp->window->options, "xterm-keys")) key |= KEYC_XTERM; window_pane_key(fs->wp, item->client, fs->s, fs->wl, key, NULL); return (item); } table = key_bindings_get_table(wme->mode->key_table(wme), 1); bd = key_bindings_get(table, key & ~KEYC_XTERM); if (bd != NULL) { table->references++; item = key_bindings_dispatch(bd, item, c, NULL, &item->target); key_bindings_unref_table(table); } return (item); } static struct cmdq_item * cmd_send_keys_inject_string(struct client *c, struct cmd_find_state *fs, struct cmdq_item *item, struct args *args, int i) { const char *s = args->argv[i]; struct utf8_data *ud, *uc; wchar_t wc; key_code key; char *endptr; long n; int literal; if (args_has(args, 'H')) { n = strtol(s, &endptr, 16); if (*s =='\0' || n < 0 || n > 0xff || *endptr != '\0') return (item); return (cmd_send_keys_inject_key(c, fs, item, KEYC_LITERAL|n)); } literal = args_has(args, 'l'); if (!literal) { key = key_string_lookup_string(s); if (key != KEYC_NONE && key != KEYC_UNKNOWN) return (cmd_send_keys_inject_key(c, fs, item, key)); literal = 1; } if (literal) { ud = utf8_fromcstr(s); for (uc = ud; uc->size != 0; uc++) { if (utf8_combine(uc, &wc) != UTF8_DONE) continue; item = cmd_send_keys_inject_key(c, fs, item, wc); } free(ud); } return (item); } static enum cmd_retval cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct client *c = cmd_find_client(item, NULL, 1); struct cmd_find_state *fs = &item->target; struct window_pane *wp = item->target.wp; struct session *s = item->target.s; struct winlink *wl = item->target.wl; struct mouse_event *m = &item->shared->mouse; struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes); int i; key_code key; u_int np = 1; char *cause = NULL; if (args_has(args, 'N')) { np = args_strtonum(args, 'N', 1, UINT_MAX, &cause); if (cause != NULL) { cmdq_error(item, "repeat count %s", cause); free(cause); return (CMD_RETURN_ERROR); } if (wme != NULL && (args_has(args, 'X') || args->argc == 0)) { if (wme == NULL || wme->mode->command == NULL) { cmdq_error(item, "not in a mode"); return (CMD_RETURN_ERROR); } wme->prefix = np; } } if (args_has(args, 'X')) { if (wme == NULL || wme->mode->command == NULL) { cmdq_error(item, "not in a mode"); return (CMD_RETURN_ERROR); } if (!m->valid) m = NULL; wme->mode->command(wme, c, s, wl, args, m); return (CMD_RETURN_NORMAL); } if (args_has(args, 'M')) { wp = cmd_mouse_pane(m, &s, NULL); if (wp == NULL) { cmdq_error(item, "no mouse target"); return (CMD_RETURN_ERROR); } window_pane_key(wp, item->client, s, wl, m->key, m); return (CMD_RETURN_NORMAL); } if (self->entry == &cmd_send_prefix_entry) { if (args_has(args, '2')) key = options_get_number(s->options, "prefix2"); else key = options_get_number(s->options, "prefix"); cmd_send_keys_inject_key(c, fs, item, key); return (CMD_RETURN_NORMAL); } if (args_has(args, 'R')) { window_pane_reset_palette(wp); input_reset(wp, 1); } for (; np != 0; np--) { for (i = 0; i < args->argc; i++) item = cmd_send_keys_inject_string(c, fs, item, args, i); } return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-set-buffer.c100644 001750 001750 00000006043 13462323664 0011177/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Add, set, append to or delete a paste buffer. */ static enum cmd_retval cmd_set_buffer_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_set_buffer_entry = { .name = "set-buffer", .alias = "setb", .args = { "ab:n:", 0, 1 }, .usage = "[-a] " CMD_BUFFER_USAGE " [-n new-buffer-name] data", .flags = CMD_AFTERHOOK, .exec = cmd_set_buffer_exec }; const struct cmd_entry cmd_delete_buffer_entry = { .name = "delete-buffer", .alias = "deleteb", .args = { "b:", 0, 0 }, .usage = CMD_BUFFER_USAGE, .flags = CMD_AFTERHOOK, .exec = cmd_set_buffer_exec }; static enum cmd_retval cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct paste_buffer *pb; char *bufdata, *cause; const char *bufname, *olddata; size_t bufsize, newsize; bufname = args_get(args, 'b'); if (bufname == NULL) pb = NULL; else pb = paste_get_name(bufname); if (self->entry == &cmd_delete_buffer_entry) { if (pb == NULL) pb = paste_get_top(&bufname); if (pb == NULL) { cmdq_error(item, "no buffer"); return (CMD_RETURN_ERROR); } paste_free(pb); return (CMD_RETURN_NORMAL); } if (args_has(args, 'n')) { if (pb == NULL) pb = paste_get_top(&bufname); if (pb == NULL) { cmdq_error(item, "no buffer"); return (CMD_RETURN_ERROR); } if (paste_rename(bufname, args_get(args, 'n'), &cause) != 0) { cmdq_error(item, "%s", cause); free(cause); return (CMD_RETURN_ERROR); } return (CMD_RETURN_NORMAL); } if (args->argc != 1) { cmdq_error(item, "no data specified"); return (CMD_RETURN_ERROR); } if ((newsize = strlen(args->argv[0])) == 0) return (CMD_RETURN_NORMAL); bufsize = 0; bufdata = NULL; if (args_has(args, 'a') && pb != NULL) { olddata = paste_buffer_data(pb, &bufsize); bufdata = xmalloc(bufsize); memcpy(bufdata, olddata, bufsize); } bufdata = xrealloc(bufdata, bufsize + newsize); memcpy(bufdata + bufsize, args->argv[0], newsize); bufsize += newsize; if (paste_set(bufdata, bufsize, bufname, &cause) != 0) { cmdq_error(item, "%s", cause); free(bufdata); free(cause); return (CMD_RETURN_ERROR); } return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-set-environment.c100644 001750 001750 00000005227 13462323664 0012275/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Set an environment variable. */ static enum cmd_retval cmd_set_environment_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_set_environment_entry = { .name = "set-environment", .alias = "setenv", .args = { "grt:u", 1, 2 }, .usage = "[-gru] " CMD_TARGET_SESSION_USAGE " name [value]", .target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL }, .flags = CMD_AFTERHOOK, .exec = cmd_set_environment_exec }; static enum cmd_retval cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct environ *env; const char *name, *value, *target; name = args->argv[0]; if (*name == '\0') { cmdq_error(item, "empty variable name"); return (CMD_RETURN_ERROR); } if (strchr(name, '=') != NULL) { cmdq_error(item, "variable name contains ="); return (CMD_RETURN_ERROR); } if (args->argc < 2) value = NULL; else value = args->argv[1]; if (args_has(self->args, 'g')) env = global_environ; else { if (item->target.s == NULL) { target = args_get(args, 't'); if (target != NULL) cmdq_error(item, "no such session: %s", target); else cmdq_error(item, "no current session"); return (CMD_RETURN_ERROR); } env = item->target.s->environ; } if (args_has(self->args, 'u')) { if (value != NULL) { cmdq_error(item, "can't specify a value with -u"); return (CMD_RETURN_ERROR); } environ_unset(env, name); } else if (args_has(self->args, 'r')) { if (value != NULL) { cmdq_error(item, "can't specify a value with -r"); return (CMD_RETURN_ERROR); } environ_clear(env, name); } else { if (value == NULL) { cmdq_error(item, "no value specified"); return (CMD_RETURN_ERROR); } environ_set(env, name, "%s", value); } return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-set-option.c100644 001750 001750 00000025402 13517540563 0011236/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" /* * Set an option. */ static enum cmd_retval cmd_set_option_exec(struct cmd *, struct cmdq_item *); static int cmd_set_option_set(struct cmd *, struct cmdq_item *, struct options *, struct options_entry *, const char *); static int cmd_set_option_flag(struct cmdq_item *, const struct options_table_entry *, struct options *, const char *); static int cmd_set_option_choice(struct cmdq_item *, const struct options_table_entry *, struct options *, const char *); const struct cmd_entry cmd_set_option_entry = { .name = "set-option", .alias = "set", .args = { "aFgopqst:uw", 1, 2 }, .usage = "[-aFgopqsuw] " CMD_TARGET_PANE_USAGE " option [value]", .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, .flags = CMD_AFTERHOOK, .exec = cmd_set_option_exec }; const struct cmd_entry cmd_set_window_option_entry = { .name = "set-window-option", .alias = "setw", .args = { "aFgoqt:u", 1, 2 }, .usage = "[-aFgoqu] " CMD_TARGET_WINDOW_USAGE " option [value]", .target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL }, .flags = CMD_AFTERHOOK, .exec = cmd_set_option_exec }; const struct cmd_entry cmd_set_hook_entry = { .name = "set-hook", .alias = NULL, .args = { "agRt:u", 1, 2 }, .usage = "[-agRu] " CMD_TARGET_SESSION_USAGE " hook [command]", .target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL }, .flags = CMD_AFTERHOOK, .exec = cmd_set_option_exec }; static enum cmd_retval cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; int append = args_has(args, 'a'); struct cmd_find_state *fs = &item->target; struct client *c, *loop; struct session *s = fs->s; struct winlink *wl = fs->wl; struct window *w; struct window_pane *wp; struct options *oo; struct options_entry *parent, *o; char *name, *argument, *value = NULL, *cause; int window, idx, already, error, ambiguous; int scope; struct style *sy; window = (self->entry == &cmd_set_window_option_entry); /* Expand argument. */ c = cmd_find_client(item, NULL, 1); argument = format_single(item, args->argv[0], c, s, wl, NULL); /* If set-hook -R, fire the hook straight away. */ if (self->entry == &cmd_set_hook_entry && args_has(args, 'R')) { notify_hook(item, argument); free(argument); return (CMD_RETURN_NORMAL); } /* Parse option name and index. */ name = options_match(argument, &idx, &ambiguous); if (name == NULL) { if (args_has(args, 'q')) goto out; if (ambiguous) cmdq_error(item, "ambiguous option: %s", argument); else cmdq_error(item, "invalid option: %s", argument); goto fail; } if (args->argc < 2) value = NULL; else if (args_has(args, 'F')) value = format_single(item, args->argv[1], c, s, wl, NULL); else value = xstrdup(args->argv[1]); /* Get the scope and table for the option .*/ scope = options_scope_from_name(args, window, name, fs, &oo, &cause); if (scope == OPTIONS_TABLE_NONE) { if (args_has(args, 'q')) goto out; cmdq_error(item, "%s", cause); free(cause); goto fail; } o = options_get_only(oo, name); parent = options_get(oo, name); /* Check that array options and indexes match up. */ if (idx != -1 && (*name == '@' || !options_isarray(parent))) { cmdq_error(item, "not an array: %s", argument); goto fail; } /* With -o, check this option is not already set. */ if (!args_has(args, 'u') && args_has(args, 'o')) { if (idx == -1) already = (o != NULL); else { if (o == NULL) already = 0; else already = (options_array_get(o, idx) != NULL); } if (already) { if (args_has(args, 'q')) goto out; cmdq_error(item, "already set: %s", argument); goto fail; } } /* Change the option. */ if (args_has(args, 'u')) { if (o == NULL) goto out; if (idx == -1) { if (*name == '@') options_remove(o); else if (oo == global_options || oo == global_s_options || oo == global_w_options) options_default(oo, options_table_entry(o)); else options_remove(o); } else if (options_array_set(o, idx, NULL, 0, &cause) != 0) { cmdq_error(item, "%s", cause); free(cause); goto fail; } } else if (*name == '@') { if (value == NULL) { cmdq_error(item, "empty value"); goto fail; } options_set_string(oo, name, append, "%s", value); } else if (idx == -1 && !options_isarray(parent)) { error = cmd_set_option_set(self, item, oo, parent, value); if (error != 0) goto fail; } else { if (value == NULL) { cmdq_error(item, "empty value"); goto fail; } if (o == NULL) o = options_empty(oo, options_table_entry(parent)); if (idx == -1) { if (!append) options_array_clear(o); if (options_array_assign(o, value, &cause) != 0) { cmdq_error(item, "%s", cause); free(cause); goto fail; } } else if (options_array_set(o, idx, value, append, &cause) != 0) { cmdq_error(item, "%s", cause); free(cause); goto fail; } } /* Update timers and so on for various options. */ if (strcmp(name, "automatic-rename") == 0) { RB_FOREACH(w, windows, &windows) { if (w->active == NULL) continue; if (options_get_number(w->options, "automatic-rename")) w->active->flags |= PANE_CHANGED; } } if (strcmp(name, "key-table") == 0) { TAILQ_FOREACH(loop, &clients, entry) server_client_set_key_table(loop, NULL); } if (strcmp(name, "user-keys") == 0) { TAILQ_FOREACH(loop, &clients, entry) { if (loop->tty.flags & TTY_OPENED) tty_keys_build(&loop->tty); } } if (strcmp(name, "status-fg") == 0 || strcmp(name, "status-bg") == 0) { sy = options_get_style(oo, "status-style"); sy->gc.fg = options_get_number(oo, "status-fg"); sy->gc.bg = options_get_number(oo, "status-bg"); } if (strcmp(name, "status-style") == 0) { sy = options_get_style(oo, "status-style"); options_set_number(oo, "status-fg", sy->gc.fg); options_set_number(oo, "status-bg", sy->gc.bg); } if (strcmp(name, "status") == 0 || strcmp(name, "status-interval") == 0) status_timer_start_all(); if (strcmp(name, "monitor-silence") == 0) alerts_reset_all(); if (strcmp(name, "window-style") == 0 || strcmp(name, "window-active-style") == 0) { RB_FOREACH(wp, window_pane_tree, &all_window_panes) wp->flags |= PANE_STYLECHANGED; } if (strcmp(name, "pane-border-status") == 0) { RB_FOREACH(w, windows, &windows) layout_fix_panes(w); } RB_FOREACH(s, sessions, &sessions) status_update_cache(s); /* * Update sizes and redraw. May not always be necessary but do it * anyway. */ recalculate_sizes(); TAILQ_FOREACH(loop, &clients, entry) { if (loop->session != NULL) server_redraw_client(loop); } out: free(argument); free(value); free(name); return (CMD_RETURN_NORMAL); fail: free(argument); free(value); free(name); return (CMD_RETURN_ERROR); } static int cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo, struct options_entry *parent, const char *value) { const struct options_table_entry *oe; struct args *args = self->args; int append = args_has(args, 'a'); struct options_entry *o; long long number; const char *errstr, *new; char *old; key_code key; oe = options_table_entry(parent); if (value == NULL && oe->type != OPTIONS_TABLE_FLAG && oe->type != OPTIONS_TABLE_CHOICE) { cmdq_error(item, "empty value"); return (-1); } switch (oe->type) { case OPTIONS_TABLE_STRING: old = xstrdup(options_get_string(oo, oe->name)); options_set_string(oo, oe->name, append, "%s", value); new = options_get_string(oo, oe->name); if (oe->pattern != NULL && fnmatch(oe->pattern, new, 0) != 0) { options_set_string(oo, oe->name, 0, "%s", old); free(old); cmdq_error(item, "value is invalid: %s", value); return (-1); } free(old); return (0); case OPTIONS_TABLE_NUMBER: number = strtonum(value, oe->minimum, oe->maximum, &errstr); if (errstr != NULL) { cmdq_error(item, "value is %s: %s", errstr, value); return (-1); } options_set_number(oo, oe->name, number); return (0); case OPTIONS_TABLE_KEY: key = key_string_lookup_string(value); if (key == KEYC_UNKNOWN) { cmdq_error(item, "bad key: %s", value); return (-1); } options_set_number(oo, oe->name, key); return (0); case OPTIONS_TABLE_COLOUR: if ((number = colour_fromstring(value)) == -1) { cmdq_error(item, "bad colour: %s", value); return (-1); } options_set_number(oo, oe->name, number); return (0); case OPTIONS_TABLE_FLAG: return (cmd_set_option_flag(item, oe, oo, value)); case OPTIONS_TABLE_CHOICE: return (cmd_set_option_choice(item, oe, oo, value)); case OPTIONS_TABLE_STYLE: o = options_set_style(oo, oe->name, append, value); if (o == NULL) { cmdq_error(item, "bad style: %s", value); return (-1); } return (0); case OPTIONS_TABLE_COMMAND: break; } return (-1); } static int cmd_set_option_flag(struct cmdq_item *item, 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 (strcmp(value, "1") == 0 || strcasecmp(value, "on") == 0 || strcasecmp(value, "yes") == 0) flag = 1; else if (strcmp(value, "0") == 0 || strcasecmp(value, "off") == 0 || strcasecmp(value, "no") == 0) flag = 0; else { cmdq_error(item, "bad value: %s", value); return (-1); } options_set_number(oo, oe->name, flag); return (0); } static int cmd_set_option_choice(struct cmdq_item *item, const struct options_table_entry *oe, struct options *oo, const char *value) { const char **cp; int n, choice = -1; if (value == NULL) { choice = options_get_number(oo, oe->name); if (choice < 2) choice = !choice; } else { n = 0; for (cp = oe->choices; *cp != NULL; cp++) { if (strcmp(*cp, value) == 0) choice = n; n++; } if (choice == -1) { cmdq_error(item, "unknown value: %s", value); return (-1); } } options_set_number(oo, oe->name, choice); return (0); } tmux-3.0a/cmd-show-environment.c100644 001750 001750 00000007072 13462323664 0012462/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Show environment. */ static enum cmd_retval cmd_show_environment_exec(struct cmd *, struct cmdq_item *); static char *cmd_show_environment_escape(struct environ_entry *); static void cmd_show_environment_print(struct cmd *, struct cmdq_item *, struct environ_entry *); const struct cmd_entry cmd_show_environment_entry = { .name = "show-environment", .alias = "showenv", .args = { "gst:", 0, 1 }, .usage = "[-gs] " CMD_TARGET_SESSION_USAGE " [name]", .target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL }, .flags = CMD_AFTERHOOK, .exec = cmd_show_environment_exec }; static char * cmd_show_environment_escape(struct environ_entry *envent) { const char *value = envent->value; char c, *out, *ret; out = ret = xmalloc(strlen(value) * 2 + 1); /* at most twice the size */ while ((c = *value++) != '\0') { /* POSIX interprets $ ` " and \ in double quotes. */ if (c == '$' || c == '`' || c == '"' || c == '\\') *out++ = '\\'; *out++ = c; } *out = '\0'; return (ret); } static void cmd_show_environment_print(struct cmd *self, struct cmdq_item *item, struct environ_entry *envent) { char *escaped; if (!args_has(self->args, 's')) { if (envent->value != NULL) cmdq_print(item, "%s=%s", envent->name, envent->value); else cmdq_print(item, "-%s", envent->name); return; } if (envent->value != NULL) { escaped = cmd_show_environment_escape(envent); cmdq_print(item, "%s=\"%s\"; export %s;", envent->name, escaped, envent->name); free(escaped); } else cmdq_print(item, "unset %s;", envent->name); } static enum cmd_retval cmd_show_environment_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct environ *env; struct environ_entry *envent; const char *target; if ((target = args_get(args, 't')) != NULL) { if (item->target.s == NULL) { cmdq_error(item, "no such session: %s", target); return (CMD_RETURN_ERROR); } } if (args_has(self->args, 'g')) env = global_environ; else { if (item->target.s == NULL) { target = args_get(args, 't'); if (target != NULL) cmdq_error(item, "no such session: %s", target); else cmdq_error(item, "no current session"); return (CMD_RETURN_ERROR); } env = item->target.s->environ; } if (args->argc != 0) { envent = environ_find(env, args->argv[0]); if (envent == NULL) { cmdq_error(item, "unknown variable: %s", args->argv[0]); return (CMD_RETURN_ERROR); } cmd_show_environment_print(self, item, envent); return (CMD_RETURN_NORMAL); } envent = environ_first(env); while (envent != NULL) { cmd_show_environment_print(self, item, envent); envent = environ_next(envent); } return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-show-messages.c100644 001750 001750 00000004774 13466241542 0011731/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" /* * Show client message log. */ static enum cmd_retval cmd_show_messages_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_show_messages_entry = { .name = "show-messages", .alias = "showmsgs", .args = { "JTt:", 0, 0 }, .usage = "[-JT] " CMD_TARGET_CLIENT_USAGE, .flags = CMD_AFTERHOOK, .exec = cmd_show_messages_exec }; static int cmd_show_messages_terminals(struct cmdq_item *, int); static int cmd_show_messages_terminals(struct cmdq_item *item, int blank) { struct tty_term *term; u_int i, n; n = 0; LIST_FOREACH(term, &tty_terms, entry) { if (blank) { cmdq_print(item, "%s", ""); blank = 0; } cmdq_print(item, "Terminal %u: %s [references=%u, flags=0x%x]:", n, term->name, term->references, term->flags); n++; for (i = 0; i < tty_term_ncodes(); i++) cmdq_print(item, "%s", tty_term_describe(term, i)); } return (n != 0); } static enum cmd_retval cmd_show_messages_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct client *c; struct message_entry *msg; char *tim; int done, blank; if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); done = blank = 0; if (args_has(args, 'T')) { blank = cmd_show_messages_terminals(item, blank); done = 1; } if (args_has(args, 'J')) { job_print_summary(item, blank); done = 1; } if (done) return (CMD_RETURN_NORMAL); TAILQ_FOREACH(msg, &c->message_log, entry) { tim = ctime(&msg->msg_time); *strchr(tim, '\n') = '\0'; cmdq_print(item, "%s %s", tim, msg->msg); } return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-show-options.c100644 001750 001750 00000014506 13517540563 0011611/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Show options. */ static enum cmd_retval cmd_show_options_exec(struct cmd *, struct cmdq_item *); static void cmd_show_options_print(struct cmd *, struct cmdq_item *, struct options_entry *, int, int); static enum cmd_retval cmd_show_options_all(struct cmd *, struct cmdq_item *, int, struct options *); const struct cmd_entry cmd_show_options_entry = { .name = "show-options", .alias = "show", .args = { "AgHpqst:vw", 0, 1 }, .usage = "[-AgHpqsvw] " CMD_TARGET_PANE_USAGE " [option]", .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, .flags = CMD_AFTERHOOK, .exec = cmd_show_options_exec }; const struct cmd_entry cmd_show_window_options_entry = { .name = "show-window-options", .alias = "showw", .args = { "gvt:", 0, 1 }, .usage = "[-gv] " CMD_TARGET_WINDOW_USAGE " [option]", .target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL }, .flags = CMD_AFTERHOOK, .exec = cmd_show_options_exec }; const struct cmd_entry cmd_show_hooks_entry = { .name = "show-hooks", .alias = NULL, .args = { "gt:", 0, 1 }, .usage = "[-g] " CMD_TARGET_SESSION_USAGE, .target = { 't', CMD_FIND_SESSION, 0 }, .flags = CMD_AFTERHOOK, .exec = cmd_show_options_exec }; static enum cmd_retval cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct cmd_find_state *fs = &item->target; struct client *c = cmd_find_client(item, NULL, 1); struct session *s = item->target.s; struct winlink *wl = item->target.wl; struct options *oo; char *argument, *name = NULL, *cause; int window, idx, ambiguous, parent, scope; struct options_entry *o; window = (self->entry == &cmd_show_window_options_entry); if (args->argc == 0) { scope = options_scope_from_flags(args, window, fs, &oo, &cause); if (scope == OPTIONS_TABLE_NONE) { if (args_has(args, 'q')) return (CMD_RETURN_NORMAL); cmdq_error(item, "%s", cause); free(cause); return (CMD_RETURN_ERROR); } return (cmd_show_options_all(self, item, scope, oo)); } argument = format_single(item, args->argv[0], c, s, wl, NULL); name = options_match(argument, &idx, &ambiguous); if (name == NULL) { if (args_has(args, 'q')) goto fail; if (ambiguous) cmdq_error(item, "ambiguous option: %s", argument); else cmdq_error(item, "invalid option: %s", argument); goto fail; } scope = options_scope_from_name(args, window, name, fs, &oo, &cause); if (scope == OPTIONS_TABLE_NONE) { if (args_has(args, 'q')) goto fail; cmdq_error(item, "%s", cause); free(cause); goto fail; } o = options_get_only(oo, name); if (args_has(args, 'A') && o == NULL) { o = options_get(oo, name); parent = 1; } else parent = 0; if (o != NULL) cmd_show_options_print(self, item, o, idx, parent); free(name); free(argument); return (CMD_RETURN_NORMAL); fail: free(name); free(argument); return (CMD_RETURN_ERROR); } static void cmd_show_options_print(struct cmd *self, struct cmdq_item *item, struct options_entry *o, int idx, int parent) { struct options_array_item *a; const char *name = options_name(o); char *value, *tmp = NULL, *escaped; if (idx != -1) { xasprintf(&tmp, "%s[%d]", name, idx); name = tmp; } else { if (options_isarray(o)) { a = options_array_first(o); if (a == NULL) { if (!args_has(self->args, 'v')) cmdq_print(item, "%s", name); return; } while (a != NULL) { idx = options_array_item_index(a); cmd_show_options_print(self, item, o, idx, parent); a = options_array_next(a); } return; } } value = options_tostring(o, idx, 0); if (args_has(self->args, 'v')) cmdq_print(item, "%s", value); else if (options_isstring(o)) { escaped = args_escape(value); if (parent) cmdq_print(item, "%s* %s", name, escaped); else cmdq_print(item, "%s %s", name, escaped); free(escaped); } else { if (parent) cmdq_print(item, "%s* %s", name, value); else cmdq_print(item, "%s %s", name, value); } free(value); free(tmp); } static enum cmd_retval cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope, struct options *oo) { const struct options_table_entry *oe; struct options_entry *o; struct options_array_item *a; const char *name; u_int idx; int parent; o = options_first(oo); while (o != NULL) { if (options_table_entry(o) == NULL) cmd_show_options_print(self, item, o, -1, 0); o = options_next(o); } for (oe = options_table; oe->name != NULL; oe++) { if (~oe->scope & scope) continue; if ((self->entry != &cmd_show_hooks_entry && !args_has(self->args, 'H') && oe != NULL && (oe->flags & OPTIONS_TABLE_IS_HOOK)) || (self->entry == &cmd_show_hooks_entry && (oe == NULL || (~oe->flags & OPTIONS_TABLE_IS_HOOK)))) continue; o = options_get_only(oo, oe->name); if (o == NULL) { if (!args_has(self->args, 'A')) continue; o = options_get(oo, oe->name); if (o == NULL) continue; parent = 1; } else parent = 0; if (!options_isarray(o)) cmd_show_options_print(self, item, o, -1, parent); else if ((a = options_array_first(o)) == NULL) { if (!args_has(self->args, 'v')) { name = options_name(o); if (parent) cmdq_print(item, "%s*", name); else cmdq_print(item, "%s", name); } } else { while (a != NULL) { idx = options_array_item_index(a); cmd_show_options_print(self, item, o, idx, parent); a = options_array_next(a); } } } return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-source-file.c100644 001750 001750 00000005664 13535442560 0011360/* $OpenBSD$ */ /* * 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 #include #include #include #include "tmux.h" /* * Sources a configuration file. */ static enum cmd_retval cmd_source_file_exec(struct cmd *, struct cmdq_item *); static enum cmd_retval cmd_source_file_done(struct cmdq_item *, void *); const struct cmd_entry cmd_source_file_entry = { .name = "source-file", .alias = "source", .args = { "nqv", 1, -1 }, .usage = "[-nqv] path ...", .flags = 0, .exec = cmd_source_file_exec }; static enum cmd_retval cmd_source_file_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; int flags = 0; struct client *c = item->client; struct cmdq_item *new_item, *after; enum cmd_retval retval; char *pattern, *cwd; const char *path, *error; glob_t g; int i; u_int j; if (args_has(args, 'q')) flags |= CMD_PARSE_QUIET; if (args_has(args, 'n')) flags |= CMD_PARSE_PARSEONLY; if (args_has(args, 'v')) flags |= CMD_PARSE_VERBOSE; utf8_stravis(&cwd, server_client_get_cwd(c, NULL), VIS_GLOB); retval = CMD_RETURN_NORMAL; for (i = 0; i < args->argc; i++) { path = args->argv[i]; if (*path == '/') pattern = xstrdup(path); else xasprintf(&pattern, "%s/%s", cwd, path); log_debug("%s: %s", __func__, pattern); if (glob(pattern, 0, NULL, &g) != 0) { error = strerror(errno); if (errno != ENOENT || (~flags & CMD_PARSE_QUIET)) { cmdq_error(item, "%s: %s", path, error); retval = CMD_RETURN_ERROR; } free(pattern); continue; } free(pattern); after = item; for (j = 0; j < g.gl_pathc; j++) { path = g.gl_pathv[j]; if (load_cfg(path, c, after, flags, &new_item) < 0) retval = CMD_RETURN_ERROR; else if (new_item != NULL) after = new_item; } globfree(&g); } if (cfg_finished) { if (retval == CMD_RETURN_ERROR && c->session == NULL) c->retval = 1; new_item = cmdq_get_callback(cmd_source_file_done, NULL); cmdq_insert_after(item, new_item); } free(cwd); return (retval); } static enum cmd_retval cmd_source_file_done(struct cmdq_item *item, __unused void *data) { cfg_print_causes(item); return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-split-window.c100644 001750 001750 00000011053 13570677043 0011575/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "tmux.h" /* * Split a window (add a new pane). */ #define SPLIT_WINDOW_TEMPLATE "#{session_name}:#{window_index}.#{pane_index}" static enum cmd_retval cmd_split_window_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_split_window_entry = { .name = "split-window", .alias = "splitw", .args = { "bc:de:fF:hIl:p:Pt:v", 0, -1 }, .usage = "[-bdefhIPv] [-c start-directory] [-e environment] " "[-F format] [-p percentage|-l size] " CMD_TARGET_PANE_USAGE " [command]", .target = { 't', CMD_FIND_PANE, 0 }, .flags = 0, .exec = cmd_split_window_exec }; static enum cmd_retval cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; struct cmd_find_state *current = &item->shared->current; struct spawn_context sc; struct client *c = cmd_find_client(item, NULL, 1); struct session *s = item->target.s; struct winlink *wl = item->target.wl; struct window_pane *wp = item->target.wp, *new_wp; enum layout_type type; struct layout_cell *lc; struct cmd_find_state fs; int size, percentage, flags, input; const char *template, *add; char *cause, *cp; struct args_value *value; if (args_has(args, 'h')) type = LAYOUT_LEFTRIGHT; else type = LAYOUT_TOPBOTTOM; if (args_has(args, 'l')) { size = args_strtonum(args, 'l', 0, INT_MAX, &cause); if (cause != NULL) { cmdq_error(item, "create pane failed: -l %s", cause); free(cause); return (CMD_RETURN_ERROR); } } else if (args_has(args, 'p')) { percentage = args_strtonum(args, 'p', 0, INT_MAX, &cause); if (cause != NULL) { cmdq_error(item, "create pane failed: -p %s", cause); free(cause); return (CMD_RETURN_ERROR); } if (type == LAYOUT_TOPBOTTOM) size = (wp->sy * percentage) / 100; else size = (wp->sx * percentage) / 100; } else size = -1; server_unzoom_window(wp->window); input = (args_has(args, 'I') && args->argc == 0); flags = 0; if (args_has(args, 'b')) flags |= SPAWN_BEFORE; if (args_has(args, 'f')) flags |= SPAWN_FULLSIZE; if (input || (args->argc == 1 && *args->argv[0] == '\0')) flags |= SPAWN_EMPTY; lc = layout_split_pane(wp, type, size, flags); if (lc == NULL) { cmdq_error(item, "no space for new pane"); return (CMD_RETURN_ERROR); } memset(&sc, 0, sizeof sc); sc.item = item; sc.s = s; sc.wl = wl; sc.wp0 = wp; sc.lc = lc; sc.name = NULL; sc.argc = args->argc; sc.argv = args->argv; sc.environ = environ_create(); add = args_first_value(args, 'e', &value); while (add != NULL) { environ_put(sc.environ, add); add = args_next_value(&value); } sc.idx = -1; sc.cwd = args_get(args, 'c'); sc.flags = flags; if (args_has(args, 'd')) sc.flags |= SPAWN_DETACHED; if ((new_wp = spawn_pane(&sc, &cause)) == NULL) { layout_close_pane(new_wp); cmdq_error(item, "create pane failed: %s", cause); free(cause); return (CMD_RETURN_ERROR); } if (input && window_pane_start_input(new_wp, item, &cause) != 0) { layout_close_pane(new_wp); window_remove_pane(wp->window, new_wp); cmdq_error(item, "%s", cause); free(cause); return (CMD_RETURN_ERROR); } if (!args_has(args, 'd')) cmd_find_from_winlink_pane(current, wl, new_wp, 0); server_redraw_window(wp->window); server_status_session(s); if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) template = SPLIT_WINDOW_TEMPLATE; cp = format_single(item, template, c, s, wl, new_wp); cmdq_print(item, "%s", cp); free(cp); } cmd_find_from_winlink_pane(&fs, wl, new_wp, 0); cmdq_insert_hook(s, item, &fs, "after-split-window"); environ_free(sc.environ); if (input) return (CMD_RETURN_WAIT); return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-swap-pane.c100644 001750 001750 00000007231 13570677043 0011033/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Swap two panes. */ static enum cmd_retval cmd_swap_pane_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_swap_pane_entry = { .name = "swap-pane", .alias = "swapp", .args = { "dDs:t:U", 0, 0 }, .usage = "[-dDU] " CMD_SRCDST_PANE_USAGE, .source = { 's', CMD_FIND_PANE, CMD_FIND_DEFAULT_MARKED }, .target = { 't', CMD_FIND_PANE, 0 }, .flags = 0, .exec = cmd_swap_pane_exec }; static enum cmd_retval cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) { 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_w = item->target.wl->window; dst_wp = item->target.wp; src_w = item->source.wl->window; src_wp = item->source.wp; server_unzoom_window(dst_w); if (args_has(self->args, 'D')) { src_w = dst_w; 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_w = dst_w; src_wp = TAILQ_PREV(dst_wp, window_panes, entry); if (src_wp == NULL) src_wp = TAILQ_LAST(&dst_w->panes, window_panes); } server_unzoom_window(src_w); if (src_wp == dst_wp) return (CMD_RETURN_NORMAL); 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; options_set_parent(src_wp->options, dst_w->options); src_wp->flags |= PANE_STYLECHANGED; dst_wp->window = src_w; options_set_parent(dst_wp->options, src_w->options); dst_wp->flags |= PANE_STYLECHANGED; 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, 1); window_set_active_pane(dst_w, src_wp, 1); } else { tmp_wp = dst_wp; window_set_active_pane(src_w, tmp_wp, 1); } } else { if (src_w->active == src_wp) window_set_active_pane(src_w, dst_wp, 1); if (dst_w->active == dst_wp) window_set_active_pane(dst_w, src_wp, 1); } 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 (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-swap-window.c100644 001750 001750 00000005121 13537640350 0011405/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Swap one window with another. */ static enum cmd_retval cmd_swap_window_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_swap_window_entry = { .name = "swap-window", .alias = "swapw", .args = { "ds:t:", 0, 0 }, .usage = "[-d] " CMD_SRCDST_WINDOW_USAGE, .source = { 's', CMD_FIND_WINDOW, CMD_FIND_DEFAULT_MARKED }, .target = { 't', CMD_FIND_WINDOW, 0 }, .flags = 0, .exec = cmd_swap_window_exec }; static enum cmd_retval cmd_swap_window_exec(struct cmd *self, struct cmdq_item *item) { struct session *src, *dst; struct session_group *sg_src, *sg_dst; struct winlink *wl_src, *wl_dst; struct window *w_src, *w_dst; wl_src = item->source.wl; src = item->source.s; sg_src = session_group_contains(src); wl_dst = item->target.wl; dst = item->target.s; sg_dst = session_group_contains(dst); if (src != dst && sg_src != NULL && sg_dst != NULL && sg_src == sg_dst) { cmdq_error(item, "can't move window, sessions are grouped"); return (CMD_RETURN_ERROR); } if (wl_dst->window == wl_src->window) return (CMD_RETURN_NORMAL); w_dst = wl_dst->window; TAILQ_REMOVE(&w_dst->winlinks, wl_dst, wentry); w_src = wl_src->window; TAILQ_REMOVE(&w_src->winlinks, wl_src, wentry); wl_dst->window = w_src; TAILQ_INSERT_TAIL(&w_src->winlinks, wl_dst, wentry); wl_src->window = w_dst; TAILQ_INSERT_TAIL(&w_dst->winlinks, wl_src, wentry); 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 (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-switch-client.c100644 001750 001750 00000007773 13570677043 0011730/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Switch client to a different session. */ static enum cmd_retval cmd_switch_client_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_switch_client_entry = { .name = "switch-client", .alias = "switchc", .args = { "lc:Enpt:rT:", 0, 0 }, .usage = "[-Elnpr] [-c target-client] [-t target-session] " "[-T key-table]", /* -t is special */ .flags = CMD_READONLY, .exec = cmd_switch_client_exec }; static enum cmd_retval cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; const char *tflag = args_get(args, 't'); enum cmd_find_type type; int flags; struct client *c; struct session *s; struct winlink *wl; struct window_pane *wp; const char *tablename; struct key_table *table; if ((c = cmd_find_client(item, args_get(args, 'c'), 0)) == NULL) return (CMD_RETURN_ERROR); if (tflag != NULL && tflag[strcspn(tflag, ":.%")] != '\0') { type = CMD_FIND_PANE; flags = 0; } else { type = CMD_FIND_SESSION; flags = CMD_FIND_PREFER_UNATTACHED; } if (cmd_find_target(&item->target, item, tflag, type, flags) != 0) return (CMD_RETURN_ERROR); s = item->target.s; wl = item->target.wl; wp = item->target.wp; if (args_has(args, 'r')) c->flags ^= CLIENT_READONLY; tablename = args_get(args, 'T'); if (tablename != NULL) { table = key_bindings_get_table(tablename, 0); if (table == NULL) { cmdq_error(item, "table %s doesn't exist", tablename); return (CMD_RETURN_ERROR); } table->references++; key_bindings_unref_table(c->keytable); c->keytable = table; return (CMD_RETURN_NORMAL); } if (args_has(args, 'n')) { if ((s = session_next_session(c->session)) == NULL) { cmdq_error(item, "can't find next session"); return (CMD_RETURN_ERROR); } } else if (args_has(args, 'p')) { if ((s = session_previous_session(c->session)) == NULL) { cmdq_error(item, "can't find previous session"); return (CMD_RETURN_ERROR); } } else if (args_has(args, 'l')) { if (c->last_session != NULL && session_alive(c->last_session)) s = c->last_session; else s = NULL; if (s == NULL) { cmdq_error(item, "can't find last session"); return (CMD_RETURN_ERROR); } } else { if (item->client == NULL) return (CMD_RETURN_NORMAL); if (wl != NULL) { server_unzoom_window(wl->window); if (wp != NULL) { window_redraw_active_switch(wp->window, wp); window_set_active_pane(wp->window, wp, 1); } session_set_current(s, wl); cmd_find_from_session(&item->shared->current, s, 0); } } if (!args_has(args, 'E')) environ_update(s->options, c->environ, s->environ); if (c->session != NULL && c->session != s) c->last_session = c->session; c->session = s; if (~item->shared->flags & CMDQ_SHARED_REPEAT) server_client_set_key_table(c, NULL); tty_update_client_offset(c); status_timer_start(c); notify_client("client-session-changed", c); session_update_activity(s, NULL); gettimeofday(&s->last_attached_time, NULL); recalculate_sizes(); server_check_unattached(); server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; alerts_check_session(s); return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-unbind-key.c100644 001750 001750 00000005074 13466230030 0011171/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Unbind key from command. */ static enum cmd_retval cmd_unbind_key_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_unbind_key_entry = { .name = "unbind-key", .alias = "unbind", .args = { "anT:", 0, 1 }, .usage = "[-an] [-T key-table] key", .flags = CMD_AFTERHOOK, .exec = cmd_unbind_key_exec }; static enum cmd_retval cmd_unbind_key_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; key_code key; const char *tablename; if (!args_has(args, 'a')) { if (args->argc != 1) { cmdq_error(item, "missing key"); return (CMD_RETURN_ERROR); } key = key_string_lookup_string(args->argv[0]); if (key == KEYC_NONE || key == KEYC_UNKNOWN) { cmdq_error(item, "unknown key: %s", args->argv[0]); return (CMD_RETURN_ERROR); } } else { if (args->argc != 0) { cmdq_error(item, "key given with -a"); return (CMD_RETURN_ERROR); } key = KEYC_UNKNOWN; } if (key == KEYC_UNKNOWN) { tablename = args_get(args, 'T'); if (tablename == NULL) { key_bindings_remove_table("root"); key_bindings_remove_table("prefix"); return (CMD_RETURN_NORMAL); } if (key_bindings_get_table(tablename, 0) == NULL) { cmdq_error(item, "table %s doesn't exist", tablename); return (CMD_RETURN_ERROR); } key_bindings_remove_table(tablename); return (CMD_RETURN_NORMAL); } if (args_has(args, 'T')) { tablename = args_get(args, 'T'); if (key_bindings_get_table(tablename, 0) == NULL) { cmdq_error(item, "table %s doesn't exist", tablename); return (CMD_RETURN_ERROR); } } else if (args_has(args, 'n')) tablename = "root"; else tablename = "prefix"; key_bindings_remove(tablename, key); return (CMD_RETURN_NORMAL); } tmux-3.0a/cmd-wait-for.c100644 001750 001750 00000014355 13517540563 0010672/* $OpenBSD$ */ /* * Copyright (c) 2013 Nicholas Marriott * Copyright (c) 2013 Thiago de Arruda * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN 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" /* * Block or wake a client on a named wait channel. */ static enum cmd_retval cmd_wait_for_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_wait_for_entry = { .name = "wait-for", .alias = "wait", .args = { "LSU", 1, 1 }, .usage = "[-L|-S|-U] channel", .flags = 0, .exec = cmd_wait_for_exec }; struct wait_item { struct cmdq_item *item; TAILQ_ENTRY(wait_item) entry; }; struct wait_channel { const char *name; int locked; int woken; TAILQ_HEAD(, wait_item) waiters; TAILQ_HEAD(, wait_item) lockers; RB_ENTRY(wait_channel) entry; }; RB_HEAD(wait_channels, wait_channel); static struct wait_channels wait_channels = RB_INITIALIZER(wait_channels); static int wait_channel_cmp(struct wait_channel *, struct wait_channel *); RB_GENERATE_STATIC(wait_channels, wait_channel, entry, wait_channel_cmp); static int wait_channel_cmp(struct wait_channel *wc1, struct wait_channel *wc2) { return (strcmp(wc1->name, wc2->name)); } static enum cmd_retval cmd_wait_for_signal(struct cmdq_item *, const char *, struct wait_channel *); static enum cmd_retval cmd_wait_for_wait(struct cmdq_item *, const char *, struct wait_channel *); static enum cmd_retval cmd_wait_for_lock(struct cmdq_item *, const char *, struct wait_channel *); static enum cmd_retval cmd_wait_for_unlock(struct cmdq_item *, const char *, struct wait_channel *); static struct wait_channel *cmd_wait_for_add(const char *); static void cmd_wait_for_remove(struct wait_channel *); static struct wait_channel * cmd_wait_for_add(const char *name) { struct wait_channel *wc; wc = xmalloc(sizeof *wc); wc->name = xstrdup(name); wc->locked = 0; wc->woken = 0; TAILQ_INIT(&wc->waiters); TAILQ_INIT(&wc->lockers); RB_INSERT(wait_channels, &wait_channels, wc); log_debug("add wait channel %s", wc->name); return (wc); } static void cmd_wait_for_remove(struct wait_channel *wc) { if (wc->locked) return; if (!TAILQ_EMPTY(&wc->waiters) || !wc->woken) return; log_debug("remove wait channel %s", wc->name); RB_REMOVE(wait_channels, &wait_channels, wc); free((void *)wc->name); free(wc); } static enum cmd_retval cmd_wait_for_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; const char *name = args->argv[0]; struct wait_channel *wc, wc0; wc0.name = name; wc = RB_FIND(wait_channels, &wait_channels, &wc0); if (args_has(args, 'S')) return (cmd_wait_for_signal(item, name, wc)); if (args_has(args, 'L')) return (cmd_wait_for_lock(item, name, wc)); if (args_has(args, 'U')) return (cmd_wait_for_unlock(item, name, wc)); return (cmd_wait_for_wait(item, name, wc)); } static enum cmd_retval cmd_wait_for_signal(__unused struct cmdq_item *item, const char *name, struct wait_channel *wc) { struct wait_item *wi, *wi1; if (wc == NULL) wc = cmd_wait_for_add(name); if (TAILQ_EMPTY(&wc->waiters) && !wc->woken) { log_debug("signal wait channel %s, no waiters", wc->name); wc->woken = 1; return (CMD_RETURN_NORMAL); } log_debug("signal wait channel %s, with waiters", wc->name); TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) { cmdq_continue(wi->item); TAILQ_REMOVE(&wc->waiters, wi, entry); free(wi); } cmd_wait_for_remove(wc); return (CMD_RETURN_NORMAL); } static enum cmd_retval cmd_wait_for_wait(struct cmdq_item *item, const char *name, struct wait_channel *wc) { struct client *c = item->client; struct wait_item *wi; if (c == NULL) { cmdq_error(item, "not able to wait"); return (CMD_RETURN_ERROR); } if (wc == NULL) wc = cmd_wait_for_add(name); if (wc->woken) { log_debug("wait channel %s already woken (%p)", wc->name, c); cmd_wait_for_remove(wc); return (CMD_RETURN_NORMAL); } log_debug("wait channel %s not woken (%p)", wc->name, c); wi = xcalloc(1, sizeof *wi); wi->item = item; TAILQ_INSERT_TAIL(&wc->waiters, wi, entry); return (CMD_RETURN_WAIT); } static enum cmd_retval cmd_wait_for_lock(struct cmdq_item *item, const char *name, struct wait_channel *wc) { struct wait_item *wi; if (item->client == NULL) { cmdq_error(item, "not able to lock"); return (CMD_RETURN_ERROR); } if (wc == NULL) wc = cmd_wait_for_add(name); if (wc->locked) { wi = xcalloc(1, sizeof *wi); wi->item = item; TAILQ_INSERT_TAIL(&wc->lockers, wi, entry); return (CMD_RETURN_WAIT); } wc->locked = 1; return (CMD_RETURN_NORMAL); } static enum cmd_retval cmd_wait_for_unlock(struct cmdq_item *item, const char *name, struct wait_channel *wc) { struct wait_item *wi; if (wc == NULL || !wc->locked) { cmdq_error(item, "channel %s not locked", name); return (CMD_RETURN_ERROR); } if ((wi = TAILQ_FIRST(&wc->lockers)) != NULL) { cmdq_continue(wi->item); TAILQ_REMOVE(&wc->lockers, wi, entry); free(wi); } else { wc->locked = 0; cmd_wait_for_remove(wc); } return (CMD_RETURN_NORMAL); } void cmd_wait_for_flush(void) { struct wait_channel *wc, *wc1; struct wait_item *wi, *wi1; RB_FOREACH_SAFE(wc, wait_channels, &wait_channels, wc1) { TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) { cmdq_continue(wi->item); TAILQ_REMOVE(&wc->waiters, wi, entry); free(wi); } wc->woken = 1; TAILQ_FOREACH_SAFE(wi, &wc->lockers, entry, wi1) { cmdq_continue(wi->item); TAILQ_REMOVE(&wc->lockers, wi, entry); free(wi); } wc->locked = 0; cmd_wait_for_remove(wc); } } tmux-3.0a/colour.c100644 001750 001750 00000023362 13517540563 0007702/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott * Copyright (c) 2016 Avi Halachmi * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN 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" static int colour_dist_sq(int R, int G, int B, int r, int g, int b) { return ((R - r) * (R - r) + (G - g) * (G - g) + (B - b) * (B - b)); } static int colour_to_6cube(int v) { if (v < 48) return (0); if (v < 114) return (1); return ((v - 35) / 40); } /* * Convert an RGB triplet to the xterm(1) 256 colour palette. * * xterm provides a 6x6x6 colour cube (16 - 231) and 24 greys (232 - 255). We * map our RGB colour to the closest in the cube, also work out the closest * grey, and use the nearest of the two. * * Note that the xterm has much lower resolution for darker colours (they are * not evenly spread out), so our 6 levels are not evenly spread: 0x0, 0x5f * (95), 0x87 (135), 0xaf (175), 0xd7 (215) and 0xff (255). Greys are more * evenly spread (8, 18, 28 ... 238). */ int colour_find_rgb(u_char r, u_char g, u_char b) { static const int q2c[6] = { 0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff }; int qr, qg, qb, cr, cg, cb, d, idx; int grey_avg, grey_idx, grey; /* Map RGB to 6x6x6 cube. */ qr = colour_to_6cube(r); cr = q2c[qr]; qg = colour_to_6cube(g); cg = q2c[qg]; qb = colour_to_6cube(b); cb = q2c[qb]; /* If we have hit the colour exactly, return early. */ if (cr == r && cg == g && cb == b) return ((16 + (36 * qr) + (6 * qg) + qb) | COLOUR_FLAG_256); /* Work out the closest grey (average of RGB). */ grey_avg = (r + g + b) / 3; if (grey_avg > 238) grey_idx = 23; else grey_idx = (grey_avg - 3) / 10; grey = 8 + (10 * grey_idx); /* Is grey or 6x6x6 colour closest? */ d = colour_dist_sq(cr, cg, cb, r, g, b); if (colour_dist_sq(grey, grey, grey, r, g, b) < d) idx = 232 + grey_idx; else idx = 16 + (36 * qr) + (6 * qg) + qb; return (idx | COLOUR_FLAG_256); } /* Join RGB into a colour. */ int colour_join_rgb(u_char r, u_char g, u_char b) { return ((((int)((r) & 0xff)) << 16) | (((int)((g) & 0xff)) << 8) | (((int)((b) & 0xff))) | COLOUR_FLAG_RGB); } /* Split colour into RGB. */ void colour_split_rgb(int c, u_char *r, u_char *g, u_char *b) { *r = (c >> 16) & 0xff; *g = (c >> 8) & 0xff; *b = c & 0xff; } /* Convert colour to a string. */ const char * colour_tostring(int c) { static char s[32]; u_char r, g, b; if (c & COLOUR_FLAG_RGB) { colour_split_rgb(c, &r, &g, &b); xsnprintf(s, sizeof s, "#%02x%02x%02x", r, g, b); return (s); } if (c & COLOUR_FLAG_256) { xsnprintf(s, sizeof s, "colour%u", c & 0xff); 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"); case 9: return ("terminal"); case 90: return ("brightblack"); case 91: return ("brightred"); case 92: return ("brightgreen"); case 93: return ("brightyellow"); case 94: return ("brightblue"); case 95: return ("brightmagenta"); case 96: return ("brightcyan"); case 97: return ("brightwhite"); } return ("invalid"); } /* Convert colour from string. */ int colour_fromstring(const char *s) { const char *errstr; const char *cp; int n; u_char r, g, b; 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", &r, &g, &b); if (n != 3) return (-1); return (colour_join_rgb(r, g, b)); } if (strncasecmp(s, "colour", (sizeof "colour") - 1) == 0) { n = strtonum(s + (sizeof "colour") - 1, 0, 255, &errstr); if (errstr != NULL) return (-1); return (n | COLOUR_FLAG_256); } if (strcasecmp(s, "default") == 0) return (8); if (strcasecmp(s, "terminal") == 0) return (9); if (strcasecmp(s, "black") == 0 || strcmp(s, "0") == 0) return (0); if (strcasecmp(s, "red") == 0 || strcmp(s, "1") == 0) return (1); if (strcasecmp(s, "green") == 0 || strcmp(s, "2") == 0) return (2); if (strcasecmp(s, "yellow") == 0 || strcmp(s, "3") == 0) return (3); if (strcasecmp(s, "blue") == 0 || strcmp(s, "4") == 0) return (4); if (strcasecmp(s, "magenta") == 0 || strcmp(s, "5") == 0) return (5); if (strcasecmp(s, "cyan") == 0 || strcmp(s, "6") == 0) return (6); if (strcasecmp(s, "white") == 0 || strcmp(s, "7") == 0) return (7); if (strcasecmp(s, "brightblack") == 0 || strcmp(s, "90") == 0) return (90); if (strcasecmp(s, "brightred") == 0 || strcmp(s, "91") == 0) return (91); if (strcasecmp(s, "brightgreen") == 0 || strcmp(s, "92") == 0) return (92); if (strcasecmp(s, "brightyellow") == 0 || strcmp(s, "93") == 0) return (93); if (strcasecmp(s, "brightblue") == 0 || strcmp(s, "94") == 0) return (94); if (strcasecmp(s, "brightmagenta") == 0 || strcmp(s, "95") == 0) return (95); if (strcasecmp(s, "brightcyan") == 0 || strcmp(s, "96") == 0) return (96); if (strcasecmp(s, "brightwhite") == 0 || strcmp(s, "97") == 0) return (97); return (-1); } /* Convert 256 colour to RGB colour. */ int colour_256toRGB(int c) { static const int table[256] = { 0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080, 0x008080, 0xc0c0c0, 0x808080, 0xff0000, 0x00ff00, 0xffff00, 0x0000ff, 0xff00ff, 0x00ffff, 0xffffff, 0x000000, 0x00005f, 0x000087, 0x0000af, 0x0000d7, 0x0000ff, 0x005f00, 0x005f5f, 0x005f87, 0x005faf, 0x005fd7, 0x005fff, 0x008700, 0x00875f, 0x008787, 0x0087af, 0x0087d7, 0x0087ff, 0x00af00, 0x00af5f, 0x00af87, 0x00afaf, 0x00afd7, 0x00afff, 0x00d700, 0x00d75f, 0x00d787, 0x00d7af, 0x00d7d7, 0x00d7ff, 0x00ff00, 0x00ff5f, 0x00ff87, 0x00ffaf, 0x00ffd7, 0x00ffff, 0x5f0000, 0x5f005f, 0x5f0087, 0x5f00af, 0x5f00d7, 0x5f00ff, 0x5f5f00, 0x5f5f5f, 0x5f5f87, 0x5f5faf, 0x5f5fd7, 0x5f5fff, 0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af, 0x5f87d7, 0x5f87ff, 0x5faf00, 0x5faf5f, 0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff, 0x5fd700, 0x5fd75f, 0x5fd787, 0x5fd7af, 0x5fd7d7, 0x5fd7ff, 0x5fff00, 0x5fff5f, 0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff, 0x870000, 0x87005f, 0x870087, 0x8700af, 0x8700d7, 0x8700ff, 0x875f00, 0x875f5f, 0x875f87, 0x875faf, 0x875fd7, 0x875fff, 0x878700, 0x87875f, 0x878787, 0x8787af, 0x8787d7, 0x8787ff, 0x87af00, 0x87af5f, 0x87af87, 0x87afaf, 0x87afd7, 0x87afff, 0x87d700, 0x87d75f, 0x87d787, 0x87d7af, 0x87d7d7, 0x87d7ff, 0x87ff00, 0x87ff5f, 0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff, 0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af, 0xaf00d7, 0xaf00ff, 0xaf5f00, 0xaf5f5f, 0xaf5f87, 0xaf5faf, 0xaf5fd7, 0xaf5fff, 0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af, 0xaf87d7, 0xaf87ff, 0xafaf00, 0xafaf5f, 0xafaf87, 0xafafaf, 0xafafd7, 0xafafff, 0xafd700, 0xafd75f, 0xafd787, 0xafd7af, 0xafd7d7, 0xafd7ff, 0xafff00, 0xafff5f, 0xafff87, 0xafffaf, 0xafffd7, 0xafffff, 0xd70000, 0xd7005f, 0xd70087, 0xd700af, 0xd700d7, 0xd700ff, 0xd75f00, 0xd75f5f, 0xd75f87, 0xd75faf, 0xd75fd7, 0xd75fff, 0xd78700, 0xd7875f, 0xd78787, 0xd787af, 0xd787d7, 0xd787ff, 0xd7af00, 0xd7af5f, 0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff, 0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af, 0xd7d7d7, 0xd7d7ff, 0xd7ff00, 0xd7ff5f, 0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff, 0xff0000, 0xff005f, 0xff0087, 0xff00af, 0xff00d7, 0xff00ff, 0xff5f00, 0xff5f5f, 0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff, 0xff8700, 0xff875f, 0xff8787, 0xff87af, 0xff87d7, 0xff87ff, 0xffaf00, 0xffaf5f, 0xffaf87, 0xffafaf, 0xffafd7, 0xffafff, 0xffd700, 0xffd75f, 0xffd787, 0xffd7af, 0xffd7d7, 0xffd7ff, 0xffff00, 0xffff5f, 0xffff87, 0xffffaf, 0xffffd7, 0xffffff, 0x080808, 0x121212, 0x1c1c1c, 0x262626, 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e, 0x585858, 0x626262, 0x6c6c6c, 0x767676, 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e, 0xa8a8a8, 0xb2b2b2, 0xbcbcbc, 0xc6c6c6, 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee }; return (table[c & 0xff] | COLOUR_FLAG_RGB); } /* Convert 256 colour to 16 colour. */ int colour_256to16(int c) { static const 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 & 0xff]); } tmux-3.0a/compat.h100644 001750 001750 00000016743 13504653146 0007672/* * 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 #include #include #include #include #include #include #include #ifndef __GNUC__ #define __attribute__(a) #endif #ifndef __unused #define __unused __attribute__ ((__unused__)) #endif #ifndef __dead #define __dead __attribute__ ((__noreturn__)) #endif #ifndef __packed #define __packed __attribute__ ((__packed__)) #endif #ifndef ECHOPRT #define ECHOPRT 0 #endif #ifndef ACCESSPERMS #define ACCESSPERMS (S_IRWXU|S_IRWXG|S_IRWXO) #endif #if !defined(FIONREAD) && defined(__sun) #include #endif #ifdef HAVE_ERR_H #include #else void err(int, const char *, ...); void errx(int, const char *, ...); void warn(const char *, ...); void warnx(const char *, ...); #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/" #define _PATH_DEFPATH "/usr/bin:/bin" #endif #ifndef __OpenBSD__ #define pledge(s, p) (0) #endif #ifdef HAVE_STDINT_H #include #else #include #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_LIBUTIL_H #include #endif #ifdef HAVE_PTY_H #include #endif #ifdef HAVE_UTIL_H #include #endif #ifdef HAVE_VIS #include #else #include "compat/vis.h" #endif #ifdef HAVE_IMSG #include #else #include "compat/imsg.h" #endif #ifdef BROKEN_CMSG_FIRSTHDR #undef CMSG_FIRSTHDR #define CMSG_FIRSTHDR(mhdr) \ ((mhdr)->msg_controllen >= sizeof(struct cmsghdr) ? \ (struct cmsghdr *)(mhdr)->msg_control : \ (struct cmsghdr *)NULL) #endif #ifndef CMSG_ALIGN #ifdef _CMSG_DATA_ALIGN #define CMSG_ALIGN _CMSG_DATA_ALIGN #else #define CMSG_ALIGN(len) (((len) + sizeof(long) - 1) & ~(sizeof(long) - 1)) #endif #endif #ifndef CMSG_SPACE #define CMSG_SPACE(len) (CMSG_ALIGN(sizeof(struct cmsghdr)) + CMSG_ALIGN(len)) #endif #ifndef CMSG_LEN #define CMSG_LEN(len) (CMSG_ALIGN(sizeof(struct cmsghdr)) + (len)) #endif #ifndef O_DIRECTORY #define O_DIRECTORY 0 #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 timersub #define timersub(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 < 0) { \ (vvp)->tv_sec--; \ (vvp)->tv_usec += 1000000; \ } \ } while (0) #endif #ifndef TTY_NAME_MAX #define TTY_NAME_MAX 32 #endif #ifndef HOST_NAME_MAX #define HOST_NAME_MAX 255 #endif #ifndef HAVE_FLOCK #define LOCK_SH 0 #define LOCK_EX 0 #define LOCK_NB 0 #define flock(fd, op) (0) #endif #ifndef HAVE_EXPLICIT_BZERO /* explicit_bzero.c */ void explicit_bzero(void *, size_t); #endif #ifndef HAVE_GETDTABLECOUNT /* getdtablecount.c */ int getdtablecount(void); #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_STRNLEN /* strnlen.c */ size_t strnlen(const char *, size_t); #endif #ifndef HAVE_STRNDUP /* strndup.c */ char *strndup(const char *, size_t); #endif #ifndef HAVE_MEMMEM /* memmem.c */ void *memmem(const void *, size_t, const void *, size_t); #endif #ifndef HAVE_DAEMON /* daemon.c */ int daemon(int, int); #endif #ifndef HAVE_GETPROGNAME /* getprogname.c */ const char *getprogname(void); #endif #ifndef HAVE_SETPROCTITLE /* setproctitle.c */ void setproctitle(const char *, ...); #endif #ifndef HAVE_B64_NTOP /* base64.c */ #undef b64_ntop #undef b64_pton int b64_ntop(const char *, size_t, char *, size_t); int b64_pton(const char *, u_char *, size_t); #endif #ifndef HAVE_FDFORKPTY /* fdforkpty.c */ int getptmfd(void); pid_t fdforkpty(int, int *, char *, struct termios *, struct winsize *); #endif #ifndef HAVE_FORKPTY /* forkpty.c */ 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 #ifndef HAVE_CFMAKERAW /* cfmakeraw.c */ void cfmakeraw(struct termios *); #endif #ifndef HAVE_FREEZERO /* freezero.c */ void freezero(void *, size_t); #endif #ifndef HAVE_REALLOCARRAY /* reallocarray.c */ void *reallocarray(void *, size_t, size_t); #endif #ifndef HAVE_RECALLOCARRAY /* recallocarray.c */ void *recallocarray(void *, size_t, size_t, size_t); #endif #ifdef HAVE_UTF8PROC /* utf8proc.c */ int utf8proc_wcwidth(wchar_t); int utf8proc_mbtowc(wchar_t *, const char *, size_t); int utf8proc_wctomb(char *, wchar_t); #endif #ifndef HAVE_GETOPT /* 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 */ tmux-3.0a/control-notify.c100644 001750 001750 00000013565 13517540563 0011371/* $OpenBSD$ */ /* * Copyright (c) 2012 Nicholas Marriott * Copyright (c) 2012 George Nachman * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN 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" #define CONTROL_SHOULD_NOTIFY_CLIENT(c) \ ((c) != NULL && ((c)->flags & CLIENT_CONTROL)) void control_notify_input(struct client *c, struct window_pane *wp, const u_char *buf, size_t len) { struct evbuffer *message; u_int i; if (c->session == NULL) return; if (c->flags & CLIENT_CONTROL_NOOUTPUT) return; /* * Only write input if the window pane is linked to a window belonging * to the client's session. */ if (winlink_find_by_window(&c->session->windows, wp->window) != NULL) { message = evbuffer_new(); if (message == NULL) fatalx("out of memory"); evbuffer_add_printf(message, "%%output %%%u ", wp->id); for (i = 0; i < len; i++) { if (buf[i] < ' ' || buf[i] == '\\') evbuffer_add_printf(message, "\\%03o", buf[i]); else evbuffer_add_printf(message, "%c", buf[i]); } control_write_buffer(c, message); evbuffer_free(message); } } void control_notify_pane_mode_changed(int pane) { struct client *c; TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) continue; control_write(c, "%%pane-mode-changed %%%u", pane); } } void control_notify_window_layout_changed(struct window *w) { struct client *c; struct session *s; struct winlink *wl; const char *template; char *cp; template = "%layout-change #{window_id} #{window_layout} " "#{window_visible_layout} #{window_flags}"; TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) continue; s = c->session; if (winlink_find_by_window_id(&s->windows, w->id) == NULL) continue; /* * When the last pane in a window is closed it won't have a * layout root and we don't need to inform the client about the * layout change because the whole window will go away soon. */ if (w->layout_root == NULL) continue; wl = winlink_find_by_window(&s->windows, w); if (wl != NULL) { cp = format_single(NULL, template, c, NULL, wl, NULL); control_write(c, "%s", cp); free(cp); } } } void control_notify_window_pane_changed(struct window *w) { struct client *c; TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) continue; control_write(c, "%%window-pane-changed @%u %%%u", w->id, w->active->id); } } void control_notify_window_unlinked(__unused struct session *s, struct window *w) { struct client *c; struct session *cs; TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) continue; cs = c->session; if (winlink_find_by_window_id(&cs->windows, w->id) != NULL) control_write(c, "%%window-close @%u", w->id); else control_write(c, "%%unlinked-window-close @%u", w->id); } } void control_notify_window_linked(__unused struct session *s, struct window *w) { struct client *c; struct session *cs; TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) continue; cs = c->session; if (winlink_find_by_window_id(&cs->windows, w->id) != NULL) control_write(c, "%%window-add @%u", w->id); else control_write(c, "%%unlinked-window-add @%u", w->id); } } void control_notify_window_renamed(struct window *w) { struct client *c; struct session *cs; TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) continue; cs = c->session; if (winlink_find_by_window_id(&cs->windows, w->id) != NULL) { control_write(c, "%%window-renamed @%u %s", w->id, w->name); } else { control_write(c, "%%unlinked-window-renamed @%u %s", w->id, w->name); } } } void control_notify_client_session_changed(struct client *cc) { struct client *c; struct session *s; if (cc->session == NULL) return; s = cc->session; TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) continue; if (cc == c) { control_write(c, "%%session-changed $%u %s", s->id, s->name); } else { control_write(c, "%%client-session-changed %s $%u %s", cc->name, s->id, s->name); } } } void control_notify_session_renamed(struct session *s) { struct client *c; TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) continue; control_write(c, "%%session-renamed $%u %s", s->id, s->name); } } void control_notify_session_created(__unused struct session *s) { struct client *c; TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) continue; control_write(c, "%%sessions-changed"); } } void control_notify_session_closed(__unused struct session *s) { struct client *c; TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) continue; control_write(c, "%%sessions-changed"); } } void control_notify_session_window_changed(struct session *s) { struct client *c; TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) continue; control_write(c, "%%session-window-changed $%u @%u", s->id, s->curw->window->id); } } tmux-3.0a/control.c100644 001750 001750 00000005244 13517540563 0010056/* $OpenBSD$ */ /* * Copyright (c) 2012 Nicholas Marriott * Copyright (c) 2012 George Nachman * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN 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" /* Write a line. */ void control_write(struct client *c, const char *fmt, ...) { va_list ap; va_start(ap, fmt); evbuffer_add_vprintf(c->stdout_data, fmt, ap); va_end(ap); evbuffer_add(c->stdout_data, "\n", 1); server_client_push_stdout(c); } /* Write a buffer, adding a terminal newline. Empties buffer. */ void control_write_buffer(struct client *c, struct evbuffer *buffer) { evbuffer_add_buffer(c->stdout_data, buffer); evbuffer_add(c->stdout_data, "\n", 1); server_client_push_stdout(c); } /* Control error callback. */ static enum cmd_retval control_error(struct cmdq_item *item, void *data) { struct client *c = item->client; char *error = data; cmdq_guard(item, "begin", 1); control_write(c, "parse error: %s", error); cmdq_guard(item, "error", 1); free(error); return (CMD_RETURN_NORMAL); } /* Control input callback. Read lines and fire commands. */ void control_callback(struct client *c, int closed, __unused void *data) { char *line; struct cmdq_item *item; struct cmd_parse_result *pr; if (closed) c->flags |= CLIENT_EXIT; for (;;) { line = evbuffer_readln(c->stdin_data, NULL, EVBUFFER_EOL_LF); if (line == NULL) break; if (*line == '\0') { /* empty line exit */ free(line); c->flags |= CLIENT_EXIT; break; } pr = cmd_parse_from_string(line, NULL); switch (pr->status) { case CMD_PARSE_EMPTY: break; case CMD_PARSE_ERROR: item = cmdq_get_callback(control_error, pr->error); cmdq_append(c, item); break; case CMD_PARSE_SUCCESS: item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0); item->shared->flags |= CMDQ_SHARED_CONTROL; cmdq_append(c, item); cmd_list_free(pr->cmdlist); break; } free(line); } } tmux-3.0a/environ.c100644 001750 001750 00000013304 13504653146 0010050/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" /* * Environment - manipulate a set of environment variables. */ RB_HEAD(environ, environ_entry); static int environ_cmp(struct environ_entry *, struct environ_entry *); RB_GENERATE_STATIC(environ, environ_entry, entry, environ_cmp); static int environ_cmp(struct environ_entry *envent1, struct environ_entry *envent2) { return (strcmp(envent1->name, envent2->name)); } /* Initialise the environment. */ struct environ * environ_create(void) { struct environ *env; env = xcalloc(1, sizeof *env); RB_INIT(env); return (env); } /* Free an environment. */ void environ_free(struct environ *env) { struct environ_entry *envent, *envent1; RB_FOREACH_SAFE(envent, environ, env, envent1) { RB_REMOVE(environ, env, envent); free(envent->name); free(envent->value); free(envent); } free(env); } struct environ_entry * environ_first(struct environ *env) { return (RB_MIN(environ, env)); } struct environ_entry * environ_next(struct environ_entry *envent) { return (RB_NEXT(environ, env, envent)); } /* Copy one environment into another. */ void environ_copy(struct environ *srcenv, struct environ *dstenv) { struct environ_entry *envent; RB_FOREACH(envent, environ, srcenv) { if (envent->value == NULL) environ_clear(dstenv, envent->name); else environ_set(dstenv, envent->name, "%s", envent->value); } } /* Find an environment variable. */ struct environ_entry * environ_find(struct environ *env, const char *name) { struct environ_entry envent; envent.name = (char *) name; return (RB_FIND(environ, env, &envent)); } /* Set an environment variable. */ void environ_set(struct environ *env, const char *name, const char *fmt, ...) { struct environ_entry *envent; va_list ap; va_start(ap, fmt); if ((envent = environ_find(env, name)) != NULL) { free(envent->value); xvasprintf(&envent->value, fmt, ap); } else { envent = xmalloc(sizeof *envent); envent->name = xstrdup(name); xvasprintf(&envent->value, fmt, ap); RB_INSERT(environ, env, envent); } va_end(ap); } /* Clear an environment variable. */ void environ_clear(struct environ *env, const char *name) { struct environ_entry *envent; if ((envent = environ_find(env, name)) != NULL) { free(envent->value); envent->value = NULL; } else { envent = xmalloc(sizeof *envent); envent->name = xstrdup(name); 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, "%s", value); free(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); free(envent->name); free(envent->value); free(envent); } /* Copy variables from a destination into a source * environment. */ void environ_update(struct options *oo, struct environ *src, struct environ *dst) { struct environ_entry *envent; struct options_entry *o; struct options_array_item *a; union options_value *ov; o = options_get(oo, "update-environment"); if (o == NULL) return; a = options_array_first(o); while (a != NULL) { ov = options_array_item_value(a); if ((envent = environ_find(src, ov->string)) == NULL) environ_clear(dst, ov->string); else environ_set(dst, envent->name, "%s", envent->value); a = options_array_next(a); } } /* Push environment into the real environment - use after fork(). */ void environ_push(struct environ *env) { struct environ_entry *envent; environ = xcalloc(1, sizeof *environ); RB_FOREACH(envent, environ, env) { if (envent->value != NULL && *envent->name != '\0') setenv(envent->name, envent->value, 1); } } /* Log the environment. */ void environ_log(struct environ *env, const char *fmt, ...) { struct environ_entry *envent; va_list ap; char *prefix; va_start(ap, fmt); vasprintf(&prefix, fmt, ap); va_end(ap); RB_FOREACH(envent, environ, env) { if (envent->value != NULL && *envent->name != '\0') { log_debug("%s%s=%s", prefix, envent->name, envent->value); } } free(prefix); } /* Create initial environment for new child. */ struct environ * environ_for_session(struct session *s, int no_TERM) { struct environ *env; const char *value; int idx; env = environ_create(); environ_copy(global_environ, env); if (s != NULL) environ_copy(s->environ, env); if (!no_TERM) { value = options_get_string(global_options, "default-terminal"); environ_set(env, "TERM", "%s", value); } if (s != NULL) idx = s->id; else idx = -1; environ_set(env, "TMUX", "%s,%ld,%d", socket_path, (long)getpid(), idx); return (env); } tmux-3.0a/format.c100644 001750 001750 00000155524 13570677043 0007700/* $OpenBSD$ */ /* * 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 #include #include #include #include #include #include #include #include #include #include "tmux.h" /* * Build a list of key-value pairs and use them to expand #{key} entries in a * string. */ struct format_entry; typedef void (*format_cb)(struct format_tree *, struct format_entry *); static char *format_job_get(struct format_tree *, const char *); static void format_job_timer(int, short, void *); static char *format_find(struct format_tree *, const char *, int); static void format_add_cb(struct format_tree *, const char *, format_cb); static void format_add_tv(struct format_tree *, const char *, struct timeval *); static int format_replace(struct format_tree *, const char *, size_t, char **, size_t *, size_t *); static void format_defaults_session(struct format_tree *, struct session *); static void format_defaults_client(struct format_tree *, struct client *); static void format_defaults_winlink(struct format_tree *, struct winlink *); /* Entry in format job tree. */ struct format_job { struct client *client; u_int tag; const char *cmd; const char *expanded; time_t last; char *out; int updated; struct job *job; int status; RB_ENTRY(format_job) entry; }; /* Format job tree. */ static struct event format_job_event; static int format_job_cmp(struct format_job *, struct format_job *); static RB_HEAD(format_job_tree, format_job) format_jobs = RB_INITIALIZER(); RB_GENERATE_STATIC(format_job_tree, format_job, entry, format_job_cmp); /* Format job tree comparison function. */ static int format_job_cmp(struct format_job *fj1, struct format_job *fj2) { if (fj1->tag < fj2->tag) return (-1); if (fj1->tag > fj2->tag) return (1); return (strcmp(fj1->cmd, fj2->cmd)); } /* Format modifiers. */ #define FORMAT_TIMESTRING 0x1 #define FORMAT_BASENAME 0x2 #define FORMAT_DIRNAME 0x4 #define FORMAT_QUOTE 0x8 #define FORMAT_LITERAL 0x10 #define FORMAT_EXPAND 0x20 #define FORMAT_EXPANDTIME 0x40 #define FORMAT_SESSIONS 0x80 #define FORMAT_WINDOWS 0x100 #define FORMAT_PANES 0x200 /* Limit on recursion. */ #define FORMAT_LOOP_LIMIT 10 /* Entry in format tree. */ struct format_entry { char *key; char *value; time_t t; format_cb cb; RB_ENTRY(format_entry) entry; }; /* Format entry tree. */ struct format_tree { struct client *c; struct session *s; struct winlink *wl; struct window *w; struct window_pane *wp; struct cmdq_item *item; struct client *client; u_int tag; int flags; time_t time; u_int loop; struct mouse_event m; RB_HEAD(format_entry_tree, format_entry) tree; }; static int format_entry_cmp(struct format_entry *, struct format_entry *); RB_GENERATE_STATIC(format_entry_tree, format_entry, entry, format_entry_cmp); /* Format modifier. */ struct format_modifier { char modifier[3]; u_int size; char **argv; int argc; }; /* Format entry tree comparison function. */ static int format_entry_cmp(struct format_entry *fe1, struct format_entry *fe2) { return (strcmp(fe1->key, fe2->key)); } /* Single-character uppercase aliases. */ static const char *format_upper[] = { NULL, /* A */ NULL, /* B */ NULL, /* C */ "pane_id", /* D */ NULL, /* E */ "window_flags", /* F */ NULL, /* G */ "host", /* H */ "window_index", /* I */ NULL, /* J */ NULL, /* K */ NULL, /* L */ NULL, /* M */ NULL, /* N */ NULL, /* O */ "pane_index", /* P */ NULL, /* Q */ NULL, /* R */ "session_name", /* S */ "pane_title", /* T */ NULL, /* U */ NULL, /* V */ "window_name", /* W */ NULL, /* X */ NULL, /* Y */ NULL /* Z */ }; /* Single-character lowercase aliases. */ static const char *format_lower[] = { NULL, /* a */ NULL, /* b */ NULL, /* c */ NULL, /* d */ NULL, /* e */ NULL, /* f */ NULL, /* g */ "host_short", /* h */ NULL, /* i */ NULL, /* j */ NULL, /* k */ NULL, /* l */ NULL, /* m */ NULL, /* n */ NULL, /* o */ NULL, /* p */ NULL, /* q */ NULL, /* r */ NULL, /* s */ NULL, /* t */ NULL, /* u */ NULL, /* v */ NULL, /* w */ NULL, /* x */ NULL, /* y */ NULL /* z */ }; /* Is logging enabled? */ static inline int format_logging(struct format_tree *ft) { return (log_get_level() != 0 || (ft->flags & FORMAT_VERBOSE)); } /* Log a message if verbose. */ static void printflike(3, 4) format_log1(struct format_tree *ft, const char *from, const char *fmt, ...) { va_list ap; char *s; static const char spaces[] = " "; if (!format_logging(ft)) return; va_start(ap, fmt); vasprintf(&s, fmt, ap); va_end(ap); log_debug("%s: %s", from, s); if (ft->item != NULL && (ft->flags & FORMAT_VERBOSE)) cmdq_print(ft->item, "#%.*s%s", ft->loop, spaces, s); free(s); } #define format_log(ft, fmt, ...) format_log1(ft, __func__, fmt, ##__VA_ARGS__) /* Format job update callback. */ static void format_job_update(struct job *job) { struct format_job *fj = job_get_data(job); struct evbuffer *evb = job_get_event(job)->input; char *line = NULL, *next; time_t t; while ((next = evbuffer_readline(evb)) != NULL) { free(line); line = next; } if (line == NULL) return; fj->updated = 1; free(fj->out); fj->out = line; log_debug("%s: %p %s: %s", __func__, fj, fj->cmd, fj->out); t = time(NULL); if (fj->status && fj->last != t) { if (fj->client != NULL) server_status_client(fj->client); fj->last = t; } } /* Format job complete callback. */ static void format_job_complete(struct job *job) { struct format_job *fj = job_get_data(job); struct evbuffer *evb = job_get_event(job)->input; char *line, *buf; size_t len; fj->job = NULL; buf = NULL; if ((line = evbuffer_readline(evb)) == NULL) { len = EVBUFFER_LENGTH(evb); buf = xmalloc(len + 1); if (len != 0) memcpy(buf, EVBUFFER_DATA(evb), len); buf[len] = '\0'; } else buf = line; log_debug("%s: %p %s: %s", __func__, fj, fj->cmd, buf); if (*buf != '\0' || !fj->updated) { free(fj->out); fj->out = buf; } else free(buf); if (fj->status) { if (fj->client != NULL) server_status_client(fj->client); fj->status = 0; } } /* Find a job. */ static char * format_job_get(struct format_tree *ft, const char *cmd) { struct format_job_tree *jobs; struct format_job fj0, *fj; time_t t; char *expanded; int force; if (ft->client == NULL) jobs = &format_jobs; else if (ft->client->jobs != NULL) jobs = ft->client->jobs; else { jobs = ft->client->jobs = xmalloc(sizeof *ft->client->jobs); RB_INIT(jobs); } fj0.tag = ft->tag; fj0.cmd = cmd; if ((fj = RB_FIND(format_job_tree, jobs, &fj0)) == NULL) { fj = xcalloc(1, sizeof *fj); fj->client = ft->client; fj->tag = ft->tag; fj->cmd = xstrdup(cmd); fj->expanded = NULL; xasprintf(&fj->out, "<'%s' not ready>", fj->cmd); RB_INSERT(format_job_tree, jobs, fj); } expanded = format_expand(ft, cmd); if (fj->expanded == NULL || strcmp(expanded, fj->expanded) != 0) { free((void *)fj->expanded); fj->expanded = xstrdup(expanded); force = 1; } else force = (ft->flags & FORMAT_FORCE); t = time(NULL); if (force && fj->job != NULL) job_free(fj->job); if (force || (fj->job == NULL && fj->last != t)) { fj->job = job_run(expanded, NULL, server_client_get_cwd(ft->client, NULL), format_job_update, format_job_complete, NULL, fj, JOB_NOWAIT); if (fj->job == NULL) { free(fj->out); xasprintf(&fj->out, "<'%s' didn't start>", fj->cmd); } fj->last = t; fj->updated = 0; } if (ft->flags & FORMAT_STATUS) fj->status = 1; free(expanded); return (format_expand(ft, fj->out)); } /* Remove old jobs. */ static void format_job_tidy(struct format_job_tree *jobs, int force) { struct format_job *fj, *fj1; time_t now; now = time(NULL); RB_FOREACH_SAFE(fj, format_job_tree, jobs, fj1) { if (!force && (fj->last > now || now - fj->last < 3600)) continue; RB_REMOVE(format_job_tree, jobs, fj); log_debug("%s: %s", __func__, fj->cmd); if (fj->job != NULL) job_free(fj->job); free((void *)fj->expanded); free((void *)fj->cmd); free(fj->out); free(fj); } } /* Remove old jobs for client. */ void format_lost_client(struct client *c) { if (c->jobs != NULL) format_job_tidy(c->jobs, 1); free(c->jobs); } /* Remove old jobs periodically. */ static void format_job_timer(__unused int fd, __unused short events, __unused void *arg) { struct client *c; struct timeval tv = { .tv_sec = 60 }; format_job_tidy(&format_jobs, 0); TAILQ_FOREACH(c, &clients, entry) { if (c->jobs != NULL) format_job_tidy(c->jobs, 0); } evtimer_del(&format_job_event); evtimer_add(&format_job_event, &tv); } /* Callback for host. */ static void format_cb_host(__unused struct format_tree *ft, struct format_entry *fe) { char host[HOST_NAME_MAX + 1]; if (gethostname(host, sizeof host) != 0) fe->value = xstrdup(""); else fe->value = xstrdup(host); } /* Callback for host_short. */ static void format_cb_host_short(__unused struct format_tree *ft, struct format_entry *fe) { char host[HOST_NAME_MAX + 1], *cp; if (gethostname(host, sizeof host) != 0) fe->value = xstrdup(""); else { if ((cp = strchr(host, '.')) != NULL) *cp = '\0'; fe->value = xstrdup(host); } } /* Callback for pid. */ static void format_cb_pid(__unused struct format_tree *ft, struct format_entry *fe) { xasprintf(&fe->value, "%ld", (long)getpid()); } /* Callback for session_alerts. */ static void format_cb_session_alerts(struct format_tree *ft, struct format_entry *fe) { struct session *s = ft->s; struct winlink *wl; char alerts[1024], tmp[16]; if (s == NULL) return; *alerts = '\0'; RB_FOREACH(wl, winlinks, &s->windows) { if ((wl->flags & WINLINK_ALERTFLAGS) == 0) continue; xsnprintf(tmp, sizeof tmp, "%u", wl->idx); if (*alerts != '\0') strlcat(alerts, ",", sizeof alerts); strlcat(alerts, tmp, sizeof alerts); if (wl->flags & WINLINK_ACTIVITY) strlcat(alerts, "#", sizeof alerts); if (wl->flags & WINLINK_BELL) strlcat(alerts, "!", sizeof alerts); if (wl->flags & WINLINK_SILENCE) strlcat(alerts, "~", sizeof alerts); } fe->value = xstrdup(alerts); } /* Callback for session_stack. */ static void format_cb_session_stack(struct format_tree *ft, struct format_entry *fe) { struct session *s = ft->s; struct winlink *wl; char result[1024], tmp[16]; if (s == NULL) return; xsnprintf(result, sizeof result, "%u", s->curw->idx); TAILQ_FOREACH(wl, &s->lastw, sentry) { xsnprintf(tmp, sizeof tmp, "%u", wl->idx); if (*result != '\0') strlcat(result, ",", sizeof result); strlcat(result, tmp, sizeof result); } fe->value = xstrdup(result); } /* Callback for window_stack_index. */ static void format_cb_window_stack_index(struct format_tree *ft, struct format_entry *fe) { struct session *s = ft->wl->session; struct winlink *wl; u_int idx; idx = 0; TAILQ_FOREACH(wl, &s->lastw, sentry) { idx++; if (wl == ft->wl) break; } if (wl != NULL) xasprintf(&fe->value, "%u", idx); else fe->value = xstrdup("0"); } /* Callback for window_layout. */ static void format_cb_window_layout(struct format_tree *ft, struct format_entry *fe) { struct window *w = ft->w; if (w == NULL) return; if (w->saved_layout_root != NULL) fe->value = layout_dump(w->saved_layout_root); else fe->value = layout_dump(w->layout_root); } /* Callback for window_visible_layout. */ static void format_cb_window_visible_layout(struct format_tree *ft, struct format_entry *fe) { struct window *w = ft->w; if (w == NULL) return; fe->value = layout_dump(w->layout_root); } /* Callback for pane_start_command. */ static void format_cb_start_command(struct format_tree *ft, struct format_entry *fe) { struct window_pane *wp = ft->wp; if (wp == NULL) return; fe->value = cmd_stringify_argv(wp->argc, wp->argv); } /* Callback for pane_current_command. */ static void format_cb_current_command(struct format_tree *ft, struct format_entry *fe) { struct window_pane *wp = ft->wp; char *cmd; if (wp == NULL) return; cmd = osdep_get_name(wp->fd, wp->tty); if (cmd == NULL || *cmd == '\0') { free(cmd); cmd = cmd_stringify_argv(wp->argc, wp->argv); if (cmd == NULL || *cmd == '\0') { free(cmd); cmd = xstrdup(wp->shell); } } fe->value = parse_window_name(cmd); free(cmd); } /* Callback for pane_current_path. */ static void format_cb_current_path(struct format_tree *ft, struct format_entry *fe) { struct window_pane *wp = ft->wp; char *cwd; if (wp == NULL) return; cwd = osdep_get_cwd(wp->fd); if (cwd != NULL) fe->value = xstrdup(cwd); } /* Callback for history_bytes. */ static void format_cb_history_bytes(struct format_tree *ft, struct format_entry *fe) { struct window_pane *wp = ft->wp; struct grid *gd; struct grid_line *gl; unsigned long long size; u_int i; if (wp == NULL) return; gd = wp->base.grid; size = 0; for (i = 0; i < gd->hsize; i++) { gl = grid_get_line(gd, i); size += gl->cellsize * sizeof *gl->celldata; size += gl->extdsize * sizeof *gl->extddata; } size += gd->hsize * sizeof *gl; xasprintf(&fe->value, "%llu", size); } /* Callback for pane_tabs. */ static void format_cb_pane_tabs(struct format_tree *ft, struct format_entry *fe) { struct window_pane *wp = ft->wp; struct evbuffer *buffer; u_int i; int size; if (wp == NULL) return; buffer = evbuffer_new(); if (buffer == NULL) fatalx("out of memory"); for (i = 0; i < wp->base.grid->sx; i++) { if (!bit_test(wp->base.tabs, i)) continue; if (EVBUFFER_LENGTH(buffer) > 0) evbuffer_add(buffer, ",", 1); evbuffer_add_printf(buffer, "%u", i); } if ((size = EVBUFFER_LENGTH(buffer)) != 0) xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer)); evbuffer_free(buffer); } /* Callback for session_group_list. */ static void format_cb_session_group_list(struct format_tree *ft, struct format_entry *fe) { struct session *s = ft->s; struct session_group *sg; struct session *loop; struct evbuffer *buffer; int size; if (s == NULL) return; sg = session_group_contains(s); if (sg == NULL) return; buffer = evbuffer_new(); if (buffer == NULL) fatalx("out of memory"); TAILQ_FOREACH(loop, &sg->sessions, gentry) { if (EVBUFFER_LENGTH(buffer) > 0) evbuffer_add(buffer, ",", 1); evbuffer_add_printf(buffer, "%s", loop->name); } if ((size = EVBUFFER_LENGTH(buffer)) != 0) xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer)); evbuffer_free(buffer); } /* Callback for pane_in_mode. */ static void format_cb_pane_in_mode(struct format_tree *ft, struct format_entry *fe) { struct window_pane *wp = ft->wp; u_int n = 0; struct window_mode_entry *wme; if (wp == NULL) return; TAILQ_FOREACH(wme, &wp->modes, entry) n++; xasprintf(&fe->value, "%u", n); } /* Callback for cursor_character. */ static void format_cb_cursor_character(struct format_tree *ft, struct format_entry *fe) { struct window_pane *wp = ft->wp; struct grid_cell gc; if (wp == NULL) return; grid_view_get_cell(wp->base.grid, wp->base.cx, wp->base.cy, &gc); if (~gc.flags & GRID_FLAG_PADDING) xasprintf(&fe->value, "%.*s", (int)gc.data.size, gc.data.data); } /* Callback for mouse_word. */ static void format_cb_mouse_word(struct format_tree *ft, struct format_entry *fe) { struct window_pane *wp; u_int x, y, end; struct grid *gd; struct grid_line *gl; struct grid_cell gc; const char *ws; struct utf8_data *ud = NULL; size_t size = 0; int found = 0; if (!ft->m.valid) return; wp = cmd_mouse_pane(&ft->m, NULL, NULL); if (wp == NULL) return; if (!TAILQ_EMPTY (&wp->modes)) return; if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0) return; gd = wp->base.grid; ws = options_get_string(global_s_options, "word-separators"); y = gd->hsize + y; for (;;) { grid_get_cell(gd, x, y, &gc); if (gc.flags & GRID_FLAG_PADDING) break; if (utf8_cstrhas(ws, &gc.data)) { found = 1; break; } if (x == 0) { if (y == 0) break; gl = &gd->linedata[y - 1]; if (~gl->flags & GRID_LINE_WRAPPED) break; y--; x = grid_line_length(gd, y); if (x == 0) break; } x--; } for (;;) { if (found) { end = grid_line_length(gd, y); if (end == 0 || x == end - 1) { if (y == gd->hsize + gd->sy - 1) break; gl = &gd->linedata[y]; if (~gl->flags & GRID_LINE_WRAPPED) break; y++; x = 0; } else x++; } found = 1; grid_get_cell(gd, x, y, &gc); if (gc.flags & GRID_FLAG_PADDING) break; if (utf8_cstrhas(ws, &gc.data)) break; ud = xreallocarray(ud, size + 2, sizeof *ud); memcpy(&ud[size++], &gc.data, sizeof *ud); } if (size != 0) { ud[size].size = 0; fe->value = utf8_tocstr(ud); free(ud); } } /* Callback for mouse_line. */ static void format_cb_mouse_line(struct format_tree *ft, struct format_entry *fe) { struct window_pane *wp; u_int x, y; struct grid *gd; struct grid_cell gc; struct utf8_data *ud = NULL; size_t size = 0; if (!ft->m.valid) return; wp = cmd_mouse_pane(&ft->m, NULL, NULL); if (wp == NULL) return; if (!TAILQ_EMPTY (&wp->modes)) return; if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0) return; gd = wp->base.grid; y = gd->hsize + y; for (x = 0; x < grid_line_length(gd, y); x++) { grid_get_cell(gd, x, y, &gc); if (gc.flags & GRID_FLAG_PADDING) break; ud = xreallocarray(ud, size + 2, sizeof *ud); memcpy(&ud[size++], &gc.data, sizeof *ud); } if (size != 0) { ud[size].size = 0; fe->value = utf8_tocstr(ud); free(ud); } } /* Merge a format tree. */ static void format_merge(struct format_tree *ft, struct format_tree *from) { struct format_entry *fe; RB_FOREACH(fe, format_entry_tree, &from->tree) { if (fe->value != NULL) format_add(ft, fe->key, "%s", fe->value); } } /* Add item bits to tree. */ static void format_create_add_item(struct format_tree *ft, struct cmdq_item *item) { struct mouse_event *m; struct window_pane *wp; u_int x, y; if (item->cmd != NULL) format_add(ft, "command", "%s", item->cmd->entry->name); if (item->shared == NULL) return; if (item->shared->formats != NULL) format_merge(ft, item->shared->formats); m = &item->shared->mouse; if (m->valid && ((wp = cmd_mouse_pane(m, NULL, NULL)) != NULL)) { format_add(ft, "mouse_pane", "%%%u", wp->id); if (cmd_mouse_at(wp, m, &x, &y, 0) == 0) { format_add(ft, "mouse_x", "%u", x); format_add(ft, "mouse_y", "%u", y); format_add_cb(ft, "mouse_word", format_cb_mouse_word); format_add_cb(ft, "mouse_line", format_cb_mouse_line); } } memcpy(&ft->m, m, sizeof ft->m); } /* Create a new tree. */ struct format_tree * format_create(struct client *c, struct cmdq_item *item, int tag, int flags) { struct format_tree *ft; const struct window_mode **wm; char tmp[64]; if (!event_initialized(&format_job_event)) { evtimer_set(&format_job_event, format_job_timer, NULL); format_job_timer(-1, 0, NULL); } ft = xcalloc(1, sizeof *ft); RB_INIT(&ft->tree); if (c != NULL) { ft->client = c; ft->client->references++; } ft->item = item; ft->tag = tag; ft->flags = flags; ft->time = time(NULL); format_add(ft, "version", "%s", VERSION); format_add_cb(ft, "host", format_cb_host); format_add_cb(ft, "host_short", format_cb_host_short); format_add_cb(ft, "pid", format_cb_pid); format_add(ft, "socket_path", "%s", socket_path); format_add_tv(ft, "start_time", &start_time); for (wm = all_window_modes; *wm != NULL; wm++) { if ((*wm)->default_format != NULL) { xsnprintf(tmp, sizeof tmp, "%s_format", (*wm)->name); tmp[strcspn(tmp, "-")] = '_'; format_add(ft, tmp, "%s", (*wm)->default_format); } } if (item != NULL) format_create_add_item(ft, item); return (ft); } /* Free a tree. */ void format_free(struct format_tree *ft) { struct format_entry *fe, *fe1; RB_FOREACH_SAFE(fe, format_entry_tree, &ft->tree, fe1) { RB_REMOVE(format_entry_tree, &ft->tree, fe); free(fe->value); free(fe->key); free(fe); } if (ft->client != NULL) server_client_unref(ft->client); free(ft); } /* Walk each format. */ void format_each(struct format_tree *ft, void (*cb)(const char *, const char *, void *), void *arg) { struct format_entry *fe; static char s[64]; RB_FOREACH(fe, format_entry_tree, &ft->tree) { if (fe->t != 0) { xsnprintf(s, sizeof s, "%lld", (long long)fe->t); cb(fe->key, fe->value, s); } else { if (fe->value == NULL && fe->cb != NULL) { fe->cb(ft, fe); if (fe->value == NULL) fe->value = xstrdup(""); } cb(fe->key, fe->value, arg); } } } /* Add a key-value pair. */ void format_add(struct format_tree *ft, const char *key, const char *fmt, ...) { struct format_entry *fe; struct format_entry *fe_now; va_list ap; fe = xmalloc(sizeof *fe); fe->key = xstrdup(key); fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe); if (fe_now != NULL) { free(fe->key); free(fe); free(fe_now->value); fe = fe_now; } fe->cb = NULL; fe->t = 0; va_start(ap, fmt); xvasprintf(&fe->value, fmt, ap); va_end(ap); } /* Add a key and time. */ static void format_add_tv(struct format_tree *ft, const char *key, struct timeval *tv) { struct format_entry *fe; struct format_entry *fe_now; fe = xmalloc(sizeof *fe); fe->key = xstrdup(key); fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe); if (fe_now != NULL) { free(fe->key); free(fe); free(fe_now->value); fe = fe_now; } fe->cb = NULL; fe->t = tv->tv_sec; fe->value = NULL; } /* Add a key and function. */ static void format_add_cb(struct format_tree *ft, const char *key, format_cb cb) { struct format_entry *fe; struct format_entry *fe_now; fe = xmalloc(sizeof *fe); fe->key = xstrdup(key); fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe); if (fe_now != NULL) { free(fe->key); free(fe); free(fe_now->value); fe = fe_now; } fe->cb = cb; fe->t = 0; fe->value = NULL; } /* Quote special characters in string. */ static char * format_quote(const char *s) { const char *cp; char *out, *at; at = out = xmalloc(strlen(s) * 2 + 1); for (cp = s; *cp != '\0'; cp++) { if (strchr("|&;<>()$`\\\"'*?[# =%", *cp) != NULL) *at++ = '\\'; *at++ = *cp; } *at = '\0'; return (out); } /* Find a format entry. */ static char * format_find(struct format_tree *ft, const char *key, int modifiers) { struct format_entry *fe, fe_find; struct environ_entry *envent; static char s[64]; struct options_entry *o; int idx; char *found, *saved; if (~modifiers & FORMAT_TIMESTRING) { o = options_parse_get(global_options, key, &idx, 0); if (o == NULL && ft->wp != NULL) o = options_parse_get(ft->wp->options, key, &idx, 0); if (o == NULL && ft->w != NULL) o = options_parse_get(ft->w->options, key, &idx, 0); if (o == NULL) o = options_parse_get(global_w_options, key, &idx, 0); if (o == NULL && ft->s != NULL) o = options_parse_get(ft->s->options, key, &idx, 0); if (o == NULL) o = options_parse_get(global_s_options, key, &idx, 0); if (o != NULL) { found = options_tostring(o, idx, 1); goto found; } } found = NULL; fe_find.key = (char *) key; fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find); if (fe != NULL) { if (modifiers & FORMAT_TIMESTRING) { if (fe->t == 0) return (NULL); ctime_r(&fe->t, s); s[strcspn(s, "\n")] = '\0'; found = xstrdup(s); goto found; } if (fe->t != 0) { xasprintf(&found, "%lld", (long long)fe->t); goto found; } if (fe->value == NULL && fe->cb != NULL) fe->cb(ft, fe); if (fe->value == NULL) fe->value = xstrdup(""); found = xstrdup(fe->value); goto found; } if (~modifiers & FORMAT_TIMESTRING) { envent = NULL; if (ft->s != NULL) envent = environ_find(ft->s->environ, key); if (envent == NULL) envent = environ_find(global_environ, key); if (envent != NULL && envent->value != NULL) { found = xstrdup(envent->value); goto found; } } return (NULL); found: if (found == NULL) return (NULL); if (modifiers & FORMAT_BASENAME) { saved = found; found = xstrdup(basename(saved)); free(saved); } if (modifiers & FORMAT_DIRNAME) { saved = found; found = xstrdup(dirname(saved)); free(saved); } if (modifiers & FORMAT_QUOTE) { saved = found; found = xstrdup(format_quote(saved)); free(saved); } return (found); } /* Skip until end. */ const char * format_skip(const char *s, const char *end) { int brackets = 0; for (; *s != '\0'; s++) { if (*s == '#' && s[1] == '{') brackets++; if (*s == '#' && strchr(",#{}", s[1]) != NULL) { s++; continue; } if (*s == '}') brackets--; if (strchr(end, *s) != NULL && brackets == 0) break; } if (*s == '\0') return (NULL); return (s); } /* Return left and right alternatives separated by commas. */ static int format_choose(struct format_tree *ft, const char *s, char **left, char **right, int expand) { const char *cp; char *left0, *right0; cp = format_skip(s, ","); if (cp == NULL) return (-1); left0 = xstrndup(s, cp - s); right0 = xstrdup(cp + 1); if (expand) { *left = format_expand(ft, left0); free(left0); *right = format_expand(ft, right0); free(right0); } else { *left = left0; *right = right0; } return (0); } /* Is this true? */ int format_true(const char *s) { if (s != NULL && *s != '\0' && (s[0] != '0' || s[1] != '\0')) return (1); return (0); } /* Check if modifier end. */ static int format_is_end(char c) { return (c == ';' || c == ':'); } /* Add to modifier list. */ static void format_add_modifier(struct format_modifier **list, u_int *count, const char *c, size_t n, char **argv, int argc) { struct format_modifier *fm; *list = xreallocarray(*list, (*count) + 1, sizeof **list); fm = &(*list)[(*count)++]; memcpy(fm->modifier, c, n); fm->modifier[n] = '\0'; fm->size = n; fm->argv = argv; fm->argc = argc; } /* Free modifier list. */ static void format_free_modifiers(struct format_modifier *list, u_int count) { u_int i; for (i = 0; i < count; i++) cmd_free_argv(list[i].argc, list[i].argv); free(list); } /* Build modifier list. */ static struct format_modifier * format_build_modifiers(struct format_tree *ft, const char **s, u_int *count) { const char *cp = *s, *end; struct format_modifier *list = NULL; char c, last[] = "X;:", **argv, *value; int argc; /* * Modifiers are a ; separated list of the forms: * l,m,C,b,d,t,q,E,T,S,W,P,<,> * =a * =/a * =/a/ * s/a/b/ * s/a/b * ||,&&,!=,==,<=,>= */ *count = 0; while (*cp != '\0' && *cp != ':') { /* Skip and separator character. */ if (*cp == ';') cp++; /* Check single character modifiers with no arguments. */ if (strchr("lbdtqETSWP<>", cp[0]) != NULL && format_is_end(cp[1])) { format_add_modifier(&list, count, cp, 1, NULL, 0); cp++; continue; } /* Then try double character with no arguments. */ if ((memcmp("||", cp, 2) == 0 || memcmp("&&", cp, 2) == 0 || memcmp("!=", cp, 2) == 0 || memcmp("==", cp, 2) == 0 || memcmp("<=", cp, 2) == 0 || memcmp(">=", cp, 2) == 0) && format_is_end(cp[2])) { format_add_modifier(&list, count, cp, 2, NULL, 0); cp += 2; continue; } /* Now try single character with arguments. */ if (strchr("mCs=", cp[0]) == NULL) break; c = cp[0]; /* No arguments provided. */ if (format_is_end(cp[1])) { format_add_modifier(&list, count, cp, 1, NULL, 0); cp++; continue; } argv = NULL; argc = 0; /* Single argument with no wrapper character. */ if (!ispunct(cp[1]) || cp[1] == '-') { end = format_skip(cp + 1, ":;"); if (end == NULL) break; argv = xcalloc(1, sizeof *argv); value = xstrndup(cp + 1, end - (cp + 1)); argv[0] = format_expand(ft, value); free(value); argc = 1; format_add_modifier(&list, count, &c, 1, argv, argc); cp = end; continue; } /* Multiple arguments with a wrapper character. */ last[0] = cp[1]; cp++; do { if (cp[0] == last[0] && format_is_end(cp[1])) { cp++; break; } end = format_skip(cp + 1, last); if (end == NULL) break; cp++; argv = xreallocarray (argv, argc + 1, sizeof *argv); value = xstrndup(cp, end - cp); argv[argc++] = format_expand(ft, value); free(value); cp = end; } while (!format_is_end(cp[0])); format_add_modifier(&list, count, &c, 1, argv, argc); } if (*cp != ':') { format_free_modifiers(list, *count); *count = 0; return (NULL); } *s = cp + 1; return list; } /* Match against an fnmatch(3) pattern or regular expression. */ static char * format_match(struct format_modifier *fm, const char *pattern, const char *text) { const char *s = ""; regex_t r; int flags = 0; if (fm->argc >= 1) s = fm->argv[0]; if (strchr(s, 'r') == NULL) { if (strchr(s, 'i') != NULL) flags |= FNM_CASEFOLD; if (fnmatch(pattern, text, flags) != 0) return (xstrdup("0")); } else { flags = REG_EXTENDED|REG_NOSUB; if (strchr(s, 'i') != NULL) flags |= REG_ICASE; if (regcomp(&r, pattern, flags) != 0) return (xstrdup("0")); if (regexec(&r, text, 0, NULL, 0) != 0) { regfree(&r); return (xstrdup("0")); } regfree(&r); } return (xstrdup("1")); } /* Perform substitution in string. */ static char * format_sub(struct format_modifier *fm, const char *text, const char *pattern, const char *with) { char *value; int flags = REG_EXTENDED; if (fm->argc >= 3 && strchr(fm->argv[2], 'i') != NULL) flags |= REG_ICASE; value = regsub(pattern, with, text, flags); if (value == NULL) return (xstrdup(text)); return (value); } /* Search inside pane. */ static char * format_search(struct format_modifier *fm, struct window_pane *wp, const char *s) { int ignore = 0, regex = 0; char *value; if (fm->argc >= 1) { if (strchr(fm->argv[0], 'i') != NULL) ignore = 1; if (strchr(fm->argv[0], 'r') != NULL) regex = 1; } xasprintf(&value, "%u", window_pane_search(wp, s, regex, ignore)); return (value); } /* Loop over sessions. */ static char * format_loop_sessions(struct format_tree *ft, const char *fmt) { struct client *c = ft->client; struct cmdq_item *item = ft->item; struct format_tree *nft; char *expanded, *value; size_t valuelen; struct session *s; value = xcalloc(1, 1); valuelen = 1; RB_FOREACH(s, sessions, &sessions) { format_log(ft, "session loop: $%u", s->id); nft = format_create(c, item, FORMAT_NONE, ft->flags); nft->loop = ft->loop; format_defaults(nft, ft->c, s, NULL, NULL); expanded = format_expand(nft, fmt); format_free(nft); valuelen += strlen(expanded); value = xrealloc(value, valuelen); strlcat(value, expanded, valuelen); free(expanded); } return (value); } /* Loop over windows. */ static char * format_loop_windows(struct format_tree *ft, const char *fmt) { struct client *c = ft->client; struct cmdq_item *item = ft->item; struct format_tree *nft; char *all, *active, *use, *expanded, *value; size_t valuelen; struct winlink *wl; struct window *w; if (ft->s == NULL) { format_log(ft, "window loop but no session"); return (NULL); } if (format_choose(ft, fmt, &all, &active, 0) != 0) { all = xstrdup(fmt); active = NULL; } value = xcalloc(1, 1); valuelen = 1; RB_FOREACH(wl, winlinks, &ft->s->windows) { w = wl->window; format_log(ft, "window loop: %u @%u", wl->idx, w->id); if (active != NULL && wl == ft->s->curw) use = active; else use = all; nft = format_create(c, item, FORMAT_WINDOW|w->id, ft->flags); nft->loop = ft->loop; format_defaults(nft, ft->c, ft->s, wl, NULL); expanded = format_expand(nft, use); format_free(nft); valuelen += strlen(expanded); value = xrealloc(value, valuelen); strlcat(value, expanded, valuelen); free(expanded); } free(active); free(all); return (value); } /* Loop over panes. */ static char * format_loop_panes(struct format_tree *ft, const char *fmt) { struct client *c = ft->client; struct cmdq_item *item = ft->item; struct format_tree *nft; char *all, *active, *use, *expanded, *value; size_t valuelen; struct window_pane *wp; if (ft->w == NULL) { format_log(ft, "pane loop but no window"); return (NULL); } if (format_choose(ft, fmt, &all, &active, 0) != 0) { all = xstrdup(fmt); active = NULL; } value = xcalloc(1, 1); valuelen = 1; TAILQ_FOREACH(wp, &ft->w->panes, entry) { format_log(ft, "pane loop: %%%u", wp->id); if (active != NULL && wp == ft->w->active) use = active; else use = all; nft = format_create(c, item, FORMAT_PANE|wp->id, ft->flags); nft->loop = ft->loop; format_defaults(nft, ft->c, ft->s, ft->wl, wp); expanded = format_expand(nft, use); format_free(nft); valuelen += strlen(expanded); value = xrealloc(value, valuelen); strlcat(value, expanded, valuelen); free(expanded); } free(active); free(all); return (value); } /* Replace a key. */ static int format_replace(struct format_tree *ft, const char *key, size_t keylen, char **buf, size_t *len, size_t *off) { struct window_pane *wp = ft->wp; const char *errptr, *copy, *cp, *marker = NULL; char *copy0, *condition, *found, *new; char *value, *left, *right; size_t valuelen; int modifiers = 0, limit = 0, j; struct format_modifier *list, *fm, *cmp = NULL, *search = NULL; struct format_modifier *sub = NULL; u_int i, count; /* Make a copy of the key. */ copy = copy0 = xstrndup(key, keylen); /* Process modifier list. */ list = format_build_modifiers(ft, ©, &count); for (i = 0; i < count; i++) { fm = &list[i]; if (format_logging(ft)) { format_log(ft, "modifier %u is %s", i, fm->modifier); for (j = 0; j < fm->argc; j++) { format_log(ft, "modifier %u argument %d: %s", i, j, fm->argv[j]); } } if (fm->size == 1) { switch (fm->modifier[0]) { case 'm': case '<': case '>': cmp = fm; break; case 'C': search = fm; break; case 's': if (fm->argc < 2) break; sub = fm; break; case '=': if (fm->argc < 1) break; limit = strtonum(fm->argv[0], INT_MIN, INT_MAX, &errptr); if (errptr != NULL) limit = 0; if (fm->argc >= 2 && fm->argv[1] != NULL) marker = fm->argv[1]; break; case 'l': modifiers |= FORMAT_LITERAL; break; case 'b': modifiers |= FORMAT_BASENAME; break; case 'd': modifiers |= FORMAT_DIRNAME; break; case 't': modifiers |= FORMAT_TIMESTRING; break; case 'q': modifiers |= FORMAT_QUOTE; break; case 'E': modifiers |= FORMAT_EXPAND; break; case 'T': modifiers |= FORMAT_EXPANDTIME; break; case 'S': modifiers |= FORMAT_SESSIONS; break; case 'W': modifiers |= FORMAT_WINDOWS; break; case 'P': modifiers |= FORMAT_PANES; break; } } else if (fm->size == 2) { if (strcmp(fm->modifier, "||") == 0 || strcmp(fm->modifier, "&&") == 0 || strcmp(fm->modifier, "==") == 0 || strcmp(fm->modifier, "!=") == 0 || strcmp(fm->modifier, ">=") == 0 || strcmp(fm->modifier, "<=") == 0) cmp = fm; } } /* Is this a literal string? */ if (modifiers & FORMAT_LITERAL) { value = xstrdup(copy); goto done; } /* Is this a loop, comparison or condition? */ if (modifiers & FORMAT_SESSIONS) { value = format_loop_sessions(ft, copy); if (value == NULL) goto fail; } else if (modifiers & FORMAT_WINDOWS) { value = format_loop_windows(ft, copy); if (value == NULL) goto fail; } else if (modifiers & FORMAT_PANES) { value = format_loop_panes(ft, copy); if (value == NULL) goto fail; } else if (search != NULL) { /* Search in pane. */ new = format_expand(ft, copy); if (wp == NULL) { format_log(ft, "search '%s' but no pane", new); value = xstrdup("0"); } else { format_log(ft, "search '%s' pane %%%u", new, wp->id); value = format_search(fm, wp, new); } free(new); } else if (cmp != NULL) { /* Comparison of left and right. */ if (format_choose(ft, copy, &left, &right, 1) != 0) { format_log(ft, "compare %s syntax error: %s", cmp->modifier, copy); goto fail; } format_log(ft, "compare %s left is: %s", cmp->modifier, left); format_log(ft, "compare %s right is: %s", cmp->modifier, right); if (strcmp(cmp->modifier, "||") == 0) { if (format_true(left) || format_true(right)) value = xstrdup("1"); else value = xstrdup("0"); } else if (strcmp(cmp->modifier, "&&") == 0) { if (format_true(left) && format_true(right)) value = xstrdup("1"); else value = xstrdup("0"); } else if (strcmp(cmp->modifier, "==") == 0) { if (strcmp(left, right) == 0) value = xstrdup("1"); else value = xstrdup("0"); } else if (strcmp(cmp->modifier, "!=") == 0) { if (strcmp(left, right) != 0) value = xstrdup("1"); else value = xstrdup("0"); } else if (strcmp(cmp->modifier, "<") == 0) { if (strcmp(left, right) < 0) value = xstrdup("1"); else value = xstrdup("0"); } else if (strcmp(cmp->modifier, ">") == 0) { if (strcmp(left, right) > 0) value = xstrdup("1"); else value = xstrdup("0"); } else if (strcmp(cmp->modifier, "<=") == 0) { if (strcmp(left, right) <= 0) value = xstrdup("1"); else value = xstrdup("0"); } else if (strcmp(cmp->modifier, ">=") == 0) { if (strcmp(left, right) >= 0) value = xstrdup("1"); else value = xstrdup("0"); } else if (strcmp(cmp->modifier, "m") == 0) value = format_match(cmp, left, right); free(right); free(left); } else if (*copy == '?') { /* Conditional: check first and choose second or third. */ cp = format_skip(copy + 1, ","); if (cp == NULL) { format_log(ft, "condition syntax error: %s", copy + 1); goto fail; } condition = xstrndup(copy + 1, cp - (copy + 1)); format_log(ft, "condition is: %s", condition); found = format_find(ft, condition, modifiers); if (found == NULL) { /* * If the condition not found, try to expand it. If * the expansion doesn't have any effect, then assume * false. */ found = format_expand(ft, condition); if (strcmp(found, condition) == 0) { free(found); found = xstrdup(""); format_log(ft, "condition '%s' found: %s", condition, found); } else { format_log(ft, "condition '%s' not found; assuming false", condition); } } else format_log(ft, "condition '%s' found", condition); if (format_choose(ft, cp + 1, &left, &right, 0) != 0) { format_log(ft, "condition '%s' syntax error: %s", condition, cp + 1); free(found); goto fail; } if (format_true(found)) { format_log(ft, "condition '%s' is true", condition); value = format_expand(ft, left); } else { format_log(ft, "condition '%s' is false", condition); value = format_expand(ft, right); } free(right); free(left); free(condition); free(found); } else { /* Neither: look up directly. */ value = format_find(ft, copy, modifiers); if (value == NULL) { format_log(ft, "format '%s' not found", copy); value = xstrdup(""); } else format_log(ft, "format '%s' found: %s", copy, value); } done: /* Expand again if required. */ if (modifiers & FORMAT_EXPAND) { new = format_expand(ft, value); free(value); value = new; } else if (modifiers & FORMAT_EXPANDTIME) { new = format_expand_time(ft, value); free(value); value = new; } /* Perform substitution if any. */ if (sub != NULL) { left = format_expand(ft, sub->argv[0]); right = format_expand(ft, sub->argv[1]); new = format_sub(sub, value, left, right); format_log(ft, "substitute '%s' to '%s': %s", left, right, new); free(value); value = new; free(right); free(left); } /* Truncate the value if needed. */ if (limit > 0) { new = format_trim_left(value, limit); if (marker != NULL && strcmp(new, value) != 0) { free(value); xasprintf(&value, "%s%s", new, marker); } else { free(value); value = new; } format_log(ft, "applied length limit %d: %s", limit, value); } else if (limit < 0) { new = format_trim_right(value, -limit); if (marker != NULL && strcmp(new, value) != 0) { free(value); xasprintf(&value, "%s%s", marker, new); } else { free(value); value = new; } format_log(ft, "applied length limit %d: %s", limit, value); } /* Expand the buffer and copy in the value. */ valuelen = strlen(value); while (*len - *off < valuelen + 1) { *buf = xreallocarray(*buf, 2, *len); *len *= 2; } memcpy(*buf + *off, value, valuelen); *off += valuelen; format_log(ft, "replaced '%s' with '%s'", copy0, value); free(value); format_free_modifiers(list, count); free(copy0); return (0); fail: format_log(ft, "failed %s", copy0); format_free_modifiers(list, count); free(copy0); return (-1); } /* Expand keys in a template. */ static char * format_expand1(struct format_tree *ft, const char *fmt, int time) { char *buf, *out, *name; const char *ptr, *s; size_t off, len, n, outlen; int ch, brackets; struct tm *tm; char expanded[8192]; if (fmt == NULL || *fmt == '\0') return (xstrdup("")); if (ft->loop == FORMAT_LOOP_LIMIT) return (xstrdup("")); ft->loop++; format_log(ft, "expanding format: %s", fmt); if (time) { tm = localtime(&ft->time); if (strftime(expanded, sizeof expanded, fmt, tm) == 0) { format_log(ft, "format is too long"); return (xstrdup("")); } if (format_logging(ft) && strcmp(expanded, fmt) != 0) format_log(ft, "after time expanded: %s", expanded); fmt = expanded; } len = 64; buf = xmalloc(len); off = 0; while (*fmt != '\0') { if (*fmt != '#') { while (len - off < 2) { buf = xreallocarray(buf, 2, len); len *= 2; } buf[off++] = *fmt++; continue; } fmt++; ch = (u_char)*fmt++; switch (ch) { case '(': brackets = 1; for (ptr = fmt; *ptr != '\0'; ptr++) { if (*ptr == '(') brackets++; if (*ptr == ')' && --brackets == 0) break; } if (*ptr != ')' || brackets != 0) break; n = ptr - fmt; name = xstrndup(fmt, n); format_log(ft, "found #(): %s", name); if (ft->flags & FORMAT_NOJOBS) { out = xstrdup(""); format_log(ft, "#() is disabled"); } else { out = format_job_get(ft, name); format_log(ft, "#() result: %s", out); } free(name); outlen = strlen(out); while (len - off < outlen + 1) { buf = xreallocarray(buf, 2, len); len *= 2; } memcpy(buf + off, out, outlen); off += outlen; free(out); fmt += n + 1; continue; case '{': ptr = format_skip((char *)fmt - 2, "}"); if (ptr == NULL) break; n = ptr - fmt; format_log(ft, "found #{}: %.*s", (int)n, fmt); if (format_replace(ft, fmt, n, &buf, &len, &off) != 0) break; fmt += n + 1; continue; case '}': case '#': case ',': format_log(ft, "found #%c", ch); while (len - off < 2) { buf = xreallocarray(buf, 2, len); len *= 2; } buf[off++] = ch; continue; default: s = NULL; if (ch >= 'A' && ch <= 'Z') s = format_upper[ch - 'A']; else if (ch >= 'a' && ch <= 'z') s = format_lower[ch - 'a']; if (s == NULL) { while (len - off < 3) { buf = xreallocarray(buf, 2, len); len *= 2; } buf[off++] = '#'; buf[off++] = ch; continue; } n = strlen(s); format_log(ft, "found #%c: %s", ch, s); if (format_replace(ft, s, n, &buf, &len, &off) != 0) break; continue; } break; } buf[off] = '\0'; format_log(ft, "result is: %s", buf); ft->loop--; return (buf); } /* Expand keys in a template, passing through strftime first. */ char * format_expand_time(struct format_tree *ft, const char *fmt) { return (format_expand1(ft, fmt, 1)); } /* Expand keys in a template. */ char * format_expand(struct format_tree *ft, const char *fmt) { return (format_expand1(ft, fmt, 0)); } /* Expand a single string. */ char * format_single(struct cmdq_item *item, const char *fmt, struct client *c, struct session *s, struct winlink *wl, struct window_pane *wp) { struct format_tree *ft; char *expanded; if (item != NULL) ft = format_create(item->client, item, FORMAT_NONE, 0); else ft = format_create(NULL, item, FORMAT_NONE, 0); format_defaults(ft, c, s, wl, wp); expanded = format_expand(ft, fmt); format_free(ft); return (expanded); } /* Set defaults for any of arguments that are not NULL. */ void format_defaults(struct format_tree *ft, struct client *c, struct session *s, struct winlink *wl, struct window_pane *wp) { if (c != NULL && c->name != NULL) log_debug("%s: c=%s", __func__, c->name); else log_debug("%s: c=none", __func__); if (s != NULL) log_debug("%s: s=$%u", __func__, s->id); else log_debug("%s: s=none", __func__); if (wl != NULL) log_debug("%s: wl=%u w=@%u", __func__, wl->idx, wl->window->id); else log_debug("%s: wl=none", __func__); if (wp != NULL) log_debug("%s: wp=%%%u", __func__, wp->id); else log_debug("%s: wp=none", __func__); if (c != NULL && s != NULL && c->session != s) log_debug("%s: session does not match", __func__); format_add(ft, "session_format", "%d", s != NULL); format_add(ft, "window_format", "%d", wl != NULL); format_add(ft, "pane_format", "%d", wp != NULL); if (s == NULL && c != NULL) s = c->session; if (wl == NULL && s != NULL) wl = s->curw; if (wp == NULL && wl != NULL) wp = wl->window->active; if (c != NULL) format_defaults_client(ft, c); if (s != NULL) format_defaults_session(ft, s); if (wl != NULL) format_defaults_winlink(ft, wl); if (wp != NULL) format_defaults_pane(ft, wp); } /* Set default format keys for a session. */ static void format_defaults_session(struct format_tree *ft, struct session *s) { struct session_group *sg; ft->s = s; format_add(ft, "session_name", "%s", s->name); format_add(ft, "session_windows", "%u", winlink_count(&s->windows)); format_add(ft, "session_id", "$%u", s->id); sg = session_group_contains(s); format_add(ft, "session_grouped", "%d", sg != NULL); if (sg != NULL) { format_add(ft, "session_group", "%s", sg->name); format_add(ft, "session_group_size", "%u", session_group_count (sg)); format_add_cb(ft, "session_group_list", format_cb_session_group_list); } format_add_tv(ft, "session_created", &s->creation_time); format_add_tv(ft, "session_last_attached", &s->last_attached_time); format_add_tv(ft, "session_activity", &s->activity_time); format_add(ft, "session_attached", "%u", s->attached); format_add(ft, "session_many_attached", "%d", s->attached > 1); format_add_cb(ft, "session_alerts", format_cb_session_alerts); format_add_cb(ft, "session_stack", format_cb_session_stack); } /* Set default format keys for a client. */ static void format_defaults_client(struct format_tree *ft, struct client *c) { struct session *s; const char *name; struct tty *tty = &c->tty; const char *types[] = TTY_TYPES; if (ft->s == NULL) ft->s = c->session; ft->c = c; format_add(ft, "client_name", "%s", c->name); format_add(ft, "client_pid", "%ld", (long) c->pid); format_add(ft, "client_height", "%u", tty->sy); format_add(ft, "client_width", "%u", tty->sx); format_add(ft, "client_tty", "%s", c->ttyname); format_add(ft, "client_control_mode", "%d", !!(c->flags & CLIENT_CONTROL)); if (tty->term_name != NULL) format_add(ft, "client_termname", "%s", tty->term_name); if (tty->term_name != NULL) format_add(ft, "client_termtype", "%s", types[tty->term_type]); format_add_tv(ft, "client_created", &c->creation_time); format_add_tv(ft, "client_activity", &c->activity_time); format_add(ft, "client_written", "%zu", c->written); format_add(ft, "client_discarded", "%zu", c->discarded); name = server_client_get_key_table(c); if (strcmp(c->keytable->name, name) == 0) format_add(ft, "client_prefix", "%d", 0); else format_add(ft, "client_prefix", "%d", 1); format_add(ft, "client_key_table", "%s", c->keytable->name); if (tty->flags & TTY_UTF8) format_add(ft, "client_utf8", "%d", 1); else format_add(ft, "client_utf8", "%d", 0); if (c->flags & CLIENT_READONLY) format_add(ft, "client_readonly", "%d", 1); else format_add(ft, "client_readonly", "%d", 0); s = c->session; if (s != NULL) format_add(ft, "client_session", "%s", s->name); s = c->last_session; if (s != NULL && session_alive(s)) format_add(ft, "client_last_session", "%s", s->name); } /* Set default format keys for a window. */ void format_defaults_window(struct format_tree *ft, struct window *w) { ft->w = w; format_add_tv(ft, "window_activity", &w->activity_time); format_add(ft, "window_id", "@%u", w->id); format_add(ft, "window_name", "%s", w->name); format_add(ft, "window_width", "%u", w->sx); format_add(ft, "window_height", "%u", w->sy); format_add_cb(ft, "window_layout", format_cb_window_layout); format_add_cb(ft, "window_visible_layout", format_cb_window_visible_layout); format_add(ft, "window_panes", "%u", window_count_panes(w)); format_add(ft, "window_zoomed_flag", "%d", !!(w->flags & WINDOW_ZOOMED)); } /* Set default format keys for a winlink. */ static void format_defaults_winlink(struct format_tree *ft, struct winlink *wl) { struct client *c = ft->c; struct session *s = wl->session; struct window *w = wl->window; int flag; u_int ox, oy, sx, sy; if (ft->w == NULL) ft->w = wl->window; ft->wl = wl; format_defaults_window(ft, w); if (c != NULL) { flag = tty_window_offset(&c->tty, &ox, &oy, &sx, &sy); format_add(ft, "window_bigger", "%d", flag); if (flag) { format_add(ft, "window_offset_x", "%u", ox); format_add(ft, "window_offset_y", "%u", oy); } } format_add(ft, "window_index", "%d", wl->idx); format_add_cb(ft, "window_stack_index", format_cb_window_stack_index); format_add(ft, "window_flags", "%s", window_printable_flags(wl)); format_add(ft, "window_active", "%d", wl == s->curw); format_add(ft, "window_start_flag", "%d", !!(wl == RB_MIN(winlinks, &s->windows))); format_add(ft, "window_end_flag", "%d", !!(wl == RB_MAX(winlinks, &s->windows))); format_add(ft, "window_bell_flag", "%d", !!(wl->flags & WINLINK_BELL)); format_add(ft, "window_activity_flag", "%d", !!(wl->flags & WINLINK_ACTIVITY)); format_add(ft, "window_silence_flag", "%d", !!(wl->flags & WINLINK_SILENCE)); format_add(ft, "window_last_flag", "%d", !!(wl == TAILQ_FIRST(&s->lastw))); format_add(ft, "window_linked", "%d", session_is_linked(s, wl->window)); } /* Set default format keys for a window pane. */ void format_defaults_pane(struct format_tree *ft, struct window_pane *wp) { struct window *w = wp->window; struct grid *gd = wp->base.grid; int status = wp->status; u_int idx; struct window_mode_entry *wme; if (ft->w == NULL) ft->w = w; ft->wp = wp; format_add(ft, "history_size", "%u", gd->hsize); format_add(ft, "history_limit", "%u", gd->hlimit); format_add_cb(ft, "history_bytes", format_cb_history_bytes); if (window_pane_index(wp, &idx) != 0) fatalx("index not found"); format_add(ft, "pane_index", "%u", idx); format_add(ft, "pane_width", "%u", wp->sx); format_add(ft, "pane_height", "%u", wp->sy); format_add(ft, "pane_title", "%s", wp->base.title); format_add(ft, "pane_id", "%%%u", wp->id); format_add(ft, "pane_active", "%d", wp == w->active); format_add(ft, "pane_input_off", "%d", !!(wp->flags & PANE_INPUTOFF)); format_add(ft, "pane_pipe", "%d", wp->pipe_fd != -1); if ((wp->flags & PANE_STATUSREADY) && WIFEXITED(status)) format_add(ft, "pane_dead_status", "%d", WEXITSTATUS(status)); if (~wp->flags & PANE_EMPTY) format_add(ft, "pane_dead", "%d", wp->fd == -1); else format_add(ft, "pane_dead", "0"); if (server_check_marked() && marked_pane.wp == wp) format_add(ft, "pane_marked", "1"); else format_add(ft, "pane_marked", "0"); format_add(ft, "pane_marked_set", "%d", server_check_marked()); format_add(ft, "pane_left", "%u", wp->xoff); format_add(ft, "pane_top", "%u", wp->yoff); format_add(ft, "pane_right", "%u", wp->xoff + wp->sx - 1); format_add(ft, "pane_bottom", "%u", wp->yoff + wp->sy - 1); format_add(ft, "pane_at_left", "%d", wp->xoff == 0); format_add(ft, "pane_at_top", "%d", wp->yoff == 0); format_add(ft, "pane_at_right", "%d", wp->xoff + wp->sx == w->sx); format_add(ft, "pane_at_bottom", "%d", wp->yoff + wp->sy == w->sy); wme = TAILQ_FIRST(&wp->modes); if (wme != NULL) { format_add(ft, "pane_mode", "%s", wme->mode->name); if (wme->mode->formats != NULL) wme->mode->formats(wme, ft); } format_add_cb(ft, "pane_in_mode", format_cb_pane_in_mode); format_add(ft, "pane_synchronized", "%d", !!options_get_number(w->options, "synchronize-panes")); if (wp->searchstr != NULL) format_add(ft, "pane_search_string", "%s", wp->searchstr); format_add(ft, "pane_tty", "%s", wp->tty); format_add(ft, "pane_pid", "%ld", (long) wp->pid); format_add_cb(ft, "pane_start_command", format_cb_start_command); format_add_cb(ft, "pane_current_command", format_cb_current_command); format_add_cb(ft, "pane_current_path", format_cb_current_path); format_add(ft, "cursor_x", "%u", wp->base.cx); format_add(ft, "cursor_y", "%u", wp->base.cy); format_add_cb(ft, "cursor_character", format_cb_cursor_character); format_add(ft, "scroll_region_upper", "%u", wp->base.rupper); format_add(ft, "scroll_region_lower", "%u", wp->base.rlower); format_add(ft, "alternate_on", "%d", wp->saved_grid ? 1 : 0); format_add(ft, "alternate_saved_x", "%u", wp->saved_cx); format_add(ft, "alternate_saved_y", "%u", wp->saved_cy); format_add(ft, "cursor_flag", "%d", !!(wp->base.mode & MODE_CURSOR)); format_add(ft, "insert_flag", "%d", !!(wp->base.mode & MODE_INSERT)); format_add(ft, "keypad_cursor_flag", "%d", !!(wp->base.mode & MODE_KCURSOR)); format_add(ft, "keypad_flag", "%d", !!(wp->base.mode & MODE_KKEYPAD)); format_add(ft, "wrap_flag", "%d", !!(wp->base.mode & MODE_WRAP)); format_add(ft, "origin_flag", "%d", !!(wp->base.mode & MODE_ORIGIN)); format_add(ft, "mouse_any_flag", "%d", !!(wp->base.mode & ALL_MOUSE_MODES)); format_add(ft, "mouse_standard_flag", "%d", !!(wp->base.mode & MODE_MOUSE_STANDARD)); format_add(ft, "mouse_button_flag", "%d", !!(wp->base.mode & MODE_MOUSE_BUTTON)); format_add(ft, "mouse_all_flag", "%d", !!(wp->base.mode & MODE_MOUSE_ALL)); format_add(ft, "mouse_utf8_flag", "%d", !!(wp->base.mode & MODE_MOUSE_UTF8)); format_add(ft, "mouse_sgr_flag", "%d", !!(wp->base.mode & MODE_MOUSE_SGR)); format_add_cb(ft, "pane_tabs", format_cb_pane_tabs); } /* Set default format keys for paste buffer. */ void format_defaults_paste_buffer(struct format_tree *ft, struct paste_buffer *pb) { struct timeval tv; size_t size; char *s; timerclear(&tv); tv.tv_sec = paste_buffer_created(pb); paste_buffer_data(pb, &size); format_add(ft, "buffer_size", "%zu", size); format_add(ft, "buffer_name", "%s", paste_buffer_name(pb)); format_add_tv(ft, "buffer_created", &tv); s = paste_make_sample(pb); format_add(ft, "buffer_sample", "%s", s); free(s); } tmux-3.0a/format-draw.c100644 001750 001750 00000055721 13570677043 0010631/* $OpenBSD$ */ /* * Copyright (c) 2019 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" /* Format range. */ struct format_range { u_int index; struct screen *s; u_int start; u_int end; enum style_range_type type; u_int argument; TAILQ_ENTRY(format_range) entry; }; TAILQ_HEAD(format_ranges, format_range); /* Does this range match this style? */ static int format_is_type(struct format_range *fr, struct style *sy) { if (fr->type != sy->range_type) return (0); if (fr->type == STYLE_RANGE_WINDOW && fr->argument != sy->range_argument) return (0); return (1); } /* Free a range. */ static void format_free_range(struct format_ranges *frs, struct format_range *fr) { TAILQ_REMOVE(frs, fr, entry); free(fr); } /* Fix range positions. */ static void format_update_ranges(struct format_ranges *frs, struct screen *s, u_int offset, u_int start, u_int width) { struct format_range *fr, *fr1; if (frs == NULL) return; TAILQ_FOREACH_SAFE(fr, frs, entry, fr1) { if (fr->s != s) continue; if (fr->end <= start || fr->start >= start + width) { format_free_range(frs, fr); continue; } if (fr->start < start) fr->start = start; if (fr->end > start + width) fr->end = start + width; if (fr->start == fr->end) { format_free_range(frs, fr); continue; } fr->start -= start; fr->end -= start; fr->start += offset; fr->end += offset; } } /* Draw a part of the format. */ static void format_draw_put(struct screen_write_ctx *octx, u_int ocx, u_int ocy, struct screen *s, struct format_ranges *frs, u_int offset, u_int start, u_int width) { /* * The offset is how far from the cursor on the target screen; start * and width how much to copy from the source screen. */ screen_write_cursormove(octx, ocx + offset, ocy, 0); screen_write_fast_copy(octx, s, start, 0, width, 1); format_update_ranges(frs, s, offset, start, width); } /* Draw list part of format. */ static void format_draw_put_list(struct screen_write_ctx *octx, u_int ocx, u_int ocy, u_int offset, u_int width, struct screen *list, struct screen *list_left, struct screen *list_right, int focus_start, int focus_end, struct format_ranges *frs) { u_int start, focus_centre; /* If there is enough space for the list, draw it entirely. */ if (width >= list->cx) { format_draw_put(octx, ocx, ocy, list, frs, offset, 0, width); return; } /* The list needs to be trimmed. Try to keep the focus visible. */ focus_centre = focus_start + (focus_end - focus_start) / 2; if (focus_centre < width / 2) start = 0; else start = focus_centre - width / 2; if (start + width > list->cx) start = list->cx - width; /* Draw <> markers at either side if needed. */ if (start != 0 && width > list_left->cx) { screen_write_cursormove(octx, ocx + offset, ocy, 0); screen_write_fast_copy(octx, list_left, 0, 0, list_left->cx, 1); offset += list_left->cx; start += list_left->cx; width -= list_left->cx; } if (start + width < list->cx && width > list_right->cx) { screen_write_cursormove(octx, ocx + offset + width - 1, ocy, 0); screen_write_fast_copy(octx, list_right, 0, 0, list_right->cx, 1); width -= list_right->cx; } /* Draw the list screen itself. */ format_draw_put(octx, ocx, ocy, list, frs, offset, start, width); } /* Draw format with no list. */ static void format_draw_none(struct screen_write_ctx *octx, u_int available, u_int ocx, u_int ocy, struct screen *left, struct screen *centre, struct screen *right, struct format_ranges *frs) { u_int width_left, width_centre, width_right; width_left = left->cx; width_centre = centre->cx; width_right = right->cx; /* * Try to keep as much of the left and right as possible at the expense * of the centre. */ while (width_left + width_centre + width_right > available) { if (width_centre > 0) width_centre--; else if (width_right > 0) width_right--; else width_left--; } /* Write left. */ format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left); /* Write right at available - width_right. */ format_draw_put(octx, ocx, ocy, right, frs, available - width_right, right->cx - width_right, width_right); /* * Write centre halfway between * width_left * and * available - width_right. */ format_draw_put(octx, ocx, ocy, centre, frs, width_left + ((available - width_right) - width_left) / 2 - width_centre / 2, centre->cx / 2 - width_centre / 2, width_centre); } /* Draw format with list on the left. */ static void format_draw_left(struct screen_write_ctx *octx, u_int available, u_int ocx, u_int ocy, struct screen *left, struct screen *centre, struct screen *right, struct screen *list, struct screen *list_left, struct screen *list_right, struct screen *after, int focus_start, int focus_end, struct format_ranges *frs) { u_int width_left, width_centre, width_right; u_int width_list, width_after; struct screen_write_ctx ctx; width_left = left->cx; width_centre = centre->cx; width_right = right->cx; width_list = list->cx; width_after = after->cx; /* * Trim first the centre, then the list, then the right, then after the * list, then the left. */ while (width_left + width_centre + width_right + width_list + width_after > available) { if (width_centre > 0) width_centre--; else if (width_list > 0) width_list--; else if (width_right > 0) width_right--; else if (width_after > 0) width_after--; else width_left--; } /* If there is no list left, pass off to the no list function. */ if (width_list == 0) { screen_write_start(&ctx, NULL, left); screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1); screen_write_stop(&ctx); format_draw_none(octx, available, ocx, ocy, left, centre, right, frs); return; } /* Write left at 0. */ format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left); /* Write right at available - width_right. */ format_draw_put(octx, ocx, ocy, right, frs, available - width_right, right->cx - width_right, width_right); /* Write after at width_left + width_list. */ format_draw_put(octx, ocx, ocy, after, frs, width_left + width_list, 0, width_after); /* * Write centre halfway between * width_left + width_list + width_after * and * available - width_right. */ format_draw_put(octx, ocx, ocy, centre, frs, (width_left + width_list + width_after) + ((available - width_right) - (width_left + width_list + width_after)) / 2 - width_centre / 2, centre->cx / 2 - width_centre / 2, width_centre); /* * The list now goes from * width_left * to * width_left + width_list. * If there is no focus given, keep the left in focus. */ if (focus_start == -1 || focus_end == -1) focus_start = focus_end = 0; format_draw_put_list(octx, ocx, ocy, width_left, width_list, list, list_left, list_right, focus_start, focus_end, frs); } /* Draw format with list in the centre. */ static void format_draw_centre(struct screen_write_ctx *octx, u_int available, u_int ocx, u_int ocy, struct screen *left, struct screen *centre, struct screen *right, struct screen *list, struct screen *list_left, struct screen *list_right, struct screen *after, int focus_start, int focus_end, struct format_ranges *frs) { u_int width_left, width_centre, width_right; u_int width_list, width_after, middle; struct screen_write_ctx ctx; width_left = left->cx; width_centre = centre->cx; width_right = right->cx; width_list = list->cx; width_after = after->cx; /* * Trim first the list, then after the list, then the centre, then the * right, then the left. */ while (width_left + width_centre + width_right + width_list + width_after > available) { if (width_list > 0) width_list--; else if (width_after > 0) width_after--; else if (width_centre > 0) width_centre--; else if (width_right > 0) width_right--; else width_left--; } /* If there is no list left, pass off to the no list function. */ if (width_list == 0) { screen_write_start(&ctx, NULL, centre); screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1); screen_write_stop(&ctx); format_draw_none(octx, available, ocx, ocy, left, centre, right, frs); return; } /* Write left at 0. */ format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left); /* Write right at available - width_right. */ format_draw_put(octx, ocx, ocy, right, frs, available - width_right, right->cx - width_right, width_right); /* * All three centre sections are offset from the middle of the * available space. */ middle = (width_left + ((available - width_right) - width_left) / 2); /* * Write centre at * middle - width_list / 2 - width_centre. */ format_draw_put(octx, ocx, ocy, centre, frs, middle - width_list / 2 - width_centre, 0, width_centre); /* * Write after at * middle - width_list / 2 + width_list */ format_draw_put(octx, ocx, ocy, after, frs, middle - width_list / 2 + width_list, 0, width_after); /* * The list now goes from * middle - width_list / 2 * to * middle + width_list / 2 * If there is no focus given, keep the centre in focus. */ if (focus_start == -1 || focus_end == -1) focus_start = focus_end = list->cx / 2; format_draw_put_list(octx, ocx, ocy, middle - width_list / 2, width_list, list, list_left, list_right, focus_start, focus_end, frs); } /* Draw format with list on the right. */ static void format_draw_right(struct screen_write_ctx *octx, u_int available, u_int ocx, u_int ocy, struct screen *left, struct screen *centre, struct screen *right, struct screen *list, struct screen *list_left, struct screen *list_right, struct screen *after, int focus_start, int focus_end, struct format_ranges *frs) { u_int width_left, width_centre, width_right; u_int width_list, width_after; struct screen_write_ctx ctx; width_left = left->cx; width_centre = centre->cx; width_right = right->cx; width_list = list->cx; width_after = after->cx; /* * Trim first the centre, then the list, then the right, then * after the list, then the left. */ while (width_left + width_centre + width_right + width_list + width_after > available) { if (width_centre > 0) width_centre--; else if (width_list > 0) width_list--; else if (width_right > 0) width_right--; else if (width_after > 0) width_after--; else width_left--; } /* If there is no list left, pass off to the no list function. */ if (width_list == 0) { screen_write_start(&ctx, NULL, right); screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1); screen_write_stop(&ctx); format_draw_none(octx, available, ocx, ocy, left, centre, right, frs); return; } /* Write left at 0. */ format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left); /* Write after at available - width_after. */ format_draw_put(octx, ocx, ocy, after, frs, available - width_after, after->cx - width_after, width_after); /* * Write right at * available - width_right - width_list - width_after. */ format_draw_put(octx, ocx, ocy, right, frs, available - width_right - width_list - width_after, 0, width_right); /* * Write centre halfway between * width_left * and * available - width_right - width_list - width_after. */ format_draw_put(octx, ocx, ocy, centre, frs, width_left + ((available - width_right - width_list - width_after) - width_left) / 2 - width_centre / 2, centre->cx / 2 - width_centre / 2, width_centre); /* * The list now goes from * available - width_list - width_after * to * available - width_after * If there is no focus given, keep the right in focus. */ if (focus_start == -1 || focus_end == -1) focus_start = focus_end = 0; format_draw_put_list(octx, ocx, ocy, available - width_list - width_after, width_list, list, list_left, list_right, focus_start, focus_end, frs); } /* Draw a format to a screen. */ void format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, u_int available, const char *expanded, struct style_ranges *srs) { enum { LEFT, CENTRE, RIGHT, LIST, LIST_LEFT, LIST_RIGHT, AFTER, TOTAL } current = LEFT, last = LEFT; const char *names[] = { "LEFT", "CENTRE", "RIGHT", "LIST", "LIST_LEFT", "LIST_RIGHT", "AFTER" }; size_t size = strlen(expanded); struct screen *os = octx->s, s[TOTAL]; struct screen_write_ctx ctx[TOTAL]; u_int ocx = os->cx, ocy = os->cy, i, width[TOTAL]; u_int map[] = { LEFT, LEFT, CENTRE, RIGHT }; int focus_start = -1, focus_end = -1; int list_state = -1, fill = -1; enum style_align list_align = STYLE_ALIGN_DEFAULT; struct grid_cell gc; struct style sy; struct utf8_data *ud = &sy.gc.data; const char *cp, *end; enum utf8_state more; char *tmp; struct format_range *fr = NULL, *fr1; struct format_ranges frs; struct style_range *sr; style_set(&sy, base); TAILQ_INIT(&frs); log_debug("%s: %s", __func__, expanded); /* * We build three screens for left, right, centre alignment, one for * the list, one for anything after the list and two for the list left * and right markers. */ for (i = 0; i < TOTAL; i++) { screen_init(&s[i], size, 1, 0); screen_write_start(&ctx[i], NULL, &s[i]); screen_write_clearendofline(&ctx[i], base->bg); width[i] = 0; } /* * Walk the string and add to the corresponding screens, * parsing styles as we go. */ cp = expanded; while (*cp != '\0') { if (cp[0] != '#' || cp[1] != '[') { /* See if this is a UTF-8 character. */ if ((more = utf8_open(ud, *cp)) == UTF8_MORE) { while (*++cp != '\0' && more == UTF8_MORE) more = utf8_append(ud, *cp); if (more != UTF8_DONE) cp -= ud->have; } /* Not a UTF-8 character - ASCII or not valid. */ if (more != UTF8_DONE) { if (*cp < 0x20 || *cp > 0x7e) { /* Ignore nonprintable characters. */ cp++; continue; } utf8_set(ud, *cp); cp++; } /* Draw the cell to the current screen. */ screen_write_cell(&ctx[current], &sy.gc); width[current] += ud->width; continue; } /* This is a style. Work out where the end is and parse it. */ end = format_skip(cp + 2, "]"); if (end == NULL) { log_debug("%s: no terminating ] at '%s'", __func__, cp + 2); TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1) format_free_range(&frs, fr); goto out; } tmp = xstrndup(cp + 2, end - (cp + 2)); if (style_parse(&sy, base, tmp) != 0) { log_debug("%s: invalid style '%s'", __func__, tmp); free(tmp); cp = end + 1; continue; } log_debug("%s: style '%s' -> '%s'", __func__, tmp, style_tostring(&sy)); free(tmp); /* If this style has a fill colour, store it for later. */ if (sy.fill != 8) fill = sy.fill; /* Check the list state. */ switch (sy.list) { case STYLE_LIST_ON: /* * Entering the list, exiting a marker, or exiting the * focus. */ if (list_state != 0) { if (fr != NULL) { /* abort any region */ free(fr); fr = NULL; } list_state = 0; list_align = sy.align; } /* End the focus if started. */ if (focus_start != -1 && focus_end == -1) focus_end = s[LIST].cx; current = LIST; break; case STYLE_LIST_FOCUS: /* Entering the focus. */ if (list_state != 0) /* not inside the list */ break; if (focus_start == -1) /* focus already started */ focus_start = s[LIST].cx; break; case STYLE_LIST_OFF: /* Exiting or outside the list. */ if (list_state == 0) { if (fr != NULL) { /* abort any region */ free(fr); fr = NULL; } if (focus_start != -1 && focus_end == -1) focus_end = s[LIST].cx; map[list_align] = AFTER; if (list_align == STYLE_ALIGN_LEFT) map[STYLE_ALIGN_DEFAULT] = AFTER; list_state = 1; } current = map[sy.align]; break; case STYLE_LIST_LEFT_MARKER: /* Entering left marker. */ if (list_state != 0) /* not inside the list */ break; if (s[LIST_LEFT].cx != 0) /* already have marker */ break; if (fr != NULL) { /* abort any region */ free(fr); fr = NULL; } if (focus_start != -1 && focus_end == -1) focus_start = focus_end = -1; current = LIST_LEFT; break; case STYLE_LIST_RIGHT_MARKER: /* Entering right marker. */ if (list_state != 0) /* not inside the list */ break; if (s[LIST_RIGHT].cx != 0) /* already have marker */ break; if (fr != NULL) { /* abort any region */ free(fr); fr = NULL; } if (focus_start != -1 && focus_end == -1) focus_start = focus_end = -1; current = LIST_RIGHT; break; } if (current != last) { log_debug("%s: change %s -> %s", __func__, names[last], names[current]); last = current; } /* * Check if the range style has changed and if so end the * current range and start a new one if needed. */ if (srs != NULL) { if (fr != NULL && !format_is_type(fr, &sy)) { if (s[current].cx != fr->start) { fr->end = s[current].cx + 1; TAILQ_INSERT_TAIL(&frs, fr, entry); } else free(fr); fr = NULL; } if (fr == NULL && sy.range_type != STYLE_RANGE_NONE) { fr = xcalloc(1, sizeof *fr); fr->index = current; fr->s = &s[current]; fr->start = s[current].cx; fr->type = sy.range_type; fr->argument = sy.range_argument; } } cp = end + 1; } free(fr); for (i = 0; i < TOTAL; i++) { screen_write_stop(&ctx[i]); log_debug("%s: width %s is %u", __func__, names[i], width[i]); } if (focus_start != -1 && focus_end != -1) log_debug("%s: focus %d-%d", __func__, focus_start, focus_end); TAILQ_FOREACH(fr, &frs, entry) { log_debug("%s: range %d|%u is %s %u-%u", __func__, fr->type, fr->argument, names[fr->index], fr->start, fr->end); } /* Clear the available area. */ if (fill != -1) { memcpy(&gc, &grid_default_cell, sizeof gc); gc.bg = fill; for (i = 0; i < available; i++) screen_write_putc(octx, &gc, ' '); } /* * Draw the screens. How they are arranged depends on where the list * appearsq. */ switch (list_align) { case STYLE_ALIGN_DEFAULT: /* No list. */ format_draw_none(octx, available, ocx, ocy, &s[LEFT], &s[CENTRE], &s[RIGHT], &frs); break; case STYLE_ALIGN_LEFT: /* List is part of the left. */ format_draw_left(octx, available, ocx, ocy, &s[LEFT], &s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs); break; case STYLE_ALIGN_CENTRE: /* List is part of the centre. */ format_draw_centre(octx, available, ocx, ocy, &s[LEFT], &s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs); break; case STYLE_ALIGN_RIGHT: /* List is part of the right. */ format_draw_right(octx, available, ocx, ocy, &s[LEFT], &s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs); break; } /* Create ranges to return. */ TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1) { sr = xcalloc(1, sizeof *sr); sr->type = fr->type; sr->argument = fr->argument; sr->start = fr->start; sr->end = fr->end; TAILQ_INSERT_TAIL(srs, sr, entry); log_debug("%s: range %d|%u at %u-%u", __func__, sr->type, sr->argument, sr->start, sr->end); format_free_range(&frs, fr); } out: /* Free the screens. */ for (i = 0; i < TOTAL; i++) screen_free(&s[i]); /* Restore the original cursor position. */ screen_write_cursormove(octx, ocx, ocy, 0); } /* Get width, taking #[] into account. */ u_int format_width(const char *expanded) { const char *cp, *end; u_int width = 0; struct utf8_data ud; enum utf8_state more; cp = expanded; while (*cp != '\0') { if (cp[0] == '#' && cp[1] == '[') { end = format_skip(cp + 2, "]"); if (end == NULL) return 0; cp = end + 1; } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) { while (*++cp != '\0' && more == UTF8_MORE) more = utf8_append(&ud, *cp); if (more == UTF8_DONE) width += ud.width; else cp -= ud.have; } else if (*cp > 0x1f && *cp < 0x7f) { width++; cp++; } else cp++; } return (width); } /* Trim on the left, taking #[] into account. */ char * format_trim_left(const char *expanded, u_int limit) { char *copy, *out; const char *cp = expanded, *end; u_int width = 0; struct utf8_data ud; enum utf8_state more; out = copy = xmalloc(strlen(expanded) + 1); while (*cp != '\0') { if (cp[0] == '#' && cp[1] == '[') { end = format_skip(cp + 2, "]"); if (end == NULL) break; memcpy(out, cp, end + 1 - cp); out += (end + 1 - cp); cp = end + 1; } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) { while (*++cp != '\0' && more == UTF8_MORE) more = utf8_append(&ud, *cp); if (more == UTF8_DONE) { if (width + ud.width <= limit) { memcpy(out, ud.data, ud.size); out += ud.size; } width += ud.width; } else cp -= ud.have; } else if (*cp > 0x1f && *cp < 0x7f) { if (width + 1 <= limit) *out++ = *cp; width++; cp++; } else cp++; } *out = '\0'; return (copy); } /* Trim on the right, taking #[] into account. */ char * format_trim_right(const char *expanded, u_int limit) { char *copy, *out; const char *cp = expanded, *end; u_int width = 0, total_width, skip; struct utf8_data ud; enum utf8_state more; total_width = format_width(expanded); if (total_width <= limit) return (xstrdup(expanded)); skip = total_width - limit; out = copy = xmalloc(strlen(expanded) + 1); while (*cp != '\0') { if (cp[0] == '#' && cp[1] == '[') { end = format_skip(cp + 2, "]"); if (end == NULL) break; memcpy(out, cp, end + 1 - cp); out += (end + 1 - cp); cp = end + 1; } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) { while (*++cp != '\0' && more == UTF8_MORE) more = utf8_append(&ud, *cp); if (more == UTF8_DONE) { if (width >= skip) { memcpy(out, ud.data, ud.size); out += ud.size; } width += ud.width; } else cp -= ud.have; } else if (*cp > 0x1f && *cp < 0x7f) { if (width >= skip) *out++ = *cp; width++; cp++; } else cp++; } *out = '\0'; return (copy); } tmux-3.0a/grid-view.c100644 001750 001750 00000012400 13525573465 0010271/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "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. */ void grid_view_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc) { grid_get_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py), gc); } /* 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); } /* Set cells. */ void grid_view_set_cells(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc, const char *s, size_t slen) { grid_set_cells(gd, grid_view_x(gd, px), grid_view_y(gd, py), gc, s, slen); } /* Clear into history. */ void grid_view_clear_history(struct grid *gd, u_int bg) { struct grid_line *gl; u_int yy, last; /* Find the last used line. */ last = 0; for (yy = 0; yy < gd->sy; yy++) { gl = grid_get_line(gd, grid_view_y(gd, yy)); if (gl->cellused != 0) last = yy + 1; } if (last == 0) { grid_view_clear(gd, 0, 0, gd->sx, gd->sy, bg); return; } /* Scroll the lines into the history. */ for (yy = 0; yy < last; yy++) { grid_collect_history(gd); grid_scroll_history(gd, bg); } if (last < gd->sy) grid_view_clear(gd, 0, 0, gd->sx, gd->sy - last, bg); gd->hscrolled = 0; } /* Clear area. */ void grid_view_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg) { px = grid_view_x(gd, px); py = grid_view_y(gd, py); grid_clear(gd, px, py, nx, ny, bg); } /* Scroll region up. */ void grid_view_scroll_region_up(struct grid *gd, u_int rupper, u_int rlower, u_int bg) { if (gd->flags & GRID_HISTORY) { grid_collect_history(gd); if (rupper == 0 && rlower == gd->sy - 1) grid_scroll_history(gd, bg); else { rupper = grid_view_y(gd, rupper); rlower = grid_view_y(gd, rlower); grid_scroll_history_region(gd, rupper, rlower, bg); } } else { rupper = grid_view_y(gd, rupper); rlower = grid_view_y(gd, rlower); grid_move_lines(gd, rupper, rupper + 1, rlower - rupper, bg); } } /* Scroll region down. */ void grid_view_scroll_region_down(struct grid *gd, u_int rupper, u_int rlower, u_int bg) { rupper = grid_view_y(gd, rupper); rlower = grid_view_y(gd, rlower); grid_move_lines(gd, rupper + 1, rupper, rlower - rupper, bg); } /* Insert lines. */ void grid_view_insert_lines(struct grid *gd, u_int py, u_int ny, u_int bg) { u_int sy; py = grid_view_y(gd, py); sy = grid_view_y(gd, gd->sy); grid_move_lines(gd, py + ny, py, sy - py - ny, bg); } /* Insert lines in region. */ void grid_view_insert_lines_region(struct grid *gd, u_int rlower, u_int py, u_int ny, u_int bg) { u_int ny2; 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, bg); grid_clear(gd, 0, py + ny2, gd->sx, ny - ny2, bg); } /* Delete lines. */ void grid_view_delete_lines(struct grid *gd, u_int py, u_int ny, u_int bg) { u_int sy; py = grid_view_y(gd, py); sy = grid_view_y(gd, gd->sy); grid_move_lines(gd, py, py + ny, sy - py - ny, bg); grid_clear(gd, 0, sy - ny, gd->sx, py + ny - (sy - ny), bg); } /* 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 bg) { u_int ny2; 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, bg); grid_clear(gd, 0, py + ny2, gd->sx, ny - ny2, bg); } /* Insert characters. */ void grid_view_insert_cells(struct grid *gd, u_int px, u_int py, u_int nx, u_int bg) { u_int sx; 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, bg); else grid_move_cells(gd, px + nx, px, py, sx - px - nx, bg); } /* Delete characters. */ void grid_view_delete_cells(struct grid *gd, u_int px, u_int py, u_int nx, u_int bg) { u_int sx; 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, bg); grid_clear(gd, sx - nx, py, px + nx - (sx - nx), 1, bg); } /* Convert cells into a string. */ char * grid_view_string_cells(struct grid *gd, u_int px, u_int py, u_int nx) { px = grid_view_x(gd, px); py = grid_view_y(gd, py); return (grid_string_cells(gd, px, py, nx, NULL, 0, 0, 0)); } tmux-3.0a/grid.c100644 001750 001750 00000075055 13570677043 0007335/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "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, 1, 1 }, 0, 0, 8, 8, 0 }; /* Cleared grid cell data. */ const struct grid_cell grid_cleared_cell = { { { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 0 }; static const struct grid_cell_entry grid_cleared_entry = { GRID_FLAG_CLEARED, { .data = { 0, 8, 8, ' ' } } }; static void grid_empty_line(struct grid *, u_int, u_int); /* Store cell in entry. */ static void grid_store_cell(struct grid_cell_entry *gce, const struct grid_cell *gc, u_char c) { gce->flags = (gc->flags & ~GRID_FLAG_CLEARED); gce->data.fg = gc->fg & 0xff; if (gc->fg & COLOUR_FLAG_256) gce->flags |= GRID_FLAG_FG256; gce->data.bg = gc->bg & 0xff; if (gc->bg & COLOUR_FLAG_256) gce->flags |= GRID_FLAG_BG256; gce->data.attr = gc->attr; gce->data.data = c; } /* Check if a cell should be an extended cell. */ static int grid_need_extended_cell(const struct grid_cell_entry *gce, const struct grid_cell *gc) { if (gce->flags & GRID_FLAG_EXTENDED) return (1); if (gc->attr > 0xff) return (1); if (gc->data.size != 1 || gc->data.width != 1) return (1); if ((gc->fg & COLOUR_FLAG_RGB) || (gc->bg & COLOUR_FLAG_RGB)) return (1); if (gc->us != 0) /* only supports 256 or RGB */ return (1); return (0); } /* Get an extended cell. */ static void grid_get_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce, int flags) { u_int at = gl->extdsize + 1; gl->extddata = xreallocarray(gl->extddata, at, sizeof *gl->extddata); gl->extdsize = at; gce->offset = at - 1; gce->flags = (flags | GRID_FLAG_EXTENDED); } /* Set cell as extended. */ static struct grid_cell * grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce, const struct grid_cell *gc) { struct grid_cell *gcp; int flags = (gc->flags & ~GRID_FLAG_CLEARED); if (~gce->flags & GRID_FLAG_EXTENDED) grid_get_extended_cell(gl, gce, flags); else if (gce->offset >= gl->extdsize) fatalx("offset too big"); gl->flags |= GRID_LINE_EXTENDED; gcp = &gl->extddata[gce->offset]; memcpy(gcp, gc, sizeof *gcp); gcp->flags = flags; return (gcp); } /* Free up unused extended cells. */ static void grid_compact_line(struct grid_line *gl) { int new_extdsize = 0; struct grid_cell *new_extddata; struct grid_cell_entry *gce; struct grid_cell *gc; u_int px, idx; if (gl->extdsize == 0) return; for (px = 0; px < gl->cellsize; px++) { gce = &gl->celldata[px]; if (gce->flags & GRID_FLAG_EXTENDED) new_extdsize++; } if (new_extdsize == 0) { free(gl->extddata); gl->extddata = NULL; gl->extdsize = 0; return; } new_extddata = xreallocarray(NULL, new_extdsize, sizeof *gl->extddata); idx = 0; for (px = 0; px < gl->cellsize; px++) { gce = &gl->celldata[px]; if (gce->flags & GRID_FLAG_EXTENDED) { gc = &gl->extddata[gce->offset]; memcpy(&new_extddata[idx], gc, sizeof *gc); gce->offset = idx++; } } free(gl->extddata); gl->extddata = new_extddata; gl->extdsize = new_extdsize; } /* Get line data. */ struct grid_line * grid_get_line(struct grid *gd, u_int line) { return (&gd->linedata[line]); } /* Adjust number of lines. */ void grid_adjust_lines(struct grid *gd, u_int lines) { gd->linedata = xreallocarray(gd->linedata, lines, sizeof *gd->linedata); } /* Copy default into a cell. */ static void grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg) { struct grid_line *gl = &gd->linedata[py]; struct grid_cell_entry *gce = &gl->celldata[px]; struct grid_cell *gc; memcpy(gce, &grid_cleared_entry, sizeof *gce); if (bg & COLOUR_FLAG_RGB) { grid_get_extended_cell(gl, gce, gce->flags); gl->flags |= GRID_LINE_EXTENDED; gc = &gl->extddata[gce->offset]; memcpy(gc, &grid_cleared_cell, sizeof *gc); gc->bg = bg; } else { if (bg & COLOUR_FLAG_256) gce->flags |= GRID_FLAG_BG256; gce->data.bg = bg; } } /* Check grid y position. */ static int grid_check_y(struct grid *gd, const char* from, u_int py) { if (py >= gd->hsize + gd->sy) { log_debug("%s: y out of range: %u", from, py); return (-1); } return (0); } /* Compare grid cells. Return 1 if equal, 0 if not. */ int grid_cells_equal(const struct grid_cell *gca, const struct grid_cell *gcb) { if (gca->fg != gcb->fg || gca->bg != gcb->bg) return (0); if (gca->attr != gcb->attr || gca->flags != gcb->flags) return (0); if (gca->data.width != gcb->data.width) return (0); if (gca->data.size != gcb->data.size) return (0); return (memcmp(gca->data.data, gcb->data.data, gca->data.size) == 0); } /* Free one line. */ static void grid_free_line(struct grid *gd, u_int py) { free(gd->linedata[py].celldata); gd->linedata[py].celldata = NULL; free(gd->linedata[py].extddata); gd->linedata[py].extddata = NULL; } /* Free several lines. */ static void grid_free_lines(struct grid *gd, u_int py, u_int ny) { u_int yy; for (yy = py; yy < py + ny; yy++) grid_free_line(gd, yy); } /* 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->hscrolled = 0; gd->hsize = 0; gd->hlimit = hlimit; if (gd->sy != 0) gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata); else gd->linedata = NULL; return (gd); } /* Destroy grid. */ void grid_destroy(struct grid *gd) { grid_free_lines(gd, 0, gd->hsize + gd->sy); free(gd->linedata); free(gd); } /* Compare grids. */ int grid_compare(struct grid *ga, struct grid *gb) { struct grid_line *gla, *glb; struct grid_cell gca, gcb; u_int xx, yy; if (ga->sx != gb->sx || ga->sy != gb->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 < gla->cellsize; xx++) { grid_get_cell(ga, xx, yy, &gca); grid_get_cell(gb, xx, yy, &gcb); if (!grid_cells_equal(&gca, &gcb)) return (1); } } return (0); } /* Trim lines from the history. */ static void grid_trim_history(struct grid *gd, u_int ny) { grid_free_lines(gd, 0, ny); memmove(&gd->linedata[0], &gd->linedata[ny], (gd->hsize + gd->sy - ny) * (sizeof *gd->linedata)); } /* * 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 ny; if (gd->hsize == 0 || gd->hsize < gd->hlimit) return; ny = gd->hlimit / 10; if (ny < 1) ny = 1; if (ny > gd->hsize) ny = gd->hsize; /* * Free the lines from 0 to ny then move the remaining lines over * them. */ grid_trim_history(gd, ny); gd->hsize -= ny; if (gd->hscrolled > gd->hsize) gd->hscrolled = gd->hsize; } /* * 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 bg) { u_int yy; yy = gd->hsize + gd->sy; gd->linedata = xreallocarray(gd->linedata, yy + 1, sizeof *gd->linedata); grid_empty_line(gd, yy, bg); gd->hscrolled++; grid_compact_line(&gd->linedata[gd->hsize]); gd->hsize++; } /* Clear the history. */ void grid_clear_history(struct grid *gd) { grid_trim_history(gd, gd->hsize); gd->hscrolled = 0; gd->hsize = 0; gd->linedata = xreallocarray(gd->linedata, gd->sy, sizeof *gd->linedata); } /* 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, u_int bg) { struct grid_line *gl_history, *gl_upper; u_int yy; /* Create a space for a new line. */ yy = gd->hsize + gd->sy; gd->linedata = xreallocarray(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++; /* 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); grid_empty_line(gd, lower, bg); /* Move the history offset down over the line. */ gd->hscrolled++; gd->hsize++; } /* Expand line to fit to cell. */ static void grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg) { struct grid_line *gl; u_int xx; gl = &gd->linedata[py]; if (sx <= gl->cellsize) return; if (sx < gd->sx / 4) sx = gd->sx / 4; else if (sx < gd->sx / 2) sx = gd->sx / 2; else sx = gd->sx; gl->celldata = xreallocarray(gl->celldata, sx, sizeof *gl->celldata); for (xx = gl->cellsize; xx < sx; xx++) grid_clear_cell(gd, xx, py, bg); gl->cellsize = sx; } /* Empty a line and set background colour if needed. */ static void grid_empty_line(struct grid *gd, u_int py, u_int bg) { memset(&gd->linedata[py], 0, sizeof gd->linedata[py]); if (!COLOUR_DEFAULT(bg)) grid_expand_line(gd, py, gd->sx, bg); } /* Peek at grid line. */ const struct grid_line * grid_peek_line(struct grid *gd, u_int py) { if (grid_check_y(gd, __func__, py) != 0) return (NULL); return (&gd->linedata[py]); } /* Get cell from line. */ static void grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc) { struct grid_cell_entry *gce = &gl->celldata[px]; if (gce->flags & GRID_FLAG_EXTENDED) { if (gce->offset >= gl->extdsize) memcpy(gc, &grid_default_cell, sizeof *gc); else memcpy(gc, &gl->extddata[gce->offset], sizeof *gc); return; } gc->flags = gce->flags & ~(GRID_FLAG_FG256|GRID_FLAG_BG256); gc->attr = gce->data.attr; gc->fg = gce->data.fg; if (gce->flags & GRID_FLAG_FG256) gc->fg |= COLOUR_FLAG_256; gc->bg = gce->data.bg; if (gce->flags & GRID_FLAG_BG256) gc->bg |= COLOUR_FLAG_256; gc->us = 0; utf8_set(&gc->data, gce->data.data); } /* Get cell for reading. */ void grid_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc) { if (grid_check_y(gd, __func__, py) != 0 || px >= gd->linedata[py].cellsize) memcpy(gc, &grid_default_cell, sizeof *gc); else grid_get_cell1(&gd->linedata[py], px, gc); } /* Set cell at relative position. */ void grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) { struct grid_line *gl; struct grid_cell_entry *gce; if (grid_check_y(gd, __func__, py) != 0) return; grid_expand_line(gd, py, px + 1, 8); gl = &gd->linedata[py]; if (px + 1 > gl->cellused) gl->cellused = px + 1; gce = &gl->celldata[px]; if (grid_need_extended_cell(gce, gc)) grid_extended_cell(gl, gce, gc); else grid_store_cell(gce, gc, gc->data.data[0]); } /* Set cells at relative position. */ void grid_set_cells(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc, const char *s, size_t slen) { struct grid_line *gl; struct grid_cell_entry *gce; struct grid_cell *gcp; u_int i; if (grid_check_y(gd, __func__, py) != 0) return; grid_expand_line(gd, py, px + slen, 8); gl = &gd->linedata[py]; if (px + slen > gl->cellused) gl->cellused = px + slen; for (i = 0; i < slen; i++) { gce = &gl->celldata[px + i]; if (grid_need_extended_cell(gce, gc)) { gcp = grid_extended_cell(gl, gce, gc); utf8_set(&gcp->data, s[i]); } else grid_store_cell(gce, gc, s[i]); } } /* Clear area. */ void grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg) { struct grid_line *gl; u_int xx, yy, ox, sx; if (nx == 0 || ny == 0) return; if (px == 0 && nx == gd->sx) { grid_clear_lines(gd, py, ny, bg); return; } if (grid_check_y(gd, __func__, py) != 0) return; if (grid_check_y(gd, __func__, py + ny - 1) != 0) return; for (yy = py; yy < py + ny; yy++) { gl = &gd->linedata[yy]; sx = gd->sx; if (sx > gl->cellsize) sx = gl->cellsize; ox = nx; if (COLOUR_DEFAULT(bg)) { if (px > sx) continue; if (px + nx > sx) ox = sx - px; } grid_expand_line(gd, yy, px + ox, 8); /* default bg first */ for (xx = px; xx < px + ox; xx++) grid_clear_cell(gd, xx, yy, bg); } } /* Clear lines. This just frees and truncates the lines. */ void grid_clear_lines(struct grid *gd, u_int py, u_int ny, u_int bg) { u_int yy; if (ny == 0) return; if (grid_check_y(gd, __func__, py) != 0) return; if (grid_check_y(gd, __func__, py + ny - 1) != 0) return; for (yy = py; yy < py + ny; yy++) { grid_free_line(gd, yy); grid_empty_line(gd, yy, bg); } } /* Move a group of lines. */ void grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny, u_int bg) { u_int yy; if (ny == 0 || py == dy) return; if (grid_check_y(gd, __func__, py) != 0) return; if (grid_check_y(gd, __func__, py + ny - 1) != 0) return; if (grid_check_y(gd, __func__, dy) != 0) return; if (grid_check_y(gd, __func__, 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_free_line(gd, yy); } memmove(&gd->linedata[dy], &gd->linedata[py], ny * (sizeof *gd->linedata)); /* * Wipe any lines that have been moved (without freeing them - they are * still present). */ for (yy = py; yy < py + ny; yy++) { if (yy < dy || yy >= dy + ny) grid_empty_line(gd, yy, bg); } } /* Move a group of cells. */ void grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx, u_int bg) { struct grid_line *gl; u_int xx; if (nx == 0 || px == dx) return; if (grid_check_y(gd, __func__, py) != 0) return; gl = &gd->linedata[py]; grid_expand_line(gd, py, px + nx, 8); grid_expand_line(gd, py, dx + nx, 8); memmove(&gl->celldata[dx], &gl->celldata[px], nx * sizeof *gl->celldata); if (dx + nx > gl->cellused) gl->cellused = dx + nx; /* Wipe any cells that have been moved. */ for (xx = px; xx < px + nx; xx++) { if (xx >= dx && xx < dx + nx) continue; grid_clear_cell(gd, xx, py, bg); } } /* Get ANSI foreground sequence. */ static size_t grid_string_cells_fg(const struct grid_cell *gc, int *values) { size_t n; u_char r, g, b; n = 0; if (gc->fg & COLOUR_FLAG_256) { values[n++] = 38; values[n++] = 5; values[n++] = gc->fg & 0xff; } else if (gc->fg & COLOUR_FLAG_RGB) { values[n++] = 38; values[n++] = 2; colour_split_rgb(gc->fg, &r, &g, &b); values[n++] = r; values[n++] = g; values[n++] = b; } else { switch (gc->fg) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: values[n++] = gc->fg + 30; break; case 8: values[n++] = 39; break; case 90: case 91: case 92: case 93: case 94: case 95: case 96: case 97: values[n++] = gc->fg; break; } } return (n); } /* Get ANSI background sequence. */ static size_t grid_string_cells_bg(const struct grid_cell *gc, int *values) { size_t n; u_char r, g, b; n = 0; if (gc->bg & COLOUR_FLAG_256) { values[n++] = 48; values[n++] = 5; values[n++] = gc->bg & 0xff; } else if (gc->bg & COLOUR_FLAG_RGB) { values[n++] = 48; values[n++] = 2; colour_split_rgb(gc->bg, &r, &g, &b); values[n++] = r; values[n++] = g; values[n++] = b; } else { switch (gc->bg) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: values[n++] = gc->bg + 40; break; case 8: values[n++] = 49; break; case 100: case 101: case 102: case 103: case 104: case 105: case 106: case 107: values[n++] = gc->bg - 10; break; } } return (n); } /* * Returns ANSI code to set particular attributes (colour, bold and so on) * given a current state. */ static void grid_string_cells_code(const struct grid_cell *lastgc, const struct grid_cell *gc, char *buf, size_t len, int escape_c0) { int oldc[64], newc[64], s[128]; size_t noldc, nnewc, n, i; u_int attr = gc->attr, lastattr = lastgc->attr; char tmp[64]; struct { u_int mask; u_int code; } attrs[] = { { GRID_ATTR_BRIGHT, 1 }, { GRID_ATTR_DIM, 2 }, { GRID_ATTR_ITALICS, 3 }, { GRID_ATTR_UNDERSCORE, 4 }, { GRID_ATTR_BLINK, 5 }, { GRID_ATTR_REVERSE, 7 }, { GRID_ATTR_HIDDEN, 8 }, { GRID_ATTR_STRIKETHROUGH, 9 }, { GRID_ATTR_UNDERSCORE_2, 42 }, { GRID_ATTR_UNDERSCORE_3, 43 }, { GRID_ATTR_UNDERSCORE_4, 44 }, { GRID_ATTR_UNDERSCORE_5, 45 }, { GRID_ATTR_OVERLINE, 53 }, }; n = 0; /* If any attribute is removed, begin with 0. */ for (i = 0; i < nitems(attrs); i++) { if (!(attr & attrs[i].mask) && (lastattr & attrs[i].mask)) { s[n++] = 0; lastattr &= GRID_ATTR_CHARSET; break; } } /* For each attribute that is newly set, add its code. */ for (i = 0; i < nitems(attrs); i++) { if ((attr & attrs[i].mask) && !(lastattr & attrs[i].mask)) s[n++] = attrs[i].code; } /* Write the attributes. */ *buf = '\0'; if (n > 0) { if (escape_c0) strlcat(buf, "\\033[", len); else strlcat(buf, "\033[", len); for (i = 0; i < n; i++) { if (s[i] < 10) xsnprintf(tmp, sizeof tmp, "%d", s[i]); else { xsnprintf(tmp, sizeof tmp, "%d:%d", s[i] / 10, s[i] % 10); } strlcat(buf, tmp, len); if (i + 1 < n) strlcat(buf, ";", len); } strlcat(buf, "m", len); } /* If the foreground colour changed, write its parameters. */ nnewc = grid_string_cells_fg(gc, newc); noldc = grid_string_cells_fg(lastgc, oldc); if (nnewc != noldc || memcmp(newc, oldc, nnewc * sizeof newc[0]) != 0 || (n != 0 && s[0] == 0)) { if (escape_c0) strlcat(buf, "\\033[", len); else strlcat(buf, "\033[", len); for (i = 0; i < nnewc; i++) { if (i + 1 < nnewc) xsnprintf(tmp, sizeof tmp, "%d;", newc[i]); else xsnprintf(tmp, sizeof tmp, "%d", newc[i]); strlcat(buf, tmp, len); } strlcat(buf, "m", len); } /* If the background colour changed, append its parameters. */ nnewc = grid_string_cells_bg(gc, newc); noldc = grid_string_cells_bg(lastgc, oldc); if (nnewc != noldc || memcmp(newc, oldc, nnewc * sizeof newc[0]) != 0 || (n != 0 && s[0] == 0)) { if (escape_c0) strlcat(buf, "\\033[", len); else strlcat(buf, "\033[", len); for (i = 0; i < nnewc; i++) { if (i + 1 < nnewc) xsnprintf(tmp, sizeof tmp, "%d;", newc[i]); else xsnprintf(tmp, sizeof tmp, "%d", newc[i]); strlcat(buf, tmp, len); } strlcat(buf, "m", len); } /* Append shift in/shift out if needed. */ if ((attr & GRID_ATTR_CHARSET) && !(lastattr & GRID_ATTR_CHARSET)) { if (escape_c0) strlcat(buf, "\\016", len); /* SO */ else strlcat(buf, "\016", len); /* SO */ } if (!(attr & GRID_ATTR_CHARSET) && (lastattr & GRID_ATTR_CHARSET)) { if (escape_c0) strlcat(buf, "\\017", len); /* SI */ else strlcat(buf, "\017", len); /* SI */ } } /* Convert cells into a string. */ char * grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, struct grid_cell **lastgc, int with_codes, int escape_c0, int trim) { struct grid_cell gc; static struct grid_cell lastgc1; const char *data; char *buf, code[128]; size_t len, off, size, codelen; u_int xx; const struct grid_line *gl; if (lastgc != NULL && *lastgc == NULL) { memcpy(&lastgc1, &grid_default_cell, sizeof lastgc1); *lastgc = &lastgc1; } len = 128; buf = xmalloc(len); off = 0; gl = grid_peek_line(gd, py); for (xx = px; xx < px + nx; xx++) { if (gl == NULL || xx >= gl->cellsize) break; grid_get_cell(gd, xx, py, &gc); if (gc.flags & GRID_FLAG_PADDING) continue; if (with_codes) { grid_string_cells_code(*lastgc, &gc, code, sizeof code, escape_c0); codelen = strlen(code); memcpy(*lastgc, &gc, sizeof **lastgc); } else codelen = 0; data = gc.data.data; size = gc.data.size; if (escape_c0 && size == 1 && *data == '\\') { data = "\\\\"; size = 2; } while (len < off + size + codelen + 1) { buf = xreallocarray(buf, 2, len); len *= 2; } if (codelen != 0) { memcpy(buf + off, code, codelen); off += codelen; } memcpy(buf + off, data, size); off += size; } if (trim) { while (off > 0 && buf[off - 1] == ' ') off--; } buf[off] = '\0'; return (buf); } /* * Duplicate a set of lines between two grids. Both source and destination * should be big enough. */ 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; 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_free_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 = xreallocarray(NULL, srcl->cellsize, sizeof *dstl->celldata); memcpy(dstl->celldata, srcl->celldata, srcl->cellsize * sizeof *dstl->celldata); } else dstl->celldata = NULL; if (srcl->extdsize != 0) { dstl->extdsize = srcl->extdsize; dstl->extddata = xreallocarray(NULL, dstl->extdsize, sizeof *dstl->extddata); memcpy(dstl->extddata, srcl->extddata, dstl->extdsize * sizeof *dstl->extddata); } sy++; dy++; } } /* Mark line as dead. */ static void grid_reflow_dead(struct grid_line *gl) { memset(gl, 0, sizeof *gl); gl->flags = GRID_LINE_DEAD; } /* Add lines, return the first new one. */ static struct grid_line * grid_reflow_add(struct grid *gd, u_int n) { struct grid_line *gl; u_int sy = gd->sy + n; gd->linedata = xreallocarray(gd->linedata, sy, sizeof *gd->linedata); gl = &gd->linedata[gd->sy]; memset(gl, 0, n * (sizeof *gl)); gd->sy = sy; return (gl); } /* Move a line across. */ static struct grid_line * grid_reflow_move(struct grid *gd, struct grid_line *from) { struct grid_line *to; to = grid_reflow_add(gd, 1); memcpy(to, from, sizeof *to); grid_reflow_dead(from); return (to); } /* Join line below onto this one. */ static void grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy, u_int width, int already) { struct grid_line *gl, *from = NULL; struct grid_cell gc; u_int lines, left, i, to, line, want = 0; u_int at; int wrapped = 1; /* * Add a new target line. */ if (!already) { to = target->sy; gl = grid_reflow_move(target, &gd->linedata[yy]); } else { to = target->sy - 1; gl = &target->linedata[to]; } at = gl->cellused; /* * Loop until no more to consume or the target line is full. */ lines = 0; for (;;) { /* * If this is now the last line, there is nothing more to be * done. */ if (yy + 1 + lines == gd->hsize + gd->sy) break; line = yy + 1 + lines; /* If the next line is empty, skip it. */ if (~gd->linedata[line].flags & GRID_LINE_WRAPPED) wrapped = 0; if (gd->linedata[line].cellused == 0) { if (!wrapped) break; lines++; continue; } /* * Is the destination line now full? Copy the first character * separately because we need to leave "from" set to the last * line if this line is full. */ grid_get_cell1(&gd->linedata[line], 0, &gc); if (width + gc.data.width > sx) break; width += gc.data.width; grid_set_cell(target, at, to, &gc); at++; /* Join as much more as possible onto the current line. */ from = &gd->linedata[line]; for (want = 1; want < from->cellused; want++) { grid_get_cell1(from, want, &gc); if (width + gc.data.width > sx) break; width += gc.data.width; grid_set_cell(target, at, to, &gc); at++; } lines++; /* * If this line wasn't wrapped or we didn't consume the entire * line, don't try to join any further lines. */ if (!wrapped || want != from->cellused || width == sx) break; } if (lines == 0) return; /* * If we didn't consume the entire final line, then remove what we did * consume. If we consumed the entire line and it wasn't wrapped, * remove the wrap flag from this line. */ left = from->cellused - want; if (left != 0) { grid_move_cells(gd, 0, want, yy + lines, left, 8); from->cellsize = from->cellused = left; lines--; } else if (!wrapped) gl->flags &= ~GRID_LINE_WRAPPED; /* Remove the lines that were completely consumed. */ for (i = yy + 1; i < yy + 1 + lines; i++) { free(gd->linedata[i].celldata); free(gd->linedata[i].extddata); grid_reflow_dead(&gd->linedata[i]); } /* Adjust scroll position. */ if (gd->hscrolled > to + lines) gd->hscrolled -= lines; else if (gd->hscrolled > to) gd->hscrolled = to; } /* Split this line into several new ones */ static void grid_reflow_split(struct grid *target, struct grid *gd, u_int sx, u_int yy, u_int at) { struct grid_line *gl = &gd->linedata[yy], *first; struct grid_cell gc; u_int line, lines, width, i, xx; u_int used = gl->cellused; int flags = gl->flags; /* How many lines do we need to insert? We know we need at least two. */ if (~gl->flags & GRID_LINE_EXTENDED) lines = 1 + (gl->cellused - 1) / sx; else { lines = 2; width = 0; for (i = at; i < used; i++) { grid_get_cell1(gl, i, &gc); if (width + gc.data.width > sx) { lines++; width = 0; } width += gc.data.width; } } /* Insert new lines. */ line = target->sy + 1; first = grid_reflow_add(target, lines); /* Copy sections from the original line. */ width = 0; xx = 0; for (i = at; i < used; i++) { grid_get_cell1(gl, i, &gc); if (width + gc.data.width > sx) { target->linedata[line].flags |= GRID_LINE_WRAPPED; line++; width = 0; xx = 0; } width += gc.data.width; grid_set_cell(target, xx, line, &gc); xx++; } if (flags & GRID_LINE_WRAPPED) target->linedata[line].flags |= GRID_LINE_WRAPPED; /* Move the remainder of the original line. */ gl->cellsize = gl->cellused = at; gl->flags |= GRID_LINE_WRAPPED; memcpy(first, gl, sizeof *first); grid_reflow_dead(gl); /* Adjust the scroll position. */ if (yy <= gd->hscrolled) gd->hscrolled += lines - 1; /* * If the original line had the wrapped flag and there is still space * in the last new line, try to join with the next lines. */ if (width < sx && (flags & GRID_LINE_WRAPPED)) grid_reflow_join(target, gd, sx, yy, width, 1); } /* Reflow lines on grid to new width. */ void grid_reflow(struct grid *gd, u_int sx) { struct grid *target; struct grid_line *gl; struct grid_cell gc; u_int yy, width, i, at, first; /* * Create a destination grid. This is just used as a container for the * line data and may not be fully valid. */ target = grid_create(gd->sx, 0, 0); /* * Loop over each source line. */ for (yy = 0; yy < gd->hsize + gd->sy; yy++) { gl = &gd->linedata[yy]; if (gl->flags & GRID_LINE_DEAD) continue; /* * Work out the width of this line. first is the width of the * first character, at is the point at which the available * width is hit, and width is the full line width. */ first = at = width = 0; if (~gl->flags & GRID_LINE_EXTENDED) { first = 1; width = gl->cellused; if (width > sx) at = sx; else at = width; } else { for (i = 0; i < gl->cellused; i++) { grid_get_cell1(gl, i, &gc); if (i == 0) first = gc.data.width; if (at == 0 && width + gc.data.width > sx) at = i; width += gc.data.width; } } /* * If the line is exactly right or the first character is wider * than the targe width, just move it across unchanged. */ if (width == sx || first > sx) { grid_reflow_move(target, gl); continue; } /* * If the line is too big, it needs to be split, whether or not * it was previously wrapped. */ if (width > sx) { grid_reflow_split(target, gd, sx, yy, at); continue; } /* * If the line was previously wrapped, join as much as possible * of the next line. */ if (gl->flags & GRID_LINE_WRAPPED) grid_reflow_join(target, gd, sx, yy, width, 0); else grid_reflow_move(target, gl); } /* * Replace the old grid with the new. */ if (target->sy < gd->sy) grid_reflow_add(target, gd->sy - target->sy); gd->hsize = target->sy - gd->sy; if (gd->hscrolled > gd->hsize) gd->hscrolled = gd->hsize; free(gd->linedata); gd->linedata = target->linedata; free(target); } /* Convert to position based on wrapped lines. */ void grid_wrap_position(struct grid *gd, u_int px, u_int py, u_int *wx, u_int *wy) { u_int ax = 0, ay = 0, yy; for (yy = 0; yy < py; yy++) { if (gd->linedata[yy].flags & GRID_LINE_WRAPPED) ax += gd->linedata[yy].cellused; else { ax = 0; ay++; } } if (px >= gd->linedata[yy].cellused) ax = UINT_MAX; else ax += px; *wx = ax; *wy = ay; } /* Convert position based on wrapped lines back. */ void grid_unwrap_position(struct grid *gd, u_int *px, u_int *py, u_int wx, u_int wy) { u_int yy, ax = 0, ay = 0; for (yy = 0; yy < gd->hsize + gd->sy - 1; yy++) { if (ay == wy) break; if (gd->linedata[yy].flags & GRID_LINE_WRAPPED) ax += gd->linedata[yy].cellused; else { ax = 0; ay++; } } /* * yy is now 0 on the unwrapped line which contains wx. Walk forwards * until we find the end or the line now containing wx. */ if (wx == UINT_MAX) { while (gd->linedata[yy].flags & GRID_LINE_WRAPPED) yy++; wx = gd->linedata[yy].cellused; } else { while (gd->linedata[yy].flags & GRID_LINE_WRAPPED) { if (wx < gd->linedata[yy].cellused) break; wx -= gd->linedata[yy].cellused; yy++; } } *px = wx; *py = yy; } /* Get length of line. */ u_int grid_line_length(struct grid *gd, u_int py) { struct grid_cell gc; u_int px; px = grid_get_line(gd, py)->cellsize; if (px > gd->sx) px = gd->sx; while (px > 0) { grid_get_cell(gd, px - 1, py, &gc); if (gc.data.size != 1 || *gc.data.data != ' ') break; px--; } return (px); } tmux-3.0a/input-keys.c100644 001750 001750 00000022304 13570677043 0010505/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * 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). */ static void input_key_mouse(struct window_pane *, struct mouse_event *); struct input_key_ent { key_code key; const char *data; int flags; #define INPUTKEY_KEYPAD 0x1 /* keypad key */ #define INPUTKEY_CURSOR 0x2 /* cursor key */ }; static const struct input_key_ent input_keys[] = { /* Backspace key. */ { KEYC_BSPACE, "\177", 0 }, /* Paste keys. */ { KEYC_PASTE_START, "\033[200~", 0 }, { KEYC_PASTE_END, "\033[201~", 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_F1|KEYC_SHIFT, "\033[25~", 0 }, { KEYC_F2|KEYC_SHIFT, "\033[26~", 0 }, { KEYC_F3|KEYC_SHIFT, "\033[28~", 0 }, { KEYC_F4|KEYC_SHIFT, "\033[29~", 0 }, { KEYC_F5|KEYC_SHIFT, "\033[31~", 0 }, { KEYC_F6|KEYC_SHIFT, "\033[32~", 0 }, { KEYC_F7|KEYC_SHIFT, "\033[33~", 0 }, { KEYC_F8|KEYC_SHIFT, "\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 }, }; /* Split a character into two UTF-8 bytes. */ static size_t input_split2(u_int c, u_char *dst) { if (c > 0x7f) { dst[0] = (c >> 6) | 0xc0; dst[1] = (c & 0x3f) | 0x80; return (2); } dst[0] = c; return (1); } /* Translate a key code into an output key sequence. */ void input_key(struct window_pane *wp, key_code key, struct mouse_event *m) { const struct input_key_ent *ike; u_int i; size_t dlen; char *out; key_code justkey; struct utf8_data ud; log_debug("writing key 0x%llx (%s) to %%%u", key, key_string_lookup_key(key), wp->id); /* If this is a mouse key, pass off to mouse function. */ if (KEYC_IS_MOUSE(key)) { if (m != NULL && m->wp != -1 && (u_int)m->wp == wp->id) input_key_mouse(wp, m); return; } /* Literal keys go as themselves (can't be more than eight bits). */ if (key & KEYC_LITERAL) { ud.data[0] = (u_char)key; bufferevent_write(wp->event, &ud.data[0], 1); return; } /* * If this is a normal 7-bit key, just send it, with a leading escape * if necessary. If it is a UTF-8 key, split it and send it. */ justkey = (key & ~(KEYC_XTERM|KEYC_ESCAPE)); if (justkey <= 0x7f) { if (key & KEYC_ESCAPE) bufferevent_write(wp->event, "\033", 1); ud.data[0] = justkey; bufferevent_write(wp->event, &ud.data[0], 1); return; } if (justkey > 0x7f && justkey < KEYC_BASE) { if (utf8_split(justkey, &ud) != UTF8_DONE) return; if (key & KEYC_ESCAPE) bufferevent_write(wp->event, "\033", 1); bufferevent_write(wp->event, ud.data, ud.size); 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)); free(out); return; } } key &= ~KEYC_XTERM; /* 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_debug("key 0x%llx missing", key); return; } dlen = strlen(ike->data); log_debug("found key 0x%llx: \"%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. */ static void input_key_mouse(struct window_pane *wp, struct mouse_event *m) { struct screen *s = wp->screen; int mode = s->mode; char buf[40]; size_t len; u_int x, y; if ((mode & ALL_MOUSE_MODES) == 0) return; if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) return; if (!window_pane_visible(wp)) return; /* If this pane is not in button or all mode, discard motion events. */ if (MOUSE_DRAG(m->b) && (mode & (MODE_MOUSE_BUTTON|MODE_MOUSE_ALL)) == 0) return; /* * If this event is a release event and not in all mode, discard it. * In SGR mode we can tell absolutely because a release is normally * shown by the last character. Without SGR, we check if the last * buttons was also a release. */ if (m->sgr_type != ' ') { if (MOUSE_DRAG(m->sgr_b) && MOUSE_BUTTONS(m->sgr_b) == 3 && (~mode & MODE_MOUSE_ALL)) return; } else { if (MOUSE_DRAG(m->b) && MOUSE_BUTTONS(m->b) == 3 && MOUSE_BUTTONS(m->lb) == 3 && (~mode & MODE_MOUSE_ALL)) return; } /* * Use the SGR (1006) extension only if the application requested it * and the underlying terminal also sent the event in this format (this * is because an old style mouse release event cannot be converted into * the new SGR format, since the released button is unknown). Otherwise * pretend that tmux doesn't speak this extension, and fall back to the * UTF-8 (1005) extension if the application requested, or to the * legacy format. */ if (m->sgr_type != ' ' && (s->mode & MODE_MOUSE_SGR)) { len = xsnprintf(buf, sizeof buf, "\033[<%u;%u;%u%c", m->sgr_b, x + 1, y + 1, m->sgr_type); } else if (s->mode & MODE_MOUSE_UTF8) { if (m->b > 0x7ff - 32 || x > 0x7ff - 33 || y > 0x7ff - 33) return; len = xsnprintf(buf, sizeof buf, "\033[M"); len += input_split2(m->b + 32, &buf[len]); len += input_split2(x + 33, &buf[len]); len += input_split2(y + 33, &buf[len]); } else { if (m->b > 223) return; len = xsnprintf(buf, sizeof buf, "\033[M"); buf[len++] = m->b + 32; buf[len++] = x + 33; buf[len++] = y + 33; } log_debug("writing mouse %.*s to %%%u", (int)len, buf, wp->id); bufferevent_write(wp->event, buf, len); } tmux-3.0a/input.c100644 001750 001750 00000162446 13570677043 0007550/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "tmux.h" /* * Based on the description by Paul Williams at: * * https://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 terminals. */ /* Input parser cell. */ struct input_cell { struct grid_cell cell; int set; int g0set; /* 1 if ACS */ int g1set; /* 1 if ACS */ }; /* Input parser argument. */ struct input_param { enum { INPUT_MISSING, INPUT_NUMBER, INPUT_STRING } type; union { int num; char *str; }; }; /* Input parser context. */ struct input_ctx { struct window_pane *wp; struct screen_write_ctx ctx; struct input_cell cell; struct input_cell old_cell; u_int old_cx; u_int old_cy; int old_mode; u_char interm_buf[4]; size_t interm_len; u_char param_buf[64]; size_t param_len; #define INPUT_BUF_START 32 #define INPUT_BUF_LIMIT 1048576 u_char *input_buf; size_t input_len; size_t input_space; enum { INPUT_END_ST, INPUT_END_BEL } input_end; struct input_param param_list[24]; u_int param_list_len; struct utf8_data utf8data; int utf8started; int ch; int last; int flags; #define INPUT_DISCARD 0x1 const struct input_state *state; struct event timer; /* * All input received since we were last in the ground state. Sent to * control clients on connection. */ struct evbuffer *since_ground; }; /* Helper functions. */ struct input_transition; static int input_split(struct input_ctx *); static int input_get(struct input_ctx *, u_int, int, int); static void printflike(2, 3) input_reply(struct input_ctx *, const char *, ...); static void input_set_state(struct window_pane *, const struct input_transition *); static void input_reset_cell(struct input_ctx *); static void input_osc_4(struct input_ctx *, const char *); static void input_osc_10(struct input_ctx *, const char *); static void input_osc_11(struct input_ctx *, const char *); static void input_osc_52(struct input_ctx *, const char *); static void input_osc_104(struct input_ctx *, const char *); /* Transition entry/exit handlers. */ static void input_clear(struct input_ctx *); static void input_ground(struct input_ctx *); static void input_enter_dcs(struct input_ctx *); static void input_enter_osc(struct input_ctx *); static void input_exit_osc(struct input_ctx *); static void input_enter_apc(struct input_ctx *); static void input_exit_apc(struct input_ctx *); static void input_enter_rename(struct input_ctx *); static void input_exit_rename(struct input_ctx *); /* Input state handlers. */ static int input_print(struct input_ctx *); static int input_intermediate(struct input_ctx *); static int input_parameter(struct input_ctx *); static int input_input(struct input_ctx *); static int input_c0_dispatch(struct input_ctx *); static int input_esc_dispatch(struct input_ctx *); static int input_csi_dispatch(struct input_ctx *); static void input_csi_dispatch_rm(struct input_ctx *); static void input_csi_dispatch_rm_private(struct input_ctx *); static void input_csi_dispatch_sm(struct input_ctx *); static void input_csi_dispatch_sm_private(struct input_ctx *); static void input_csi_dispatch_winops(struct input_ctx *); static void input_csi_dispatch_sgr_256(struct input_ctx *, int, u_int *); static void input_csi_dispatch_sgr_rgb(struct input_ctx *, int, u_int *); static void input_csi_dispatch_sgr(struct input_ctx *); static int input_dcs_dispatch(struct input_ctx *); static int input_top_bit_set(struct input_ctx *); static int input_end_bel(struct input_ctx *); /* Command table comparison function. */ static 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_SCSG0_OFF, INPUT_ESC_SCSG0_ON, INPUT_ESC_SCSG1_OFF, INPUT_ESC_SCSG1_ON, INPUT_ESC_ST, }; /* Escape command table. */ static const struct input_table_entry input_esc_table[] = { { '0', "(", INPUT_ESC_SCSG0_ON }, { '0', ")", INPUT_ESC_SCSG1_ON }, { '7', "", INPUT_ESC_DECSC }, { '8', "", INPUT_ESC_DECRC }, { '8', "#", INPUT_ESC_DECALN }, { '=', "", INPUT_ESC_DECKPAM }, { '>', "", INPUT_ESC_DECKPNM }, { 'B', "(", INPUT_ESC_SCSG0_OFF }, { 'B', ")", INPUT_ESC_SCSG1_OFF }, { 'D', "", INPUT_ESC_IND }, { 'E', "", INPUT_ESC_NEL }, { 'H', "", INPUT_ESC_HTS }, { 'M', "", INPUT_ESC_RI }, { '\\', "", INPUT_ESC_ST }, { 'c', "", INPUT_ESC_RIS }, }; /* Control (CSI) commands. */ enum input_csi_type { INPUT_CSI_CBT, INPUT_CSI_CNL, INPUT_CSI_CPL, INPUT_CSI_CUB, INPUT_CSI_CUD, INPUT_CSI_CUF, INPUT_CSI_CUP, INPUT_CSI_CUU, INPUT_CSI_DA, INPUT_CSI_DA_TWO, INPUT_CSI_DCH, INPUT_CSI_DECSCUSR, INPUT_CSI_DECSTBM, INPUT_CSI_DL, INPUT_CSI_DSR, INPUT_CSI_ECH, INPUT_CSI_ED, INPUT_CSI_EL, INPUT_CSI_HPA, INPUT_CSI_ICH, INPUT_CSI_IL, INPUT_CSI_RCP, INPUT_CSI_REP, INPUT_CSI_RM, INPUT_CSI_RM_PRIVATE, INPUT_CSI_SCP, INPUT_CSI_SD, INPUT_CSI_SGR, INPUT_CSI_SM, INPUT_CSI_SM_PRIVATE, INPUT_CSI_SU, INPUT_CSI_TBC, INPUT_CSI_VPA, INPUT_CSI_WINOPS, }; /* Control (CSI) command table. */ static 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 }, { 'E', "", INPUT_CSI_CNL }, { 'F', "", INPUT_CSI_CPL }, { '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 }, { 'S', "", INPUT_CSI_SU }, { 'T', "", INPUT_CSI_SD }, { 'X', "", INPUT_CSI_ECH }, { 'Z', "", INPUT_CSI_CBT }, { '`', "", INPUT_CSI_HPA }, { 'b', "", INPUT_CSI_REP }, { 'c', "", INPUT_CSI_DA }, { 'c', ">", INPUT_CSI_DA_TWO }, { '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 }, { 's', "", INPUT_CSI_SCP }, { 't', "", INPUT_CSI_WINOPS }, { 'u', "", INPUT_CSI_RCP }, }; /* 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. */ static const struct input_transition input_state_ground_table[]; static const struct input_transition input_state_esc_enter_table[]; static const struct input_transition input_state_esc_intermediate_table[]; static const struct input_transition input_state_csi_enter_table[]; static const struct input_transition input_state_csi_parameter_table[]; static const struct input_transition input_state_csi_intermediate_table[]; static const struct input_transition input_state_csi_ignore_table[]; static const struct input_transition input_state_dcs_enter_table[]; static const struct input_transition input_state_dcs_parameter_table[]; static const struct input_transition input_state_dcs_intermediate_table[]; static const struct input_transition input_state_dcs_handler_table[]; static const struct input_transition input_state_dcs_escape_table[]; static const struct input_transition input_state_dcs_ignore_table[]; static const struct input_transition input_state_osc_string_table[]; static const struct input_transition input_state_apc_string_table[]; static const struct input_transition input_state_rename_string_table[]; static const struct input_transition input_state_consume_st_table[]; /* ground state definition. */ static const struct input_state input_state_ground = { "ground", input_ground, NULL, input_state_ground_table }; /* esc_enter state definition. */ static const struct input_state input_state_esc_enter = { "esc_enter", input_clear, NULL, input_state_esc_enter_table }; /* esc_intermediate state definition. */ static const struct input_state input_state_esc_intermediate = { "esc_intermediate", NULL, NULL, input_state_esc_intermediate_table }; /* csi_enter state definition. */ static const struct input_state input_state_csi_enter = { "csi_enter", input_clear, NULL, input_state_csi_enter_table }; /* csi_parameter state definition. */ static const struct input_state input_state_csi_parameter = { "csi_parameter", NULL, NULL, input_state_csi_parameter_table }; /* csi_intermediate state definition. */ static const struct input_state input_state_csi_intermediate = { "csi_intermediate", NULL, NULL, input_state_csi_intermediate_table }; /* csi_ignore state definition. */ static const struct input_state input_state_csi_ignore = { "csi_ignore", NULL, NULL, input_state_csi_ignore_table }; /* dcs_enter state definition. */ static const struct input_state input_state_dcs_enter = { "dcs_enter", input_enter_dcs, NULL, input_state_dcs_enter_table }; /* dcs_parameter state definition. */ static const struct input_state input_state_dcs_parameter = { "dcs_parameter", NULL, NULL, input_state_dcs_parameter_table }; /* dcs_intermediate state definition. */ static const struct input_state input_state_dcs_intermediate = { "dcs_intermediate", NULL, NULL, input_state_dcs_intermediate_table }; /* dcs_handler state definition. */ static const struct input_state input_state_dcs_handler = { "dcs_handler", NULL, NULL, input_state_dcs_handler_table }; /* dcs_escape state definition. */ static const struct input_state input_state_dcs_escape = { "dcs_escape", NULL, NULL, input_state_dcs_escape_table }; /* dcs_ignore state definition. */ static const struct input_state input_state_dcs_ignore = { "dcs_ignore", NULL, NULL, input_state_dcs_ignore_table }; /* osc_string state definition. */ static 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. */ static 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. */ static 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. */ static const struct input_state input_state_consume_st = { "consume_st", input_enter_rename, NULL, /* rename also waits for ST */ input_state_consume_st_table }; /* ground state table. */ static 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, 0xff, input_top_bit_set, NULL }, { -1, -1, NULL, NULL } }; /* esc_enter state table. */ static 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_intermediate state table. */ static 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. */ static 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, input_parameter, &input_state_csi_parameter }, { 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. */ static 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, input_parameter, NULL }, { 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. */ static 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. */ static 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. */ static 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. */ static 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_intermediate state table. */ static 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. */ static 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. */ static 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. */ static 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. */ static const struct input_transition input_state_osc_string_table[] = { INPUT_STATE_ANYWHERE, { 0x00, 0x06, NULL, NULL }, { 0x07, 0x07, input_end_bel, &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. */ static 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. */ static 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. */ static 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 } }; /* Input table compare. */ static 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(ictx->interm_buf, entry->interm)); } /* * Timer - if this expires then have been waiting for a terminator for too * long, so reset to ground. */ static void input_timer_callback(__unused int fd, __unused short events, void *arg) { struct input_ctx *ictx = arg; struct window_pane *wp = ictx->wp; log_debug("%s: %%%u %s expired" , __func__, wp->id, ictx->state->name); input_reset(wp, 0); } /* Start the timer. */ static void input_start_timer(struct input_ctx *ictx) { struct timeval tv = { .tv_usec = 100000 }; event_del(&ictx->timer); event_add(&ictx->timer, &tv); } /* Reset cell state to default. */ static void input_reset_cell(struct input_ctx *ictx) { memcpy(&ictx->cell.cell, &grid_default_cell, sizeof ictx->cell.cell); ictx->cell.set = 0; ictx->cell.g0set = ictx->cell.g1set = 0; memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); ictx->old_cx = 0; ictx->old_cy = 0; } /* Save screen state. */ static void input_save_state(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; struct screen *s = sctx->s; memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); ictx->old_cx = s->cx; ictx->old_cy = s->cy; ictx->old_mode = s->mode; } static void input_restore_state(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; memcpy(&ictx->cell, &ictx->old_cell, sizeof ictx->cell); if (ictx->old_mode & MODE_ORIGIN) screen_write_mode_set(sctx, MODE_ORIGIN); else screen_write_mode_clear(sctx, MODE_ORIGIN); screen_write_cursormove(sctx, ictx->old_cx, ictx->old_cy, 0); } /* Initialise input parser. */ void input_init(struct window_pane *wp) { struct input_ctx *ictx; ictx = wp->ictx = xcalloc(1, sizeof *ictx); ictx->input_space = INPUT_BUF_START; ictx->input_buf = xmalloc(INPUT_BUF_START); ictx->since_ground = evbuffer_new(); if (ictx->since_ground == NULL) fatalx("out of memory"); evtimer_set(&ictx->timer, input_timer_callback, ictx); input_reset(wp, 0); } /* Destroy input parser. */ void input_free(struct window_pane *wp) { struct input_ctx *ictx = wp->ictx; u_int i; for (i = 0; i < ictx->param_list_len; i++) { if (ictx->param_list[i].type == INPUT_STRING) free(ictx->param_list[i].str); } event_del(&ictx->timer); free(ictx->input_buf); evbuffer_free(ictx->since_ground); free(ictx); wp->ictx = NULL; } /* Reset input state and clear screen. */ void input_reset(struct window_pane *wp, int clear) { struct input_ctx *ictx = wp->ictx; struct screen_write_ctx *sctx = &ictx->ctx; input_reset_cell(ictx); if (clear) { if (TAILQ_EMPTY(&wp->modes)) screen_write_start(sctx, wp, &wp->base); else screen_write_start(sctx, NULL, &wp->base); screen_write_reset(sctx); screen_write_stop(sctx); } input_clear(ictx); ictx->last = -1; ictx->state = &input_state_ground; ictx->flags = 0; } /* Return pending data. */ struct evbuffer * input_pending(struct window_pane *wp) { return (wp->ictx->since_ground); } /* Change input state. */ static void input_set_state(struct window_pane *wp, const struct input_transition *itr) { struct input_ctx *ictx = wp->ictx; if (ictx->state->exit != NULL) ictx->state->exit(ictx); ictx->state = itr->state; if (ictx->state->enter != NULL) ictx->state->enter(ictx); } /* Parse input. */ void input_parse(struct window_pane *wp) { struct evbuffer *evb = wp->event->input; input_parse_buffer(wp, EVBUFFER_DATA(evb), EVBUFFER_LENGTH(evb)); evbuffer_drain(evb, EVBUFFER_LENGTH(evb)); } /* Parse given input. */ void input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len) { struct input_ctx *ictx = wp->ictx; struct screen_write_ctx *sctx = &ictx->ctx; const struct input_transition *itr; size_t off = 0; if (len == 0) return; window_update_activity(wp->window); wp->flags |= PANE_CHANGED; notify_input(wp, buf, len); /* * Open the screen. Use NULL wp if there is a mode set as don't want to * update the tty. */ if (TAILQ_EMPTY(&wp->modes)) screen_write_start(sctx, wp, &wp->base); else screen_write_start(sctx, NULL, &wp->base); ictx->wp = wp; log_debug("%s: %%%u %s, %zu bytes: %.*s", __func__, wp->id, ictx->state->name, len, (int)len, buf); /* Parse the input. */ while (off < len) { ictx->ch = buf[off++]; /* 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"); } /* * Any state except print stops the current collection. This is * an optimization to avoid checking if the attributes have * changed for every character. It will stop unnecessarily for * sequences that don't make a terminal change, but they should * be the minority. */ if (itr->handler != input_print) screen_write_collect_end(sctx); /* * 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) input_set_state(wp, itr); /* If not in ground state, save input. */ if (ictx->state != &input_state_ground) evbuffer_add(ictx->since_ground, &ictx->ch, 1); } /* Close the screen. */ screen_write_stop(sctx); } /* Split the parameter list (if any). */ static int input_split(struct input_ctx *ictx) { const char *errstr; char *ptr, *out; struct input_param *ip; u_int i; for (i = 0; i < ictx->param_list_len; i++) { if (ictx->param_list[i].type == INPUT_STRING) free(ictx->param_list[i].str); } ictx->param_list_len = 0; if (ictx->param_len == 0) return (0); ip = &ictx->param_list[0]; ptr = ictx->param_buf; while ((out = strsep(&ptr, ";")) != NULL) { if (*out == '\0') ip->type = INPUT_MISSING; else { if (strchr(out, ':') != NULL) { ip->type = INPUT_STRING; ip->str = xstrdup(out); } else { ip->type = INPUT_NUMBER; ip->num = strtonum(out, 0, INT_MAX, &errstr); if (errstr != NULL) return (-1); } } ip = &ictx->param_list[++ictx->param_list_len]; if (ictx->param_list_len == nitems(ictx->param_list)) return (-1); } for (i = 0; i < ictx->param_list_len; i++) { ip = &ictx->param_list[i]; if (ip->type == INPUT_MISSING) log_debug("parameter %u: missing", i); else if (ip->type == INPUT_STRING) log_debug("parameter %u: string %s", i, ip->str); else if (ip->type == INPUT_NUMBER) log_debug("parameter %u: number %d", i, ip->num); } return (0); } /* Get an argument or return default value. */ static int input_get(struct input_ctx *ictx, u_int validx, int minval, int defval) { struct input_param *ip; int retval; if (validx >= ictx->param_list_len) return (defval); ip = &ictx->param_list[validx]; if (ip->type == INPUT_MISSING) return (defval); if (ip->type == INPUT_STRING) return (-1); retval = ip->num; if (retval < minval) return (minval); return (retval); } /* Reply to terminal query. */ static void input_reply(struct input_ctx *ictx, const char *fmt, ...) { va_list ap; char *reply; va_start(ap, fmt); xvasprintf(&reply, fmt, ap); va_end(ap); bufferevent_write(ictx->wp->event, reply, strlen(reply)); free(reply); } /* Clear saved state. */ static void input_clear(struct input_ctx *ictx) { event_del(&ictx->timer); *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->input_end = INPUT_END_ST; ictx->flags &= ~INPUT_DISCARD; } /* Reset for ground state. */ static void input_ground(struct input_ctx *ictx) { event_del(&ictx->timer); evbuffer_drain(ictx->since_ground, EVBUFFER_LENGTH(ictx->since_ground)); if (ictx->input_space > INPUT_BUF_START) { ictx->input_space = INPUT_BUF_START; ictx->input_buf = xrealloc(ictx->input_buf, INPUT_BUF_START); } } /* Output this character to the screen. */ static int input_print(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; int set; ictx->utf8started = 0; /* can't be valid UTF-8 */ set = ictx->cell.set == 0 ? ictx->cell.g0set : ictx->cell.g1set; if (set == 1) ictx->cell.cell.attr |= GRID_ATTR_CHARSET; else ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET; utf8_set(&ictx->cell.cell.data, ictx->ch); screen_write_collect_add(sctx, &ictx->cell.cell); ictx->last = ictx->ch; ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET; return (0); } /* Collect intermediate string. */ static 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. */ static 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. */ static int input_input(struct input_ctx *ictx) { size_t available; available = ictx->input_space; while (ictx->input_len + 1 >= available) { available *= 2; if (available > INPUT_BUF_LIMIT) { ictx->flags |= INPUT_DISCARD; return (0); } ictx->input_buf = xrealloc(ictx->input_buf, available); ictx->input_space = available; } ictx->input_buf[ictx->input_len++] = ictx->ch; ictx->input_buf[ictx->input_len] = '\0'; return (0); } /* Execute C0 control sequence. */ static 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; ictx->utf8started = 0; /* can't be valid UTF-8 */ log_debug("%s: '%c'", __func__, ictx->ch); switch (ictx->ch) { case '\000': /* NUL */ break; case '\007': /* BEL */ alerts_queue(wp->window, 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, ictx->cell.cell.bg); if (s->mode & MODE_CRLF) screen_write_carriagereturn(sctx); break; case '\015': /* CR */ screen_write_carriagereturn(sctx); break; case '\016': /* SO */ ictx->cell.set = 1; break; case '\017': /* SI */ ictx->cell.set = 0; break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } ictx->last = -1; return (0); } /* Execute escape sequence. */ static 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: window_pane_reset_palette(ictx->wp); input_reset_cell(ictx); screen_write_reset(sctx); break; case INPUT_ESC_IND: screen_write_linefeed(sctx, 0, ictx->cell.cell.bg); break; case INPUT_ESC_NEL: screen_write_carriagereturn(sctx); screen_write_linefeed(sctx, 0, ictx->cell.cell.bg); 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, ictx->cell.cell.bg); break; case INPUT_ESC_DECKPAM: screen_write_mode_set(sctx, MODE_KKEYPAD); break; case INPUT_ESC_DECKPNM: screen_write_mode_clear(sctx, MODE_KKEYPAD); break; case INPUT_ESC_DECSC: input_save_state(ictx); break; case INPUT_ESC_DECRC: input_restore_state(ictx); break; case INPUT_ESC_DECALN: screen_write_alignmenttest(sctx); break; case INPUT_ESC_SCSG0_ON: ictx->cell.g0set = 1; break; case INPUT_ESC_SCSG0_OFF: ictx->cell.g0set = 0; break; case INPUT_ESC_SCSG1_ON: ictx->cell.g1set = 1; break; case INPUT_ESC_SCSG1_OFF: ictx->cell.g1set = 0; break; case INPUT_ESC_ST: /* ST terminates OSC but the state transition already did it. */ break; } ictx->last = -1; return (0); } /* Execute control sequence. */ static int input_csi_dispatch(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; struct screen *s = sctx->s; struct input_table_entry *entry; int i, n, m; u_int cx, bg = ictx->cell.cell.bg; if (ictx->flags & INPUT_DISCARD) return (0); log_debug("%s: '%c' \"%s\" \"%s\"", __func__, ictx->ch, ictx->interm_buf, ictx->param_buf); if (input_split(ictx) != 0) return (0); 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. */ cx = s->cx; if (cx > screen_size_x(s) - 1) cx = screen_size_x(s) - 1; n = input_get(ictx, 0, 1, 1); if (n == -1) break; while (cx > 0 && n-- > 0) { do cx--; while (cx > 0 && !bit_test(s->tabs, cx)); } s->cx = cx; break; case INPUT_CSI_CUB: n = input_get(ictx, 0, 1, 1); if (n != -1) screen_write_cursorleft(sctx, n); break; case INPUT_CSI_CUD: n = input_get(ictx, 0, 1, 1); if (n != -1) screen_write_cursordown(sctx, n); break; case INPUT_CSI_CUF: n = input_get(ictx, 0, 1, 1); if (n != -1) screen_write_cursorright(sctx, n); break; case INPUT_CSI_CUP: n = input_get(ictx, 0, 1, 1); m = input_get(ictx, 1, 1, 1); if (n != -1 && m != -1) screen_write_cursormove(sctx, m - 1, n - 1, 1); break; case INPUT_CSI_WINOPS: input_csi_dispatch_winops(ictx); break; case INPUT_CSI_CUU: n = input_get(ictx, 0, 1, 1); if (n != -1) screen_write_cursorup(sctx, n); break; case INPUT_CSI_CNL: n = input_get(ictx, 0, 1, 1); if (n != -1) { screen_write_carriagereturn(sctx); screen_write_cursordown(sctx, n); } break; case INPUT_CSI_CPL: n = input_get(ictx, 0, 1, 1); if (n != -1) { screen_write_carriagereturn(sctx); screen_write_cursorup(sctx, n); } break; case INPUT_CSI_DA: switch (input_get(ictx, 0, 0, 0)) { case -1: break; case 0: input_reply(ictx, "\033[?1;2c"); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_DA_TWO: switch (input_get(ictx, 0, 0, 0)) { case -1: break; case 0: input_reply(ictx, "\033[>84;0;0c"); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_ECH: n = input_get(ictx, 0, 1, 1); if (n != -1) screen_write_clearcharacter(sctx, n, bg); break; case INPUT_CSI_DCH: n = input_get(ictx, 0, 1, 1); if (n != -1) screen_write_deletecharacter(sctx, n, bg); break; case INPUT_CSI_DECSTBM: n = input_get(ictx, 0, 1, 1); m = input_get(ictx, 1, 1, screen_size_y(s)); if (n != -1 && m != -1) screen_write_scrollregion(sctx, n - 1, m - 1); break; case INPUT_CSI_DL: n = input_get(ictx, 0, 1, 1); if (n != -1) screen_write_deleteline(sctx, n, bg); break; case INPUT_CSI_DSR: switch (input_get(ictx, 0, 0, 0)) { case -1: break; 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 -1: break; case 0: screen_write_clearendofscreen(sctx, bg); break; case 1: screen_write_clearstartofscreen(sctx, bg); break; case 2: screen_write_clearscreen(sctx, bg); break; case 3: if (input_get(ictx, 1, 0, 0) == 0) { /* * Linux console extension to clear history * (for example before locking the screen). */ screen_write_clearhistory(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 -1: break; case 0: screen_write_clearendofline(sctx, bg); break; case 1: screen_write_clearstartofline(sctx, bg); break; case 2: screen_write_clearline(sctx, bg); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_HPA: n = input_get(ictx, 0, 1, 1); if (n != -1) screen_write_cursormove(sctx, n - 1, -1, 1); break; case INPUT_CSI_ICH: n = input_get(ictx, 0, 1, 1); if (n != -1) screen_write_insertcharacter(sctx, n, bg); break; case INPUT_CSI_IL: n = input_get(ictx, 0, 1, 1); if (n != -1) screen_write_insertline(sctx, n, bg); break; case INPUT_CSI_REP: n = input_get(ictx, 0, 1, 1); if (n == -1) break; if (ictx->last == -1) break; ictx->ch = ictx->last; for (i = 0; i < n; i++) input_print(ictx); break; case INPUT_CSI_RCP: input_restore_state(ictx); break; case INPUT_CSI_RM: input_csi_dispatch_rm(ictx); break; case INPUT_CSI_RM_PRIVATE: input_csi_dispatch_rm_private(ictx); break; case INPUT_CSI_SCP: input_save_state(ictx); break; case INPUT_CSI_SGR: input_csi_dispatch_sgr(ictx); break; case INPUT_CSI_SM: input_csi_dispatch_sm(ictx); break; case INPUT_CSI_SM_PRIVATE: input_csi_dispatch_sm_private(ictx); break; case INPUT_CSI_SU: n = input_get(ictx, 0, 1, 1); if (n != -1) screen_write_scrollup(sctx, n, bg); break; case INPUT_CSI_SD: n = input_get(ictx, 0, 1, 1); if (n != -1) screen_write_scrolldown(sctx, n, bg); break; case INPUT_CSI_TBC: switch (input_get(ictx, 0, 0, 0)) { case -1: break; 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); if (n != -1) screen_write_cursormove(sctx, -1, n - 1, 1); break; case INPUT_CSI_DECSCUSR: n = input_get(ictx, 0, 0, 0); if (n != -1) screen_set_cursor_style(s, n); break; } ictx->last = -1; return (0); } /* Handle CSI RM. */ static void input_csi_dispatch_rm(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; u_int i; for (i = 0; i < ictx->param_list_len; i++) { switch (input_get(ictx, i, 0, -1)) { case -1: break; case 4: /* IRM */ screen_write_mode_clear(sctx, MODE_INSERT); break; case 34: screen_write_mode_set(sctx, MODE_BLINKING); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } } } /* Handle CSI private RM. */ static void input_csi_dispatch_rm_private(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; struct window_pane *wp = ictx->wp; u_int i; for (i = 0; i < ictx->param_list_len; i++) { switch (input_get(ictx, i, 0, -1)) { case -1: break; case 1: /* DECCKM */ screen_write_mode_clear(sctx, MODE_KCURSOR); break; case 3: /* DECCOLM */ screen_write_cursormove(sctx, 0, 0, 1); screen_write_clearscreen(sctx, ictx->cell.cell.bg); break; case 6: /* DECOM */ screen_write_mode_clear(sctx, MODE_ORIGIN); screen_write_cursormove(sctx, 0, 0, 1); break; case 7: /* DECAWM */ screen_write_mode_clear(sctx, MODE_WRAP); break; case 12: screen_write_mode_clear(sctx, MODE_BLINKING); break; case 25: /* TCEM */ screen_write_mode_clear(sctx, MODE_CURSOR); break; case 1000: case 1001: case 1002: case 1003: screen_write_mode_clear(sctx, ALL_MOUSE_MODES); break; case 1004: screen_write_mode_clear(sctx, MODE_FOCUSON); break; case 1005: screen_write_mode_clear(sctx, MODE_MOUSE_UTF8); break; case 1006: screen_write_mode_clear(sctx, MODE_MOUSE_SGR); break; case 47: case 1047: window_pane_alternate_off(wp, &ictx->cell.cell, 0); break; case 1049: window_pane_alternate_off(wp, &ictx->cell.cell, 1); break; case 2004: screen_write_mode_clear(sctx, MODE_BRACKETPASTE); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } } } /* Handle CSI SM. */ static void input_csi_dispatch_sm(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; u_int i; for (i = 0; i < ictx->param_list_len; i++) { switch (input_get(ictx, i, 0, -1)) { case -1: break; case 4: /* IRM */ screen_write_mode_set(sctx, MODE_INSERT); break; case 34: screen_write_mode_clear(sctx, MODE_BLINKING); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } } } /* Handle CSI private SM. */ static void input_csi_dispatch_sm_private(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; struct window_pane *wp = ictx->wp; u_int i; for (i = 0; i < ictx->param_list_len; i++) { switch (input_get(ictx, i, 0, -1)) { case -1: break; case 1: /* DECCKM */ screen_write_mode_set(sctx, MODE_KCURSOR); break; case 3: /* DECCOLM */ screen_write_cursormove(sctx, 0, 0, 1); screen_write_clearscreen(sctx, ictx->cell.cell.bg); break; case 6: /* DECOM */ screen_write_mode_set(sctx, MODE_ORIGIN); screen_write_cursormove(sctx, 0, 0, 1); break; case 7: /* DECAWM */ screen_write_mode_set(sctx, MODE_WRAP); break; case 12: screen_write_mode_set(sctx, MODE_BLINKING); break; case 25: /* TCEM */ screen_write_mode_set(sctx, MODE_CURSOR); break; case 1000: screen_write_mode_clear(sctx, ALL_MOUSE_MODES); screen_write_mode_set(sctx, MODE_MOUSE_STANDARD); break; case 1002: screen_write_mode_clear(sctx, ALL_MOUSE_MODES); screen_write_mode_set(sctx, MODE_MOUSE_BUTTON); break; case 1003: screen_write_mode_clear(sctx, ALL_MOUSE_MODES); screen_write_mode_set(sctx, MODE_MOUSE_ALL); break; case 1004: if (sctx->s->mode & MODE_FOCUSON) break; screen_write_mode_set(sctx, MODE_FOCUSON); wp->flags |= PANE_FOCUSPUSH; /* force update */ break; case 1005: screen_write_mode_set(sctx, MODE_MOUSE_UTF8); break; case 1006: screen_write_mode_set(sctx, MODE_MOUSE_SGR); break; case 47: case 1047: window_pane_alternate_on(wp, &ictx->cell.cell, 0); break; case 1049: window_pane_alternate_on(wp, &ictx->cell.cell, 1); break; case 2004: screen_write_mode_set(sctx, MODE_BRACKETPASTE); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } } } /* Handle CSI window operations. */ static void input_csi_dispatch_winops(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; struct window_pane *wp = ictx->wp; int n, m; m = 0; while ((n = input_get(ictx, m, 0, -1)) != -1) { switch (n) { case 1: case 2: case 5: case 6: case 7: case 11: case 13: case 14: case 19: case 20: case 21: case 24: break; case 3: case 4: case 8: m++; if (input_get(ictx, m, 0, -1) == -1) return; /* FALLTHROUGH */ case 9: case 10: m++; if (input_get(ictx, m, 0, -1) == -1) return; break; case 22: m++; switch (input_get(ictx, m, 0, -1)) { case -1: return; case 0: case 2: screen_push_title(sctx->s); break; } break; case 23: m++; switch (input_get(ictx, m, 0, -1)) { case -1: return; case 0: case 2: screen_pop_title(sctx->s); server_status_window(ictx->wp->window); break; } break; case 18: input_reply(ictx, "\033[8;%u;%ut", wp->sy, wp->sx); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } m++; } } /* Helper for 256 colour SGR. */ static int input_csi_dispatch_sgr_256_do(struct input_ctx *ictx, int fgbg, int c) { struct grid_cell *gc = &ictx->cell.cell; if (c == -1 || c > 255) { if (fgbg == 38) gc->fg = 8; else if (fgbg == 48) gc->bg = 8; } else { if (fgbg == 38) gc->fg = c | COLOUR_FLAG_256; else if (fgbg == 48) gc->bg = c | COLOUR_FLAG_256; else if (fgbg == 58) gc->us = c | COLOUR_FLAG_256; } return (1); } /* Handle CSI SGR for 256 colours. */ static void input_csi_dispatch_sgr_256(struct input_ctx *ictx, int fgbg, u_int *i) { int c; c = input_get(ictx, (*i) + 1, 0, -1); if (input_csi_dispatch_sgr_256_do(ictx, fgbg, c)) (*i)++; } /* Helper for RGB colour SGR. */ static int input_csi_dispatch_sgr_rgb_do(struct input_ctx *ictx, int fgbg, int r, int g, int b) { struct grid_cell *gc = &ictx->cell.cell; if (r == -1 || r > 255) return (0); if (g == -1 || g > 255) return (0); if (b == -1 || b > 255) return (0); if (fgbg == 38) gc->fg = colour_join_rgb(r, g, b); else if (fgbg == 48) gc->bg = colour_join_rgb(r, g, b); else if (fgbg == 58) gc->us = colour_join_rgb(r, g, b); return (1); } /* Handle CSI SGR for RGB colours. */ static void input_csi_dispatch_sgr_rgb(struct input_ctx *ictx, int fgbg, u_int *i) { int r, g, b; r = input_get(ictx, (*i) + 1, 0, -1); g = input_get(ictx, (*i) + 2, 0, -1); b = input_get(ictx, (*i) + 3, 0, -1); if (input_csi_dispatch_sgr_rgb_do(ictx, fgbg, r, g, b)) (*i) += 3; } /* Handle CSI SGR with a ISO parameter. */ static void input_csi_dispatch_sgr_colon(struct input_ctx *ictx, u_int i) { struct grid_cell *gc = &ictx->cell.cell; char *s = ictx->param_list[i].str, *copy, *ptr, *out; int p[8]; u_int n; const char *errstr; for (n = 0; n < nitems(p); n++) p[n] = -1; n = 0; ptr = copy = xstrdup(s); while ((out = strsep(&ptr, ":")) != NULL) { if (*out != '\0') { p[n++] = strtonum(out, 0, INT_MAX, &errstr); if (errstr != NULL || n == nitems(p)) { free(copy); return; } } else n++; log_debug("%s: %u = %d", __func__, n - 1, p[n - 1]); } free(copy); if (n == 0) return; if (p[0] == 4) { if (n != 2) return; switch (p[1]) { case 0: gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; break; case 1: gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; gc->attr |= GRID_ATTR_UNDERSCORE; break; case 2: gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; gc->attr |= GRID_ATTR_UNDERSCORE_2; break; case 3: gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; gc->attr |= GRID_ATTR_UNDERSCORE_3; break; case 4: gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; gc->attr |= GRID_ATTR_UNDERSCORE_4; break; case 5: gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; gc->attr |= GRID_ATTR_UNDERSCORE_5; break; } return; } if (n < 2 || (p[0] != 38 && p[0] != 48 && p[0] != 58)) return; switch (p[1]) { case 2: if (n < 3) break; if (n == 5) i = 2; else i = 3; if (n < i + 3) break; input_csi_dispatch_sgr_rgb_do(ictx, p[0], p[i], p[i + 1], p[i + 2]); break; case 5: if (n < 3) break; input_csi_dispatch_sgr_256_do(ictx, p[0], p[2]); break; } } /* Handle CSI SGR. */ static void input_csi_dispatch_sgr(struct input_ctx *ictx) { struct grid_cell *gc = &ictx->cell.cell; u_int i; int n; if (ictx->param_list_len == 0) { memcpy(gc, &grid_default_cell, sizeof *gc); return; } for (i = 0; i < ictx->param_list_len; i++) { if (ictx->param_list[i].type == INPUT_STRING) { input_csi_dispatch_sgr_colon(ictx, i); continue; } n = input_get(ictx, i, 0, 0); if (n == -1) continue; if (n == 38 || n == 48 || n == 58) { i++; switch (input_get(ictx, i, 0, -1)) { case 2: input_csi_dispatch_sgr_rgb(ictx, n, &i); break; case 5: input_csi_dispatch_sgr_256(ictx, n, &i); break; } continue; } switch (n) { case 0: memcpy(gc, &grid_default_cell, sizeof *gc); 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_ALL_UNDERSCORE; 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 9: gc->attr |= GRID_ATTR_STRIKETHROUGH; 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_ALL_UNDERSCORE; break; case 25: gc->attr &= ~GRID_ATTR_BLINK; break; case 27: gc->attr &= ~GRID_ATTR_REVERSE; break; case 28: gc->attr &= ~GRID_ATTR_HIDDEN; break; case 29: gc->attr &= ~GRID_ATTR_STRIKETHROUGH; break; case 30: case 31: case 32: case 33: case 34: case 35: case 36: case 37: gc->fg = n - 30; break; case 39: gc->fg = 8; break; case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47: gc->bg = n - 40; break; case 49: gc->bg = 8; break; case 53: gc->attr |= GRID_ATTR_OVERLINE; break; case 55: gc->attr &= ~GRID_ATTR_OVERLINE; break; case 59: gc->us = 0; break; case 90: case 91: case 92: case 93: case 94: case 95: case 96: case 97: gc->fg = n; break; case 100: case 101: case 102: case 103: case 104: case 105: case 106: case 107: gc->bg = n - 10; break; } } } /* End of input with BEL. */ static int input_end_bel(struct input_ctx *ictx) { log_debug("%s", __func__); ictx->input_end = INPUT_END_BEL; return (0); } /* DCS string started. */ static void input_enter_dcs(struct input_ctx *ictx) { log_debug("%s", __func__); input_clear(ictx); input_start_timer(ictx); ictx->last = -1; } /* DCS terminator (ST) received. */ static int input_dcs_dispatch(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; u_char *buf = ictx->input_buf; size_t len = ictx->input_len; const char prefix[] = "tmux;"; const u_int prefixlen = (sizeof prefix) - 1; if (ictx->flags & INPUT_DISCARD) return (0); log_debug("%s: \"%s\"", __func__, buf); if (len >= prefixlen && strncmp(buf, prefix, prefixlen) == 0) screen_write_rawstring(sctx, buf + prefixlen, len - prefixlen); return (0); } /* OSC string started. */ static void input_enter_osc(struct input_ctx *ictx) { log_debug("%s", __func__); input_clear(ictx); input_start_timer(ictx); ictx->last = -1; } /* OSC terminator (ST) received. */ static void input_exit_osc(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; u_char *p = ictx->input_buf; u_int option; if (ictx->flags & INPUT_DISCARD) return; if (ictx->input_len < 1 || *p < '0' || *p > '9') return; log_debug("%s: \"%s\" (end %s)", __func__, p, ictx->input_end == INPUT_END_ST ? "ST" : "BEL"); option = 0; while (*p >= '0' && *p <= '9') option = option * 10 + *p++ - '0'; if (*p == ';') p++; switch (option) { case 0: case 2: if (utf8_isvalid(p)) { screen_set_title(sctx->s, p); server_status_window(ictx->wp->window); } break; case 4: input_osc_4(ictx, p); break; case 10: input_osc_10(ictx, p); break; case 11: input_osc_11(ictx, p); break; case 12: if (utf8_isvalid(p) && *p != '?') /* ? is colour request */ screen_set_cursor_colour(sctx->s, p); break; case 52: input_osc_52(ictx, p); break; case 104: input_osc_104(ictx, p); break; case 112: if (*p == '\0') /* no arguments allowed */ screen_set_cursor_colour(sctx->s, ""); break; default: log_debug("%s: unknown '%u'", __func__, option); break; } } /* APC string started. */ static void input_enter_apc(struct input_ctx *ictx) { log_debug("%s", __func__); input_clear(ictx); input_start_timer(ictx); ictx->last = -1; } /* APC terminator (ST) received. */ static void input_exit_apc(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; if (ictx->flags & INPUT_DISCARD) return; log_debug("%s: \"%s\"", __func__, ictx->input_buf); if (!utf8_isvalid(ictx->input_buf)) return; screen_set_title(sctx->s, ictx->input_buf); server_status_window(ictx->wp->window); } /* Rename string started. */ static void input_enter_rename(struct input_ctx *ictx) { log_debug("%s", __func__); input_clear(ictx); input_start_timer(ictx); ictx->last = -1; } /* Rename terminator (ST) received. */ static void input_exit_rename(struct input_ctx *ictx) { if (ictx->flags & INPUT_DISCARD) return; if (!options_get_number(ictx->wp->options, "allow-rename")) return; log_debug("%s: \"%s\"", __func__, ictx->input_buf); if (!utf8_isvalid(ictx->input_buf)) return; window_set_name(ictx->wp->window, ictx->input_buf); options_set_number(ictx->wp->window->options, "automatic-rename", 0); server_status_window(ictx->wp->window); } /* Open UTF-8 character. */ static int input_top_bit_set(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; struct utf8_data *ud = &ictx->utf8data; ictx->last = -1; if (!ictx->utf8started) { if (utf8_open(ud, ictx->ch) != UTF8_MORE) return (0); ictx->utf8started = 1; return (0); } switch (utf8_append(ud, ictx->ch)) { case UTF8_MORE: return (0); case UTF8_ERROR: ictx->utf8started = 0; return (0); case UTF8_DONE: break; } ictx->utf8started = 0; log_debug("%s %hhu '%*s' (width %hhu)", __func__, ud->size, (int)ud->size, ud->data, ud->width); utf8_copy(&ictx->cell.cell.data, ud); screen_write_collect_add(sctx, &ictx->cell.cell); return (0); } /* Handle the OSC 4 sequence for setting (multiple) palette entries. */ static void input_osc_4(struct input_ctx *ictx, const char *p) { struct window_pane *wp = ictx->wp; char *copy, *s, *next = NULL; long idx; u_int r, g, b; copy = s = xstrdup(p); while (s != NULL && *s != '\0') { idx = strtol(s, &next, 10); if (*next++ != ';') goto bad; if (idx < 0 || idx >= 0x100) goto bad; s = strsep(&next, ";"); if (sscanf(s, "rgb:%2x/%2x/%2x", &r, &g, &b) != 3) { s = next; continue; } window_pane_set_palette(wp, idx, colour_join_rgb(r, g, b)); s = next; } free(copy); return; bad: log_debug("bad OSC 4: %s", p); free(copy); } /* Handle the OSC 10 sequence for setting foreground colour. */ static void input_osc_10(struct input_ctx *ictx, const char *p) { struct window_pane *wp = ictx->wp; u_int r, g, b; char tmp[16]; if (sscanf(p, "rgb:%2x/%2x/%2x", &r, &g, &b) != 3) goto bad; xsnprintf(tmp, sizeof tmp, "fg=#%02x%02x%02x", r, g, b); options_set_style(wp->options, "window-style", 1, tmp); options_set_style(wp->options, "window-active-style", 1, tmp); wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); return; bad: log_debug("bad OSC 10: %s", p); } /* Handle the OSC 11 sequence for setting background colour. */ static void input_osc_11(struct input_ctx *ictx, const char *p) { struct window_pane *wp = ictx->wp; u_int r, g, b; char tmp[16]; if (sscanf(p, "rgb:%2x/%2x/%2x", &r, &g, &b) != 3) goto bad; xsnprintf(tmp, sizeof tmp, "bg=#%02x%02x%02x", r, g, b); options_set_style(wp->options, "window-style", 1, tmp); options_set_style(wp->options, "window-active-style", 1, tmp); wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); return; bad: log_debug("bad OSC 11: %s", p); } /* Handle the OSC 52 sequence for setting the clipboard. */ static void input_osc_52(struct input_ctx *ictx, const char *p) { struct window_pane *wp = ictx->wp; char *end; const char *buf; size_t len; u_char *out; int outlen, state; struct screen_write_ctx ctx; struct paste_buffer *pb; state = options_get_number(global_options, "set-clipboard"); if (state != 2) return; if ((end = strchr(p, ';')) == NULL) return; end++; if (*end == '\0') return; log_debug("%s: %s", __func__, end); if (strcmp(end, "?") == 0) { if ((pb = paste_get_top(NULL)) != NULL) { buf = paste_buffer_data(pb, &len); outlen = 4 * ((len + 2) / 3) + 1; out = xmalloc(outlen); if ((outlen = b64_ntop(buf, len, out, outlen)) == -1) { free(out); return; } } else { outlen = 0; out = NULL; } bufferevent_write(wp->event, "\033]52;;", 6); if (outlen != 0) bufferevent_write(wp->event, out, outlen); if (ictx->input_end == INPUT_END_BEL) bufferevent_write(wp->event, "\007", 1); else bufferevent_write(wp->event, "\033\\", 2); free(out); return; } len = (strlen(end) / 4) * 3; if (len == 0) return; out = xmalloc(len); if ((outlen = b64_pton(end, out, len)) == -1) { free(out); return; } screen_write_start(&ctx, wp, NULL); screen_write_setselection(&ctx, out, outlen); screen_write_stop(&ctx); notify_pane("pane-set-clipboard", wp); paste_add(NULL, out, outlen); } /* Handle the OSC 104 sequence for unsetting (multiple) palette entries. */ static void input_osc_104(struct input_ctx *ictx, const char *p) { struct window_pane *wp = ictx->wp; char *copy, *s; long idx; if (*p == '\0') { window_pane_reset_palette(wp); return; } copy = s = xstrdup(p); while (*s != '\0') { idx = strtol(s, &s, 10); if (*s != '\0' && *s != ';') goto bad; if (idx < 0 || idx >= 0x100) goto bad; window_pane_unset_palette(wp, idx); if (*s == ';') s++; } free(copy); return; bad: log_debug("bad OSC 104: %s", p); free(copy); } tmux-3.0a/job.c100644 001750 001750 00000015741 13517540563 0007153/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include "tmux.h" /* * Job scheduling. Run queued commands in the background and record their * output. */ static void job_read_callback(struct bufferevent *, void *); static void job_write_callback(struct bufferevent *, void *); static void job_error_callback(struct bufferevent *, short, void *); /* A single job. */ struct job { enum { JOB_RUNNING, JOB_DEAD, JOB_CLOSED } state; int flags; char *cmd; pid_t pid; int status; int fd; struct bufferevent *event; job_update_cb updatecb; job_complete_cb completecb; job_free_cb freecb; void *data; LIST_ENTRY(job) entry; }; /* All jobs list. */ static LIST_HEAD(joblist, job) all_jobs = LIST_HEAD_INITIALIZER(all_jobs); /* Start a job running, if it isn't already. */ struct job * job_run(const char *cmd, struct session *s, const char *cwd, job_update_cb updatecb, job_complete_cb completecb, job_free_cb freecb, void *data, int flags) { struct job *job; struct environ *env; pid_t pid; int nullfd, out[2]; const char *home; sigset_t set, oldset; if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0) return (NULL); log_debug("%s: cmd=%s, cwd=%s", __func__, cmd, cwd == NULL ? "" : cwd); /* * Do not set TERM during .tmux.conf, it is nice to be able to use * if-shell to decide on default-terminal based on outside TERM. */ env = environ_for_session(s, !cfg_finished); sigfillset(&set); sigprocmask(SIG_BLOCK, &set, &oldset); switch (pid = fork()) { case -1: sigprocmask(SIG_SETMASK, &oldset, NULL); environ_free(env); close(out[0]); close(out[1]); return (NULL); case 0: proc_clear_signals(server_proc, 1); sigprocmask(SIG_SETMASK, &oldset, NULL); if (cwd == NULL || chdir(cwd) != 0) { if ((home = find_home()) == NULL || chdir(home) != 0) chdir("/"); } environ_push(env); environ_free(env); if (dup2(out[1], STDIN_FILENO) == -1) fatal("dup2 failed"); if (dup2(out[1], STDOUT_FILENO) == -1) fatal("dup2 failed"); if (out[1] != STDIN_FILENO && out[1] != STDOUT_FILENO) close(out[1]); close(out[0]); nullfd = open(_PATH_DEVNULL, O_RDWR, 0); if (nullfd == -1) fatal("open failed"); if (dup2(nullfd, STDERR_FILENO) == -1) fatal("dup2 failed"); if (nullfd != STDERR_FILENO) close(nullfd); closefrom(STDERR_FILENO + 1); execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL); fatal("execl failed"); } sigprocmask(SIG_SETMASK, &oldset, NULL); environ_free(env); close(out[1]); job = xmalloc(sizeof *job); job->state = JOB_RUNNING; job->flags = flags; job->cmd = xstrdup(cmd); job->pid = pid; job->status = 0; LIST_INSERT_HEAD(&all_jobs, job, entry); job->updatecb = updatecb; job->completecb = completecb; job->freecb = freecb; job->data = data; job->fd = out[0]; setblocking(job->fd, 0); job->event = bufferevent_new(job->fd, job_read_callback, job_write_callback, job_error_callback, job); if (job->event == NULL) fatalx("out of memory"); bufferevent_enable(job->event, EV_READ|EV_WRITE); 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, entry); free(job->cmd); if (job->freecb != NULL && job->data != NULL) job->freecb(job->data); if (job->pid != -1) kill(job->pid, SIGTERM); if (job->event != NULL) bufferevent_free(job->event); if (job->fd != -1) close(job->fd); free(job); } /* Job buffer read callback. */ static void job_read_callback(__unused struct bufferevent *bufev, void *data) { struct job *job = data; if (job->updatecb != NULL) job->updatecb(job); } /* * Job buffer write callback. Fired when the buffer falls below watermark * (default is empty). If all the data has been written, disable the write * event. */ static void job_write_callback(__unused struct bufferevent *bufev, void *data) { struct job *job = data; size_t len = EVBUFFER_LENGTH(EVBUFFER_OUTPUT(job->event)); log_debug("job write %p: %s, pid %ld, output left %zu", job, job->cmd, (long) job->pid, len); if (len == 0) { shutdown(job->fd, SHUT_WR); bufferevent_disable(job->event, EV_WRITE); } } /* Job buffer error callback. */ static void job_error_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->state == JOB_DEAD) { if (job->completecb != NULL) job->completecb(job); job_free(job); } else { bufferevent_disable(job->event, EV_READ); job->state = JOB_CLOSED; } } /* Job died (waitpid() returned its pid). */ void job_check_died(pid_t pid, int status) { struct job *job; LIST_FOREACH(job, &all_jobs, entry) { if (pid == job->pid) break; } if (job == NULL) return; log_debug("job died %p: %s, pid %ld", job, job->cmd, (long) job->pid); job->status = status; if (job->state == JOB_CLOSED) { if (job->completecb != NULL) job->completecb(job); job_free(job); } else { job->pid = -1; job->state = JOB_DEAD; } } /* Get job status. */ int job_get_status(struct job *job) { return (job->status); } /* Get job data. */ void * job_get_data(struct job *job) { return (job->data); } /* Get job event. */ struct bufferevent * job_get_event(struct job *job) { return (job->event); } /* Kill all jobs. */ void job_kill_all(void) { struct job *job; LIST_FOREACH(job, &all_jobs, entry) { if (job->pid != -1) kill(job->pid, SIGTERM); } } /* Are any jobs still running? */ int job_still_running(void) { struct job *job; LIST_FOREACH(job, &all_jobs, entry) { if ((~job->flags & JOB_NOWAIT) && job->state == JOB_RUNNING) return (1); } return (0); } /* Print job summary. */ void job_print_summary(struct cmdq_item *item, int blank) { struct job *job; u_int n = 0; LIST_FOREACH(job, &all_jobs, entry) { if (blank) { cmdq_print(item, "%s", ""); blank = 0; } cmdq_print(item, "Job %u: %s [fd=%d, pid=%ld, status=%d]", n, job->cmd, job->fd, (long)job->pid, job->status); n++; } } tmux-3.0a/key-bindings.c100644 001750 001750 00000050053 13570677043 0010762/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" #define DEFAULT_CLIENT_MENU \ " 'Detach' 'd' {detach-client}" \ " 'Detach & Kill' 'X' {detach-client -P}" \ " 'Detach Others' 'o' {detach-client -a}" \ " ''" \ " 'Lock' 'l' {lock-client}" #define DEFAULT_SESSION_MENU \ " 'Next' 'n' {switch-client -n}" \ " 'Previous' 'p' {switch-client -p}" \ " ''" \ " 'Renumber' 'N' {move-window -r}" \ " 'Rename' 'n' {command-prompt -I \"#S\" \"rename-session -- '%%'\"}" \ " ''" \ " 'New Session' 's' {new-session}" \ " 'New Window' 'w' {new-window}" #define DEFAULT_WINDOW_MENU \ " 'Swap Left' 'l' {swap-window -t:-1}" \ " 'Swap Right' 'r' {swap-window -t:+1}" \ " '#{?pane_marked_set,,-}Swap Marked' 's' {swap-window}" \ " ''" \ " 'Kill' 'X' {kill-window}" \ " 'Respawn' 'R' {respawn-window -k}" \ " '#{?pane_marked,Unmark,Mark}' 'm' {select-pane -m}" \ " 'Rename' 'n' {command-prompt -I \"#W\" \"rename-window -- '%%'\"}" \ " ''" \ " 'New After' 'w' {new-window -a}" \ " 'New At End' 'W' {new-window}" #define DEFAULT_PANE_MENU \ " '#{?mouse_word,Search For #[underscore]#{=/9/...:mouse_word},}' 'C-r' {copy-mode -t=; send -Xt= search-backward \"#{q:mouse_word}\"}" \ " '#{?mouse_word,Type #[underscore]#{=/9/...:mouse_word},}' 'C-y' {send-keys -l -- \"#{q:mouse_word}\"}" \ " '#{?mouse_word,Copy #[underscore]#{=/9/...:mouse_word},}' 'c' {set-buffer -- \"#{q:mouse_word}\"}" \ " '#{?mouse_line,Copy Line,}' 'l' {set-buffer -- \"#{q:mouse_line}\"}" \ " ''" \ " 'Horizontal Split' 'h' {split-window -h}" \ " 'Vertical Split' 'v' {split-window -v}" \ " ''" \ " 'Swap Up' 'u' {swap-pane -U}" \ " 'Swap Down' 'd' {swap-pane -D}" \ " '#{?pane_marked_set,,-}Swap Marked' 's' {swap-pane}" \ " ''" \ " 'Kill' 'X' {kill-pane}" \ " 'Respawn' 'R' {respawn-pane -k}" \ " '#{?pane_marked,Unmark,Mark}' 'm' {select-pane -m}" \ " '#{?window_zoomed_flag,Unzoom,Zoom}' 'z' {resize-pane -Z}" static int key_bindings_cmp(struct key_binding *, struct key_binding *); RB_GENERATE_STATIC(key_bindings, key_binding, entry, key_bindings_cmp); static int key_table_cmp(struct key_table *, struct key_table *); RB_GENERATE_STATIC(key_tables, key_table, entry, key_table_cmp); static struct key_tables key_tables = RB_INITIALIZER(&key_tables); static int key_table_cmp(struct key_table *table1, struct key_table *table2) { return (strcmp(table1->name, table2->name)); } static int key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2) { if (bd1->key < bd2->key) return (-1); if (bd1->key > bd2->key) return (1); return (0); } struct key_table * key_bindings_get_table(const char *name, int create) { struct key_table table_find, *table; table_find.name = name; table = RB_FIND(key_tables, &key_tables, &table_find); if (table != NULL || !create) return (table); table = xmalloc(sizeof *table); table->name = xstrdup(name); RB_INIT(&table->key_bindings); table->references = 1; /* one reference in key_tables */ RB_INSERT(key_tables, &key_tables, table); return (table); } struct key_table * key_bindings_first_table(void) { return (RB_MIN(key_tables, &key_tables)); } struct key_table * key_bindings_next_table(struct key_table *table) { return (RB_NEXT(key_tables, &key_tables, table)); } void key_bindings_unref_table(struct key_table *table) { struct key_binding *bd; struct key_binding *bd1; if (--table->references != 0) return; RB_FOREACH_SAFE(bd, key_bindings, &table->key_bindings, bd1) { RB_REMOVE(key_bindings, &table->key_bindings, bd); cmd_list_free(bd->cmdlist); free(bd); } free((void *)table->name); free(table); } struct key_binding * key_bindings_get(struct key_table *table, key_code key) { struct key_binding bd; bd.key = key; return (RB_FIND(key_bindings, &table->key_bindings, &bd)); } struct key_binding * key_bindings_first(struct key_table *table) { return (RB_MIN(key_bindings, &table->key_bindings)); } struct key_binding * key_bindings_next(__unused struct key_table *table, struct key_binding *bd) { return (RB_NEXT(key_bindings, &table->key_bindings, bd)); } void key_bindings_add(const char *name, key_code key, int repeat, struct cmd_list *cmdlist) { struct key_table *table; struct key_binding bd_find, *bd; table = key_bindings_get_table(name, 1); bd_find.key = (key & ~KEYC_XTERM); bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find); if (bd != NULL) { RB_REMOVE(key_bindings, &table->key_bindings, bd); cmd_list_free(bd->cmdlist); free(bd); } bd = xcalloc(1, sizeof *bd); bd->key = key; RB_INSERT(key_bindings, &table->key_bindings, bd); if (repeat) bd->flags |= KEY_BINDING_REPEAT; bd->cmdlist = cmdlist; } void key_bindings_remove(const char *name, key_code key) { struct key_table *table; struct key_binding bd_find, *bd; table = key_bindings_get_table(name, 0); if (table == NULL) return; bd_find.key = (key & ~KEYC_XTERM); bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find); if (bd == NULL) return; RB_REMOVE(key_bindings, &table->key_bindings, bd); cmd_list_free(bd->cmdlist); free(bd); if (RB_EMPTY(&table->key_bindings)) { RB_REMOVE(key_tables, &key_tables, table); key_bindings_unref_table(table); } } void key_bindings_remove_table(const char *name) { struct key_table *table; struct client *c; table = key_bindings_get_table(name, 0); if (table != NULL) { RB_REMOVE(key_tables, &key_tables, table); key_bindings_unref_table(table); } TAILQ_FOREACH(c, &clients, entry) { if (c->keytable == table) server_client_set_key_table(c, NULL); } } void key_bindings_init(void) { static const char *defaults[] = { "bind C-b send-prefix", "bind C-o rotate-window", "bind C-z suspend-client", "bind Space next-layout", "bind ! break-pane", "bind '\"' split-window", "bind '#' list-buffers", "bind '$' command-prompt -I'#S' \"rename-session -- '%%'\"", "bind % split-window -h", "bind & confirm-before -p\"kill-window #W? (y/n)\" kill-window", "bind \"'\" command-prompt -pindex \"select-window -t ':%%'\"", "bind ( switch-client -p", "bind ) switch-client -n", "bind , command-prompt -I'#W' \"rename-window -- '%%'\"", "bind - delete-buffer", "bind . command-prompt \"move-window -t '%%'\"", "bind 0 select-window -t:=0", "bind 1 select-window -t:=1", "bind 2 select-window -t:=2", "bind 3 select-window -t:=3", "bind 4 select-window -t:=4", "bind 5 select-window -t:=5", "bind 6 select-window -t:=6", "bind 7 select-window -t:=7", "bind 8 select-window -t:=8", "bind 9 select-window -t:=9", "bind : command-prompt", "bind \\; last-pane", "bind = choose-buffer -Z", "bind ? list-keys", "bind D choose-client -Z", "bind E select-layout -E", "bind L switch-client -l", "bind M select-pane -M", "bind [ copy-mode", "bind ] paste-buffer", "bind c new-window", "bind d detach-client", "bind f command-prompt \"find-window -Z -- '%%'\"", "bind i display-message", "bind l last-window", "bind m select-pane -m", "bind n next-window", "bind o select-pane -t:.+", "bind p previous-window", "bind q display-panes", "bind r refresh-client", "bind s choose-tree -Zs", "bind t clock-mode", "bind w choose-tree -Zw", "bind x confirm-before -p\"kill-pane #P? (y/n)\" kill-pane", "bind z resize-pane -Z", "bind '{' swap-pane -U", "bind '}' swap-pane -D", "bind '~' show-messages", "bind PPage copy-mode -u", "bind -r Up select-pane -U", "bind -r Down select-pane -D", "bind -r Left select-pane -L", "bind -r Right select-pane -R", "bind M-1 select-layout even-horizontal", "bind M-2 select-layout even-vertical", "bind M-3 select-layout main-horizontal", "bind M-4 select-layout main-vertical", "bind M-5 select-layout tiled", "bind M-n next-window -a", "bind M-o rotate-window -D", "bind M-p previous-window -a", "bind -r S-Up refresh-client -U 10", "bind -r S-Down refresh-client -D 10", "bind -r S-Left refresh-client -L 10", "bind -r S-Right refresh-client -R 10", "bind -r DC refresh-client -c", "bind -r M-Up resize-pane -U 5", "bind -r M-Down resize-pane -D 5", "bind -r M-Left resize-pane -L 5", "bind -r M-Right resize-pane -R 5", "bind -r C-Up resize-pane -U", "bind -r C-Down resize-pane -D", "bind -r C-Left resize-pane -L", "bind -r C-Right resize-pane -R", "bind -n MouseDown1Pane select-pane -t=\\; send-keys -M", "bind -n MouseDrag1Border resize-pane -M", "bind -n MouseDown1Status select-window -t=", "bind -n WheelDownStatus next-window", "bind -n WheelUpStatus previous-window", "bind -n MouseDrag1Pane if -Ft= '#{mouse_any_flag}' 'if -Ft= \"#{pane_in_mode}\" \"copy-mode -M\" \"send-keys -M\"' 'copy-mode -M'", "bind -n WheelUpPane if -Ft= '#{mouse_any_flag}' 'send-keys -M' 'if -Ft= \"#{pane_in_mode}\" \"send-keys -M\" \"copy-mode -et=\"'", "bind -n MouseDown3StatusRight display-menu -t= -xM -yS -T \"#[align=centre]#{client_name}\" " DEFAULT_CLIENT_MENU, "bind -n MouseDown3StatusLeft display-menu -t= -xM -yS -T \"#[align=centre]#{session_name}\" " DEFAULT_SESSION_MENU, "bind -n MouseDown3Status display-menu -t= -xW -yS -T \"#[align=centre]#{window_index}:#{window_name}\" " DEFAULT_WINDOW_MENU, "bind < display-menu -xW -yS -T \"#[align=centre]#{window_index}:#{window_name}\" " DEFAULT_WINDOW_MENU, "bind -n MouseDown3Pane if -Ft= '#{||:#{mouse_any_flag},#{pane_in_mode}}' 'select-pane -t=; send-keys -M' {display-menu -t= -xM -yM -T \"#[align=centre]#{pane_index} (#{pane_id})\" " DEFAULT_PANE_MENU "}", "bind -n M-MouseDown3Pane display-menu -t= -xM -yM -T \"#[align=centre]#{pane_index} (#{pane_id})\" " DEFAULT_PANE_MENU, "bind > display-menu -xP -yP -T \"#[align=centre]#{pane_index} (#{pane_id})\" " DEFAULT_PANE_MENU, "bind -Tcopy-mode C-Space send -X begin-selection", "bind -Tcopy-mode C-a send -X start-of-line", "bind -Tcopy-mode C-c send -X cancel", "bind -Tcopy-mode C-e send -X end-of-line", "bind -Tcopy-mode C-f send -X cursor-right", "bind -Tcopy-mode C-b send -X cursor-left", "bind -Tcopy-mode C-g send -X clear-selection", "bind -Tcopy-mode C-k send -X copy-end-of-line", "bind -Tcopy-mode C-n send -X cursor-down", "bind -Tcopy-mode C-p send -X cursor-up", "bind -Tcopy-mode C-r command-prompt -ip'(search up)' -I'#{pane_search_string}' 'send -X search-backward-incremental \"%%%\"'", "bind -Tcopy-mode C-s command-prompt -ip'(search down)' -I'#{pane_search_string}' 'send -X search-forward-incremental \"%%%\"'", "bind -Tcopy-mode C-v send -X page-down", "bind -Tcopy-mode C-w send -X copy-selection-and-cancel", "bind -Tcopy-mode Escape send -X cancel", "bind -Tcopy-mode Space send -X page-down", "bind -Tcopy-mode , send -X jump-reverse", "bind -Tcopy-mode \\; send -X jump-again", "bind -Tcopy-mode F command-prompt -1p'(jump backward)' 'send -X jump-backward \"%%%\"'", "bind -Tcopy-mode N send -X search-reverse", "bind -Tcopy-mode R send -X rectangle-toggle", "bind -Tcopy-mode T command-prompt -1p'(jump to backward)' 'send -X jump-to-backward \"%%%\"'", "bind -Tcopy-mode f command-prompt -1p'(jump forward)' 'send -X jump-forward \"%%%\"'", "bind -Tcopy-mode g command-prompt -p'(goto line)' 'send -X goto-line \"%%%\"'", "bind -Tcopy-mode n send -X search-again", "bind -Tcopy-mode q send -X cancel", "bind -Tcopy-mode t command-prompt -1p'(jump to forward)' 'send -X jump-to-forward \"%%%\"'", "bind -Tcopy-mode Home send -X start-of-line", "bind -Tcopy-mode End send -X end-of-line", "bind -Tcopy-mode MouseDown1Pane select-pane", "bind -Tcopy-mode MouseDrag1Pane select-pane\\; send -X begin-selection", "bind -Tcopy-mode MouseDragEnd1Pane send -X copy-selection-and-cancel", "bind -Tcopy-mode WheelUpPane select-pane\\; send -N5 -X scroll-up", "bind -Tcopy-mode WheelDownPane select-pane\\; send -N5 -X scroll-down", "bind -Tcopy-mode DoubleClick1Pane select-pane\\; send -X select-word", "bind -Tcopy-mode TripleClick1Pane select-pane\\; send -X select-line", "bind -Tcopy-mode NPage send -X page-down", "bind -Tcopy-mode PPage send -X page-up", "bind -Tcopy-mode Up send -X cursor-up", "bind -Tcopy-mode Down send -X cursor-down", "bind -Tcopy-mode Left send -X cursor-left", "bind -Tcopy-mode Right send -X cursor-right", "bind -Tcopy-mode M-1 command-prompt -Np'(repeat)' -I1 'send -N \"%%%\"'", "bind -Tcopy-mode M-2 command-prompt -Np'(repeat)' -I2 'send -N \"%%%\"'", "bind -Tcopy-mode M-3 command-prompt -Np'(repeat)' -I3 'send -N \"%%%\"'", "bind -Tcopy-mode M-4 command-prompt -Np'(repeat)' -I4 'send -N \"%%%\"'", "bind -Tcopy-mode M-5 command-prompt -Np'(repeat)' -I5 'send -N \"%%%\"'", "bind -Tcopy-mode M-6 command-prompt -Np'(repeat)' -I6 'send -N \"%%%\"'", "bind -Tcopy-mode M-7 command-prompt -Np'(repeat)' -I7 'send -N \"%%%\"'", "bind -Tcopy-mode M-8 command-prompt -Np'(repeat)' -I8 'send -N \"%%%\"'", "bind -Tcopy-mode M-9 command-prompt -Np'(repeat)' -I9 'send -N \"%%%\"'", "bind -Tcopy-mode M-< send -X history-top", "bind -Tcopy-mode M-> send -X history-bottom", "bind -Tcopy-mode M-R send -X top-line", "bind -Tcopy-mode M-b send -X previous-word", "bind -Tcopy-mode C-M-b send -X previous-matching-bracket", "bind -Tcopy-mode M-f send -X next-word-end", "bind -Tcopy-mode C-M-f send -X next-matching-bracket", "bind -Tcopy-mode M-m send -X back-to-indentation", "bind -Tcopy-mode M-r send -X middle-line", "bind -Tcopy-mode M-v send -X page-up", "bind -Tcopy-mode M-w send -X copy-selection-and-cancel", "bind -Tcopy-mode 'M-{' send -X previous-paragraph", "bind -Tcopy-mode 'M-}' send -X next-paragraph", "bind -Tcopy-mode M-Up send -X halfpage-up", "bind -Tcopy-mode M-Down send -X halfpage-down", "bind -Tcopy-mode C-Up send -X scroll-up", "bind -Tcopy-mode C-Down send -X scroll-down", "bind -Tcopy-mode-vi C-c send -X cancel", "bind -Tcopy-mode-vi C-d send -X halfpage-down", "bind -Tcopy-mode-vi C-e send -X scroll-down", "bind -Tcopy-mode-vi C-b send -X page-up", "bind -Tcopy-mode-vi C-f send -X page-down", "bind -Tcopy-mode-vi C-h send -X cursor-left", "bind -Tcopy-mode-vi C-j send -X copy-selection-and-cancel", "bind -Tcopy-mode-vi Enter send -X copy-selection-and-cancel", "bind -Tcopy-mode-vi C-u send -X halfpage-up", "bind -Tcopy-mode-vi C-v send -X rectangle-toggle", "bind -Tcopy-mode-vi C-y send -X scroll-up", "bind -Tcopy-mode-vi Escape send -X clear-selection", "bind -Tcopy-mode-vi Space send -X begin-selection", "bind -Tcopy-mode-vi '$' send -X end-of-line", "bind -Tcopy-mode-vi , send -X jump-reverse", "bind -Tcopy-mode-vi / command-prompt -p'(search down)' 'send -X search-forward \"%%%\"'", "bind -Tcopy-mode-vi 0 send -X start-of-line", "bind -Tcopy-mode-vi 1 command-prompt -Np'(repeat)' -I1 'send -N \"%%%\"'", "bind -Tcopy-mode-vi 2 command-prompt -Np'(repeat)' -I2 'send -N \"%%%\"'", "bind -Tcopy-mode-vi 3 command-prompt -Np'(repeat)' -I3 'send -N \"%%%\"'", "bind -Tcopy-mode-vi 4 command-prompt -Np'(repeat)' -I4 'send -N \"%%%\"'", "bind -Tcopy-mode-vi 5 command-prompt -Np'(repeat)' -I5 'send -N \"%%%\"'", "bind -Tcopy-mode-vi 6 command-prompt -Np'(repeat)' -I6 'send -N \"%%%\"'", "bind -Tcopy-mode-vi 7 command-prompt -Np'(repeat)' -I7 'send -N \"%%%\"'", "bind -Tcopy-mode-vi 8 command-prompt -Np'(repeat)' -I8 'send -N \"%%%\"'", "bind -Tcopy-mode-vi 9 command-prompt -Np'(repeat)' -I9 'send -N \"%%%\"'", "bind -Tcopy-mode-vi : command-prompt -p'(goto line)' 'send -X goto-line \"%%%\"'", "bind -Tcopy-mode-vi \\; send -X jump-again", "bind -Tcopy-mode-vi ? command-prompt -p'(search up)' 'send -X search-backward \"%%%\"'", "bind -Tcopy-mode-vi A send -X append-selection-and-cancel", "bind -Tcopy-mode-vi B send -X previous-space", "bind -Tcopy-mode-vi D send -X copy-end-of-line", "bind -Tcopy-mode-vi E send -X next-space-end", "bind -Tcopy-mode-vi F command-prompt -1p'(jump backward)' 'send -X jump-backward \"%%%\"'", "bind -Tcopy-mode-vi G send -X history-bottom", "bind -Tcopy-mode-vi H send -X top-line", "bind -Tcopy-mode-vi J send -X scroll-down", "bind -Tcopy-mode-vi K send -X scroll-up", "bind -Tcopy-mode-vi L send -X bottom-line", "bind -Tcopy-mode-vi M send -X middle-line", "bind -Tcopy-mode-vi N send -X search-reverse", "bind -Tcopy-mode-vi T command-prompt -1p'(jump to backward)' 'send -X jump-to-backward \"%%%\"'", "bind -Tcopy-mode-vi V send -X select-line", "bind -Tcopy-mode-vi W send -X next-space", "bind -Tcopy-mode-vi ^ send -X back-to-indentation", "bind -Tcopy-mode-vi b send -X previous-word", "bind -Tcopy-mode-vi e send -X next-word-end", "bind -Tcopy-mode-vi f command-prompt -1p'(jump forward)' 'send -X jump-forward \"%%%\"'", "bind -Tcopy-mode-vi g send -X history-top", "bind -Tcopy-mode-vi h send -X cursor-left", "bind -Tcopy-mode-vi j send -X cursor-down", "bind -Tcopy-mode-vi k send -X cursor-up", "bind -Tcopy-mode-vi l send -X cursor-right", "bind -Tcopy-mode-vi n send -X search-again", "bind -Tcopy-mode-vi o send -X other-end", "bind -Tcopy-mode-vi q send -X cancel", "bind -Tcopy-mode-vi t command-prompt -1p'(jump to forward)' 'send -X jump-to-forward \"%%%\"'", "bind -Tcopy-mode-vi v send -X rectangle-toggle", "bind -Tcopy-mode-vi w send -X next-word", "bind -Tcopy-mode-vi '{' send -X previous-paragraph", "bind -Tcopy-mode-vi '}' send -X next-paragraph", "bind -Tcopy-mode-vi % send -X next-matching-bracket", "bind -Tcopy-mode-vi MouseDown1Pane select-pane", "bind -Tcopy-mode-vi MouseDrag1Pane select-pane\\; send -X begin-selection", "bind -Tcopy-mode-vi MouseDragEnd1Pane send -X copy-selection-and-cancel", "bind -Tcopy-mode-vi WheelUpPane select-pane\\; send -N5 -X scroll-up", "bind -Tcopy-mode-vi WheelDownPane select-pane\\; send -N5 -X scroll-down", "bind -Tcopy-mode-vi DoubleClick1Pane select-pane\\; send -X select-word", "bind -Tcopy-mode-vi TripleClick1Pane select-pane\\; send -X select-line", "bind -Tcopy-mode-vi BSpace send -X cursor-left", "bind -Tcopy-mode-vi NPage send -X page-down", "bind -Tcopy-mode-vi PPage send -X page-up", "bind -Tcopy-mode-vi Up send -X cursor-up", "bind -Tcopy-mode-vi Down send -X cursor-down", "bind -Tcopy-mode-vi Left send -X cursor-left", "bind -Tcopy-mode-vi Right send -X cursor-right", "bind -Tcopy-mode-vi C-Up send -X scroll-up", "bind -Tcopy-mode-vi C-Down send -X scroll-down", }; u_int i; struct cmd_parse_result *pr; for (i = 0; i < nitems(defaults); i++) { pr = cmd_parse_from_string(defaults[i], NULL); if (pr->status != CMD_PARSE_SUCCESS) fatalx("bad default key: %s", defaults[i]); cmdq_append(NULL, cmdq_get_command(pr->cmdlist, NULL, NULL, 0)); cmd_list_free(pr->cmdlist); } } static enum cmd_retval key_bindings_read_only(struct cmdq_item *item, __unused void *data) { cmdq_error(item, "client is read-only"); return (CMD_RETURN_ERROR); } struct cmdq_item * key_bindings_dispatch(struct key_binding *bd, struct cmdq_item *item, struct client *c, struct mouse_event *m, struct cmd_find_state *fs) { struct cmd *cmd; struct cmdq_item *new_item; int readonly; readonly = 1; TAILQ_FOREACH(cmd, &bd->cmdlist->list, qentry) { if (!(cmd->entry->flags & CMD_READONLY)) readonly = 0; } if (!readonly && (c->flags & CLIENT_READONLY)) new_item = cmdq_get_callback(key_bindings_read_only, NULL); else { new_item = cmdq_get_command(bd->cmdlist, fs, m, 0); if (bd->flags & KEY_BINDING_REPEAT) new_item->shared->flags |= CMDQ_SHARED_REPEAT; } if (item != NULL) cmdq_insert_after(item, new_item); else cmdq_append(c, new_item); return (new_item); } tmux-3.0a/key-string.c100644 001750 001750 00000021612 13570677043 0010472/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" static key_code key_string_search_table(const char *); static key_code key_string_get_modifiers(const char **); static const struct { const char *string; key_code 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 }, { "IC", KEYC_IC }, { "Insert", KEYC_IC }, { "DC", KEYC_DC }, { "Delete", KEYC_DC }, { "Home", KEYC_HOME }, { "End", KEYC_END }, { "NPage", KEYC_NPAGE }, { "PageDown", KEYC_NPAGE }, { "PgDn", KEYC_NPAGE }, { "PPage", KEYC_PPAGE }, { "PageUp", KEYC_PPAGE }, { "PgUp", 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 }, /* Mouse keys. */ KEYC_MOUSE_STRING(MOUSEDOWN1, MouseDown1), KEYC_MOUSE_STRING(MOUSEDOWN2, MouseDown2), KEYC_MOUSE_STRING(MOUSEDOWN3, MouseDown3), KEYC_MOUSE_STRING(MOUSEUP1, MouseUp1), KEYC_MOUSE_STRING(MOUSEUP2, MouseUp2), KEYC_MOUSE_STRING(MOUSEUP3, MouseUp3), KEYC_MOUSE_STRING(MOUSEDRAG1, MouseDrag1), KEYC_MOUSE_STRING(MOUSEDRAG2, MouseDrag2), KEYC_MOUSE_STRING(MOUSEDRAG3, MouseDrag3), KEYC_MOUSE_STRING(MOUSEDRAGEND1, MouseDragEnd1), KEYC_MOUSE_STRING(MOUSEDRAGEND2, MouseDragEnd2), KEYC_MOUSE_STRING(MOUSEDRAGEND3, MouseDragEnd3), KEYC_MOUSE_STRING(WHEELUP, WheelUp), KEYC_MOUSE_STRING(WHEELDOWN, WheelDown), KEYC_MOUSE_STRING(DOUBLECLICK1, DoubleClick1), KEYC_MOUSE_STRING(DOUBLECLICK2, DoubleClick2), KEYC_MOUSE_STRING(DOUBLECLICK3, DoubleClick3), KEYC_MOUSE_STRING(TRIPLECLICK1, TripleClick1), KEYC_MOUSE_STRING(TRIPLECLICK2, TripleClick2), KEYC_MOUSE_STRING(TRIPLECLICK3, TripleClick3), }; /* Find key string in table. */ static key_code key_string_search_table(const char *string) { u_int i, user; for (i = 0; i < nitems(key_string_table); i++) { if (strcasecmp(string, key_string_table[i].string) == 0) return (key_string_table[i].key); } if (sscanf(string, "User%u", &user) == 1 && user < KEYC_NUSER) return (KEYC_USER + user); return (KEYC_UNKNOWN); } /* Find modifiers. */ static key_code key_string_get_modifiers(const char **string) { key_code 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; default: *string = NULL; return (0); } *string += 2; } return (modifiers); } /* Lookup a string and convert to a key value. */ key_code key_string_lookup_string(const char *string) { static const char *other = "!#()+,-.0123456789:;<=>?'\r\t"; key_code key; u_int u; key_code modifiers; struct utf8_data ud; u_int i; enum utf8_state more; wchar_t wc; /* Is this no key or any key? */ if (strcasecmp(string, "None") == 0) return (KEYC_NONE); if (strcasecmp(string, "Any") == 0) return (KEYC_ANY); /* Is this a hexadecimal value? */ if (string[0] == '0' && string[1] == 'x') { if (sscanf(string + 2, "%x", &u) != 1) return (KEYC_UNKNOWN); if (u > 0x1fffff) return (KEYC_UNKNOWN); return (u); } /* Check for modifiers. */ modifiers = 0; if (string[0] == '^' && string[1] != '\0') { modifiers |= KEYC_CTRL; string++; } modifiers |= key_string_get_modifiers(&string); if (string == NULL || string[0] == '\0') return (KEYC_UNKNOWN); /* Is this a standard ASCII key? */ if (string[1] == '\0' && (u_char)string[0] <= 127) { key = (u_char)string[0]; if (key < 32 || key == 127) return (KEYC_UNKNOWN); } else { /* Try as a UTF-8 key. */ if ((more = utf8_open(&ud, (u_char)*string)) == UTF8_MORE) { if (strlen(string) != ud.size) return (KEYC_UNKNOWN); for (i = 1; i < ud.size; i++) more = utf8_append(&ud, (u_char)string[i]); if (more != UTF8_DONE) return (KEYC_UNKNOWN); if (utf8_combine(&ud, &wc) != UTF8_DONE) return (KEYC_UNKNOWN); return (wc | modifiers); } /* Otherwise look the key up in the table. */ key = key_string_search_table(string); if (key == KEYC_UNKNOWN) return (KEYC_UNKNOWN); } /* Convert the standard control keys. */ if (key < KEYC_BASE && (modifiers & KEYC_CTRL) && !strchr(other, key)) { 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_UNKNOWN); modifiers &= ~KEYC_CTRL; } return (key | modifiers); } /* Convert a key code into string format, with prefix if necessary. */ const char * key_string_lookup_key(key_code key) { static char out[32]; char tmp[8]; u_int i; struct utf8_data ud; size_t off; *out = '\0'; /* Handle no key. */ if (key == KEYC_NONE) return ("None"); /* Handle special keys. */ if (key == KEYC_UNKNOWN) return ("Unknown"); if (key == KEYC_ANY) return ("Any"); if (key == KEYC_FOCUS_IN) return ("FocusIn"); if (key == KEYC_FOCUS_OUT) return ("FocusOut"); if (key == KEYC_PASTE_START) return ("PasteStart"); if (key == KEYC_PASTE_END) return ("PasteEnd"); if (key == KEYC_MOUSE) return ("Mouse"); if (key == KEYC_DRAGGING) return ("Dragging"); if (key == KEYC_MOUSEMOVE_PANE) return ("MouseMovePane"); if (key == KEYC_MOUSEMOVE_STATUS) return ("MouseMoveStatus"); if (key == KEYC_MOUSEMOVE_STATUS_LEFT) return ("MouseMoveStatusLeft"); if (key == KEYC_MOUSEMOVE_STATUS_RIGHT) return ("MouseMoveStatusRight"); if (key == KEYC_MOUSEMOVE_BORDER) return ("MouseMoveBorder"); if (key >= KEYC_USER && key < KEYC_USER + KEYC_NUSER) { snprintf(out, sizeof out, "User%u", (u_int)(key - KEYC_USER)); return (out); } /* Literal keys are themselves. */ if (key & KEYC_LITERAL) { snprintf(out, sizeof out, "%c", (int)(key & 0xff)); return (out); } /* * 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); } /* Is this a UTF-8 key? */ if (key > 127 && key < KEYC_BASE) { if (utf8_split(key, &ud) == UTF8_DONE) { off = strlen(out); memcpy(out + off, ud.data, ud.size); out[off + ud.size] = '\0'; return (out); } } /* Invalid keys are errors. */ if (key == 127 || key > 255) { snprintf(out, sizeof out, "Invalid#%llx", key); return (out); } /* Check for standard or control key. */ if (key <= 32) { if (key == 0 || key > 26) xsnprintf(tmp, sizeof tmp, "C-%c", (int)(64 + key)); else xsnprintf(tmp, sizeof tmp, "C-%c", (int)(96 + key)); } else if (key >= 32 && key <= 126) { tmp[0] = key; tmp[1] = '\0'; } else if (key >= 128) xsnprintf(tmp, sizeof tmp, "\\%llo", key); strlcat(out, tmp, sizeof out); return (out); } tmux-3.0a/layout-custom.c100644 001750 001750 00000017552 13570677043 0011233/* $OpenBSD$ */ /* * 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" static struct layout_cell *layout_find_bottomright(struct layout_cell *); static u_short layout_checksum(const char *); static int layout_append(struct layout_cell *, char *, size_t); static struct layout_cell *layout_construct(struct layout_cell *, const char **); static void layout_assign(struct window_pane **, struct layout_cell *); /* Find the bottom-right cell. */ static 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. */ static 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 layout_cell *root) { char layout[8192], *out; *layout = '\0'; if (layout_append(root, layout, sizeof layout) != 0) return (NULL); xasprintf(&out, "%04hx,%s", layout_checksum(layout), layout); return (out); } /* Append information for a single cell. */ static 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); if (lc->wp != NULL) { tmplen = xsnprintf(tmp, sizeof tmp, "%ux%u,%u,%u,%u", lc->sx, lc->sy, lc->xoff, lc->yoff, lc->wp->id); } else { 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); } /* Check layout sizes fit. */ static int layout_check(struct layout_cell *lc) { struct layout_cell *lcchild; u_int n = 0; switch (lc->type) { case LAYOUT_WINDOWPANE: break; case LAYOUT_LEFTRIGHT: TAILQ_FOREACH(lcchild, &lc->cells, entry) { if (lcchild->sy != lc->sy) return (0); if (!layout_check(lcchild)) return (0); n += lcchild->sx + 1; } if (n - 1 != lc->sx) return (0); break; case LAYOUT_TOPBOTTOM: TAILQ_FOREACH(lcchild, &lc->cells, entry) { if (lcchild->sx != lc->sx) return (0); if (!layout_check(lcchild)) return (0); n += lcchild->sy + 1; } if (n - 1 != lc->sy) return (0); break; } return (1); } /* 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 = 0, sy = 0; 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(w, lcchild, &lc); } /* * It appears older versions of tmux were able to generate layouts with * an incorrect top cell size - if it is larger than the top child then * correct that (if this is still wrong the check code will catch it). */ switch (lc->type) { case LAYOUT_WINDOWPANE: break; case LAYOUT_LEFTRIGHT: TAILQ_FOREACH(lcchild, &lc->cells, entry) { sy = lcchild->sy + 1; sx += lcchild->sx + 1; } break; case LAYOUT_TOPBOTTOM: TAILQ_FOREACH(lcchild, &lc->cells, entry) { sx = lcchild->sx + 1; sy += lcchild->sy + 1; } break; } if (lc->type != LAYOUT_WINDOWPANE && (lc->sx != sx || lc->sy != sy)) { log_debug("fix layout %u,%u to %u,%u", lc->sx, lc->sy, sx,sy); layout_print_cell(lc, __func__, 0); lc->sx = sx - 1; lc->sy = sy - 1; } /* Check the new layout. */ if (!layout_check(lc)) return (-1); /* Resize to the layout size. */ 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(w); layout_fix_panes(w); recalculate_sizes(); layout_print_cell(lc, __func__, 0); notify_window("window-layout-changed", w); return (0); fail: layout_free_cell(lc); return (-1); } /* Assign panes into cells. */ static 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. */ static struct layout_cell * layout_construct(struct layout_cell *lcparent, const char **layout) { struct layout_cell *lc, *lcchild; u_int sx, sy, xoff, yoff; const char *saved; 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)++; if (**layout == ',') { saved = *layout; (*layout)++; while (isdigit((u_char) **layout)) (*layout)++; if (**layout == 'x') *layout = saved; } 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); } tmux-3.0a/layout-set.c100644 001750 001750 00000026612 13570677043 0010511/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Set window layouts - predefined methods to arrange windows. These are * one-off and generate a layout tree. */ static void layout_set_even_h(struct window *); static void layout_set_even_v(struct window *); static void layout_set_main_h(struct window *); static void layout_set_main_v(struct window *); static void layout_set_tiled(struct window *); static 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 }, }; 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); } static void layout_set_even(struct window *w, enum layout_type type) { struct window_pane *wp; struct layout_cell *lc, *lcnew; u_int n, sx, sy; layout_print_cell(w->layout_root, __func__, 1); /* Get number of panes. */ n = window_count_panes(w); if (n <= 1) return; /* Free the old root and construct a new. */ layout_free(w); lc = w->layout_root = layout_create_cell(NULL); if (type == LAYOUT_LEFTRIGHT) { sx = (n * (PANE_MINIMUM + 1)) - 1; if (sx < w->sx) sx = w->sx; sy = w->sy; } else { sy = (n * (PANE_MINIMUM + 1)) - 1; if (sy < w->sy) sy = w->sy; sx = w->sx; } layout_set_size(lc, sx, sy, 0, 0); layout_make_node(lc, type); /* Build new leaf cells. */ TAILQ_FOREACH(wp, &w->panes, entry) { lcnew = layout_create_cell(lc); layout_make_leaf(lcnew, wp); lcnew->sx = w->sx; lcnew->sy = w->sy; TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry); } /* Spread out cells. */ layout_spread_cell(w, lc); /* Fix cell offsets. */ layout_fix_offsets(w); layout_fix_panes(w); layout_print_cell(w->layout_root, __func__, 1); window_resize(w, lc->sx, lc->sy); notify_window("window-layout-changed", w); server_redraw_window(w); } static void layout_set_even_h(struct window *w) { layout_set_even(w, LAYOUT_LEFTRIGHT); } static void layout_set_even_v(struct window *w) { layout_set_even(w, LAYOUT_TOPBOTTOM); } static void layout_set_main_h(struct window *w) { struct window_pane *wp; struct layout_cell *lc, *lcmain, *lcother, *lcchild; u_int n, mainh, otherh, sx, sy; 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 */ /* Find available height - take off one line for the border. */ sy = w->sy - 1; /* Get the main pane height and work out the other pane height. */ mainh = options_get_number(w->options, "main-pane-height"); if (mainh + PANE_MINIMUM >= sy) { if (sy <= PANE_MINIMUM + PANE_MINIMUM) mainh = PANE_MINIMUM; else mainh = sy - PANE_MINIMUM; otherh = PANE_MINIMUM; } else { otherh = options_get_number(w->options, "other-pane-height"); if (otherh == 0) otherh = sy - mainh; else if (otherh > sy || sy - otherh < mainh) otherh = sy - mainh; else mainh = sy - otherh; } /* Work out what width is needed. */ sx = (n * (PANE_MINIMUM + 1)) - 1; if (sx < w->sx) sx = w->sx; /* Free old tree and create a new root. */ layout_free(w); lc = w->layout_root = layout_create_cell(NULL); layout_set_size(lc, sx, mainh + otherh + 1, 0, 0); layout_make_node(lc, LAYOUT_TOPBOTTOM); /* Create the main pane. */ lcmain = layout_create_cell(lc); layout_set_size(lcmain, sx, mainh, 0, 0); layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes)); TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry); /* Create the other pane. */ lcother = layout_create_cell(lc); layout_set_size(lcother, sx, otherh, 0, 0); if (n == 1) { wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry); layout_make_leaf(lcother, wp); TAILQ_INSERT_TAIL(&lc->cells, lcother, entry); } else { layout_make_node(lcother, LAYOUT_LEFTRIGHT); TAILQ_INSERT_TAIL(&lc->cells, lcother, entry); /* Add the remaining panes as children. */ TAILQ_FOREACH(wp, &w->panes, entry) { if (wp == TAILQ_FIRST(&w->panes)) continue; lcchild = layout_create_cell(lcother); layout_set_size(lcchild, PANE_MINIMUM, otherh, 0, 0); layout_make_leaf(lcchild, wp); TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry); } layout_spread_cell(w, lcother); } /* Fix cell offsets. */ layout_fix_offsets(w); layout_fix_panes(w); layout_print_cell(w->layout_root, __func__, 1); window_resize(w, lc->sx, lc->sy); notify_window("window-layout-changed", w); server_redraw_window(w); } static void layout_set_main_v(struct window *w) { struct window_pane *wp; struct layout_cell *lc, *lcmain, *lcother, *lcchild; u_int n, mainw, otherw, sx, sy; 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 */ /* Find available width - take off one line for the border. */ sx = w->sx - 1; /* Get the main pane width and work out the other pane width. */ mainw = options_get_number(w->options, "main-pane-width"); if (mainw + PANE_MINIMUM >= sx) { if (sx <= PANE_MINIMUM + PANE_MINIMUM) mainw = PANE_MINIMUM; else mainw = sx - PANE_MINIMUM; otherw = PANE_MINIMUM; } else { otherw = options_get_number(w->options, "other-pane-width"); if (otherw == 0) otherw = sx - mainw; else if (otherw > sx || sx - otherw < mainw) otherw = sx - mainw; else mainw = sx - otherw; } /* Work out what height is needed. */ sy = (n * (PANE_MINIMUM + 1)) - 1; if (sy < w->sy) sy = w->sy; /* Free old tree and create a new root. */ layout_free(w); lc = w->layout_root = layout_create_cell(NULL); layout_set_size(lc, mainw + otherw + 1, sy, 0, 0); layout_make_node(lc, LAYOUT_LEFTRIGHT); /* Create the main pane. */ lcmain = layout_create_cell(lc); layout_set_size(lcmain, mainw, sy, 0, 0); layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes)); TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry); /* Create the other pane. */ lcother = layout_create_cell(lc); layout_set_size(lcother, otherw, sy, 0, 0); if (n == 1) { wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry); layout_make_leaf(lcother, wp); TAILQ_INSERT_TAIL(&lc->cells, lcother, entry); } else { layout_make_node(lcother, LAYOUT_TOPBOTTOM); TAILQ_INSERT_TAIL(&lc->cells, lcother, entry); /* Add the remaining panes as children. */ TAILQ_FOREACH(wp, &w->panes, entry) { if (wp == TAILQ_FIRST(&w->panes)) continue; lcchild = layout_create_cell(lcother); layout_set_size(lcchild, otherw, PANE_MINIMUM, 0, 0); layout_make_leaf(lcchild, wp); TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry); } layout_spread_cell(w, lcother); } /* Fix cell offsets. */ layout_fix_offsets(w); layout_fix_panes(w); layout_print_cell(w->layout_root, __func__, 1); window_resize(w, lc->sx, lc->sy); notify_window("window-layout-changed", w); 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, sx, sy; 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); sx = ((width + 1) * columns) - 1; if (sx < w->sx) sx = w->sx; sy = ((height + 1) * rows) - 1; if (sy < w->sy) sy = w->sy; layout_set_size(lc, sx, sy, 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(w, 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(w, lcrow, LAYOUT_TOPBOTTOM, w->sy - used); } /* Fix cell offsets. */ layout_fix_offsets(w); layout_fix_panes(w); layout_print_cell(w->layout_root, __func__, 1); window_resize(w, lc->sx, lc->sy); notify_window("window-layout-changed", w); server_redraw_window(w); } tmux-3.0a/layout.c100644 001750 001750 00000066530 13517540563 0007720/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * Copyright (c) 2016 Stephen Kent * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN 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. */ static u_int layout_resize_check(struct window *, struct layout_cell *, enum layout_type); static int layout_resize_pane_grow(struct window *, struct layout_cell *, enum layout_type, int, int); static int layout_resize_pane_shrink(struct window *, struct layout_cell *, enum layout_type, int); static u_int layout_new_pane_size(struct window *, u_int, struct layout_cell *, enum layout_type, u_int, u_int, u_int); static int layout_set_size_check(struct window *, struct layout_cell *, enum layout_type, int); static void layout_resize_child_cells(struct window *, struct layout_cell *); 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; } free(lc); } void layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n) { struct layout_cell *lcchild; const char *type; switch (lc->type) { case LAYOUT_LEFTRIGHT: type = "LEFTRIGHT"; break; case LAYOUT_TOPBOTTOM: type = "TOPBOTTOM"; break; case LAYOUT_WINDOWPANE: type = "WINDOWPANE"; break; default: type = "UNKNOWN"; break; } log_debug("%s:%*s%p type %s [parent %p] wp=%p [%u,%u %ux%u]", hdr, n, " ", 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; } } struct layout_cell * layout_search_by_border(struct layout_cell *lc, u_int x, u_int y) { struct layout_cell *lcchild, *last = NULL; TAILQ_FOREACH(lcchild, &lc->cells, entry) { if (x >= lcchild->xoff && x < lcchild->xoff + lcchild->sx && y >= lcchild->yoff && y < lcchild->yoff + lcchild->sy) { /* Inside the cell - recurse. */ return (layout_search_by_border(lcchild, x, y)); } if (last == NULL) { last = lcchild; continue; } switch (lc->type) { case LAYOUT_LEFTRIGHT: if (x < lcchild->xoff && x >= last->xoff + last->sx) return (last); break; case LAYOUT_TOPBOTTOM: if (y < lcchild->yoff && y >= last->yoff + last->sy) return (last); break; case LAYOUT_WINDOWPANE: break; } last = lcchild; } return (NULL); } 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 for a child cell. */ static void layout_fix_offsets1(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_offsets1(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_offsets1(lcchild); yoff += lcchild->sy + 1; } } } /* Update cell offsets based on their sizes. */ void layout_fix_offsets(struct window *w) { struct layout_cell *lc = w->layout_root; lc->xoff = 0; lc->yoff = 0; layout_fix_offsets1(lc); } /* Is this a top cell? */ static int layout_cell_is_top(struct window *w, struct layout_cell *lc) { struct layout_cell *next; while (lc != w->layout_root) { next = lc->parent; if (next->type == LAYOUT_TOPBOTTOM && lc != TAILQ_FIRST(&next->cells)) return (0); lc = next; } return (1); } /* Is this a bottom cell? */ static int layout_cell_is_bottom(struct window *w, struct layout_cell *lc) { struct layout_cell *next; while (lc != w->layout_root) { next = lc->parent; if (next->type == LAYOUT_TOPBOTTOM && lc != TAILQ_LAST(&next->cells, layout_cells)) return (0); lc = next; } return (1); } /* * Returns 1 if we need to add an extra line for the pane status line. This is * the case for the most upper or lower panes only. */ static int layout_add_border(struct window *w, struct layout_cell *lc, int status) { if (status == PANE_STATUS_TOP) return (layout_cell_is_top(w, lc)); if (status == PANE_STATUS_BOTTOM) return (layout_cell_is_bottom(w, lc)); return (0); } /* Update pane offsets and sizes based on their cells. */ void layout_fix_panes(struct window *w) { struct window_pane *wp; struct layout_cell *lc; int status; status = options_get_number(w->options, "pane-border-status"); TAILQ_FOREACH(wp, &w->panes, entry) { if ((lc = wp->layout_cell) == NULL) continue; wp->xoff = lc->xoff; wp->yoff = lc->yoff; if (layout_add_border(w, lc, status)) { if (status == PANE_STATUS_TOP) wp->yoff++; window_pane_resize(wp, lc->sx, lc->sy - 1); } else window_pane_resize(wp, lc->sx, lc->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 count; switch (lc->type) { case LAYOUT_WINDOWPANE: return (1); case LAYOUT_LEFTRIGHT: case LAYOUT_TOPBOTTOM: count = 0; TAILQ_FOREACH(lcchild, &lc->cells, entry) count += layout_count_cells(lcchild); return (count); default: fatalx("bad layout type"); } } /* Calculate how much size is available to be removed from a cell. */ static u_int layout_resize_check(struct window *w, struct layout_cell *lc, enum layout_type type) { struct layout_cell *lcchild; u_int available, minimum; int status; status = options_get_number(w->options, "pane-border-status"); if (lc->type == LAYOUT_WINDOWPANE) { /* Space available in this cell only. */ if (type == LAYOUT_LEFTRIGHT) { available = lc->sx; minimum = PANE_MINIMUM; } else { available = lc->sy; if (layout_add_border(w, lc, status)) minimum = PANE_MINIMUM + 1; else minimum = PANE_MINIMUM; } if (available > minimum) available -= 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(w, 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(w, 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 window *w, 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(w, 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(w, lcchild, type, 1); change--; continue; } if (layout_resize_check(w, lcchild, type) > 0) { layout_resize_adjust(w, lcchild, type, -1); change++; } } } } /* Destroy a cell and redistribute the space. */ void layout_destroy_cell(struct window *w, 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 (lcother != NULL && lcparent->type == LAYOUT_LEFTRIGHT) layout_resize_adjust(w, lcother, lcparent->type, lc->sx + 1); else if (lcother != NULL) layout_resize_adjust(w, 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 window_pane *wp) { 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, wp); layout_fix_panes(w); } 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 - lc->sx; xlimit = layout_resize_check(w, 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(w, lc, LAYOUT_LEFTRIGHT, xchange); /* Adjust vertically in a similar fashion. */ ychange = sy - lc->sy; ylimit = layout_resize_check(w, 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(w, lc, LAYOUT_TOPBOTTOM, ychange); /* Fix cell offsets. */ layout_fix_offsets(w); layout_fix_panes(w); } /* Resize a pane to an absolute size. */ void layout_resize_pane_to(struct window_pane *wp, enum layout_type type, u_int new_size) { struct layout_cell *lc, *lcparent; int change, 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; /* Work out the size adjustment. */ if (type == LAYOUT_LEFTRIGHT) size = lc->sx; else size = lc->sy; if (lc == TAILQ_LAST(&lcparent->cells, layout_cells)) change = size - new_size; else change = new_size - size; /* Resize the pane. */ layout_resize_pane(wp, type, change, 1); } void layout_resize_layout(struct window *w, struct layout_cell *lc, enum layout_type type, int change, int opposite) { int needed, size; /* Grow or shrink the cell. */ needed = change; while (needed != 0) { if (change > 0) { size = layout_resize_pane_grow(w, lc, type, needed, opposite); needed -= size; } else { size = layout_resize_pane_shrink(w, lc, type, needed); needed += size; } if (size == 0) /* no more change possible */ break; } /* Fix cell offsets. */ layout_fix_offsets(w); layout_fix_panes(w); notify_window("window-layout-changed", w); } /* Resize a single pane within the layout. */ void layout_resize_pane(struct window_pane *wp, enum layout_type type, int change, int opposite) { struct layout_cell *lc, *lcparent; 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); layout_resize_layout(wp->window, lc, type, change, opposite); } /* Helper function to grow pane. */ static int layout_resize_pane_grow(struct window *w, struct layout_cell *lc, enum layout_type type, int needed, int opposite) { struct layout_cell *lcadd, *lcremove; u_int size = 0; /* 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(w, lcremove, type); if (size > 0) break; lcremove = TAILQ_NEXT(lcremove, entry); } /* If none found, look towards the head. */ if (opposite && lcremove == NULL) { lcremove = TAILQ_PREV(lc, layout_cells, entry); while (lcremove != NULL) { size = layout_resize_check(w, 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(w, lcadd, type, size); layout_resize_adjust(w, lcremove, type, -size); return (size); } /* Helper function to shrink pane. */ static int layout_resize_pane_shrink(struct window *w, 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(w, 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(w, lcadd, type, size); layout_resize_adjust(w, 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); } /* Calculate the new pane size for resized parent. */ static u_int layout_new_pane_size(struct window *w, u_int previous, struct layout_cell *lc, enum layout_type type, u_int size, u_int count_left, u_int size_left) { u_int new_size, min, max, available; /* If this is the last cell, it can take all of the remaining size. */ if (count_left == 1) return (size_left); /* How much is available in this parent? */ available = layout_resize_check(w, lc, type); /* * Work out the minimum size of this cell and the new size * proportionate to the previous size. */ min = (PANE_MINIMUM + 1) * (count_left - 1); if (type == LAYOUT_LEFTRIGHT) { if (lc->sx - available > min) min = lc->sx - available; new_size = (lc->sx * size) / previous; } else { if (lc->sy - available > min) min = lc->sy - available; new_size = (lc->sy * size) / previous; } /* Check against the maximum and minimum size. */ max = size_left - min; if (new_size > max) new_size = max; if (new_size < PANE_MINIMUM) new_size = PANE_MINIMUM; return (new_size); } /* Check if the cell and all its children can be resized to a specific size. */ static int layout_set_size_check(struct window *w, struct layout_cell *lc, enum layout_type type, int size) { struct layout_cell *lcchild; u_int new_size, available, previous, count, idx; /* Cells with no children must just be bigger than minimum. */ if (lc->type == LAYOUT_WINDOWPANE) return (size >= PANE_MINIMUM); available = size; /* Count number of children. */ count = 0; TAILQ_FOREACH(lcchild, &lc->cells, entry) count++; /* Check new size will work for each child. */ if (lc->type == type) { if (available < (count * 2) - 1) return (0); if (type == LAYOUT_LEFTRIGHT) previous = lc->sx; else previous = lc->sy; idx = 0; TAILQ_FOREACH(lcchild, &lc->cells, entry) { new_size = layout_new_pane_size(w, previous, lcchild, type, size, count - idx, available); if (idx == count - 1) { if (new_size > available) return (0); available -= new_size; } else { if (new_size + 1 > available) return (0); available -= new_size + 1; } if (!layout_set_size_check(w, lcchild, type, new_size)) return (0); idx++; } } else { TAILQ_FOREACH(lcchild, &lc->cells, entry) { if (lcchild->type == LAYOUT_WINDOWPANE) continue; if (!layout_set_size_check(w, lcchild, type, size)) return (0); } } return (1); } /* Resize all child cells to fit within the current cell. */ static void layout_resize_child_cells(struct window *w, struct layout_cell *lc) { struct layout_cell *lcchild; u_int previous, available, count, idx; if (lc->type == LAYOUT_WINDOWPANE) return; /* What is the current size used? */ count = 0; previous = 0; TAILQ_FOREACH(lcchild, &lc->cells, entry) { count++; if (lc->type == LAYOUT_LEFTRIGHT) previous += lcchild->sx; else if (lc->type == LAYOUT_TOPBOTTOM) previous += lcchild->sy; } previous += (count - 1); /* And how much is available? */ available = 0; if (lc->type == LAYOUT_LEFTRIGHT) available = lc->sx; else if (lc->type == LAYOUT_TOPBOTTOM) available = lc->sy; /* Resize children into the new size. */ idx = 0; TAILQ_FOREACH(lcchild, &lc->cells, entry) { if (lc->type == LAYOUT_TOPBOTTOM) { lcchild->sx = lc->sx; lcchild->xoff = lc->xoff; } else { lcchild->sx = layout_new_pane_size(w, previous, lcchild, lc->type, lc->sx, count - idx, available); available -= (lcchild->sx + 1); } if (lc->type == LAYOUT_LEFTRIGHT) lcchild->sy = lc->sy; else { lcchild->sy = layout_new_pane_size(w, previous, lcchild, lc->type, lc->sy, count - idx, available); available -= (lcchild->sy + 1); } layout_resize_child_cells(w, lcchild); idx++; } } /* * 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, int flags) { struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2; u_int sx, sy, xoff, yoff, size1, size2, minimum; u_int new_size, saved_size, resize_first = 0; int full_size = (flags & SPAWN_FULLSIZE), status; /* * If full_size is specified, add a new cell at the top of the window * layout. Otherwise, split the cell for the current pane. */ if (full_size) lc = wp->window->layout_root; else lc = wp->layout_cell; status = options_get_number(wp->window->options, "pane-border-status"); /* 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 (layout_add_border(wp->window, lc, status)) minimum = PANE_MINIMUM * 2 + 2; else minimum = PANE_MINIMUM * 2 + 1; if (sy < minimum) return (NULL); break; default: fatalx("bad layout type"); } /* * Calculate 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. */ if (type == LAYOUT_LEFTRIGHT) saved_size = sx; else saved_size = sy; if (size < 0) size2 = ((saved_size + 1) / 2) - 1; else if (flags & SPAWN_BEFORE) size2 = saved_size - size - 1; else size2 = size; if (size2 < PANE_MINIMUM) size2 = PANE_MINIMUM; else if (size2 > saved_size - 2) size2 = saved_size - 2; size1 = saved_size - 1 - size2; /* Which size are we using? */ if (flags & SPAWN_BEFORE) new_size = size2; else new_size = size1; /* Confirm there is enough space for full size pane. */ if (full_size && !layout_set_size_check(wp->window, lc, type, new_size)) return (NULL); 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. */ lcparent = lc->parent; lcnew = layout_create_cell(lcparent); if (flags & SPAWN_BEFORE) TAILQ_INSERT_BEFORE(lc, lcnew, entry); else TAILQ_INSERT_AFTER(&lcparent->cells, lc, lcnew, entry); } else if (full_size && lc->parent == NULL && lc->type == type) { /* * If the new full size pane is the same type as the root * split, insert the new pane under the existing root cell * instead of creating a new root cell. The existing layout * must be resized before inserting the new cell. */ if (lc->type == LAYOUT_LEFTRIGHT) { lc->sx = new_size; layout_resize_child_cells(wp->window, lc); lc->sx = saved_size; } else if (lc->type == LAYOUT_TOPBOTTOM) { lc->sy = new_size; layout_resize_child_cells(wp->window, lc); lc->sy = saved_size; } resize_first = 1; /* Create the new cell. */ lcnew = layout_create_cell(lc); size = saved_size - 1 - new_size; if (lc->type == LAYOUT_LEFTRIGHT) layout_set_size(lcnew, size, sy, 0, 0); else if (lc->type == LAYOUT_TOPBOTTOM) layout_set_size(lcnew, sx, size, 0, 0); if (flags & SPAWN_BEFORE) TAILQ_INSERT_HEAD(&lc->cells, lcnew, entry); else TAILQ_INSERT_TAIL(&lc->cells, 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); if (flags & SPAWN_BEFORE) TAILQ_INSERT_HEAD(&lcparent->cells, lcnew, entry); else TAILQ_INSERT_TAIL(&lcparent->cells, lcnew, entry); } if (flags & SPAWN_BEFORE) { lc1 = lcnew; lc2 = lc; } else { lc1 = lc; lc2 = lcnew; } /* * Set new cell sizes. size1 is the size of the top/left and size2 the * bottom/right. */ if (!resize_first && type == LAYOUT_LEFTRIGHT) { layout_set_size(lc1, size1, sy, xoff, yoff); layout_set_size(lc2, size2, sy, xoff + lc1->sx + 1, yoff); } else if (!resize_first && type == LAYOUT_TOPBOTTOM) { layout_set_size(lc1, sx, size1, xoff, yoff); layout_set_size(lc2, sx, size2, xoff, yoff + lc1->sy + 1); } if (full_size) { if (!resize_first) layout_resize_child_cells(wp->window, lc); layout_fix_offsets(wp->window); } else layout_make_leaf(lc, wp); return (lcnew); } /* Destroy the cell associated with a pane. */ void layout_close_pane(struct window_pane *wp) { struct window *w = wp->window; /* Remove the cell. */ layout_destroy_cell(w, wp->layout_cell, &w->layout_root); /* Fix pane offsets and sizes. */ if (w->layout_root != NULL) { layout_fix_offsets(w); layout_fix_panes(w); } notify_window("window-layout-changed", w); } int layout_spread_cell(struct window *w, struct layout_cell *parent) { struct layout_cell *lc; u_int number, each, size, this; int change, changed, status; number = 0; TAILQ_FOREACH (lc, &parent->cells, entry) number++; if (number <= 1) return (0); status = options_get_number(w->options, "pane-border-status"); if (parent->type == LAYOUT_LEFTRIGHT) size = parent->sx; else if (parent->type == LAYOUT_TOPBOTTOM) { if (layout_add_border(w, parent, status)) size = parent->sy - 1; else size = parent->sy; } else return (0); if (size < number - 1) return (0); each = (size - (number - 1)) / number; if (each == 0) return (0); changed = 0; TAILQ_FOREACH (lc, &parent->cells, entry) { if (TAILQ_NEXT(lc, entry) == NULL) each = size - ((each + 1) * (number - 1)); change = 0; if (parent->type == LAYOUT_LEFTRIGHT) { change = each - (int)lc->sx; layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, change); } else if (parent->type == LAYOUT_TOPBOTTOM) { if (layout_add_border(w, lc, status)) this = each + 1; else this = each; change = this - (int)lc->sy; layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, change); } if (change != 0) changed = 1; } return (changed); } void layout_spread_out(struct window_pane *wp) { struct layout_cell *parent; struct window *w = wp->window; parent = wp->layout_cell->parent; if (parent == NULL) return; do { if (layout_spread_cell(w, parent)) { layout_fix_offsets(w); layout_fix_panes(w); break; } } while ((parent = parent->parent) != NULL); } tmux-3.0a/log.c100644 001750 001750 00000006152 13570677043 0007161/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "tmux.h" static FILE *log_file; static int log_level; static void log_event_cb(int, const char *); static void log_vwrite(const char *, va_list); /* Log callback for libevent. */ static void log_event_cb(__unused int severity, const char *msg) { log_debug("%s", msg); } /* Increment log level. */ void log_add_level(void) { log_level++; } /* Get log level. */ int log_get_level(void) { return (log_level); } /* Open logging to file. */ void log_open(const char *name) { char *path; if (log_level == 0) return; log_close(); xasprintf(&path, "tmux-%s-%ld.log", name, (long)getpid()); log_file = fopen(path, "a"); free(path); if (log_file == NULL) return; setvbuf(log_file, NULL, _IOLBF, 0); event_set_log_callback(log_event_cb); } /* Toggle logging. */ void log_toggle(const char *name) { if (log_level == 0) { log_level = 1; log_open(name); log_debug("log opened"); } else { log_debug("log closed"); log_level = 0; log_close(); } } /* Close logging. */ void log_close(void) { if (log_file != NULL) fclose(log_file); log_file = NULL; event_set_log_callback(NULL); } /* Write a log message. */ static void log_vwrite(const char *msg, va_list ap) { char *fmt, *out; struct timeval tv; if (log_file == NULL) return; if (vasprintf(&fmt, msg, ap) == -1) exit(1); if (stravis(&out, fmt, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL) == -1) exit(1); gettimeofday(&tv, NULL); if (fprintf(log_file, "%lld.%06d %s\n", (long long)tv.tv_sec, (int)tv.tv_usec, out) == -1) exit(1); fflush(log_file); free(out); free(fmt); } /* Log a debug message. */ void log_debug(const char *msg, ...) { va_list ap; va_start(ap, msg); log_vwrite(msg, ap); va_end(ap); } /* Log a critical error with error string and die. */ __dead void fatal(const char *msg, ...) { char *fmt; va_list ap; va_start(ap, msg); if (asprintf(&fmt, "fatal: %s: %s", msg, strerror(errno)) == -1) exit(1); log_vwrite(fmt, ap); va_end(ap); exit(1); } /* Log a critical error and die. */ __dead void fatalx(const char *msg, ...) { char *fmt; va_list ap; va_start(ap, msg); if (asprintf(&fmt, "fatal: %s", msg) == -1) exit(1); log_vwrite(fmt, ap); va_end(ap); exit(1); } tmux-3.0a/menu.c100644 001750 001750 00000016402 13562601163 0007332/* $OpenBSD$ */ /* * Copyright (c) 2019 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 menu_data { struct cmdq_item *item; int flags; struct cmd_find_state fs; struct screen s; u_int px; u_int py; struct menu *menu; int choice; menu_choice_cb cb; void *data; }; void menu_add_items(struct menu *menu, const struct menu_item *items, struct cmdq_item *qitem, struct client *c, struct cmd_find_state *fs) { const struct menu_item *loop; for (loop = items; loop->name != NULL; loop++) menu_add_item(menu, loop, qitem, c, fs); } void menu_add_item(struct menu *menu, const struct menu_item *item, struct cmdq_item *qitem, struct client *c, struct cmd_find_state *fs) { struct menu_item *new_item; const char *key, *cmd; char *s, *name; u_int width; int line; line = (item == NULL || item->name == NULL || *item->name == '\0'); if (line && menu->count == 0) return; menu->items = xreallocarray(menu->items, menu->count + 1, sizeof *menu->items); new_item = &menu->items[menu->count++]; memset(new_item, 0, sizeof *new_item); if (line) return; if (fs != NULL) s = format_single(qitem, item->name, c, fs->s, fs->wl, fs->wp); else s = format_single(qitem, item->name, c, NULL, NULL, NULL); if (*s == '\0') { /* no item if empty after format expanded */ menu->count--; return; } if (*s != '-' && item->key != KEYC_UNKNOWN && item->key != KEYC_NONE) { key = key_string_lookup_key(item->key); xasprintf(&name, "%s#[default] #[align=right](%s)", s, key); } else xasprintf(&name, "%s", s); new_item->name = name; free(s); cmd = item->command; if (cmd != NULL) { if (fs != NULL) s = format_single(qitem, cmd, c, fs->s, fs->wl, fs->wp); else s = format_single(qitem, cmd, c, NULL, NULL, NULL); } else s = NULL; new_item->command = s; new_item->key = item->key; width = format_width(new_item->name); if (width > menu->width) menu->width = width; } struct menu * menu_create(const char *title) { struct menu *menu; menu = xcalloc(1, sizeof *menu); menu->title = xstrdup(title); return (menu); } void menu_free(struct menu *menu) { u_int i; for (i = 0; i < menu->count; i++) { free((void *)menu->items[i].name); free((void *)menu->items[i].command); } free(menu->items); free((void *)menu->title); free(menu); } static void menu_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0) { struct menu_data *md = c->overlay_data; struct tty *tty = &c->tty; struct screen *s = &md->s; struct menu *menu = md->menu; struct screen_write_ctx ctx; u_int i, px, py; screen_write_start(&ctx, NULL, s); screen_write_clearscreen(&ctx, 8); screen_write_menu(&ctx, menu, md->choice); screen_write_stop(&ctx); px = md->px; py = md->py; for (i = 0; i < screen_size_y(&md->s); i++) tty_draw_line(tty, NULL, s, 0, i, menu->width + 4, px, py + i); if (~md->flags & MENU_NOMOUSE) tty_update_mode(tty, MODE_MOUSE_ALL, NULL); } static void menu_free_cb(struct client *c) { struct menu_data *md = c->overlay_data; if (md->item != NULL) cmdq_continue(md->item); if (md->cb != NULL) md->cb(md->menu, UINT_MAX, KEYC_NONE, md->data); screen_free(&md->s); menu_free(md->menu); free(md); } static int menu_key_cb(struct client *c, struct key_event *event) { struct menu_data *md = c->overlay_data; struct menu *menu = md->menu; struct mouse_event *m = &event->m; u_int i; int count = menu->count, old = md->choice; const struct menu_item *item; struct cmdq_item *new_item; struct cmd_parse_result *pr; const char *name; if (KEYC_IS_MOUSE(event->key)) { if (md->flags & MENU_NOMOUSE) { if (MOUSE_BUTTONS(m->b) != 0) return (1); return (0); } if (m->x < md->px || m->x > md->px + 4 + menu->width || m->y < md->py + 1 || m->y > md->py + 1 + count - 1) { if (MOUSE_RELEASE(m->b)) return (1); if (md->choice != -1) { md->choice = -1; c->flags |= CLIENT_REDRAWOVERLAY; } return (0); } if (MOUSE_RELEASE(m->b)) goto chosen; md->choice = m->y - (md->py + 1); if (md->choice != old) c->flags |= CLIENT_REDRAWOVERLAY; return (0); } for (i = 0; i < (u_int)count; i++) { name = menu->items[i].name; if (name == NULL || *name == '-') continue; if (event->key == menu->items[i].key) { md->choice = i; goto chosen; } } switch (event->key) { case KEYC_UP: case 'k': if (old == -1) old = 0; do { if (md->choice == -1 || md->choice == 0) md->choice = count - 1; else md->choice--; name = menu->items[md->choice].name; } while ((name == NULL || *name == '-') && md->choice != old); c->flags |= CLIENT_REDRAWOVERLAY; return (0); case KEYC_DOWN: case 'j': if (old == -1) old = 0; do { if (md->choice == -1 || md->choice == count - 1) md->choice = 0; else md->choice++; name = menu->items[md->choice].name; } while ((name == NULL || *name == '-') && md->choice != old); c->flags |= CLIENT_REDRAWOVERLAY; return (0); case '\r': goto chosen; case '\033': /* Escape */ case '\003': /* C-c */ case '\007': /* C-g */ case 'q': return (1); } return (0); chosen: if (md->choice == -1) return (1); item = &menu->items[md->choice]; if (item->name == NULL || *item->name == '-') return (1); if (md->cb != NULL) { md->cb(md->menu, md->choice, item->key, md->data); md->cb = NULL; return (1); } pr = cmd_parse_from_string(item->command, NULL); switch (pr->status) { case CMD_PARSE_EMPTY: new_item = NULL; break; case CMD_PARSE_ERROR: new_item = cmdq_get_error(pr->error); free(pr->error); cmdq_append(c, new_item); break; case CMD_PARSE_SUCCESS: if (md->item != NULL) m = &md->item->shared->mouse; else m = NULL; new_item = cmdq_get_command(pr->cmdlist, &md->fs, m, 0); cmd_list_free(pr->cmdlist); cmdq_append(c, new_item); break; } return (1); } int menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px, u_int py, struct client *c, struct cmd_find_state *fs, menu_choice_cb cb, void *data) { struct menu_data *md; if (c->tty.sx < menu->width + 4 || c->tty.sy < menu->count + 2) return (-1); md = xcalloc(1, sizeof *md); md->item = item; md->flags = flags; if (fs != NULL) cmd_find_copy_state(&md->fs, fs); screen_init(&md->s, menu->width + 4, menu->count + 2, 0); md->px = px; md->py = py; md->menu = menu; md->choice = -1; md->cb = cb; md->data = data; server_client_set_overlay(c, 0, menu_draw_cb, menu_key_cb, menu_free_cb, md); return (0); } tmux-3.0a/mode-tree.c100644 001750 001750 00000055465 13570677043 0010274/* $OpenBSD$ */ /* * Copyright (c) 2017 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" struct mode_tree_item; TAILQ_HEAD(mode_tree_list, mode_tree_item); struct mode_tree_data { int dead; u_int references; int zoomed; struct window_pane *wp; void *modedata; const struct menu_item *menu; const char **sort_list; u_int sort_size; u_int sort_type; mode_tree_build_cb buildcb; mode_tree_draw_cb drawcb; mode_tree_search_cb searchcb; mode_tree_menu_cb menucb; struct mode_tree_list children; struct mode_tree_list saved; struct mode_tree_line *line_list; u_int line_size; u_int depth; u_int width; u_int height; u_int offset; u_int current; struct screen screen; int preview; char *search; char *filter; int no_matches; }; struct mode_tree_item { struct mode_tree_item *parent; void *itemdata; u_int line; uint64_t tag; const char *name; const char *text; int expanded; int tagged; struct mode_tree_list children; TAILQ_ENTRY(mode_tree_item) entry; }; struct mode_tree_line { struct mode_tree_item *item; u_int depth; int last; int flat; }; struct mode_tree_menu { struct mode_tree_data *data; struct client *c; u_int line; void *itemdata; }; static void mode_tree_free_items(struct mode_tree_list *); static const struct menu_item mode_tree_menu_items[] = { { "Scroll Left", '<', NULL }, { "Scroll Right", '>', NULL }, { "", KEYC_NONE, NULL }, { "Cancel", 'q', NULL }, { NULL, KEYC_NONE, NULL } }; static struct mode_tree_item * mode_tree_find_item(struct mode_tree_list *mtl, uint64_t tag) { struct mode_tree_item *mti, *child; TAILQ_FOREACH(mti, mtl, entry) { if (mti->tag == tag) return (mti); child = mode_tree_find_item(&mti->children, tag); if (child != NULL) return (child); } return (NULL); } static void mode_tree_free_item(struct mode_tree_item *mti) { mode_tree_free_items(&mti->children); free((void *)mti->name); free((void *)mti->text); free(mti); } static void mode_tree_free_items(struct mode_tree_list *mtl) { struct mode_tree_item *mti, *mti1; TAILQ_FOREACH_SAFE(mti, mtl, entry, mti1) { TAILQ_REMOVE(mtl, mti, entry); mode_tree_free_item(mti); } } static void mode_tree_check_selected(struct mode_tree_data *mtd) { /* * If the current line would now be off screen reset the offset to the * last visible line. */ if (mtd->current > mtd->height - 1) mtd->offset = mtd->current - mtd->height + 1; } static void mode_tree_clear_lines(struct mode_tree_data *mtd) { free(mtd->line_list); mtd->line_list = NULL; mtd->line_size = 0; } static void mode_tree_build_lines(struct mode_tree_data *mtd, struct mode_tree_list *mtl, u_int depth) { struct mode_tree_item *mti; struct mode_tree_line *line; u_int i; int flat = 1; mtd->depth = depth; TAILQ_FOREACH(mti, mtl, entry) { mtd->line_list = xreallocarray(mtd->line_list, mtd->line_size + 1, sizeof *mtd->line_list); line = &mtd->line_list[mtd->line_size++]; line->item = mti; line->depth = depth; line->last = (mti == TAILQ_LAST(mtl, mode_tree_list)); mti->line = (mtd->line_size - 1); if (!TAILQ_EMPTY(&mti->children)) flat = 0; if (mti->expanded) mode_tree_build_lines(mtd, &mti->children, depth + 1); } TAILQ_FOREACH(mti, mtl, entry) { for (i = 0; i < mtd->line_size; i++) { line = &mtd->line_list[i]; if (line->item == mti) line->flat = flat; } } } static void mode_tree_clear_tagged(struct mode_tree_list *mtl) { struct mode_tree_item *mti; TAILQ_FOREACH(mti, mtl, entry) { mti->tagged = 0; mode_tree_clear_tagged(&mti->children); } } static void mode_tree_up(struct mode_tree_data *mtd, int wrap) { if (mtd->current == 0) { if (wrap) { mtd->current = mtd->line_size - 1; if (mtd->line_size >= mtd->height) mtd->offset = mtd->line_size - mtd->height; } } else { mtd->current--; if (mtd->current < mtd->offset) mtd->offset--; } } void mode_tree_down(struct mode_tree_data *mtd, int wrap) { if (mtd->current == mtd->line_size - 1) { if (wrap) { mtd->current = 0; mtd->offset = 0; } } else { mtd->current++; if (mtd->current > mtd->offset + mtd->height - 1) mtd->offset++; } } void * mode_tree_get_current(struct mode_tree_data *mtd) { return (mtd->line_list[mtd->current].item->itemdata); } void mode_tree_expand_current(struct mode_tree_data *mtd) { if (!mtd->line_list[mtd->current].item->expanded) { mtd->line_list[mtd->current].item->expanded = 1; mode_tree_build(mtd); } } void mode_tree_set_current(struct mode_tree_data *mtd, uint64_t tag) { u_int i; for (i = 0; i < mtd->line_size; i++) { if (mtd->line_list[i].item->tag == tag) break; } if (i != mtd->line_size) { mtd->current = i; if (mtd->current > mtd->height - 1) mtd->offset = mtd->current - mtd->height + 1; else mtd->offset = 0; } else { mtd->current = 0; mtd->offset = 0; } } u_int mode_tree_count_tagged(struct mode_tree_data *mtd) { struct mode_tree_item *mti; u_int i, tagged; tagged = 0; for (i = 0; i < mtd->line_size; i++) { mti = mtd->line_list[i].item; if (mti->tagged) tagged++; } return (tagged); } void mode_tree_each_tagged(struct mode_tree_data *mtd, mode_tree_each_cb cb, struct client *c, key_code key, int current) { struct mode_tree_item *mti; u_int i; int fired; fired = 0; for (i = 0; i < mtd->line_size; i++) { mti = mtd->line_list[i].item; if (mti->tagged) { fired = 1; cb(mtd->modedata, mti->itemdata, c, key); } } if (!fired && current) { mti = mtd->line_list[mtd->current].item; cb(mtd->modedata, mti->itemdata, c, key); } } struct mode_tree_data * mode_tree_start(struct window_pane *wp, struct args *args, mode_tree_build_cb buildcb, mode_tree_draw_cb drawcb, mode_tree_search_cb searchcb, mode_tree_menu_cb menucb, void *modedata, const struct menu_item *menu, const char **sort_list, u_int sort_size, struct screen **s) { struct mode_tree_data *mtd; const char *sort; u_int i; mtd = xcalloc(1, sizeof *mtd); mtd->references = 1; mtd->wp = wp; mtd->modedata = modedata; mtd->menu = menu; mtd->sort_list = sort_list; mtd->sort_size = sort_size; mtd->sort_type = 0; mtd->preview = !args_has(args, 'N'); sort = args_get(args, 'O'); if (sort != NULL) { for (i = 0; i < sort_size; i++) { if (strcasecmp(sort, sort_list[i]) == 0) mtd->sort_type = i; } } if (args_has(args, 'f')) mtd->filter = xstrdup(args_get(args, 'f')); else mtd->filter = NULL; mtd->buildcb = buildcb; mtd->drawcb = drawcb; mtd->searchcb = searchcb; mtd->menucb = menucb; TAILQ_INIT(&mtd->children); *s = &mtd->screen; screen_init(*s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); (*s)->mode &= ~MODE_CURSOR; return (mtd); } void mode_tree_zoom(struct mode_tree_data *mtd, struct args *args) { struct window_pane *wp = mtd->wp; if (args_has(args, 'Z')) { mtd->zoomed = (wp->window->flags & WINDOW_ZOOMED); if (!mtd->zoomed && window_zoom(wp) == 0) server_redraw_window(wp->window); } else mtd->zoomed = -1; } void mode_tree_build(struct mode_tree_data *mtd) { struct screen *s = &mtd->screen; uint64_t tag; if (mtd->line_list != NULL) tag = mtd->line_list[mtd->current].item->tag; else tag = UINT64_MAX; TAILQ_CONCAT(&mtd->saved, &mtd->children, entry); TAILQ_INIT(&mtd->children); mtd->buildcb(mtd->modedata, mtd->sort_type, &tag, mtd->filter); mtd->no_matches = TAILQ_EMPTY(&mtd->children); if (mtd->no_matches) mtd->buildcb(mtd->modedata, mtd->sort_type, &tag, NULL); mode_tree_free_items(&mtd->saved); TAILQ_INIT(&mtd->saved); mode_tree_clear_lines(mtd); mode_tree_build_lines(mtd, &mtd->children, 0); if (tag == UINT64_MAX) tag = mtd->line_list[mtd->current].item->tag; mode_tree_set_current(mtd, tag); mtd->width = screen_size_x(s); if (mtd->preview) { mtd->height = (screen_size_y(s) / 3) * 2; if (mtd->height > mtd->line_size) mtd->height = screen_size_y(s) / 2; if (mtd->height < 10) mtd->height = screen_size_y(s); if (screen_size_y(s) - mtd->height < 2) mtd->height = screen_size_y(s); } else mtd->height = screen_size_y(s); mode_tree_check_selected(mtd); } static void mode_tree_remove_ref(struct mode_tree_data *mtd) { if (--mtd->references == 0) free(mtd); } void mode_tree_free(struct mode_tree_data *mtd) { struct window_pane *wp = mtd->wp; if (mtd->zoomed == 0) server_unzoom_window(wp->window); mode_tree_free_items(&mtd->children); mode_tree_clear_lines(mtd); screen_free(&mtd->screen); free(mtd->search); free(mtd->filter); mtd->dead = 1; mode_tree_remove_ref(mtd); } void mode_tree_resize(struct mode_tree_data *mtd, u_int sx, u_int sy) { struct screen *s = &mtd->screen; screen_resize(s, sx, sy, 0); mode_tree_build(mtd); mode_tree_draw(mtd); mtd->wp->flags |= PANE_REDRAW; } struct mode_tree_item * mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent, void *itemdata, uint64_t tag, const char *name, const char *text, int expanded) { struct mode_tree_item *mti, *saved; log_debug("%s: %llu, %s %s", __func__, (unsigned long long)tag, name, text); mti = xcalloc(1, sizeof *mti); mti->parent = parent; mti->itemdata = itemdata; mti->tag = tag; mti->name = xstrdup(name); mti->text = xstrdup(text); saved = mode_tree_find_item(&mtd->saved, tag); if (saved != NULL) { if (parent == NULL || parent->expanded) mti->tagged = saved->tagged; mti->expanded = saved->expanded; } else if (expanded == -1) mti->expanded = 1; else mti->expanded = expanded; TAILQ_INIT(&mti->children); if (parent != NULL) TAILQ_INSERT_TAIL(&parent->children, mti, entry); else TAILQ_INSERT_TAIL(&mtd->children, mti, entry); return (mti); } void mode_tree_remove(struct mode_tree_data *mtd, struct mode_tree_item *mti) { struct mode_tree_item *parent = mti->parent; if (parent != NULL) TAILQ_REMOVE(&parent->children, mti, entry); else TAILQ_REMOVE(&mtd->children, mti, entry); mode_tree_free_item(mti); } void mode_tree_draw(struct mode_tree_data *mtd) { struct window_pane *wp = mtd->wp; struct screen *s = &mtd->screen; struct mode_tree_line *line; struct mode_tree_item *mti; struct options *oo = wp->window->options; struct screen_write_ctx ctx; struct grid_cell gc0, gc; u_int w, h, i, j, sy, box_x, box_y, width; char *text, *start, key[7]; const char *tag, *symbol; size_t size, n; int keylen; if (mtd->line_size == 0) return; memcpy(&gc0, &grid_default_cell, sizeof gc0); memcpy(&gc, &grid_default_cell, sizeof gc); style_apply(&gc, oo, "mode-style"); w = mtd->width; h = mtd->height; screen_write_start(&ctx, NULL, s); screen_write_clearscreen(&ctx, 8); if (mtd->line_size > 10) keylen = 6; else keylen = 4; for (i = 0; i < mtd->line_size; i++) { if (i < mtd->offset) continue; if (i > mtd->offset + h - 1) break; line = &mtd->line_list[i]; mti = line->item; screen_write_cursormove(&ctx, 0, i - mtd->offset, 0); if (i < 10) snprintf(key, sizeof key, "(%c) ", '0' + i); else if (i < 36) snprintf(key, sizeof key, "(M-%c)", 'a' + (i - 10)); else *key = '\0'; if (line->flat) symbol = ""; else if (TAILQ_EMPTY(&mti->children)) symbol = " "; else if (mti->expanded) symbol = "- "; else symbol = "+ "; if (line->depth == 0) start = xstrdup(symbol); else { size = (4 * line->depth) + 32; start = xcalloc(1, size); for (j = 1; j < line->depth; j++) { if (mti->parent != NULL && mtd->line_list[mti->parent->line].last) strlcat(start, " ", size); else strlcat(start, "\001x\001 ", size); } if (line->last) strlcat(start, "\001mq\001> ", size); else strlcat(start, "\001tq\001> ", size); strlcat(start, symbol, size); } if (mti->tagged) tag = "*"; else tag = ""; xasprintf(&text, "%-*s%s%s%s: ", keylen, key, start, mti->name, tag); width = utf8_cstrwidth(text); free(start); if (mti->tagged) { gc.attr ^= GRID_ATTR_BRIGHT; gc0.attr ^= GRID_ATTR_BRIGHT; } if (i != mtd->current) { screen_write_clearendofline(&ctx, 8); screen_write_puts(&ctx, &gc0, "%s", text); format_draw(&ctx, &gc0, w - width, mti->text, NULL); } else { screen_write_clearendofline(&ctx, gc.bg); screen_write_puts(&ctx, &gc, "%s", text); format_draw(&ctx, &gc, w - width, mti->text, NULL); } free(text); if (mti->tagged) { gc.attr ^= GRID_ATTR_BRIGHT; gc0.attr ^= GRID_ATTR_BRIGHT; } } sy = screen_size_y(s); if (!mtd->preview || sy <= 4 || h <= 4 || sy - h <= 4 || w <= 4) { screen_write_stop(&ctx); return; } line = &mtd->line_list[mtd->current]; mti = line->item; screen_write_cursormove(&ctx, 0, h, 0); screen_write_box(&ctx, w, sy - h); xasprintf(&text, " %s (sort: %s)", mti->name, mtd->sort_list[mtd->sort_type]); if (w - 2 >= strlen(text)) { screen_write_cursormove(&ctx, 1, h, 0); screen_write_puts(&ctx, &gc0, "%s", text); if (mtd->no_matches) n = (sizeof "no matches") - 1; else n = (sizeof "active") - 1; if (mtd->filter != NULL && w - 2 >= strlen(text) + 10 + n + 2) { screen_write_puts(&ctx, &gc0, " (filter: "); if (mtd->no_matches) screen_write_puts(&ctx, &gc, "no matches"); else screen_write_puts(&ctx, &gc0, "active"); screen_write_puts(&ctx, &gc0, ") "); } } free(text); box_x = w - 4; box_y = sy - h - 2; if (box_x != 0 && box_y != 0) { screen_write_cursormove(&ctx, 2, h + 1, 0); mtd->drawcb(mtd->modedata, mti->itemdata, &ctx, box_x, box_y); } screen_write_stop(&ctx); } static struct mode_tree_item * mode_tree_search_for(struct mode_tree_data *mtd) { struct mode_tree_item *mti, *last, *next; if (mtd->search == NULL) return (NULL); mti = last = mtd->line_list[mtd->current].item; for (;;) { if (!TAILQ_EMPTY(&mti->children)) mti = TAILQ_FIRST(&mti->children); else if ((next = TAILQ_NEXT(mti, entry)) != NULL) mti = next; else { for (;;) { mti = mti->parent; if (mti == NULL) break; if ((next = TAILQ_NEXT(mti, entry)) != NULL) { mti = next; break; } } } if (mti == NULL) mti = TAILQ_FIRST(&mtd->children); if (mti == last) break; if (mtd->searchcb == NULL) { if (strstr(mti->name, mtd->search) != NULL) return (mti); continue; } if (mtd->searchcb(mtd->modedata, mti->itemdata, mtd->search)) return (mti); } return (NULL); } static void mode_tree_search_set(struct mode_tree_data *mtd) { struct mode_tree_item *mti, *loop; uint64_t tag; mti = mode_tree_search_for(mtd); if (mti == NULL) return; tag = mti->tag; loop = mti->parent; while (loop != NULL) { loop->expanded = 1; loop = loop->parent; } mode_tree_build(mtd); mode_tree_set_current(mtd, tag); mode_tree_draw(mtd); mtd->wp->flags |= PANE_REDRAW; } static int mode_tree_search_callback(__unused struct client *c, void *data, const char *s, __unused int done) { struct mode_tree_data *mtd = data; if (mtd->dead) return (0); free(mtd->search); if (s == NULL || *s == '\0') { mtd->search = NULL; return (0); } mtd->search = xstrdup(s); mode_tree_search_set(mtd); return (0); } static void mode_tree_search_free(void *data) { mode_tree_remove_ref(data); } static int mode_tree_filter_callback(__unused struct client *c, void *data, const char *s, __unused int done) { struct mode_tree_data *mtd = data; if (mtd->dead) return (0); if (mtd->filter != NULL) free(mtd->filter); if (s == NULL || *s == '\0') mtd->filter = NULL; else mtd->filter = xstrdup(s); mode_tree_build(mtd); mode_tree_draw(mtd); mtd->wp->flags |= PANE_REDRAW; return (0); } static void mode_tree_filter_free(void *data) { mode_tree_remove_ref(data); } static void mode_tree_menu_callback(__unused struct menu *menu, __unused u_int idx, key_code key, void *data) { struct mode_tree_menu *mtm = data; struct mode_tree_data *mtd = mtm->data; struct mode_tree_item *mti; if (mtd->dead || key == KEYC_NONE) goto out; if (mtm->line >= mtd->line_size) goto out; mti = mtd->line_list[mtm->line].item; if (mti->itemdata != mtm->itemdata) goto out; mtd->current = mtm->line; mtd->menucb (mtd->modedata, mtm->c, key); out: mode_tree_remove_ref(mtd); free(mtm); } static void mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x, u_int y, int outside) { struct mode_tree_item *mti; struct menu *menu; const struct menu_item *items; struct mode_tree_menu *mtm; char *title; u_int line; if (mtd->offset + y > mtd->line_size - 1) line = mtd->current; else line = mtd->offset + y; mti = mtd->line_list[line].item; if (!outside) { items = mtd->menu; xasprintf(&title, "#[align=centre]%s", mti->name); } else { items = mode_tree_menu_items; title = xstrdup(""); } menu = menu_create(title); menu_add_items(menu, items, NULL, NULL, NULL); free(title); mtm = xmalloc(sizeof *mtm); mtm->data = mtd; mtm->c = c; mtm->line = line; mtm->itemdata = mti->itemdata; mtd->references++; if (menu_display(menu, 0, NULL, x, y, c, NULL, mode_tree_menu_callback, mtm) != 0) menu_free(menu); } int mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, struct mouse_event *m, u_int *xp, u_int *yp) { struct mode_tree_line *line; struct mode_tree_item *current, *parent; u_int i, x, y; int choice; key_code tmp; if (KEYC_IS_MOUSE(*key) && m != NULL) { if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) { *key = KEYC_NONE; return (0); } if (xp != NULL) *xp = x; if (yp != NULL) *yp = y; if (x > mtd->width || y > mtd->height) { if (*key == KEYC_MOUSEDOWN3_PANE) mode_tree_display_menu(mtd, c, x, y, 1); if (!mtd->preview) *key = KEYC_NONE; return (0); } if (mtd->offset + y < mtd->line_size) { if (*key == KEYC_MOUSEDOWN1_PANE || *key == KEYC_MOUSEDOWN3_PANE || *key == KEYC_DOUBLECLICK1_PANE) mtd->current = mtd->offset + y; if (*key == KEYC_DOUBLECLICK1_PANE) *key = '\r'; else { if (*key == KEYC_MOUSEDOWN3_PANE) mode_tree_display_menu(mtd, c, x, y, 0); *key = KEYC_NONE; } } else { if (*key == KEYC_MOUSEDOWN3_PANE) mode_tree_display_menu(mtd, c, x, y, 0); *key = KEYC_NONE; } return (0); } line = &mtd->line_list[mtd->current]; current = line->item; choice = -1; if (*key >= '0' && *key <= '9') choice = (*key) - '0'; else if (((*key) & KEYC_MASK_MOD) == KEYC_ESCAPE) { tmp = (*key) & KEYC_MASK_KEY; if (tmp >= 'a' && tmp <= 'z') choice = 10 + (tmp - 'a'); } if (choice != -1) { if ((u_int)choice > mtd->line_size - 1) { *key = KEYC_NONE; return (0); } mtd->current = choice; *key = '\r'; return (0); } switch (*key) { case 'q': case '\033': /* Escape */ case '\007': /* C-g */ return (1); case KEYC_UP: case 'k': case KEYC_WHEELUP_PANE: case '\020': /* C-p */ mode_tree_up(mtd, 1); break; case KEYC_DOWN: case 'j': case KEYC_WHEELDOWN_PANE: case '\016': /* C-n */ mode_tree_down(mtd, 1); break; case 'g': case KEYC_PPAGE: case '\002': /* C-b */ for (i = 0; i < mtd->height; i++) { if (mtd->current == 0) break; mode_tree_up(mtd, 1); } break; case 'G': case KEYC_NPAGE: case '\006': /* C-f */ for (i = 0; i < mtd->height; i++) { if (mtd->current == mtd->line_size - 1) break; mode_tree_down(mtd, 1); } break; case KEYC_HOME: mtd->current = 0; mtd->offset = 0; break; case KEYC_END: mtd->current = mtd->line_size - 1; if (mtd->current > mtd->height - 1) mtd->offset = mtd->current - mtd->height + 1; else mtd->offset = 0; break; case 't': /* * Do not allow parents and children to both be tagged: untag * all parents and children of current. */ if (!current->tagged) { parent = current->parent; while (parent != NULL) { parent->tagged = 0; parent = parent->parent; } mode_tree_clear_tagged(¤t->children); current->tagged = 1; } else current->tagged = 0; if (m != NULL) mode_tree_down(mtd, 0); break; case 'T': for (i = 0; i < mtd->line_size; i++) mtd->line_list[i].item->tagged = 0; break; case '\024': /* C-t */ for (i = 0; i < mtd->line_size; i++) { if (mtd->line_list[i].item->parent == NULL) mtd->line_list[i].item->tagged = 1; else mtd->line_list[i].item->tagged = 0; } break; case 'O': mtd->sort_type++; if (mtd->sort_type == mtd->sort_size) mtd->sort_type = 0; mode_tree_build(mtd); break; case KEYC_LEFT: case 'h': case '-': if (line->flat || !current->expanded) current = current->parent; if (current == NULL) mode_tree_up(mtd, 0); else { current->expanded = 0; mtd->current = current->line; mode_tree_build(mtd); } break; case KEYC_RIGHT: case 'l': case '+': if (line->flat || current->expanded) mode_tree_down(mtd, 0); else if (!line->flat) { current->expanded = 1; mode_tree_build(mtd); } break; case '?': case '/': case '\023': /* C-s */ mtd->references++; status_prompt_set(c, "(search) ", "", mode_tree_search_callback, mode_tree_search_free, mtd, PROMPT_NOFORMAT); break; case 'n': mode_tree_search_set(mtd); break; case 'f': mtd->references++; status_prompt_set(c, "(filter) ", mtd->filter, mode_tree_filter_callback, mode_tree_filter_free, mtd, PROMPT_NOFORMAT); break; case 'v': mtd->preview = !mtd->preview; mode_tree_build(mtd); if (mtd->preview) mode_tree_check_selected(mtd); break; } return (0); } void mode_tree_run_command(struct client *c, struct cmd_find_state *fs, const char *template, const char *name) { struct cmdq_item *new_item; char *command; struct cmd_parse_result *pr; command = cmd_template_replace(template, name, 1); if (command == NULL || *command == '\0') { free(command); return; } pr = cmd_parse_from_string(command, NULL); switch (pr->status) { case CMD_PARSE_EMPTY: break; case CMD_PARSE_ERROR: if (c != NULL) { *pr->error = toupper((u_char)*pr->error); status_message_set(c, "%s", pr->error); } free(pr->error); break; case CMD_PARSE_SUCCESS: new_item = cmdq_get_command(pr->cmdlist, fs, NULL, 0); cmdq_append(c, new_item); cmd_list_free(pr->cmdlist); break; } free(command); } tmux-3.0a/names.c100644 001750 001750 00000007754 13462323664 0007511/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include "tmux.h" static void name_time_callback(int, short, void *); static int name_time_expired(struct window *, struct timeval *); static char *format_window_name(struct window *); static void name_time_callback(__unused int fd, __unused short events, void *arg) { struct window *w = arg; /* The event loop will call check_window_name for us on the way out. */ log_debug("@%u name timer expired", w->id); } static int name_time_expired(struct window *w, struct timeval *tv) { struct timeval offset; timersub(tv, &w->name_time, &offset); if (offset.tv_sec != 0 || offset.tv_usec > NAME_INTERVAL) return (0); return (NAME_INTERVAL - offset.tv_usec); } void check_window_name(struct window *w) { struct timeval tv, next; char *name; int left; if (w->active == NULL) return; if (!options_get_number(w->options, "automatic-rename")) return; if (~w->active->flags & PANE_CHANGED) { log_debug("@%u active pane not changed", w->id); return; } log_debug("@%u active pane changed", w->id); gettimeofday(&tv, NULL); left = name_time_expired(w, &tv); if (left != 0) { if (!event_initialized(&w->name_event)) evtimer_set(&w->name_event, name_time_callback, w); if (!evtimer_pending(&w->name_event, NULL)) { log_debug("@%u name timer queued (%d left)", w->id, left); timerclear(&next); next.tv_usec = left; event_add(&w->name_event, &next); } else { log_debug("@%u name timer already queued (%d left)", w->id, left); } return; } memcpy(&w->name_time, &tv, sizeof w->name_time); if (event_initialized(&w->name_event)) evtimer_del(&w->name_event); w->active->flags &= ~PANE_CHANGED; name = format_window_name(w); if (strcmp(name, w->name) != 0) { log_debug("@%u new name %s (was %s)", w->id, name, w->name); window_set_name(w, name); server_status_window(w); } else log_debug("@%u name not changed (still %s)", w->id, w->name); free(name); } char * default_window_name(struct window *w) { char *cmd, *s; cmd = cmd_stringify_argv(w->active->argc, w->active->argv); if (cmd != NULL && *cmd != '\0') s = parse_window_name(cmd); else s = parse_window_name(w->active->shell); free(cmd); return (s); } static char * format_window_name(struct window *w) { struct format_tree *ft; const char *fmt; char *name; ft = format_create(NULL, NULL, FORMAT_WINDOW|w->id, 0); format_defaults_window(ft, w); format_defaults_pane(ft, w->active); fmt = options_get_string(w->options, "automatic-rename-format"); name = format_expand(ft, fmt); format_free(ft); return (name); } 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 == '-') name++; if ((ptr = strchr(name, ' ')) != NULL) *ptr = '\0'; if (*name != '\0') { ptr = name + strlen(name) - 1; while (ptr > name && !isalnum((u_char)*ptr) && !ispunct((u_char)*ptr)) *ptr-- = '\0'; } if (*name == '/') name = basename(name); name = xstrdup(name); free(copy); return (name); } tmux-3.0a/notify.c100644 001750 001750 00000014751 13504653146 0007707/* $OpenBSD$ */ /* * Copyright (c) 2012 George Nachman * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN 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 notify_entry { const char *name; struct client *client; struct session *session; struct window *window; int pane; struct cmd_find_state fs; }; static void notify_hook_formats(struct cmdq_item *item, struct session *s, struct window *w, int pane) { if (s != NULL) { cmdq_format(item, "hook_session", "$%u", s->id); cmdq_format(item, "hook_session_name", "%s", s->name); } if (w != NULL) { cmdq_format(item, "hook_window", "@%u", w->id); cmdq_format(item, "hook_window_name", "%s", w->name); } if (pane != -1) cmdq_format(item, "hook_pane", "%%%d", pane); } static void notify_insert_hook(struct cmdq_item *item, struct notify_entry *ne) { struct cmd_find_state fs; struct options *oo; struct cmdq_item *new_item; struct session *s = ne->session; struct window *w = ne->window; struct options_entry *o; struct options_array_item *a; struct cmd_list *cmdlist; log_debug("%s: %s", __func__, ne->name); cmd_find_clear_state(&fs, 0); if (cmd_find_empty_state(&ne->fs) || !cmd_find_valid_state(&ne->fs)) cmd_find_from_nothing(&fs, 0); else cmd_find_copy_state(&fs, &ne->fs); if (fs.s == NULL) oo = global_s_options; else oo = fs.s->options; o = options_get(oo, ne->name); if (o == NULL) return; a = options_array_first(o); while (a != NULL) { cmdlist = options_array_item_value(a)->cmdlist; if (cmdlist == NULL) { a = options_array_next(a); continue; } new_item = cmdq_get_command(cmdlist, &fs, NULL, CMDQ_NOHOOKS); cmdq_format(new_item, "hook", "%s", ne->name); notify_hook_formats(new_item, s, w, ne->pane); cmdq_insert_after(item, new_item); item = new_item; a = options_array_next(a); } } static enum cmd_retval notify_callback(struct cmdq_item *item, void *data) { struct notify_entry *ne = data; log_debug("%s: %s", __func__, ne->name); if (strcmp(ne->name, "pane-mode-changed") == 0) control_notify_pane_mode_changed(ne->pane); if (strcmp(ne->name, "window-layout-changed") == 0) control_notify_window_layout_changed(ne->window); if (strcmp(ne->name, "window-pane-changed") == 0) control_notify_window_pane_changed(ne->window); if (strcmp(ne->name, "window-unlinked") == 0) control_notify_window_unlinked(ne->session, ne->window); if (strcmp(ne->name, "window-linked") == 0) control_notify_window_linked(ne->session, ne->window); if (strcmp(ne->name, "window-renamed") == 0) control_notify_window_renamed(ne->window); if (strcmp(ne->name, "client-session-changed") == 0) control_notify_client_session_changed(ne->client); if (strcmp(ne->name, "session-renamed") == 0) control_notify_session_renamed(ne->session); if (strcmp(ne->name, "session-created") == 0) control_notify_session_created(ne->session); if (strcmp(ne->name, "session-closed") == 0) control_notify_session_closed(ne->session); if (strcmp(ne->name, "session-window-changed") == 0) control_notify_session_window_changed(ne->session); notify_insert_hook(item, ne); if (ne->client != NULL) server_client_unref(ne->client); if (ne->session != NULL) session_remove_ref(ne->session, __func__); if (ne->window != NULL) window_remove_ref(ne->window, __func__); if (ne->fs.s != NULL) session_remove_ref(ne->fs.s, __func__); free((void *)ne->name); free(ne); return (CMD_RETURN_NORMAL); } static void notify_add(const char *name, struct cmd_find_state *fs, struct client *c, struct session *s, struct window *w, struct window_pane *wp) { struct notify_entry *ne; struct cmdq_item *new_item; ne = xcalloc(1, sizeof *ne); ne->name = xstrdup(name); ne->client = c; ne->session = s; ne->window = w; if (wp != NULL) ne->pane = wp->id; else ne->pane = -1; if (c != NULL) c->references++; if (s != NULL) session_add_ref(s, __func__); if (w != NULL) window_add_ref(w, __func__); cmd_find_copy_state(&ne->fs, fs); if (ne->fs.s != NULL) /* cmd_find_valid_state needs session */ session_add_ref(ne->fs.s, __func__); new_item = cmdq_get_callback(notify_callback, ne); cmdq_append(NULL, new_item); } void notify_hook(struct cmdq_item *item, const char *name) { struct notify_entry ne; memset(&ne, 0, sizeof ne); ne.name = name; cmd_find_copy_state(&ne.fs, &item->target); ne.client = item->client; ne.session = item->target.s; ne.window = item->target.w; ne.pane = item->target.wp->id; notify_insert_hook(item, &ne); } void notify_input(struct window_pane *wp, const u_char *buf, size_t len) { struct client *c; TAILQ_FOREACH(c, &clients, entry) { if (c->flags & CLIENT_CONTROL) control_notify_input(c, wp, buf, len); } } void notify_client(const char *name, struct client *c) { struct cmd_find_state fs; cmd_find_from_client(&fs, c, 0); notify_add(name, &fs, c, NULL, NULL, NULL); } void notify_session(const char *name, struct session *s) { struct cmd_find_state fs; if (session_alive(s)) cmd_find_from_session(&fs, s, 0); else cmd_find_from_nothing(&fs, 0); notify_add(name, &fs, NULL, s, NULL, NULL); } void notify_winlink(const char *name, struct winlink *wl) { struct cmd_find_state fs; cmd_find_from_winlink(&fs, wl, 0); notify_add(name, &fs, NULL, wl->session, wl->window, NULL); } void notify_session_window(const char *name, struct session *s, struct window *w) { struct cmd_find_state fs; cmd_find_from_session_window(&fs, s, w, 0); notify_add(name, &fs, NULL, s, w, NULL); } void notify_window(const char *name, struct window *w) { struct cmd_find_state fs; cmd_find_from_window(&fs, w, 0); notify_add(name, &fs, NULL, NULL, w, NULL); } void notify_pane(const char *name, struct window_pane *wp) { struct cmd_find_state fs; cmd_find_from_pane(&fs, wp, 0); notify_add(name, &fs, NULL, NULL, NULL, wp); } tmux-3.0a/options-table.c100644 001750 001750 00000053652 13570677043 0011167/* $OpenBSD$ */ /* * 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 look up the real type when the * user sets an option or its value needs to be shown. */ /* Choice option type lists. */ static const char *options_table_mode_keys_list[] = { "emacs", "vi", NULL }; static const char *options_table_clock_mode_style_list[] = { "12", "24", NULL }; static const char *options_table_status_list[] = { "off", "on", "2", "3", "4", "5", NULL }; static const char *options_table_status_keys_list[] = { "emacs", "vi", NULL }; static const char *options_table_status_justify_list[] = { "left", "centre", "right", NULL }; static const char *options_table_status_position_list[] = { "top", "bottom", NULL }; static const char *options_table_bell_action_list[] = { "none", "any", "current", "other", NULL }; static const char *options_table_visual_bell_list[] = { "off", "on", "both", NULL }; static const char *options_table_pane_status_list[] = { "off", "top", "bottom", NULL }; static const char *options_table_set_clipboard_list[] = { "off", "external", "on", NULL }; static const char *options_table_window_size_list[] = { "largest", "smallest", "manual", NULL }; /* Status line format. */ #define OPTIONS_TABLE_STATUS_FORMAT1 \ "#[align=left range=left #{status-left-style}]" \ "#{T;=/#{status-left-length}:status-left}#[norange default]" \ "#[list=on align=#{status-justify}]" \ "#[list=left-marker]<#[list=right-marker]>#[list=on]" \ "#{W:" \ "#[range=window|#{window_index} " \ "#{window-status-style}" \ "#{?#{&&:#{window_last_flag}," \ "#{!=:#{window-status-last-style},default}}, " \ "#{window-status-last-style}," \ "}" \ "#{?#{&&:#{window_bell_flag}," \ "#{!=:#{window-status-bell-style},default}}, " \ "#{window-status-bell-style}," \ "#{?#{&&:#{||:#{window_activity_flag}," \ "#{window_silence_flag}}," \ "#{!=:" \ "#{window-status-activity-style}," \ "default}}, " \ "#{window-status-activity-style}," \ "}" \ "}" \ "]" \ "#{T:window-status-format}" \ "#[norange default]" \ "#{?window_end_flag,,#{window-status-separator}}" \ "," \ "#[range=window|#{window_index} list=focus " \ "#{?#{!=:#{window-status-current-style},default}," \ "#{window-status-current-style}," \ "#{window-status-style}" \ "}" \ "#{?#{&&:#{window_last_flag}," \ "#{!=:#{window-status-last-style},default}}, " \ "#{window-status-last-style}," \ "}" \ "#{?#{&&:#{window_bell_flag}," \ "#{!=:#{window-status-bell-style},default}}, " \ "#{window-status-bell-style}," \ "#{?#{&&:#{||:#{window_activity_flag}," \ "#{window_silence_flag}}," \ "#{!=:" \ "#{window-status-activity-style}," \ "default}}, " \ "#{window-status-activity-style}," \ "}" \ "}" \ "]" \ "#{T:window-status-current-format}" \ "#[norange list=on default]" \ "#{?window_end_flag,,#{window-status-separator}}" \ "}" \ "#[nolist align=right range=right #{status-right-style}]" \ "#{T;=/#{status-right-length}:status-right}#[norange default]" #define OPTIONS_TABLE_STATUS_FORMAT2 \ "#[align=centre]#{P:#{?pane_active,#[reverse],}" \ "#{pane_index}[#{pane_width}x#{pane_height}]#[default] }" static const char *options_table_status_format_default[] = { OPTIONS_TABLE_STATUS_FORMAT1, OPTIONS_TABLE_STATUS_FORMAT2, NULL }; /* Helper for hook options. */ #define OPTIONS_TABLE_HOOK(hook_name, default_value) \ { .name = hook_name, \ .type = OPTIONS_TABLE_COMMAND, \ .scope = OPTIONS_TABLE_SESSION, \ .flags = OPTIONS_TABLE_IS_ARRAY|OPTIONS_TABLE_IS_HOOK, \ .default_str = default_value, \ .separator = "" \ } /* Top-level options. */ const struct options_table_entry options_table[] = { /* Server options. */ { .name = "buffer-limit", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SERVER, .minimum = 1, .maximum = INT_MAX, .default_num = 50 }, { .name = "command-alias", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, .flags = OPTIONS_TABLE_IS_ARRAY, .default_str = "split-pane=split-window," "splitp=split-window," "server-info=show-messages -JT," "info=show-messages -JT," "choose-window=choose-tree -w," "choose-session=choose-tree -s", .separator = "," }, { .name = "default-terminal", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, .default_str = "screen" }, { .name = "escape-time", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SERVER, .minimum = 0, .maximum = INT_MAX, .default_num = 500 }, { .name = "exit-empty", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SERVER, .default_num = 1 }, { .name = "exit-unattached", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SERVER, .default_num = 0 }, { .name = "focus-events", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SERVER, .default_num = 0 }, { .name = "history-file", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, .default_str = "" }, { .name = "message-limit", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SERVER, .minimum = 0, .maximum = INT_MAX, .default_num = 100 }, { .name = "set-clipboard", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SERVER, .choices = options_table_set_clipboard_list, .default_num = 1 }, { .name = "terminal-overrides", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, .flags = OPTIONS_TABLE_IS_ARRAY, .default_str = "xterm*:XT:Ms=\\E]52;%p1%s;%p2%s\\007" ":Cs=\\E]12;%p1%s\\007:Cr=\\E]112\\007" ":Ss=\\E[%p1%d q:Se=\\E[2 q,screen*:XT", .separator = "," }, { .name = "user-keys", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, .flags = OPTIONS_TABLE_IS_ARRAY, .default_str = "", .separator = "," }, /* Session options. */ { .name = "activity-action", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_bell_action_list, .default_num = ALERT_OTHER }, { .name = "assume-paste-time", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, .default_num = 1, }, { .name = "base-index", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, .default_num = 0 }, { .name = "bell-action", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_bell_action_list, .default_num = ALERT_ANY }, { .name = "default-command", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, .default_str = "" }, { .name = "default-shell", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, .default_str = _PATH_BSHELL }, { .name = "default-size", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, .pattern = "[0-9]*x[0-9]*", .default_str = "80x24" }, { .name = "destroy-unattached", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "detach-on-destroy", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SESSION, .default_num = 1 }, { .name = "display-panes-active-colour", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_SESSION, .default_num = 1 }, { .name = "display-panes-colour", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_SESSION, .default_num = 4 }, { .name = "display-panes-time", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SESSION, .minimum = 1, .maximum = INT_MAX, .default_num = 1000 }, { .name = "display-time", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, .default_num = 750 }, { .name = "history-limit", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, .default_num = 2000 }, { .name = "key-table", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, .default_str = "root" }, { .name = "lock-after-time", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, .default_num = 0 }, { .name = "lock-command", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, .default_str = "lock -np" }, { .name = "message-command-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_SESSION, .default_str = "bg=black,fg=yellow" }, { .name = "message-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_SESSION, .default_str = "bg=yellow,fg=black" }, { .name = "mouse", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "prefix", .type = OPTIONS_TABLE_KEY, .scope = OPTIONS_TABLE_SESSION, .default_num = '\002', }, { .name = "prefix2", .type = OPTIONS_TABLE_KEY, .scope = OPTIONS_TABLE_SESSION, .default_num = KEYC_NONE, }, { .name = "renumber-windows", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "repeat-time", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = SHRT_MAX, .default_num = 500 }, { .name = "set-titles", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "set-titles-string", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, .default_str = "#S:#I:#W - \"#T\" #{session_alerts}" }, { .name = "silence-action", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_bell_action_list, .default_num = ALERT_OTHER }, { .name = "status", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_status_list, .default_num = 1 }, { .name = "status-bg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_SESSION, .default_num = 2, }, { .name = "status-fg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_SESSION, .default_num = 0, }, { .name = "status-format", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, .flags = OPTIONS_TABLE_IS_ARRAY, .default_arr = options_table_status_format_default, }, { .name = "status-interval", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, .default_num = 15 }, { .name = "status-justify", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_status_justify_list, .default_num = 0 }, { .name = "status-keys", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_status_keys_list, .default_num = MODEKEY_EMACS }, { .name = "status-left", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, .default_str = "[#S] " }, { .name = "status-left-length", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = SHRT_MAX, .default_num = 10 }, { .name = "status-left-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_SESSION, .default_str = "default" }, { .name = "status-position", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_status_position_list, .default_num = 1 }, { .name = "status-right", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, .default_str = "#{?window_bigger," "[#{window_offset_x}#,#{window_offset_y}] ,}" "\"#{=21:pane_title}\" %H:%M %d-%b-%y" }, { .name = "status-right-length", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = SHRT_MAX, .default_num = 40 }, { .name = "status-right-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_SESSION, .default_str = "default" }, { .name = "status-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_SESSION, .default_str = "bg=green,fg=black" }, { .name = "update-environment", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, .flags = OPTIONS_TABLE_IS_ARRAY, .default_str = "DISPLAY KRB5CCNAME SSH_ASKPASS SSH_AUTH_SOCK " "SSH_AGENT_PID SSH_CONNECTION WINDOWID XAUTHORITY" }, { .name = "visual-activity", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_visual_bell_list, .default_num = VISUAL_OFF }, { .name = "visual-bell", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_visual_bell_list, .default_num = VISUAL_OFF }, { .name = "visual-silence", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_visual_bell_list, .default_num = VISUAL_OFF }, { .name = "word-separators", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, .default_str = " " }, /* Window options. */ { .name = "aggressive-resize", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, .default_num = 0 }, { .name = "allow-rename", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .default_num = 0 }, { .name = "alternate-screen", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .default_num = 1 }, { .name = "automatic-rename", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, .default_num = 1 }, { .name = "automatic-rename-format", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, .default_str = "#{?pane_in_mode,[tmux],#{pane_current_command}}" "#{?pane_dead,[dead],}" }, { .name = "clock-mode-colour", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_WINDOW, .default_num = 4 }, { .name = "clock-mode-style", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_WINDOW, .choices = options_table_clock_mode_style_list, .default_num = 1 }, { .name = "main-pane-height", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_WINDOW, .minimum = 1, .maximum = INT_MAX, .default_num = 24 }, { .name = "main-pane-width", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_WINDOW, .minimum = 1, .maximum = INT_MAX, .default_num = 80 }, { .name = "mode-keys", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_WINDOW, .choices = options_table_mode_keys_list, .default_num = MODEKEY_EMACS }, { .name = "mode-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_WINDOW, .default_str = "bg=yellow,fg=black" }, { .name = "monitor-activity", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, .default_num = 0 }, { .name = "monitor-bell", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, .default_num = 1 }, { .name = "monitor-silence", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_WINDOW, .minimum = 0, .maximum = INT_MAX, .default_num = 0 }, { .name = "other-pane-height", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_WINDOW, .minimum = 0, .maximum = INT_MAX, .default_num = 0 }, { .name = "other-pane-width", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_WINDOW, .minimum = 0, .maximum = INT_MAX, .default_num = 0 }, { .name = "pane-active-border-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_WINDOW, .default_str = "fg=green" }, { .name = "pane-base-index", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_WINDOW, .minimum = 0, .maximum = USHRT_MAX, .default_num = 0 }, { .name = "pane-border-format", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, .default_str = "#{?pane_active,#[reverse],}#{pane_index}#[default] " "\"#{pane_title}\"" }, { .name = "pane-border-status", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_WINDOW, .choices = options_table_pane_status_list, .default_num = PANE_STATUS_OFF }, { .name = "pane-border-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_WINDOW, .default_str = "default" }, { .name = "remain-on-exit", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .default_num = 0 }, { .name = "synchronize-panes", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, .default_num = 0 }, { .name = "window-active-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .default_str = "default" }, { .name = "window-size", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_WINDOW, .choices = options_table_window_size_list, .default_num = WINDOW_SIZE_SMALLEST }, { .name = "window-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .default_str = "default" }, { .name = "window-status-activity-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_WINDOW, .default_str = "reverse" }, { .name = "window-status-bell-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_WINDOW, .default_str = "reverse" }, { .name = "window-status-current-format", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, .default_str = "#I:#W#{?window_flags,#{window_flags}, }" }, { .name = "window-status-current-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_WINDOW, .default_str = "default" }, { .name = "window-status-format", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, .default_str = "#I:#W#{?window_flags,#{window_flags}, }" }, { .name = "window-status-last-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_WINDOW, .default_str = "default" }, { .name = "window-status-separator", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, .default_str = " " }, { .name = "window-status-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_WINDOW, .default_str = "default" }, { .name = "wrap-search", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, .default_num = 1 }, { .name = "xterm-keys", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, .default_num = 1 }, /* Hook options. */ OPTIONS_TABLE_HOOK("after-bind-key", ""), OPTIONS_TABLE_HOOK("after-capture-pane", ""), OPTIONS_TABLE_HOOK("after-copy-mode", ""), OPTIONS_TABLE_HOOK("after-display-message", ""), OPTIONS_TABLE_HOOK("after-display-panes", ""), OPTIONS_TABLE_HOOK("after-kill-pane", ""), OPTIONS_TABLE_HOOK("after-list-buffers", ""), OPTIONS_TABLE_HOOK("after-list-clients", ""), OPTIONS_TABLE_HOOK("after-list-keys", ""), OPTIONS_TABLE_HOOK("after-list-panes", ""), OPTIONS_TABLE_HOOK("after-list-sessions", ""), OPTIONS_TABLE_HOOK("after-list-windows", ""), OPTIONS_TABLE_HOOK("after-load-buffer", ""), OPTIONS_TABLE_HOOK("after-lock-server", ""), OPTIONS_TABLE_HOOK("after-new-session", ""), OPTIONS_TABLE_HOOK("after-new-window", ""), OPTIONS_TABLE_HOOK("after-paste-buffer", ""), OPTIONS_TABLE_HOOK("after-pipe-pane", ""), OPTIONS_TABLE_HOOK("after-queue", ""), OPTIONS_TABLE_HOOK("after-refresh-client", ""), OPTIONS_TABLE_HOOK("after-rename-session", ""), OPTIONS_TABLE_HOOK("after-rename-window", ""), OPTIONS_TABLE_HOOK("after-resize-pane", ""), OPTIONS_TABLE_HOOK("after-resize-window", ""), OPTIONS_TABLE_HOOK("after-save-buffer", ""), OPTIONS_TABLE_HOOK("after-select-layout", ""), OPTIONS_TABLE_HOOK("after-select-pane", ""), OPTIONS_TABLE_HOOK("after-select-window", ""), OPTIONS_TABLE_HOOK("after-send-keys", ""), OPTIONS_TABLE_HOOK("after-set-buffer", ""), OPTIONS_TABLE_HOOK("after-set-environment", ""), OPTIONS_TABLE_HOOK("after-set-hook", ""), OPTIONS_TABLE_HOOK("after-set-option", ""), OPTIONS_TABLE_HOOK("after-show-environment", ""), OPTIONS_TABLE_HOOK("after-show-messages", ""), OPTIONS_TABLE_HOOK("after-show-options", ""), OPTIONS_TABLE_HOOK("after-split-window", ""), OPTIONS_TABLE_HOOK("after-unbind-key", ""), OPTIONS_TABLE_HOOK("alert-activity", ""), OPTIONS_TABLE_HOOK("alert-bell", ""), OPTIONS_TABLE_HOOK("alert-silence", ""), OPTIONS_TABLE_HOOK("client-attached", ""), OPTIONS_TABLE_HOOK("client-detached", ""), OPTIONS_TABLE_HOOK("client-resized", ""), OPTIONS_TABLE_HOOK("client-session-changed", ""), OPTIONS_TABLE_HOOK("pane-died", ""), OPTIONS_TABLE_HOOK("pane-exited", ""), OPTIONS_TABLE_HOOK("pane-focus-in", ""), OPTIONS_TABLE_HOOK("pane-focus-out", ""), OPTIONS_TABLE_HOOK("pane-mode-changed", ""), OPTIONS_TABLE_HOOK("pane-set-clipboard", ""), OPTIONS_TABLE_HOOK("session-closed", ""), OPTIONS_TABLE_HOOK("session-created", ""), OPTIONS_TABLE_HOOK("session-renamed", ""), OPTIONS_TABLE_HOOK("session-window-changed", ""), OPTIONS_TABLE_HOOK("window-layout-changed", ""), OPTIONS_TABLE_HOOK("window-linked", ""), OPTIONS_TABLE_HOOK("window-pane-changed", ""), OPTIONS_TABLE_HOOK("window-renamed", ""), OPTIONS_TABLE_HOOK("window-unlinked", ""), { .name = NULL } }; tmux-3.0a/options.c100644 001750 001750 00000045134 13570677043 0010076/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include "tmux.h" /* * Option handling; each option has a name, type and value and is stored in * a red-black tree. */ struct options_array_item { u_int index; union options_value value; RB_ENTRY(options_array_item) entry; }; static int options_array_cmp(struct options_array_item *a1, struct options_array_item *a2) { if (a1->index < a2->index) return (-1); if (a1->index > a2->index) return (1); return (0); } RB_GENERATE_STATIC(options_array, options_array_item, entry, options_array_cmp); struct options_entry { struct options *owner; const char *name; const struct options_table_entry *tableentry; union options_value value; RB_ENTRY(options_entry) entry; }; struct options { RB_HEAD(options_tree, options_entry) tree; struct options *parent; }; static struct options_entry *options_add(struct options *, const char *); #define OPTIONS_IS_STRING(o) \ ((o)->tableentry == NULL || \ (o)->tableentry->type == OPTIONS_TABLE_STRING) #define OPTIONS_IS_NUMBER(o) \ ((o)->tableentry != NULL && \ ((o)->tableentry->type == OPTIONS_TABLE_NUMBER || \ (o)->tableentry->type == OPTIONS_TABLE_KEY || \ (o)->tableentry->type == OPTIONS_TABLE_COLOUR || \ (o)->tableentry->type == OPTIONS_TABLE_FLAG || \ (o)->tableentry->type == OPTIONS_TABLE_CHOICE)) #define OPTIONS_IS_STYLE(o) \ ((o)->tableentry != NULL && \ (o)->tableentry->type == OPTIONS_TABLE_STYLE) #define OPTIONS_IS_COMMAND(o) \ ((o)->tableentry != NULL && \ (o)->tableentry->type == OPTIONS_TABLE_COMMAND) #define OPTIONS_IS_ARRAY(o) \ ((o)->tableentry != NULL && \ ((o)->tableentry->flags & OPTIONS_TABLE_IS_ARRAY)) static int options_cmp(struct options_entry *, struct options_entry *); RB_GENERATE_STATIC(options_tree, options_entry, entry, options_cmp); static int options_cmp(struct options_entry *lhs, struct options_entry *rhs) { return (strcmp(lhs->name, rhs->name)); } static const struct options_table_entry * options_parent_table_entry(struct options *oo, const char *s) { struct options_entry *o; if (oo->parent == NULL) fatalx("no parent options for %s", s); o = options_get(oo->parent, s); if (o == NULL) fatalx("%s not in parent options", s); return (o->tableentry); } static void options_value_free(struct options_entry *o, union options_value *ov) { if (OPTIONS_IS_STRING(o)) free(ov->string); if (OPTIONS_IS_COMMAND(o) && ov->cmdlist != NULL) cmd_list_free(ov->cmdlist); } static char * options_value_tostring(struct options_entry *o, union options_value *ov, int numeric) { char *s; if (OPTIONS_IS_COMMAND(o)) return (cmd_list_print(ov->cmdlist, 0)); if (OPTIONS_IS_STYLE(o)) return (xstrdup(style_tostring(&ov->style))); if (OPTIONS_IS_NUMBER(o)) { switch (o->tableentry->type) { case OPTIONS_TABLE_NUMBER: xasprintf(&s, "%lld", ov->number); break; case OPTIONS_TABLE_KEY: s = xstrdup(key_string_lookup_key(ov->number)); break; case OPTIONS_TABLE_COLOUR: s = xstrdup(colour_tostring(ov->number)); break; case OPTIONS_TABLE_FLAG: if (numeric) xasprintf(&s, "%lld", ov->number); else s = xstrdup(ov->number ? "on" : "off"); break; case OPTIONS_TABLE_CHOICE: s = xstrdup(o->tableentry->choices[ov->number]); break; case OPTIONS_TABLE_STRING: case OPTIONS_TABLE_STYLE: case OPTIONS_TABLE_COMMAND: fatalx("not a number option type"); } return (s); } if (OPTIONS_IS_STRING(o)) return (xstrdup(ov->string)); return (xstrdup("")); } struct options * options_create(struct options *parent) { struct options *oo; oo = xcalloc(1, sizeof *oo); RB_INIT(&oo->tree); oo->parent = parent; return (oo); } void options_free(struct options *oo) { struct options_entry *o, *tmp; RB_FOREACH_SAFE(o, options_tree, &oo->tree, tmp) options_remove(o); free(oo); } void options_set_parent(struct options *oo, struct options *parent) { oo->parent = parent; } struct options_entry * options_first(struct options *oo) { return (RB_MIN(options_tree, &oo->tree)); } struct options_entry * options_next(struct options_entry *o) { return (RB_NEXT(options_tree, &oo->tree, o)); } struct options_entry * options_get_only(struct options *oo, const char *name) { struct options_entry o; o.name = name; return (RB_FIND(options_tree, &oo->tree, &o)); } struct options_entry * options_get(struct options *oo, const char *name) { struct options_entry *o; o = options_get_only(oo, name); while (o == NULL) { oo = oo->parent; if (oo == NULL) break; o = options_get_only(oo, name); } return (o); } struct options_entry * options_empty(struct options *oo, const struct options_table_entry *oe) { struct options_entry *o; o = options_add(oo, oe->name); o->tableentry = oe; if (oe->flags & OPTIONS_TABLE_IS_ARRAY) RB_INIT(&o->value.array); return (o); } struct options_entry * options_default(struct options *oo, const struct options_table_entry *oe) { struct options_entry *o; union options_value *ov; u_int i; o = options_empty(oo, oe); ov = &o->value; if (oe->flags & OPTIONS_TABLE_IS_ARRAY) { if (oe->default_arr == NULL) { options_array_assign(o, oe->default_str, NULL); return (o); } for (i = 0; oe->default_arr[i] != NULL; i++) options_array_set(o, i, oe->default_arr[i], 0, NULL); return (o); } switch (oe->type) { case OPTIONS_TABLE_STRING: ov->string = xstrdup(oe->default_str); break; case OPTIONS_TABLE_STYLE: style_set(&ov->style, &grid_default_cell); style_parse(&ov->style, &grid_default_cell, oe->default_str); break; default: ov->number = oe->default_num; break; } return (o); } static struct options_entry * options_add(struct options *oo, const char *name) { struct options_entry *o; o = options_get_only(oo, name); if (o != NULL) options_remove(o); o = xcalloc(1, sizeof *o); o->owner = oo; o->name = xstrdup(name); RB_INSERT(options_tree, &oo->tree, o); return (o); } void options_remove(struct options_entry *o) { struct options *oo = o->owner; if (OPTIONS_IS_ARRAY(o)) options_array_clear(o); else options_value_free(o, &o->value); RB_REMOVE(options_tree, &oo->tree, o); free(o); } const char * options_name(struct options_entry *o) { return (o->name); } const struct options_table_entry * options_table_entry(struct options_entry *o) { return (o->tableentry); } static struct options_array_item * options_array_item(struct options_entry *o, u_int idx) { struct options_array_item a; a.index = idx; return (RB_FIND(options_array, &o->value.array, &a)); } static void options_array_free(struct options_entry *o, struct options_array_item *a) { options_value_free(o, &a->value); RB_REMOVE(options_array, &o->value.array, a); free(a); } void options_array_clear(struct options_entry *o) { struct options_array_item *a, *a1; if (!OPTIONS_IS_ARRAY(o)) return; RB_FOREACH_SAFE(a, options_array, &o->value.array, a1) options_array_free(o, a); } union options_value * options_array_get(struct options_entry *o, u_int idx) { struct options_array_item *a; if (!OPTIONS_IS_ARRAY(o)) return (NULL); a = options_array_item(o, idx); if (a == NULL) return (NULL); return (&a->value); } int options_array_set(struct options_entry *o, u_int idx, const char *value, int append, char **cause) { struct options_array_item *a; char *new; struct cmd_parse_result *pr; if (!OPTIONS_IS_ARRAY(o)) { if (cause != NULL) *cause = xstrdup("not an array"); return (-1); } if (OPTIONS_IS_COMMAND(o) && value != NULL) { pr = cmd_parse_from_string(value, NULL); switch (pr->status) { case CMD_PARSE_EMPTY: if (cause != NULL) *cause = xstrdup("empty command"); return (-1); case CMD_PARSE_ERROR: if (cause != NULL) *cause = pr->error; else free(pr->error); return (-1); case CMD_PARSE_SUCCESS: break; } } a = options_array_item(o, idx); if (value == NULL) { if (a != NULL) options_array_free(o, a); return (0); } if (OPTIONS_IS_STRING(o)) { if (a != NULL && append) xasprintf(&new, "%s%s", a->value.string, value); else new = xstrdup(value); } if (a == NULL) { a = xcalloc(1, sizeof *a); a->index = idx; RB_INSERT(options_array, &o->value.array, a); } else options_value_free(o, &a->value); if (OPTIONS_IS_STRING(o)) a->value.string = new; else if (OPTIONS_IS_COMMAND(o)) a->value.cmdlist = pr->cmdlist; return (0); } int options_array_assign(struct options_entry *o, const char *s, char **cause) { const char *separator; char *copy, *next, *string; u_int i; separator = o->tableentry->separator; if (separator == NULL) separator = " ,"; if (*separator == '\0') { if (*s == '\0') return (0); for (i = 0; i < UINT_MAX; i++) { if (options_array_item(o, i) == NULL) break; } return (options_array_set(o, i, s, 0, cause)); } if (*s == '\0') return (0); copy = string = xstrdup(s); while ((next = strsep(&string, separator)) != NULL) { if (*next == '\0') continue; for (i = 0; i < UINT_MAX; i++) { if (options_array_item(o, i) == NULL) break; } if (i == UINT_MAX) break; if (options_array_set(o, i, next, 0, cause) != 0) { free(copy); return (-1); } } free(copy); return (0); } struct options_array_item * options_array_first(struct options_entry *o) { if (!OPTIONS_IS_ARRAY(o)) return (NULL); return (RB_MIN(options_array, &o->value.array)); } struct options_array_item * options_array_next(struct options_array_item *a) { return (RB_NEXT(options_array, &o->value.array, a)); } u_int options_array_item_index(struct options_array_item *a) { return (a->index); } union options_value * options_array_item_value(struct options_array_item *a) { return (&a->value); } int options_isarray(struct options_entry *o) { return (OPTIONS_IS_ARRAY(o)); } int options_isstring(struct options_entry *o) { return (OPTIONS_IS_STRING(o)); } char * options_tostring(struct options_entry *o, int idx, int numeric) { struct options_array_item *a; if (OPTIONS_IS_ARRAY(o)) { if (idx == -1) return (xstrdup("")); a = options_array_item(o, idx); if (a == NULL) return (xstrdup("")); return (options_value_tostring(o, &a->value, numeric)); } return (options_value_tostring(o, &o->value, numeric)); } char * options_parse(const char *name, int *idx) { char *copy, *cp, *end; if (*name == '\0') return (NULL); copy = xstrdup(name); if ((cp = strchr(copy, '[')) == NULL) { *idx = -1; return (copy); } end = strchr(cp + 1, ']'); if (end == NULL || end[1] != '\0' || !isdigit((u_char)end[-1])) { free(copy); return (NULL); } if (sscanf(cp, "[%d]", idx) != 1 || *idx < 0) { free(copy); return (NULL); } *cp = '\0'; return (copy); } struct options_entry * options_parse_get(struct options *oo, const char *s, int *idx, int only) { struct options_entry *o; char *name; name = options_parse(s, idx); if (name == NULL) return (NULL); if (only) o = options_get_only(oo, name); else o = options_get(oo, name); free(name); return (o); } char * options_match(const char *s, int *idx, int *ambiguous) { const struct options_table_entry *oe, *found; char *name; size_t namelen; name = options_parse(s, idx); if (name == NULL) return (NULL); namelen = strlen(name); if (*name == '@') { *ambiguous = 0; return (name); } found = NULL; for (oe = options_table; oe->name != NULL; oe++) { if (strcmp(oe->name, name) == 0) { found = oe; break; } if (strncmp(oe->name, name, namelen) == 0) { if (found != NULL) { *ambiguous = 1; free(name); return (NULL); } found = oe; } } free(name); if (found == NULL) { *ambiguous = 0; return (NULL); } return (xstrdup(found->name)); } struct options_entry * options_match_get(struct options *oo, const char *s, int *idx, int only, int* ambiguous) { char *name; struct options_entry *o; name = options_match(s, idx, ambiguous); if (name == NULL) return (NULL); *ambiguous = 0; if (only) o = options_get_only(oo, name); else o = options_get(oo, name); free(name); return (o); } const char * options_get_string(struct options *oo, const char *name) { struct options_entry *o; o = options_get(oo, name); if (o == NULL) fatalx("missing option %s", name); if (!OPTIONS_IS_STRING(o)) fatalx("option %s is not a string", name); return (o->value.string); } long long options_get_number(struct options *oo, const char *name) { struct options_entry *o; o = options_get(oo, name); if (o == NULL) fatalx("missing option %s", name); if (!OPTIONS_IS_NUMBER(o)) fatalx("option %s is not a number", name); return (o->value.number); } struct style * options_get_style(struct options *oo, const char *name) { struct options_entry *o; o = options_get(oo, name); if (o == NULL) fatalx("missing option %s", name); if (!OPTIONS_IS_STYLE(o)) fatalx("option %s is not a style", name); return (&o->value.style); } struct options_entry * options_set_string(struct options *oo, const char *name, int append, const char *fmt, ...) { struct options_entry *o; va_list ap; char *s, *value; va_start(ap, fmt); xvasprintf(&s, fmt, ap); va_end(ap); o = options_get_only(oo, name); if (o != NULL && append && OPTIONS_IS_STRING(o)) { xasprintf(&value, "%s%s", o->value.string, s); free(s); } else value = s; if (o == NULL && *name == '@') o = options_add(oo, name); else if (o == NULL) { o = options_default(oo, options_parent_table_entry(oo, name)); if (o == NULL) return (NULL); } if (!OPTIONS_IS_STRING(o)) fatalx("option %s is not a string", name); free(o->value.string); o->value.string = value; return (o); } struct options_entry * options_set_number(struct options *oo, const char *name, long long value) { struct options_entry *o; if (*name == '@') fatalx("user option %s must be a string", name); o = options_get_only(oo, name); if (o == NULL) { o = options_default(oo, options_parent_table_entry(oo, name)); if (o == NULL) return (NULL); } if (!OPTIONS_IS_NUMBER(o)) fatalx("option %s is not a number", name); o->value.number = value; return (o); } struct options_entry * options_set_style(struct options *oo, const char *name, int append, const char *value) { struct options_entry *o; struct style sy; if (*name == '@') fatalx("user option %s must be a string", name); o = options_get_only(oo, name); if (o != NULL && append && OPTIONS_IS_STYLE(o)) style_copy(&sy, &o->value.style); else style_set(&sy, &grid_default_cell); if (style_parse(&sy, &grid_default_cell, value) == -1) return (NULL); if (o == NULL) { o = options_default(oo, options_parent_table_entry(oo, name)); if (o == NULL) return (NULL); } if (!OPTIONS_IS_STYLE(o)) fatalx("option %s is not a style", name); style_copy(&o->value.style, &sy); return (o); } int options_scope_from_name(struct args *args, int window, const char *name, struct cmd_find_state *fs, struct options **oo, char **cause) { struct session *s = fs->s; struct winlink *wl = fs->wl; struct window_pane *wp = fs->wp; const char *target = args_get(args, 't'); const struct options_table_entry *oe; int scope = OPTIONS_TABLE_NONE; if (*name == '@') return (options_scope_from_flags(args, window, fs, oo, cause)); for (oe = options_table; oe->name != NULL; oe++) { if (strcmp(oe->name, name) == 0) break; } if (oe->name == NULL) { xasprintf(cause, "unknown option: %s", name); return (OPTIONS_TABLE_NONE); } switch (oe->scope) { case OPTIONS_TABLE_SERVER: *oo = global_options; scope = OPTIONS_TABLE_SERVER; break; case OPTIONS_TABLE_SESSION: if (args_has(args, 'g')) { *oo = global_s_options; scope = OPTIONS_TABLE_SESSION; } else if (s == NULL && target != NULL) xasprintf(cause, "no such session: %s", target); else if (s == NULL) xasprintf(cause, "no current session"); else { *oo = s->options; scope = OPTIONS_TABLE_SESSION; } break; case OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE: if (args_has(args, 'p')) { if (wp == NULL && target != NULL) xasprintf(cause, "no such pane: %s", target); else if (wp == NULL) xasprintf(cause, "no current pane"); else { *oo = wp->options; scope = OPTIONS_TABLE_PANE; } break; } /* FALLTHROUGH */ case OPTIONS_TABLE_WINDOW: if (args_has(args, 'g')) { *oo = global_w_options; scope = OPTIONS_TABLE_WINDOW; } else if (wl == NULL && target != NULL) xasprintf(cause, "no such window: %s", target); else if (wl == NULL) xasprintf(cause, "no current window"); else { *oo = wl->window->options; scope = OPTIONS_TABLE_WINDOW; } break; } return (scope); } int options_scope_from_flags(struct args *args, int window, struct cmd_find_state *fs, struct options **oo, char **cause) { struct session *s = fs->s; struct winlink *wl = fs->wl; struct window_pane *wp = fs->wp; const char *target = args_get(args, 't'); if (args_has(args, 's')) { *oo = global_options; return (OPTIONS_TABLE_SERVER); } if (args_has(args, 'p')) { if (wp == NULL) { if (target != NULL) xasprintf(cause, "no such pane: %s", target); else xasprintf(cause, "no current pane"); return (OPTIONS_TABLE_NONE); } *oo = wp->options; return (OPTIONS_TABLE_PANE); } else if (window || args_has(args, 'w')) { if (args_has(args, 'g')) { *oo = global_w_options; return (OPTIONS_TABLE_WINDOW); } if (wl == NULL) { if (target != NULL) xasprintf(cause, "no such window: %s", target); else xasprintf(cause, "no current window"); return (OPTIONS_TABLE_NONE); } *oo = wl->window->options; return (OPTIONS_TABLE_WINDOW); } else { if (args_has(args, 'g')) { *oo = global_s_options; return (OPTIONS_TABLE_SESSION); } if (s == NULL) { if (target != NULL) xasprintf(cause, "no such session: %s", target); else xasprintf(cause, "no current session"); return (OPTIONS_TABLE_NONE); } *oo = s->options; return (OPTIONS_TABLE_SESSION); } } tmux-3.0a/paste.c100644 001750 001750 00000015250 13504653146 0007506/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" /* * Set of paste buffers. Note that paste buffer data is not necessarily a C * string! */ struct paste_buffer { char *data; size_t size; char *name; time_t created; int automatic; u_int order; RB_ENTRY(paste_buffer) name_entry; RB_ENTRY(paste_buffer) time_entry; }; static u_int paste_next_index; static u_int paste_next_order; static u_int paste_num_automatic; static RB_HEAD(paste_name_tree, paste_buffer) paste_by_name; static RB_HEAD(paste_time_tree, paste_buffer) paste_by_time; static int paste_cmp_names(const struct paste_buffer *, const struct paste_buffer *); RB_GENERATE_STATIC(paste_name_tree, paste_buffer, name_entry, paste_cmp_names); static int paste_cmp_times(const struct paste_buffer *, const struct paste_buffer *); RB_GENERATE_STATIC(paste_time_tree, paste_buffer, time_entry, paste_cmp_times); static int paste_cmp_names(const struct paste_buffer *a, const struct paste_buffer *b) { return (strcmp(a->name, b->name)); } static int paste_cmp_times(const struct paste_buffer *a, const struct paste_buffer *b) { if (a->order > b->order) return (-1); if (a->order < b->order) return (1); return (0); } /* Get paste buffer name. */ const char * paste_buffer_name(struct paste_buffer *pb) { return (pb->name); } /* Get paste buffer order. */ u_int paste_buffer_order(struct paste_buffer *pb) { return (pb->order); } /* Get paste buffer created. */ time_t paste_buffer_created(struct paste_buffer *pb) { return (pb->created); } /* Get paste buffer data. */ const char * paste_buffer_data(struct paste_buffer *pb, size_t *size) { if (size != NULL) *size = pb->size; return (pb->data); } /* Walk paste buffers by time. */ struct paste_buffer * paste_walk(struct paste_buffer *pb) { if (pb == NULL) return (RB_MIN(paste_time_tree, &paste_by_time)); return (RB_NEXT(paste_time_tree, &paste_by_time, pb)); } /* Get the most recent automatic buffer. */ struct paste_buffer * paste_get_top(const char **name) { struct paste_buffer *pb; pb = RB_MIN(paste_time_tree, &paste_by_time); if (pb == NULL) return (NULL); if (name != NULL) *name = pb->name; return (pb); } /* Get a paste buffer by name. */ struct paste_buffer * paste_get_name(const char *name) { struct paste_buffer pbfind; if (name == NULL || *name == '\0') return (NULL); pbfind.name = (char *)name; return (RB_FIND(paste_name_tree, &paste_by_name, &pbfind)); } /* Free a paste buffer. */ void paste_free(struct paste_buffer *pb) { RB_REMOVE(paste_name_tree, &paste_by_name, pb); RB_REMOVE(paste_time_tree, &paste_by_time, pb); if (pb->automatic) paste_num_automatic--; free(pb->data); free(pb->name); free(pb); } /* * Add an automatic buffer, freeing the oldest automatic item if at limit. Note * that the caller is responsible for allocating data. */ void paste_add(const char *prefix, char *data, size_t size) { struct paste_buffer *pb, *pb1; u_int limit; if (prefix == NULL) prefix = "buffer"; if (size == 0) { free(data); return; } limit = options_get_number(global_options, "buffer-limit"); RB_FOREACH_REVERSE_SAFE(pb, paste_time_tree, &paste_by_time, pb1) { if (paste_num_automatic < limit) break; if (pb->automatic) paste_free(pb); } pb = xmalloc(sizeof *pb); pb->name = NULL; do { free(pb->name); xasprintf(&pb->name, "%s%u", prefix, paste_next_index); paste_next_index++; } while (paste_get_name(pb->name) != NULL); pb->data = data; pb->size = size; pb->automatic = 1; paste_num_automatic++; pb->created = time(NULL); pb->order = paste_next_order++; RB_INSERT(paste_name_tree, &paste_by_name, pb); RB_INSERT(paste_time_tree, &paste_by_time, pb); } /* Rename a paste buffer. */ int paste_rename(const char *oldname, const char *newname, char **cause) { struct paste_buffer *pb, *pb_new; if (cause != NULL) *cause = NULL; if (oldname == NULL || *oldname == '\0') { if (cause != NULL) *cause = xstrdup("no buffer"); return (-1); } if (newname == NULL || *newname == '\0') { if (cause != NULL) *cause = xstrdup("new name is empty"); return (-1); } pb = paste_get_name(oldname); if (pb == NULL) { if (cause != NULL) xasprintf(cause, "no buffer %s", oldname); return (-1); } pb_new = paste_get_name(newname); if (pb_new != NULL) { if (cause != NULL) xasprintf(cause, "buffer %s already exists", newname); return (-1); } RB_REMOVE(paste_name_tree, &paste_by_name, pb); free(pb->name); pb->name = xstrdup(newname); if (pb->automatic) paste_num_automatic--; pb->automatic = 0; RB_INSERT(paste_name_tree, &paste_by_name, pb); return (0); } /* * Add or replace an item in the store. Note that the caller is responsible for * allocating data. */ int paste_set(char *data, size_t size, const char *name, char **cause) { struct paste_buffer *pb, *old; if (cause != NULL) *cause = NULL; if (size == 0) { free(data); return (0); } if (name == NULL) { paste_add(NULL, data, size); return (0); } if (*name == '\0') { if (cause != NULL) *cause = xstrdup("empty buffer name"); return (-1); } pb = xmalloc(sizeof *pb); pb->name = xstrdup(name); pb->data = data; pb->size = size; pb->automatic = 0; pb->order = paste_next_order++; pb->created = time(NULL); if ((old = paste_get_name(name)) != NULL) paste_free(old); RB_INSERT(paste_name_tree, &paste_by_name, pb); RB_INSERT(paste_time_tree, &paste_by_time, pb); return (0); } /* Convert start of buffer into a nice string. */ char * paste_make_sample(struct paste_buffer *pb) { char *buf; size_t len, used; const int flags = VIS_OCTAL|VIS_TAB|VIS_NL; const size_t width = 200; len = pb->size; if (len > width) len = width; buf = xreallocarray(NULL, len, 4 + 4); used = utf8_strvis(buf, pb->data, len, flags); if (pb->size > width || used > width) strlcpy(buf + width, "...", 4); return (buf); } tmux-3.0a/proc.c100644 001750 001750 00000015766 13466230031 0007340/* $OpenBSD$ */ /* * Copyright (c) 2015 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" struct tmuxproc { const char *name; int exit; void (*signalcb)(int); struct event ev_sighup; struct event ev_sigchld; struct event ev_sigcont; struct event ev_sigterm; struct event ev_sigusr1; struct event ev_sigusr2; struct event ev_sigwinch; }; struct tmuxpeer { struct tmuxproc *parent; struct imsgbuf ibuf; struct event event; int flags; #define PEER_BAD 0x1 void (*dispatchcb)(struct imsg *, void *); void *arg; }; static int peer_check_version(struct tmuxpeer *, struct imsg *); static void proc_update_event(struct tmuxpeer *); static void proc_event_cb(__unused int fd, short events, void *arg) { struct tmuxpeer *peer = arg; ssize_t n; struct imsg imsg; if (!(peer->flags & PEER_BAD) && (events & EV_READ)) { if (((n = imsg_read(&peer->ibuf)) == -1 && errno != EAGAIN) || n == 0) { peer->dispatchcb(NULL, peer->arg); return; } for (;;) { if ((n = imsg_get(&peer->ibuf, &imsg)) == -1) { peer->dispatchcb(NULL, peer->arg); return; } if (n == 0) break; log_debug("peer %p message %d", peer, imsg.hdr.type); if (peer_check_version(peer, &imsg) != 0) { if (imsg.fd != -1) close(imsg.fd); imsg_free(&imsg); break; } peer->dispatchcb(&imsg, peer->arg); imsg_free(&imsg); } } if (events & EV_WRITE) { if (msgbuf_write(&peer->ibuf.w) <= 0 && errno != EAGAIN) { peer->dispatchcb(NULL, peer->arg); return; } } if ((peer->flags & PEER_BAD) && peer->ibuf.w.queued == 0) { peer->dispatchcb(NULL, peer->arg); return; } proc_update_event(peer); } static void proc_signal_cb(int signo, __unused short events, void *arg) { struct tmuxproc *tp = arg; tp->signalcb(signo); } static int peer_check_version(struct tmuxpeer *peer, struct imsg *imsg) { int version; version = imsg->hdr.peerid & 0xff; if (imsg->hdr.type != MSG_VERSION && version != PROTOCOL_VERSION) { log_debug("peer %p bad version %d", peer, version); proc_send(peer, MSG_VERSION, -1, NULL, 0); peer->flags |= PEER_BAD; return (-1); } return (0); } static void proc_update_event(struct tmuxpeer *peer) { short events; event_del(&peer->event); events = EV_READ; if (peer->ibuf.w.queued > 0) events |= EV_WRITE; event_set(&peer->event, peer->ibuf.fd, events, proc_event_cb, peer); event_add(&peer->event, NULL); } int proc_send(struct tmuxpeer *peer, enum msgtype type, int fd, const void *buf, size_t len) { struct imsgbuf *ibuf = &peer->ibuf; void *vp = (void *)buf; int retval; if (peer->flags & PEER_BAD) return (-1); log_debug("sending message %d to peer %p (%zu bytes)", type, peer, len); retval = imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, fd, vp, len); if (retval != 1) return (-1); proc_update_event(peer); return (0); } struct tmuxproc * proc_start(const char *name) { struct tmuxproc *tp; struct utsname u; log_open(name); setproctitle("%s (%s)", name, socket_path); if (uname(&u) < 0) memset(&u, 0, sizeof u); log_debug("%s started (%ld): version %s, socket %s, protocol %d", name, (long)getpid(), VERSION, socket_path, PROTOCOL_VERSION); log_debug("on %s %s %s; libevent %s (%s)", u.sysname, u.release, u.version, event_get_version(), event_get_method()); tp = xcalloc(1, sizeof *tp); tp->name = xstrdup(name); return (tp); } void proc_loop(struct tmuxproc *tp, int (*loopcb)(void)) { log_debug("%s loop enter", tp->name); do event_loop(EVLOOP_ONCE); while (!tp->exit && (loopcb == NULL || !loopcb ())); log_debug("%s loop exit", tp->name); } void proc_exit(struct tmuxproc *tp) { tp->exit = 1; } void proc_set_signals(struct tmuxproc *tp, void (*signalcb)(int)) { struct sigaction sa; tp->signalcb = signalcb; memset(&sa, 0, sizeof sa); sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sa.sa_handler = SIG_IGN; sigaction(SIGINT, &sa, NULL); sigaction(SIGPIPE, &sa, NULL); sigaction(SIGTSTP, &sa, NULL); signal_set(&tp->ev_sighup, SIGHUP, proc_signal_cb, tp); signal_add(&tp->ev_sighup, NULL); signal_set(&tp->ev_sigchld, SIGCHLD, proc_signal_cb, tp); signal_add(&tp->ev_sigchld, NULL); signal_set(&tp->ev_sigcont, SIGCONT, proc_signal_cb, tp); signal_add(&tp->ev_sigcont, NULL); signal_set(&tp->ev_sigterm, SIGTERM, proc_signal_cb, tp); signal_add(&tp->ev_sigterm, NULL); signal_set(&tp->ev_sigusr1, SIGUSR1, proc_signal_cb, tp); signal_add(&tp->ev_sigusr1, NULL); signal_set(&tp->ev_sigusr2, SIGUSR2, proc_signal_cb, tp); signal_add(&tp->ev_sigusr2, NULL); signal_set(&tp->ev_sigwinch, SIGWINCH, proc_signal_cb, tp); signal_add(&tp->ev_sigwinch, NULL); } void proc_clear_signals(struct tmuxproc *tp, int defaults) { struct sigaction sa; memset(&sa, 0, sizeof sa); sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sa.sa_handler = SIG_DFL; sigaction(SIGINT, &sa, NULL); sigaction(SIGPIPE, &sa, NULL); sigaction(SIGTSTP, &sa, NULL); signal_del(&tp->ev_sighup); signal_del(&tp->ev_sigchld); signal_del(&tp->ev_sigcont); signal_del(&tp->ev_sigterm); signal_del(&tp->ev_sigusr1); signal_del(&tp->ev_sigusr2); signal_del(&tp->ev_sigwinch); if (defaults) { sigaction(SIGHUP, &sa, NULL); sigaction(SIGCHLD, &sa, NULL); sigaction(SIGCONT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); sigaction(SIGUSR1, &sa, NULL); sigaction(SIGUSR2, &sa, NULL); sigaction(SIGWINCH, &sa, NULL); } } struct tmuxpeer * proc_add_peer(struct tmuxproc *tp, int fd, void (*dispatchcb)(struct imsg *, void *), void *arg) { struct tmuxpeer *peer; peer = xcalloc(1, sizeof *peer); peer->parent = tp; peer->dispatchcb = dispatchcb; peer->arg = arg; imsg_init(&peer->ibuf, fd); event_set(&peer->event, fd, EV_READ, proc_event_cb, peer); log_debug("add peer %p: %d (%p)", peer, fd, arg); proc_update_event(peer); return (peer); } void proc_remove_peer(struct tmuxpeer *peer) { log_debug("remove peer %p", peer); event_del(&peer->event); imsg_clear(&peer->ibuf); close(peer->ibuf.fd); free(peer); } void proc_kill_peer(struct tmuxpeer *peer) { peer->flags |= PEER_BAD; } void proc_toggle_log(struct tmuxproc *tp) { log_toggle(tp->name); } tmux-3.0a/regsub.c100644 001750 001750 00000005410 13570677537 0007673/* $OpenBSD$ */ /* * Copyright (c) 2019 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" static void regsub_copy(char **buf, size_t *len, const char *text, size_t start, size_t end) { size_t add = end - start; *buf = xrealloc(*buf, (*len) + add + 1); memcpy((*buf) + *len, text + start, add); (*len) += add; } static void regsub_expand(char **buf, size_t *len, const char *with, const char *text, regmatch_t *m, u_int n) { const char *cp; u_int i; for (cp = with; *cp != '\0'; cp++) { if (*cp == '\\') { cp++; if (*cp >= '0' && *cp <= '9') { i = *cp - '0'; if (i < n && m[i].rm_so != m[i].rm_eo) { regsub_copy(buf, len, text, m[i].rm_so, m[i].rm_eo); continue; } } } *buf = xrealloc(*buf, (*len) + 2); (*buf)[(*len)++] = *cp; } } char * regsub(const char *pattern, const char *with, const char *text, int flags) { regex_t r; regmatch_t m[10]; ssize_t start, end, last, len = 0; int empty = 0; char *buf = NULL; if (*text == '\0') return (xstrdup("")); if (regcomp(&r, pattern, flags) != 0) return (NULL); start = 0; last = 0; end = strlen(text); while (start <= end) { if (regexec(&r, text + start, nitems(m), m, 0) != 0) { regsub_copy(&buf, &len, text, start, end); break; } /* * Append any text not part of this match (from the end of the * last match). */ regsub_copy(&buf, &len, text, last, m[0].rm_so + start); /* * If the last match was empty and this one isn't (it is either * later or has matched text), expand this match. If it is * empty, move on one character and try again from there. */ if (empty || start + m[0].rm_so != last || m[0].rm_so != m[0].rm_eo) { regsub_expand(&buf, &len, with, text + start, m, nitems(m)); last = start + m[0].rm_eo; start += m[0].rm_eo; empty = 0; } else { last = start + m[0].rm_eo; start += m[0].rm_eo + 1; empty = 1; } } buf[len] = '\0'; regfree(&r); return (buf); } tmux-3.0a/resize.c100644 001750 001750 00000013215 13570677043 0007677/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" void resize_window(struct window *w, u_int sx, u_int sy) { int zoomed; /* Check size limits. */ if (sx < WINDOW_MINIMUM) sx = WINDOW_MINIMUM; if (sx > WINDOW_MAXIMUM) sx = WINDOW_MAXIMUM; if (sy < WINDOW_MINIMUM) sy = WINDOW_MINIMUM; if (sy > WINDOW_MAXIMUM) sy = WINDOW_MAXIMUM; /* If the window is zoomed, unzoom. */ zoomed = w->flags & WINDOW_ZOOMED; if (zoomed) window_unzoom(w); /* Resize the layout first. */ layout_resize(w, sx, sy); /* Resize the window, it can be no smaller than the layout. */ if (sx < w->layout_root->sx) sx = w->layout_root->sx; if (sy < w->layout_root->sy) sy = w->layout_root->sy; window_resize(w, sx, sy); log_debug("%s: @%u resized to %u,%u; layout %u,%u", __func__, w->id, sx, sy, w->layout_root->sx, w->layout_root->sy); /* Restore the window zoom state. */ if (zoomed) window_zoom(w->active); tty_update_window_offset(w); server_redraw_window(w); notify_window("window-layout-changed", w); } static int ignore_client_size(struct client *c) { if (c->session == NULL) return (1); if (c->flags & CLIENT_NOSIZEFLAGS) return (1); if ((c->flags & CLIENT_CONTROL) && (~c->flags & CLIENT_SIZECHANGED)) return (1); return (0); } void default_window_size(struct session *s, struct window *w, u_int *sx, u_int *sy, int type) { struct client *c; u_int cx, cy; const char *value; if (type == -1) type = options_get_number(global_w_options, "window-size"); if (type == WINDOW_SIZE_MANUAL) goto manual; if (type == WINDOW_SIZE_LARGEST) { *sx = *sy = 0; TAILQ_FOREACH(c, &clients, entry) { if (ignore_client_size(c)) continue; if (w != NULL && !session_has(c->session, w)) continue; if (w == NULL && c->session != s) continue; cx = c->tty.sx; cy = c->tty.sy - status_line_size(c); if (cx > *sx) *sx = cx; if (cy > *sy) *sy = cy; } if (*sx == 0 || *sy == 0) goto manual; } else { *sx = *sy = UINT_MAX; TAILQ_FOREACH(c, &clients, entry) { if (ignore_client_size(c)) continue; if (w != NULL && !session_has(c->session, w)) continue; if (w == NULL && c->session != s) continue; cx = c->tty.sx; cy = c->tty.sy - status_line_size(c); if (cx < *sx) *sx = cx; if (cy < *sy) *sy = cy; } if (*sx == UINT_MAX || *sy == UINT_MAX) goto manual; } goto done; manual: value = options_get_string(s->options, "default-size"); if (sscanf(value, "%ux%u", sx, sy) != 2) { *sx = 80; *sy = 24; } done: if (*sx < WINDOW_MINIMUM) *sx = WINDOW_MINIMUM; if (*sx > WINDOW_MAXIMUM) *sx = WINDOW_MAXIMUM; if (*sy < WINDOW_MINIMUM) *sy = WINDOW_MINIMUM; if (*sy > WINDOW_MAXIMUM) *sy = WINDOW_MAXIMUM; } void recalculate_sizes(void) { struct session *s; struct client *c; struct window *w; u_int sx, sy, cx, cy; int type, current, has, changed; /* * Clear attached count and update saved status line information for * each session. */ RB_FOREACH(s, sessions, &sessions) { s->attached = 0; status_update_cache(s); } /* * Increment attached count and check the status line size for each * client. */ TAILQ_FOREACH(c, &clients, entry) { if (ignore_client_size(c)) continue; s = c->session; if (c->tty.sy <= s->statuslines || (c->flags & CLIENT_CONTROL)) c->flags |= CLIENT_STATUSOFF; else c->flags &= ~CLIENT_STATUSOFF; s->attached++; } /* Walk each window and adjust the size. */ RB_FOREACH(w, windows, &windows) { if (w->active == NULL) continue; log_debug("%s: @%u is %u,%u", __func__, w->id, w->sx, w->sy); type = options_get_number(w->options, "window-size"); if (type == WINDOW_SIZE_MANUAL) continue; current = options_get_number(w->options, "aggressive-resize"); changed = 1; if (type == WINDOW_SIZE_LARGEST) { sx = sy = 0; TAILQ_FOREACH(c, &clients, entry) { if (ignore_client_size(c)) continue; s = c->session; if (current) has = (s->curw->window == w); else has = session_has(s, w); if (!has) continue; cx = c->tty.sx; cy = c->tty.sy - status_line_size(c); if (cx > sx) sx = cx; if (cy > sy) sy = cy; } if (sx == 0 || sy == 0) changed = 0; } else { sx = sy = UINT_MAX; TAILQ_FOREACH(c, &clients, entry) { if (ignore_client_size(c)) continue; s = c->session; if (current) has = (s->curw->window == w); else has = session_has(s, w); if (!has) continue; cx = c->tty.sx; cy = c->tty.sy - status_line_size(c); if (cx < sx) sx = cx; if (cy < sy) sy = cy; } if (sx == UINT_MAX || sy == UINT_MAX) changed = 0; } if (w->sx == sx && w->sy == sy) changed = 0; if (!changed) { tty_update_window_offset(w); continue; } log_debug("%s: @%u changed to %u,%u", __func__, w->id, sx, sy); resize_window(w, sx, sy); } } tmux-3.0a/screen-redraw.c100644 001750 001750 00000040417 13517540563 0011140/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" static void screen_redraw_draw_borders(struct screen_redraw_ctx *); static void screen_redraw_draw_panes(struct screen_redraw_ctx *); static void screen_redraw_draw_status(struct screen_redraw_ctx *); static void screen_redraw_draw_pane(struct screen_redraw_ctx *, 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. */ static 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 (2); } /* 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 (3); if (py == wp->yoff + wp->sy) return (4); } /* Outside pane. */ return (-1); } /* Check if a cell is on the pane border. */ static 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. */ static int screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, struct window_pane **wpp) { struct window *w = c->session->curw->window; struct window_pane *wp; int borders; u_int right, line; *wpp = NULL; if (px > w->sx || py > w->sy) return (CELL_OUTSIDE); if (pane_status != PANE_STATUS_OFF) { TAILQ_FOREACH(wp, &w->panes, entry) { if (!window_pane_visible(wp)) continue; if (pane_status == PANE_STATUS_TOP) line = wp->yoff - 1; else line = wp->yoff + wp->sy; right = wp->xoff + 2 + wp->status_size - 1; if (py == line && px >= wp->xoff + 2 && px <= right) return (CELL_INSIDE); } } TAILQ_FOREACH(wp, &w->panes, entry) { if (!window_pane_visible(wp)) continue; *wpp = wp; /* 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 (pane_status == PANE_STATUS_TOP) { if (py != 0 && screen_redraw_cell_border(c, px, py - 1)) borders |= 2; } else { 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); } /* Check if the border of a particular pane. */ static int screen_redraw_check_is(u_int px, u_int py, int type, int pane_status, struct window *w, struct window_pane *wantwp, struct window_pane *wp) { int border; /* Is this off the active pane border? */ border = screen_redraw_cell_border1(wantwp, px, py); if (border == 0 || border == -1) return (0); if (pane_status == PANE_STATUS_TOP && border == 4) return (0); if (pane_status == PANE_STATUS_BOTTOM && border == 3) return (0); /* If there are more than two panes, that's enough. */ if (window_count_panes(w) != 2) return (1); /* Else if the cell is not a border cell, forget it. */ if (wp == NULL || (type == CELL_OUTSIDE || type == CELL_INSIDE)) return (1); /* With status lines mark the entire line. */ if (pane_status != PANE_STATUS_OFF) return (1); /* Check if the pane covers the whole width. */ if (wp->xoff == 0 && wp->sx == w->sx) { /* This can either be the top pane or the bottom pane. */ if (wp->yoff == 0) { /* top pane */ if (wp == wantwp) return (px <= wp->sx / 2); return (px > wp->sx / 2); } return (0); } /* Check if the pane covers the whole height. */ if (wp->yoff == 0 && wp->sy == w->sy) { /* This can either be the left pane or the right pane. */ if (wp->xoff == 0) { /* left pane */ if (wp == wantwp) return (py <= wp->sy / 2); return (py > wp->sy / 2); } return (0); } return (1); } /* Update pane status. */ static int screen_redraw_make_pane_status(struct client *c, struct window *w, struct window_pane *wp) { struct grid_cell gc; const char *fmt; struct format_tree *ft; char *expanded; u_int width, i; struct screen_write_ctx ctx; struct screen old; if (wp == w->active) style_apply(&gc, w->options, "pane-active-border-style"); else style_apply(&gc, w->options, "pane-border-style"); fmt = options_get_string(w->options, "pane-border-format"); ft = format_create(c, NULL, FORMAT_PANE|wp->id, FORMAT_STATUS); format_defaults(ft, c, NULL, NULL, wp); expanded = format_expand_time(ft, fmt); if (wp->sx < 4) wp->status_size = width = 0; else wp->status_size = width = wp->sx - 4; memcpy(&old, &wp->status_screen, sizeof old); screen_init(&wp->status_screen, width, 1, 0); wp->status_screen.mode = 0; screen_write_start(&ctx, NULL, &wp->status_screen); gc.attr |= GRID_ATTR_CHARSET; for (i = 0; i < width; i++) screen_write_putc(&ctx, &gc, 'q'); gc.attr &= ~GRID_ATTR_CHARSET; screen_write_cursormove(&ctx, 0, 0, 0); format_draw(&ctx, &gc, width, expanded, NULL); screen_write_stop(&ctx); free(expanded); format_free(ft); if (grid_compare(wp->status_screen.grid, old.grid) == 0) { screen_free(&old); return (0); } screen_free(&old); return (1); } /* Draw pane status. */ static void screen_redraw_draw_pane_status(struct screen_redraw_ctx *ctx) { struct client *c = ctx->c; struct window *w = c->session->curw->window; struct tty *tty = &c->tty; struct window_pane *wp; struct screen *s; u_int i, x, width, xoff, yoff, size; log_debug("%s: %s @%u", __func__, c->name, w->id); TAILQ_FOREACH(wp, &w->panes, entry) { if (!window_pane_visible(wp)) continue; s = &wp->status_screen; size = wp->status_size; if (ctx->pane_status == PANE_STATUS_TOP) yoff = wp->yoff - 1; else yoff = wp->yoff + wp->sy; xoff = wp->xoff + 2; if (xoff + size <= ctx->ox || xoff >= ctx->ox + ctx->sx || yoff < ctx->oy || yoff >= ctx->oy + ctx->sy) continue; if (xoff >= ctx->ox && xoff + size <= ctx->ox + ctx->sx) { /* All visible. */ i = 0; x = xoff - ctx->ox; width = size; } else if (xoff < ctx->ox && xoff + size > ctx->ox + ctx->sx) { /* Both left and right not visible. */ i = ctx->ox; x = 0; width = ctx->sx; } else if (xoff < ctx->ox) { /* Left not visible. */ i = ctx->ox - xoff; x = 0; width = size - i; } else { /* Right not visible. */ i = 0; x = xoff - ctx->ox; width = size - x; } if (ctx->statustop) yoff += ctx->statuslines; tty_draw_line(tty, NULL, s, i, 0, width, x, yoff - ctx->oy); } tty_cursor(tty, 0, 0); } /* Update status line and change flags if unchanged. */ static int screen_redraw_update(struct client *c, int flags) { struct window *w = c->session->curw->window; struct window_pane *wp; struct options *wo = w->options; int redraw; 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 && (~flags & CLIENT_REDRAWSTATUSALWAYS)) flags &= ~CLIENT_REDRAWSTATUS; if (c->overlay_draw != NULL) flags |= CLIENT_REDRAWOVERLAY; if (options_get_number(wo, "pane-border-status") != PANE_STATUS_OFF) { redraw = 0; TAILQ_FOREACH(wp, &w->panes, entry) { if (screen_redraw_make_pane_status(c, w, wp)) redraw = 1; } if (redraw) flags |= CLIENT_REDRAWBORDERS; } return (flags); } /* Set up redraw context. */ static void screen_redraw_set_context(struct client *c, struct screen_redraw_ctx *ctx) { struct session *s = c->session; struct options *oo = s->options; struct window *w = s->curw->window; struct options *wo = w->options; u_int lines; memset(ctx, 0, sizeof *ctx); ctx->c = c; lines = status_line_size(c); if (c->message_string != NULL || c->prompt_string != NULL) lines = (lines == 0) ? 1 : lines; if (lines != 0 && options_get_number(oo, "status-position") == 0) ctx->statustop = 1; ctx->statuslines = lines; ctx->pane_status = options_get_number(wo, "pane-border-status"); tty_window_offset(&c->tty, &ctx->ox, &ctx->oy, &ctx->sx, &ctx->sy); log_debug("%s: %s @%u ox=%u oy=%u sx=%u sy=%u %u/%d", __func__, c->name, w->id, ctx->ox, ctx->oy, ctx->sx, ctx->sy, ctx->statuslines, ctx->statustop); } /* Redraw entire screen. */ void screen_redraw_screen(struct client *c) { struct screen_redraw_ctx ctx; int flags; if (c->flags & CLIENT_SUSPENDED) return; flags = screen_redraw_update(c, c->flags); screen_redraw_set_context(c, &ctx); if (flags & (CLIENT_REDRAWWINDOW|CLIENT_REDRAWBORDERS)) { if (ctx.pane_status != PANE_STATUS_OFF) screen_redraw_draw_pane_status(&ctx); screen_redraw_draw_borders(&ctx); } if (flags & CLIENT_REDRAWWINDOW) screen_redraw_draw_panes(&ctx); if (ctx.statuslines != 0 && (flags & (CLIENT_REDRAWSTATUS|CLIENT_REDRAWSTATUSALWAYS))) screen_redraw_draw_status(&ctx); if (c->overlay_draw != NULL && (flags & CLIENT_REDRAWOVERLAY)) c->overlay_draw(c, &ctx); tty_reset(&c->tty); } /* Redraw a single pane. */ void screen_redraw_pane(struct client *c, struct window_pane *wp) { struct screen_redraw_ctx ctx; if (c->overlay_draw != NULL || !window_pane_visible(wp)) return; screen_redraw_set_context(c, &ctx); screen_redraw_draw_pane(&ctx, wp); tty_reset(&c->tty); } /* Draw a border cell. */ static void screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j, struct grid_cell *m_active_gc, struct grid_cell *active_gc, struct grid_cell *m_other_gc, struct grid_cell *other_gc) { struct client *c = ctx->c; struct session *s = c->session; struct window *w = s->curw->window; struct tty *tty = &c->tty; struct window_pane *wp; struct window_pane *active = w->active; struct window_pane *marked = marked_pane.wp; u_int type, x = ctx->ox + i, y = ctx->oy + j; int flag, pane_status = ctx->pane_status; type = screen_redraw_check_cell(c, x, y, pane_status, &wp); if (type == CELL_INSIDE) return; flag = screen_redraw_check_is(x, y, type, pane_status, w, active, wp); if (server_is_marked(s, s->curw, marked_pane.wp) && screen_redraw_check_is(x, y, type, pane_status, w, marked, wp)) { if (flag) tty_attributes(tty, m_active_gc, NULL); else tty_attributes(tty, m_other_gc, NULL); } else if (flag) tty_attributes(tty, active_gc, NULL); else tty_attributes(tty, other_gc, NULL); if (ctx->statustop) tty_cursor(tty, i, ctx->statuslines + j); else tty_cursor(tty, i, j); tty_putc(tty, CELL_BORDERS[type]); } /* Draw the borders. */ static void screen_redraw_draw_borders(struct screen_redraw_ctx *ctx) { struct client *c = ctx->c; struct session *s = c->session; struct window *w = s->curw->window; struct tty *tty = &c->tty; struct options *oo = w->options; struct grid_cell m_active_gc, active_gc, m_other_gc, other_gc; u_int i, j; log_debug("%s: %s @%u", __func__, c->name, w->id); style_apply(&other_gc, oo, "pane-border-style"); style_apply(&active_gc, oo, "pane-active-border-style"); active_gc.attr = other_gc.attr = GRID_ATTR_CHARSET; memcpy(&m_other_gc, &other_gc, sizeof m_other_gc); m_other_gc.attr ^= GRID_ATTR_REVERSE; memcpy(&m_active_gc, &active_gc, sizeof m_active_gc); m_active_gc.attr ^= GRID_ATTR_REVERSE; for (j = 0; j < tty->sy - ctx->statuslines; j++) { for (i = 0; i < tty->sx; i++) { screen_redraw_draw_borders_cell(ctx, i, j, &m_active_gc, &active_gc, &m_other_gc, &other_gc); } } } /* Draw the panes. */ static void screen_redraw_draw_panes(struct screen_redraw_ctx *ctx) { struct client *c = ctx->c; struct window *w = c->session->curw->window; struct window_pane *wp; log_debug("%s: %s @%u", __func__, c->name, w->id); TAILQ_FOREACH(wp, &w->panes, entry) { if (window_pane_visible(wp)) screen_redraw_draw_pane(ctx, wp); } } /* Draw the status line. */ static void screen_redraw_draw_status(struct screen_redraw_ctx *ctx) { struct client *c = ctx->c; struct window *w = c->session->curw->window; struct tty *tty = &c->tty; struct screen *s = c->status.active; u_int i, y; log_debug("%s: %s @%u", __func__, c->name, w->id); if (ctx->statustop) y = 0; else y = c->tty.sy - ctx->statuslines; for (i = 0; i < ctx->statuslines; i++) tty_draw_line(tty, NULL, s, 0, i, UINT_MAX, 0, y + i); } /* Draw one pane. */ static void screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp) { struct client *c = ctx->c; struct window *w = c->session->curw->window; struct tty *tty = &c->tty; struct screen *s; u_int i, j, top, x, y, width; log_debug("%s: %s @%u %%%u", __func__, c->name, w->id, wp->id); if (wp->xoff + wp->sx <= ctx->ox || wp->xoff >= ctx->ox + ctx->sx) return; if (ctx->statustop) top = ctx->statuslines; else top = 0; s = wp->screen; for (j = 0; j < wp->sy; j++) { if (wp->yoff + j < ctx->oy || wp->yoff + j >= ctx->oy + ctx->sy) continue; y = top + wp->yoff + j - ctx->oy; if (wp->xoff >= ctx->ox && wp->xoff + wp->sx <= ctx->ox + ctx->sx) { /* All visible. */ i = 0; x = wp->xoff - ctx->ox; width = wp->sx; } else if (wp->xoff < ctx->ox && wp->xoff + wp->sx > ctx->ox + ctx->sx) { /* Both left and right not visible. */ i = ctx->ox; x = 0; width = ctx->sx; } else if (wp->xoff < ctx->ox) { /* Left not visible. */ i = ctx->ox - wp->xoff; x = 0; width = wp->sx - i; } else { /* Right not visible. */ i = 0; x = wp->xoff - ctx->ox; width = ctx->sx - x; } log_debug("%s: %s %%%u line %u,%u at %u,%u, width %u", __func__, c->name, wp->id, i, j, x, y, width); tty_draw_line(tty, wp, s, i, j, width, x, y); } } tmux-3.0a/screen-write.c100644 001750 001750 00000114613 13570677043 0011011/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" static void screen_write_initctx(struct screen_write_ctx *, struct tty_ctx *); static void screen_write_collect_clear(struct screen_write_ctx *, u_int, u_int); static void screen_write_collect_scroll(struct screen_write_ctx *); static void screen_write_collect_flush(struct screen_write_ctx *, int); static int screen_write_overwrite(struct screen_write_ctx *, struct grid_cell *, u_int); static const struct grid_cell *screen_write_combine(struct screen_write_ctx *, const struct utf8_data *, u_int *); static const struct grid_cell screen_write_pad_cell = { { { 0 }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 0, 8, 8 }; struct screen_write_collect_item { u_int x; int wrapped; u_int used; char data[256]; struct grid_cell gc; TAILQ_ENTRY(screen_write_collect_item) entry; }; struct screen_write_collect_line { TAILQ_HEAD(, screen_write_collect_item) items; }; static void screen_write_offset_timer(__unused int fd, __unused short events, void *data) { struct window *w = data; tty_update_window_offset(w); } /* Set cursor position. */ static void screen_write_set_cursor(struct screen_write_ctx *ctx, int cx, int cy) { struct window_pane *wp = ctx->wp; struct window *w; struct screen *s = ctx->s; struct timeval tv = { .tv_usec = 10000 }; if (cx != -1 && (u_int)cx == s->cx && cy != -1 && (u_int)cy == s->cy) return; if (cx != -1) { if ((u_int)cx > screen_size_x(s)) /* allow last column */ cx = screen_size_x(s) - 1; s->cx = cx; } if (cy != -1) { if ((u_int)cy > screen_size_y(s) - 1) cy = screen_size_y(s) - 1; s->cy = cy; } if (wp == NULL) return; w = wp->window; if (!event_initialized(&w->offset_timer)) evtimer_set(&w->offset_timer, screen_write_offset_timer, w); if (!evtimer_pending(&w->offset_timer, NULL)) evtimer_add(&w->offset_timer, &tv); } /* Initialize writing with a window. */ void screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, struct screen *s) { char tmp[32]; u_int y; memset(ctx, 0, sizeof *ctx); ctx->wp = wp; if (wp != NULL && s == NULL) ctx->s = wp->screen; else ctx->s = s; ctx->list = xcalloc(screen_size_y(ctx->s), sizeof *ctx->list); for (y = 0; y < screen_size_y(ctx->s); y++) TAILQ_INIT(&ctx->list[y].items); ctx->item = xcalloc(1, sizeof *ctx->item); ctx->scrolled = 0; ctx->bg = 8; if (wp != NULL) { snprintf(tmp, sizeof tmp, "pane %%%u (at %u,%u)", wp->id, wp->xoff, wp->yoff); } log_debug("%s: size %ux%u, %s", __func__, screen_size_x(ctx->s), screen_size_y(ctx->s), wp == NULL ? "no pane" : tmp); } /* Finish writing. */ void screen_write_stop(struct screen_write_ctx *ctx) { screen_write_collect_end(ctx); screen_write_collect_flush(ctx, 0); log_debug("%s: %u cells (%u written, %u skipped)", __func__, ctx->cells, ctx->written, ctx->skipped); free(ctx->item); free(ctx->list); /* flush will have emptied */ } /* Reset screen state. */ void screen_write_reset(struct screen_write_ctx *ctx) { struct screen *s = ctx->s; screen_reset_tabs(s); screen_write_scrollregion(ctx, 0, screen_size_y(s) - 1); s->mode = MODE_CURSOR | MODE_WRAP; screen_write_clearscreen(ctx, 8); screen_write_set_cursor(ctx, 0, 0); } /* Write character. */ void screen_write_putc(struct screen_write_ctx *ctx, const struct grid_cell *gcp, u_char ch) { struct grid_cell gc; memcpy(&gc, gcp, sizeof gc); utf8_set(&gc.data, ch); screen_write_cell(ctx, &gc); } /* Calculate string length. */ size_t screen_write_strlen(const char *fmt, ...) { va_list ap; char *msg; struct utf8_data ud; u_char *ptr; size_t left, size = 0; enum utf8_state more; va_start(ap, fmt); xvasprintf(&msg, fmt, ap); va_end(ap); ptr = msg; while (*ptr != '\0') { if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) { ptr++; left = strlen(ptr); if (left < (size_t)ud.size - 1) break; while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE) ptr++; ptr++; if (more == UTF8_DONE) size += ud.width; } else { if (*ptr > 0x1f && *ptr < 0x7f) size++; ptr++; } } free(msg); return (size); } /* Write simple string (no UTF-8 or maximum length). */ void screen_write_puts(struct screen_write_ctx *ctx, const struct grid_cell *gcp, const char *fmt, ...) { va_list ap; va_start(ap, fmt); screen_write_vnputs(ctx, -1, gcp, fmt, ap); va_end(ap); } /* Write string with length limit (-1 for unlimited). */ void screen_write_nputs(struct screen_write_ctx *ctx, ssize_t maxlen, const struct grid_cell *gcp, const char *fmt, ...) { va_list ap; va_start(ap, fmt); screen_write_vnputs(ctx, maxlen, gcp, fmt, ap); va_end(ap); } void screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, const struct grid_cell *gcp, const char *fmt, va_list ap) { struct grid_cell gc; struct utf8_data *ud = &gc.data; char *msg; u_char *ptr; size_t left, size = 0; enum utf8_state more; memcpy(&gc, gcp, sizeof gc); xvasprintf(&msg, fmt, ap); ptr = msg; while (*ptr != '\0') { if (*ptr > 0x7f && utf8_open(ud, *ptr) == UTF8_MORE) { ptr++; left = strlen(ptr); if (left < (size_t)ud->size - 1) break; while ((more = utf8_append(ud, *ptr)) == UTF8_MORE) ptr++; ptr++; if (more != UTF8_DONE) continue; if (maxlen > 0 && size + ud->width > (size_t)maxlen) { while (size < (size_t)maxlen) { screen_write_putc(ctx, &gc, ' '); size++; } break; } size += ud->width; screen_write_cell(ctx, &gc); } else { if (maxlen > 0 && size + 1 > (size_t)maxlen) break; if (*ptr == '\001') gc.attr ^= GRID_ATTR_CHARSET; else if (*ptr > 0x1f && *ptr < 0x7f) { size++; screen_write_putc(ctx, &gc, *ptr); } ptr++; } } free(msg); } /* Copy from another screen. Assumes target region is big enough. */ void screen_write_copy(struct screen_write_ctx *ctx, struct screen *src, u_int px, u_int py, u_int nx, u_int ny, bitstr_t *mbs, const struct grid_cell *mgc) { struct screen *s = ctx->s; struct grid *gd = src->grid; struct grid_cell gc; u_int xx, yy, cx, cy, b; if (nx == 0 || ny == 0) return; cx = s->cx; cy = s->cy; for (yy = py; yy < py + ny; yy++) { for (xx = px; xx < px + nx; xx++) { grid_get_cell(gd, xx, yy, &gc); if (mbs != NULL) { b = (yy * screen_size_x(src)) + xx; if (bit_test(mbs, b)) { gc.attr = mgc->attr; gc.fg = mgc->fg; gc.bg = mgc->bg; } } if (xx + gc.data.width <= px + nx) screen_write_cell(ctx, &gc); } cy++; screen_write_set_cursor(ctx, cx, cy); } } /* * Copy from another screen but without the selection stuff. Also assumes the * target region is already big enough. */ void screen_write_fast_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_cell gc; u_int xx, yy, cx, cy; if (nx == 0 || ny == 0) return; cy = s->cy; for (yy = py; yy < py + ny; yy++) { if (yy >= gd->hsize + gd->sy) break; cx = s->cx; for (xx = px; xx < px + nx; xx++) { if (xx >= grid_get_line(gd, yy)->cellsize) break; grid_get_cell(gd, xx, yy, &gc); if (xx + gc.data.width > px + nx) break; grid_view_set_cell(ctx->s->grid, cx, cy, &gc); cx++; } cy++; } } /* Draw a horizontal line on screen. */ void screen_write_hline(struct screen_write_ctx *ctx, u_int nx, int left, int right) { struct screen *s = ctx->s; struct grid_cell gc; u_int cx, cy, i; cx = s->cx; cy = s->cy; memcpy(&gc, &grid_default_cell, sizeof gc); gc.attr |= GRID_ATTR_CHARSET; screen_write_putc(ctx, &gc, left ? 't' : 'q'); for (i = 1; i < nx - 1; i++) screen_write_putc(ctx, &gc, 'q'); screen_write_putc(ctx, &gc, right ? 'u' : 'q'); screen_write_set_cursor(ctx, cx, cy); } /* Draw a horizontal line on screen. */ void screen_write_vline(struct screen_write_ctx *ctx, u_int ny, int top, int bottom) { struct screen *s = ctx->s; struct grid_cell gc; u_int cx, cy, i; cx = s->cx; cy = s->cy; memcpy(&gc, &grid_default_cell, sizeof gc); gc.attr |= GRID_ATTR_CHARSET; screen_write_putc(ctx, &gc, top ? 'w' : 'x'); for (i = 1; i < ny - 1; i++) { screen_write_set_cursor(ctx, cx, cy + i); screen_write_putc(ctx, &gc, 'x'); } screen_write_set_cursor(ctx, cx, cy + ny - 1); screen_write_putc(ctx, &gc, bottom ? 'v' : 'x'); screen_write_set_cursor(ctx, cx, cy); } /* Draw a menu on screen. */ void screen_write_menu(struct screen_write_ctx *ctx, struct menu *menu, int choice) { struct screen *s = ctx->s; struct grid_cell gc; u_int cx, cy, i, j; const char *name; cx = s->cx; cy = s->cy; memcpy(&gc, &grid_default_cell, sizeof gc); screen_write_box(ctx, menu->width + 4, menu->count + 2); screen_write_cursormove(ctx, cx + 2, cy, 0); format_draw(ctx, &gc, menu->width, menu->title, NULL); for (i = 0; i < menu->count; i++) { name = menu->items[i].name; if (name == NULL) { screen_write_cursormove(ctx, cx, cy + 1 + i, 0); screen_write_hline(ctx, menu->width + 4, 1, 1); } else { if (choice >= 0 && i == (u_int)choice && *name != '-') gc.attr |= GRID_ATTR_REVERSE; screen_write_cursormove(ctx, cx + 2, cy + 1 + i, 0); for (j = 0; j < menu->width; j++) screen_write_putc(ctx, &gc, ' '); screen_write_cursormove(ctx, cx + 2, cy + 1 + i, 0); if (*name == '-') { name++; gc.attr |= GRID_ATTR_DIM; format_draw(ctx, &gc, menu->width, name, NULL); gc.attr &= ~GRID_ATTR_DIM; } else format_draw(ctx, &gc, menu->width, name, NULL); if (choice >= 0 && i == (u_int)choice) gc.attr &= ~GRID_ATTR_REVERSE; } } screen_write_set_cursor(ctx, cx, cy); } /* Draw a box on screen. */ void screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny) { struct screen *s = ctx->s; struct grid_cell gc; u_int cx, cy, i; cx = s->cx; cy = s->cy; memcpy(&gc, &grid_default_cell, sizeof gc); gc.attr |= GRID_ATTR_CHARSET; screen_write_putc(ctx, &gc, 'l'); for (i = 1; i < nx - 1; i++) screen_write_putc(ctx, &gc, 'q'); screen_write_putc(ctx, &gc, 'k'); screen_write_set_cursor(ctx, cx, cy + ny - 1); screen_write_putc(ctx, &gc, 'm'); for (i = 1; i < nx - 1; i++) screen_write_putc(ctx, &gc, 'q'); screen_write_putc(ctx, &gc, 'j'); for (i = 1; i < ny - 1; i++) { screen_write_set_cursor(ctx, cx, cy + i); screen_write_putc(ctx, &gc, 'x'); } for (i = 1; i < ny - 1; i++) { screen_write_set_cursor(ctx, cx + nx - 1, cy + i); screen_write_putc(ctx, &gc, 'x'); } screen_write_set_cursor(ctx, cx, cy); } /* * Write a preview version of a window. Assumes target area is big enough and * already cleared. */ void screen_write_preview(struct screen_write_ctx *ctx, struct screen *src, u_int nx, u_int ny) { struct screen *s = ctx->s; struct grid_cell gc; u_int cx, cy, px, py; cx = s->cx; cy = s->cy; /* * If the cursor is on, pick the area around the cursor, otherwise use * the top left. */ if (src->mode & MODE_CURSOR) { px = src->cx; if (px < nx / 3) px = 0; else px = px - nx / 3; if (px + nx > screen_size_x(src)) { if (nx > screen_size_x(src)) px = 0; else px = screen_size_x(src) - nx; } py = src->cy; if (py < ny / 3) py = 0; else py = py - ny / 3; if (py + ny > screen_size_y(src)) { if (ny > screen_size_y(src)) py = 0; else py = screen_size_y(src) - ny; } } else { px = 0; py = 0; } screen_write_fast_copy(ctx, src, px, src->grid->hsize + py, nx, ny); if (src->mode & MODE_CURSOR) { grid_view_get_cell(src->grid, src->cx, src->cy, &gc); gc.attr |= GRID_ATTR_REVERSE; screen_write_set_cursor(ctx, cx + (src->cx - px), cy + (src->cy - py)); screen_write_cell(ctx, &gc); } } /* Set up context for TTY command. */ static void screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx) { struct screen *s = ctx->s; memset(ttyctx, 0, sizeof *ttyctx); ttyctx->wp = ctx->wp; ttyctx->ocx = s->cx; ttyctx->ocy = s->cy; ttyctx->orlower = s->rlower; ttyctx->orupper = s->rupper; } /* Set a mode. */ void screen_write_mode_set(struct screen_write_ctx *ctx, int mode) { struct screen *s = ctx->s; s->mode |= mode; } /* Clear a mode. */ void screen_write_mode_clear(struct screen_write_ctx *ctx, int mode) { struct screen *s = ctx->s; s->mode &= ~mode; } /* Cursor up by ny. */ void screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny) { struct screen *s = ctx->s; u_int cx = s->cx, cy = s->cy; if (ny == 0) ny = 1; if (cy < s->rupper) { /* Above region. */ if (ny > cy) ny = cy; } else { /* Below region. */ if (ny > cy - s->rupper) ny = cy - s->rupper; } if (cx == screen_size_x(s)) cx--; cy -= ny; screen_write_set_cursor(ctx, cx, cy); } /* Cursor down by ny. */ void screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny) { struct screen *s = ctx->s; u_int cx = s->cx, cy = s->cy; if (ny == 0) ny = 1; if (cy > s->rlower) { /* Below region. */ if (ny > screen_size_y(s) - 1 - cy) ny = screen_size_y(s) - 1 - cy; } else { /* Above region. */ if (ny > s->rlower - cy) ny = s->rlower - cy; } if (cx == screen_size_x(s)) cx--; else if (ny == 0) return; cy += ny; screen_write_set_cursor(ctx, cx, cy); } /* Cursor right by nx. */ void screen_write_cursorright(struct screen_write_ctx *ctx, u_int nx) { struct screen *s = ctx->s; u_int cx = s->cx, cy = s->cy; if (nx == 0) nx = 1; if (nx > screen_size_x(s) - 1 - cx) nx = screen_size_x(s) - 1 - cx; if (nx == 0) return; cx += nx; screen_write_set_cursor(ctx, cx, cy); } /* Cursor left by nx. */ void screen_write_cursorleft(struct screen_write_ctx *ctx, u_int nx) { struct screen *s = ctx->s; u_int cx = s->cx, cy = s->cy; if (nx == 0) nx = 1; if (nx > cx) nx = cx; if (nx == 0) return; cx -= nx; screen_write_set_cursor(ctx, cx, cy); } /* 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; u_int cx = s->cx, cy = s->cy; if (cx == 0) { if (cy == 0) return; gl = grid_get_line(s->grid, s->grid->hsize + cy - 1); if (gl->flags & GRID_LINE_WRAPPED) { cy--; cx = screen_size_x(s) - 1; } } else cx--; screen_write_set_cursor(ctx, cx, cy); } /* 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; memcpy(&gc, &grid_default_cell, sizeof gc); utf8_set(&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); } screen_write_set_cursor(ctx, 0, 0); s->rupper = 0; s->rlower = screen_size_y(s) - 1; screen_write_initctx(ctx, &ttyctx); screen_write_collect_clear(ctx, 0, 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, u_int bg) { 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; if (s->cx > screen_size_x(s) - 1) return; screen_write_initctx(ctx, &ttyctx); ttyctx.bg = bg; grid_view_insert_cells(s->grid, s->cx, s->cy, nx, bg); screen_write_collect_flush(ctx, 0); ttyctx.num = nx; tty_write(tty_cmd_insertcharacter, &ttyctx); } /* Delete nx characters. */ void screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) { 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; if (s->cx > screen_size_x(s) - 1) return; screen_write_initctx(ctx, &ttyctx); ttyctx.bg = bg; grid_view_delete_cells(s->grid, s->cx, s->cy, nx, bg); screen_write_collect_flush(ctx, 0); ttyctx.num = nx; tty_write(tty_cmd_deletecharacter, &ttyctx); } /* Clear nx characters. */ void screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) { 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; if (s->cx > screen_size_x(s) - 1) return; screen_write_initctx(ctx, &ttyctx); ttyctx.bg = bg; grid_view_clear(s->grid, s->cx, s->cy, nx, 1, bg); screen_write_collect_flush(ctx, 0); ttyctx.num = nx; tty_write(tty_cmd_clearcharacter, &ttyctx); } /* Insert ny lines. */ void screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg) { struct screen *s = ctx->s; struct grid *gd = s->grid; 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); ttyctx.bg = bg; grid_view_insert_lines(gd, s->cy, ny, bg); screen_write_collect_flush(ctx, 0); 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); ttyctx.bg = bg; if (s->cy < s->rupper || s->cy > s->rlower) grid_view_insert_lines(gd, s->cy, ny, bg); else grid_view_insert_lines_region(gd, s->rlower, s->cy, ny, bg); screen_write_collect_flush(ctx, 0); ttyctx.num = ny; tty_write(tty_cmd_insertline, &ttyctx); } /* Delete ny lines. */ void screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) { struct screen *s = ctx->s; struct grid *gd = s->grid; 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); ttyctx.bg = bg; grid_view_delete_lines(gd, s->cy, ny, bg); screen_write_collect_flush(ctx, 0); 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); ttyctx.bg = bg; if (s->cy < s->rupper || s->cy > s->rlower) grid_view_delete_lines(gd, s->cy, ny, bg); else grid_view_delete_lines_region(gd, s->rlower, s->cy, ny, bg); screen_write_collect_flush(ctx, 0); ttyctx.num = ny; tty_write(tty_cmd_deleteline, &ttyctx); } /* Clear line at cursor. */ void screen_write_clearline(struct screen_write_ctx *ctx, u_int bg) { struct screen *s = ctx->s; struct grid_line *gl; struct tty_ctx ttyctx; u_int sx = screen_size_x(s); gl = grid_get_line(s->grid, s->grid->hsize + s->cy); if (gl->cellsize == 0 && COLOUR_DEFAULT(bg)) return; screen_write_initctx(ctx, &ttyctx); ttyctx.bg = bg; grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); screen_write_collect_clear(ctx, s->cy, 1); screen_write_collect_flush(ctx, 0); tty_write(tty_cmd_clearline, &ttyctx); } /* Clear to end of line from cursor. */ void screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg) { struct screen *s = ctx->s; struct grid_line *gl; struct tty_ctx ttyctx; u_int sx = screen_size_x(s); gl = grid_get_line(s->grid, s->grid->hsize + s->cy); if (s->cx > sx - 1 || (s->cx >= gl->cellsize && COLOUR_DEFAULT(bg))) return; screen_write_initctx(ctx, &ttyctx); ttyctx.bg = bg; grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg); if (s->cx == 0) screen_write_collect_clear(ctx, s->cy, 1); screen_write_collect_flush(ctx, 0); tty_write(tty_cmd_clearendofline, &ttyctx); } /* Clear to start of line from cursor. */ void screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg) { struct screen *s = ctx->s; struct tty_ctx ttyctx; u_int sx = screen_size_x(s); screen_write_initctx(ctx, &ttyctx); ttyctx.bg = bg; if (s->cx > sx - 1) grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); else grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg); if (s->cx > sx - 1) screen_write_collect_clear(ctx, s->cy, 1); screen_write_collect_flush(ctx, 0); tty_write(tty_cmd_clearstartofline, &ttyctx); } /* Move cursor to px,py. */ void screen_write_cursormove(struct screen_write_ctx *ctx, int px, int py, int origin) { struct screen *s = ctx->s; if (origin && py != -1 && (s->mode & MODE_ORIGIN)) { if ((u_int)py > s->rlower - s->rupper) py = s->rlower; else py += s->rupper; } if (px != -1 && (u_int)px > screen_size_x(s) - 1) px = screen_size_x(s) - 1; if (py != -1 && (u_int)py > screen_size_y(s) - 1) py = screen_size_y(s) - 1; screen_write_set_cursor(ctx, px, py); } /* Reverse index (up with scroll). */ void screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg) { struct screen *s = ctx->s; struct tty_ctx ttyctx; screen_write_initctx(ctx, &ttyctx); ttyctx.bg = bg; if (s->cy == s->rupper) grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg); else if (s->cy > 0) screen_write_set_cursor(ctx, -1, s->cy - 1); screen_write_collect_flush(ctx, 0); 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; screen_write_collect_flush(ctx, 0); /* Cursor moves to top-left. */ screen_write_set_cursor(ctx, 0, 0); s->rupper = rupper; s->rlower = rlower; } /* Line feed. */ void screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg) { struct screen *s = ctx->s; struct grid *gd = s->grid; struct grid_line *gl; gl = grid_get_line(gd, gd->hsize + s->cy); if (wrapped) gl->flags |= GRID_LINE_WRAPPED; else gl->flags &= ~GRID_LINE_WRAPPED; log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy, s->rupper, s->rlower); if (bg != ctx->bg) { screen_write_collect_flush(ctx, 1); ctx->bg = bg; } if (s->cy == s->rlower) { grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg); screen_write_collect_scroll(ctx); ctx->scrolled++; } else if (s->cy < screen_size_y(s) - 1) screen_write_set_cursor(ctx, -1, s->cy + 1); } /* Scroll up. */ void screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg) { struct screen *s = ctx->s; struct grid *gd = s->grid; u_int i; if (lines == 0) lines = 1; else if (lines > s->rlower - s->rupper + 1) lines = s->rlower - s->rupper + 1; if (bg != ctx->bg) { screen_write_collect_flush(ctx, 1); ctx->bg = bg; } for (i = 0; i < lines; i++) { grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg); screen_write_collect_scroll(ctx); } ctx->scrolled += lines; } /* Scroll down. */ void screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg) { struct screen *s = ctx->s; struct grid *gd = s->grid; struct tty_ctx ttyctx; u_int i; screen_write_initctx(ctx, &ttyctx); ttyctx.bg = bg; if (lines == 0) lines = 1; else if (lines > s->rlower - s->rupper + 1) lines = s->rlower - s->rupper + 1; for (i = 0; i < lines; i++) grid_view_scroll_region_down(gd, s->rupper, s->rlower, bg); screen_write_collect_flush(ctx, 0); ttyctx.num = lines; tty_write(tty_cmd_scrolldown, &ttyctx); } /* Carriage return (cursor to start of line). */ void screen_write_carriagereturn(struct screen_write_ctx *ctx) { screen_write_set_cursor(ctx, 0, -1); } /* Clear to end of screen from cursor. */ void screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg) { struct screen *s = ctx->s; struct grid *gd = s->grid; struct tty_ctx ttyctx; u_int sx = screen_size_x(s), sy = screen_size_y(s); screen_write_initctx(ctx, &ttyctx); ttyctx.bg = bg; /* Scroll into history if it is enabled and clearing entire screen. */ if (s->cx == 0 && s->cy == 0 && (gd->flags & GRID_HISTORY)) grid_view_clear_history(gd, bg); else { if (s->cx <= sx - 1) grid_view_clear(gd, s->cx, s->cy, sx - s->cx, 1, bg); grid_view_clear(gd, 0, s->cy + 1, sx, sy - (s->cy + 1), bg); } screen_write_collect_clear(ctx, s->cy + 1, sy - (s->cy + 1)); screen_write_collect_flush(ctx, 0); tty_write(tty_cmd_clearendofscreen, &ttyctx); } /* Clear to start of screen. */ void screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg) { struct screen *s = ctx->s; struct tty_ctx ttyctx; u_int sx = screen_size_x(s); screen_write_initctx(ctx, &ttyctx); ttyctx.bg = bg; if (s->cy > 0) grid_view_clear(s->grid, 0, 0, sx, s->cy, bg); if (s->cx > sx - 1) grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); else grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg); screen_write_collect_clear(ctx, 0, s->cy); screen_write_collect_flush(ctx, 0); tty_write(tty_cmd_clearstartofscreen, &ttyctx); } /* Clear entire screen. */ void screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg) { struct screen *s = ctx->s; struct tty_ctx ttyctx; u_int sx = screen_size_x(s), sy = screen_size_y(s); screen_write_initctx(ctx, &ttyctx); ttyctx.bg = bg; /* Scroll into history if it is enabled. */ if (s->grid->flags & GRID_HISTORY) grid_view_clear_history(s->grid, bg); else grid_view_clear(s->grid, 0, 0, sx, sy, bg); screen_write_collect_clear(ctx, 0, sy); tty_write(tty_cmd_clearscreen, &ttyctx); } /* Clear entire history. */ void screen_write_clearhistory(struct screen_write_ctx *ctx) { grid_clear_history(ctx->s->grid); } /* Clear a collected line. */ static void screen_write_collect_clear(struct screen_write_ctx *ctx, u_int y, u_int n) { struct screen_write_collect_item *ci, *tmp; u_int i; size_t size; for (i = y; i < y + n; i++) { if (TAILQ_EMPTY(&ctx->list[i].items)) continue; size = 0; TAILQ_FOREACH_SAFE(ci, &ctx->list[i].items, entry, tmp) { size += ci->used; TAILQ_REMOVE(&ctx->list[i].items, ci, entry); free(ci); } ctx->skipped += size; log_debug("%s: dropped %zu bytes (line %u)", __func__, size, i); } } /* Scroll collected lines up. */ static void screen_write_collect_scroll(struct screen_write_ctx *ctx) { struct screen *s = ctx->s; struct screen_write_collect_line *cl; u_int y; log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy, s->rupper, s->rlower); screen_write_collect_clear(ctx, s->rupper, 1); for (y = s->rupper; y < s->rlower; y++) { cl = &ctx->list[y + 1]; TAILQ_CONCAT(&ctx->list[y].items, &cl->items, entry); TAILQ_INIT(&cl->items); } } /* Flush collected lines. */ static void screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only) { struct screen *s = ctx->s; struct screen_write_collect_item *ci, *tmp; u_int y, cx, cy, items = 0; struct tty_ctx ttyctx; size_t written = 0; if (ctx->scrolled != 0) { log_debug("%s: scrolled %u (region %u-%u)", __func__, ctx->scrolled, s->rupper, s->rlower); if (ctx->scrolled > s->rlower - s->rupper + 1) ctx->scrolled = s->rlower - s->rupper + 1; screen_write_initctx(ctx, &ttyctx); ttyctx.num = ctx->scrolled; ttyctx.bg = ctx->bg; tty_write(tty_cmd_scrollup, &ttyctx); } ctx->scrolled = 0; ctx->bg = 8; if (scroll_only) return; cx = s->cx; cy = s->cy; for (y = 0; y < screen_size_y(s); y++) { TAILQ_FOREACH_SAFE(ci, &ctx->list[y].items, entry, tmp) { screen_write_set_cursor(ctx, ci->x, y); screen_write_initctx(ctx, &ttyctx); ttyctx.cell = &ci->gc; ttyctx.wrapped = ci->wrapped; ttyctx.ptr = ci->data; ttyctx.num = ci->used; tty_write(tty_cmd_cells, &ttyctx); items++; written += ci->used; TAILQ_REMOVE(&ctx->list[y].items, ci, entry); free(ci); } } s->cx = cx; s->cy = cy; log_debug("%s: flushed %u items (%zu bytes)", __func__, items, written); ctx->written += written; } /* Finish and store collected cells. */ void screen_write_collect_end(struct screen_write_ctx *ctx) { struct screen *s = ctx->s; struct screen_write_collect_item *ci = ctx->item; struct grid_cell gc; u_int xx; if (ci->used == 0) return; ci->data[ci->used] = '\0'; ci->x = s->cx; TAILQ_INSERT_TAIL(&ctx->list[s->cy].items, ci, entry); ctx->item = xcalloc(1, sizeof *ctx->item); log_debug("%s: %u %s (at %u,%u)", __func__, ci->used, ci->data, s->cx, s->cy); if (s->cx != 0) { for (xx = s->cx; xx > 0; xx--) { grid_view_get_cell(s->grid, xx, s->cy, &gc); if (~gc.flags & GRID_FLAG_PADDING) break; grid_view_set_cell(s->grid, xx, s->cy, &grid_default_cell); } if (gc.data.width > 1) { grid_view_set_cell(s->grid, xx, s->cy, &grid_default_cell); } } memcpy(&gc, &ci->gc, sizeof gc); grid_view_set_cells(s->grid, s->cx, s->cy, &gc, ci->data, ci->used); screen_write_set_cursor(ctx, s->cx + ci->used, -1); for (xx = s->cx; xx < screen_size_x(s); xx++) { grid_view_get_cell(s->grid, xx, s->cy, &gc); if (~gc.flags & GRID_FLAG_PADDING) break; grid_view_set_cell(s->grid, xx, s->cy, &grid_default_cell); } } /* Write cell data, collecting if necessary. */ void screen_write_collect_add(struct screen_write_ctx *ctx, const struct grid_cell *gc) { struct screen *s = ctx->s; struct screen_write_collect_item *ci; u_int sx = screen_size_x(s); int collect; /* * Don't need to check that the attributes and whatnot are still the * same - input_parse will end the collection when anything that isn't * a plain character is encountered. Also nothing should make it here * that isn't a single ASCII character. */ collect = 1; if (gc->data.width != 1 || gc->data.size != 1 || *gc->data.data >= 0x7f) collect = 0; else if (gc->attr & GRID_ATTR_CHARSET) collect = 0; else if (~s->mode & MODE_WRAP) collect = 0; else if (s->mode & MODE_INSERT) collect = 0; else if (s->sel != NULL) collect = 0; if (!collect) { screen_write_collect_end(ctx); screen_write_collect_flush(ctx, 0); screen_write_cell(ctx, gc); return; } ctx->cells++; if (s->cx > sx - 1 || ctx->item->used > sx - 1 - s->cx) screen_write_collect_end(ctx); ci = ctx->item; /* may have changed */ if (s->cx > sx - 1) { log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy); ci->wrapped = 1; screen_write_linefeed(ctx, 1, 8); screen_write_set_cursor(ctx, 0, -1); } if (ci->used == 0) memcpy(&ci->gc, gc, sizeof ci->gc); ci->data[ci->used++] = gc->data.data[0]; if (ci->used == (sizeof ci->data) - 1) screen_write_collect_end(ctx); } /* Write cell data. */ void screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) { struct screen *s = ctx->s; struct grid *gd = s->grid; struct grid_line *gl; struct grid_cell_entry *gce; struct grid_cell tmp_gc, now_gc; struct tty_ctx ttyctx; u_int sx = screen_size_x(s), sy = screen_size_y(s); u_int width = gc->data.width, xx, last, cx, cy; int selected, skip = 1; /* Ignore padding cells. */ if (gc->flags & GRID_FLAG_PADDING) return; ctx->cells++; /* If the width is zero, combine onto the previous character. */ if (width == 0) { screen_write_collect_flush(ctx, 0); if ((gc = screen_write_combine(ctx, &gc->data, &xx)) != 0) { cx = s->cx; cy = s->cy; screen_write_set_cursor(ctx, xx, s->cy); screen_write_initctx(ctx, &ttyctx); ttyctx.cell = gc; tty_write(tty_cmd_cell, &ttyctx); s->cx = cx; s->cy = cy; } return; } /* Flush any existing scrolling. */ screen_write_collect_flush(ctx, 1); /* If this character doesn't fit, ignore it. */ if ((~s->mode & MODE_WRAP) && width > 1 && (width > sx || (s->cx != sx && s->cx > sx - width))) return; /* If in insert mode, make space for the cells. */ if (s->mode & MODE_INSERT) { grid_view_insert_cells(s->grid, s->cx, s->cy, width, 8); skip = 0; } /* Check this will fit on the current line and wrap if not. */ if ((s->mode & MODE_WRAP) && s->cx > sx - width) { log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy); screen_write_linefeed(ctx, 1, 8); screen_write_set_cursor(ctx, 0, -1); screen_write_collect_flush(ctx, 1); } /* Sanity check cursor position. */ if (s->cx > sx - width || s->cy > sy - 1) return; screen_write_initctx(ctx, &ttyctx); /* Handle overwriting of UTF-8 characters. */ gl = grid_get_line(s->grid, s->grid->hsize + s->cy); if (gl->flags & GRID_LINE_EXTENDED) { grid_view_get_cell(gd, s->cx, s->cy, &now_gc); if (screen_write_overwrite(ctx, &now_gc, width)) skip = 0; } /* * 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++) { log_debug("%s: new padding at %u,%u", __func__, xx, s->cy); grid_view_set_cell(gd, xx, s->cy, &screen_write_pad_cell); skip = 0; } /* If no change, do not draw. */ if (skip) { if (s->cx >= gl->cellsize) skip = grid_cells_equal(gc, &grid_default_cell); else { gce = &gl->celldata[s->cx]; if (gce->flags & GRID_FLAG_EXTENDED) skip = 0; else if (gc->flags != gce->flags) skip = 0; else if (gc->attr != gce->data.attr) skip = 0; else if (gc->fg != gce->data.fg) skip = 0; else if (gc->bg != gce->data.bg) skip = 0; else if (gc->data.width != 1) skip = 0; else if (gc->data.size != 1) skip = 0; else if (gce->data.data != gc->data.data[0]) skip = 0; } } /* Update the selected flag and set the cell. */ selected = screen_check_selection(s, s->cx, s->cy); if (selected && (~gc->flags & GRID_FLAG_SELECTED)) { memcpy(&tmp_gc, gc, sizeof tmp_gc); tmp_gc.flags |= GRID_FLAG_SELECTED; grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc); } else if (!selected && (gc->flags & GRID_FLAG_SELECTED)) { memcpy(&tmp_gc, gc, sizeof tmp_gc); tmp_gc.flags &= ~GRID_FLAG_SELECTED; grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc); } else if (!skip) grid_view_set_cell(gd, s->cx, s->cy, gc); if (selected) skip = 0; /* * Move the cursor. If not wrapping, stick at the last character and * replace it. */ last = !(s->mode & MODE_WRAP); if (s->cx <= sx - last - width) screen_write_set_cursor(ctx, s->cx + width, -1); else screen_write_set_cursor(ctx, sx - last, -1); /* Create space for character in insert mode. */ if (s->mode & MODE_INSERT) { screen_write_collect_flush(ctx, 0); ttyctx.num = width; tty_write(tty_cmd_insertcharacter, &ttyctx); } /* Write to the screen. */ if (!skip) { if (selected) { screen_select_cell(s, &tmp_gc, gc); ttyctx.cell = &tmp_gc; } else ttyctx.cell = gc; tty_write(tty_cmd_cell, &ttyctx); ctx->written++; } else ctx->skipped++; } /* Combine a UTF-8 zero-width character onto the previous. */ static const struct grid_cell * screen_write_combine(struct screen_write_ctx *ctx, const struct utf8_data *ud, u_int *xx) { struct screen *s = ctx->s; struct grid *gd = s->grid; static struct grid_cell gc; u_int n; /* Can't combine if at 0. */ if (s->cx == 0) return (NULL); /* Empty data is out. */ if (ud->size == 0) fatalx("UTF-8 data empty"); /* Retrieve the previous cell. */ for (n = 1; n <= s->cx; n++) { grid_view_get_cell(gd, s->cx - n, s->cy, &gc); if (~gc.flags & GRID_FLAG_PADDING) break; } if (n > s->cx) return (NULL); *xx = s->cx - n; /* Check there is enough space. */ if (gc.data.size + ud->size > sizeof gc.data.data) return (NULL); log_debug("%s: %.*s onto %.*s at %u,%u", __func__, (int)ud->size, ud->data, (int)gc.data.size, gc.data.data, *xx, s->cy); /* Append the data. */ memcpy(gc.data.data + gc.data.size, ud->data, ud->size); gc.data.size += ud->size; /* Set the new cell. */ grid_view_set_cell(gd, *xx, s->cy, &gc); return (&gc); } /* * 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. */ static int screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc, u_int width) { struct screen *s = ctx->s; struct grid *gd = s->grid; struct grid_cell tmp_gc; u_int xx; int done = 0; 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) { grid_view_get_cell(gd, xx, s->cy, &tmp_gc); if (~tmp_gc.flags & GRID_FLAG_PADDING) break; log_debug("%s: padding at %u,%u", __func__, xx, s->cy); grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); } /* Overwrite the character at the start of this padding. */ log_debug("%s: character at %u,%u", __func__, xx, s->cy); grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); done = 1; } /* * Overwrite any padding cells that belong to any UTF-8 characters * we'll be overwriting with the current character. */ if (width != 1 || gc->data.width != 1 || gc->flags & GRID_FLAG_PADDING) { xx = s->cx + width - 1; while (++xx < screen_size_x(s)) { grid_view_get_cell(gd, xx, s->cy, &tmp_gc); if (~tmp_gc.flags & GRID_FLAG_PADDING) break; log_debug("%s: overwrite at %u,%u", __func__, xx, s->cy); grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); done = 1; } } return (done); } /* Set external clipboard. */ void screen_write_setselection(struct screen_write_ctx *ctx, u_char *str, u_int len) { struct tty_ctx ttyctx; screen_write_initctx(ctx, &ttyctx); ttyctx.ptr = str; ttyctx.num = len; tty_write(tty_cmd_setselection, &ttyctx); } /* Write unmodified string. */ void screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len) { struct tty_ctx ttyctx; screen_write_initctx(ctx, &ttyctx); ttyctx.ptr = str; ttyctx.num = len; tty_write(tty_cmd_rawstring, &ttyctx); } tmux-3.0a/screen.c100644 001750 001750 00000024663 13570677043 0007666/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" /* Selected area in screen. */ struct screen_sel { int hidden; int rectangle; int modekeys; u_int sx; u_int sy; u_int ex; u_int ey; struct grid_cell cell; }; /* Entry on title stack. */ struct screen_title_entry { char *text; TAILQ_ENTRY(screen_title_entry) entry; }; TAILQ_HEAD(screen_titles, screen_title_entry); static void screen_resize_y(struct screen *, u_int); static void screen_reflow(struct screen *, u_int); /* Free titles stack. */ static void screen_free_titles(struct screen *s) { struct screen_title_entry *title_entry; if (s->titles == NULL) return; while ((title_entry = TAILQ_FIRST(s->titles)) != NULL) { TAILQ_REMOVE(s->titles, title_entry, entry); free(title_entry->text); free(title_entry); } free(s->titles); s->titles = NULL; } /* Create a new screen. */ void screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit) { s->grid = grid_create(sx, sy, hlimit); s->title = xstrdup(""); s->titles = NULL; s->cstyle = 0; s->ccolour = xstrdup(""); s->tabs = NULL; s->sel = 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, 8); screen_clear_selection(s); screen_free_titles(s); } /* Destroy a screen. */ void screen_free(struct screen *s) { free(s->sel); free(s->tabs); free(s->title); free(s->ccolour); grid_destroy(s->grid); screen_free_titles(s); } /* Reset tabs to default, eight spaces apart. */ void screen_reset_tabs(struct screen *s) { u_int i; free(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 <= 6) s->cstyle = style; } /* Set screen cursor colour. */ void screen_set_cursor_colour(struct screen *s, const char *colour) { free(s->ccolour); s->ccolour = xstrdup(colour); } /* Set screen title. */ void screen_set_title(struct screen *s, const char *title) { free(s->title); utf8_stravis(&s->title, title, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); } /* Push the current title onto the stack. */ void screen_push_title(struct screen *s) { struct screen_title_entry *title_entry; if (s->titles == NULL) { s->titles = xmalloc(sizeof *s->titles); TAILQ_INIT(s->titles); } title_entry = xmalloc(sizeof *title_entry); title_entry->text = xstrdup(s->title); TAILQ_INSERT_HEAD(s->titles, title_entry, entry); } /* * Pop a title from the stack and set it as the screen title. If the stack is * empty, do nothing. */ void screen_pop_title(struct screen *s) { struct screen_title_entry *title_entry; if (s->titles == NULL) return; title_entry = TAILQ_FIRST(s->titles); if (title_entry != NULL) { screen_set_title(s, title_entry->text); TAILQ_REMOVE(s->titles, title_entry, entry); free(title_entry->text); free(title_entry); } } /* Resize screen. */ void screen_resize(struct screen *s, u_int sx, u_int sy, int reflow) { if (sx < 1) sx = 1; if (sy < 1) sy = 1; if (sx != screen_size_x(s)) { s->grid->sx = sx; screen_reset_tabs(s); } else reflow = 0; if (sy != screen_size_y(s)) screen_resize_y(s, sy); if (reflow) screen_reflow(s, sx); } static 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 scrolled * history (not explicitly cleared from view) 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, 8); } 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. */ available = s->cy; if (gd->flags & GRID_HISTORY) { gd->hscrolled += needed; gd->hsize += needed; } else if (needed > 0 && available > 0) { if (available > needed) available = needed; grid_view_delete_lines(gd, 0, available, 8); } s->cy -= needed; } /* Resize line array. */ grid_adjust_lines(gd, gd->hsize + sy); /* Size increasing. */ if (sy > oldy) { needed = sy - oldy; /* * Try to pull as much as possible out of scrolled history, if * is is enabled. */ available = gd->hscrolled; if (gd->flags & GRID_HISTORY && available > 0) { if (available > needed) available = needed; gd->hscrolled -= available; 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(grid_get_line(gd, i), 0, sizeof(struct grid_line)); } /* 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 rectangle, int modekeys, struct grid_cell *gc) { if (s->sel == NULL) s->sel = xcalloc(1, sizeof *s->sel); memcpy(&s->sel->cell, gc, sizeof s->sel->cell); s->sel->hidden = 0; s->sel->rectangle = rectangle; s->sel->modekeys = modekeys; s->sel->sx = sx; s->sel->sy = sy; s->sel->ex = ex; s->sel->ey = ey; } /* Clear selection. */ void screen_clear_selection(struct screen *s) { free(s->sel); s->sel = NULL; } /* Hide selection. */ void screen_hide_selection(struct screen *s) { if (s->sel != NULL) s->sel->hidden = 1; } /* Check if cell in selection. */ int screen_check_selection(struct screen *s, u_int px, u_int py) { struct screen_sel *sel = s->sel; u_int xx; if (sel == NULL || sel->hidden) return (0); if (sel->rectangle) { 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) return (0); if (sel->modekeys == MODEKEY_EMACS) xx = (sel->ex == 0 ? 0 : sel->ex - 1); else xx = sel->ex; if (py == sel->ey && px > xx) 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->ey && px < sel->ex) return (0); if (sel->modekeys == MODEKEY_EMACS) xx = sel->sx - 1; else xx = sel->sx; if (py == sel->sy && (sel->sx == 0 || px > xx)) 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 (sel->modekeys == MODEKEY_EMACS) xx = sel->sx - 1; else xx = sel->sx; if (px > xx || px < sel->ex) return (0); } else { /* selection start (sx) is on the left */ if (sel->modekeys == MODEKEY_EMACS) xx = (sel->ex == 0 ? 0 : sel->ex - 1); else xx = sel->ex; if (px < sel->sx || px > xx) return (0); } } } return (1); } /* Get selected grid cell. */ void screen_select_cell(struct screen *s, struct grid_cell *dst, const struct grid_cell *src) { if (s->sel == NULL || s->sel->hidden) return; memcpy(dst, &s->sel->cell, sizeof *dst); utf8_copy(&dst->data, &src->data); dst->attr = dst->attr & ~GRID_ATTR_CHARSET; dst->attr |= src->attr & GRID_ATTR_CHARSET; dst->flags = src->flags; } /* Reflow wrapped lines. */ static void screen_reflow(struct screen *s, u_int new_x) { u_int cx = s->cx, cy = s->grid->hsize + s->cy, wx, wy; struct timeval start, tv; gettimeofday(&start, NULL); grid_wrap_position(s->grid, cx, cy, &wx, &wy); log_debug("%s: cursor %u,%u is %u,%u", __func__, cx, cy, wx, wy); grid_reflow(s->grid, new_x); grid_unwrap_position(s->grid, &cx, &cy, wx, wy); log_debug("%s: new cursor is %u,%u", __func__, cx, cy); if (cy >= s->grid->hsize) { s->cx = cx; s->cy = cy - s->grid->hsize; } else { s->cx = 0; s->cy = 0; } gettimeofday(&tv, NULL); timersub(&tv, &start, &tv); log_debug("%s: reflow took %llu.%06u seconds", __func__, (unsigned long long)tv.tv_sec, (u_int)tv.tv_usec); } tmux-3.0a/server-client.c100644 001750 001750 00000151444 13570677043 0011167/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include "tmux.h" static void server_client_free(int, short, void *); static void server_client_check_focus(struct window_pane *); static void server_client_check_resize(struct window_pane *); static key_code server_client_check_mouse(struct client *, struct key_event *); static void server_client_repeat_timer(int, short, void *); static void server_client_click_timer(int, short, void *); static void server_client_check_exit(struct client *); static void server_client_check_redraw(struct client *); static void server_client_set_title(struct client *); static void server_client_reset_state(struct client *); static int server_client_assume_paste(struct session *); static void server_client_clear_overlay(struct client *); static void server_client_dispatch(struct imsg *, void *); static void server_client_dispatch_command(struct client *, struct imsg *); static void server_client_dispatch_identify(struct client *, struct imsg *); static void server_client_dispatch_shell(struct client *); /* Number of attached clients. */ u_int server_client_how_many(void) { struct client *c; u_int n; n = 0; TAILQ_FOREACH(c, &clients, entry) { if (c->session != NULL && (~c->flags & CLIENT_DETACHING)) n++; } return (n); } /* Overlay timer callback. */ static void server_client_overlay_timer(__unused int fd, __unused short events, void *data) { server_client_clear_overlay(data); } /* Set an overlay on client. */ void server_client_set_overlay(struct client *c, u_int delay, overlay_draw_cb drawcb, overlay_key_cb keycb, overlay_free_cb freecb, void *data) { struct timeval tv; if (c->overlay_draw != NULL) server_client_clear_overlay(c); tv.tv_sec = delay / 1000; tv.tv_usec = (delay % 1000) * 1000L; if (event_initialized(&c->overlay_timer)) evtimer_del(&c->overlay_timer); evtimer_set(&c->overlay_timer, server_client_overlay_timer, c); if (delay != 0) evtimer_add(&c->overlay_timer, &tv); c->overlay_draw = drawcb; c->overlay_key = keycb; c->overlay_free = freecb; c->overlay_data = data; c->tty.flags |= (TTY_FREEZE|TTY_NOCURSOR); server_redraw_client(c); } /* Clear overlay mode on client. */ static void server_client_clear_overlay(struct client *c) { if (c->overlay_draw == NULL) return; if (event_initialized(&c->overlay_timer)) evtimer_del(&c->overlay_timer); if (c->overlay_free != NULL) c->overlay_free(c); c->overlay_draw = NULL; c->overlay_key = NULL; c->tty.flags &= ~(TTY_FREEZE|TTY_NOCURSOR); server_redraw_client(c); } /* Check if this client is inside this server. */ int server_client_check_nested(struct client *c) { struct environ_entry *envent; struct window_pane *wp; envent = environ_find(c->environ, "TMUX"); if (envent == NULL || *envent->value == '\0') return (0); RB_FOREACH(wp, window_pane_tree, &all_window_panes) { if (strcmp(wp->tty, c->ttyname) == 0) return (1); } return (0); } /* Set client key table. */ void server_client_set_key_table(struct client *c, const char *name) { if (name == NULL) name = server_client_get_key_table(c); key_bindings_unref_table(c->keytable); c->keytable = key_bindings_get_table(name, 1); c->keytable->references++; } /* Get default key table. */ const char * server_client_get_key_table(struct client *c) { struct session *s = c->session; const char *name; if (s == NULL) return ("root"); name = options_get_string(s->options, "key-table"); if (*name == '\0') return ("root"); return (name); } /* Is this table the default key table? */ static int server_client_is_default_key_table(struct client *c, struct key_table *table) { return (strcmp(table->name, server_client_get_key_table(c)) == 0); } /* Create a new client. */ struct client * server_client_create(int fd) { struct client *c; setblocking(fd, 0); c = xcalloc(1, sizeof *c); c->references = 1; c->peer = proc_add_peer(server_proc, fd, server_client_dispatch, c); if (gettimeofday(&c->creation_time, NULL) != 0) fatal("gettimeofday failed"); memcpy(&c->activity_time, &c->creation_time, sizeof c->activity_time); c->environ = environ_create(); c->fd = -1; c->cwd = NULL; TAILQ_INIT(&c->queue); c->stdin_data = evbuffer_new(); if (c->stdin_data == NULL) fatalx("out of memory"); c->stdout_data = evbuffer_new(); if (c->stdout_data == NULL) fatalx("out of memory"); c->stderr_data = evbuffer_new(); if (c->stderr_data == NULL) fatalx("out of memory"); c->tty.fd = -1; c->title = NULL; c->session = NULL; c->last_session = NULL; c->tty.sx = 80; c->tty.sy = 24; status_init(c); c->message_string = NULL; TAILQ_INIT(&c->message_log); c->prompt_string = NULL; c->prompt_buffer = NULL; c->prompt_index = 0; c->flags |= CLIENT_FOCUSED; c->keytable = key_bindings_get_table("root", 1); c->keytable->references++; evtimer_set(&c->repeat_timer, server_client_repeat_timer, c); evtimer_set(&c->click_timer, server_client_click_timer, c); TAILQ_INSERT_TAIL(&clients, c, entry); log_debug("new client %p", c); return (c); } /* Open client terminal if needed. */ int server_client_open(struct client *c, char **cause) { if (c->flags & CLIENT_CONTROL) return (0); if (strcmp(c->ttyname, "/dev/tty") == 0) { *cause = xstrdup("can't use /dev/tty"); return (-1); } if (!(c->flags & CLIENT_TERMINAL)) { *cause = xstrdup("not a terminal"); return (-1); } if (tty_open(&c->tty, cause) != 0) return (-1); return (0); } /* Lost a client. */ void server_client_lost(struct client *c) { struct message_entry *msg, *msg1; c->flags |= CLIENT_DEAD; server_client_clear_overlay(c); status_prompt_clear(c); status_message_clear(c); if (c->stdin_callback != NULL) c->stdin_callback(c, 1, c->stdin_callback_data); TAILQ_REMOVE(&clients, c, entry); log_debug("lost client %p", c); /* * 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); free(c->ttyname); free(c->term); evbuffer_free(c->stdin_data); evbuffer_free(c->stdout_data); if (c->stderr_data != c->stdout_data) evbuffer_free(c->stderr_data); status_free(c); free(c->title); free((void *)c->cwd); evtimer_del(&c->repeat_timer); evtimer_del(&c->click_timer); key_bindings_unref_table(c->keytable); free(c->message_string); if (event_initialized(&c->message_timer)) evtimer_del(&c->message_timer); TAILQ_FOREACH_SAFE(msg, &c->message_log, entry, msg1) { free(msg->msg); TAILQ_REMOVE(&c->message_log, msg, entry); free(msg); } free(c->prompt_saved); free(c->prompt_string); free(c->prompt_buffer); format_lost_client(c); environ_free(c->environ); proc_remove_peer(c->peer); c->peer = NULL; server_client_unref(c); server_add_accept(0); /* may be more file descriptors now */ recalculate_sizes(); server_check_unattached(); server_update_socket(); } /* Remove reference from a client. */ void server_client_unref(struct client *c) { log_debug("unref client %p (%d references)", c, c->references); c->references--; if (c->references == 0) event_once(-1, EV_TIMEOUT, server_client_free, c, NULL); } /* Free dead client. */ static void server_client_free(__unused int fd, __unused short events, void *arg) { struct client *c = arg; log_debug("free client %p (%d references)", c, c->references); if (!TAILQ_EMPTY(&c->queue)) fatalx("queue not empty"); if (c->references == 0) { free((void *)c->name); free(c); } } /* Suspend a client. */ void server_client_suspend(struct client *c) { struct session *s = c->session; if (s == NULL || (c->flags & CLIENT_DETACHING)) return; tty_stop_tty(&c->tty); c->flags |= CLIENT_SUSPENDED; proc_send(c->peer, MSG_SUSPEND, -1, NULL, 0); } /* Detach a client. */ void server_client_detach(struct client *c, enum msgtype msgtype) { struct session *s = c->session; if (s == NULL || (c->flags & CLIENT_DETACHING)) return; c->flags |= CLIENT_DETACHING; notify_client("client-detached", c); proc_send(c->peer, msgtype, -1, s->name, strlen(s->name) + 1); } /* Execute command to replace a client. */ void server_client_exec(struct client *c, const char *cmd) { struct session *s = c->session; char *msg; const char *shell; size_t cmdsize, shellsize; if (*cmd == '\0') return; cmdsize = strlen(cmd) + 1; if (s != NULL) shell = options_get_string(s->options, "default-shell"); else shell = options_get_string(global_s_options, "default-shell"); shellsize = strlen(shell) + 1; msg = xmalloc(cmdsize + shellsize); memcpy(msg, cmd, cmdsize); memcpy(msg + cmdsize, shell, shellsize); proc_send(c->peer, MSG_EXEC, -1, msg, cmdsize + shellsize); free(msg); } /* Check for mouse keys. */ static key_code server_client_check_mouse(struct client *c, struct key_event *event) { struct mouse_event *m = &event->m; struct session *s = c->session; struct winlink *wl; struct window_pane *wp; u_int x, y, b, sx, sy, px, py; int flag; key_code key; struct timeval tv; struct style_range *sr; enum { NOTYPE, MOVE, DOWN, UP, DRAG, WHEEL, DOUBLE, TRIPLE } type = NOTYPE; enum { NOWHERE, PANE, STATUS, STATUS_LEFT, STATUS_RIGHT, STATUS_DEFAULT, BORDER } where = NOWHERE; log_debug("%s mouse %02x at %u,%u (last %u,%u) (%d)", c->name, m->b, m->x, m->y, m->lx, m->ly, c->tty.mouse_drag_flag); /* What type of event is this? */ if ((m->sgr_type != ' ' && MOUSE_DRAG(m->sgr_b) && MOUSE_BUTTONS(m->sgr_b) == 3) || (m->sgr_type == ' ' && MOUSE_DRAG(m->b) && MOUSE_BUTTONS(m->b) == 3 && MOUSE_BUTTONS(m->lb) == 3)) { type = MOVE; x = m->x, y = m->y, b = 0; log_debug("move at %u,%u", x, y); } else if (MOUSE_DRAG(m->b)) { type = DRAG; if (c->tty.mouse_drag_flag) { x = m->x, y = m->y, b = m->b; if (x == m->lx && y == m->ly) return (KEYC_UNKNOWN); log_debug("drag update at %u,%u", x, y); } else { x = m->lx, y = m->ly, b = m->lb; log_debug("drag start at %u,%u", x, y); } } else if (MOUSE_WHEEL(m->b)) { type = WHEEL; x = m->x, y = m->y, b = m->b; log_debug("wheel at %u,%u", x, y); } else if (MOUSE_RELEASE(m->b)) { type = UP; x = m->x, y = m->y, b = m->lb; log_debug("up at %u,%u", x, y); } else { if (c->flags & CLIENT_DOUBLECLICK) { evtimer_del(&c->click_timer); c->flags &= ~CLIENT_DOUBLECLICK; if (m->b == c->click_button) { type = DOUBLE; x = m->x, y = m->y, b = m->b; log_debug("double-click at %u,%u", x, y); flag = CLIENT_TRIPLECLICK; goto add_timer; } } else if (c->flags & CLIENT_TRIPLECLICK) { evtimer_del(&c->click_timer); c->flags &= ~CLIENT_TRIPLECLICK; if (m->b == c->click_button) { type = TRIPLE; x = m->x, y = m->y, b = m->b; log_debug("triple-click at %u,%u", x, y); goto have_event; } } type = DOWN; x = m->x, y = m->y, b = m->b; log_debug("down at %u,%u", x, y); flag = CLIENT_DOUBLECLICK; add_timer: if (KEYC_CLICK_TIMEOUT != 0) { c->flags |= flag; c->click_button = m->b; tv.tv_sec = KEYC_CLICK_TIMEOUT / 1000; tv.tv_usec = (KEYC_CLICK_TIMEOUT % 1000) * 1000L; evtimer_del(&c->click_timer); evtimer_add(&c->click_timer, &tv); } } have_event: if (type == NOTYPE) return (KEYC_UNKNOWN); /* Save the session. */ m->s = s->id; m->w = -1; /* Is this on the status line? */ m->statusat = status_at_line(c); m->statuslines = status_line_size(c); if (m->statusat != -1 && y >= (u_int)m->statusat && y < m->statusat + m->statuslines) { sr = status_get_range(c, x, y - m->statusat); if (sr == NULL) { where = STATUS_DEFAULT; } else { switch (sr->type) { case STYLE_RANGE_NONE: return (KEYC_UNKNOWN); case STYLE_RANGE_LEFT: where = STATUS_LEFT; break; case STYLE_RANGE_RIGHT: where = STATUS_RIGHT; break; case STYLE_RANGE_WINDOW: wl = winlink_find_by_index(&s->windows, sr->argument); if (wl == NULL) return (KEYC_UNKNOWN); m->w = wl->window->id; where = STATUS; break; } } } /* Not on status line. Adjust position and check for border or pane. */ if (where == NOWHERE) { px = x; if (m->statusat == 0 && y >= m->statuslines) py = y - m->statuslines; else if (m->statusat > 0 && y >= (u_int)m->statusat) py = m->statusat - 1; else py = y; tty_window_offset(&c->tty, &m->ox, &m->oy, &sx, &sy); log_debug("mouse window @%u at %u,%u (%ux%u)", s->curw->window->id, m->ox, m->oy, sx, sy); if (px > sx || py > sy) return (KEYC_UNKNOWN); px = px + m->ox; py = py + m->oy; /* Try the pane borders if not zoomed. */ if (~s->curw->window->flags & WINDOW_ZOOMED) { TAILQ_FOREACH(wp, &s->curw->window->panes, entry) { if ((wp->xoff + wp->sx == px && wp->yoff <= 1 + py && wp->yoff + wp->sy >= py) || (wp->yoff + wp->sy == py && wp->xoff <= 1 + px && wp->xoff + wp->sx >= px)) break; } if (wp != NULL) where = BORDER; } /* Otherwise try inside the pane. */ if (where == NOWHERE) { wp = window_get_active_at(s->curw->window, px, py); if (wp != NULL) where = PANE; } if (where == NOWHERE) return (KEYC_UNKNOWN); if (where == PANE) log_debug("mouse %u,%u on pane %%%u", x, y, wp->id); else if (where == BORDER) log_debug("mouse on pane %%%u border", wp->id); m->wp = wp->id; m->w = wp->window->id; } else m->wp = -1; /* Stop dragging if needed. */ if (type != DRAG && type != WHEEL && c->tty.mouse_drag_flag) { if (c->tty.mouse_drag_release != NULL) c->tty.mouse_drag_release(c, m); c->tty.mouse_drag_update = NULL; c->tty.mouse_drag_release = NULL; /* * End a mouse drag by passing a MouseDragEnd key corresponding * to the button that started the drag. */ switch (c->tty.mouse_drag_flag) { case 1: if (where == PANE) key = KEYC_MOUSEDRAGEND1_PANE; if (where == STATUS) key = KEYC_MOUSEDRAGEND1_STATUS; if (where == STATUS_LEFT) key = KEYC_MOUSEDRAGEND1_STATUS_LEFT; if (where == STATUS_RIGHT) key = KEYC_MOUSEDRAGEND1_STATUS_RIGHT; if (where == STATUS_DEFAULT) key = KEYC_MOUSEDRAGEND1_STATUS_DEFAULT; if (where == BORDER) key = KEYC_MOUSEDRAGEND1_BORDER; break; case 2: if (where == PANE) key = KEYC_MOUSEDRAGEND2_PANE; if (where == STATUS) key = KEYC_MOUSEDRAGEND2_STATUS; if (where == STATUS_LEFT) key = KEYC_MOUSEDRAGEND2_STATUS_LEFT; if (where == STATUS_RIGHT) key = KEYC_MOUSEDRAGEND2_STATUS_RIGHT; if (where == STATUS_DEFAULT) key = KEYC_MOUSEDRAGEND2_STATUS_DEFAULT; if (where == BORDER) key = KEYC_MOUSEDRAGEND2_BORDER; break; case 3: if (where == PANE) key = KEYC_MOUSEDRAGEND3_PANE; if (where == STATUS) key = KEYC_MOUSEDRAGEND3_STATUS; if (where == STATUS_LEFT) key = KEYC_MOUSEDRAGEND3_STATUS_LEFT; if (where == STATUS_RIGHT) key = KEYC_MOUSEDRAGEND3_STATUS_RIGHT; if (where == STATUS_DEFAULT) key = KEYC_MOUSEDRAGEND3_STATUS_DEFAULT; if (where == BORDER) key = KEYC_MOUSEDRAGEND3_BORDER; break; default: key = KEYC_MOUSE; break; } c->tty.mouse_drag_flag = 0; return (key); } /* Convert to a key binding. */ key = KEYC_UNKNOWN; switch (type) { case NOTYPE: break; case MOVE: if (where == PANE) key = KEYC_MOUSEMOVE_PANE; if (where == STATUS) key = KEYC_MOUSEMOVE_STATUS; if (where == STATUS_LEFT) key = KEYC_MOUSEMOVE_STATUS_LEFT; if (where == STATUS_RIGHT) key = KEYC_MOUSEMOVE_STATUS_RIGHT; if (where == STATUS_DEFAULT) key = KEYC_MOUSEMOVE_STATUS_DEFAULT; if (where == BORDER) key = KEYC_MOUSEMOVE_BORDER; break; case DRAG: if (c->tty.mouse_drag_update != NULL) key = KEYC_DRAGGING; else { switch (MOUSE_BUTTONS(b)) { case 0: if (where == PANE) key = KEYC_MOUSEDRAG1_PANE; if (where == STATUS) key = KEYC_MOUSEDRAG1_STATUS; if (where == STATUS_LEFT) key = KEYC_MOUSEDRAG1_STATUS_LEFT; if (where == STATUS_RIGHT) key = KEYC_MOUSEDRAG1_STATUS_RIGHT; if (where == STATUS_DEFAULT) key = KEYC_MOUSEDRAG1_STATUS_DEFAULT; if (where == BORDER) key = KEYC_MOUSEDRAG1_BORDER; break; case 1: if (where == PANE) key = KEYC_MOUSEDRAG2_PANE; if (where == STATUS) key = KEYC_MOUSEDRAG2_STATUS; if (where == STATUS_LEFT) key = KEYC_MOUSEDRAG2_STATUS_LEFT; if (where == STATUS_RIGHT) key = KEYC_MOUSEDRAG2_STATUS_RIGHT; if (where == STATUS_DEFAULT) key = KEYC_MOUSEDRAG2_STATUS_DEFAULT; if (where == BORDER) key = KEYC_MOUSEDRAG2_BORDER; break; case 2: if (where == PANE) key = KEYC_MOUSEDRAG3_PANE; if (where == STATUS) key = KEYC_MOUSEDRAG3_STATUS; if (where == STATUS_LEFT) key = KEYC_MOUSEDRAG3_STATUS_LEFT; if (where == STATUS_RIGHT) key = KEYC_MOUSEDRAG3_STATUS_RIGHT; if (where == STATUS_DEFAULT) key = KEYC_MOUSEDRAG3_STATUS_DEFAULT; if (where == BORDER) key = KEYC_MOUSEDRAG3_BORDER; break; } } /* * Begin a drag by setting the flag to a non-zero value that * corresponds to the mouse button in use. */ c->tty.mouse_drag_flag = MOUSE_BUTTONS(b) + 1; break; case WHEEL: if (MOUSE_BUTTONS(b) == MOUSE_WHEEL_UP) { if (where == PANE) key = KEYC_WHEELUP_PANE; if (where == STATUS) key = KEYC_WHEELUP_STATUS; if (where == STATUS_LEFT) key = KEYC_WHEELUP_STATUS_LEFT; if (where == STATUS_RIGHT) key = KEYC_WHEELUP_STATUS_RIGHT; if (where == STATUS_DEFAULT) key = KEYC_WHEELUP_STATUS_DEFAULT; if (where == BORDER) key = KEYC_WHEELUP_BORDER; } else { if (where == PANE) key = KEYC_WHEELDOWN_PANE; if (where == STATUS) key = KEYC_WHEELDOWN_STATUS; if (where == STATUS_LEFT) key = KEYC_WHEELDOWN_STATUS_LEFT; if (where == STATUS_RIGHT) key = KEYC_WHEELDOWN_STATUS_RIGHT; if (where == STATUS_DEFAULT) key = KEYC_WHEELDOWN_STATUS_DEFAULT; if (where == BORDER) key = KEYC_WHEELDOWN_BORDER; } break; case UP: switch (MOUSE_BUTTONS(b)) { case 0: if (where == PANE) key = KEYC_MOUSEUP1_PANE; if (where == STATUS) key = KEYC_MOUSEUP1_STATUS; if (where == STATUS_LEFT) key = KEYC_MOUSEUP1_STATUS_LEFT; if (where == STATUS_RIGHT) key = KEYC_MOUSEUP1_STATUS_RIGHT; if (where == STATUS_DEFAULT) key = KEYC_MOUSEUP1_STATUS_DEFAULT; if (where == BORDER) key = KEYC_MOUSEUP1_BORDER; break; case 1: if (where == PANE) key = KEYC_MOUSEUP2_PANE; if (where == STATUS) key = KEYC_MOUSEUP2_STATUS; if (where == STATUS_LEFT) key = KEYC_MOUSEUP2_STATUS_LEFT; if (where == STATUS_RIGHT) key = KEYC_MOUSEUP2_STATUS_RIGHT; if (where == STATUS_DEFAULT) key = KEYC_MOUSEUP2_STATUS_DEFAULT; if (where == BORDER) key = KEYC_MOUSEUP2_BORDER; break; case 2: if (where == PANE) key = KEYC_MOUSEUP3_PANE; if (where == STATUS) key = KEYC_MOUSEUP3_STATUS; if (where == STATUS_LEFT) key = KEYC_MOUSEUP3_STATUS_LEFT; if (where == STATUS_RIGHT) key = KEYC_MOUSEUP3_STATUS_RIGHT; if (where == STATUS_DEFAULT) key = KEYC_MOUSEUP3_STATUS_DEFAULT; if (where == BORDER) key = KEYC_MOUSEUP3_BORDER; break; } break; case DOWN: switch (MOUSE_BUTTONS(b)) { case 0: if (where == PANE) key = KEYC_MOUSEDOWN1_PANE; if (where == STATUS) key = KEYC_MOUSEDOWN1_STATUS; if (where == STATUS_LEFT) key = KEYC_MOUSEDOWN1_STATUS_LEFT; if (where == STATUS_RIGHT) key = KEYC_MOUSEDOWN1_STATUS_RIGHT; if (where == STATUS_DEFAULT) key = KEYC_MOUSEDOWN1_STATUS_DEFAULT; if (where == BORDER) key = KEYC_MOUSEDOWN1_BORDER; break; case 1: if (where == PANE) key = KEYC_MOUSEDOWN2_PANE; if (where == STATUS) key = KEYC_MOUSEDOWN2_STATUS; if (where == STATUS_LEFT) key = KEYC_MOUSEDOWN2_STATUS_LEFT; if (where == STATUS_RIGHT) key = KEYC_MOUSEDOWN2_STATUS_RIGHT; if (where == STATUS_DEFAULT) key = KEYC_MOUSEDOWN2_STATUS_DEFAULT; if (where == BORDER) key = KEYC_MOUSEDOWN2_BORDER; break; case 2: if (where == PANE) key = KEYC_MOUSEDOWN3_PANE; if (where == STATUS) key = KEYC_MOUSEDOWN3_STATUS; if (where == STATUS_LEFT) key = KEYC_MOUSEDOWN3_STATUS_LEFT; if (where == STATUS_RIGHT) key = KEYC_MOUSEDOWN3_STATUS_RIGHT; if (where == STATUS_DEFAULT) key = KEYC_MOUSEDOWN3_STATUS_DEFAULT; if (where == BORDER) key = KEYC_MOUSEDOWN3_BORDER; break; } break; case DOUBLE: switch (MOUSE_BUTTONS(b)) { case 0: if (where == PANE) key = KEYC_DOUBLECLICK1_PANE; if (where == STATUS) key = KEYC_DOUBLECLICK1_STATUS; if (where == STATUS_LEFT) key = KEYC_DOUBLECLICK1_STATUS_LEFT; if (where == STATUS_RIGHT) key = KEYC_DOUBLECLICK1_STATUS_RIGHT; if (where == STATUS_DEFAULT) key = KEYC_DOUBLECLICK1_STATUS_DEFAULT; if (where == BORDER) key = KEYC_DOUBLECLICK1_BORDER; break; case 1: if (where == PANE) key = KEYC_DOUBLECLICK2_PANE; if (where == STATUS) key = KEYC_DOUBLECLICK2_STATUS; if (where == STATUS_LEFT) key = KEYC_DOUBLECLICK2_STATUS_LEFT; if (where == STATUS_RIGHT) key = KEYC_DOUBLECLICK2_STATUS_RIGHT; if (where == STATUS_DEFAULT) key = KEYC_DOUBLECLICK2_STATUS_DEFAULT; if (where == BORDER) key = KEYC_DOUBLECLICK2_BORDER; break; case 2: if (where == PANE) key = KEYC_DOUBLECLICK3_PANE; if (where == STATUS) key = KEYC_DOUBLECLICK3_STATUS; if (where == STATUS_LEFT) key = KEYC_DOUBLECLICK3_STATUS_LEFT; if (where == STATUS_RIGHT) key = KEYC_DOUBLECLICK3_STATUS_RIGHT; if (where == STATUS_DEFAULT) key = KEYC_DOUBLECLICK3_STATUS_DEFAULT; if (where == BORDER) key = KEYC_DOUBLECLICK3_BORDER; break; } break; case TRIPLE: switch (MOUSE_BUTTONS(b)) { case 0: if (where == PANE) key = KEYC_TRIPLECLICK1_PANE; if (where == STATUS) key = KEYC_TRIPLECLICK1_STATUS; if (where == STATUS_LEFT) key = KEYC_TRIPLECLICK1_STATUS_LEFT; if (where == STATUS_RIGHT) key = KEYC_TRIPLECLICK1_STATUS_RIGHT; if (where == STATUS_DEFAULT) key = KEYC_TRIPLECLICK1_STATUS_DEFAULT; if (where == BORDER) key = KEYC_TRIPLECLICK1_BORDER; break; case 1: if (where == PANE) key = KEYC_TRIPLECLICK2_PANE; if (where == STATUS) key = KEYC_TRIPLECLICK2_STATUS; if (where == STATUS_LEFT) key = KEYC_TRIPLECLICK2_STATUS_LEFT; if (where == STATUS_RIGHT) key = KEYC_TRIPLECLICK2_STATUS_RIGHT; if (where == STATUS_DEFAULT) key = KEYC_TRIPLECLICK2_STATUS_DEFAULT; if (where == BORDER) key = KEYC_TRIPLECLICK2_BORDER; break; case 2: if (where == PANE) key = KEYC_TRIPLECLICK3_PANE; if (where == STATUS) key = KEYC_TRIPLECLICK3_STATUS; if (where == STATUS_LEFT) key = KEYC_TRIPLECLICK3_STATUS_LEFT; if (where == STATUS_RIGHT) key = KEYC_TRIPLECLICK3_STATUS_RIGHT; if (where == STATUS_DEFAULT) key = KEYC_TRIPLECLICK3_STATUS_DEFAULT; if (where == BORDER) key = KEYC_TRIPLECLICK3_BORDER; break; } break; } if (key == KEYC_UNKNOWN) return (KEYC_UNKNOWN); /* Apply modifiers if any. */ if (b & MOUSE_MASK_META) key |= KEYC_ESCAPE; if (b & MOUSE_MASK_CTRL) key |= KEYC_CTRL; if (b & MOUSE_MASK_SHIFT) key |= KEYC_SHIFT; return (key); } /* Is this fast enough to probably be a paste? */ static int server_client_assume_paste(struct session *s) { struct timeval tv; int t; if ((t = options_get_number(s->options, "assume-paste-time")) == 0) return (0); timersub(&s->activity_time, &s->last_activity_time, &tv); if (tv.tv_sec == 0 && tv.tv_usec < t * 1000) { log_debug("session %s pasting (flag %d)", s->name, !!(s->flags & SESSION_PASTING)); if (s->flags & SESSION_PASTING) return (1); s->flags |= SESSION_PASTING; return (0); } log_debug("session %s not pasting", s->name); s->flags &= ~SESSION_PASTING; return (0); } /* * Handle data key input from client. This owns and can modify the key event it * is given and is responsible for freeing it. */ static enum cmd_retval server_client_key_callback(struct cmdq_item *item, void *data) { struct client *c = item->client; struct key_event *event = data; key_code key = event->key; struct mouse_event *m = &event->m; struct session *s = c->session; struct winlink *wl; struct window_pane *wp; struct window_mode_entry *wme; struct timeval tv; struct key_table *table, *first; struct key_binding *bd; int xtimeout, flags; struct cmd_find_state fs; key_code key0; /* Check the client is good to accept input. */ if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0) goto out; wl = s->curw; /* Update the activity timer. */ if (gettimeofday(&c->activity_time, NULL) != 0) fatal("gettimeofday failed"); session_update_activity(s, &c->activity_time); /* Check for mouse keys. */ m->valid = 0; if (key == KEYC_MOUSE) { if (c->flags & CLIENT_READONLY) goto out; key = server_client_check_mouse(c, event); if (key == KEYC_UNKNOWN) goto out; m->valid = 1; m->key = key; /* * Mouse drag is in progress, so fire the callback (now that * the mouse event is valid). */ if (key == KEYC_DRAGGING) { c->tty.mouse_drag_update(c, m); goto out; } } /* Find affected pane. */ if (!KEYC_IS_MOUSE(key) || cmd_find_from_mouse(&fs, m, 0) != 0) cmd_find_from_session(&fs, s, 0); wp = fs.wp; /* Forward mouse keys if disabled. */ if (KEYC_IS_MOUSE(key) && !options_get_number(s->options, "mouse")) goto forward_key; /* Treat everything as a regular key when pasting is detected. */ if (!KEYC_IS_MOUSE(key) && server_client_assume_paste(s)) goto forward_key; /* * Work out the current key table. If the pane is in a mode, use * the mode table instead of the default key table. */ if (server_client_is_default_key_table(c, c->keytable) && wp != NULL && (wme = TAILQ_FIRST(&wp->modes)) != NULL && wme->mode->key_table != NULL) table = key_bindings_get_table(wme->mode->key_table(wme), 1); else table = c->keytable; first = table; table_changed: /* * The prefix always takes precedence and forces a switch to the prefix * table, unless we are already there. */ key0 = (key & ~KEYC_XTERM); if ((key0 == (key_code)options_get_number(s->options, "prefix") || key0 == (key_code)options_get_number(s->options, "prefix2")) && strcmp(table->name, "prefix") != 0) { server_client_set_key_table(c, "prefix"); server_status_client(c); goto out; } flags = c->flags; try_again: /* Log key table. */ if (wp == NULL) log_debug("key table %s (no pane)", table->name); else log_debug("key table %s (pane %%%u)", table->name, wp->id); if (c->flags & CLIENT_REPEAT) log_debug("currently repeating"); /* Try to see if there is a key binding in the current table. */ bd = key_bindings_get(table, key0); if (bd != NULL) { /* * Key was matched in this table. If currently repeating but a * non-repeating binding was found, stop repeating and try * again in the root table. */ if ((c->flags & CLIENT_REPEAT) && (~bd->flags & KEY_BINDING_REPEAT)) { log_debug("found in key table %s (not repeating)", table->name); server_client_set_key_table(c, NULL); first = table = c->keytable; c->flags &= ~CLIENT_REPEAT; server_status_client(c); goto table_changed; } log_debug("found in key table %s", table->name); /* * Take a reference to this table to make sure the key binding * doesn't disappear. */ table->references++; /* * If this is a repeating key, start the timer. Otherwise reset * the client back to the root table. */ xtimeout = options_get_number(s->options, "repeat-time"); if (xtimeout != 0 && (bd->flags & KEY_BINDING_REPEAT)) { c->flags |= CLIENT_REPEAT; tv.tv_sec = xtimeout / 1000; tv.tv_usec = (xtimeout % 1000) * 1000L; evtimer_del(&c->repeat_timer); evtimer_add(&c->repeat_timer, &tv); } else { c->flags &= ~CLIENT_REPEAT; server_client_set_key_table(c, NULL); } server_status_client(c); /* Execute the key binding. */ key_bindings_dispatch(bd, item, c, m, &fs); key_bindings_unref_table(table); goto out; } /* * No match, try the ANY key. */ if (key0 != KEYC_ANY) { key0 = KEYC_ANY; goto try_again; } /* * No match in this table. If not in the root table or if repeating, * switch the client back to the root table and try again. */ log_debug("not found in key table %s", table->name); if (!server_client_is_default_key_table(c, table) || (c->flags & CLIENT_REPEAT)) { log_debug("trying in root table"); server_client_set_key_table(c, NULL); table = c->keytable; if (c->flags & CLIENT_REPEAT) first = table; c->flags &= ~CLIENT_REPEAT; server_status_client(c); goto table_changed; } /* * No match in the root table either. If this wasn't the first table * tried, don't pass the key to the pane. */ if (first != table && (~flags & CLIENT_REPEAT)) { server_client_set_key_table(c, NULL); server_status_client(c); goto out; } forward_key: if (c->flags & CLIENT_READONLY) goto out; if (wp != NULL) window_pane_key(wp, c, s, wl, key, m); out: free(event); return (CMD_RETURN_NORMAL); } /* Handle a key event. */ int server_client_handle_key(struct client *c, struct key_event *event) { struct session *s = c->session; struct cmdq_item *item; /* Check the client is good to accept input. */ if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0) return (0); /* * Key presses in overlay mode and the command prompt are a special * case. The queue might be blocked so they need to be processed * immediately rather than queued. */ if (~c->flags & CLIENT_READONLY) { status_message_clear(c); if (c->prompt_string != NULL) { if (status_prompt_key(c, event->key) == 0) return (0); } if (c->overlay_key != NULL) { switch (c->overlay_key(c, event)) { case 0: return (0); case 1: server_client_clear_overlay(c); return (0); } } server_client_clear_overlay(c); } /* * Add the key to the queue so it happens after any commands queued by * previous keys. */ item = cmdq_get_callback(server_client_key_callback, event); cmdq_append(c, item); return (1); } /* Client functions that need to happen every loop. */ void server_client_loop(void) { struct client *c; struct window *w; struct window_pane *wp; struct winlink *wl; struct session *s; int focus; TAILQ_FOREACH(c, &clients, entry) { 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. Also check pane focus and resize. */ focus = options_get_number(global_options, "focus-events"); RB_FOREACH(w, windows, &windows) { TAILQ_FOREACH(wl, &w->winlinks, wentry) { s = wl->session; if (s->attached != 0 && s->curw == wl) break; } TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->fd != -1) { if (focus) server_client_check_focus(wp); if (wl != NULL) server_client_check_resize(wp); } wp->flags &= ~PANE_REDRAW; } check_window_name(w); } } /* Check if we need to force a resize. */ static int server_client_resize_force(struct window_pane *wp) { struct timeval tv = { .tv_usec = 100000 }; struct winsize ws; /* * If we are resizing to the same size as when we entered the loop * (that is, to the same size the application currently thinks it is), * tmux may have gone through several resizes internally and thrown * away parts of the screen. So we need the application to actually * redraw even though its final size has not changed. */ if (wp->flags & PANE_RESIZEFORCE) { wp->flags &= ~PANE_RESIZEFORCE; return (0); } if (wp->sx != wp->osx || wp->sy != wp->osy || wp->sx <= 1 || wp->sy <= 1) return (0); memset(&ws, 0, sizeof ws); ws.ws_col = wp->sx; ws.ws_row = wp->sy - 1; if (wp->fd != -1 && ioctl(wp->fd, TIOCSWINSZ, &ws) == -1) #ifdef __sun if (errno != EINVAL && errno != ENXIO) #endif fatal("ioctl failed"); log_debug("%s: %%%u forcing resize", __func__, wp->id); evtimer_add(&wp->resize_timer, &tv); wp->flags |= PANE_RESIZEFORCE; return (1); } /* Resize timer event. */ static void server_client_resize_event(__unused int fd, __unused short events, void *data) { struct window_pane *wp = data; struct winsize ws; evtimer_del(&wp->resize_timer); if (!(wp->flags & PANE_RESIZE)) return; if (server_client_resize_force(wp)) return; memset(&ws, 0, sizeof ws); ws.ws_col = wp->sx; ws.ws_row = wp->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 && errno != ENXIO) #endif fatal("ioctl failed"); log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, wp->sx, wp->sy); wp->flags &= ~PANE_RESIZE; wp->osx = wp->sx; wp->osy = wp->sy; } /* Check if pane should be resized. */ static void server_client_check_resize(struct window_pane *wp) { struct timeval tv = { .tv_usec = 250000 }; if (!(wp->flags & PANE_RESIZE)) return; log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, wp->sx, wp->sy); if (!event_initialized(&wp->resize_timer)) evtimer_set(&wp->resize_timer, server_client_resize_event, wp); /* * The first resize should happen immediately, so if the timer is not * running, do it now. */ if (!evtimer_pending(&wp->resize_timer, NULL)) server_client_resize_event(-1, 0, wp); /* * If the pane is in the alternate screen, let the timer expire and * resize to give the application a chance to redraw. If not, keep * pushing the timer back. */ if (wp->saved_grid != NULL && evtimer_pending(&wp->resize_timer, NULL)) return; evtimer_del(&wp->resize_timer); evtimer_add(&wp->resize_timer, &tv); } /* Check whether pane should be focused. */ static void server_client_check_focus(struct window_pane *wp) { struct client *c; int push; /* Do we need to push the focus state? */ push = wp->flags & PANE_FOCUSPUSH; wp->flags &= ~PANE_FOCUSPUSH; /* If we're not the active pane in our window, we're not focused. */ if (wp->window->active != wp) goto not_focused; /* If we're in a mode, we're not focused. */ if (wp->screen != &wp->base) goto not_focused; /* * If our window is the current window in any focused clients with an * attached session, we're focused. */ TAILQ_FOREACH(c, &clients, entry) { if (c->session == NULL || !(c->flags & CLIENT_FOCUSED)) continue; if (c->session->attached == 0) continue; if (c->session->curw->window == wp->window) goto focused; } not_focused: if (push || (wp->flags & PANE_FOCUSED)) { if (wp->base.mode & MODE_FOCUSON) bufferevent_write(wp->event, "\033[O", 3); notify_pane("pane-focus-out", wp); } wp->flags &= ~PANE_FOCUSED; return; focused: if (push || !(wp->flags & PANE_FOCUSED)) { if (wp->base.mode & MODE_FOCUSON) bufferevent_write(wp->event, "\033[I", 3); notify_pane("pane-focus-in", wp); session_update_activity(c->session, NULL); } wp->flags |= PANE_FOCUSED; } /* * 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. */ static void server_client_reset_state(struct client *c) { struct window *w = c->session->curw->window; struct window_pane *wp = w->active, *loop; struct screen *s = wp->screen; struct options *oo = c->session->options; int mode, cursor = 0; u_int cx = 0, cy = 0, ox, oy, sx, sy; if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED)) return; if (c->overlay_draw != NULL) return; mode = s->mode; tty_region_off(&c->tty); tty_margin_off(&c->tty); /* Move cursor to pane cursor and offset. */ cursor = 0; tty_window_offset(&c->tty, &ox, &oy, &sx, &sy); if (wp->xoff + s->cx >= ox && wp->xoff + s->cx <= ox + sx && wp->yoff + s->cy >= oy && wp->yoff + s->cy <= oy + sy) { cursor = 1; cx = wp->xoff + s->cx - ox; cy = wp->yoff + s->cy - oy; if (status_at_line(c) == 0) cy += status_line_size(c); } if (!cursor) mode &= ~MODE_CURSOR; tty_cursor(&c->tty, cx, cy); /* * Set mouse mode if requested. To support dragging, always use button * mode. */ if (options_get_number(oo, "mouse")) { mode &= ~ALL_MOUSE_MODES; TAILQ_FOREACH(loop, &w->panes, entry) { if (loop->screen->mode & MODE_MOUSE_ALL) mode |= MODE_MOUSE_ALL; } if (~mode & MODE_MOUSE_ALL) mode |= MODE_MOUSE_BUTTON; } /* Clear bracketed paste mode if at the prompt. */ if (c->prompt_string != NULL) mode &= ~MODE_BRACKETPASTE; /* Set the terminal mode and reset attributes. */ tty_update_mode(&c->tty, mode, s); tty_reset(&c->tty); } /* Repeat time callback. */ static void server_client_repeat_timer(__unused int fd, __unused short events, void *data) { struct client *c = data; if (c->flags & CLIENT_REPEAT) { server_client_set_key_table(c, NULL); c->flags &= ~CLIENT_REPEAT; server_status_client(c); } } /* Double-click callback. */ static void server_client_click_timer(__unused int fd, __unused short events, void *data) { struct client *c = data; c->flags &= ~(CLIENT_DOUBLECLICK|CLIENT_TRIPLECLICK); } /* Check if client should be exited. */ static void server_client_check_exit(struct client *c) { if (~c->flags & CLIENT_EXIT) return; if (c->flags & CLIENT_EXITED) return; if (EVBUFFER_LENGTH(c->stdin_data) != 0) return; if (EVBUFFER_LENGTH(c->stdout_data) != 0) return; if (EVBUFFER_LENGTH(c->stderr_data) != 0) return; if (c->flags & CLIENT_ATTACHED) notify_client("client-detached", c); proc_send(c->peer, MSG_EXIT, -1, &c->retval, sizeof c->retval); c->flags |= CLIENT_EXITED; } /* Redraw timer callback. */ static void server_client_redraw_timer(__unused int fd, __unused short events, __unused void* data) { log_debug("redraw timer fired"); } /* Check for client redraws. */ static void server_client_check_redraw(struct client *c) { struct session *s = c->session; struct tty *tty = &c->tty; struct window_pane *wp; int needed, flags; struct timeval tv = { .tv_usec = 1000 }; static struct event ev; size_t left; if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED)) return; if (c->flags & CLIENT_ALLREDRAWFLAGS) { log_debug("%s: redraw%s%s%s%s", c->name, (c->flags & CLIENT_REDRAWWINDOW) ? " window" : "", (c->flags & CLIENT_REDRAWSTATUS) ? " status" : "", (c->flags & CLIENT_REDRAWBORDERS) ? " borders" : "", (c->flags & CLIENT_REDRAWOVERLAY) ? " overlay" : ""); } /* * If there is outstanding data, defer the redraw until it has been * consumed. We can just add a timer to get out of the event loop and * end up back here. */ needed = 0; if (c->flags & CLIENT_ALLREDRAWFLAGS) needed = 1; else { TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) { if (wp->flags & PANE_REDRAW) { needed = 1; break; } } } if (needed && (left = EVBUFFER_LENGTH(tty->out)) != 0) { log_debug("%s: redraw deferred (%zu left)", c->name, left); if (!evtimer_initialized(&ev)) evtimer_set(&ev, server_client_redraw_timer, NULL); if (!evtimer_pending(&ev, NULL)) { log_debug("redraw timer started"); evtimer_add(&ev, &tv); } /* * We may have got here for a single pane redraw, but force a * full redraw next time in case other panes have been updated. */ c->flags |= CLIENT_ALLREDRAWFLAGS; return; } else if (needed) log_debug("%s: redraw needed", c->name); flags = tty->flags & (TTY_BLOCK|TTY_FREEZE|TTY_NOCURSOR); tty->flags = (tty->flags & ~(TTY_BLOCK|TTY_FREEZE)) | TTY_NOCURSOR; if (~c->flags & CLIENT_REDRAWWINDOW) { /* * If not redrawing the entire window, check whether each pane * needs to be redrawn. */ TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) { if (wp->flags & PANE_REDRAW) { tty_update_mode(tty, tty->mode, NULL); screen_redraw_pane(c, wp); } } } if (c->flags & CLIENT_ALLREDRAWFLAGS) { if (options_get_number(s->options, "set-titles")) server_client_set_title(c); screen_redraw_screen(c); } tty->flags = (tty->flags & ~(TTY_FREEZE|TTY_NOCURSOR)) | flags; tty_update_mode(tty, tty->mode, NULL); c->flags &= ~(CLIENT_ALLREDRAWFLAGS|CLIENT_STATUSFORCE); if (needed) { /* * We would have deferred the redraw unless the output buffer * was empty, so we can record how many bytes the redraw * generated. */ c->redraw = EVBUFFER_LENGTH(tty->out); log_debug("%s: redraw added %zu bytes", c->name, c->redraw); } } /* Set client title. */ static void server_client_set_title(struct client *c) { struct session *s = c->session; const char *template; char *title; struct format_tree *ft; template = options_get_string(s->options, "set-titles-string"); ft = format_create(c, NULL, FORMAT_NONE, 0); format_defaults(ft, c, NULL, NULL, NULL); title = format_expand_time(ft, template); if (c->title == NULL || strcmp(title, c->title) != 0) { free(c->title); c->title = xstrdup(title); tty_set_title(&c->tty, c->title); } free(title); format_free(ft); } /* Dispatch message from client. */ static void server_client_dispatch(struct imsg *imsg, void *arg) { struct client *c = arg; struct msg_stdin_data stdindata; const char *data; ssize_t datalen; struct session *s; if (c->flags & CLIENT_DEAD) return; if (imsg == NULL) { server_client_lost(c); return; } data = imsg->data; datalen = imsg->hdr.len - IMSG_HEADER_SIZE; switch (imsg->hdr.type) { case MSG_IDENTIFY_FLAGS: case MSG_IDENTIFY_TERM: case MSG_IDENTIFY_TTYNAME: case MSG_IDENTIFY_CWD: case MSG_IDENTIFY_STDIN: case MSG_IDENTIFY_ENVIRON: case MSG_IDENTIFY_CLIENTPID: case MSG_IDENTIFY_DONE: server_client_dispatch_identify(c, imsg); break; case MSG_COMMAND: server_client_dispatch_command(c, imsg); break; case MSG_STDIN: if (datalen != sizeof stdindata) fatalx("bad MSG_STDIN size"); memcpy(&stdindata, data, sizeof stdindata); if (c->stdin_callback == NULL) break; if (stdindata.size <= 0) c->stdin_closed = 1; else { evbuffer_add(c->stdin_data, stdindata.data, stdindata.size); } c->stdin_callback(c, c->stdin_closed, c->stdin_callback_data); break; case MSG_RESIZE: if (datalen != 0) fatalx("bad MSG_RESIZE size"); if (c->flags & CLIENT_CONTROL) break; server_client_clear_overlay(c); tty_resize(&c->tty); recalculate_sizes(); server_redraw_client(c); if (c->session != NULL) notify_client("client-resized", c); break; case MSG_EXITING: if (datalen != 0) fatalx("bad MSG_EXITING size"); c->session = NULL; tty_close(&c->tty); proc_send(c->peer, MSG_EXITED, -1, 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 (c->tty.fd == -1) /* exited in the meantime */ break; s = c->session; if (gettimeofday(&c->activity_time, NULL) != 0) fatal("gettimeofday failed"); tty_start_tty(&c->tty); server_redraw_client(c); recalculate_sizes(); if (s != NULL) session_update_activity(s, &c->activity_time); break; case MSG_SHELL: if (datalen != 0) fatalx("bad MSG_SHELL size"); server_client_dispatch_shell(c); break; } } /* Callback when command is done. */ static enum cmd_retval server_client_command_done(struct cmdq_item *item, __unused void *data) { struct client *c = item->client; if (~c->flags & CLIENT_ATTACHED) c->flags |= CLIENT_EXIT; return (CMD_RETURN_NORMAL); } /* Handle command message. */ static void server_client_dispatch_command(struct client *c, struct imsg *imsg) { struct msg_command_data data; char *buf; size_t len; int argc; char **argv, *cause; struct cmd_parse_result *pr; if (c->flags & CLIENT_EXIT) return; if (imsg->hdr.len - IMSG_HEADER_SIZE < sizeof data) fatalx("bad MSG_COMMAND size"); memcpy(&data, imsg->data, sizeof data); buf = (char *)imsg->data + sizeof data; len = imsg->hdr.len - IMSG_HEADER_SIZE - sizeof data; if (len > 0 && buf[len - 1] != '\0') fatalx("bad MSG_COMMAND string"); argc = data.argc; if (cmd_unpack_argv(buf, len, argc, &argv) != 0) { cause = xstrdup("command too long"); goto error; } if (argc == 0) { argc = 1; argv = xcalloc(1, sizeof *argv); *argv = xstrdup("new-session"); } pr = cmd_parse_from_arguments(argc, argv, NULL); switch (pr->status) { case CMD_PARSE_EMPTY: cause = xstrdup("empty command"); goto error; case CMD_PARSE_ERROR: cause = pr->error; goto error; case CMD_PARSE_SUCCESS: break; } cmd_free_argv(argc, argv); cmdq_append(c, cmdq_get_command(pr->cmdlist, NULL, NULL, 0)); cmdq_append(c, cmdq_get_callback(server_client_command_done, NULL)); cmd_list_free(pr->cmdlist); return; error: cmd_free_argv(argc, argv); cmdq_append(c, cmdq_get_error(cause)); free(cause); c->flags |= CLIENT_EXIT; } /* Handle identify message. */ static void server_client_dispatch_identify(struct client *c, struct imsg *imsg) { const char *data, *home; size_t datalen; int flags; char *name; if (c->flags & CLIENT_IDENTIFIED) fatalx("out-of-order identify message"); data = imsg->data; datalen = imsg->hdr.len - IMSG_HEADER_SIZE; switch (imsg->hdr.type) { case MSG_IDENTIFY_FLAGS: if (datalen != sizeof flags) fatalx("bad MSG_IDENTIFY_FLAGS size"); memcpy(&flags, data, sizeof flags); c->flags |= flags; log_debug("client %p IDENTIFY_FLAGS %#x", c, flags); break; case MSG_IDENTIFY_TERM: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_TERM string"); c->term = xstrdup(data); log_debug("client %p IDENTIFY_TERM %s", c, data); break; case MSG_IDENTIFY_TTYNAME: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_TTYNAME string"); c->ttyname = xstrdup(data); log_debug("client %p IDENTIFY_TTYNAME %s", c, data); break; case MSG_IDENTIFY_CWD: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_CWD string"); if (access(data, X_OK) == 0) c->cwd = xstrdup(data); else if ((home = find_home()) != NULL) c->cwd = xstrdup(home); else c->cwd = xstrdup("/"); log_debug("client %p IDENTIFY_CWD %s", c, data); break; case MSG_IDENTIFY_STDIN: if (datalen != 0) fatalx("bad MSG_IDENTIFY_STDIN size"); c->fd = imsg->fd; log_debug("client %p IDENTIFY_STDIN %d", c, imsg->fd); break; case MSG_IDENTIFY_ENVIRON: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_ENVIRON string"); if (strchr(data, '=') != NULL) environ_put(c->environ, data); log_debug("client %p IDENTIFY_ENVIRON %s", c, data); break; case MSG_IDENTIFY_CLIENTPID: if (datalen != sizeof c->pid) fatalx("bad MSG_IDENTIFY_CLIENTPID size"); memcpy(&c->pid, data, sizeof c->pid); log_debug("client %p IDENTIFY_CLIENTPID %ld", c, (long)c->pid); break; default: break; } if (imsg->hdr.type != MSG_IDENTIFY_DONE) return; c->flags |= CLIENT_IDENTIFIED; if (*c->ttyname != '\0') name = xstrdup(c->ttyname); else xasprintf(&name, "client-%ld", (long)c->pid); c->name = name; log_debug("client %p name is %s", c, c->name); #ifdef __CYGWIN__ c->fd = open(c->ttyname, O_RDWR|O_NOCTTY); #endif if (c->flags & CLIENT_CONTROL) { c->stdin_callback = control_callback; evbuffer_free(c->stderr_data); c->stderr_data = c->stdout_data; if (c->flags & CLIENT_CONTROLCONTROL) evbuffer_add_printf(c->stdout_data, "\033P1000p"); proc_send(c->peer, MSG_STDIN, -1, NULL, 0); c->tty.fd = -1; close(c->fd); c->fd = -1; } else if (c->fd != -1) { if (tty_init(&c->tty, c, c->fd, c->term) != 0) { close(c->fd); c->fd = -1; } else { if (c->flags & CLIENT_UTF8) c->tty.flags |= TTY_UTF8; if (c->flags & CLIENT_256COLOURS) c->tty.term_flags |= TERM_256COLOURS; tty_resize(&c->tty); c->flags |= CLIENT_TERMINAL; } } /* * If this is the first client that has finished identifying, load * configuration files. */ if ((~c->flags & CLIENT_EXIT) && !cfg_finished && c == TAILQ_FIRST(&clients) && TAILQ_NEXT(c, entry) == NULL) start_cfg(); } /* Handle shell message. */ static void server_client_dispatch_shell(struct client *c) { const char *shell; shell = options_get_string(global_s_options, "default-shell"); if (*shell == '\0' || areshell(shell)) shell = _PATH_BSHELL; proc_send(c->peer, MSG_SHELL, -1, shell, strlen(shell) + 1); proc_kill_peer(c->peer); } /* Event callback to push more stdout data if any left. */ static void server_client_stdout_cb(__unused int fd, __unused short events, void *arg) { struct client *c = arg; if (~c->flags & CLIENT_DEAD) server_client_push_stdout(c); server_client_unref(c); } /* Push stdout to client if possible. */ void server_client_push_stdout(struct client *c) { struct msg_stdout_data data; size_t sent, left; left = EVBUFFER_LENGTH(c->stdout_data); while (left != 0) { sent = left; if (sent > sizeof data.data) sent = sizeof data.data; memcpy(data.data, EVBUFFER_DATA(c->stdout_data), sent); data.size = sent; if (proc_send(c->peer, MSG_STDOUT, -1, &data, sizeof data) != 0) break; evbuffer_drain(c->stdout_data, sent); left = EVBUFFER_LENGTH(c->stdout_data); log_debug("%s: client %p, sent %zu, left %zu", __func__, c, sent, left); } if (left != 0) { c->references++; event_once(-1, EV_TIMEOUT, server_client_stdout_cb, c, NULL); log_debug("%s: client %p, queued", __func__, c); } } /* Event callback to push more stderr data if any left. */ static void server_client_stderr_cb(__unused int fd, __unused short events, void *arg) { struct client *c = arg; if (~c->flags & CLIENT_DEAD) server_client_push_stderr(c); server_client_unref(c); } /* Push stderr to client if possible. */ void server_client_push_stderr(struct client *c) { struct msg_stderr_data data; size_t sent, left; if (c->stderr_data == c->stdout_data) { server_client_push_stdout(c); return; } left = EVBUFFER_LENGTH(c->stderr_data); while (left != 0) { sent = left; if (sent > sizeof data.data) sent = sizeof data.data; memcpy(data.data, EVBUFFER_DATA(c->stderr_data), sent); data.size = sent; if (proc_send(c->peer, MSG_STDERR, -1, &data, sizeof data) != 0) break; evbuffer_drain(c->stderr_data, sent); left = EVBUFFER_LENGTH(c->stderr_data); log_debug("%s: client %p, sent %zu, left %zu", __func__, c, sent, left); } if (left != 0) { c->references++; event_once(-1, EV_TIMEOUT, server_client_stderr_cb, c, NULL); log_debug("%s: client %p, queued", __func__, c); } } /* Add to client message log. */ void server_client_add_message(struct client *c, const char *fmt, ...) { struct message_entry *msg, *msg1; char *s; va_list ap; u_int limit; va_start(ap, fmt); xvasprintf(&s, fmt, ap); va_end(ap); log_debug("message %s (client %p)", s, c); msg = xcalloc(1, sizeof *msg); msg->msg_time = time(NULL); msg->msg_num = c->message_next++; msg->msg = s; TAILQ_INSERT_TAIL(&c->message_log, msg, entry); limit = options_get_number(global_options, "message-limit"); TAILQ_FOREACH_SAFE(msg, &c->message_log, entry, msg1) { if (msg->msg_num + limit >= c->message_next) break; free(msg->msg); TAILQ_REMOVE(&c->message_log, msg, entry); free(msg); } } /* Get client working directory. */ const char * server_client_get_cwd(struct client *c, struct session *s) { const char *home; if (!cfg_finished && cfg_client != NULL) return (cfg_client->cwd); if (c != NULL && c->session == NULL && c->cwd != NULL) return (c->cwd); if (s != NULL && s->cwd != NULL) return (s->cwd); if (c != NULL && (s = c->session) != NULL && s->cwd != NULL) return (s->cwd); if ((home = find_home()) != NULL) return (home); return ("/"); } /* Resolve an absolute path or relative to client working directory. */ char * server_client_get_path(struct client *c, const char *file) { char *path, resolved[PATH_MAX]; if (*file == '/') path = xstrdup(file); else xasprintf(&path, "%s/%s", server_client_get_cwd(c, NULL), file); if (realpath(path, resolved) == NULL) return (path); free(path); return (xstrdup(resolved)); } tmux-3.0a/server-fn.c100644 001750 001750 00000024077 13517540563 0010312/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include "tmux.h" static struct session *server_next_session(struct session *); static void server_destroy_session_group(struct session *); void server_redraw_client(struct client *c) { c->flags |= CLIENT_ALLREDRAWFLAGS; } void server_status_client(struct client *c) { c->flags |= CLIENT_REDRAWSTATUS; } void server_redraw_session(struct session *s) { struct client *c; TAILQ_FOREACH(c, &clients, entry) { if (c->session == s) server_redraw_client(c); } } void server_redraw_session_group(struct session *s) { struct session_group *sg; if ((sg = session_group_contains(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; TAILQ_FOREACH(c, &clients, entry) { if (c->session == s) server_status_client(c); } } void server_status_session_group(struct session *s) { struct session_group *sg; if ((sg = session_group_contains(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; TAILQ_FOREACH(c, &clients, entry) { if (c->session != NULL && c->session->curw->window == w) server_redraw_client(c); } } void server_redraw_window_borders(struct window *w) { struct client *c; TAILQ_FOREACH(c, &clients, entry) { if (c->session != NULL && c->session->curw->window == w) c->flags |= CLIENT_REDRAWBORDERS; } } 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 anywhere it is the * current window. */ RB_FOREACH(s, sessions, &sessions) { if (session_has(s, w)) server_status_session(s); } } void server_lock(void) { struct client *c; TAILQ_FOREACH(c, &clients, entry) { if (c->session != NULL) server_lock_client(c); } } void server_lock_session(struct session *s) { struct client *c; TAILQ_FOREACH(c, &clients, entry) { if (c->session == s) server_lock_client(c); } } void server_lock_client(struct client *c) { const char *cmd; if (c->flags & CLIENT_CONTROL) return; if (c->flags & CLIENT_SUSPENDED) return; cmd = options_get_string(c->session->options, "lock-command"); if (*cmd == '\0' || strlen(cmd) + 1 > MAX_IMSGSIZE - IMSG_HEADER_SIZE) 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)); tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_E3)); c->flags |= CLIENT_SUSPENDED; proc_send(c->peer, MSG_LOCK, -1, cmd, strlen(cmd) + 1); } void server_kill_pane(struct window_pane *wp) { struct window *w = wp->window; if (window_count_panes(w) == 1) { server_kill_window(w); recalculate_sizes(); } else { server_unzoom_window(w); layout_close_pane(wp); window_remove_pane(w, wp); server_redraw_window(w); } } void server_kill_window(struct window *w) { struct session *s, *next_s, *target_s; struct session_group *sg; 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)) continue; server_unzoom_window(w); 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); } if (options_get_number(s->options, "renumber-windows")) { if ((sg = session_group_contains(s)) != NULL) { TAILQ_FOREACH(target_s, &sg->sessions, gentry) session_renumber_windows(target_s); } else session_renumber_windows(s); } } recalculate_sizes(); } 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_contains(src); dstsg = session_group_contains(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. */ notify_session_window("window-unlinked", dst, dstwl->window); 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, int notify) { struct window *w = wp->window; struct screen_write_ctx ctx; struct grid_cell gc; time_t t; char tim[26]; if (wp->fd != -1) { #ifdef HAVE_UTEMPTER utempter_remove_record(wp->fd); #endif bufferevent_free(wp->event); wp->event = NULL; close(wp->fd); wp->fd = -1; } if (options_get_number(wp->options, "remain-on-exit")) { if (~wp->flags & PANE_STATUSREADY) return; if (wp->flags & PANE_STATUSDRAWN) return; wp->flags |= PANE_STATUSDRAWN; if (notify) notify_pane("pane-died", wp); screen_write_start(&ctx, wp, &wp->base); screen_write_scrollregion(&ctx, 0, screen_size_y(ctx.s) - 1); screen_write_cursormove(&ctx, 0, screen_size_y(ctx.s) - 1, 0); screen_write_linefeed(&ctx, 1, 8); memcpy(&gc, &grid_default_cell, sizeof gc); time(&t); ctime_r(&t, tim); if (WIFEXITED(wp->status)) { screen_write_nputs(&ctx, -1, &gc, "Pane is dead (status %d, %s)", WEXITSTATUS(wp->status), tim); } else if (WIFSIGNALED(wp->status)) { screen_write_nputs(&ctx, -1, &gc, "Pane is dead (signal %d, %s)", WTERMSIG(wp->status), tim); } screen_write_stop(&ctx); wp->flags |= PANE_REDRAW; return; } if (notify) notify_pane("pane-exited", wp); server_unzoom_window(w); layout_close_pane(wp); window_remove_pane(w, wp); if (TAILQ_EMPTY(&w->panes)) server_kill_window(w); else server_redraw_window(w); } static void server_destroy_session_group(struct session *s) { struct session_group *sg; struct session *s1; if ((sg = session_group_contains(s)) == NULL) server_destroy_session(s); else { TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) { server_destroy_session(s); session_destroy(s, 1, __func__); } } } static 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; if (!options_get_number(s->options, "detach-on-destroy")) s_new = server_next_session(s); else s_new = NULL; TAILQ_FOREACH(c, &clients, entry) { if (c->session != s) continue; if (s_new == NULL) { c->session = NULL; c->flags |= CLIENT_EXIT; } else { c->last_session = NULL; c->session = s_new; server_client_set_key_table(c, NULL); tty_update_client_offset(c); status_timer_start(c); notify_client("client-session-changed", c); session_update_activity(s_new, NULL); gettimeofday(&s_new->last_attached_time, NULL); server_redraw_client(c); alerts_check_session(s_new); } } 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->attached != 0) continue; if (options_get_number (s->options, "destroy-unattached")) session_destroy(s, 1, __func__); } } int server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int, void *), void *cb_data, char **cause) { if (c == NULL || c->session != NULL) { *cause = xstrdup("no client with stdin"); return (-1); } if (c->flags & CLIENT_TERMINAL) { *cause = xstrdup("stdin is a tty"); return (-1); } if (c->stdin_callback != NULL) { *cause = xstrdup("stdin is in use"); return (-1); } c->stdin_callback_data = cb_data; c->stdin_callback = cb; c->references++; if (c->stdin_closed) c->stdin_callback(c, 1, c->stdin_callback_data); proc_send(c->peer, MSG_STDIN, -1, NULL, 0); return (0); } void server_unzoom_window(struct window *w) { if (window_unzoom(w) == 0) server_redraw_window(w); } tmux-3.0a/server.c100644 001750 001750 00000023413 13537637521 0007705/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tmux.h" /* * Main server functions. */ struct clients clients; struct tmuxproc *server_proc; static int server_fd = -1; static int server_exit; static struct event server_ev_accept; struct cmd_find_state marked_pane; static int server_loop(void); static void server_send_exit(void); static void server_accept(int, short, void *); static void server_signal(int); static void server_child_signal(void); static void server_child_exited(pid_t, int); static void server_child_stopped(pid_t, int); /* Set marked pane. */ void server_set_marked(struct session *s, struct winlink *wl, struct window_pane *wp) { cmd_find_clear_state(&marked_pane, 0); marked_pane.s = s; marked_pane.wl = wl; marked_pane.w = wl->window; marked_pane.wp = wp; } /* Clear marked pane. */ void server_clear_marked(void) { cmd_find_clear_state(&marked_pane, 0); } /* Is this the marked pane? */ int server_is_marked(struct session *s, struct winlink *wl, struct window_pane *wp) { if (s == NULL || wl == NULL || wp == NULL) return (0); if (marked_pane.s != s || marked_pane.wl != wl) return (0); if (marked_pane.wp != wp) return (0); return (server_check_marked()); } /* Check if the marked pane is still valid. */ int server_check_marked(void) { return (cmd_find_valid_state(&marked_pane)); } /* Create server socket. */ static int server_create_socket(char **cause) { struct sockaddr_un sa; size_t size; mode_t mask; int fd, saved_errno; 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; goto fail; } unlink(sa.sun_path); if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) goto fail; mask = umask(S_IXUSR|S_IXGRP|S_IRWXO); if (bind(fd, (struct sockaddr *)&sa, sizeof sa) == -1) { saved_errno = errno; close(fd); errno = saved_errno; goto fail; } umask(mask); if (listen(fd, 128) == -1) { saved_errno = errno; close(fd); errno = saved_errno; goto fail; } setblocking(fd, 0); return (fd); fail: if (cause != NULL) { xasprintf(cause, "error creating %s (%s)", socket_path, strerror(errno)); } return (-1); } /* Fork new server. */ int server_start(struct tmuxproc *client, struct event_base *base, int lockfd, char *lockfile) { int pair[2]; sigset_t set, oldset; struct client *c; char *cause = NULL; if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) fatal("socketpair failed"); sigfillset(&set); sigprocmask(SIG_BLOCK, &set, &oldset); switch (fork()) { case -1: fatal("fork failed"); case 0: break; default: sigprocmask(SIG_SETMASK, &oldset, NULL); close(pair[1]); return (pair[0]); } close(pair[0]); if (daemon(1, 0) != 0) fatal("daemon failed"); proc_clear_signals(client, 0); if (event_reinit(base) != 0) fatalx("event_reinit failed"); server_proc = proc_start("server"); proc_set_signals(server_proc, server_signal); sigprocmask(SIG_SETMASK, &oldset, NULL); if (log_get_level() > 1) tty_create_log(); if (pledge("stdio rpath wpath cpath fattr unix getpw recvfd proc exec " "tty ps", NULL) != 0) fatal("pledge failed"); RB_INIT(&windows); RB_INIT(&all_window_panes); TAILQ_INIT(&clients); RB_INIT(&sessions); key_bindings_init(); gettimeofday(&start_time, NULL); server_fd = server_create_socket(&cause); if (server_fd != -1) server_update_socket(); c = server_client_create(pair[1]); if (lockfd >= 0) { unlink(lockfile); free(lockfile); close(lockfd); } if (cause != NULL) { cmdq_append(c, cmdq_get_error(cause)); free(cause); c->flags |= CLIENT_EXIT; } server_add_accept(0); proc_loop(server_proc, server_loop); job_kill_all(); status_prompt_save_history(); exit(0); } /* Server loop callback. */ static int server_loop(void) { struct client *c; u_int items; do { items = cmdq_next(NULL); TAILQ_FOREACH(c, &clients, entry) { if (c->flags & CLIENT_IDENTIFIED) items += cmdq_next(c); } } while (items != 0); server_client_loop(); if (!options_get_number(global_options, "exit-empty") && !server_exit) return (0); if (!options_get_number(global_options, "exit-unattached")) { if (!RB_EMPTY(&sessions)) return (0); } TAILQ_FOREACH(c, &clients, entry) { if (c->session != NULL) return (0); } /* * No attached clients therefore want to exit - flush any waiting * clients but don't actually exit until they've gone. */ cmd_wait_for_flush(); if (!TAILQ_EMPTY(&clients)) return (0); if (job_still_running()) return (0); return (1); } /* Exit the server by killing all clients and windows. */ static void server_send_exit(void) { struct client *c, *c1; struct session *s, *s1; cmd_wait_for_flush(); TAILQ_FOREACH_SAFE(c, &clients, entry, c1) { if (c->flags & CLIENT_SUSPENDED) server_client_lost(c); else { if (c->flags & CLIENT_ATTACHED) notify_client("client-detached", c); proc_send(c->peer, MSG_SHUTDOWN, -1, NULL, 0); } c->session = NULL; } RB_FOREACH_SAFE(s, sessions, &sessions, s1) session_destroy(s, 1, __func__); } /* 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->attached != 0) { n++; break; } } if (n != last) { last = n; if (stat(socket_path, &sb) != 0) return; mode = sb.st_mode & ACCESSPERMS; 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. */ static void server_accept(int fd, short events, __unused void *data) { struct sockaddr_storage sa; socklen_t slen = sizeof sa; int newfd; server_add_accept(0); if (!(events & EV_READ)) return; newfd = accept(fd, (struct sockaddr *) &sa, &slen); if (newfd == -1) { if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED) return; if (errno == ENFILE || errno == EMFILE) { /* Delete and don't try again for 1 second. */ server_add_accept(1); return; } fatal("accept failed"); } if (server_exit) { close(newfd); return; } server_client_create(newfd); } /* * Add accept event. If timeout is nonzero, add as a timeout instead of a read * event - used to backoff when running out of file descriptors. */ void server_add_accept(int timeout) { struct timeval tv = { timeout, 0 }; if (server_fd == -1) return; if (event_initialized(&server_ev_accept)) event_del(&server_ev_accept); if (timeout == 0) { event_set(&server_ev_accept, server_fd, EV_READ, server_accept, NULL); event_add(&server_ev_accept, NULL); } else { event_set(&server_ev_accept, server_fd, EV_TIMEOUT, server_accept, NULL); event_add(&server_ev_accept, &tv); } } /* Signal handler. */ static void server_signal(int sig) { int fd; log_debug("%s: %s", __func__, strsignal(sig)); switch (sig) { case SIGTERM: server_exit = 1; server_send_exit(); break; case SIGCHLD: server_child_signal(); break; case SIGUSR1: event_del(&server_ev_accept); fd = server_create_socket(NULL); if (fd != -1) { close(server_fd); server_fd = fd; server_update_socket(); } server_add_accept(0); break; case SIGUSR2: proc_toggle_log(server_proc); break; } } /* Handle SIGCHLD. */ static 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. */ static void server_child_exited(pid_t pid, int status) { struct window *w, *w1; struct window_pane *wp; RB_FOREACH_SAFE(w, windows, &windows, w1) { TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->pid == pid) { wp->status = status; wp->flags |= PANE_STATUSREADY; log_debug("%%%u exited", wp->id); wp->flags |= PANE_EXITED; if (window_pane_destroy_ready(wp)) server_destroy_pane(wp, 1); break; } } } job_check_died(pid, status); } /* Handle stopped children. */ static void server_child_stopped(pid_t pid, int status) { struct window *w; struct window_pane *wp; if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU) return; RB_FOREACH(w, windows, &windows) { TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->pid == pid) { if (killpg(pid, SIGCONT) != 0) kill(pid, SIGCONT); } } } } tmux-3.0a/session.c100644 001750 001750 00000040122 13504653146 0010051/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "tmux.h" struct sessions sessions; static u_int next_session_id; struct session_groups session_groups = RB_INITIALIZER(&session_groups); static void session_free(int, short, void *); static void session_lock_timer(int, short, void *); static struct winlink *session_next_alert(struct winlink *); static struct winlink *session_previous_alert(struct winlink *); static void session_group_remove(struct session *); static void session_group_synchronize1(struct session *, struct session *); int session_cmp(struct session *s1, struct session *s2) { return (strcmp(s1->name, s2->name)); } RB_GENERATE(sessions, session, entry, session_cmp); static int session_group_cmp(struct session_group *s1, struct session_group *s2) { return (strcmp(s1->name, s2->name)); } RB_GENERATE_STATIC(session_groups, session_group, entry, session_group_cmp); /* * 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 = (char *) name; return (RB_FIND(sessions, &sessions, &s)); } /* Find session by id parsed from a string. */ struct session * session_find_by_id_str(const char *s) { const char *errstr; u_int id; if (*s != '$') return (NULL); id = strtonum(s + 1, 0, UINT_MAX, &errstr); if (errstr != NULL) return (NULL); return (session_find_by_id(id)); } /* Find session by id. */ struct session * session_find_by_id(u_int id) { struct session *s; RB_FOREACH(s, sessions, &sessions) { if (s->id == id) return (s); } return (NULL); } /* Create a new session. */ struct session * session_create(const char *prefix, const char *name, const char *cwd, struct environ *env, struct options *oo, struct termios *tio) { struct session *s; s = xcalloc(1, sizeof *s); s->references = 1; s->flags = 0; s->cwd = xstrdup(cwd); s->curw = NULL; TAILQ_INIT(&s->lastw); RB_INIT(&s->windows); s->environ = env; s->options = oo; status_update_cache(s); s->tio = NULL; if (tio != NULL) { s->tio = xmalloc(sizeof *s->tio); memcpy(s->tio, tio, sizeof *s->tio); } if (name != NULL) { s->name = xstrdup(name); s->id = next_session_id++; } else { s->name = NULL; do { s->id = next_session_id++; free(s->name); if (prefix != NULL) xasprintf(&s->name, "%s-%u", prefix, s->id); else xasprintf(&s->name, "%u", s->id); } while (RB_FIND(sessions, &sessions, s) != NULL); } RB_INSERT(sessions, &sessions, s); log_debug("new session %s $%u", s->name, s->id); if (gettimeofday(&s->creation_time, NULL) != 0) fatal("gettimeofday failed"); session_update_activity(s, &s->creation_time); return (s); } /* Add a reference to a session. */ void session_add_ref(struct session *s, const char *from) { s->references++; log_debug("%s: %s %s, now %d", __func__, s->name, from, s->references); } /* Remove a reference from a session. */ void session_remove_ref(struct session *s, const char *from) { s->references--; log_debug("%s: %s %s, now %d", __func__, s->name, from, s->references); if (s->references == 0) event_once(-1, EV_TIMEOUT, session_free, s, NULL); } /* Free session. */ static void session_free(__unused int fd, __unused short events, void *arg) { struct session *s = arg; log_debug("session %s freed (%d references)", s->name, s->references); if (s->references == 0) { environ_free(s->environ); options_free(s->options); free(s->name); free(s); } } /* Destroy a session. */ void session_destroy(struct session *s, int notify, const char *from) { struct winlink *wl; log_debug("session %s destroyed (%s)", s->name, from); s->curw = NULL; RB_REMOVE(sessions, &sessions, s); if (notify) notify_session("session-closed", s); free(s->tio); if (event_initialized(&s->lock_timer)) event_del(&s->lock_timer); session_group_remove(s); while (!TAILQ_EMPTY(&s->lastw)) winlink_stack_remove(&s->lastw, TAILQ_FIRST(&s->lastw)); while (!RB_EMPTY(&s->windows)) { wl = RB_ROOT(&s->windows); notify_session_window("window-unlinked", s, wl->window); winlink_remove(&s->windows, wl); } free((void *)s->cwd); session_remove_ref(s, __func__); } /* Check a session name is valid: not empty and no colons or periods. */ int session_check_name(const char *name) { return (*name != '\0' && name[strcspn(name, ":.")] == '\0'); } /* Lock session if it has timed out. */ static void session_lock_timer(__unused int fd, __unused short events, void *arg) { struct session *s = arg; if (s->attached == 0) return; log_debug("session %s locked, activity time %lld", s->name, (long long)s->activity_time.tv_sec); server_lock_session(s); recalculate_sizes(); } /* Update activity time. */ void session_update_activity(struct session *s, struct timeval *from) { struct timeval *last = &s->last_activity_time; struct timeval tv; memcpy(last, &s->activity_time, sizeof *last); if (from == NULL) gettimeofday(&s->activity_time, NULL); else memcpy(&s->activity_time, from, sizeof s->activity_time); log_debug("session $%u %s activity %lld.%06d (last %lld.%06d)", s->id, s->name, (long long)s->activity_time.tv_sec, (int)s->activity_time.tv_usec, (long long)last->tv_sec, (int)last->tv_usec); if (evtimer_initialized(&s->lock_timer)) evtimer_del(&s->lock_timer); else evtimer_set(&s->lock_timer, session_lock_timer, s); if (s->attached != 0) { timerclear(&tv); tv.tv_sec = options_get_number(s->options, "lock-after-time"); if (tv.tv_sec != 0) evtimer_add(&s->lock_timer, &tv); } } /* 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); } /* 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); } wl->session = s; winlink_set_window(wl, w); notify_session_window("window-linked", s, 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; notify_session_window("window-unlinked", s, wl->window); winlink_stack_remove(&s->lastw, wl); winlink_remove(&s->windows, wl); session_group_synchronize_from(s); if (RB_EMPTY(&s->windows)) { session_destroy(s, 1, __func__); return (1); } return (0); } /* Return if session has window. */ int session_has(struct session *s, struct window *w) { struct winlink *wl; TAILQ_FOREACH(wl, &w->winlinks, wentry) { if (wl->session == s) return (1); } return (0); } /* * Return 1 if a window is linked outside this session (not including session * groups). The window must be in this session! */ int session_is_linked(struct session *s, struct window *w) { struct session_group *sg; if ((sg = session_group_contains(s)) != NULL) return (w->references != session_group_count(sg)); return (w->references != 1); } static 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); } return (session_set_current(s, wl)); } static 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); } return (session_set_current(s, wl)); } /* Move session to specific window. */ int session_select(struct session *s, int idx) { struct winlink *wl; wl = winlink_find_by_index(&s->windows, idx); return (session_set_current(s, wl)); } /* 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); return (session_set_current(s, wl)); } /* Set current winlink to wl .*/ int session_set_current(struct session *s, struct winlink *wl) { 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; winlink_clear_flags(wl); window_update_activity(wl->window); tty_update_window_offset(wl->window); notify_session("session-window-changed", s); return (0); } /* Find the session group containing a session. */ struct session_group * session_group_contains(struct session *target) { struct session_group *sg; struct session *s; RB_FOREACH(sg, session_groups, &session_groups) { TAILQ_FOREACH(s, &sg->sessions, gentry) { if (s == target) return (sg); } } return (NULL); } /* Find session group by name. */ struct session_group * session_group_find(const char *name) { struct session_group sg; sg.name = name; return (RB_FIND(session_groups, &session_groups, &sg)); } /* Create a new session group. */ struct session_group * session_group_new(const char *name) { struct session_group *sg; if ((sg = session_group_find(name)) != NULL) return (sg); sg = xcalloc(1, sizeof *sg); sg->name = xstrdup(name); TAILQ_INIT(&sg->sessions); RB_INSERT(session_groups, &session_groups, sg); return (sg); } /* Add a session to a session group. */ void session_group_add(struct session_group *sg, struct session *s) { if (session_group_contains(s) == NULL) TAILQ_INSERT_TAIL(&sg->sessions, s, gentry); } /* Remove a session from its group and destroy the group if empty. */ static void session_group_remove(struct session *s) { struct session_group *sg; if ((sg = session_group_contains(s)) == NULL) return; TAILQ_REMOVE(&sg->sessions, s, gentry); if (TAILQ_EMPTY(&sg->sessions)) { RB_REMOVE(session_groups, &session_groups, sg); free(sg); } } /* Count number of sessions in session group. */ u_int session_group_count(struct session_group *sg) { struct session *s; u_int n; n = 0; TAILQ_FOREACH(s, &sg->sessions, gentry) n++; return (n); } /* 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_contains(s)) == NULL) return; target = NULL; TAILQ_FOREACH(target, &sg->sessions, gentry) { if (target != s) break; } if (target != NULL) 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_contains(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. */ static 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); wl2->session = s; winlink_set_window(wl2, wl->window); notify_session_window("window-linked", s, wl2->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); wl2 = winlink_find_by_window_id(&s->windows, wl->window->id); if (wl2 == NULL) notify_session_window("window-unlinked", s, wl->window); winlink_remove(&old_windows, wl); } } /* Renumber the windows across winlinks attached to a specific session. */ void session_renumber_windows(struct session *s) { struct winlink *wl, *wl1, *wl_new; struct winlinks old_wins; struct winlink_stack old_lastw; int new_idx, new_curw_idx; /* Save and replace old window list. */ memcpy(&old_wins, &s->windows, sizeof old_wins); RB_INIT(&s->windows); /* Start renumbering from the base-index if it's set. */ new_idx = options_get_number(s->options, "base-index"); new_curw_idx = 0; /* Go through the winlinks and assign new indexes. */ RB_FOREACH(wl, winlinks, &old_wins) { wl_new = winlink_add(&s->windows, new_idx); wl_new->session = s; winlink_set_window(wl_new, wl->window); wl_new->flags |= wl->flags & WINLINK_ALERTFLAGS; if (wl == s->curw) new_curw_idx = wl_new->idx; new_idx++; } /* Fix the stack of last windows now. */ memcpy(&old_lastw, &s->lastw, sizeof old_lastw); TAILQ_INIT(&s->lastw); TAILQ_FOREACH(wl, &old_lastw, sentry) { wl_new = winlink_find_by_window(&s->windows, wl->window); if (wl_new != NULL) TAILQ_INSERT_TAIL(&s->lastw, wl_new, sentry); } /* Set the current window. */ s->curw = winlink_find_by_index(&s->windows, new_curw_idx); /* Free the old winlinks (reducing window references too). */ RB_FOREACH_SAFE(wl, winlinks, &old_wins, wl1) winlink_remove(&old_wins, wl); } tmux-3.0a/spawn.c100644 001750 001750 00000027601 13570677043 0007532/* $OpenBSD$ */ /* * Copyright (c) 2019 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" /* * Set up the environment and create a new window and pane or a new pane. * * We need to set up the following items: * * - history limit, comes from the session; * * - base index, comes from the session; * * - current working directory, may be specified - if it isn't it comes from * either the client or the session; * * - PATH variable, comes from the client if any, otherwise from the session * environment; * * - shell, comes from default-shell; * * - termios, comes from the session; * * - remaining environment, comes from the session. */ static void spawn_log(const char *from, struct spawn_context *sc) { struct session *s = sc->s; struct winlink *wl = sc->wl; struct window_pane *wp0 = sc->wp0; char tmp[128]; const char *name; log_debug("%s: %s, flags=%#x", from, sc->item->name, sc->flags); if (wl != NULL && wp0 != NULL) xsnprintf(tmp, sizeof tmp, "wl=%d wp0=%%%u", wl->idx, wp0->id); else if (wl != NULL) xsnprintf(tmp, sizeof tmp, "wl=%d wp0=none", wl->idx); else if (wp0 != NULL) xsnprintf(tmp, sizeof tmp, "wl=none wp0=%%%u", wp0->id); else xsnprintf(tmp, sizeof tmp, "wl=none wp0=none"); log_debug("%s: s=$%u %s idx=%d", from, s->id, tmp, sc->idx); name = sc->name; if (name == NULL) name = "none"; log_debug("%s: name=%s", from, name); } struct winlink * spawn_window(struct spawn_context *sc, char **cause) { struct session *s = sc->s; struct window *w; struct window_pane *wp; struct winlink *wl; int idx = sc->idx; u_int sx, sy; spawn_log(__func__, sc); /* * If the window already exists, we are respawning, so destroy all the * panes except one. */ if (sc->flags & SPAWN_RESPAWN) { w = sc->wl->window; if (~sc->flags & SPAWN_KILL) { TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->fd != -1) break; } if (wp != NULL) { xasprintf(cause, "window %s:%d still active", s->name, sc->wl->idx); return (NULL); } } sc->wp0 = TAILQ_FIRST(&w->panes); TAILQ_REMOVE(&w->panes, sc->wp0, entry); layout_free(w); window_destroy_panes(w); TAILQ_INSERT_HEAD(&w->panes, sc->wp0, entry); window_pane_resize(sc->wp0, w->sx, w->sy); layout_init(w, sc->wp0); window_set_active_pane(w, sc->wp0, 0); } /* * Otherwise we have no window so we will need to create one. First * check if the given index already exists and destroy it if so. */ if ((~sc->flags & SPAWN_RESPAWN) && idx != -1) { wl = winlink_find_by_index(&s->windows, idx); if (wl != NULL && (~sc->flags & SPAWN_KILL)) { xasprintf(cause, "index %d in use", idx); return (NULL); } if (wl != NULL) { /* * Can't use session_detach as it will destroy session * if this makes it empty. */ wl->flags &= ~WINLINK_ALERTFLAGS; notify_session_window("window-unlinked", s, wl->window); winlink_stack_remove(&s->lastw, wl); winlink_remove(&s->windows, wl); if (s->curw == wl) { s->curw = NULL; sc->flags &= ~SPAWN_DETACHED; } } } /* Then create a window if needed. */ if (~sc->flags & SPAWN_RESPAWN) { if (idx == -1) idx = -1 - options_get_number(s->options, "base-index"); if ((sc->wl = winlink_add(&s->windows, idx)) == NULL) { xasprintf(cause, "couldn't add window %d", idx); return (NULL); } default_window_size(s, NULL, &sx, &sy, -1); if ((w = window_create(sx, sy)) == NULL) { winlink_remove(&s->windows, sc->wl); xasprintf(cause, "couldn't create window %d", idx); return (NULL); } if (s->curw == NULL) s->curw = sc->wl; sc->wl->session = s; winlink_set_window(sc->wl, w); } else w = NULL; sc->flags |= SPAWN_NONOTIFY; /* Spawn the pane. */ wp = spawn_pane(sc, cause); if (wp == NULL) { if (~sc->flags & SPAWN_RESPAWN) winlink_remove(&s->windows, sc->wl); return (NULL); } /* Set the name of the new window. */ if (~sc->flags & SPAWN_RESPAWN) { if (sc->name != NULL) { w->name = xstrdup(sc->name); options_set_number(w->options, "automatic-rename", 0); } else w->name = xstrdup(default_window_name(w)); } /* Switch to the new window if required. */ if (~sc->flags & SPAWN_DETACHED) session_select(s, sc->wl->idx); /* Fire notification if new window. */ if (~sc->flags & SPAWN_RESPAWN) notify_session_window("window-linked", s, w); session_group_synchronize_from(s); return (sc->wl); } struct window_pane * spawn_pane(struct spawn_context *sc, char **cause) { struct cmdq_item *item = sc->item; struct client *c = item->client; struct session *s = sc->s; struct window *w = sc->wl->window; struct window_pane *new_wp; struct environ *child; struct environ_entry *ee; char **argv, *cp, **argvp, *argv0, *cwd; const char *cmd, *tmp; int argc; u_int idx; struct termios now; u_int hlimit; struct winsize ws; sigset_t set, oldset; spawn_log(__func__, sc); /* * If we are respawning then get rid of the old process. Otherwise * either create a new cell or assign to the one we are given. */ hlimit = options_get_number(s->options, "history-limit"); if (sc->flags & SPAWN_RESPAWN) { if (sc->wp0->fd != -1 && (~sc->flags & SPAWN_KILL)) { window_pane_index(sc->wp0, &idx); xasprintf(cause, "pane %s:%d.%u still active", s->name, sc->wl->idx, idx); return (NULL); } if (sc->wp0->fd != -1) { bufferevent_free(sc->wp0->event); close(sc->wp0->fd); } window_pane_reset_mode_all(sc->wp0); screen_reinit(&sc->wp0->base); input_init(sc->wp0); new_wp = sc->wp0; new_wp->flags &= ~(PANE_STATUSREADY|PANE_STATUSDRAWN); } else if (sc->lc == NULL) { new_wp = window_add_pane(w, NULL, hlimit, sc->flags); layout_init(w, new_wp); } else { new_wp = window_add_pane(w, sc->wp0, hlimit, sc->flags); layout_assign_pane(sc->lc, new_wp); } /* * Now we have a pane with nothing running in it ready for the new * process. Work out the command and arguments. */ if (sc->argc == 0 && (~sc->flags & SPAWN_RESPAWN)) { cmd = options_get_string(s->options, "default-command"); if (cmd != NULL && *cmd != '\0') { argc = 1; argv = (char **)&cmd; } else { argc = 0; argv = NULL; } } else { argc = sc->argc; argv = sc->argv; } /* * Replace the stored arguments if there are new ones. If not, the * existing ones will be used (they will only exist for respawn). */ if (argc > 0) { cmd_free_argv(new_wp->argc, new_wp->argv); new_wp->argc = argc; new_wp->argv = cmd_copy_argv(argc, argv); } /* * Work out the current working directory. If respawning, use * the pane's stored one unless specified. */ if (sc->cwd != NULL) cwd = format_single(item, sc->cwd, c, s, NULL, NULL); else if (~sc->flags & SPAWN_RESPAWN) cwd = xstrdup(server_client_get_cwd(c, s)); else cwd = NULL; if (cwd != NULL) { free(new_wp->cwd); new_wp->cwd = cwd; } /* Create an environment for this pane. */ child = environ_for_session(s, 0); if (sc->environ != NULL) environ_copy(sc->environ, child); environ_set(child, "TMUX_PANE", "%%%u", new_wp->id); /* * Then the PATH environment variable. The session one is replaced from * the client if there is one because otherwise running "tmux new * myprogram" wouldn't work if myprogram isn't in the session's path. */ if (c != NULL && c->session == NULL) { /* only unattached clients */ ee = environ_find(c->environ, "PATH"); if (ee != NULL) environ_set(child, "PATH", "%s", ee->value); } if (environ_find(child, "PATH") == NULL) environ_set(child, "%s", _PATH_DEFPATH); /* Then the shell. If respawning, use the old one. */ if (~sc->flags & SPAWN_RESPAWN) { tmp = options_get_string(s->options, "default-shell"); if (*tmp == '\0' || areshell(tmp)) tmp = _PATH_BSHELL; free(new_wp->shell); new_wp->shell = xstrdup(tmp); } environ_set(child, "SHELL", "%s", new_wp->shell); /* Log the arguments we are going to use. */ log_debug("%s: shell=%s", __func__, new_wp->shell); if (new_wp->argc != 0) { cp = cmd_stringify_argv(new_wp->argc, new_wp->argv); log_debug("%s: cmd=%s", __func__, cp); free(cp); } if (cwd != NULL) log_debug("%s: cwd=%s", __func__, cwd); cmd_log_argv(new_wp->argc, new_wp->argv, "%s", __func__); environ_log(child, "%s: environment ", __func__); /* Initialize the window size. */ memset(&ws, 0, sizeof ws); ws.ws_col = screen_size_x(&new_wp->base); ws.ws_row = screen_size_y(&new_wp->base); /* Block signals until fork has completed. */ sigfillset(&set); sigprocmask(SIG_BLOCK, &set, &oldset); /* If the command is empty, don't fork a child process. */ if (sc->flags & SPAWN_EMPTY) { new_wp->flags |= PANE_EMPTY; new_wp->base.mode &= ~MODE_CURSOR; new_wp->base.mode |= MODE_CRLF; goto complete; } /* Fork the new process. */ new_wp->pid = fdforkpty(ptm_fd, &new_wp->fd, new_wp->tty, NULL, &ws); if (new_wp->pid == -1) { xasprintf(cause, "fork failed: %s", strerror(errno)); new_wp->fd = -1; if (~sc->flags & SPAWN_RESPAWN) { layout_close_pane(new_wp); window_remove_pane(w, new_wp); } sigprocmask(SIG_SETMASK, &oldset, NULL); return (NULL); } /* In the parent process, everything is done now. */ if (new_wp->pid != 0) goto complete; /* * Child process. Change to the working directory or home if that * fails. */ if (chdir(new_wp->cwd) != 0) { if ((tmp = find_home()) == NULL || chdir(tmp) != 0) chdir("/"); } /* * Update terminal escape characters from the session if available and * force VERASE to tmux's \177. */ if (tcgetattr(STDIN_FILENO, &now) != 0) _exit(1); if (s->tio != NULL) memcpy(now.c_cc, s->tio->c_cc, sizeof now.c_cc); now.c_cc[VERASE] = '\177'; if (tcsetattr(STDIN_FILENO, TCSANOW, &now) != 0) _exit(1); /* Clean up file descriptors and signals and update the environment. */ closefrom(STDERR_FILENO + 1); proc_clear_signals(server_proc, 1); sigprocmask(SIG_SETMASK, &oldset, NULL); log_close(); environ_push(child); /* * If given multiple arguments, use execvp(). Copy the arguments to * ensure they end in a NULL. */ if (new_wp->argc != 0 && new_wp->argc != 1) { argvp = cmd_copy_argv(new_wp->argc, new_wp->argv); execvp(argvp[0], argvp); _exit(1); } /* * If one argument, pass it to $SHELL -c. Otherwise create a login * shell. */ cp = strrchr(new_wp->shell, '/'); if (new_wp->argc == 1) { tmp = new_wp->argv[0]; if (cp != NULL && cp[1] != '\0') xasprintf(&argv0, "%s", cp + 1); else xasprintf(&argv0, "%s", new_wp->shell); execl(new_wp->shell, argv0, "-c", tmp, (char *)NULL); _exit(1); } if (cp != NULL && cp[1] != '\0') xasprintf(&argv0, "-%s", cp + 1); else xasprintf(&argv0, "-%s", new_wp->shell); execl(new_wp->shell, argv0, (char *)NULL); _exit(1); complete: new_wp->pipe_off = 0; new_wp->flags &= ~PANE_EXITED; sigprocmask(SIG_SETMASK, &oldset, NULL); window_pane_set_event(new_wp); if (sc->flags & SPAWN_RESPAWN) return (new_wp); if ((~sc->flags & SPAWN_DETACHED) || w->active == NULL) { if (sc->flags & SPAWN_NONOTIFY) window_set_active_pane(w, new_wp, 0); else window_set_active_pane(w, new_wp, 1); } if (~sc->flags & SPAWN_NONOTIFY) notify_window("window-layout-changed", w); return (new_wp); } tmux-3.0a/status.c100644 001750 001750 00000105536 13506620447 0007724/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include "tmux.h" static void status_message_callback(int, short, void *); static void status_timer_callback(int, short, void *); static char *status_prompt_find_history_file(void); static const char *status_prompt_up_history(u_int *); static const char *status_prompt_down_history(u_int *); static void status_prompt_add_history(const char *); static char **status_prompt_complete_list(u_int *, const char *); static char *status_prompt_complete_prefix(char **, u_int); static char *status_prompt_complete(struct session *, const char *); /* Status prompt history. */ #define PROMPT_HISTORY 100 static char **status_prompt_hlist; static u_int status_prompt_hsize; /* Find the history file to load/save from/to. */ static char * status_prompt_find_history_file(void) { const char *home, *history_file; char *path; history_file = options_get_string(global_options, "history-file"); if (*history_file == '\0') return (NULL); if (*history_file == '/') return (xstrdup(history_file)); if (history_file[0] != '~' || history_file[1] != '/') return (NULL); if ((home = find_home()) == NULL) return (NULL); xasprintf(&path, "%s%s", home, history_file + 1); return (path); } /* Load status prompt history from file. */ void status_prompt_load_history(void) { FILE *f; char *history_file, *line, *tmp; size_t length; if ((history_file = status_prompt_find_history_file()) == NULL) return; log_debug("loading history from %s", history_file); f = fopen(history_file, "r"); if (f == NULL) { log_debug("%s: %s", history_file, strerror(errno)); free(history_file); return; } free(history_file); for (;;) { if ((line = fgetln(f, &length)) == NULL) break; if (length > 0) { if (line[length - 1] == '\n') { line[length - 1] = '\0'; status_prompt_add_history(line); } else { tmp = xmalloc(length + 1); memcpy(tmp, line, length); tmp[length] = '\0'; status_prompt_add_history(tmp); free(tmp); } } } fclose(f); } /* Save status prompt history to file. */ void status_prompt_save_history(void) { FILE *f; u_int i; char *history_file; if ((history_file = status_prompt_find_history_file()) == NULL) return; log_debug("saving history to %s", history_file); f = fopen(history_file, "w"); if (f == NULL) { log_debug("%s: %s", history_file, strerror(errno)); free(history_file); return; } free(history_file); for (i = 0; i < status_prompt_hsize; i++) { fputs(status_prompt_hlist[i], f); fputc('\n', f); } fclose(f); } /* Status timer callback. */ static void status_timer_callback(__unused int fd, __unused short events, void *arg) { struct client *c = arg; struct session *s = c->session; struct timeval tv; evtimer_del(&c->status.timer); if (s == NULL) return; if (c->message_string == NULL && c->prompt_string == NULL) c->flags |= CLIENT_REDRAWSTATUS; timerclear(&tv); tv.tv_sec = options_get_number(s->options, "status-interval"); if (tv.tv_sec != 0) evtimer_add(&c->status.timer, &tv); log_debug("client %p, status interval %d", c, (int)tv.tv_sec); } /* Start status timer for client. */ void status_timer_start(struct client *c) { struct session *s = c->session; if (event_initialized(&c->status.timer)) evtimer_del(&c->status.timer); else evtimer_set(&c->status.timer, status_timer_callback, c); if (s != NULL && options_get_number(s->options, "status")) status_timer_callback(-1, 0, c); } /* Start status timer for all clients. */ void status_timer_start_all(void) { struct client *c; TAILQ_FOREACH(c, &clients, entry) status_timer_start(c); } /* Update status cache. */ void status_update_cache(struct session *s) { s->statuslines = options_get_number(s->options, "status"); if (s->statuslines == 0) s->statusat = -1; else if (options_get_number(s->options, "status-position") == 0) s->statusat = 0; else s->statusat = 1; } /* Get screen line of status line. -1 means off. */ int status_at_line(struct client *c) { struct session *s = c->session; if (c->flags & (CLIENT_STATUSOFF|CLIENT_CONTROL)) return (-1); if (s->statusat != 1) return (s->statusat); return (c->tty.sy - status_line_size(c)); } /* Get size of status line for client's session. 0 means off. */ u_int status_line_size(struct client *c) { struct session *s = c->session; if (c->flags & (CLIENT_STATUSOFF|CLIENT_CONTROL)) return (0); return (s->statuslines); } /* Get window at window list position. */ struct style_range * status_get_range(struct client *c, u_int x, u_int y) { struct status_line *sl = &c->status; struct style_range *sr; if (y >= nitems(sl->entries)) return (NULL); TAILQ_FOREACH(sr, &sl->entries[y].ranges, entry) { if (x >= sr->start && x < sr->end) return (sr); } return (NULL); } /* Free all ranges. */ static void status_free_ranges(struct style_ranges *srs) { struct style_range *sr, *sr1; TAILQ_FOREACH_SAFE(sr, srs, entry, sr1) { TAILQ_REMOVE(srs, sr, entry); free(sr); } } /* Save old status line. */ static void status_push_screen(struct client *c) { struct status_line *sl = &c->status; if (sl->active == &sl->screen) { sl->active = xmalloc(sizeof *sl->active); screen_init(sl->active, c->tty.sx, status_line_size(c), 0); } sl->references++; } /* Restore old status line. */ static void status_pop_screen(struct client *c) { struct status_line *sl = &c->status; if (--sl->references == 0) { screen_free(sl->active); free(sl->active); sl->active = &sl->screen; } } /* Initialize status line. */ void status_init(struct client *c) { struct status_line *sl = &c->status; u_int i; for (i = 0; i < nitems(sl->entries); i++) TAILQ_INIT(&sl->entries[i].ranges); screen_init(&sl->screen, c->tty.sx, 1, 0); sl->active = &sl->screen; } /* Free status line. */ void status_free(struct client *c) { struct status_line *sl = &c->status; u_int i; for (i = 0; i < nitems(sl->entries); i++) { status_free_ranges(&sl->entries[i].ranges); free((void *)sl->entries[i].expanded); } if (event_initialized(&sl->timer)) evtimer_del(&sl->timer); if (sl->active != &sl->screen) { screen_free(sl->active); free(sl->active); } screen_free(&sl->screen); } /* Draw status line for client. */ int status_redraw(struct client *c) { struct status_line *sl = &c->status; struct status_line_entry *sle; struct session *s = c->session; struct screen_write_ctx ctx; struct grid_cell gc; u_int lines, i, n, width = c->tty.sx; int flags, force = 0, changed = 0; struct options_entry *o; union options_value *ov; struct format_tree *ft; char *expanded; log_debug("%s enter", __func__); /* Shouldn't get here if not the active screen. */ if (sl->active != &sl->screen) fatalx("not the active screen"); /* No status line? */ lines = status_line_size(c); if (c->tty.sy == 0 || lines == 0) return (1); /* Set up default colour. */ style_apply(&gc, s->options, "status-style"); if (!grid_cells_equal(&gc, &sl->style)) { force = 1; memcpy(&sl->style, &gc, sizeof sl->style); } /* Resize the target screen. */ if (screen_size_x(&sl->screen) != width || screen_size_y(&sl->screen) != lines) { screen_resize(&sl->screen, width, lines, 0); changed = force = 1; } screen_write_start(&ctx, NULL, &sl->screen); /* Create format tree. */ flags = FORMAT_STATUS; if (c->flags & CLIENT_STATUSFORCE) flags |= FORMAT_FORCE; ft = format_create(c, NULL, FORMAT_NONE, flags); format_defaults(ft, c, NULL, NULL, NULL); /* Write the status lines. */ o = options_get(s->options, "status-format"); if (o == NULL) { for (n = 0; n < width * lines; n++) screen_write_putc(&ctx, &gc, ' '); } else { for (i = 0; i < lines; i++) { screen_write_cursormove(&ctx, 0, i, 0); ov = options_array_get(o, i); if (ov == NULL) { for (n = 0; n < width; n++) screen_write_putc(&ctx, &gc, ' '); continue; } sle = &sl->entries[i]; expanded = format_expand_time(ft, ov->string); if (!force && sle->expanded != NULL && strcmp(expanded, sle->expanded) == 0) { free(expanded); continue; } changed = 1; for (n = 0; n < width; n++) screen_write_putc(&ctx, &gc, ' '); screen_write_cursormove(&ctx, 0, i, 0); status_free_ranges(&sle->ranges); format_draw(&ctx, &gc, width, expanded, &sle->ranges); free(sle->expanded); sle->expanded = expanded; } } screen_write_stop(&ctx); /* Free the format tree. */ format_free(ft); /* Return if the status line has changed. */ log_debug("%s exit: force=%d, changed=%d", __func__, force, changed); return (force || changed); } /* Set a status line message. */ void status_message_set(struct client *c, const char *fmt, ...) { struct timeval tv; va_list ap; int delay; status_message_clear(c); status_push_screen(c); va_start(ap, fmt); xvasprintf(&c->message_string, fmt, ap); va_end(ap); server_client_add_message(c, "%s", c->message_string); delay = options_get_number(c->session->options, "display-time"); if (delay > 0) { tv.tv_sec = delay / 1000; tv.tv_usec = (delay % 1000) * 1000L; if (event_initialized(&c->message_timer)) 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_REDRAWSTATUS; } /* Clear status line message. */ void status_message_clear(struct client *c) { if (c->message_string == NULL) return; free(c->message_string); c->message_string = NULL; if (c->prompt_string == NULL) c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE); c->flags |= CLIENT_ALLREDRAWFLAGS; /* was frozen and may have changed */ status_pop_screen(c); } /* Clear status line message after timer expires. */ static 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 status_line *sl = &c->status; struct screen_write_ctx ctx; struct session *s = c->session; struct screen old_screen; size_t len; u_int lines, offset; struct grid_cell gc; if (c->tty.sx == 0 || c->tty.sy == 0) return (0); memcpy(&old_screen, sl->active, sizeof old_screen); lines = status_line_size(c); if (lines <= 1) lines = 1; screen_init(sl->active, c->tty.sx, lines, 0); len = screen_write_strlen("%s", c->message_string); if (len > c->tty.sx) len = c->tty.sx; style_apply(&gc, s->options, "message-style"); screen_write_start(&ctx, NULL, sl->active); screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines - 1); screen_write_cursormove(&ctx, 0, lines - 1, 0); for (offset = 0; offset < c->tty.sx; offset++) screen_write_putc(&ctx, &gc, ' '); screen_write_cursormove(&ctx, 0, lines - 1, 0); screen_write_nputs(&ctx, len, &gc, "%s", c->message_string); screen_write_stop(&ctx); if (grid_compare(sl->active->grid, old_screen.grid) == 0) { screen_free(&old_screen); return (0); } screen_free(&old_screen); return (1); } /* Enable status line prompt. */ void status_prompt_set(struct client *c, const char *msg, const char *input, prompt_input_cb inputcb, prompt_free_cb freecb, void *data, int flags) { struct format_tree *ft; char *tmp, *cp; ft = format_create(c, NULL, FORMAT_NONE, 0); format_defaults(ft, c, NULL, NULL, NULL); if (input == NULL) input = ""; if (flags & PROMPT_NOFORMAT) tmp = xstrdup(input); else tmp = format_expand_time(ft, input); status_message_clear(c); status_prompt_clear(c); status_push_screen(c); c->prompt_string = format_expand_time(ft, msg); c->prompt_buffer = utf8_fromcstr(tmp); c->prompt_index = utf8_strlen(c->prompt_buffer); c->prompt_inputcb = inputcb; c->prompt_freecb = freecb; c->prompt_data = data; c->prompt_hindex = 0; c->prompt_flags = flags; c->prompt_mode = PROMPT_ENTRY; if (~flags & PROMPT_INCREMENTAL) c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE); c->flags |= CLIENT_REDRAWSTATUS; if ((flags & PROMPT_INCREMENTAL) && *tmp != '\0') { xasprintf(&cp, "=%s", tmp); c->prompt_inputcb(c, c->prompt_data, cp, 0); free(cp); } free(tmp); format_free(ft); } /* Remove status line prompt. */ void status_prompt_clear(struct client *c) { if (c->prompt_string == NULL) return; if (c->prompt_freecb != NULL && c->prompt_data != NULL) c->prompt_freecb(c->prompt_data); free(c->prompt_string); c->prompt_string = NULL; free(c->prompt_buffer); c->prompt_buffer = NULL; free(c->prompt_saved); c->prompt_saved = NULL; c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE); c->flags |= CLIENT_ALLREDRAWFLAGS; /* was frozen and may have changed */ status_pop_screen(c); } /* Update status line prompt with a new prompt string. */ void status_prompt_update(struct client *c, const char *msg, const char *input) { struct format_tree *ft; char *tmp; ft = format_create(c, NULL, FORMAT_NONE, 0); format_defaults(ft, c, NULL, NULL, NULL); tmp = format_expand_time(ft, input); free(c->prompt_string); c->prompt_string = format_expand_time(ft, msg); free(c->prompt_buffer); c->prompt_buffer = utf8_fromcstr(tmp); c->prompt_index = utf8_strlen(c->prompt_buffer); c->prompt_hindex = 0; c->flags |= CLIENT_REDRAWSTATUS; free(tmp); format_free(ft); } /* Draw client prompt on status line of present else on last line. */ int status_prompt_redraw(struct client *c) { struct status_line *sl = &c->status; struct screen_write_ctx ctx; struct session *s = c->session; struct screen old_screen; u_int i, lines, offset, left, start, width; u_int pcursor, pwidth; struct grid_cell gc, cursorgc; if (c->tty.sx == 0 || c->tty.sy == 0) return (0); memcpy(&old_screen, sl->active, sizeof old_screen); lines = status_line_size(c); if (lines <= 1) lines = 1; screen_init(sl->active, c->tty.sx, lines, 0); if (c->prompt_mode == PROMPT_COMMAND) style_apply(&gc, s->options, "message-command-style"); else style_apply(&gc, s->options, "message-style"); memcpy(&cursorgc, &gc, sizeof cursorgc); cursorgc.attr ^= GRID_ATTR_REVERSE; start = screen_write_strlen("%s", c->prompt_string); if (start > c->tty.sx) start = c->tty.sx; screen_write_start(&ctx, NULL, sl->active); screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines - 1); screen_write_cursormove(&ctx, 0, lines - 1, 0); for (offset = 0; offset < c->tty.sx; offset++) screen_write_putc(&ctx, &gc, ' '); screen_write_cursormove(&ctx, 0, lines - 1, 0); screen_write_nputs(&ctx, start, &gc, "%s", c->prompt_string); screen_write_cursormove(&ctx, start, lines - 1, 0); left = c->tty.sx - start; if (left == 0) goto finished; pcursor = utf8_strwidth(c->prompt_buffer, c->prompt_index); pwidth = utf8_strwidth(c->prompt_buffer, -1); if (pcursor >= left) { /* * The cursor would be outside the screen so start drawing * with it on the right. */ offset = (pcursor - left) + 1; pwidth = left; } else offset = 0; if (pwidth > left) pwidth = left; width = 0; for (i = 0; c->prompt_buffer[i].size != 0; i++) { if (width < offset) { width += c->prompt_buffer[i].width; continue; } if (width >= offset + pwidth) break; width += c->prompt_buffer[i].width; if (width > offset + pwidth) break; if (i != c->prompt_index) { utf8_copy(&gc.data, &c->prompt_buffer[i]); screen_write_cell(&ctx, &gc); } else { utf8_copy(&cursorgc.data, &c->prompt_buffer[i]); screen_write_cell(&ctx, &cursorgc); } } if (sl->active->cx < screen_size_x(sl->active) && c->prompt_index >= i) screen_write_putc(&ctx, &cursorgc, ' '); finished: screen_write_stop(&ctx); if (grid_compare(sl->active->grid, old_screen.grid) == 0) { screen_free(&old_screen); return (0); } screen_free(&old_screen); return (1); } /* Is this a separator? */ static int status_prompt_in_list(const char *ws, const struct utf8_data *ud) { if (ud->size != 1 || ud->width != 1) return (0); return (strchr(ws, *ud->data) != NULL); } /* Is this a space? */ static int status_prompt_space(const struct utf8_data *ud) { if (ud->size != 1 || ud->width != 1) return (0); return (*ud->data == ' '); } /* * Translate key from emacs to vi. Return 0 to drop key, 1 to process the key * as an emacs key; return 2 to append to the buffer. */ static int status_prompt_translate_key(struct client *c, key_code key, key_code *new_key) { if (c->prompt_mode == PROMPT_ENTRY) { switch (key) { case '\003': /* C-c */ case '\010': /* C-h */ case '\011': /* Tab */ case '\025': /* C-u */ case '\027': /* C-w */ case '\n': case '\r': case KEYC_BSPACE: case KEYC_DC: case KEYC_DOWN: case KEYC_END: case KEYC_HOME: case KEYC_LEFT: case KEYC_RIGHT: case KEYC_UP: *new_key = key; return (1); case '\033': /* Escape */ c->prompt_mode = PROMPT_COMMAND; c->flags |= CLIENT_REDRAWSTATUS; return (0); } *new_key = key; return (2); } switch (key) { case 'A': case 'I': case 'C': case 's': case 'a': c->prompt_mode = PROMPT_ENTRY; c->flags |= CLIENT_REDRAWSTATUS; break; /* switch mode and... */ case 'S': c->prompt_mode = PROMPT_ENTRY; c->flags |= CLIENT_REDRAWSTATUS; *new_key = '\025'; /* C-u */ return (1); case 'i': case '\033': /* Escape */ c->prompt_mode = PROMPT_ENTRY; c->flags |= CLIENT_REDRAWSTATUS; return (0); } switch (key) { case 'A': case '$': *new_key = KEYC_END; return (1); case 'I': case '0': case '^': *new_key = KEYC_HOME; return (1); case 'C': case 'D': *new_key = '\013'; /* C-k */ return (1); case KEYC_BSPACE: case 'X': *new_key = KEYC_BSPACE; return (1); case 'b': case 'B': *new_key = 'b'|KEYC_ESCAPE; return (1); case 'd': *new_key = '\025'; return (1); case 'e': case 'E': case 'w': case 'W': *new_key = 'f'|KEYC_ESCAPE; return (1); case 'p': *new_key = '\031'; /* C-y */ return (1); case 's': case KEYC_DC: case 'x': *new_key = KEYC_DC; return (1); case KEYC_DOWN: case 'j': *new_key = KEYC_DOWN; return (1); case KEYC_LEFT: case 'h': *new_key = KEYC_LEFT; return (1); case 'a': case KEYC_RIGHT: case 'l': *new_key = KEYC_RIGHT; return (1); case KEYC_UP: case 'k': *new_key = KEYC_UP; return (1); case '\010' /* C-h */: case '\003' /* C-c */: case '\n': case '\r': return (1); } return (0); } /* Paste into prompt. */ static int status_prompt_paste(struct client *c) { struct paste_buffer *pb; const char *bufdata; size_t size, n, bufsize; u_int i; struct utf8_data *ud, *udp; enum utf8_state more; size = utf8_strlen(c->prompt_buffer); if (c->prompt_saved != NULL) { ud = c->prompt_saved; n = utf8_strlen(c->prompt_saved); } else { if ((pb = paste_get_top(NULL)) == NULL) return (0); bufdata = paste_buffer_data(pb, &bufsize); ud = xreallocarray(NULL, bufsize + 1, sizeof *ud); udp = ud; for (i = 0; i != bufsize; /* nothing */) { more = utf8_open(udp, bufdata[i]); if (more == UTF8_MORE) { while (++i != bufsize && more == UTF8_MORE) more = utf8_append(udp, bufdata[i]); if (more == UTF8_DONE) { udp++; continue; } i -= udp->have; } if (bufdata[i] <= 31 || bufdata[i] >= 127) break; utf8_set(udp, bufdata[i]); udp++; i++; } udp->size = 0; n = udp - ud; } if (n == 0) return (0); c->prompt_buffer = xreallocarray(c->prompt_buffer, size + n + 1, sizeof *c->prompt_buffer); if (c->prompt_index == size) { memcpy(c->prompt_buffer + c->prompt_index, ud, n * sizeof *c->prompt_buffer); c->prompt_index += n; c->prompt_buffer[c->prompt_index].size = 0; } else { memmove(c->prompt_buffer + c->prompt_index + n, c->prompt_buffer + c->prompt_index, (size + 1 - c->prompt_index) * sizeof *c->prompt_buffer); memcpy(c->prompt_buffer + c->prompt_index, ud, n * sizeof *c->prompt_buffer); c->prompt_index += n; } if (ud != c->prompt_saved) free(ud); return (1); } /* Handle keys in prompt. */ int status_prompt_key(struct client *c, key_code key) { struct options *oo = c->session->options; char *s, *cp, word[64], prefix = '='; const char *histstr, *ws = NULL; size_t size, n, off, idx, used; struct utf8_data tmp, *first, *last, *ud; int keys; size = utf8_strlen(c->prompt_buffer); if (c->prompt_flags & PROMPT_NUMERIC) { if (key >= '0' && key <= '9') goto append_key; s = utf8_tocstr(c->prompt_buffer); c->prompt_inputcb(c, c->prompt_data, s, 1); status_prompt_clear(c); free(s); return (1); } key &= ~KEYC_XTERM; keys = options_get_number(c->session->options, "status-keys"); if (keys == MODEKEY_VI) { switch (status_prompt_translate_key(c, key, &key)) { case 1: goto process_key; case 2: goto append_key; default: return (0); } } process_key: switch (key) { case KEYC_LEFT: case '\002': /* C-b */ if (c->prompt_index > 0) { c->prompt_index--; break; } break; case KEYC_RIGHT: case '\006': /* C-f */ if (c->prompt_index < size) { c->prompt_index++; break; } break; case KEYC_HOME: case '\001': /* C-a */ if (c->prompt_index != 0) { c->prompt_index = 0; break; } break; case KEYC_END: case '\005': /* C-e */ if (c->prompt_index != size) { c->prompt_index = size; break; } break; case '\011': /* Tab */ if (c->prompt_buffer[0].size == 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 && !status_prompt_space(first)) first--; while (first->size != 0 && status_prompt_space(first)) first++; last = &c->prompt_buffer[idx]; while (last->size != 0 && !status_prompt_space(last)) last++; while (last > c->prompt_buffer && status_prompt_space(last)) last--; if (last->size != 0) last++; if (last <= first) break; used = 0; for (ud = first; ud < last; ud++) { if (used + ud->size >= sizeof word) break; memcpy(word + used, ud->data, ud->size); used += ud->size; } if (ud != last) break; word[used] = '\0'; /* And try to complete it. */ if ((s = status_prompt_complete(c->session, word)) == NULL) break; /* Trim out word. */ n = size - (last - c->prompt_buffer) + 1; /* with \0 */ memmove(first, last, n * sizeof *c->prompt_buffer); size -= last - first; /* Insert the new word. */ size += strlen(s); off = first - c->prompt_buffer; c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 1, sizeof *c->prompt_buffer); first = c->prompt_buffer + off; memmove(first + strlen(s), first, n * sizeof *c->prompt_buffer); for (idx = 0; idx < strlen(s); idx++) utf8_set(&first[idx], s[idx]); c->prompt_index = (first - c->prompt_buffer) + strlen(s); free(s); goto changed; case KEYC_BSPACE: case '\010': /* C-h */ if (c->prompt_index != 0) { if (c->prompt_index == size) c->prompt_buffer[--c->prompt_index].size = 0; else { memmove(c->prompt_buffer + c->prompt_index - 1, c->prompt_buffer + c->prompt_index, (size + 1 - c->prompt_index) * sizeof *c->prompt_buffer); c->prompt_index--; } goto changed; } break; case KEYC_DC: case '\004': /* C-d */ if (c->prompt_index != size) { memmove(c->prompt_buffer + c->prompt_index, c->prompt_buffer + c->prompt_index + 1, (size + 1 - c->prompt_index) * sizeof *c->prompt_buffer); goto changed; } break; case '\025': /* C-u */ c->prompt_buffer[0].size = 0; c->prompt_index = 0; goto changed; case '\013': /* C-k */ if (c->prompt_index < size) { c->prompt_buffer[c->prompt_index].size = 0; goto changed; } break; case '\027': /* C-w */ ws = options_get_string(oo, "word-separators"); idx = c->prompt_index; /* Find a non-separator. */ while (idx != 0) { idx--; if (!status_prompt_in_list(ws, &c->prompt_buffer[idx])) break; } /* Find the separator at the beginning of the word. */ while (idx != 0) { idx--; if (status_prompt_in_list(ws, &c->prompt_buffer[idx])) { /* Go back to the word. */ idx++; break; } } free(c->prompt_saved); c->prompt_saved = xcalloc(sizeof *c->prompt_buffer, (c->prompt_index - idx) + 1); memcpy(c->prompt_saved, c->prompt_buffer + idx, (c->prompt_index - idx) * sizeof *c->prompt_buffer); memmove(c->prompt_buffer + idx, c->prompt_buffer + c->prompt_index, (size + 1 - c->prompt_index) * sizeof *c->prompt_buffer); memset(c->prompt_buffer + size - (c->prompt_index - idx), '\0', (c->prompt_index - idx) * sizeof *c->prompt_buffer); c->prompt_index = idx; goto changed; case 'f'|KEYC_ESCAPE: case KEYC_RIGHT|KEYC_CTRL: ws = options_get_string(oo, "word-separators"); /* Find a word. */ while (c->prompt_index != size) { idx = ++c->prompt_index; if (!status_prompt_in_list(ws, &c->prompt_buffer[idx])) break; } /* Find the separator at the end of the word. */ while (c->prompt_index != size) { idx = ++c->prompt_index; if (status_prompt_in_list(ws, &c->prompt_buffer[idx])) break; } /* Back up to the end-of-word like vi. */ if (options_get_number(oo, "status-keys") == MODEKEY_VI && c->prompt_index != 0) c->prompt_index--; goto changed; case 'b'|KEYC_ESCAPE: case KEYC_LEFT|KEYC_CTRL: ws = options_get_string(oo, "word-separators"); /* Find a non-separator. */ while (c->prompt_index != 0) { idx = --c->prompt_index; if (!status_prompt_in_list(ws, &c->prompt_buffer[idx])) break; } /* Find the separator at the beginning of the word. */ while (c->prompt_index != 0) { idx = --c->prompt_index; if (status_prompt_in_list(ws, &c->prompt_buffer[idx])) { /* Go back to the word. */ c->prompt_index++; break; } } goto changed; case KEYC_UP: case '\020': /* C-p */ histstr = status_prompt_up_history(&c->prompt_hindex); if (histstr == NULL) break; free(c->prompt_buffer); c->prompt_buffer = utf8_fromcstr(histstr); c->prompt_index = utf8_strlen(c->prompt_buffer); goto changed; case KEYC_DOWN: case '\016': /* C-n */ histstr = status_prompt_down_history(&c->prompt_hindex); if (histstr == NULL) break; free(c->prompt_buffer); c->prompt_buffer = utf8_fromcstr(histstr); c->prompt_index = utf8_strlen(c->prompt_buffer); goto changed; case '\031': /* C-y */ if (status_prompt_paste(c)) goto changed; break; case '\024': /* C-t */ idx = c->prompt_index; if (idx < size) idx++; if (idx >= 2) { utf8_copy(&tmp, &c->prompt_buffer[idx - 2]); utf8_copy(&c->prompt_buffer[idx - 2], &c->prompt_buffer[idx - 1]); utf8_copy(&c->prompt_buffer[idx - 1], &tmp); c->prompt_index = idx; goto changed; } break; case '\r': case '\n': s = utf8_tocstr(c->prompt_buffer); if (*s != '\0') status_prompt_add_history(s); if (c->prompt_inputcb(c, c->prompt_data, s, 1) == 0) status_prompt_clear(c); free(s); break; case '\033': /* Escape */ case '\003': /* C-c */ case '\007': /* C-g */ if (c->prompt_inputcb(c, c->prompt_data, NULL, 1) == 0) status_prompt_clear(c); break; case '\022': /* C-r */ if (c->prompt_flags & PROMPT_INCREMENTAL) { prefix = '-'; goto changed; } break; case '\023': /* C-s */ if (c->prompt_flags & PROMPT_INCREMENTAL) { prefix = '+'; goto changed; } break; default: goto append_key; } c->flags |= CLIENT_REDRAWSTATUS; return (0); append_key: if (key <= 0x1f || key >= KEYC_BASE) return (0); if (utf8_split(key, &tmp) != UTF8_DONE) return (0); c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 2, sizeof *c->prompt_buffer); if (c->prompt_index == size) { utf8_copy(&c->prompt_buffer[c->prompt_index], &tmp); c->prompt_index++; c->prompt_buffer[c->prompt_index].size = 0; } else { memmove(c->prompt_buffer + c->prompt_index + 1, c->prompt_buffer + c->prompt_index, (size + 1 - c->prompt_index) * sizeof *c->prompt_buffer); utf8_copy(&c->prompt_buffer[c->prompt_index], &tmp); c->prompt_index++; } if (c->prompt_flags & PROMPT_SINGLE) { s = utf8_tocstr(c->prompt_buffer); if (strlen(s) != 1) status_prompt_clear(c); else if (c->prompt_inputcb(c, c->prompt_data, s, 1) == 0) status_prompt_clear(c); free(s); } changed: c->flags |= CLIENT_REDRAWSTATUS; if (c->prompt_flags & PROMPT_INCREMENTAL) { s = utf8_tocstr(c->prompt_buffer); xasprintf(&cp, "%c%s", prefix, s); c->prompt_inputcb(c, c->prompt_data, cp, 0); free(cp); free(s); } return (0); } /* Get previous line from the history. */ static const char * status_prompt_up_history(u_int *idx) { /* * History runs from 0 to size - 1. Index is from 0 to size. Zero is * empty. */ if (status_prompt_hsize == 0 || *idx == status_prompt_hsize) return (NULL); (*idx)++; return (status_prompt_hlist[status_prompt_hsize - *idx]); } /* Get next line from the history. */ static const char * status_prompt_down_history(u_int *idx) { if (status_prompt_hsize == 0 || *idx == 0) return (""); (*idx)--; if (*idx == 0) return (""); return (status_prompt_hlist[status_prompt_hsize - *idx]); } /* Add line to the history. */ static void status_prompt_add_history(const char *line) { size_t size; if (status_prompt_hsize > 0 && strcmp(status_prompt_hlist[status_prompt_hsize - 1], line) == 0) return; if (status_prompt_hsize == PROMPT_HISTORY) { free(status_prompt_hlist[0]); size = (PROMPT_HISTORY - 1) * sizeof *status_prompt_hlist; memmove(&status_prompt_hlist[0], &status_prompt_hlist[1], size); status_prompt_hlist[status_prompt_hsize - 1] = xstrdup(line); return; } status_prompt_hlist = xreallocarray(status_prompt_hlist, status_prompt_hsize + 1, sizeof *status_prompt_hlist); status_prompt_hlist[status_prompt_hsize++] = xstrdup(line); } /* Build completion list. */ char ** status_prompt_complete_list(u_int *size, const char *s) { char **list = NULL; const char **layout, *value, *cp; const struct cmd_entry **cmdent; const struct options_table_entry *oe; u_int idx; size_t slen = strlen(s), valuelen; struct options_entry *o; struct options_array_item *a; const char *layouts[] = { "even-horizontal", "even-vertical", "main-horizontal", "main-vertical", "tiled", NULL }; *size = 0; for (cmdent = cmd_table; *cmdent != NULL; cmdent++) { if (strncmp((*cmdent)->name, s, slen) == 0) { list = xreallocarray(list, (*size) + 1, sizeof *list); list[(*size)++] = xstrdup((*cmdent)->name); } } for (oe = options_table; oe->name != NULL; oe++) { if (strncmp(oe->name, s, slen) == 0) { list = xreallocarray(list, (*size) + 1, sizeof *list); list[(*size)++] = xstrdup(oe->name); } } for (layout = layouts; *layout != NULL; layout++) { if (strncmp(*layout, s, slen) == 0) { list = xreallocarray(list, (*size) + 1, sizeof *list); list[(*size)++] = xstrdup(*layout); } } o = options_get_only(global_options, "command-alias"); if (o != NULL) { a = options_array_first(o); while (a != NULL) { value = options_array_item_value(a)->string; if ((cp = strchr(value, '=')) == NULL) goto next; valuelen = cp - value; if (slen > valuelen || strncmp(value, s, slen) != 0) goto next; list = xreallocarray(list, (*size) + 1, sizeof *list); list[(*size)++] = xstrndup(value, valuelen); next: a = options_array_next(a); } } for (idx = 0; idx < (*size); idx++) log_debug("complete %u: %s", idx, list[idx]); return (list); } /* Find longest prefix. */ static char * status_prompt_complete_prefix(char **list, u_int size) { char *out; u_int i; size_t j; out = xstrdup(list[0]); for (i = 1; i < size; i++) { j = strlen(list[i]); if (j > strlen(out)) j = strlen(out); for (; j > 0; j--) { if (out[j - 1] != list[i][j - 1]) out[j - 1] = '\0'; } } return (out); } /* Complete word. */ static char * status_prompt_complete(struct session *session, const char *s) { char **list = NULL; const char *colon; u_int size = 0, i; struct session *s_loop; struct winlink *wl; struct window *w; char *copy, *out, *tmp; if (*s == '\0') return (NULL); out = NULL; if (strncmp(s, "-t", 2) != 0 && strncmp(s, "-s", 2) != 0) { list = status_prompt_complete_list(&size, s); if (size == 0) out = NULL; else if (size == 1) xasprintf(&out, "%s ", list[0]); else out = status_prompt_complete_prefix(list, size); for (i = 0; i < size; i++) free(list[i]); free(list); return (out); } copy = xstrdup(s); colon = ":"; if (copy[strlen(copy) - 1] == ':') copy[strlen(copy) - 1] = '\0'; else colon = ""; s = copy + 2; RB_FOREACH(s_loop, sessions, &sessions) { if (strncmp(s_loop->name, s, strlen(s)) == 0) { list = xreallocarray(list, size + 2, sizeof *list); list[size++] = s_loop->name; } } if (size == 1) { out = xstrdup(list[0]); if (session_find(list[0]) != NULL) colon = ":"; } else if (size != 0) out = status_prompt_complete_prefix(list, size); if (out != NULL) { xasprintf(&tmp, "-%c%s%s", copy[1], out, colon); free(out); out = tmp; goto found; } colon = ""; if (*s == ':') { RB_FOREACH(wl, winlinks, &session->windows) { xasprintf(&tmp, ":%s", wl->window->name); if (strncmp(tmp, s, strlen(s)) == 0){ list = xreallocarray(list, size + 1, sizeof *list); list[size++] = tmp; continue; } free(tmp); xasprintf(&tmp, ":%d", wl->idx); if (strncmp(tmp, s, strlen(s)) == 0) { list = xreallocarray(list, size + 1, sizeof *list); list[size++] = tmp; continue; } free(tmp); } } else { RB_FOREACH(s_loop, sessions, &sessions) { RB_FOREACH(wl, winlinks, &s_loop->windows) { w = wl->window; xasprintf(&tmp, "%s:%s", s_loop->name, w->name); if (strncmp(tmp, s, strlen(s)) == 0) { list = xreallocarray(list, size + 1, sizeof *list); list[size++] = tmp; continue; } free(tmp); xasprintf(&tmp, "%s:%d", s_loop->name, wl->idx); if (strncmp(tmp, s, strlen(s)) == 0) { list = xreallocarray(list, size + 1, sizeof *list); list[size++] = tmp; continue; } free(tmp); } } } if (size == 1) { out = xstrdup(list[0]); colon = " "; } else if (size != 0) out = status_prompt_complete_prefix(list, size); if (out != NULL) { xasprintf(&tmp, "-%c%s%s", copy[1], out, colon); out = tmp; } for (i = 0; i < size; i++) free((void *)list[i]); found: free(copy); free(list); return (out); } tmux-3.0a/style.c100644 001750 001750 00000020030 13570677043 0007527/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * Copyright (c) 2014 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" /* Mask for bits not included in style. */ #define STYLE_ATTR_MASK (~GRID_ATTR_CHARSET) /* Default style. */ static struct style style_default = { { { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0 }, 8, STYLE_ALIGN_DEFAULT, STYLE_LIST_OFF, STYLE_RANGE_NONE, 0 }; /* * Parse an embedded style of the form "fg=colour,bg=colour,bright,...". * Note that this adds onto the given style, so it must have been initialized * alredy. */ int style_parse(struct style *sy, const struct grid_cell *base, const char *in) { struct style saved; const char delimiters[] = " ,", *cp; char tmp[256], *found; int value; size_t end; if (*in == '\0') return (0); style_copy(&saved, sy); do { while (*in != '\0' && strchr(delimiters, *in) != NULL) in++; if (*in == '\0') break; end = strcspn(in, delimiters); if (end > (sizeof tmp) - 1) goto error; memcpy(tmp, in, end); tmp[end] = '\0'; if (strcasecmp(tmp, "default") == 0) { sy->gc.fg = base->fg; sy->gc.bg = base->bg; sy->gc.attr = base->attr; sy->gc.flags = base->flags; } else if (strcasecmp(tmp, "nolist") == 0) sy->list = STYLE_LIST_OFF; else if (strncasecmp(tmp, "list=", 5) == 0) { if (strcasecmp(tmp + 5, "on") == 0) sy->list = STYLE_LIST_ON; else if (strcasecmp(tmp + 5, "focus") == 0) sy->list = STYLE_LIST_FOCUS; else if (strcasecmp(tmp + 5, "left-marker") == 0) sy->list = STYLE_LIST_LEFT_MARKER; else if (strcasecmp(tmp + 5, "right-marker") == 0) sy->list = STYLE_LIST_RIGHT_MARKER; else goto error; } else if (strcasecmp(tmp, "norange") == 0) { sy->range_type = style_default.range_type; sy->range_argument = style_default.range_type; } else if (end > 6 && strncasecmp(tmp, "range=", 6) == 0) { found = strchr(tmp + 6, '|'); if (found != NULL) { *found++ = '\0'; if (*found == '\0') goto error; for (cp = found; *cp != '\0'; cp++) { if (!isdigit((u_char)*cp)) goto error; } } if (strcasecmp(tmp + 6, "left") == 0) { if (found != NULL) goto error; sy->range_type = STYLE_RANGE_LEFT; sy->range_argument = 0; } else if (strcasecmp(tmp + 6, "right") == 0) { if (found != NULL) goto error; sy->range_type = STYLE_RANGE_RIGHT; sy->range_argument = 0; } else if (strcasecmp(tmp + 6, "window") == 0) { if (found == NULL) goto error; sy->range_type = STYLE_RANGE_WINDOW; sy->range_argument = atoi(found); } } else if (strcasecmp(tmp, "noalign") == 0) sy->align = style_default.align; else if (end > 6 && strncasecmp(tmp, "align=", 6) == 0) { if (strcasecmp(tmp + 6, "left") == 0) sy->align = STYLE_ALIGN_LEFT; else if (strcasecmp(tmp + 6, "centre") == 0) sy->align = STYLE_ALIGN_CENTRE; else if (strcasecmp(tmp + 6, "right") == 0) sy->align = STYLE_ALIGN_RIGHT; else goto error; } else if (end > 5 && strncasecmp(tmp, "fill=", 5) == 0) { if ((value = colour_fromstring(tmp + 5)) == -1) goto error; sy->fill = value; } else if (end > 3 && strncasecmp(tmp + 1, "g=", 2) == 0) { if ((value = colour_fromstring(tmp + 3)) == -1) goto error; if (*in == 'f' || *in == 'F') { if (value != 8) sy->gc.fg = value; else sy->gc.fg = base->fg; } else if (*in == 'b' || *in == 'B') { if (value != 8) sy->gc.bg = value; else sy->gc.bg = base->bg; } else goto error; } else if (strcasecmp(tmp, "none") == 0) sy->gc.attr = 0; else if (end > 2 && strncasecmp(tmp, "no", 2) == 0) { if ((value = attributes_fromstring(tmp + 2)) == -1) goto error; sy->gc.attr &= ~value; } else { if ((value = attributes_fromstring(tmp)) == -1) goto error; sy->gc.attr |= value; } in += end + strspn(in + end, delimiters); } while (*in != '\0'); return (0); error: style_copy(sy, &saved); return (-1); } /* Convert style to a string. */ const char * style_tostring(struct style *sy) { struct grid_cell *gc = &sy->gc; int off = 0; const char *comma = "", *tmp = ""; static char s[256]; char b[16]; *s = '\0'; if (sy->list != STYLE_LIST_OFF) { if (sy->list == STYLE_LIST_ON) tmp = "on"; else if (sy->list == STYLE_LIST_FOCUS) tmp = "focus"; else if (sy->list == STYLE_LIST_LEFT_MARKER) tmp = "left-marker"; else if (sy->list == STYLE_LIST_RIGHT_MARKER) tmp = "right-marker"; off += xsnprintf(s + off, sizeof s - off, "%slist=%s", comma, tmp); comma = ","; } if (sy->range_type != STYLE_RANGE_NONE) { if (sy->range_type == STYLE_RANGE_LEFT) tmp = "left"; else if (sy->range_type == STYLE_RANGE_RIGHT) tmp = "right"; else if (sy->range_type == STYLE_RANGE_WINDOW) { snprintf(b, sizeof b, "window|%u", sy->range_argument); tmp = b; } off += xsnprintf(s + off, sizeof s - off, "%srange=%s", comma, tmp); comma = ","; } if (sy->align != STYLE_ALIGN_DEFAULT) { if (sy->align == STYLE_ALIGN_LEFT) tmp = "left"; else if (sy->align == STYLE_ALIGN_CENTRE) tmp = "centre"; else if (sy->align == STYLE_ALIGN_RIGHT) tmp = "right"; off += xsnprintf(s + off, sizeof s - off, "%salign=%s", comma, tmp); comma = ","; } if (sy->fill != 8) { off += xsnprintf(s + off, sizeof s - off, "%sfill=%s", comma, colour_tostring(sy->fill)); comma = ","; } if (gc->fg != 8) { off += xsnprintf(s + off, sizeof s - off, "%sfg=%s", comma, colour_tostring(gc->fg)); comma = ","; } if (gc->bg != 8) { off += xsnprintf(s + off, sizeof s - off, "%sbg=%s", comma, colour_tostring(gc->bg)); comma = ","; } if (gc->attr != 0 && gc->attr != GRID_ATTR_CHARSET) { xsnprintf(s + off, sizeof s - off, "%s%s", comma, attributes_tostring(gc->attr)); comma = ","; } if (*s == '\0') return ("default"); return (s); } /* Apply a style. */ void style_apply(struct grid_cell *gc, struct options *oo, const char *name) { struct style *sy; memcpy(gc, &grid_default_cell, sizeof *gc); sy = options_get_style(oo, name); gc->fg = sy->gc.fg; gc->bg = sy->gc.bg; gc->attr |= sy->gc.attr; } /* Apply a style, updating if default. */ void style_apply_update(struct grid_cell *gc, struct options *oo, const char *name) { struct style *sy; sy = options_get_style(oo, name); if (sy->gc.fg != 8) gc->fg = sy->gc.fg; if (sy->gc.bg != 8) gc->bg = sy->gc.bg; if (sy->gc.attr != 0) gc->attr |= sy->gc.attr; } /* Initialize style from cell. */ void style_set(struct style *sy, const struct grid_cell *gc) { memcpy(sy, &style_default, sizeof *sy); memcpy(&sy->gc, gc, sizeof sy->gc); } /* Copy style. */ void style_copy(struct style *dst, struct style *src) { memcpy(dst, src, sizeof *dst); } /* Check if two styles are (visibly) the same. */ int style_equal(struct style *sy1, struct style *sy2) { struct grid_cell *gc1 = &sy1->gc; struct grid_cell *gc2 = &sy2->gc; if (gc1->fg != gc2->fg) return (0); if (gc1->bg != gc2->bg) return (0); if ((gc1->attr & STYLE_ATTR_MASK) != (gc2->attr & STYLE_ATTR_MASK)) return (0); if (sy1->fill != sy2->fill) return (0); if (sy1->align != sy2->align) return (0); return (1); } /* Is this style default? */ int style_is_default(struct style *sy) { return (style_equal(sy, &style_default)); } tmux-3.0a/tmux.c100644 001750 001750 00000020703 13570677043 0007373/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "tmux.h" 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 timeval start_time; const char *socket_path; int ptm_fd = -1; const char *shell_command; static __dead void usage(void); static char *make_label(const char *, char **); static const char *getshell(void); static int checkshell(const char *); static __dead void usage(void) { fprintf(stderr, "usage: %s [-2CluvV] [-c shell-command] [-f file] [-L socket-name]\n" " [-S socket-path] [command [flags]]\n", getprogname()); exit(1); } static 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); } static int checkshell(const char *shell) { if (shell == NULL || *shell != '/') return (0); if (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 = getprogname(); if (*progname == '-') progname++; if (strcmp(ptr, progname) == 0) return (1); return (0); } static char * make_label(const char *label, char **cause) { char *base, resolved[PATH_MAX], *path, *s; struct stat sb; uid_t uid; *cause = NULL; if (label == NULL) label = "default"; uid = getuid(); if ((s = getenv("TMUX_TMPDIR")) != NULL && *s != '\0') xasprintf(&base, "%s/tmux-%ld", s, (long)uid); else xasprintf(&base, "%s/tmux-%ld", _PATH_TMP, (long)uid); if (realpath(base, resolved) == NULL && strlcpy(resolved, base, sizeof resolved) >= sizeof resolved) { errno = ERANGE; free(base); goto fail; } if (mkdir(resolved, S_IRWXU) != 0 && errno != EEXIST) goto fail; if (lstat(resolved, &sb) != 0) goto fail; if (!S_ISDIR(sb.st_mode)) { errno = ENOTDIR; goto fail; } if (sb.st_uid != uid || (sb.st_mode & S_IRWXO) != 0) { errno = EACCES; goto fail; } xasprintf(&path, "%s/%s", resolved, label); return (path); fail: xasprintf(cause, "error creating %s (%s)", resolved, strerror(errno)); return (NULL); } 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); } } const char * find_cwd(void) { char resolved1[PATH_MAX], resolved2[PATH_MAX]; static char cwd[PATH_MAX]; const char *pwd; if (getcwd(cwd, sizeof cwd) == NULL) return (NULL); if ((pwd = getenv("PWD")) == NULL || *pwd == '\0') return (cwd); /* * We want to use PWD so that symbolic links are maintained, * but only if it matches the actual working directory. */ if (realpath(pwd, resolved1) == NULL) return (cwd); if (realpath(cwd, resolved2) == NULL) return (cwd); if (strcmp(resolved1, resolved2) != 0) return (cwd); return (pwd); } const char * find_home(void) { struct passwd *pw; static const char *home; if (home != NULL) return (home); home = getenv("HOME"); if (home == NULL || *home == '\0') { pw = getpwuid(getuid()); if (pw != NULL) home = pw->pw_dir; else home = NULL; } return (home); } int main(int argc, char **argv) { char *path, *label, *cause, **var; const char *s, *shell, *cwd; int opt, flags, keys; const struct options_table_entry *oe; if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL && setlocale(LC_CTYPE, "C.UTF-8") == NULL) { if (setlocale(LC_CTYPE, "") == NULL) errx(1, "invalid LC_ALL, LC_CTYPE or LANG"); s = nl_langinfo(CODESET); if (strcasecmp(s, "UTF-8") != 0 && strcasecmp(s, "UTF8") != 0) errx(1, "need UTF-8 locale (LC_CTYPE) but have %s", s); } setlocale(LC_TIME, ""); tzset(); if (**argv == '-') flags = CLIENT_LOGIN; else flags = 0; label = path = NULL; while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUVv")) != -1) { switch (opt) { case '2': flags |= CLIENT_256COLOURS; break; case 'c': shell_command = optarg; break; case 'C': if (flags & CLIENT_CONTROL) flags |= CLIENT_CONTROLCONTROL; else flags |= CLIENT_CONTROL; break; case 'V': printf("%s %s\n", getprogname(), VERSION); exit(0); case 'f': set_cfg_file(optarg); break; case 'l': flags |= CLIENT_LOGIN; break; case 'L': free(label); label = xstrdup(optarg); break; case 'q': break; case 'S': free(path); path = xstrdup(optarg); break; case 'u': flags |= CLIENT_UTF8; break; case 'v': log_add_level(); break; default: usage(); } } argc -= optind; argv += optind; if (shell_command != NULL && argc != 0) usage(); if ((ptm_fd = getptmfd()) == -1) err(1, "getptmfd"); if (pledge("stdio rpath wpath cpath flock fattr unix getpw sendfd " "recvfd proc exec tty ps", NULL) != 0) err(1, "pledge"); /* * tmux is a UTF-8 terminal, so if TMUX is set, assume UTF-8. * Otherwise, if the user has set LC_ALL, LC_CTYPE or LANG to contain * UTF-8, it is a safe assumption that either they are using a UTF-8 * terminal, or if not they know that output from UTF-8-capable * programs may be wrong. */ if (getenv("TMUX") != NULL) flags |= CLIENT_UTF8; else { s = getenv("LC_ALL"); if (s == NULL || *s == '\0') s = getenv("LC_CTYPE"); if (s == NULL || *s == '\0') s = getenv("LANG"); if (s == NULL || *s == '\0') s = ""; if (strcasestr(s, "UTF-8") != NULL || strcasestr(s, "UTF8") != NULL) flags |= CLIENT_UTF8; } global_environ = environ_create(); for (var = environ; *var != NULL; var++) environ_put(global_environ, *var); if ((cwd = find_cwd()) != NULL) environ_set(global_environ, "PWD", "%s", cwd); global_options = options_create(NULL); global_s_options = options_create(NULL); global_w_options = options_create(NULL); for (oe = options_table; oe->name != NULL; oe++) { if (oe->scope & OPTIONS_TABLE_SERVER) options_default(global_options, oe); if (oe->scope & OPTIONS_TABLE_SESSION) options_default(global_s_options, oe); if (oe->scope & OPTIONS_TABLE_WINDOW) options_default(global_w_options, oe); } /* * The default shell comes from SHELL or from the user's passwd entry * if available. */ shell = getshell(); options_set_string(global_s_options, "default-shell", 0, "%s", shell); /* 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); } /* * If socket is specified on the command-line with -S or -L, it is * used. Otherwise, $TMUX is checked and if that fails "default" is * used. */ if (path == NULL && label == NULL) { s = getenv("TMUX"); if (s != NULL && *s != '\0' && *s != ',') { path = xstrdup(s); path[strcspn(path, ",")] = '\0'; } } if (path == NULL && (path = make_label(label, &cause)) == NULL) { if (cause != NULL) { fprintf(stderr, "%s\n", cause); free(cause); } exit(1); } socket_path = path; free(label); /* Pass control to the client. */ exit(client_main(osdep_event_init(), argc, argv, flags)); } tmux-3.0a/tmux.h100644 001750 001750 00000226062 13570677043 0007406/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef TMUX_H #define TMUX_H #include #include #include #include #include #include #include #include #ifdef HAVE_UTEMPTER #include #endif #include "compat.h" #include "xmalloc.h" extern char **environ; struct args; struct args_value; struct client; struct cmd_find_state; struct cmdq_item; struct cmdq_list; struct environ; struct format_job_tree; struct format_tree; struct input_ctx; struct job; struct mode_tree_data; struct mouse_event; struct options; struct options_entry; struct options_array_item; struct session; struct tmuxpeer; struct tmuxproc; struct winlink; /* Client-server protocol version. */ #define PROTOCOL_VERSION 8 /* Default global configuration file. */ #ifndef TMUX_CONF #define TMUX_CONF "/etc/tmux.conf" #endif /* Minimum layout cell size, NOT including border lines. */ #define PANE_MINIMUM 1 /* Minimum and maximum window size. */ #define WINDOW_MINIMUM PANE_MINIMUM #define WINDOW_MAXIMUM 10000 /* Automatic name refresh interval, in microseconds. Must be < 1 second. */ #define NAME_INTERVAL 500000 /* Maximum size of data to hold from a pane. */ #define READ_SIZE 4096 /* Attribute to make GCC check printf-like arguments. */ #define printflike(a, b) __attribute__ ((format (printf, a, b))) /* Number of items in array. */ #ifndef nitems #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) #endif /* Alert option values. */ #define ALERT_NONE 0 #define ALERT_ANY 1 #define ALERT_CURRENT 2 #define ALERT_OTHER 3 /* Visual option values. */ #define VISUAL_OFF 0 #define VISUAL_ON 1 #define VISUAL_BOTH 2 /* Special key codes. */ #define KEYC_NONE 0xffff00000000ULL #define KEYC_UNKNOWN 0xfffe00000000ULL #define KEYC_BASE 0x000010000000ULL #define KEYC_USER 0x000020000000ULL /* Available user keys. */ #define KEYC_NUSER 1000 /* Key modifier bits. */ #define KEYC_ESCAPE 0x200000000000ULL #define KEYC_CTRL 0x400000000000ULL #define KEYC_SHIFT 0x800000000000ULL #define KEYC_XTERM 0x1000000000000ULL #define KEYC_LITERAL 0x2000000000000ULL /* Mask to obtain key w/o modifiers. */ #define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT|KEYC_XTERM|KEYC_LITERAL) #define KEYC_MASK_KEY (~KEYC_MASK_MOD) /* Is this a mouse key? */ #define KEYC_IS_MOUSE(key) (((key) & KEYC_MASK_KEY) >= KEYC_MOUSE && \ ((key) & KEYC_MASK_KEY) < KEYC_BSPACE) /* Multiple click timeout. */ #define KEYC_CLICK_TIMEOUT 300 /* Mouse key codes. */ #define KEYC_MOUSE_KEY(name) \ KEYC_ ## name ## _PANE, \ KEYC_ ## name ## _STATUS, \ KEYC_ ## name ## _STATUS_LEFT, \ KEYC_ ## name ## _STATUS_RIGHT, \ KEYC_ ## name ## _STATUS_DEFAULT, \ KEYC_ ## name ## _BORDER #define KEYC_MOUSE_STRING(name, s) \ { #s "Pane", KEYC_ ## name ## _PANE }, \ { #s "Status", KEYC_ ## name ## _STATUS }, \ { #s "StatusLeft", KEYC_ ## name ## _STATUS_LEFT }, \ { #s "StatusRight", KEYC_ ## name ## _STATUS_RIGHT }, \ { #s "StatusDefault", KEYC_ ## name ## _STATUS_DEFAULT }, \ { #s "Border", KEYC_ ## name ## _BORDER } /* * A single key. This can be ASCII or Unicode or one of the keys starting at * KEYC_BASE. */ typedef unsigned long long key_code; /* Special key codes. */ enum { /* Focus events. */ KEYC_FOCUS_IN = KEYC_BASE, KEYC_FOCUS_OUT, /* "Any" key, used if not found in key table. */ KEYC_ANY, /* Paste brackets. */ KEYC_PASTE_START, KEYC_PASTE_END, /* Mouse keys. */ KEYC_MOUSE, /* unclassified mouse event */ KEYC_DRAGGING, /* dragging in progress */ KEYC_MOUSE_KEY(MOUSEMOVE), KEYC_MOUSE_KEY(MOUSEDOWN1), KEYC_MOUSE_KEY(MOUSEDOWN2), KEYC_MOUSE_KEY(MOUSEDOWN3), KEYC_MOUSE_KEY(MOUSEUP1), KEYC_MOUSE_KEY(MOUSEUP2), KEYC_MOUSE_KEY(MOUSEUP3), KEYC_MOUSE_KEY(MOUSEDRAG1), KEYC_MOUSE_KEY(MOUSEDRAG2), KEYC_MOUSE_KEY(MOUSEDRAG3), KEYC_MOUSE_KEY(MOUSEDRAGEND1), KEYC_MOUSE_KEY(MOUSEDRAGEND2), KEYC_MOUSE_KEY(MOUSEDRAGEND3), KEYC_MOUSE_KEY(WHEELUP), KEYC_MOUSE_KEY(WHEELDOWN), KEYC_MOUSE_KEY(DOUBLECLICK1), KEYC_MOUSE_KEY(DOUBLECLICK2), KEYC_MOUSE_KEY(DOUBLECLICK3), KEYC_MOUSE_KEY(TRIPLECLICK1), KEYC_MOUSE_KEY(TRIPLECLICK2), KEYC_MOUSE_KEY(TRIPLECLICK3), /* 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_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_ACSC, TTYC_AX, TTYC_BCE, TTYC_BEL, TTYC_BLINK, TTYC_BOLD, TTYC_CIVIS, TTYC_CLEAR, TTYC_CNORM, TTYC_COLORS, TTYC_CR, TTYC_CS, TTYC_CSR, TTYC_CUB, TTYC_CUB1, TTYC_CUD, TTYC_CUD1, TTYC_CUF, TTYC_CUF1, TTYC_CUP, TTYC_CUU, TTYC_CUU1, TTYC_CVVIS, TTYC_DCH, TTYC_DCH1, TTYC_DIM, TTYC_DL, TTYC_DL1, TTYC_E3, TTYC_ECH, TTYC_ED, TTYC_EL, TTYC_EL1, TTYC_ENACS, TTYC_FSL, TTYC_HOME, TTYC_HPA, TTYC_ICH, TTYC_ICH1, TTYC_IL, TTYC_IL1, TTYC_INDN, TTYC_INVIS, TTYC_KCBT, TTYC_KCUB1, TTYC_KCUD1, TTYC_KCUF1, TTYC_KCUU1, TTYC_KDC2, TTYC_KDC3, TTYC_KDC4, TTYC_KDC5, TTYC_KDC6, TTYC_KDC7, TTYC_KDCH1, TTYC_KDN2, TTYC_KDN3, TTYC_KDN4, TTYC_KDN5, TTYC_KDN6, TTYC_KDN7, TTYC_KEND, TTYC_KEND2, TTYC_KEND3, TTYC_KEND4, TTYC_KEND5, TTYC_KEND6, TTYC_KEND7, TTYC_KF1, TTYC_KF10, TTYC_KF11, TTYC_KF12, TTYC_KF13, TTYC_KF14, TTYC_KF15, TTYC_KF16, TTYC_KF17, TTYC_KF18, TTYC_KF19, TTYC_KF2, TTYC_KF20, TTYC_KF21, TTYC_KF22, TTYC_KF23, TTYC_KF24, TTYC_KF25, TTYC_KF26, TTYC_KF27, TTYC_KF28, TTYC_KF29, TTYC_KF3, TTYC_KF30, TTYC_KF31, TTYC_KF32, TTYC_KF33, TTYC_KF34, TTYC_KF35, TTYC_KF36, TTYC_KF37, TTYC_KF38, TTYC_KF39, TTYC_KF4, TTYC_KF40, TTYC_KF41, TTYC_KF42, TTYC_KF43, TTYC_KF44, TTYC_KF45, TTYC_KF46, TTYC_KF47, TTYC_KF48, TTYC_KF49, TTYC_KF5, TTYC_KF50, TTYC_KF51, TTYC_KF52, TTYC_KF53, TTYC_KF54, TTYC_KF55, TTYC_KF56, TTYC_KF57, TTYC_KF58, TTYC_KF59, TTYC_KF6, TTYC_KF60, TTYC_KF61, TTYC_KF62, TTYC_KF63, TTYC_KF7, TTYC_KF8, TTYC_KF9, TTYC_KHOM2, TTYC_KHOM3, TTYC_KHOM4, TTYC_KHOM5, TTYC_KHOM6, TTYC_KHOM7, TTYC_KHOME, TTYC_KIC2, TTYC_KIC3, TTYC_KIC4, TTYC_KIC5, TTYC_KIC6, TTYC_KIC7, TTYC_KICH1, TTYC_KIND, TTYC_KLFT2, TTYC_KLFT3, TTYC_KLFT4, TTYC_KLFT5, TTYC_KLFT6, TTYC_KLFT7, TTYC_KMOUS, TTYC_KNP, TTYC_KNXT2, TTYC_KNXT3, TTYC_KNXT4, TTYC_KNXT5, TTYC_KNXT6, TTYC_KNXT7, TTYC_KPP, TTYC_KPRV2, TTYC_KPRV3, TTYC_KPRV4, TTYC_KPRV5, TTYC_KPRV6, TTYC_KPRV7, TTYC_KRI, 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, TTYC_OP, TTYC_REV, TTYC_RGB, TTYC_RI, TTYC_RIN, TTYC_RMACS, TTYC_RMCUP, TTYC_RMKX, TTYC_SE, TTYC_SETAB, TTYC_SETAF, TTYC_SETRGBB, TTYC_SETRGBF, TTYC_SETULC, TTYC_SGR0, TTYC_SITM, TTYC_SMACS, TTYC_SMCUP, TTYC_SMOL, TTYC_SMKX, TTYC_SMSO, TTYC_SMULX, TTYC_SMUL, TTYC_SMXX, TTYC_SS, TTYC_TC, TTYC_TSL, TTYC_U8, TTYC_VPA, TTYC_XENL, TTYC_XT, }; /* Message codes. */ enum msgtype { MSG_VERSION = 12, MSG_IDENTIFY_FLAGS = 100, MSG_IDENTIFY_TERM, MSG_IDENTIFY_TTYNAME, MSG_IDENTIFY_OLDCWD, /* unused */ MSG_IDENTIFY_STDIN, MSG_IDENTIFY_ENVIRON, MSG_IDENTIFY_DONE, MSG_IDENTIFY_CLIENTPID, MSG_IDENTIFY_CWD, MSG_COMMAND = 200, MSG_DETACH, MSG_DETACHKILL, MSG_EXIT, MSG_EXITED, MSG_EXITING, MSG_LOCK, MSG_READY, MSG_RESIZE, MSG_SHELL, MSG_SHUTDOWN, MSG_STDERR, MSG_STDIN, MSG_STDOUT, MSG_SUSPEND, MSG_UNLOCK, MSG_WAKEUP, MSG_EXEC, }; /* * Message data. * * Don't forget to bump PROTOCOL_VERSION if any of these change! */ struct msg_command_data { int argc; }; /* followed by packed argv */ struct msg_stdin_data { ssize_t size; char data[BUFSIZ]; }; struct msg_stdout_data { ssize_t size; char data[BUFSIZ]; }; struct msg_stderr_data { ssize_t size; char data[BUFSIZ]; }; /* Mode keys. */ #define MODEKEY_EMACS 0 #define MODEKEY_VI 1 /* Modes. */ #define MODE_CURSOR 0x1 #define MODE_INSERT 0x2 #define MODE_KCURSOR 0x4 #define MODE_KKEYPAD 0x8 /* set = application, clear = number */ #define MODE_WRAP 0x10 /* whether lines wrap */ #define MODE_MOUSE_STANDARD 0x20 #define MODE_MOUSE_BUTTON 0x40 #define MODE_BLINKING 0x80 #define MODE_MOUSE_UTF8 0x100 #define MODE_MOUSE_SGR 0x200 #define MODE_BRACKETPASTE 0x400 #define MODE_FOCUSON 0x800 #define MODE_MOUSE_ALL 0x1000 #define MODE_ORIGIN 0x2000 #define MODE_CRLF 0x4000 #define ALL_MODES 0xffffff #define ALL_MOUSE_MODES (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON|MODE_MOUSE_ALL) /* * A single UTF-8 character. UTF8_SIZE must be big enough to hold * combining characters as well, currently at most five (of three * bytes) are supported. */ #define UTF8_SIZE 18 struct utf8_data { u_char data[UTF8_SIZE]; u_char have; u_char size; u_char width; /* 0xff if invalid */ } __packed; enum utf8_state { UTF8_MORE, UTF8_DONE, UTF8_ERROR }; /* Colour flags. */ #define COLOUR_FLAG_256 0x01000000 #define COLOUR_FLAG_RGB 0x02000000 /* Special colours. */ #define COLOUR_DEFAULT(c) ((c) == 8 || (c) == 9) /* Grid attributes. Anything above 0xff is stored in an extended cell. */ #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 */ #define GRID_ATTR_STRIKETHROUGH 0x100 #define GRID_ATTR_UNDERSCORE_2 0x200 #define GRID_ATTR_UNDERSCORE_3 0x400 #define GRID_ATTR_UNDERSCORE_4 0x800 #define GRID_ATTR_UNDERSCORE_5 0x1000 #define GRID_ATTR_OVERLINE 0x2000 /* All underscore attributes. */ #define GRID_ATTR_ALL_UNDERSCORE \ (GRID_ATTR_UNDERSCORE| \ GRID_ATTR_UNDERSCORE_2| \ GRID_ATTR_UNDERSCORE_3| \ GRID_ATTR_UNDERSCORE_4| \ GRID_ATTR_UNDERSCORE_5) /* Grid flags. */ #define GRID_FLAG_FG256 0x1 #define GRID_FLAG_BG256 0x2 #define GRID_FLAG_PADDING 0x4 #define GRID_FLAG_EXTENDED 0x8 #define GRID_FLAG_SELECTED 0x10 #define GRID_FLAG_NOPALETTE 0x20 #define GRID_FLAG_CLEARED 0x40 /* Grid line flags. */ #define GRID_LINE_WRAPPED 0x1 #define GRID_LINE_EXTENDED 0x2 #define GRID_LINE_DEAD 0x4 /* Grid cell data. */ struct grid_cell { struct utf8_data data; /* 21 bytes */ u_short attr; u_char flags; int fg; int bg; int us; } __packed; struct grid_cell_entry { u_char flags; union { u_int offset; struct { u_char attr; u_char fg; u_char bg; u_char data; } data; }; } __packed; /* Grid line. */ struct grid_line { u_int cellused; u_int cellsize; struct grid_cell_entry *celldata; u_int extdsize; struct grid_cell *extddata; 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 hscrolled; u_int hsize; u_int hlimit; struct grid_line *linedata; }; /* Style alignment. */ enum style_align { STYLE_ALIGN_DEFAULT, STYLE_ALIGN_LEFT, STYLE_ALIGN_CENTRE, STYLE_ALIGN_RIGHT }; /* Style list. */ enum style_list { STYLE_LIST_OFF, STYLE_LIST_ON, STYLE_LIST_FOCUS, STYLE_LIST_LEFT_MARKER, STYLE_LIST_RIGHT_MARKER, }; /* Style range. */ enum style_range_type { STYLE_RANGE_NONE, STYLE_RANGE_LEFT, STYLE_RANGE_RIGHT, STYLE_RANGE_WINDOW }; struct style_range { enum style_range_type type; u_int argument; u_int start; u_int end; /* not included */ TAILQ_ENTRY(style_range) entry; }; TAILQ_HEAD(style_ranges, style_range); /* Style option. */ struct style { struct grid_cell gc; int fill; enum style_align align; enum style_list list; enum style_range_type range_type; u_int range_argument; }; /* Virtual screen. */ struct screen_sel; struct screen_titles; struct screen { char *title; struct screen_titles *titles; 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_collect_item; struct screen_write_collect_line; struct screen_write_ctx { struct window_pane *wp; struct screen *s; struct screen_write_collect_item *item; struct screen_write_collect_line *list; u_int scrolled; u_int bg; u_int cells; u_int written; u_int skipped; }; /* Screen redraw context. */ struct screen_redraw_ctx { struct client *c; u_int statuslines; int statustop; int pane_status; u_int sx; u_int sy; u_int ox; u_int oy; }; /* 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) /* Menu. */ struct menu_item { const char *name; key_code key; const char *command; }; struct menu { const char *title; struct menu_item *items; u_int count; u_int width; }; typedef void (*menu_choice_cb)(struct menu *, u_int, key_code, void *); #define MENU_NOMOUSE 0x1 /* * Window mode. Windows can be in several modes and this is used to call the * right function to handle input and output. */ struct window_mode_entry; struct window_mode { const char *name; const char *default_format; struct screen *(*init)(struct window_mode_entry *, struct cmd_find_state *, struct args *); void (*free)(struct window_mode_entry *); void (*resize)(struct window_mode_entry *, u_int, u_int); void (*key)(struct window_mode_entry *, struct client *, struct session *, struct winlink *, key_code, struct mouse_event *); const char *(*key_table)(struct window_mode_entry *); void (*command)(struct window_mode_entry *, struct client *, struct session *, struct winlink *, struct args *, struct mouse_event *); void (*formats)(struct window_mode_entry *, struct format_tree *); }; #define WINDOW_MODE_TIMEOUT 180 /* Active window mode. */ struct window_mode_entry { struct window_pane *wp; const struct window_mode *mode; void *data; struct screen *screen; u_int prefix; TAILQ_ENTRY (window_mode_entry) entry; }; /* Child window structure. */ struct window_pane { u_int id; u_int active_point; struct window *window; struct options *options; struct layout_cell *layout_cell; struct layout_cell *saved_layout_cell; u_int sx; u_int sy; u_int osx; u_int osy; u_int xoff; u_int yoff; int flags; #define PANE_REDRAW 0x1 #define PANE_DROP 0x2 #define PANE_FOCUSED 0x4 #define PANE_RESIZE 0x8 #define PANE_RESIZEFORCE 0x10 #define PANE_FOCUSPUSH 0x20 #define PANE_INPUTOFF 0x40 #define PANE_CHANGED 0x80 #define PANE_EXITED 0x100 #define PANE_STATUSREADY 0x200 #define PANE_STATUSDRAWN 0x400 #define PANE_EMPTY 0x800 #define PANE_STYLECHANGED 0x1000 int argc; char **argv; char *shell; char *cwd; pid_t pid; char tty[TTY_NAME_MAX]; int status; int fd; struct bufferevent *event; u_int disabled; struct event resize_timer; struct input_ctx *ictx; struct style cached_style; struct style cached_active_style; int *palette; int pipe_fd; struct bufferevent *pipe_event; size_t pipe_off; struct screen *screen; struct screen base; struct screen status_screen; size_t status_size; /* Saved in alternative screen mode. */ u_int saved_cx; u_int saved_cy; struct grid *saved_grid; struct grid_cell saved_cell; TAILQ_HEAD (, window_mode_entry) modes; struct event modetimer; time_t modelast; char *searchstr; 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 { u_int id; char *name; struct event name_event; struct timeval name_time; struct event alerts_timer; struct event offset_timer; struct timeval activity_time; struct window_pane *active; struct window_pane *last; struct window_panes panes; int lastlayout; struct layout_cell *layout_root; struct layout_cell *saved_layout_root; char *old_layout; u_int sx; u_int sy; int flags; #define WINDOW_BELL 0x1 #define WINDOW_ACTIVITY 0x2 #define WINDOW_SILENCE 0x4 #define WINDOW_ZOOMED 0x8 #define WINDOW_ALERTFLAGS (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_SILENCE) int alerts_queued; TAILQ_ENTRY(window) alerts_entry; struct options *options; u_int references; TAILQ_HEAD(, winlink) winlinks; RB_ENTRY(window) entry; }; RB_HEAD(windows, window); /* Entry on local window list. */ struct winlink { int idx; struct session *session; struct window *window; int flags; #define WINLINK_BELL 0x1 #define WINLINK_ACTIVITY 0x2 #define WINLINK_SILENCE 0x4 #define WINLINK_ALERTFLAGS (WINLINK_BELL|WINLINK_ACTIVITY|WINLINK_SILENCE) RB_ENTRY(winlink) entry; TAILQ_ENTRY(winlink) wentry; TAILQ_ENTRY(winlink) sentry; }; RB_HEAD(winlinks, winlink); TAILQ_HEAD(winlink_stack, winlink); /* Window size option. */ #define WINDOW_SIZE_LARGEST 0 #define WINDOW_SIZE_SMALLEST 1 #define WINDOW_SIZE_MANUAL 2 /* Pane border status option. */ #define PANE_STATUS_OFF 0 #define PANE_STATUS_TOP 1 #define PANE_STATUS_BOTTOM 2 /* 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; }; /* Environment variable. */ struct environ_entry { char *name; char *value; RB_ENTRY(environ_entry) entry; }; /* Client session. */ struct session_group { const char *name; TAILQ_HEAD(, session) sessions; RB_ENTRY(session_group) entry; }; RB_HEAD(session_groups, session_group); struct session { u_int id; char *name; const char *cwd; struct timeval creation_time; struct timeval last_attached_time; struct timeval activity_time; struct timeval last_activity_time; struct event lock_timer; struct winlink *curw; struct winlink_stack lastw; struct winlinks windows; int statusat; u_int statuslines; struct options *options; #define SESSION_PASTING 0x1 #define SESSION_ALERTED 0x2 int flags; u_int attached; struct termios *tio; struct environ *environ; int references; TAILQ_ENTRY(session) gentry; RB_ENTRY(session) entry; }; RB_HEAD(sessions, session); /* Mouse button masks. */ #define MOUSE_MASK_BUTTONS 3 #define MOUSE_MASK_SHIFT 4 #define MOUSE_MASK_META 8 #define MOUSE_MASK_CTRL 16 #define MOUSE_MASK_DRAG 32 #define MOUSE_MASK_WHEEL 64 /* Mouse wheel states. */ #define MOUSE_WHEEL_UP 0 #define MOUSE_WHEEL_DOWN 1 /* Mouse helpers. */ #define MOUSE_BUTTONS(b) ((b) & MOUSE_MASK_BUTTONS) #define MOUSE_WHEEL(b) ((b) & MOUSE_MASK_WHEEL) #define MOUSE_DRAG(b) ((b) & MOUSE_MASK_DRAG) #define MOUSE_RELEASE(b) (((b) & MOUSE_MASK_BUTTONS) == 3) /* Mouse input. */ struct mouse_event { int valid; key_code key; int statusat; u_int statuslines; u_int x; u_int y; u_int b; u_int lx; u_int ly; u_int lb; u_int ox; u_int oy; int s; int w; int wp; u_int sgr_type; u_int sgr_b; }; /* Key event. */ struct key_event { key_code key; struct mouse_event m; }; /* TTY information. */ struct tty_key { char ch; key_code key; struct tty_key *left; struct tty_key *right; struct tty_key *next; }; struct tty_code; struct tty_term { char *name; u_int references; char acs[UCHAR_MAX + 1][2]; struct tty_code *codes; #define TERM_256COLOURS 0x1 #define TERM_EARLYWRAP 0x2 int flags; LIST_ENTRY(tty_term) entry; }; LIST_HEAD(tty_terms, tty_term); struct tty { struct client *client; u_int sx; u_int sy; u_int cx; u_int cy; u_int cstyle; char *ccolour; int oflag; u_int oox; u_int ooy; u_int osx; u_int osy; int mode; u_int rlower; u_int rupper; u_int rleft; u_int rright; int fd; struct event event_in; struct evbuffer *in; struct event event_out; struct evbuffer *out; struct event timer; size_t discarded; struct termios tio; struct grid_cell cell; int last_wp; struct grid_cell last_cell; #define TTY_NOCURSOR 0x1 #define TTY_FREEZE 0x2 #define TTY_TIMER 0x4 #define TTY_UTF8 0x8 #define TTY_STARTED 0x10 #define TTY_OPENED 0x20 #define TTY_FOCUS 0x40 #define TTY_BLOCK 0x80 int flags; struct tty_term *term; char *term_name; int term_flags; enum { TTY_VT100, TTY_VT101, TTY_VT102, TTY_VT220, TTY_VT320, TTY_VT420, TTY_UNKNOWN } term_type; u_int mouse_last_x; u_int mouse_last_y; u_int mouse_last_b; int mouse_drag_flag; void (*mouse_drag_update)(struct client *, struct mouse_event *); void (*mouse_drag_release)(struct client *, struct mouse_event *); struct event key_timer; struct tty_key *key_tree; }; #define TTY_TYPES \ { "VT100", "VT101", "VT102", "VT220", "VT320", "VT420", "Unknown" } /* TTY command context. */ struct tty_ctx { struct window_pane *wp; const struct grid_cell *cell; int wrapped; 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; /* Pane offset. */ u_int xoff; u_int yoff; /* The background colour used for clearing (erasing). */ u_int bg; /* Window offset and size. */ int bigger; u_int ox; u_int oy; u_int sx; u_int sy; }; /* Saved message entry. */ struct message_entry { char *msg; u_int msg_num; time_t msg_time; TAILQ_ENTRY(message_entry) entry; }; /* Parsed arguments structures. */ struct args_entry; RB_HEAD(args_tree, args_entry); struct args { struct args_tree tree; int argc; char **argv; }; /* Command find structures. */ enum cmd_find_type { CMD_FIND_PANE, CMD_FIND_WINDOW, CMD_FIND_SESSION, }; struct cmd_find_state { int flags; struct cmd_find_state *current; struct session *s; struct winlink *wl; struct window *w; struct window_pane *wp; int idx; }; /* Command find flags. */ #define CMD_FIND_PREFER_UNATTACHED 0x1 #define CMD_FIND_QUIET 0x2 #define CMD_FIND_WINDOW_INDEX 0x4 #define CMD_FIND_DEFAULT_MARKED 0x8 #define CMD_FIND_EXACT_SESSION 0x10 #define CMD_FIND_EXACT_WINDOW 0x20 #define CMD_FIND_CANFAIL 0x40 /* Command and list of commands. */ struct cmd { const struct cmd_entry *entry; struct args *args; u_int group; char *file; u_int line; char *alias; int argc; char **argv; TAILQ_ENTRY(cmd) qentry; }; TAILQ_HEAD(cmds, cmd); struct cmd_list { int references; u_int group; struct cmds list; }; /* Command return values. */ enum cmd_retval { CMD_RETURN_ERROR = -1, CMD_RETURN_NORMAL = 0, CMD_RETURN_WAIT, CMD_RETURN_STOP }; /* Command parse result. */ enum cmd_parse_status { CMD_PARSE_EMPTY, CMD_PARSE_ERROR, CMD_PARSE_SUCCESS }; struct cmd_parse_result { enum cmd_parse_status status; struct cmd_list *cmdlist; char *error; }; struct cmd_parse_input { int flags; #define CMD_PARSE_QUIET 0x1 #define CMD_PARSE_PARSEONLY 0x2 #define CMD_PARSE_NOALIAS 0x4 #define CMD_PARSE_VERBOSE 0x8 const char *file; u_int line; struct cmdq_item *item; struct client *c; struct cmd_find_state fs; }; /* Command queue item type. */ enum cmdq_type { CMDQ_COMMAND, CMDQ_CALLBACK, }; /* Command queue item shared state. */ struct cmdq_shared { int references; int flags; #define CMDQ_SHARED_REPEAT 0x1 #define CMDQ_SHARED_CONTROL 0x2 struct format_tree *formats; struct mouse_event mouse; struct cmd_find_state current; }; /* Command queue item. */ typedef enum cmd_retval (*cmdq_cb) (struct cmdq_item *, void *); struct cmdq_item { char *name; struct cmdq_list *queue; struct cmdq_item *next; struct client *client; enum cmdq_type type; u_int group; u_int number; time_t time; int flags; #define CMDQ_FIRED 0x1 #define CMDQ_WAITING 0x2 #define CMDQ_NOHOOKS 0x4 struct cmdq_shared *shared; struct cmd_find_state source; struct cmd_find_state target; struct cmd_list *cmdlist; struct cmd *cmd; cmdq_cb cb; void *data; TAILQ_ENTRY(cmdq_item) entry; }; TAILQ_HEAD(cmdq_list, cmdq_item); /* Command definition flag. */ struct cmd_entry_flag { char flag; enum cmd_find_type type; int flags; }; /* Command definition. */ struct cmd_entry { const char *name; const char *alias; struct { const char *template; int lower; int upper; } args; const char *usage; struct cmd_entry_flag source; struct cmd_entry_flag target; #define CMD_STARTSERVER 0x1 #define CMD_READONLY 0x2 #define CMD_AFTERHOOK 0x4 int flags; enum cmd_retval (*exec)(struct cmd *, struct cmdq_item *); }; /* Status line. */ #define STATUS_LINES_LIMIT 5 struct status_line_entry { char *expanded; struct style_ranges ranges; }; struct status_line { struct event timer; struct screen screen; struct screen *active; int references; struct grid_cell style; struct status_line_entry entries[STATUS_LINES_LIMIT]; }; /* Client connection. */ typedef int (*prompt_input_cb)(struct client *, void *, const char *, int); typedef void (*prompt_free_cb)(void *); typedef void (*overlay_draw_cb)(struct client *, struct screen_redraw_ctx *); typedef int (*overlay_key_cb)(struct client *, struct key_event *); typedef void (*overlay_free_cb)(struct client *); struct client { const char *name; struct tmuxpeer *peer; struct cmdq_list queue; pid_t pid; int fd; struct event event; int retval; struct timeval creation_time; struct timeval activity_time; struct environ *environ; struct format_job_tree *jobs; char *title; const char *cwd; char *term; char *ttyname; struct tty tty; size_t written; size_t discarded; size_t redraw; void (*stdin_callback)(struct client *, int, void *); void *stdin_callback_data; struct evbuffer *stdin_data; int stdin_closed; struct evbuffer *stdout_data; struct evbuffer *stderr_data; struct event repeat_timer; struct event click_timer; u_int click_button; struct status_line status; #define CLIENT_TERMINAL 0x1 #define CLIENT_LOGIN 0x2 #define CLIENT_EXIT 0x4 #define CLIENT_REDRAWWINDOW 0x8 #define CLIENT_REDRAWSTATUS 0x10 #define CLIENT_REPEAT 0x20 #define CLIENT_SUSPENDED 0x40 #define CLIENT_ATTACHED 0x80 #define CLIENT_EXITED 0x100 #define CLIENT_DEAD 0x200 #define CLIENT_REDRAWBORDERS 0x400 #define CLIENT_READONLY 0x800 #define CLIENT_DETACHING 0x1000 #define CLIENT_CONTROL 0x2000 #define CLIENT_CONTROLCONTROL 0x4000 #define CLIENT_FOCUSED 0x8000 #define CLIENT_UTF8 0x10000 #define CLIENT_256COLOURS 0x20000 #define CLIENT_IDENTIFIED 0x40000 #define CLIENT_STATUSFORCE 0x80000 #define CLIENT_DOUBLECLICK 0x100000 #define CLIENT_TRIPLECLICK 0x200000 #define CLIENT_SIZECHANGED 0x400000 #define CLIENT_STATUSOFF 0x800000 #define CLIENT_REDRAWSTATUSALWAYS 0x1000000 #define CLIENT_REDRAWOVERLAY 0x2000000 #define CLIENT_CONTROL_NOOUTPUT 0x4000000 #define CLIENT_ALLREDRAWFLAGS \ (CLIENT_REDRAWWINDOW| \ CLIENT_REDRAWSTATUS| \ CLIENT_REDRAWSTATUSALWAYS| \ CLIENT_REDRAWBORDERS| \ CLIENT_REDRAWOVERLAY) #define CLIENT_NOSIZEFLAGS \ (CLIENT_DEAD| \ CLIENT_SUSPENDED| \ CLIENT_DETACHING) int flags; struct key_table *keytable; char *message_string; struct event message_timer; u_int message_next; TAILQ_HEAD(, message_entry) message_log; char *prompt_string; struct utf8_data *prompt_buffer; size_t prompt_index; prompt_input_cb prompt_inputcb; prompt_free_cb prompt_freecb; void *prompt_data; u_int prompt_hindex; enum { PROMPT_ENTRY, PROMPT_COMMAND } prompt_mode; struct utf8_data *prompt_saved; #define PROMPT_SINGLE 0x1 #define PROMPT_NUMERIC 0x2 #define PROMPT_INCREMENTAL 0x4 #define PROMPT_NOFORMAT 0x8 int prompt_flags; struct session *session; struct session *last_session; int references; void *pan_window; u_int pan_ox; u_int pan_oy; overlay_draw_cb overlay_draw; overlay_key_cb overlay_key; overlay_free_cb overlay_free; void *overlay_data; struct event overlay_timer; TAILQ_ENTRY(client) entry; }; TAILQ_HEAD(clients, client); /* Key binding and key table. */ struct key_binding { key_code key; struct cmd_list *cmdlist; int flags; #define KEY_BINDING_REPEAT 0x1 RB_ENTRY(key_binding) entry; }; RB_HEAD(key_bindings, key_binding); struct key_table { const char *name; struct key_bindings key_bindings; u_int references; RB_ENTRY(key_table) entry; }; RB_HEAD(key_tables, key_table); /* Option data. */ RB_HEAD(options_array, options_array_item); union options_value { char *string; long long number; struct style style; struct options_array array; struct cmd_list *cmdlist; }; /* Option table entries. */ enum options_table_type { OPTIONS_TABLE_STRING, OPTIONS_TABLE_NUMBER, OPTIONS_TABLE_KEY, OPTIONS_TABLE_COLOUR, OPTIONS_TABLE_FLAG, OPTIONS_TABLE_CHOICE, OPTIONS_TABLE_STYLE, OPTIONS_TABLE_COMMAND }; #define OPTIONS_TABLE_NONE 0 #define OPTIONS_TABLE_SERVER 0x1 #define OPTIONS_TABLE_SESSION 0x2 #define OPTIONS_TABLE_WINDOW 0x4 #define OPTIONS_TABLE_PANE 0x8 #define OPTIONS_TABLE_IS_ARRAY 0x1 #define OPTIONS_TABLE_IS_HOOK 0x2 struct options_table_entry { const char *name; enum options_table_type type; int scope; int flags; u_int minimum; u_int maximum; const char **choices; const char *default_str; long long default_num; const char **default_arr; const char *separator; const char *pattern; }; /* 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-name]" /* Spawn common context. */ struct spawn_context { struct cmdq_item *item; struct session *s; struct winlink *wl; struct window_pane *wp0; struct layout_cell *lc; const char *name; char **argv; int argc; struct environ *environ; int idx; const char *cwd; int flags; #define SPAWN_KILL 0x1 #define SPAWN_DETACHED 0x2 #define SPAWN_RESPAWN 0x4 #define SPAWN_BEFORE 0x8 #define SPAWN_NONOTIFY 0x10 #define SPAWN_FULLSIZE 0x20 #define SPAWN_EMPTY 0x40 }; /* 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 timeval start_time; extern const char *socket_path; extern const char *shell_command; extern int ptm_fd; extern const char *shell_command; int areshell(const char *); void setblocking(int, int); const char *find_cwd(void); const char *find_home(void); /* proc.c */ struct imsg; int proc_send(struct tmuxpeer *, enum msgtype, int, const void *, size_t); struct tmuxproc *proc_start(const char *); void proc_loop(struct tmuxproc *, int (*)(void)); void proc_exit(struct tmuxproc *); void proc_set_signals(struct tmuxproc *, void(*)(int)); void proc_clear_signals(struct tmuxproc *, int); struct tmuxpeer *proc_add_peer(struct tmuxproc *, int, void (*)(struct imsg *, void *), void *); void proc_remove_peer(struct tmuxpeer *); void proc_kill_peer(struct tmuxpeer *); void proc_toggle_log(struct tmuxproc *); /* cfg.c */ extern int cfg_finished; extern struct client *cfg_client; void start_cfg(void); int load_cfg(const char *, struct client *, struct cmdq_item *, int, struct cmdq_item **); void set_cfg_file(const char *); void printflike(1, 2) cfg_add_cause(const char *, ...); void cfg_print_causes(struct cmdq_item *); void cfg_show_causes(struct session *); /* paste.c */ struct paste_buffer; const char *paste_buffer_name(struct paste_buffer *); u_int paste_buffer_order(struct paste_buffer *); time_t paste_buffer_created(struct paste_buffer *); const char *paste_buffer_data(struct paste_buffer *, size_t *); struct paste_buffer *paste_walk(struct paste_buffer *); struct paste_buffer *paste_get_top(const char **); struct paste_buffer *paste_get_name(const char *); void paste_free(struct paste_buffer *); void paste_add(const char *, char *, size_t); int paste_rename(const char *, const char *, char **); int paste_set(char *, size_t, const char *, char **); char *paste_make_sample(struct paste_buffer *); /* format.c */ #define FORMAT_STATUS 0x1 #define FORMAT_FORCE 0x2 #define FORMAT_NOJOBS 0x4 #define FORMAT_VERBOSE 0x8 #define FORMAT_NONE 0 #define FORMAT_PANE 0x80000000U #define FORMAT_WINDOW 0x40000000U struct format_tree; const char *format_skip(const char *, const char *); int format_true(const char *); struct format_tree *format_create(struct client *, struct cmdq_item *, int, int); void format_free(struct format_tree *); void printflike(3, 4) format_add(struct format_tree *, const char *, const char *, ...); void format_each(struct format_tree *, void (*)(const char *, const char *, void *), void *); char *format_expand_time(struct format_tree *, const char *); char *format_expand(struct format_tree *, const char *); char *format_single(struct cmdq_item *, const char *, struct client *, struct session *, struct winlink *, struct window_pane *); void format_defaults(struct format_tree *, struct client *, struct session *, struct winlink *, struct window_pane *); void format_defaults_window(struct format_tree *, struct window *); void format_defaults_pane(struct format_tree *, struct window_pane *); void format_defaults_paste_buffer(struct format_tree *, struct paste_buffer *); void format_lost_client(struct client *); /* format-draw.c */ void format_draw(struct screen_write_ctx *, const struct grid_cell *, u_int, const char *, struct style_ranges *); u_int format_width(const char *); char *format_trim_left(const char *, u_int); char *format_trim_right(const char *, u_int); /* notify.c */ void notify_hook(struct cmdq_item *, const char *); void notify_input(struct window_pane *, const u_char *, size_t); void notify_client(const char *, struct client *); void notify_session(const char *, struct session *); void notify_winlink(const char *, struct winlink *); void notify_session_window(const char *, struct session *, struct window *); void notify_window(const char *, struct window *); void notify_pane(const char *, struct window_pane *); /* options.c */ struct options *options_create(struct options *); void options_free(struct options *); void options_set_parent(struct options *, struct options *); struct options_entry *options_first(struct options *); struct options_entry *options_next(struct options_entry *); struct options_entry *options_empty(struct options *, const struct options_table_entry *); struct options_entry *options_default(struct options *, const struct options_table_entry *); const char *options_name(struct options_entry *); const struct options_table_entry *options_table_entry(struct options_entry *); struct options_entry *options_get_only(struct options *, const char *); struct options_entry *options_get(struct options *, const char *); void options_remove(struct options_entry *); void options_array_clear(struct options_entry *); union options_value *options_array_get(struct options_entry *, u_int); int options_array_set(struct options_entry *, u_int, const char *, int, char **); int options_array_assign(struct options_entry *, const char *, char **); struct options_array_item *options_array_first(struct options_entry *); struct options_array_item *options_array_next(struct options_array_item *); u_int options_array_item_index(struct options_array_item *); union options_value *options_array_item_value(struct options_array_item *); int options_isarray(struct options_entry *); int options_isstring(struct options_entry *); char *options_tostring(struct options_entry *, int, int); char *options_parse(const char *, int *); struct options_entry *options_parse_get(struct options *, const char *, int *, int); char *options_match(const char *, int *, int *); struct options_entry *options_match_get(struct options *, const char *, int *, int, int *); const char *options_get_string(struct options *, const char *); long long options_get_number(struct options *, const char *); struct style *options_get_style(struct options *, const char *); struct options_entry * printflike(4, 5) options_set_string(struct options *, const char *, int, const char *, ...); struct options_entry *options_set_number(struct options *, const char *, long long); struct options_entry *options_set_style(struct options *, const char *, int, const char *); int options_scope_from_name(struct args *, int, const char *, struct cmd_find_state *, struct options **, char **); int options_scope_from_flags(struct args *, int, struct cmd_find_state *, struct options **, char **); /* options-table.c */ extern const struct options_table_entry options_table[]; /* job.c */ typedef void (*job_update_cb) (struct job *); typedef void (*job_complete_cb) (struct job *); typedef void (*job_free_cb) (void *); #define JOB_NOWAIT 0x1 struct job *job_run(const char *, struct session *, const char *, job_update_cb, job_complete_cb, job_free_cb, void *, int); void job_free(struct job *); void job_check_died(pid_t, int); int job_get_status(struct job *); void *job_get_data(struct job *); struct bufferevent *job_get_event(struct job *); void job_kill_all(void); int job_still_running(void); void job_print_summary(struct cmdq_item *, int); /* environ.c */ struct environ *environ_create(void); void environ_free(struct environ *); struct environ_entry *environ_first(struct environ *); struct environ_entry *environ_next(struct environ_entry *); void environ_copy(struct environ *, struct environ *); struct environ_entry *environ_find(struct environ *, const char *); void printflike(3, 4) environ_set(struct environ *, const char *, const char *, ...); void environ_clear(struct environ *, const char *); void environ_put(struct environ *, const char *); void environ_unset(struct environ *, const char *); void environ_update(struct options *, struct environ *, struct environ *); void environ_push(struct environ *); void printflike(2, 3) environ_log(struct environ *, const char *, ...); struct environ *environ_for_session(struct session *, int); /* tty.c */ void tty_create_log(void); int tty_window_bigger(struct tty *); int tty_window_offset(struct tty *, u_int *, u_int *, u_int *, u_int *); void tty_update_window_offset(struct window *); void tty_update_client_offset(struct client *); void tty_raw(struct tty *, const char *); void tty_attributes(struct tty *, const struct grid_cell *, struct window_pane *); void tty_reset(struct tty *); void tty_region_off(struct tty *); void tty_margin_off(struct tty *); 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_putcode3(struct tty *, enum tty_code_code, int, 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_putn(struct tty *, const void *, size_t, u_int); int tty_init(struct tty *, struct client *, int, char *); void tty_resize(struct tty *); void tty_set_size(struct tty *, u_int, u_int); 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_draw_line(struct tty *, struct window_pane *, struct screen *, u_int, u_int, u_int, u_int, u_int); int tty_open(struct tty *, char **); void tty_close(struct tty *); void tty_free(struct tty *); void tty_set_type(struct tty *, int); void tty_write(void (*)(struct tty *, const struct tty_ctx *), 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_cells(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_clearcharacter(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_scrollup(struct tty *, const struct tty_ctx *); void tty_cmd_scrolldown(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; u_int tty_term_ncodes(void); struct tty_term *tty_term_find(char *, int, char **); void tty_term_free(struct tty_term *); int tty_term_has(struct tty_term *, enum tty_code_code); const char *tty_term_string(struct tty_term *, enum tty_code_code); const char *tty_term_string1(struct tty_term *, enum tty_code_code, int); const char *tty_term_string2(struct tty_term *, enum tty_code_code, int, int); const char *tty_term_string3(struct tty_term *, enum tty_code_code, int, 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); const char *tty_term_describe(struct tty_term *, enum tty_code_code); /* tty-acs.c */ int tty_acs_needed(struct tty *); const char *tty_acs_get(struct tty *, u_char); /* tty-keys.c */ void tty_keys_build(struct tty *); void tty_keys_free(struct tty *); int tty_keys_next(struct tty *); /* arguments.c */ void args_set(struct args *, u_char, const char *); struct args *args_parse(const char *, int, char **); void args_free(struct args *); char *args_print(struct args *); char *args_escape(const char *); int args_has(struct args *, u_char); const char *args_get(struct args *, u_char); const char *args_first_value(struct args *, u_char, struct args_value **); const char *args_next_value(struct args_value **); long long args_strtonum(struct args *, u_char, long long, long long, char **); /* cmd-find.c */ int cmd_find_target(struct cmd_find_state *, struct cmdq_item *, const char *, enum cmd_find_type, int); struct client *cmd_find_best_client(struct session *); struct client *cmd_find_client(struct cmdq_item *, const char *, int); void cmd_find_clear_state(struct cmd_find_state *, int); int cmd_find_empty_state(struct cmd_find_state *); int cmd_find_valid_state(struct cmd_find_state *); void cmd_find_copy_state(struct cmd_find_state *, struct cmd_find_state *); void cmd_find_from_session(struct cmd_find_state *, struct session *, int); void cmd_find_from_winlink(struct cmd_find_state *, struct winlink *, int); int cmd_find_from_session_window(struct cmd_find_state *, struct session *, struct window *, int); int cmd_find_from_window(struct cmd_find_state *, struct window *, int); void cmd_find_from_winlink_pane(struct cmd_find_state *, struct winlink *, struct window_pane *, int); int cmd_find_from_pane(struct cmd_find_state *, struct window_pane *, int); int cmd_find_from_client(struct cmd_find_state *, struct client *, int); int cmd_find_from_mouse(struct cmd_find_state *, struct mouse_event *, int); int cmd_find_from_nothing(struct cmd_find_state *, int); /* cmd.c */ void printflike(3, 4) cmd_log_argv(int, char **, const char *, ...); void cmd_prepend_argv(int *, char ***, char *); void cmd_append_argv(int *, char ***, char *); int cmd_pack_argv(int, char **, char *, size_t); int cmd_unpack_argv(char *, size_t, int, char ***); char **cmd_copy_argv(int, char **); void cmd_free_argv(int, char **); char *cmd_stringify_argv(int, char **); char *cmd_get_alias(const char *); struct cmd *cmd_parse(int, char **, const char *, u_int, char **); void cmd_free(struct cmd *); char *cmd_print(struct cmd *); int cmd_mouse_at(struct window_pane *, struct mouse_event *, u_int *, u_int *, int); struct winlink *cmd_mouse_window(struct mouse_event *, struct session **); struct window_pane *cmd_mouse_pane(struct mouse_event *, struct session **, struct winlink **); char *cmd_template_replace(const char *, const char *, int); extern const struct cmd_entry *cmd_table[]; /* cmd-attach-session.c */ enum cmd_retval cmd_attach_session(struct cmdq_item *, const char *, int, int, int, const char *, int); /* cmd-parse.c */ void cmd_parse_empty(struct cmd_parse_input *); struct cmd_parse_result *cmd_parse_from_file(FILE *, struct cmd_parse_input *); struct cmd_parse_result *cmd_parse_from_string(const char *, struct cmd_parse_input *); struct cmd_parse_result *cmd_parse_from_arguments(int, char **, struct cmd_parse_input *); /* cmd-list.c */ struct cmd_list *cmd_list_new(void); void cmd_list_append(struct cmd_list *, struct cmd *); void cmd_list_move(struct cmd_list *, struct cmd_list *); void cmd_list_free(struct cmd_list *); char *cmd_list_print(struct cmd_list *, int); /* cmd-queue.c */ struct cmdq_item *cmdq_get_command(struct cmd_list *, struct cmd_find_state *, struct mouse_event *, int); #define cmdq_get_callback(cb, data) cmdq_get_callback1(#cb, cb, data) struct cmdq_item *cmdq_get_callback1(const char *, cmdq_cb, void *); struct cmdq_item *cmdq_get_error(const char *); void cmdq_insert_after(struct cmdq_item *, struct cmdq_item *); void cmdq_append(struct client *, struct cmdq_item *); void cmdq_insert_hook(struct session *, struct cmdq_item *, struct cmd_find_state *, const char *, ...); void cmdq_continue(struct cmdq_item *); void printflike(3, 4) cmdq_format(struct cmdq_item *, const char *, const char *, ...); u_int cmdq_next(struct client *); void cmdq_guard(struct cmdq_item *, const char *, int); void printflike(2, 3) cmdq_print(struct cmdq_item *, const char *, ...); void printflike(2, 3) cmdq_error(struct cmdq_item *, const char *, ...); /* cmd-wait-for.c */ void cmd_wait_for_flush(void); /* client.c */ int client_main(struct event_base *, int, char **, int); /* key-bindings.c */ struct key_table *key_bindings_get_table(const char *, int); struct key_table *key_bindings_first_table(void); struct key_table *key_bindings_next_table(struct key_table *); void key_bindings_unref_table(struct key_table *); struct key_binding *key_bindings_get(struct key_table *, key_code); struct key_binding *key_bindings_first(struct key_table *); struct key_binding *key_bindings_next(struct key_table *, struct key_binding *); void key_bindings_add(const char *, key_code, int, struct cmd_list *); void key_bindings_remove(const char *, key_code); void key_bindings_remove_table(const char *); void key_bindings_init(void); struct cmdq_item *key_bindings_dispatch(struct key_binding *, struct cmdq_item *, struct client *, struct mouse_event *, struct cmd_find_state *); /* key-string.c */ key_code key_string_lookup_string(const char *); const char *key_string_lookup_key(key_code); /* alerts.c */ void alerts_reset_all(void); void alerts_queue(struct window *, int); void alerts_check_session(struct session *); /* server.c */ extern struct tmuxproc *server_proc; extern struct clients clients; extern struct cmd_find_state marked_pane; void server_set_marked(struct session *, struct winlink *, struct window_pane *); void server_clear_marked(void); int server_is_marked(struct session *, struct winlink *, struct window_pane *); int server_check_marked(void); int server_start(struct tmuxproc *, struct event_base *, int, char *); void server_update_socket(void); void server_add_accept(int); /* server-client.c */ u_int server_client_how_many(void); void server_client_set_overlay(struct client *, u_int, overlay_draw_cb, overlay_key_cb, overlay_free_cb, void *); void server_client_set_key_table(struct client *, const char *); const char *server_client_get_key_table(struct client *); int server_client_check_nested(struct client *); int server_client_handle_key(struct client *, struct key_event *); struct client *server_client_create(int); int server_client_open(struct client *, char **); void server_client_unref(struct client *); void server_client_lost(struct client *); void server_client_suspend(struct client *); void server_client_detach(struct client *, enum msgtype); void server_client_exec(struct client *, const char *); void server_client_loop(void); void server_client_push_stdout(struct client *); void server_client_push_stderr(struct client *); void printflike(2, 3) server_client_add_message(struct client *, const char *, ...); char *server_client_get_path(struct client *, const char *); const char *server_client_get_cwd(struct client *, struct session *); /* server-fn.c */ 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 *); void server_kill_pane(struct window_pane *); 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 *, int); void server_destroy_session(struct session *); void server_check_unattached(void); int server_set_stdin_callback(struct client *, void (*)(struct client *, int, void *), void *, char **); void server_unzoom_window(struct window *); /* status.c */ void status_timer_start(struct client *); void status_timer_start_all(void); void status_update_cache(struct session *); int status_at_line(struct client *); u_int status_line_size(struct client *); struct style_range *status_get_range(struct client *, u_int, u_int); void status_init(struct client *); void status_free(struct client *); int status_redraw(struct client *); void printflike(2, 3) 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 *, prompt_input_cb, prompt_free_cb, void *, int); void status_prompt_clear(struct client *); int status_prompt_redraw(struct client *); int status_prompt_key(struct client *, key_code); void status_prompt_update(struct client *, const char *, const char *); void status_prompt_load_history(void); void status_prompt_save_history(void); /* resize.c */ void resize_window(struct window *, u_int, u_int); void default_window_size(struct session *, struct window *, u_int *, u_int *, int); void recalculate_sizes(void); /* input.c */ void input_init(struct window_pane *); void input_free(struct window_pane *); void input_reset(struct window_pane *, int); struct evbuffer *input_pending(struct window_pane *); void input_parse(struct window_pane *); void input_parse_buffer(struct window_pane *, u_char *, size_t); /* input-key.c */ void input_key(struct window_pane *, key_code, struct mouse_event *); /* xterm-keys.c */ char *xterm_keys_lookup(key_code); int xterm_keys_find(const char *, size_t, size_t *, key_code *); /* colour.c */ int colour_find_rgb(u_char, u_char, u_char); int colour_join_rgb(u_char, u_char, u_char); void colour_split_rgb(int, u_char *, u_char *, u_char *); const char *colour_tostring(int); int colour_fromstring(const char *s); int colour_256toRGB(int); int colour_256to16(int); /* attributes.c */ const char *attributes_tostring(int); int attributes_fromstring(const char *); /* grid.c */ extern const struct grid_cell grid_default_cell; int grid_cells_equal(const struct grid_cell *, const struct grid_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 *, u_int); void grid_scroll_history_region(struct grid *, u_int, u_int, u_int); void grid_clear_history(struct grid *); const struct grid_line *grid_peek_line(struct grid *, u_int); void grid_get_cell(struct grid *, u_int, u_int, struct grid_cell *); void grid_set_cell(struct grid *, u_int, u_int, const struct grid_cell *); void grid_set_cells(struct grid *, u_int, u_int, const struct grid_cell *, const char *, size_t); struct grid_line *grid_get_line(struct grid *, u_int); void grid_adjust_lines(struct grid *, u_int); void grid_clear(struct grid *, u_int, u_int, u_int, u_int, u_int); void grid_clear_lines(struct grid *, u_int, u_int, u_int); void grid_move_lines(struct grid *, u_int, u_int, u_int, u_int); void grid_move_cells(struct grid *, u_int, u_int, u_int, u_int, u_int); char *grid_string_cells(struct grid *, u_int, u_int, u_int, struct grid_cell **, int, int, int); void grid_duplicate_lines(struct grid *, u_int, struct grid *, u_int, u_int); void grid_reflow(struct grid *, u_int); void grid_wrap_position(struct grid *, u_int, u_int, u_int *, u_int *); void grid_unwrap_position(struct grid *, u_int *, u_int *, u_int, u_int); u_int grid_line_length(struct grid *, u_int); /* grid-view.c */ void grid_view_get_cell(struct grid *, u_int, u_int, struct grid_cell *); void grid_view_set_cell(struct grid *, u_int, u_int, const struct grid_cell *); void grid_view_set_cells(struct grid *, u_int, u_int, const struct grid_cell *, const char *, size_t); void grid_view_clear_history(struct grid *, u_int); void grid_view_clear(struct grid *, u_int, u_int, u_int, u_int, u_int); void grid_view_scroll_region_up(struct grid *, u_int, u_int, u_int); void grid_view_scroll_region_down(struct grid *, u_int, u_int, u_int); void grid_view_insert_lines(struct grid *, u_int, u_int, u_int); void grid_view_insert_lines_region(struct grid *, u_int, u_int, u_int, u_int); void grid_view_delete_lines(struct grid *, u_int, u_int, u_int); void grid_view_delete_lines_region(struct grid *, u_int, u_int, u_int, u_int); void grid_view_insert_cells(struct grid *, u_int, u_int, u_int, u_int); void grid_view_delete_cells(struct grid *, u_int, 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 *); void screen_write_reset(struct screen_write_ctx *); size_t printflike(1, 2) screen_write_strlen(const char *, ...); void printflike(3, 4) screen_write_puts(struct screen_write_ctx *, const struct grid_cell *, const char *, ...); void printflike(4, 5) screen_write_nputs(struct screen_write_ctx *, ssize_t, const struct grid_cell *, const char *, ...); void screen_write_vnputs(struct screen_write_ctx *, ssize_t, const struct grid_cell *, const char *, va_list); void screen_write_putc(struct screen_write_ctx *, const struct grid_cell *, u_char); void screen_write_copy(struct screen_write_ctx *, struct screen *, u_int, u_int, u_int, u_int, bitstr_t *, const struct grid_cell *); void screen_write_fast_copy(struct screen_write_ctx *, struct screen *, u_int, u_int, u_int, u_int); void screen_write_hline(struct screen_write_ctx *, u_int, int, int); void screen_write_vline(struct screen_write_ctx *, u_int, int, int); void screen_write_menu(struct screen_write_ctx *, struct menu *, int); void screen_write_box(struct screen_write_ctx *, u_int, u_int); void screen_write_preview(struct screen_write_ctx *, struct screen *, u_int, u_int); void screen_write_backspace(struct screen_write_ctx *); void screen_write_mode_set(struct screen_write_ctx *, int); void screen_write_mode_clear(struct screen_write_ctx *, int); void screen_write_cursorup(struct screen_write_ctx *, u_int); void screen_write_cursordown(struct screen_write_ctx *, u_int); void screen_write_cursorright(struct screen_write_ctx *, u_int); void screen_write_cursorleft(struct screen_write_ctx *, u_int); void screen_write_alignmenttest(struct screen_write_ctx *); void screen_write_insertcharacter(struct screen_write_ctx *, u_int, u_int); void screen_write_deletecharacter(struct screen_write_ctx *, u_int, u_int); void screen_write_clearcharacter(struct screen_write_ctx *, u_int, u_int); void screen_write_insertline(struct screen_write_ctx *, u_int, u_int); void screen_write_deleteline(struct screen_write_ctx *, u_int, u_int); void screen_write_clearline(struct screen_write_ctx *, u_int); void screen_write_clearendofline(struct screen_write_ctx *, u_int); void screen_write_clearstartofline(struct screen_write_ctx *, u_int); void screen_write_cursormove(struct screen_write_ctx *, int, int, int); void screen_write_reverseindex(struct screen_write_ctx *, u_int); void screen_write_scrollregion(struct screen_write_ctx *, u_int, u_int); void screen_write_linefeed(struct screen_write_ctx *, int, u_int); void screen_write_scrollup(struct screen_write_ctx *, u_int, u_int); void screen_write_scrolldown(struct screen_write_ctx *, u_int, u_int); void screen_write_carriagereturn(struct screen_write_ctx *); void screen_write_clearendofscreen(struct screen_write_ctx *, u_int); void screen_write_clearstartofscreen(struct screen_write_ctx *, u_int); void screen_write_clearscreen(struct screen_write_ctx *, u_int); void screen_write_clearhistory(struct screen_write_ctx *); void screen_write_collect_end(struct screen_write_ctx *); void screen_write_collect_add(struct screen_write_ctx *, const struct grid_cell *); void screen_write_cell(struct screen_write_ctx *, const struct grid_cell *); 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 *); 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_push_title(struct screen *); void screen_pop_title(struct screen *); void screen_resize(struct screen *, u_int, u_int, int); void screen_set_selection(struct screen *, u_int, u_int, u_int, u_int, u_int, int, struct grid_cell *); void screen_clear_selection(struct screen *); void screen_hide_selection(struct screen *); int screen_check_selection(struct screen *, u_int, u_int); void screen_select_cell(struct screen *, struct grid_cell *, const struct grid_cell *); /* window.c */ extern struct windows windows; extern struct window_pane_tree all_window_panes; extern const struct window_mode *all_window_modes[]; int window_cmp(struct window *, struct window *); RB_PROTOTYPE(windows, window, entry, window_cmp); 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 *); struct winlink *winlink_find_by_window_id(struct winlinks *, u_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 *); struct window *window_find_by_id_str(const char *); struct window *window_find_by_id(u_int); void window_update_activity(struct window *); struct window *window_create(u_int, u_int); void window_pane_set_event(struct window_pane *); struct window_pane *window_get_active_at(struct window *, u_int, u_int); struct window_pane *window_find_string(struct window *, const char *); int window_has_pane(struct window *, struct window_pane *); int window_set_active_pane(struct window *, struct window_pane *, int); void window_redraw_active_switch(struct window *, struct window_pane *); struct window_pane *window_add_pane(struct window *, struct window_pane *, u_int, int); void window_resize(struct window *, u_int, u_int); int window_zoom(struct window_pane *); int window_unzoom(struct window *); void window_lost_pane(struct window *, struct window_pane *); 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); int window_pane_index(struct window_pane *, u_int *); u_int window_count_panes(struct window *); void window_destroy_panes(struct window *); struct window_pane *window_pane_find_by_id_str(const char *); struct window_pane *window_pane_find_by_id(u_int); int window_pane_destroy_ready(struct window_pane *); void window_pane_resize(struct window_pane *, u_int, u_int); void window_pane_alternate_on(struct window_pane *, struct grid_cell *, int); void window_pane_alternate_off(struct window_pane *, struct grid_cell *, int); void window_pane_set_palette(struct window_pane *, u_int, int); void window_pane_unset_palette(struct window_pane *, u_int); void window_pane_reset_palette(struct window_pane *); int window_pane_get_palette(struct window_pane *, int); int window_pane_set_mode(struct window_pane *, const struct window_mode *, struct cmd_find_state *, struct args *); void window_pane_reset_mode(struct window_pane *); void window_pane_reset_mode_all(struct window_pane *); void window_pane_key(struct window_pane *, struct client *, struct session *, struct winlink *, key_code, struct mouse_event *); int window_pane_visible(struct window_pane *); u_int window_pane_search(struct window_pane *, const char *, int, int); const char *window_printable_flags(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 *); void window_set_name(struct window *, const char *); void window_add_ref(struct window *, const char *); void window_remove_ref(struct window *, const char *); void winlink_clear_flags(struct winlink *); int winlink_shuffle_up(struct session *, struct winlink *); int window_pane_start_input(struct window_pane *, struct cmdq_item *, char **); /* 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 window *, struct layout_cell *, struct layout_cell **); void layout_resize_layout(struct window *, struct layout_cell *, enum layout_type, int, int); struct layout_cell *layout_search_by_border(struct layout_cell *, u_int, u_int); 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 window *); void layout_fix_panes(struct window *); void layout_resize_adjust(struct window *, struct layout_cell *, enum layout_type, int); void layout_init(struct window *, struct window_pane *); 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, int); void layout_resize_pane_to(struct window_pane *, enum layout_type, u_int); void layout_assign_pane(struct layout_cell *, struct window_pane *); struct layout_cell *layout_split_pane(struct window_pane *, enum layout_type, int, int); void layout_close_pane(struct window_pane *); int layout_spread_cell(struct window *, struct layout_cell *); void layout_spread_out(struct window_pane *); /* layout-custom.c */ char *layout_dump(struct layout_cell *); int layout_parse(struct window *, const char *); /* layout-set.c */ 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 *); /* mode-tree.c */ typedef void (*mode_tree_build_cb)(void *, u_int, uint64_t *, const char *); typedef void (*mode_tree_draw_cb)(void *, void *, struct screen_write_ctx *, u_int, u_int); typedef int (*mode_tree_search_cb)(void *, void *, const char *); typedef void (*mode_tree_menu_cb)(void *, struct client *, key_code); typedef void (*mode_tree_each_cb)(void *, void *, struct client *, key_code); u_int mode_tree_count_tagged(struct mode_tree_data *); void *mode_tree_get_current(struct mode_tree_data *); void mode_tree_expand_current(struct mode_tree_data *); void mode_tree_set_current(struct mode_tree_data *, uint64_t); void mode_tree_each_tagged(struct mode_tree_data *, mode_tree_each_cb, struct client *, key_code, int); void mode_tree_down(struct mode_tree_data *, int); struct mode_tree_data *mode_tree_start(struct window_pane *, struct args *, mode_tree_build_cb, mode_tree_draw_cb, mode_tree_search_cb, mode_tree_menu_cb, void *, const struct menu_item *, const char **, u_int, struct screen **); void mode_tree_zoom(struct mode_tree_data *, struct args *); void mode_tree_build(struct mode_tree_data *); void mode_tree_free(struct mode_tree_data *); void mode_tree_resize(struct mode_tree_data *, u_int, u_int); struct mode_tree_item *mode_tree_add(struct mode_tree_data *, struct mode_tree_item *, void *, uint64_t, const char *, const char *, int); void mode_tree_remove(struct mode_tree_data *, struct mode_tree_item *); void mode_tree_draw(struct mode_tree_data *); int mode_tree_key(struct mode_tree_data *, struct client *, key_code *, struct mouse_event *, u_int *, u_int *); void mode_tree_run_command(struct client *, struct cmd_find_state *, const char *, const char *); /* window-buffer.c */ extern const struct window_mode window_buffer_mode; /* window-tree.c */ extern const struct window_mode window_tree_mode; /* window-clock.c */ extern const struct window_mode window_clock_mode; extern const char window_clock_table[14][5][5]; /* window-client.c */ extern const struct window_mode window_client_mode; /* window-copy.c */ extern const struct window_mode window_copy_mode; extern const struct window_mode window_view_mode; void printflike(2, 3) 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 *, int); void window_copy_start_drag(struct client *, struct mouse_event *); /* names.c */ void check_window_name(struct window *); char *default_window_name(struct window *); char *parse_window_name(const char *); /* control.c */ void control_callback(struct client *, int, void *); void printflike(2, 3) control_write(struct client *, const char *, ...); void control_write_buffer(struct client *, struct evbuffer *); /* control-notify.c */ void control_notify_input(struct client *, struct window_pane *, const u_char *, size_t); void control_notify_pane_mode_changed(int); void control_notify_window_layout_changed(struct window *); void control_notify_window_pane_changed(struct window *); void control_notify_window_unlinked(struct session *, struct window *); void control_notify_window_linked(struct session *, struct window *); void control_notify_window_renamed(struct window *); void control_notify_client_session_changed(struct client *); void control_notify_session_renamed(struct session *); void control_notify_session_created(struct session *); void control_notify_session_closed(struct session *); void control_notify_session_window_changed(struct session *); /* session.c */ extern struct sessions sessions; 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_id_str(const char *); struct session *session_find_by_id(u_int); struct session *session_create(const char *, const char *, const char *, struct environ *, struct options *, struct termios *); void session_destroy(struct session *, int, const char *); void session_add_ref(struct session *, const char *); void session_remove_ref(struct session *, const char *); int session_check_name(const char *); void session_update_activity(struct session *, struct timeval *); struct session *session_next_session(struct session *); struct session *session_previous_session(struct session *); struct winlink *session_new(struct session *, const char *, int, char **, const char *, const char *, int, char **); struct winlink *session_attach(struct session *, struct window *, int, char **); int session_detach(struct session *, struct winlink *); int session_has(struct session *, struct window *); int session_is_linked(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 *); int session_set_current(struct session *, struct winlink *); struct session_group *session_group_contains(struct session *); struct session_group *session_group_find(const char *); struct session_group *session_group_new(const char *); void session_group_add(struct session_group *, struct session *); void session_group_synchronize_to(struct session *); void session_group_synchronize_from(struct session *); u_int session_group_count(struct session_group *); void session_renumber_windows(struct session *); /* utf8.c */ void utf8_set(struct utf8_data *, u_char); void utf8_copy(struct utf8_data *, const struct utf8_data *); enum utf8_state utf8_open(struct utf8_data *, u_char); enum utf8_state utf8_append(struct utf8_data *, u_char); enum utf8_state utf8_combine(const struct utf8_data *, wchar_t *); enum utf8_state utf8_split(wchar_t, struct utf8_data *); int utf8_isvalid(const char *); int utf8_strvis(char *, const char *, size_t, int); int utf8_stravis(char **, const char *, int); char *utf8_sanitize(const char *); size_t utf8_strlen(const struct utf8_data *); u_int utf8_strwidth(const struct utf8_data *, ssize_t); struct utf8_data *utf8_fromcstr(const char *); char *utf8_tocstr(struct utf8_data *); u_int utf8_cstrwidth(const char *); char *utf8_padcstr(const char *, u_int); int utf8_cstrhas(const char *, const struct utf8_data *); /* osdep-*.c */ char *osdep_get_name(int, char *); char *osdep_get_cwd(int); struct event_base *osdep_event_init(void); /* log.c */ void log_add_level(void); int log_get_level(void); void log_open(const char *); void log_toggle(const char *); void log_close(void); void printflike(1, 2) log_debug(const char *, ...); __dead void printflike(1, 2) fatal(const char *, ...); __dead void printflike(1, 2) fatalx(const char *, ...); /* menu.c */ struct menu *menu_create(const char *); void menu_add_items(struct menu *, const struct menu_item *, struct cmdq_item *, struct client *, struct cmd_find_state *); void menu_add_item(struct menu *, const struct menu_item *, struct cmdq_item *, struct client *, struct cmd_find_state *); void menu_free(struct menu *); int menu_display(struct menu *, int, struct cmdq_item *, u_int, u_int, struct client *, struct cmd_find_state *, menu_choice_cb, void *); /* style.c */ int style_parse(struct style *,const struct grid_cell *, const char *); const char *style_tostring(struct style *); void style_apply(struct grid_cell *, struct options *, const char *); void style_apply_update(struct grid_cell *, struct options *, const char *); int style_equal(struct style *, struct style *); void style_set(struct style *, const struct grid_cell *); void style_copy(struct style *, struct style *); int style_is_default(struct style *); /* spawn.c */ struct winlink *spawn_window(struct spawn_context *, char **); struct window_pane *spawn_pane(struct spawn_context *, char **); /* regsub.c */ char *regsub(const char *, const char *, const char *, int); #endif /* TMUX_H */ tmux-3.0a/tty-acs.c100644 001750 001750 00000007662 13504653146 0007766/* $OpenBSD$ */ /* * 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" static 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; }; static const struct tty_acs_entry tty_acs_table[] = { { '+', "\342\206\222" }, /* arrow pointing right */ { ',', "\342\206\220" }, /* arrow pointing left */ { '-', "\342\206\221" }, /* arrow pointing up */ { '.', "\342\206\223" }, /* arrow pointing down */ { '0', "\342\226\256" }, /* solid square block */ { '`', "\342\227\206" }, /* diamond */ { 'a', "\342\226\222" }, /* checker board (stipple) */ { 'b', "\342\220\211" }, { 'c', "\342\220\214" }, { 'd', "\342\220\215" }, { 'e', "\342\220\212" }, { 'f', "\302\260" }, /* degree symbol */ { 'g', "\302\261" }, /* plus/minus */ { 'h', "\342\220\244" }, { 'i', "\342\220\213" }, { 'j', "\342\224\230" }, /* lower right corner */ { 'k', "\342\224\220" }, /* upper right corner */ { 'l', "\342\224\214" }, /* upper left corner */ { 'm', "\342\224\224" }, /* lower left corner */ { 'n', "\342\224\274" }, /* large plus or crossover */ { 'o', "\342\216\272" }, /* scan line 1 */ { 'p', "\342\216\273" }, /* scan line 3 */ { 'q', "\342\224\200" }, /* horizontal line */ { 'r', "\342\216\274" }, /* scan line 7 */ { 's', "\342\216\275" }, /* scan line 9 */ { 't', "\342\224\234" }, /* tee pointing right */ { 'u', "\342\224\244" }, /* tee pointing left */ { 'v', "\342\224\264" }, /* tee pointing up */ { 'w', "\342\224\254" }, /* tee pointing down */ { 'x', "\342\224\202" }, /* vertical line */ { 'y', "\342\211\244" }, /* less-than-or-equal-to */ { 'z', "\342\211\245" }, /* greater-than-or-equal-to */ { '{', "\317\200" }, /* greek pi */ { '|', "\342\211\240" }, /* not-equal */ { '}', "\302\243" }, /* UK pound sign */ { '~', "\302\267" } /* bullet */ }; static int tty_acs_cmp(const void *key, const void *value) { const struct tty_acs_entry *entry = value; u_char ch; ch = *(u_char *) key; return (ch - entry->key); } /* Should this terminal use ACS instead of UTF-8 line drawing? */ int tty_acs_needed(struct tty *tty) { if (tty == NULL) return (0); /* * If the U8 flag is present, it marks whether a terminal supports * UTF-8 and ACS together. * * If it is present and zero, we force ACS - this gives users a way to * turn off UTF-8 line drawing. * * If it is nonzero, we can fall through to the default and use UTF-8 * line drawing on UTF-8 terminals. */ if (tty_term_has(tty->term, TTYC_U8) && tty_term_number(tty->term, TTYC_U8) == 0) return (1); if (tty->flags & TTY_UTF8) return (0); return (1); } /* Retrieve ACS to output as a string. */ const char * tty_acs_get(struct tty *tty, u_char ch) { struct tty_acs_entry *entry; /* Use the ACS set instead of UTF-8 if needed. */ if (tty_acs_needed(tty)) { 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); } tmux-3.0a/tty-keys.c100644 001750 001750 00000070241 13570677043 0010171/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include "tmux.h" /* * Handle keys input from the outside terminal. tty_default_*_keys[] are a base * table of supported keys which are looked up in terminfo(5) and translated * into a ternary tree. */ static void tty_keys_add1(struct tty_key **, const char *, key_code); static void tty_keys_add(struct tty *, const char *, key_code); static void tty_keys_free1(struct tty_key *); static struct tty_key *tty_keys_find1(struct tty_key *, const char *, size_t, size_t *); static struct tty_key *tty_keys_find(struct tty *, const char *, size_t, size_t *); static int tty_keys_next1(struct tty *, const char *, size_t, key_code *, size_t *, int); static void tty_keys_callback(int, short, void *); static int tty_keys_mouse(struct tty *, const char *, size_t, size_t *, struct mouse_event *); static int tty_keys_clipboard(struct tty *, const char *, size_t, size_t *); static int tty_keys_device_attributes(struct tty *, const char *, size_t, size_t *); /* Default raw keys. */ struct tty_default_key_raw { const char *string; key_code key; }; static const struct tty_default_key_raw tty_default_raw_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. */ { "\033Oo", KEYC_KP_SLASH }, { "\033Oj", KEYC_KP_STAR }, { "\033Om", KEYC_KP_MINUS }, { "\033Ow", KEYC_KP_SEVEN }, { "\033Ox", KEYC_KP_EIGHT }, { "\033Oy", KEYC_KP_NINE }, { "\033Ok", KEYC_KP_PLUS }, { "\033Ot", KEYC_KP_FOUR }, { "\033Ou", KEYC_KP_FIVE }, { "\033Ov", KEYC_KP_SIX }, { "\033Oq", KEYC_KP_ONE }, { "\033Or", KEYC_KP_TWO }, { "\033Os", KEYC_KP_THREE }, { "\033OM", KEYC_KP_ENTER }, { "\033Op", KEYC_KP_ZERO }, { "\033On", KEYC_KP_PERIOD }, /* Arrow keys. */ { "\033OA", KEYC_UP }, { "\033OB", KEYC_DOWN }, { "\033OC", KEYC_RIGHT }, { "\033OD", KEYC_LEFT }, { "\033[A", KEYC_UP }, { "\033[B", KEYC_DOWN }, { "\033[C", KEYC_RIGHT }, { "\033[D", KEYC_LEFT }, /* Other (xterm) "cursor" keys. */ { "\033OH", KEYC_HOME }, { "\033OF", KEYC_END }, { "\033[H", KEYC_HOME }, { "\033[F", KEYC_END }, /* rxvt-style arrow + modifier keys. */ { "\033Oa", KEYC_UP|KEYC_CTRL }, { "\033Ob", KEYC_DOWN|KEYC_CTRL }, { "\033Oc", KEYC_RIGHT|KEYC_CTRL }, { "\033Od", KEYC_LEFT|KEYC_CTRL }, { "\033[a", KEYC_UP|KEYC_SHIFT }, { "\033[b", KEYC_DOWN|KEYC_SHIFT }, { "\033[c", KEYC_RIGHT|KEYC_SHIFT }, { "\033[d", KEYC_LEFT|KEYC_SHIFT }, /* rxvt-style function + modifier keys (C = ^, S = $, C-S = @). */ { "\033[11^", KEYC_F1|KEYC_CTRL }, { "\033[12^", KEYC_F2|KEYC_CTRL }, { "\033[13^", KEYC_F3|KEYC_CTRL }, { "\033[14^", KEYC_F4|KEYC_CTRL }, { "\033[15^", KEYC_F5|KEYC_CTRL }, { "\033[17^", KEYC_F6|KEYC_CTRL }, { "\033[18^", KEYC_F7|KEYC_CTRL }, { "\033[19^", KEYC_F8|KEYC_CTRL }, { "\033[20^", KEYC_F9|KEYC_CTRL }, { "\033[21^", KEYC_F10|KEYC_CTRL }, { "\033[23^", KEYC_F11|KEYC_CTRL }, { "\033[24^", KEYC_F12|KEYC_CTRL }, { "\033[2^", KEYC_IC|KEYC_CTRL }, { "\033[3^", KEYC_DC|KEYC_CTRL }, { "\033[7^", KEYC_HOME|KEYC_CTRL }, { "\033[8^", KEYC_END|KEYC_CTRL }, { "\033[6^", KEYC_NPAGE|KEYC_CTRL }, { "\033[5^", KEYC_PPAGE|KEYC_CTRL }, { "\033[11$", KEYC_F1|KEYC_SHIFT }, { "\033[12$", KEYC_F2|KEYC_SHIFT }, { "\033[13$", KEYC_F3|KEYC_SHIFT }, { "\033[14$", KEYC_F4|KEYC_SHIFT }, { "\033[15$", KEYC_F5|KEYC_SHIFT }, { "\033[17$", KEYC_F6|KEYC_SHIFT }, { "\033[18$", KEYC_F7|KEYC_SHIFT }, { "\033[19$", KEYC_F8|KEYC_SHIFT }, { "\033[20$", KEYC_F9|KEYC_SHIFT }, { "\033[21$", KEYC_F10|KEYC_SHIFT }, { "\033[23$", KEYC_F11|KEYC_SHIFT }, { "\033[24$", KEYC_F12|KEYC_SHIFT }, { "\033[2$", KEYC_IC|KEYC_SHIFT }, { "\033[3$", KEYC_DC|KEYC_SHIFT }, { "\033[7$", KEYC_HOME|KEYC_SHIFT }, { "\033[8$", KEYC_END|KEYC_SHIFT }, { "\033[6$", KEYC_NPAGE|KEYC_SHIFT }, { "\033[5$", KEYC_PPAGE|KEYC_SHIFT }, { "\033[11@", KEYC_F1|KEYC_CTRL|KEYC_SHIFT }, { "\033[12@", KEYC_F2|KEYC_CTRL|KEYC_SHIFT }, { "\033[13@", KEYC_F3|KEYC_CTRL|KEYC_SHIFT }, { "\033[14@", KEYC_F4|KEYC_CTRL|KEYC_SHIFT }, { "\033[15@", KEYC_F5|KEYC_CTRL|KEYC_SHIFT }, { "\033[17@", KEYC_F6|KEYC_CTRL|KEYC_SHIFT }, { "\033[18@", KEYC_F7|KEYC_CTRL|KEYC_SHIFT }, { "\033[19@", KEYC_F8|KEYC_CTRL|KEYC_SHIFT }, { "\033[20@", KEYC_F9|KEYC_CTRL|KEYC_SHIFT }, { "\033[21@", KEYC_F10|KEYC_CTRL|KEYC_SHIFT }, { "\033[23@", KEYC_F11|KEYC_CTRL|KEYC_SHIFT }, { "\033[24@", KEYC_F12|KEYC_CTRL|KEYC_SHIFT }, { "\033[2@", KEYC_IC|KEYC_CTRL|KEYC_SHIFT }, { "\033[3@", KEYC_DC|KEYC_CTRL|KEYC_SHIFT }, { "\033[7@", KEYC_HOME|KEYC_CTRL|KEYC_SHIFT }, { "\033[8@", KEYC_END|KEYC_CTRL|KEYC_SHIFT }, { "\033[6@", KEYC_NPAGE|KEYC_CTRL|KEYC_SHIFT }, { "\033[5@", KEYC_PPAGE|KEYC_CTRL|KEYC_SHIFT }, /* Focus tracking. */ { "\033[I", KEYC_FOCUS_IN }, { "\033[O", KEYC_FOCUS_OUT }, /* Paste keys. */ { "\033[200~", KEYC_PASTE_START }, { "\033[201~", KEYC_PASTE_END }, }; /* * Default terminfo(5) keys. Any keys that have builtin modifiers * (that is, where the key itself contains the modifiers) has the * KEYC_XTERM flag set so a leading escape is not treated as meta (and * probably removed). */ struct tty_default_key_code { enum tty_code_code code; key_code key; }; static const struct tty_default_key_code tty_default_code_keys[] = { /* Function keys. */ { TTYC_KF1, KEYC_F1 }, { TTYC_KF2, KEYC_F2 }, { TTYC_KF3, KEYC_F3 }, { TTYC_KF4, KEYC_F4 }, { TTYC_KF5, KEYC_F5 }, { TTYC_KF6, KEYC_F6 }, { TTYC_KF7, KEYC_F7 }, { TTYC_KF8, KEYC_F8 }, { TTYC_KF9, KEYC_F9 }, { TTYC_KF10, KEYC_F10 }, { TTYC_KF11, KEYC_F11 }, { TTYC_KF12, KEYC_F12 }, { TTYC_KF13, KEYC_F1|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KF14, KEYC_F2|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KF15, KEYC_F3|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KF16, KEYC_F4|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KF17, KEYC_F5|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KF18, KEYC_F6|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KF19, KEYC_F7|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KF20, KEYC_F8|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KF21, KEYC_F9|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KF22, KEYC_F10|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KF23, KEYC_F11|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KF24, KEYC_F12|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KF25, KEYC_F1|KEYC_CTRL|KEYC_XTERM }, { TTYC_KF26, KEYC_F2|KEYC_CTRL|KEYC_XTERM }, { TTYC_KF27, KEYC_F3|KEYC_CTRL|KEYC_XTERM }, { TTYC_KF28, KEYC_F4|KEYC_CTRL|KEYC_XTERM }, { TTYC_KF29, KEYC_F5|KEYC_CTRL|KEYC_XTERM }, { TTYC_KF30, KEYC_F6|KEYC_CTRL|KEYC_XTERM }, { TTYC_KF31, KEYC_F7|KEYC_CTRL|KEYC_XTERM }, { TTYC_KF32, KEYC_F8|KEYC_CTRL|KEYC_XTERM }, { TTYC_KF33, KEYC_F9|KEYC_CTRL|KEYC_XTERM }, { TTYC_KF34, KEYC_F10|KEYC_CTRL|KEYC_XTERM }, { TTYC_KF35, KEYC_F11|KEYC_CTRL|KEYC_XTERM }, { TTYC_KF36, KEYC_F12|KEYC_CTRL|KEYC_XTERM }, { TTYC_KF37, KEYC_F1|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, { TTYC_KF38, KEYC_F2|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, { TTYC_KF39, KEYC_F3|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, { TTYC_KF40, KEYC_F4|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, { TTYC_KF41, KEYC_F5|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, { TTYC_KF42, KEYC_F6|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, { TTYC_KF43, KEYC_F7|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, { TTYC_KF44, KEYC_F8|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, { TTYC_KF45, KEYC_F9|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, { TTYC_KF46, KEYC_F10|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, { TTYC_KF47, KEYC_F11|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, { TTYC_KF48, KEYC_F12|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, { TTYC_KF49, KEYC_F1|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KF50, KEYC_F2|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KF51, KEYC_F3|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KF52, KEYC_F4|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KF53, KEYC_F5|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KF54, KEYC_F6|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KF55, KEYC_F7|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KF56, KEYC_F8|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KF57, KEYC_F9|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KF58, KEYC_F10|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KF59, KEYC_F11|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KF60, KEYC_F12|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KF61, KEYC_F1|KEYC_ESCAPE|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KF62, KEYC_F2|KEYC_ESCAPE|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KF63, KEYC_F3|KEYC_ESCAPE|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KICH1, KEYC_IC }, { TTYC_KDCH1, KEYC_DC }, { TTYC_KHOME, KEYC_HOME }, { TTYC_KEND, KEYC_END }, { TTYC_KNP, KEYC_NPAGE }, { TTYC_KPP, KEYC_PPAGE }, { TTYC_KCBT, KEYC_BTAB }, /* Arrow keys from terminfo. */ { TTYC_KCUU1, KEYC_UP }, { TTYC_KCUD1, KEYC_DOWN }, { TTYC_KCUB1, KEYC_LEFT }, { TTYC_KCUF1, KEYC_RIGHT }, /* Key and modifier capabilities. */ { TTYC_KDC2, KEYC_DC|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KDC3, KEYC_DC|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KDC4, KEYC_DC|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KDC5, KEYC_DC|KEYC_CTRL|KEYC_XTERM }, { TTYC_KDC6, KEYC_DC|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, { TTYC_KDC7, KEYC_DC|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, { TTYC_KIND, KEYC_DOWN|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KDN2, KEYC_DOWN|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KDN3, KEYC_DOWN|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KDN4, KEYC_DOWN|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KDN5, KEYC_DOWN|KEYC_CTRL|KEYC_XTERM }, { TTYC_KDN6, KEYC_DOWN|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, { TTYC_KDN7, KEYC_DOWN|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, { TTYC_KEND2, KEYC_END|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KEND3, KEYC_END|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KEND4, KEYC_END|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KEND5, KEYC_END|KEYC_CTRL|KEYC_XTERM }, { TTYC_KEND6, KEYC_END|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, { TTYC_KEND7, KEYC_END|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, { TTYC_KHOM2, KEYC_HOME|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KHOM3, KEYC_HOME|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KHOM4, KEYC_HOME|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KHOM5, KEYC_HOME|KEYC_CTRL|KEYC_XTERM }, { TTYC_KHOM6, KEYC_HOME|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, { TTYC_KHOM7, KEYC_HOME|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, { TTYC_KIC2, KEYC_IC|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KIC3, KEYC_IC|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KIC4, KEYC_IC|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KIC5, KEYC_IC|KEYC_CTRL|KEYC_XTERM }, { TTYC_KIC6, KEYC_IC|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, { TTYC_KIC7, KEYC_IC|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, { TTYC_KLFT2, KEYC_LEFT|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KLFT3, KEYC_LEFT|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KLFT4, KEYC_LEFT|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KLFT5, KEYC_LEFT|KEYC_CTRL|KEYC_XTERM }, { TTYC_KLFT6, KEYC_LEFT|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, { TTYC_KLFT7, KEYC_LEFT|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, { TTYC_KNXT2, KEYC_NPAGE|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KNXT3, KEYC_NPAGE|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KNXT4, KEYC_NPAGE|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KNXT5, KEYC_NPAGE|KEYC_CTRL|KEYC_XTERM }, { TTYC_KNXT6, KEYC_NPAGE|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, { TTYC_KNXT7, KEYC_NPAGE|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, { TTYC_KPRV2, KEYC_PPAGE|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KPRV3, KEYC_PPAGE|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KPRV4, KEYC_PPAGE|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KPRV5, KEYC_PPAGE|KEYC_CTRL|KEYC_XTERM }, { TTYC_KPRV6, KEYC_PPAGE|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, { TTYC_KPRV7, KEYC_PPAGE|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, { TTYC_KRIT2, KEYC_RIGHT|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KRIT3, KEYC_RIGHT|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KRIT4, KEYC_RIGHT|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KRIT5, KEYC_RIGHT|KEYC_CTRL|KEYC_XTERM }, { TTYC_KRIT6, KEYC_RIGHT|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, { TTYC_KRIT7, KEYC_RIGHT|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, { TTYC_KRI, KEYC_UP|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KUP2, KEYC_UP|KEYC_SHIFT|KEYC_XTERM }, { TTYC_KUP3, KEYC_UP|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KUP4, KEYC_UP|KEYC_SHIFT|KEYC_ESCAPE|KEYC_XTERM }, { TTYC_KUP5, KEYC_UP|KEYC_CTRL|KEYC_XTERM }, { TTYC_KUP6, KEYC_UP|KEYC_SHIFT|KEYC_CTRL|KEYC_XTERM }, { TTYC_KUP7, KEYC_UP|KEYC_ESCAPE|KEYC_CTRL|KEYC_XTERM }, }; /* Add key to tree. */ static void tty_keys_add(struct tty *tty, const char *s, key_code 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%llx (%s)", s, key, keystr); tty_keys_add1(&tty->key_tree, s, key); } else { log_debug("replacing key %s: 0x%llx (%s)", s, key, keystr); tk->key = key; } } /* Add next node to the tree. */ static void tty_keys_add1(struct tty_key **tkp, const char *s, key_code 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_UNKNOWN; } /* 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_build(struct tty *tty) { const struct tty_default_key_raw *tdkr; const struct tty_default_key_code *tdkc; u_int i; const char *s; struct options_entry *o; struct options_array_item *a; union options_value *ov; if (tty->key_tree != NULL) tty_keys_free(tty); tty->key_tree = NULL; for (i = 0; i < nitems(tty_default_raw_keys); i++) { tdkr = &tty_default_raw_keys[i]; s = tdkr->string; if (*s != '\0') tty_keys_add(tty, s, tdkr->key); } for (i = 0; i < nitems(tty_default_code_keys); i++) { tdkc = &tty_default_code_keys[i]; s = tty_term_string(tty->term, tdkc->code); if (*s != '\0') tty_keys_add(tty, s, tdkc->key); } o = options_get(global_options, "user-keys"); if (o != NULL) { a = options_array_first(o); while (a != NULL) { i = options_array_item_index(a); ov = options_array_item_value(a); tty_keys_add(tty, ov->string, KEYC_USER + i); a = options_array_next(a); } } } /* Free the entire key tree. */ void tty_keys_free(struct tty *tty) { tty_keys_free1(tty->key_tree); } /* Free a single key. */ static 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); free(tk); } /* Lookup a key in the tree. */ static 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. */ static struct tty_key * tty_keys_find1(struct tty_key *tk, const char *buf, size_t len, size_t *size) { /* If no data, no match. */ if (len == 0) return (NULL); /* 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_UNKNOWN)) 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)); } /* Look up part of the next key. */ static int tty_keys_next1(struct tty *tty, const char *buf, size_t len, key_code *key, size_t *size, int expired) { struct client *c = tty->client; struct tty_key *tk, *tk1; struct utf8_data ud; enum utf8_state more; u_int i; wchar_t wc; int n; log_debug("%s: next key is %zu (%.*s) (expired=%d)", c->name, len, (int)len, buf, expired); /* Is this a known key? */ tk = tty_keys_find(tty, buf, len, size); if (tk != NULL && tk->key != KEYC_UNKNOWN) { tk1 = tk; do log_debug("%s: keys in list: %#llx", c->name, tk1->key); while ((tk1 = tk1->next) != NULL); if (tk->next != NULL && !expired) return (1); *key = tk->key; return (0); } /* Is this an an xterm(1) key? */ n = xterm_keys_find(buf, len, size, key); if (n == 0) return (0); if (n == 1 && !expired) return (1); /* Is this valid UTF-8? */ more = utf8_open(&ud, (u_char)*buf); if (more == UTF8_MORE) { *size = ud.size; if (len < ud.size) { if (!expired) return (1); return (-1); } for (i = 1; i < ud.size; i++) more = utf8_append(&ud, (u_char)buf[i]); if (more != UTF8_DONE) return (-1); if (utf8_combine(&ud, &wc) != UTF8_DONE) return (-1); *key = wc; log_debug("%s: UTF-8 key %.*s %#llx", c->name, (int)ud.size, buf, *key); return (0); } return (-1); } /* Process at least one key in the buffer. Return 0 if no keys present. */ int tty_keys_next(struct tty *tty) { struct client *c = tty->client; struct timeval tv; const char *buf; size_t len, size; cc_t bspace; int delay, expired = 0, n; key_code key; struct mouse_event m = { 0 }; struct key_event *event; gettimeofday(&tv, NULL); /* Get key buffer. */ buf = EVBUFFER_DATA(tty->in); len = EVBUFFER_LENGTH(tty->in); if (len == 0) return (0); log_debug("%s: keys are %zu (%.*s)", c->name, len, (int)len, buf); /* Is this a clipboard response? */ switch (tty_keys_clipboard(tty, buf, len, &size)) { case 0: /* yes */ key = KEYC_UNKNOWN; goto complete_key; case -1: /* no, or not valid */ break; case 1: /* partial */ goto partial_key; } /* Is this a device attributes response? */ switch (tty_keys_device_attributes(tty, buf, len, &size)) { case 0: /* yes */ key = KEYC_UNKNOWN; goto complete_key; case -1: /* no, or not valid */ break; case 1: /* partial */ goto partial_key; } /* Is this a mouse key press? */ switch (tty_keys_mouse(tty, buf, len, &size, &m)) { case 0: /* yes */ key = KEYC_MOUSE; goto complete_key; case -1: /* no, or not valid */ break; case -2: /* yes, but we don't care. */ key = KEYC_MOUSE; goto discard_key; case 1: /* partial */ goto partial_key; } first_key: /* Try to lookup complete key. */ n = tty_keys_next1(tty, buf, len, &key, &size, expired); if (n == 0) /* found */ goto complete_key; if (n == 1) goto partial_key; /* * If not a complete key, look for key with an escape prefix (meta * modifier). */ if (*buf == '\033' && len > 1) { /* Look for a key without the escape. */ n = tty_keys_next1(tty, buf + 1, len - 1, &key, &size, expired); if (n == 0) { /* found */ if (key & KEYC_XTERM) { /* * We want the escape key as well as the xterm * key, because the xterm sequence implicitly * includes the escape (so if we see * \033\033[1;3D we know it is an Escape * followed by M-Left, not just M-Left). */ key = '\033'; size = 1; goto complete_key; } key |= KEYC_ESCAPE; size++; goto complete_key; } if (n == 1) /* partial */ goto partial_key; } /* * At this point, we know the key is not partial (with or without * escape). So pass it through even if the timer has not expired. */ if (*buf == '\033' && len >= 2) { key = (u_char)buf[1] | KEYC_ESCAPE; size = 2; } else { key = (u_char)buf[0]; size = 1; } goto complete_key; partial_key: log_debug("%s: partial key %.*s", c->name, (int)len, buf); /* If timer is going, check for expiration. */ if (tty->flags & TTY_TIMER) { if (evtimer_initialized(&tty->key_timer) && !evtimer_pending(&tty->key_timer, NULL)) { expired = 1; goto first_key; } return (0); } /* Get the time period. */ delay = options_get_number(global_options, "escape-time"); tv.tv_sec = delay / 1000; tv.tv_usec = (delay % 1000) * 1000L; /* Start the timer. */ if (event_initialized(&tty->key_timer)) evtimer_del(&tty->key_timer); evtimer_set(&tty->key_timer, tty_keys_callback, tty); evtimer_add(&tty->key_timer, &tv); tty->flags |= TTY_TIMER; return (0); complete_key: log_debug("%s: complete key %.*s %#llx", c->name, (int)size, buf, key); /* * 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 & KEYC_MASK_KEY) == bspace) key = (key & KEYC_MASK_MOD) | KEYC_BSPACE; /* Remove data from buffer. */ evbuffer_drain(tty->in, size); /* Remove key timer. */ if (event_initialized(&tty->key_timer)) evtimer_del(&tty->key_timer); tty->flags &= ~TTY_TIMER; /* Check for focus events. */ if (key == KEYC_FOCUS_OUT) { tty->client->flags &= ~CLIENT_FOCUSED; return (1); } else if (key == KEYC_FOCUS_IN) { tty->client->flags |= CLIENT_FOCUSED; return (1); } /* Fire the key. */ if (key != KEYC_UNKNOWN) { event = xmalloc(sizeof *event); event->key = key; memcpy(&event->m, &m, sizeof event->m); if (!server_client_handle_key(c, event)) free(event); } return (1); discard_key: log_debug("%s: discard key %.*s %#llx", c->name, (int)size, buf, key); /* Remove data from buffer. */ evbuffer_drain(tty->in, size); return (1); } /* Key timer callback. */ static void tty_keys_callback(__unused int fd, __unused short events, void *data) { struct tty *tty = data; if (tty->flags & TTY_TIMER) { 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). */ static int tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size, struct mouse_event *m) { struct client *c = tty->client; u_int i, x, y, b, sgr_b; u_char sgr_type, ch; /* * Standard mouse sequences are \033[M followed by three characters * indicating button, 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. * * SGR extended mouse sequences are \033[< followed by three numbers in * decimal and separated by semicolons indicating button, X and Y. A * trailing 'M' is click or scroll and trailing 'm' release. All are * based at 0 with 1,1 top-left. */ *size = 0; x = y = b = sgr_b = 0; sgr_type = ' '; /* First two bytes are always \033[. */ if (buf[0] != '\033') return (-1); if (len == 1) return (1); if (buf[1] != '[') return (-1); if (len == 2) return (1); /* * Third byte is M in old standard (and UTF-8 extension which we do not * support), < in SGR extension. */ if (buf[2] == 'M') { /* Read the three inputs. */ *size = 3; for (i = 0; i < 3; i++) { if (len <= *size) return (1); ch = (u_char)buf[(*size)++]; if (i == 0) b = ch; else if (i == 1) x = ch; else y = ch; } log_debug("%s: mouse input: %.*s", c->name, (int)*size, buf); /* Check and return the mouse input. */ if (b < 32) return (-1); b -= 32; if (x >= 33) x -= 33; else x = 256 - x; if (y >= 33) y -= 33; else y = 256 - y; } else if (buf[2] == '<') { /* Read the three inputs. */ *size = 3; while (1) { if (len <= *size) return (1); ch = (u_char)buf[(*size)++]; if (ch == ';') break; if (ch < '0' || ch > '9') return (-1); sgr_b = 10 * sgr_b + (ch - '0'); } while (1) { if (len <= *size) return (1); ch = (u_char)buf[(*size)++]; if (ch == ';') break; if (ch < '0' || ch > '9') return (-1); x = 10 * x + (ch - '0'); } while (1) { if (len <= *size) return (1); ch = (u_char)buf[(*size)++]; if (ch == 'M' || ch == 'm') break; if (ch < '0' || ch > '9') return (-1); y = 10 * y + (ch - '0'); } log_debug("%s: mouse input (SGR): %.*s", c->name, (int)*size, buf); /* Check and return the mouse input. */ if (x < 1 || y < 1) return (-1); x--; y--; b = sgr_b; /* Type is M for press, m for release. */ sgr_type = ch; if (sgr_type == 'm') b |= 3; /* * Some terminals (like PuTTY 0.63) mistakenly send * button-release events for scroll-wheel button-press event. * Discard it before it reaches any program running inside * tmux. */ if (sgr_type == 'm' && (sgr_b & 64)) return (-2); } else return (-1); /* Fill mouse event. */ m->lx = tty->mouse_last_x; m->x = x; m->ly = tty->mouse_last_y; m->y = y; m->lb = tty->mouse_last_b; m->b = b; m->sgr_type = sgr_type; m->sgr_b = sgr_b; /* Update last mouse state. */ tty->mouse_last_x = x; tty->mouse_last_y = y; tty->mouse_last_b = b; return (0); } /* * Handle OSC 52 clipboard input. Returns 0 for success, -1 for failure, 1 for * partial. */ static int tty_keys_clipboard(__unused struct tty *tty, const char *buf, size_t len, size_t *size) { size_t end, terminator, needed; char *copy, *out; int outlen; *size = 0; /* First three bytes are always \033]52;. */ if (buf[0] != '\033') return (-1); if (len == 1) return (1); if (buf[1] != ']') return (-1); if (len == 2) return (1); if (buf[2] != '5') return (-1); if (len == 3) return (1); if (buf[3] != '2') return (-1); if (len == 4) return (1); if (buf[4] != ';') return (-1); if (len == 5) return (1); /* Find the terminator if any. */ for (end = 5; end < len; end++) { if (buf[end] == '\007') { terminator = 1; break; } if (end > 5 && buf[end - 1] == '\033' && buf[end] == '\\') { terminator = 2; break; } } if (end == len) return (1); *size = end + terminator; /* Skip the initial part. */ buf += 5; end -= 5; /* Get the second argument. */ while (end != 0 && *buf != ';') { buf++; end--; } if (end == 0 || end == 1) return (0); buf++; end--; /* It has to be a string so copy it. */ copy = xmalloc(end + 1); memcpy(copy, buf, end); copy[end] = '\0'; /* Convert from base64. */ needed = (end / 4) * 3; out = xmalloc(needed); if ((outlen = b64_pton(copy, out, len)) == -1) { free(out); free(copy); return (0); } free(copy); /* Create a new paste buffer. */ log_debug("%s: %.*s", __func__, outlen, out); paste_add(NULL, out, outlen); return (0); } /* * Handle device attributes input. Returns 0 for success, -1 for failure, 1 for * partial. */ static int tty_keys_device_attributes(struct tty *tty, const char *buf, size_t len, size_t *size) { struct client *c = tty->client; u_int i, a, b; char tmp[64], *endptr; static const char *types[] = TTY_TYPES; int type; *size = 0; /* First three bytes are always \033[?. */ if (buf[0] != '\033') return (-1); if (len == 1) return (1); if (buf[1] != '[') return (-1); if (len == 2) return (1); if (buf[2] != '?') return (-1); if (len == 3) return (1); /* Copy the rest up to a 'c'. */ for (i = 0; i < (sizeof tmp) - 1 && buf[3 + i] != 'c'; i++) { if (3 + i == len) return (1); tmp[i] = buf[3 + i]; } if (i == (sizeof tmp) - 1) return (-1); tmp[i] = '\0'; *size = 4 + i; /* Convert version numbers. */ a = strtoul(tmp, &endptr, 10); if (*endptr == ';') { b = strtoul(endptr + 1, &endptr, 10); if (*endptr != '\0' && *endptr != ';') b = 0; } else a = b = 0; /* Store terminal type. */ type = TTY_UNKNOWN; switch (a) { case 1: if (b == 2) type = TTY_VT100; else if (b == 0) type = TTY_VT101; break; case 6: type = TTY_VT102; break; case 62: type = TTY_VT220; break; case 63: type = TTY_VT320; break; case 64: type = TTY_VT420; break; } tty_set_type(tty, type); log_debug("%s: received DA %.*s (%s)", c->name, (int)*size, buf, types[type]); return (0); } tmux-3.0a/tty-term.c100644 001750 001750 00000051436 13570677043 0010172/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #if defined(HAVE_CURSES_H) #include #elif defined(HAVE_NCURSES_H) #include #endif #include #include #include #include #include "tmux.h" static void tty_term_override(struct tty_term *, const char *); static char *tty_term_strip(const char *); struct tty_terms tty_terms = LIST_HEAD_INITIALIZER(tty_terms); enum tty_code_type { TTYCODE_NONE = 0, TTYCODE_STRING, TTYCODE_NUMBER, TTYCODE_FLAG, }; struct tty_code { enum tty_code_type type; union { char *string; int number; int flag; } value; }; struct tty_term_code_entry { enum tty_code_type type; const char *name; }; static const struct tty_term_code_entry tty_term_codes[] = { [TTYC_ACSC] = { TTYCODE_STRING, "acsc" }, [TTYC_AX] = { TTYCODE_FLAG, "AX" }, [TTYC_BCE] = { TTYCODE_FLAG, "bce" }, [TTYC_BEL] = { TTYCODE_STRING, "bel" }, [TTYC_BLINK] = { TTYCODE_STRING, "blink" }, [TTYC_BOLD] = { TTYCODE_STRING, "bold" }, [TTYC_CIVIS] = { TTYCODE_STRING, "civis" }, [TTYC_CLEAR] = { TTYCODE_STRING, "clear" }, [TTYC_CNORM] = { TTYCODE_STRING, "cnorm" }, [TTYC_COLORS] = { TTYCODE_NUMBER, "colors" }, [TTYC_CR] = { TTYCODE_STRING, "Cr" }, [TTYC_CSR] = { TTYCODE_STRING, "csr" }, [TTYC_CS] = { TTYCODE_STRING, "Cs" }, [TTYC_CUB1] = { TTYCODE_STRING, "cub1" }, [TTYC_CUB] = { TTYCODE_STRING, "cub" }, [TTYC_CUD1] = { TTYCODE_STRING, "cud1" }, [TTYC_CUD] = { TTYCODE_STRING, "cud" }, [TTYC_CUF1] = { TTYCODE_STRING, "cuf1" }, [TTYC_CUF] = { TTYCODE_STRING, "cuf" }, [TTYC_CUP] = { TTYCODE_STRING, "cup" }, [TTYC_CUU1] = { TTYCODE_STRING, "cuu1" }, [TTYC_CUU] = { TTYCODE_STRING, "cuu" }, [TTYC_CVVIS] = { TTYCODE_STRING, "cvvis" }, [TTYC_DCH1] = { TTYCODE_STRING, "dch1" }, [TTYC_DCH] = { TTYCODE_STRING, "dch" }, [TTYC_DIM] = { TTYCODE_STRING, "dim" }, [TTYC_DL1] = { TTYCODE_STRING, "dl1" }, [TTYC_DL] = { TTYCODE_STRING, "dl" }, [TTYC_E3] = { TTYCODE_STRING, "E3" }, [TTYC_ECH] = { TTYCODE_STRING, "ech" }, [TTYC_ED] = { TTYCODE_STRING, "ed" }, [TTYC_EL1] = { TTYCODE_STRING, "el1" }, [TTYC_EL] = { TTYCODE_STRING, "el" }, [TTYC_ENACS] = { TTYCODE_STRING, "enacs" }, [TTYC_FSL] = { TTYCODE_STRING, "fsl" }, [TTYC_HOME] = { TTYCODE_STRING, "home" }, [TTYC_HPA] = { TTYCODE_STRING, "hpa" }, [TTYC_ICH1] = { TTYCODE_STRING, "ich1" }, [TTYC_ICH] = { TTYCODE_STRING, "ich" }, [TTYC_IL1] = { TTYCODE_STRING, "il1" }, [TTYC_IL] = { TTYCODE_STRING, "il" }, [TTYC_INDN] = { TTYCODE_STRING, "indn" }, [TTYC_INVIS] = { TTYCODE_STRING, "invis" }, [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" }, /* not kDN2 */ [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_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_KEND] = { TTYCODE_STRING, "kend" }, [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_KF1] = { TTYCODE_STRING, "kf1" }, [TTYC_KF20] = { TTYCODE_STRING, "kf20" }, [TTYC_KF21] = { TTYCODE_STRING, "kf21" }, [TTYC_KF22] = { TTYCODE_STRING, "kf22" }, [TTYC_KF23] = { TTYCODE_STRING, "kf23" }, [TTYC_KF24] = { TTYCODE_STRING, "kf24" }, [TTYC_KF25] = { TTYCODE_STRING, "kf25" }, [TTYC_KF26] = { TTYCODE_STRING, "kf26" }, [TTYC_KF27] = { TTYCODE_STRING, "kf27" }, [TTYC_KF28] = { TTYCODE_STRING, "kf28" }, [TTYC_KF29] = { TTYCODE_STRING, "kf29" }, [TTYC_KF2] = { TTYCODE_STRING, "kf2" }, [TTYC_KF30] = { TTYCODE_STRING, "kf30" }, [TTYC_KF31] = { TTYCODE_STRING, "kf31" }, [TTYC_KF32] = { TTYCODE_STRING, "kf32" }, [TTYC_KF33] = { TTYCODE_STRING, "kf33" }, [TTYC_KF34] = { TTYCODE_STRING, "kf34" }, [TTYC_KF35] = { TTYCODE_STRING, "kf35" }, [TTYC_KF36] = { TTYCODE_STRING, "kf36" }, [TTYC_KF37] = { TTYCODE_STRING, "kf37" }, [TTYC_KF38] = { TTYCODE_STRING, "kf38" }, [TTYC_KF39] = { TTYCODE_STRING, "kf39" }, [TTYC_KF3] = { TTYCODE_STRING, "kf3" }, [TTYC_KF40] = { TTYCODE_STRING, "kf40" }, [TTYC_KF41] = { TTYCODE_STRING, "kf41" }, [TTYC_KF42] = { TTYCODE_STRING, "kf42" }, [TTYC_KF43] = { TTYCODE_STRING, "kf43" }, [TTYC_KF44] = { TTYCODE_STRING, "kf44" }, [TTYC_KF45] = { TTYCODE_STRING, "kf45" }, [TTYC_KF46] = { TTYCODE_STRING, "kf46" }, [TTYC_KF47] = { TTYCODE_STRING, "kf47" }, [TTYC_KF48] = { TTYCODE_STRING, "kf48" }, [TTYC_KF49] = { TTYCODE_STRING, "kf49" }, [TTYC_KF4] = { TTYCODE_STRING, "kf4" }, [TTYC_KF50] = { TTYCODE_STRING, "kf50" }, [TTYC_KF51] = { TTYCODE_STRING, "kf51" }, [TTYC_KF52] = { TTYCODE_STRING, "kf52" }, [TTYC_KF53] = { TTYCODE_STRING, "kf53" }, [TTYC_KF54] = { TTYCODE_STRING, "kf54" }, [TTYC_KF55] = { TTYCODE_STRING, "kf55" }, [TTYC_KF56] = { TTYCODE_STRING, "kf56" }, [TTYC_KF57] = { TTYCODE_STRING, "kf57" }, [TTYC_KF58] = { TTYCODE_STRING, "kf58" }, [TTYC_KF59] = { TTYCODE_STRING, "kf59" }, [TTYC_KF5] = { TTYCODE_STRING, "kf5" }, [TTYC_KF60] = { TTYCODE_STRING, "kf60" }, [TTYC_KF61] = { TTYCODE_STRING, "kf61" }, [TTYC_KF62] = { TTYCODE_STRING, "kf62" }, [TTYC_KF63] = { TTYCODE_STRING, "kf63" }, [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_KIND] = { TTYCODE_STRING, "kind" }, [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_KRI] = { TTYCODE_STRING, "kri" }, [TTYC_KUP2] = { TTYCODE_STRING, "kUP" }, /* not kUP2 */ [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_RGB] = { TTYCODE_FLAG, "RGB" }, [TTYC_RI] = { TTYCODE_STRING, "ri" }, [TTYC_RIN] = { TTYCODE_STRING, "rin" }, [TTYC_RMACS] = { TTYCODE_STRING, "rmacs" }, [TTYC_RMCUP] = { TTYCODE_STRING, "rmcup" }, [TTYC_RMKX] = { TTYCODE_STRING, "rmkx" }, [TTYC_SETAB] = { TTYCODE_STRING, "setab" }, [TTYC_SETAF] = { TTYCODE_STRING, "setaf" }, [TTYC_SETRGBB] = { TTYCODE_STRING, "setrgbb" }, [TTYC_SETRGBF] = { TTYCODE_STRING, "setrgbf" }, [TTYC_SETULC] = { TTYCODE_STRING, "Setulc" }, [TTYC_SE] = { TTYCODE_STRING, "Se" }, [TTYC_SGR0] = { TTYCODE_STRING, "sgr0" }, [TTYC_SITM] = { TTYCODE_STRING, "sitm" }, [TTYC_SMACS] = { TTYCODE_STRING, "smacs" }, [TTYC_SMCUP] = { TTYCODE_STRING, "smcup" }, [TTYC_SMKX] = { TTYCODE_STRING, "smkx" }, [TTYC_SMOL] = { TTYCODE_STRING, "Smol" }, [TTYC_SMSO] = { TTYCODE_STRING, "smso" }, [TTYC_SMULX] = { TTYCODE_STRING, "Smulx" }, [TTYC_SMUL] = { TTYCODE_STRING, "smul" }, [TTYC_SMXX] = { TTYCODE_STRING, "smxx" }, [TTYC_SS] = { TTYCODE_STRING, "Ss" }, [TTYC_TC] = { TTYCODE_FLAG, "Tc" }, [TTYC_TSL] = { TTYCODE_STRING, "tsl" }, [TTYC_U8] = { TTYCODE_NUMBER, "U8" }, [TTYC_VPA] = { TTYCODE_STRING, "vpa" }, [TTYC_XENL] = { TTYCODE_FLAG, "xenl" }, [TTYC_XT] = { TTYCODE_FLAG, "XT" }, }; u_int tty_term_ncodes(void) { return (nitems(tty_term_codes)); } static char * tty_term_strip(const char *s) { const char *ptr; static char buf[8192]; 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)); } static char * tty_term_override_next(const char *s, size_t *offset) { static char value[8192]; size_t n = 0, at = *offset; if (s[at] == '\0') return (NULL); while (s[at] != '\0') { if (s[at] == ':') { if (s[at + 1] == ':') { value[n++] = ':'; at += 2; } else break; } else { value[n++] = s[at]; at++; } if (n == (sizeof value) - 1) return (NULL); } if (s[at] != '\0') *offset = at + 1; else *offset = at; value[n] = '\0'; return (value); } static void tty_term_override(struct tty_term *term, const char *override) { const struct tty_term_code_entry *ent; struct tty_code *code; size_t offset = 0; char *cp, *value, *s; const char *errstr; u_int i; int n, remove; s = tty_term_override_next(override, &offset); if (s == NULL || fnmatch(s, term->name, 0) != 0) return; while ((s = tty_term_override_next(override, &offset)) != NULL) { if (*s == '\0') continue; value = NULL; remove = 0; if ((cp = strchr(s, '=')) != NULL) { *cp++ = '\0'; value = xstrdup(cp); if (strunvis(value, cp) == -1) { free(value); value = xstrdup(cp); } } else if (s[strlen(s) - 1] == '@') { s[strlen(s) - 1] = '\0'; remove = 1; } else value = xstrdup(""); if (remove) log_debug("%s override: %s@", term->name, s); else if (*value == '\0') log_debug("%s override: %s", term->name, s); else log_debug("%s override: %s=%s", term->name, s, value); for (i = 0; i < tty_term_ncodes(); i++) { ent = &tty_term_codes[i]; if (strcmp(s, ent->name) != 0) continue; code = &term->codes[i]; if (remove) { code->type = TTYCODE_NONE; continue; } switch (ent->type) { case TTYCODE_NONE: break; case TTYCODE_STRING: if (code->type == TTYCODE_STRING) free(code->value.string); code->value.string = xstrdup(value); code->type = ent->type; break; case TTYCODE_NUMBER: n = strtonum(value, 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; } } free(value); } } struct tty_term * tty_term_find(char *name, int fd, char **cause) { struct tty_term *term; const struct tty_term_code_entry *ent; struct tty_code *code; struct options_entry *o; struct options_array_item *a; union options_value *ov; u_int i; int n, error; const char *s, *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; term->codes = xcalloc(tty_term_ncodes(), 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 < tty_term_ncodes(); i++) { ent = &tty_term_codes[i]; code = &term->codes[i]; code->type = TTYCODE_NONE; switch (ent->type) { case TTYCODE_NONE: break; case TTYCODE_STRING: s = tigetstr((char *) ent->name); if (s == NULL || s == (char *) -1) break; code->type = TTYCODE_STRING; code->value.string = tty_term_strip(s); break; case TTYCODE_NUMBER: n = tigetnum((char *) ent->name); if (n == -1 || n == -2) break; code->type = TTYCODE_NUMBER; code->value.number = n; break; case TTYCODE_FLAG: n = tigetflag((char *) ent->name); if (n == -1) break; code->type = TTYCODE_FLAG; code->value.flag = n; break; } } /* Apply terminal overrides. */ o = options_get_only(global_options, "terminal-overrides"); a = options_array_first(o); while (a != NULL) { ov = options_array_item_value(a); tty_term_override(term, ov->string); a = options_array_next(a); } /* Delete curses data. */ #if !defined(NCURSES_VERSION_MAJOR) || NCURSES_VERSION_MAJOR > 5 || \ (NCURSES_VERSION_MAJOR == 5 && NCURSES_VERSION_MINOR > 6) 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 colours (or more). */ if (tty_term_number(term, TTYC_COLORS) >= 256 || tty_term_has(term, TTYC_RGB)) term->flags |= TERM_256COLOURS; /* * 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; } /* * On terminals with RGB colour (Tc or RGB), fill in setrgbf and * setrgbb if they are missing. */ if ((tty_term_flag(term, TTYC_TC) || tty_term_flag(term, TTYC_RGB)) && !tty_term_has(term, TTYC_SETRGBF) && !tty_term_has(term, TTYC_SETRGBB)) { code = &term->codes[TTYC_SETRGBF]; code->value.string = xstrdup("\033[38;2;%p1%d;%p2%d;%p3%dm"); code->type = TTYCODE_STRING; code = &term->codes[TTYC_SETRGBB]; code->value.string = xstrdup("\033[48;2;%p1%d;%p2%d;%p3%dm"); code->type = TTYCODE_STRING; } /* Log it. */ for (i = 0; i < tty_term_ncodes(); i++) log_debug("%s%s", name, tty_term_describe(term, i)); 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 < tty_term_ncodes(); i++) { if (term->codes[i].type == TTYCODE_STRING) free(term->codes[i].value.string); } free(term->codes); free(term->name); free(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) fatalx("not a string: %d", code); return (term->codes[code].value.string); } const char * tty_term_string1(struct tty_term *term, enum tty_code_code code, int a) { return (tparm((char *) tty_term_string(term, code), a, 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((char *) tty_term_string(term, code), a, b, 0, 0, 0, 0, 0, 0, 0)); } const char * tty_term_string3(struct tty_term *term, enum tty_code_code code, int a, int b, int c) { return (tparm((char *) tty_term_string(term, code), a, b, c, 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((char *) 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((char *) 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) 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) fatalx("not a flag: %d", code); return (term->codes[code].value.flag); } const char * tty_term_describe(struct tty_term *term, enum tty_code_code code) { static char s[256]; char out[128]; switch (term->codes[code].type) { case TTYCODE_NONE: xsnprintf(s, sizeof s, "%4u: %s: [missing]", code, tty_term_codes[code].name); break; case TTYCODE_STRING: strnvis(out, term->codes[code].value.string, sizeof out, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); xsnprintf(s, sizeof s, "%4u: %s: (string) %s", code, tty_term_codes[code].name, out); break; case TTYCODE_NUMBER: xsnprintf(s, sizeof s, "%4u: %s: (number) %d", code, tty_term_codes[code].name, term->codes[code].value.number); break; case TTYCODE_FLAG: xsnprintf(s, sizeof s, "%4u: %s: (flag) %s", code, tty_term_codes[code].name, term->codes[code].value.flag ? "true" : "false"); break; } return (s); } tmux-3.0a/tty.c100644 001750 001750 00000176534 13570677043 0007234/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include "tmux.h" static int tty_log_fd = -1; static int tty_client_ready(struct client *, struct window_pane *); static void tty_set_italics(struct tty *); static int tty_try_colour(struct tty *, int, const char *); static void tty_force_cursor_colour(struct tty *, const char *); static void tty_cursor_pane(struct tty *, const struct tty_ctx *, u_int, u_int); static void tty_cursor_pane_unless_wrap(struct tty *, const struct tty_ctx *, u_int, u_int); static void tty_invalidate(struct tty *); static void tty_colours(struct tty *, const struct grid_cell *); static void tty_check_fg(struct tty *, struct window_pane *, struct grid_cell *); static void tty_check_bg(struct tty *, struct window_pane *, struct grid_cell *); static void tty_check_us(struct tty *, struct window_pane *, struct grid_cell *); static void tty_colours_fg(struct tty *, const struct grid_cell *); static void tty_colours_bg(struct tty *, const struct grid_cell *); static void tty_colours_us(struct tty *, const struct grid_cell *); static void tty_region_pane(struct tty *, const struct tty_ctx *, u_int, u_int); static void tty_region(struct tty *, u_int, u_int); static void tty_margin_pane(struct tty *, const struct tty_ctx *); static void tty_margin(struct tty *, u_int, u_int); static int tty_large_region(struct tty *, const struct tty_ctx *); static int tty_fake_bce(const struct tty *, struct window_pane *, u_int); static void tty_redraw_region(struct tty *, const struct tty_ctx *); static void tty_emulate_repeat(struct tty *, enum tty_code_code, enum tty_code_code, u_int); static void tty_repeat_space(struct tty *, u_int); static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int); static void tty_cell(struct tty *, const struct grid_cell *, struct window_pane *); static void tty_default_colours(struct grid_cell *, struct window_pane *); static void tty_default_attributes(struct tty *, struct window_pane *, u_int); #define tty_use_margin(tty) \ ((tty)->term_type == TTY_VT420) #define tty_pane_full_width(tty, ctx) \ ((ctx)->xoff == 0 && screen_size_x((ctx)->wp->screen) >= (tty)->sx) #define TTY_BLOCK_INTERVAL (100000 /* 100 milliseconds */) #define TTY_BLOCK_START(tty) (1 + ((tty)->sx * (tty)->sy) * 8) #define TTY_BLOCK_STOP(tty) (1 + ((tty)->sx * (tty)->sy) / 8) void tty_create_log(void) { char name[64]; xsnprintf(name, sizeof name, "tmux-out-%ld.log", (long)getpid()); tty_log_fd = open(name, O_WRONLY|O_CREAT|O_TRUNC, 0644); if (tty_log_fd != -1 && fcntl(tty_log_fd, F_SETFD, FD_CLOEXEC) == -1) fatal("fcntl failed"); } int tty_init(struct tty *tty, struct client *c, int fd, char *term) { if (!isatty(fd)) return (-1); memset(tty, 0, sizeof *tty); if (term == NULL || *term == '\0') tty->term_name = xstrdup("unknown"); else tty->term_name = xstrdup(term); tty->fd = fd; tty->client = c; tty->cstyle = 0; tty->ccolour = xstrdup(""); tty->flags = 0; tty->term_flags = 0; tty->term_type = TTY_UNKNOWN; return (0); } void tty_resize(struct tty *tty) { struct client *c = tty->client; 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; } log_debug("%s: %s now %ux%u", __func__, c->name, sx, sy); tty_set_size(tty, sx, sy); tty_invalidate(tty); } void tty_set_size(struct tty *tty, u_int sx, u_int sy) { tty->sx = sx; tty->sy = sy; } static void tty_read_callback(__unused int fd, __unused short events, void *data) { struct tty *tty = data; struct client *c = tty->client; size_t size = EVBUFFER_LENGTH(tty->in); int nread; nread = evbuffer_read(tty->in, tty->fd, -1); if (nread == 0 || nread == -1) { event_del(&tty->event_in); server_client_lost(tty->client); return; } log_debug("%s: read %d bytes (already %zu)", c->name, nread, size); while (tty_keys_next(tty)) ; } static void tty_timer_callback(__unused int fd, __unused short events, void *data) { struct tty *tty = data; struct client *c = tty->client; struct timeval tv = { .tv_usec = TTY_BLOCK_INTERVAL }; log_debug("%s: %zu discarded", c->name, tty->discarded); c->flags |= CLIENT_ALLREDRAWFLAGS; c->discarded += tty->discarded; if (tty->discarded < TTY_BLOCK_STOP(tty)) { tty->flags &= ~TTY_BLOCK; tty_invalidate(tty); return; } tty->discarded = 0; evtimer_add(&tty->timer, &tv); } static int tty_block_maybe(struct tty *tty) { struct client *c = tty->client; size_t size = EVBUFFER_LENGTH(tty->out); struct timeval tv = { .tv_usec = TTY_BLOCK_INTERVAL }; if (size < TTY_BLOCK_START(tty)) return (0); if (tty->flags & TTY_BLOCK) return (1); tty->flags |= TTY_BLOCK; log_debug("%s: can't keep up, %zu discarded", c->name, size); evbuffer_drain(tty->out, size); c->discarded += size; tty->discarded = 0; evtimer_add(&tty->timer, &tv); return (1); } static void tty_write_callback(__unused int fd, __unused short events, void *data) { struct tty *tty = data; struct client *c = tty->client; size_t size = EVBUFFER_LENGTH(tty->out); int nwrite; nwrite = evbuffer_write(tty->out, tty->fd); if (nwrite == -1) return; log_debug("%s: wrote %d bytes (of %zu)", c->name, nwrite, size); if (c->redraw > 0) { if ((size_t)nwrite >= c->redraw) c->redraw = 0; else c->redraw -= nwrite; log_debug("%s: waiting for redraw, %zu bytes left", c->name, c->redraw); } else if (tty_block_maybe(tty)) return; if (EVBUFFER_LENGTH(tty->out) != 0) event_add(&tty->event_out, NULL); } int tty_open(struct tty *tty, char **cause) { tty->term = tty_term_find(tty->term_name, tty->fd, cause); if (tty->term == NULL) { tty_close(tty); return (-1); } tty->flags |= TTY_OPENED; tty->flags &= ~(TTY_NOCURSOR|TTY_FREEZE|TTY_BLOCK|TTY_TIMER); event_set(&tty->event_in, tty->fd, EV_PERSIST|EV_READ, tty_read_callback, tty); tty->in = evbuffer_new(); if (tty->in == NULL) fatal("out of memory"); event_set(&tty->event_out, tty->fd, EV_WRITE, tty_write_callback, tty); tty->out = evbuffer_new(); if (tty->out == NULL) fatal("out of memory"); evtimer_set(&tty->timer, tty_timer_callback, tty); tty_start_tty(tty); tty_keys_build(tty); return (0); } void tty_start_tty(struct tty *tty) { struct client *c = tty->client; struct termios tio; if (tty->fd != -1 && tcgetattr(tty->fd, &tty->tio) == 0) { setblocking(tty->fd, 0); event_add(&tty->event_in, NULL); 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|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_SMKX); tty_putcode(tty, TTYC_CLEAR); if (tty_acs_needed(tty)) { log_debug("%s: using capabilities for ACS", c->name); tty_putcode(tty, TTYC_ENACS); } else log_debug("%s: using UTF-8 for ACS", c->name); tty_putcode(tty, TTYC_CNORM); if (tty_term_has(tty->term, TTYC_KMOUS)) tty_puts(tty, "\033[?1000l\033[?1002l\033[?1006l\033[?1005l"); if (tty_term_flag(tty->term, TTYC_XT)) { if (options_get_number(global_options, "focus-events")) { tty->flags |= TTY_FOCUS; tty_puts(tty, "\033[?1004h"); } tty_puts(tty, "\033[c"); } tty->flags |= TTY_STARTED; tty_invalidate(tty); tty_force_cursor_colour(tty, ""); tty->mouse_drag_flag = 0; tty->mouse_drag_update = NULL; tty->mouse_drag_release = NULL; } void tty_stop_tty(struct tty *tty) { struct winsize ws; if (!(tty->flags & TTY_STARTED)) return; tty->flags &= ~TTY_STARTED; event_del(&tty->timer); tty->flags &= ~TTY_BLOCK; event_del(&tty->event_in); event_del(&tty->event_out); /* * Be flexible about error handling and try not kill the server just * because the fd is invalid. Things like ssh -t can easily leave us * with a dead tty. */ if (ioctl(tty->fd, TIOCGWINSZ, &ws) == -1) return; if (tcsetattr(tty->fd, TCSANOW, &tty->tio) == -1) return; tty_raw(tty, tty_term_string2(tty->term, TTYC_CSR, 0, ws.ws_row - 1)); if (tty_acs_needed(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_SS) && tty->cstyle != 0) { if (tty_term_has(tty->term, TTYC_SE)) tty_raw(tty, tty_term_string(tty->term, TTYC_SE)); else tty_raw(tty, tty_term_string1(tty->term, TTYC_SS, 0)); } if (tty->mode & MODE_BRACKETPASTE) tty_raw(tty, "\033[?2004l"); 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\033[?1002l\033[?1006l\033[?1005l"); if (tty_term_flag(tty->term, TTYC_XT)) { if (tty->flags & TTY_FOCUS) { tty->flags &= ~TTY_FOCUS; tty_raw(tty, "\033[?1004l"); } } if (tty_use_margin(tty)) tty_raw(tty, "\033[?69l"); /* DECLRMM */ tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP)); setblocking(tty->fd, 1); } void tty_close(struct tty *tty) { if (event_initialized(&tty->key_timer)) evtimer_del(&tty->key_timer); tty_stop_tty(tty); if (tty->flags & TTY_OPENED) { evbuffer_free(tty->in); event_del(&tty->event_in); evbuffer_free(tty->out); event_del(&tty->event_out); 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); free(tty->ccolour); free(tty->term_name); } void tty_set_type(struct tty *tty, int type) { tty->term_type = type; if (tty_use_margin(tty)) tty_puts(tty, "\033[?69h"); /* DECLRMM */ } void tty_raw(struct tty *tty, const char *s) { ssize_t n, slen; u_int i; slen = strlen(s); for (i = 0; i < 5; i++) { n = write(tty->fd, s, slen); if (n >= 0) { s += n; slen -= n; if (slen == 0) break; } else if (n == -1 && errno != EAGAIN) break; usleep(100); } } 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_putcode3(struct tty *tty, enum tty_code_code code, int a, int b, int c) { if (a < 0 || b < 0 || c < 0) return; tty_puts(tty, tty_term_string3(tty->term, code, a, b, c)); } 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)); } static void tty_add(struct tty *tty, const char *buf, size_t len) { struct client *c = tty->client; if (tty->flags & TTY_BLOCK) { tty->discarded += len; return; } evbuffer_add(tty->out, buf, len); log_debug("%s: %.*s", c->name, (int)len, buf); c->written += len; if (tty_log_fd != -1) write(tty_log_fd, buf, len); if (tty->flags & TTY_STARTED) event_add(&tty->event_out, NULL); } void tty_puts(struct tty *tty, const char *s) { if (*s != '\0') tty_add(tty, s, strlen(s)); } void tty_putc(struct tty *tty, u_char ch) { const char *acs; if ((tty->term->flags & TERM_EARLYWRAP) && ch >= 0x20 && ch != 0x7f && tty->cy == tty->sy - 1 && tty->cx + 1 >= tty->sx) return; if (tty->cell.attr & GRID_ATTR_CHARSET) { acs = tty_acs_get(tty, ch); if (acs != NULL) tty_add(tty, acs, strlen(acs)); else tty_add(tty, &ch, 1); } else tty_add(tty, &ch, 1); if (ch >= 0x20 && ch != 0x7f) { if (tty->cx >= tty->sx) { tty->cx = 1; if (tty->cy != tty->rlower) tty->cy++; /* * On !xenl terminals, force the cursor position to * where we think it should be after a line wrap - this * means it works on sensible terminals as well. */ if (tty->term->flags & TERM_EARLYWRAP) tty_putcode2(tty, TTYC_CUP, tty->cy, tty->cx); } else tty->cx++; } } void tty_putn(struct tty *tty, const void *buf, size_t len, u_int width) { if ((tty->term->flags & TERM_EARLYWRAP) && tty->cy == tty->sy - 1 && tty->cx + len >= tty->sx) len = tty->sx - tty->cx - 1; tty_add(tty, buf, len); if (tty->cx + width > tty->sx) { tty->cx = (tty->cx + width) - tty->sx; if (tty->cx <= tty->sx) tty->cy++; else tty->cx = tty->cy = UINT_MAX; } else tty->cx += width; } static void tty_set_italics(struct tty *tty) { const char *s; if (tty_term_has(tty->term, TTYC_SITM)) { s = options_get_string(global_options, "default-terminal"); if (strcmp(s, "screen") != 0 && strncmp(s, "screen-", 7) != 0) { tty_putcode(tty, TTYC_SITM); return; } } tty_putcode(tty, TTYC_SMSO); } 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); } static 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_CS, ccolour); free(tty->ccolour); tty->ccolour = xstrdup(ccolour); } void tty_update_mode(struct tty *tty, int mode, struct screen *s) { int changed; if (s != NULL && strcmp(s->ccolour, tty->ccolour) != 0) tty_force_cursor_colour(tty, s->ccolour); if (tty->flags & TTY_NOCURSOR) mode &= ~MODE_CURSOR; changed = mode ^ tty->mode; if (changed & MODE_BLINKING) { if (tty_term_has(tty->term, TTYC_CVVIS)) tty_putcode(tty, TTYC_CVVIS); else tty_putcode(tty, TTYC_CNORM); changed |= MODE_CURSOR; } if (changed & MODE_CURSOR) { if (mode & MODE_CURSOR) tty_putcode(tty, TTYC_CNORM); else tty_putcode(tty, TTYC_CIVIS); } if (s != NULL && tty->cstyle != s->cstyle) { if (tty_term_has(tty->term, TTYC_SS)) { if (s->cstyle == 0 && tty_term_has(tty->term, TTYC_SE)) tty_putcode(tty, TTYC_SE); else tty_putcode1(tty, TTYC_SS, s->cstyle); } tty->cstyle = s->cstyle; } if (changed & ALL_MOUSE_MODES) { if (mode & ALL_MOUSE_MODES) { /* * Enable the SGR (1006) extension unconditionally, as * it is safe from misinterpretation. */ tty_puts(tty, "\033[?1006h"); if (mode & MODE_MOUSE_ALL) 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_ALL) 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"); tty_puts(tty, "\033[?1006l"); } } if (changed & MODE_BRACKETPASTE) { if (mode & MODE_BRACKETPASTE) tty_puts(tty, "\033[?2004h"); else tty_puts(tty, "\033[?2004l"); } tty->mode = mode; } static 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); } } static void tty_repeat_space(struct tty *tty, u_int n) { static char s[500]; if (*s != ' ') memset(s, ' ', sizeof s); while (n > sizeof s) { tty_putn(tty, s, sizeof s, sizeof s); n -= sizeof s; } if (n != 0) tty_putn(tty, s, n, n); } /* Is this window bigger than the terminal? */ int tty_window_bigger(struct tty *tty) { struct client *c = tty->client; struct window *w = c->session->curw->window; return (tty->sx < w->sx || tty->sy - status_line_size(c) < w->sy); } /* What offset should this window be drawn at? */ int tty_window_offset(struct tty *tty, u_int *ox, u_int *oy, u_int *sx, u_int *sy) { *ox = tty->oox; *oy = tty->ooy; *sx = tty->osx; *sy = tty->osy; return (tty->oflag); } /* What offset should this window be drawn at? */ static int tty_window_offset1(struct tty *tty, u_int *ox, u_int *oy, u_int *sx, u_int *sy) { struct client *c = tty->client; struct window *w = c->session->curw->window; struct window_pane *wp = w->active; u_int cx, cy, lines; lines = status_line_size(c); if (tty->sx >= w->sx && tty->sy - lines >= w->sy) { *ox = 0; *oy = 0; *sx = w->sx; *sy = w->sy; c->pan_window = NULL; return (0); } *sx = tty->sx; *sy = tty->sy - lines; if (c->pan_window == w) { if (*sx >= w->sx) c->pan_ox = 0; else if (c->pan_ox + *sx > w->sx) c->pan_ox = w->sx - *sx; *ox = c->pan_ox; if (*sy >= w->sy) c->pan_oy = 0; else if (c->pan_oy + *sy > w->sy) c->pan_oy = w->sy - *sy; *oy = c->pan_oy; return (1); } if (~wp->screen->mode & MODE_CURSOR) { *ox = 0; *oy = 0; } else { cx = wp->xoff + wp->screen->cx; cy = wp->yoff + wp->screen->cy; if (cx < *sx) *ox = 0; else if (cx > w->sx - *sx) *ox = w->sx - *sx; else *ox = cx - *sx / 2; if (cy < *sy) *oy = 0; else if (cy > w->sy - *sy) *oy = w->sy - *sy; else *oy = cy - *sy / 2; } c->pan_window = NULL; return (1); } /* Update stored offsets for a window and redraw if necessary. */ void tty_update_window_offset(struct window *w) { struct client *c; TAILQ_FOREACH(c, &clients, entry) { if (c->session != NULL && c->session->curw->window == w) tty_update_client_offset(c); } } /* Update stored offsets for a client and redraw if necessary. */ void tty_update_client_offset(struct client *c) { u_int ox, oy, sx, sy; if (~c->flags & CLIENT_TERMINAL) return; c->tty.oflag = tty_window_offset1(&c->tty, &ox, &oy, &sx, &sy); if (ox == c->tty.oox && oy == c->tty.ooy && sx == c->tty.osx && sy == c->tty.osy) return; log_debug ("%s: %s offset has changed (%u,%u %ux%u -> %u,%u %ux%u)", __func__, c->name, c->tty.oox, c->tty.ooy, c->tty.osx, c->tty.osy, ox, oy, sx, sy); c->tty.oox = ox; c->tty.ooy = oy; c->tty.osx = sx; c->tty.osy = sy; c->flags |= (CLIENT_REDRAWWINDOW|CLIENT_REDRAWSTATUS); } /* * Is the region large enough to be worth redrawing once later rather than * probably several times now? Currently yes if it is more than 50% of the * pane. */ static int tty_large_region(__unused struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; return (ctx->orlower - ctx->orupper >= screen_size_y(wp->screen) / 2); } /* * Return if BCE is needed but the terminal doesn't have it - it'll need to be * emulated. */ static int tty_fake_bce(const struct tty *tty, struct window_pane *wp, u_int bg) { struct grid_cell gc; if (tty_term_flag(tty->term, TTYC_BCE)) return (0); memcpy(&gc, &grid_default_cell, sizeof gc); if (wp != NULL) tty_default_colours(&gc, wp); if (!COLOUR_DEFAULT(bg) || !COLOUR_DEFAULT(gc.bg)) return (1); return (0); } /* * 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. */ static 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 large, schedule a window redraw. In most cases this is * likely to be followed by some more scrolling. */ if (tty_large_region(tty, ctx)) { 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_pane(tty, ctx, i); } else { for (i = ctx->orupper; i <= ctx->orlower; i++) tty_draw_pane(tty, ctx, i); } } /* Is this position visible in the pane? */ static int tty_is_visible(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, u_int nx, u_int ny) { u_int xoff = ctx->xoff + px, yoff = ctx->yoff + py, lines; if (!ctx->bigger) return (1); if (status_at_line(tty->client) == 0) lines = status_line_size(tty->client); else lines = 0; if (xoff + nx <= ctx->ox || xoff >= ctx->ox + ctx->sx || yoff + ny <= ctx->oy || yoff >= lines + ctx->oy + ctx->sy) return (0); return (1); } /* Clamp line position to visible part of pane. */ static int tty_clamp_line(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, u_int nx, u_int *i, u_int *x, u_int *rx, u_int *ry) { struct window_pane *wp = ctx->wp; u_int xoff = wp->xoff + px; if (!tty_is_visible(tty, ctx, px, py, nx, 1)) return (0); *ry = ctx->yoff + py - ctx->oy; if (xoff >= ctx->ox && xoff + nx <= ctx->ox + ctx->sx) { /* All visible. */ *i = 0; *x = ctx->xoff + px - ctx->ox; *rx = nx; } else if (xoff < ctx->ox && xoff + nx > ctx->ox + ctx->sx) { /* Both left and right not visible. */ *i = ctx->ox; *x = 0; *rx = ctx->sx; } else if (xoff < ctx->ox) { /* Left not visible. */ *i = ctx->ox - (ctx->xoff + px); *x = 0; *rx = nx - *i; } else { /* Right not visible. */ *i = 0; *x = (ctx->xoff + px) - ctx->ox; *rx = ctx->sx - *x; } if (*rx > nx) fatalx("%s: x too big, %u > %u", __func__, *rx, nx); return (1); } /* Clear a line. */ static void tty_clear_line(struct tty *tty, struct window_pane *wp, u_int py, u_int px, u_int nx, u_int bg) { struct client *c = tty->client; log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py); /* Nothing to clear. */ if (nx == 0) return; /* If genuine BCE is available, can try escape sequences. */ if (!tty_fake_bce(tty, wp, bg)) { /* Off the end of the line, use EL if available. */ if (px + nx >= tty->sx && tty_term_has(tty->term, TTYC_EL)) { tty_cursor(tty, px, py); tty_putcode(tty, TTYC_EL); return; } /* At the start of the line. Use EL1. */ if (px == 0 && tty_term_has(tty->term, TTYC_EL1)) { tty_cursor(tty, px + nx - 1, py); tty_putcode(tty, TTYC_EL1); return; } /* Section of line. Use ECH if possible. */ if (tty_term_has(tty->term, TTYC_ECH)) { tty_cursor(tty, px, py); tty_putcode1(tty, TTYC_ECH, nx); return; } } /* Couldn't use an escape sequence, use spaces. */ tty_cursor(tty, px, py); tty_repeat_space(tty, nx); } /* Clear a line, adjusting to visible part of pane. */ static void tty_clear_pane_line(struct tty *tty, const struct tty_ctx *ctx, u_int py, u_int px, u_int nx, u_int bg) { struct client *c = tty->client; u_int i, x, rx, ry; log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py); if (tty_clamp_line(tty, ctx, px, py, nx, &i, &x, &rx, &ry)) tty_clear_line(tty, ctx->wp, ry, x, rx, bg); } /* Clamp area position to visible part of pane. */ static int tty_clamp_area(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, u_int nx, u_int ny, u_int *i, u_int *j, u_int *x, u_int *y, u_int *rx, u_int *ry) { struct window_pane *wp = ctx->wp; u_int xoff = wp->xoff + px, yoff = wp->yoff + py; if (!tty_is_visible(tty, ctx, px, py, nx, ny)) return (0); if (xoff >= ctx->ox && xoff + nx <= ctx->ox + ctx->sx) { /* All visible. */ *i = 0; *x = ctx->xoff + px - ctx->ox; *rx = nx; } else if (xoff < ctx->ox && xoff + nx > ctx->ox + ctx->sx) { /* Both left and right not visible. */ *i = ctx->ox; *x = 0; *rx = ctx->sx; } else if (xoff < ctx->ox) { /* Left not visible. */ *i = ctx->ox - (ctx->xoff + px); *x = 0; *rx = nx - *i; } else { /* Right not visible. */ *i = 0; *x = (ctx->xoff + px) - ctx->ox; *rx = ctx->sx - *x; } if (*rx > nx) fatalx("%s: x too big, %u > %u", __func__, *rx, nx); if (yoff >= ctx->oy && yoff + ny <= ctx->oy + ctx->sy) { /* All visible. */ *j = 0; *y = ctx->yoff + py - ctx->oy; *ry = ny; } else if (yoff < ctx->oy && yoff + ny > ctx->oy + ctx->sy) { /* Both top and bottom not visible. */ *j = ctx->oy; *y = 0; *ry = ctx->sy; } else if (yoff < ctx->oy) { /* Top not visible. */ *j = ctx->oy - (ctx->yoff + py); *y = 0; *ry = ny - *j; } else { /* Bottom not visible. */ *j = 0; *y = (ctx->yoff + py) - ctx->oy; *ry = ctx->sy - *y; } if (*ry > ny) fatalx("%s: y too big, %u > %u", __func__, *ry, ny); return (1); } /* Clear an area, adjusting to visible part of pane. */ static void tty_clear_area(struct tty *tty, struct window_pane *wp, u_int py, u_int ny, u_int px, u_int nx, u_int bg) { struct client *c = tty->client; u_int yy; char tmp[64]; log_debug("%s: %s, %u,%u at %u,%u", __func__, c->name, nx, ny, px, py); /* Nothing to clear. */ if (nx == 0 || ny == 0) return; /* If genuine BCE is available, can try escape sequences. */ if (!tty_fake_bce(tty, wp, bg)) { /* Use ED if clearing off the bottom of the terminal. */ if (px == 0 && px + nx >= tty->sx && py + ny >= tty->sy && tty_term_has(tty->term, TTYC_ED)) { tty_cursor(tty, 0, py); tty_putcode(tty, TTYC_ED); return; } /* * On VT420 compatible terminals we can use DECFRA if the * background colour isn't default (because it doesn't work * after SGR 0). */ if (tty->term_type == TTY_VT420 && !COLOUR_DEFAULT(bg)) { xsnprintf(tmp, sizeof tmp, "\033[32;%u;%u;%u;%u$x", py + 1, px + 1, py + ny, px + nx); tty_puts(tty, tmp); return; } /* Full lines can be scrolled away to clear them. */ if (px == 0 && px + nx >= tty->sx && ny > 2 && tty_term_has(tty->term, TTYC_CSR) && tty_term_has(tty->term, TTYC_INDN)) { tty_region(tty, py, py + ny - 1); tty_margin_off(tty); tty_putcode1(tty, TTYC_INDN, ny); return; } /* * If margins are supported, can just scroll the area off to * clear it. */ if (nx > 2 && ny > 2 && tty_term_has(tty->term, TTYC_CSR) && tty_use_margin(tty) && tty_term_has(tty->term, TTYC_INDN)) { tty_region(tty, py, py + ny - 1); tty_margin(tty, px, px + nx - 1); tty_putcode1(tty, TTYC_INDN, ny); return; } } /* Couldn't use an escape sequence, loop over the lines. */ for (yy = py; yy < py + ny; yy++) tty_clear_line(tty, wp, yy, px, nx, bg); } /* Clear an area in a pane. */ static void tty_clear_pane_area(struct tty *tty, const struct tty_ctx *ctx, u_int py, u_int ny, u_int px, u_int nx, u_int bg) { u_int i, j, x, y, rx, ry; if (tty_clamp_area(tty, ctx, px, py, nx, ny, &i, &j, &x, &y, &rx, &ry)) tty_clear_area(tty, ctx->wp, y, ry, x, rx, bg); } static void tty_draw_pane(struct tty *tty, const struct tty_ctx *ctx, u_int py) { struct window_pane *wp = ctx->wp; struct screen *s = wp->screen; u_int nx = screen_size_x(s), i, x, rx, ry; log_debug("%s: %s %u %d", __func__, tty->client->name, py, ctx->bigger); if (!ctx->bigger) { tty_draw_line(tty, wp, s, 0, py, nx, ctx->xoff, ctx->yoff + py); return; } if (tty_clamp_line(tty, ctx, 0, py, nx, &i, &x, &rx, &ry)) tty_draw_line(tty, wp, s, i, py, rx, x, ry); } static const struct grid_cell * tty_check_codeset(struct tty *tty, const struct grid_cell *gc) { static struct grid_cell new; u_int n; /* Characters less than 0x7f are always fine, no matter what. */ if (gc->data.size == 1 && *gc->data.data < 0x7f) return (gc); /* UTF-8 terminal and a UTF-8 character - fine. */ if (tty->flags & TTY_UTF8) return (gc); /* Replace by the right number of underscores. */ n = gc->data.width; if (n > UTF8_SIZE) n = UTF8_SIZE; memcpy(&new, gc, sizeof new); new.data.size = n; memset(new.data.data, '_', n); return (&new); } void tty_draw_line(struct tty *tty, struct window_pane *wp, struct screen *s, u_int px, u_int py, u_int nx, u_int atx, u_int aty) { struct grid *gd = s->grid; struct grid_cell gc, last; const struct grid_cell *gcp; struct grid_line *gl; u_int i, j, ux, sx, width; int flags, cleared = 0, wrapped = 0; char buf[512]; size_t len; u_int cellsize; log_debug("%s: px=%u py=%u nx=%u atx=%u aty=%u", __func__, px, py, nx, atx, aty); /* * py is the line in the screen to draw. * px is the start x and nx is the width to draw. * atx,aty is the line on the terminal to draw it. */ flags = (tty->flags & TTY_NOCURSOR); tty->flags |= TTY_NOCURSOR; tty_update_mode(tty, tty->mode, s); tty_region_off(tty); tty_margin_off(tty); /* * Clamp the width to cellsize - note this is not cellused, because * there may be empty background cells after it (from BCE). */ sx = screen_size_x(s); if (nx > sx) nx = sx; cellsize = grid_get_line(gd, gd->hsize + py)->cellsize; if (sx > cellsize) sx = cellsize; if (sx > tty->sx) sx = tty->sx; if (sx > nx) sx = nx; ux = 0; if (py == 0) gl = NULL; else gl = grid_get_line(gd, gd->hsize + py - 1); if (wp == NULL || gl == NULL || (~gl->flags & GRID_LINE_WRAPPED) || atx != 0 || tty->cx < tty->sx || nx < tty->sx) { if (nx < tty->sx && atx == 0 && px + sx != nx && tty_term_has(tty->term, TTYC_EL1) && !tty_fake_bce(tty, wp, 8)) { tty_default_attributes(tty, wp, 8); tty_cursor(tty, nx - 1, aty); tty_putcode(tty, TTYC_EL1); cleared = 1; } } else { log_debug("%s: wrapped line %u", __func__, aty); wrapped = 1; } memcpy(&last, &grid_default_cell, sizeof last); len = 0; width = 0; for (i = 0; i < sx; i++) { grid_view_get_cell(gd, px + i, py, &gc); gcp = tty_check_codeset(tty, &gc); if (len != 0 && ((gcp->attr & GRID_ATTR_CHARSET) || gcp->flags != last.flags || gcp->attr != last.attr || gcp->fg != last.fg || gcp->bg != last.bg || gcp->us != last.us || ux + width + gcp->data.width > nx || (sizeof buf) - len < gcp->data.size)) { tty_attributes(tty, &last, wp); if (last.flags & GRID_FLAG_CLEARED) { log_debug("%s: %zu cleared", __func__, len); tty_clear_line(tty, wp, aty, atx + ux, width, last.bg); } else { if (!wrapped || atx != 0 || ux != 0) tty_cursor(tty, atx + ux, aty); tty_putn(tty, buf, len, width); } ux += width; len = 0; width = 0; wrapped = 0; } if (gcp->flags & GRID_FLAG_SELECTED) screen_select_cell(s, &last, gcp); else memcpy(&last, gcp, sizeof last); if (ux + gcp->data.width > nx) { tty_attributes(tty, &last, wp); tty_cursor(tty, atx + ux, aty); for (j = 0; j < gcp->data.width; j++) { if (ux + j > nx) break; tty_putc(tty, ' '); ux++; } } else if (gcp->attr & GRID_ATTR_CHARSET) { tty_attributes(tty, &last, wp); tty_cursor(tty, atx + ux, aty); for (j = 0; j < gcp->data.size; j++) tty_putc(tty, gcp->data.data[j]); ux += gc.data.width; } else { memcpy(buf + len, gcp->data.data, gcp->data.size); len += gcp->data.size; width += gcp->data.width; } } if (len != 0 && ((~last.flags & GRID_FLAG_CLEARED) || last.bg != 8)) { tty_attributes(tty, &last, wp); if (last.flags & GRID_FLAG_CLEARED) { log_debug("%s: %zu cleared (end)", __func__, len); tty_clear_line(tty, wp, aty, atx + ux, width, last.bg); } else { if (!wrapped || atx != 0 || ux != 0) tty_cursor(tty, atx + ux, aty); tty_putn(tty, buf, len, width); } ux += width; } if (!cleared && ux < nx) { log_debug("%s: %u to end of line (%zu cleared)", __func__, nx - ux, len); tty_default_attributes(tty, wp, 8); tty_clear_line(tty, wp, aty, atx + ux, nx - ux, 8); } tty->flags = (tty->flags & ~TTY_NOCURSOR) | flags; tty_update_mode(tty, tty->mode, s); } static int tty_client_ready(struct client *c, struct window_pane *wp) { if (c->session == NULL || c->tty.term == NULL) return (0); if (c->flags & (CLIENT_REDRAWWINDOW|CLIENT_SUSPENDED)) return (0); if (c->tty.flags & TTY_FREEZE) return (0); if (c->session->curw->window != wp->window) return (0); if (wp->layout_cell == NULL) return (0); return (1); } void tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *), struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; struct client *c; if (wp == NULL) return; if (wp->flags & (PANE_REDRAW|PANE_DROP)) return; TAILQ_FOREACH(c, &clients, entry) { if (!tty_client_ready(c, wp)) continue; ctx->bigger = tty_window_offset(&c->tty, &ctx->ox, &ctx->oy, &ctx->sx, &ctx->sy); ctx->xoff = wp->xoff; ctx->yoff = wp->yoff; if (status_at_line(c) == 0) ctx->yoff += status_line_size(c); cmdfn(&c->tty, ctx); } } void tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; if (ctx->bigger || !tty_pane_full_width(tty, ctx) || tty_fake_bce(tty, wp, ctx->bg) || (!tty_term_has(tty->term, TTYC_ICH) && !tty_term_has(tty->term, TTYC_ICH1))) { tty_draw_pane(tty, ctx, ctx->ocy); return; } tty_default_attributes(tty, wp, ctx->bg); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); tty_emulate_repeat(tty, TTYC_ICH, TTYC_ICH1, ctx->num); } void tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; if (ctx->bigger || !tty_pane_full_width(tty, ctx) || tty_fake_bce(tty, wp, ctx->bg) || (!tty_term_has(tty->term, TTYC_DCH) && !tty_term_has(tty->term, TTYC_DCH1))) { tty_draw_pane(tty, ctx, ctx->ocy); return; } tty_default_attributes(tty, wp, ctx->bg); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); tty_emulate_repeat(tty, TTYC_DCH, TTYC_DCH1, ctx->num); } void tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx) { if (ctx->bigger) { tty_draw_pane(tty, ctx, ctx->ocy); return; } tty_default_attributes(tty, ctx->wp, ctx->bg); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); if (tty_term_has(tty->term, TTYC_ECH) && !tty_fake_bce(tty, ctx->wp, 8)) tty_putcode1(tty, TTYC_ECH, ctx->num); else tty_repeat_space(tty, ctx->num); } void tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx) { if (ctx->bigger || !tty_pane_full_width(tty, ctx) || tty_fake_bce(tty, ctx->wp, ctx->bg) || !tty_term_has(tty->term, TTYC_CSR) || !tty_term_has(tty->term, TTYC_IL1) || ctx->wp->sx == 1 || ctx->wp->sy == 1) { tty_redraw_region(tty, ctx); return; } tty_default_attributes(tty, ctx->wp, ctx->bg); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_off(tty); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); tty_emulate_repeat(tty, TTYC_IL, TTYC_IL1, ctx->num); tty->cx = tty->cy = UINT_MAX; } void tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx) { if (ctx->bigger || !tty_pane_full_width(tty, ctx) || tty_fake_bce(tty, ctx->wp, ctx->bg) || !tty_term_has(tty->term, TTYC_CSR) || !tty_term_has(tty->term, TTYC_DL1) || ctx->wp->sx == 1 || ctx->wp->sy == 1) { tty_redraw_region(tty, ctx); return; } tty_default_attributes(tty, ctx->wp, ctx->bg); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_off(tty); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); tty_emulate_repeat(tty, TTYC_DL, TTYC_DL1, ctx->num); tty->cx = tty->cy = UINT_MAX; } void tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; u_int nx; tty_default_attributes(tty, wp, ctx->bg); nx = screen_size_x(wp->screen); tty_clear_pane_line(tty, ctx, ctx->ocy, 0, nx, ctx->bg); } void tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; u_int nx; tty_default_attributes(tty, wp, ctx->bg); nx = screen_size_x(wp->screen) - ctx->ocx; tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, nx, ctx->bg); } void tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; tty_default_attributes(tty, wp, ctx->bg); tty_clear_pane_line(tty, ctx, ctx->ocy, 0, ctx->ocx + 1, ctx->bg); } void tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; if (ctx->ocy != ctx->orupper) return; if (ctx->bigger || (!tty_pane_full_width(tty, ctx) && !tty_use_margin(tty)) || tty_fake_bce(tty, wp, 8) || !tty_term_has(tty->term, TTYC_CSR) || (!tty_term_has(tty->term, TTYC_RI) && !tty_term_has(tty->term, TTYC_RIN)) || ctx->wp->sx == 1 || ctx->wp->sy == 1) { tty_redraw_region(tty, ctx); return; } tty_default_attributes(tty, wp, ctx->bg); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_pane(tty, ctx); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper); if (tty_term_has(tty->term, TTYC_RI)) tty_putcode(tty, TTYC_RI); else tty_putcode1(tty, TTYC_RIN, 1); } void tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; if (ctx->ocy != ctx->orlower) return; if (ctx->bigger || (!tty_pane_full_width(tty, ctx) && !tty_use_margin(tty)) || tty_fake_bce(tty, wp, 8) || !tty_term_has(tty->term, TTYC_CSR) || wp->sx == 1 || wp->sy == 1) { tty_redraw_region(tty, ctx); return; } tty_default_attributes(tty, wp, ctx->bg); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_pane(tty, ctx); /* * If we want to wrap a pane while using margins, the cursor needs to * be exactly on the right of the region. If the cursor is entirely off * the edge - move it back to the right. Some terminals are funny about * this and insert extra spaces, so only use the right if margins are * enabled. */ if (ctx->xoff + ctx->ocx > tty->rright) { if (!tty_use_margin(tty)) tty_cursor(tty, 0, ctx->yoff + ctx->ocy); else tty_cursor(tty, tty->rright, ctx->yoff + ctx->ocy); } else tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); tty_putc(tty, '\n'); } void tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; u_int i; if (ctx->bigger || (!tty_pane_full_width(tty, ctx) && !tty_use_margin(tty)) || tty_fake_bce(tty, wp, 8) || !tty_term_has(tty->term, TTYC_CSR) || wp->sx == 1 || wp->sy == 1) { tty_redraw_region(tty, ctx); return; } tty_default_attributes(tty, wp, ctx->bg); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_pane(tty, ctx); if (ctx->num == 1 || !tty_term_has(tty->term, TTYC_INDN)) { if (!tty_use_margin(tty)) tty_cursor(tty, 0, tty->rlower); else tty_cursor(tty, tty->rright, tty->rlower); for (i = 0; i < ctx->num; i++) tty_putc(tty, '\n'); } else { tty_cursor(tty, 0, tty->cy); tty_putcode1(tty, TTYC_INDN, ctx->num); } } void tty_cmd_scrolldown(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; u_int i; if (ctx->bigger || (!tty_pane_full_width(tty, ctx) && !tty_use_margin(tty)) || tty_fake_bce(tty, wp, 8) || !tty_term_has(tty->term, TTYC_CSR) || (!tty_term_has(tty->term, TTYC_RI) && !tty_term_has(tty->term, TTYC_RIN)) || wp->sx == 1 || wp->sy == 1) { tty_redraw_region(tty, ctx); return; } tty_default_attributes(tty, wp, ctx->bg); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_pane(tty, ctx); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper); if (tty_term_has(tty->term, TTYC_RIN)) tty_putcode1(tty, TTYC_RIN, ctx->num); else { for (i = 0; i < ctx->num; i++) tty_putcode(tty, TTYC_RI); } } void tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; u_int px, py, nx, ny; tty_default_attributes(tty, wp, ctx->bg); tty_region_pane(tty, ctx, 0, screen_size_y(wp->screen) - 1); tty_margin_off(tty); px = 0; nx = screen_size_x(wp->screen); py = ctx->ocy + 1; ny = screen_size_y(wp->screen) - ctx->ocy - 1; tty_clear_pane_area(tty, ctx, py, ny, px, nx, ctx->bg); px = ctx->ocx; nx = screen_size_x(wp->screen) - ctx->ocx; py = ctx->ocy; tty_clear_pane_line(tty, ctx, py, px, nx, ctx->bg); } void tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; u_int px, py, nx, ny; tty_default_attributes(tty, wp, ctx->bg); tty_region_pane(tty, ctx, 0, screen_size_y(wp->screen) - 1); tty_margin_off(tty); px = 0; nx = screen_size_x(wp->screen); py = 0; ny = ctx->ocy; tty_clear_pane_area(tty, ctx, py, ny, px, nx, ctx->bg); px = 0; nx = ctx->ocx + 1; py = ctx->ocy; tty_clear_pane_line(tty, ctx, py, px, nx, ctx->bg); } void tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; u_int px, py, nx, ny; tty_default_attributes(tty, wp, ctx->bg); tty_region_pane(tty, ctx, 0, screen_size_y(wp->screen) - 1); tty_margin_off(tty); px = 0; nx = screen_size_x(wp->screen); py = 0; ny = screen_size_y(wp->screen); tty_clear_pane_area(tty, ctx, py, ny, px, nx, ctx->bg); } 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; if (ctx->bigger) { wp->flags |= PANE_REDRAW; return; } tty_attributes(tty, &grid_default_cell, wp); tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); tty_margin_off(tty); 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) { if (!tty_is_visible(tty, ctx, ctx->ocx, ctx->ocy, 1, 1)) return; if (ctx->xoff + ctx->ocx - ctx->ox > tty->sx - 1 && ctx->ocy == ctx->orlower && tty_pane_full_width(tty, ctx)) tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_margin_off(tty); tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy); tty_cell(tty, ctx->cell, ctx->wp); } void tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; if (!tty_is_visible(tty, ctx, ctx->ocx, ctx->ocy, ctx->num, 1)) return; if (ctx->bigger && (ctx->xoff + ctx->ocx < ctx->ox || ctx->xoff + ctx->ocx + ctx->num > ctx->ox + ctx->sx)) { if (!ctx->wrapped || !tty_pane_full_width(tty, ctx) || (tty->term->flags & TERM_EARLYWRAP) || ctx->xoff + ctx->ocx != 0 || ctx->yoff + ctx->ocy != tty->cy + 1 || tty->cx < tty->sx || tty->cy == tty->rlower) tty_draw_pane(tty, ctx, ctx->ocy); else wp->flags |= PANE_REDRAW; return; } tty_margin_off(tty); tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy); tty_attributes(tty, ctx->cell, ctx->wp); tty_putn(tty, ctx->ptr, ctx->num, ctx->num); } 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); free(buf); } void tty_cmd_rawstring(struct tty *tty, const struct tty_ctx *ctx) { tty_add(tty, ctx->ptr, ctx->num); tty_invalidate(tty); } static void tty_cell(struct tty *tty, const struct grid_cell *gc, struct window_pane *wp) { const struct grid_cell *gcp; /* 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, wp); /* Get the cell and if ASCII write with putc to do ACS translation. */ gcp = tty_check_codeset(tty, gc); if (gcp->data.size == 1) { if (*gcp->data.data < 0x20 || *gcp->data.data == 0x7f) return; tty_putc(tty, *gcp->data.data); return; } /* Write the data. */ tty_putn(tty, gcp->data.data, gcp->data.size, gcp->data.width); } void tty_reset(struct tty *tty) { struct grid_cell *gc = &tty->cell; if (!grid_cells_equal(gc, &grid_default_cell)) { if ((gc->attr & GRID_ATTR_CHARSET) && tty_acs_needed(tty)) tty_putcode(tty, TTYC_RMACS); tty_putcode(tty, TTYC_SGR0); memcpy(gc, &grid_default_cell, sizeof *gc); } memcpy(&tty->last_cell, &grid_default_cell, sizeof tty->last_cell); tty->last_wp = -1; } static void tty_invalidate(struct tty *tty) { memcpy(&tty->cell, &grid_default_cell, sizeof tty->cell); memcpy(&tty->last_cell, &grid_default_cell, sizeof tty->last_cell); tty->last_wp = -1; tty->cx = tty->cy = UINT_MAX; tty->rupper = tty->rleft = UINT_MAX; tty->rlower = tty->rright = UINT_MAX; if (tty->flags & TTY_STARTED) { if (tty_use_margin(tty)) tty_puts(tty, "\033[?69h"); /* DECLRMM */ tty_putcode(tty, TTYC_SGR0); tty->mode = ALL_MODES; tty_update_mode(tty, MODE_CURSOR, NULL); tty_cursor(tty, 0, 0); tty_region_off(tty); tty_margin_off(tty); } else tty->mode = MODE_CURSOR; } /* Turn off margin. */ void tty_region_off(struct tty *tty) { tty_region(tty, 0, tty->sy - 1); } /* Set region inside pane. */ static void tty_region_pane(struct tty *tty, const struct tty_ctx *ctx, u_int rupper, u_int rlower) { tty_region(tty, ctx->yoff + rupper - ctx->oy, ctx->yoff + rlower - ctx->oy); } /* Set region at absolute position. */ static 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->cx = tty->cy = UINT_MAX; } /* Turn off margin. */ void tty_margin_off(struct tty *tty) { tty_margin(tty, 0, tty->sx - 1); } /* Set margin inside pane. */ static void tty_margin_pane(struct tty *tty, const struct tty_ctx *ctx) { tty_margin(tty, ctx->xoff - ctx->ox, ctx->xoff + ctx->wp->sx - 1 - ctx->ox); } /* Set margin at absolute position. */ static void tty_margin(struct tty *tty, u_int rleft, u_int rright) { char s[64]; if (!tty_use_margin(tty)) return; if (tty->rleft == rleft && tty->rright == rright) return; tty_putcode2(tty, TTYC_CSR, tty->rupper, tty->rlower); tty->rleft = rleft; tty->rright = rright; if (rleft == 0 && rright == tty->sx - 1) snprintf(s, sizeof s, "\033[s"); else snprintf(s, sizeof s, "\033[%u;%us", rleft + 1, rright + 1); tty_puts(tty, s); tty->cx = tty->cy = UINT_MAX; } /* * Move the cursor, unless it would wrap itself when the next character is * printed. */ static void tty_cursor_pane_unless_wrap(struct tty *tty, const struct tty_ctx *ctx, u_int cx, u_int cy) { if (!ctx->wrapped || !tty_pane_full_width(tty, ctx) || (tty->term->flags & TERM_EARLYWRAP) || ctx->xoff + cx != 0 || ctx->yoff + cy != tty->cy + 1 || tty->cx < tty->sx || tty->cy == tty->rlower) tty_cursor_pane(tty, ctx, cx, cy); else log_debug("%s: will wrap at %u,%u", __func__, tty->cx, tty->cy); } /* Move cursor inside pane. */ static void tty_cursor_pane(struct tty *tty, const struct tty_ctx *ctx, u_int cx, u_int cy) { tty_cursor(tty, ctx->xoff + cx - ctx->ox, ctx->yoff + cy - ctx->oy); } /* 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_use_margin(tty) || tty->rleft == 0)) { 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_use_margin(tty) || tty->rleft == 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)) { if (change == 2 && tty_term_has(term, TTYC_CUB1)) { tty_putcode(tty, TTYC_CUB1); tty_putcode(tty, TTYC_CUB1); goto out; } 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 window_pane *wp) { struct grid_cell *tc = &tty->cell, gc2; int changed; /* Ignore cell if it is the same as the last one. */ if (wp != NULL && (int)wp->id == tty->last_wp && ~(wp->flags & PANE_STYLECHANGED) && gc->attr == tty->last_cell.attr && gc->fg == tty->last_cell.fg && gc->bg == tty->last_cell.bg && gc->us == tty->last_cell.us) return; tty->last_wp = (wp != NULL ? (int)wp->id : -1); memcpy(&tty->last_cell, gc, sizeof tty->last_cell); /* Copy cell and update default colours. */ memcpy(&gc2, gc, sizeof gc2); if (wp != NULL) tty_default_colours(&gc2, wp); /* * 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 && !COLOUR_DEFAULT(gc2.fg)) gc2.attr &= ~GRID_ATTR_REVERSE; } else { if (gc2.bg != 0 && !COLOUR_DEFAULT(gc2.bg)) gc2.attr |= GRID_ATTR_REVERSE; } } /* Fix up the colours if necessary. */ tty_check_fg(tty, wp, &gc2); tty_check_bg(tty, wp, &gc2); tty_check_us(tty, wp, &gc2); /* * If any bits are being cleared or the underline colour is now default, * reset everything. */ if ((tc->attr & ~gc2.attr) || (tc->us != gc2.us && gc2.us == 0)) tty_reset(tty); /* * Set the colours. This may call tty_reset() (so it comes next) and * may add to (NOT remove) the desired attributes. */ 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) tty_set_italics(tty); if (changed & GRID_ATTR_ALL_UNDERSCORE) { if ((changed & GRID_ATTR_UNDERSCORE) || !tty_term_has(tty->term, TTYC_SMULX)) tty_putcode(tty, TTYC_SMUL); else if (changed & GRID_ATTR_UNDERSCORE_2) tty_putcode1(tty, TTYC_SMULX, 2); else if (changed & GRID_ATTR_UNDERSCORE_3) tty_putcode1(tty, TTYC_SMULX, 3); else if (changed & GRID_ATTR_UNDERSCORE_4) tty_putcode1(tty, TTYC_SMULX, 4); else if (changed & GRID_ATTR_UNDERSCORE_5) tty_putcode1(tty, TTYC_SMULX, 5); } 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_STRIKETHROUGH) tty_putcode(tty, TTYC_SMXX); if (changed & GRID_ATTR_OVERLINE) tty_putcode(tty, TTYC_SMOL); if ((changed & GRID_ATTR_CHARSET) && tty_acs_needed(tty)) tty_putcode(tty, TTYC_SMACS); } static void tty_colours(struct tty *tty, const struct grid_cell *gc) { struct grid_cell *tc = &tty->cell; int have_ax; /* No changes? Nothing is necessary. */ if (gc->fg == tc->fg && gc->bg == tc->bg && gc->us == tc->us) 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. */ if (COLOUR_DEFAULT(gc->fg) || COLOUR_DEFAULT(gc->bg)) { /* * 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_flag(tty->term, TTYC_AX); if (!have_ax && tty_term_has(tty->term, TTYC_OP)) tty_reset(tty); else { if (COLOUR_DEFAULT(gc->fg) && !COLOUR_DEFAULT(tc->fg)) { if (have_ax) tty_puts(tty, "\033[39m"); else if (tc->fg != 7) tty_putcode1(tty, TTYC_SETAF, 7); tc->fg = gc->fg; } if (COLOUR_DEFAULT(gc->bg) && !COLOUR_DEFAULT(tc->bg)) { if (have_ax) tty_puts(tty, "\033[49m"); else if (tc->bg != 0) tty_putcode1(tty, TTYC_SETAB, 0); tc->bg = gc->bg; } } } /* Set the foreground colour. */ if (!COLOUR_DEFAULT(gc->fg) && gc->fg != tc->fg) tty_colours_fg(tty, gc); /* * Set the background colour. This must come after the foreground as * tty_colour_fg() can call tty_reset(). */ if (!COLOUR_DEFAULT(gc->bg) && gc->bg != tc->bg) tty_colours_bg(tty, gc); /* Set the underscore color. */ if (gc->us != tc->us) tty_colours_us(tty, gc); } static void tty_check_fg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc) { u_char r, g, b; u_int colours; int c; /* * Perform substitution if this pane has a palette. If the bright * attribute is set, use the bright entry in the palette by changing to * the aixterm colour. */ if (~gc->flags & GRID_FLAG_NOPALETTE) { c = gc->fg; if (c < 8 && gc->attr & GRID_ATTR_BRIGHT) c += 90; if ((c = window_pane_get_palette(wp, c)) != -1) gc->fg = c; } /* Is this a 24-bit colour? */ if (gc->fg & COLOUR_FLAG_RGB) { /* Not a 24-bit terminal? Translate to 256-colour palette. */ if (!tty_term_has(tty->term, TTYC_SETRGBF)) { colour_split_rgb(gc->fg, &r, &g, &b); gc->fg = colour_find_rgb(r, g, b); } else return; } /* How many colours does this terminal have? */ if ((tty->term->flags|tty->term_flags) & TERM_256COLOURS) colours = 256; else colours = tty_term_number(tty->term, TTYC_COLORS); /* Is this a 256-colour colour? */ if (gc->fg & COLOUR_FLAG_256) { /* And not a 256 colour mode? */ if (colours != 256) { gc->fg = colour_256to16(gc->fg); if (gc->fg & 8) { gc->fg &= 7; if (colours >= 16) gc->fg += 90; else gc->attr |= GRID_ATTR_BRIGHT; } else gc->attr &= ~GRID_ATTR_BRIGHT; } return; } /* Is this an aixterm colour? */ if (gc->fg >= 90 && gc->fg <= 97 && colours < 16) { gc->fg -= 90; gc->attr |= GRID_ATTR_BRIGHT; } } static void tty_check_bg(struct tty *tty, struct window_pane *wp, struct grid_cell *gc) { u_char r, g, b; u_int colours; int c; /* Perform substitution if this pane has a palette. */ if (~gc->flags & GRID_FLAG_NOPALETTE) { if ((c = window_pane_get_palette(wp, gc->bg)) != -1) gc->bg = c; } /* Is this a 24-bit colour? */ if (gc->bg & COLOUR_FLAG_RGB) { /* Not a 24-bit terminal? Translate to 256-colour palette. */ if (!tty_term_has(tty->term, TTYC_SETRGBB)) { colour_split_rgb(gc->bg, &r, &g, &b); gc->bg = colour_find_rgb(r, g, b); } else return; } /* How many colours does this terminal have? */ if ((tty->term->flags|tty->term_flags) & TERM_256COLOURS) colours = 256; else colours = tty_term_number(tty->term, TTYC_COLORS); /* Is this a 256-colour colour? */ if (gc->bg & COLOUR_FLAG_256) { /* * 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 (colours != 256) { gc->bg = colour_256to16(gc->bg); if (gc->bg & 8) { gc->bg &= 7; if (colours >= 16) gc->bg += 90; } } return; } /* Is this an aixterm colour? */ if (gc->bg >= 90 && gc->bg <= 97 && colours < 16) gc->bg -= 90; } static void tty_check_us(__unused struct tty *tty, struct window_pane *wp, struct grid_cell *gc) { int c; /* Perform substitution if this pane has a palette. */ if (~gc->flags & GRID_FLAG_NOPALETTE) { if ((c = window_pane_get_palette(wp, gc->us)) != -1) gc->us = c; } /* Underscore colour is set as RGB so convert a 256 colour to RGB. */ if (gc->us & COLOUR_FLAG_256) gc->us = colour_256toRGB (gc->us); } static void tty_colours_fg(struct tty *tty, const struct grid_cell *gc) { struct grid_cell *tc = &tty->cell; char s[32]; /* Is this a 24-bit or 256-colour colour? */ if (gc->fg & COLOUR_FLAG_RGB || gc->fg & COLOUR_FLAG_256) { if (tty_try_colour(tty, gc->fg, "38") == 0) goto save_fg; /* Should not get here, already converted in tty_check_fg. */ return; } /* Is this an aixterm bright colour? */ if (gc->fg >= 90 && gc->fg <= 97) { if (tty->term_flags & TERM_256COLOURS) { xsnprintf(s, sizeof s, "\033[%dm", gc->fg); tty_puts(tty, s); } else tty_putcode1(tty, TTYC_SETAF, gc->fg - 90 + 8); goto save_fg; } /* Otherwise set the foreground colour. */ tty_putcode1(tty, TTYC_SETAF, gc->fg); save_fg: /* Save the new values in the terminal current cell. */ tc->fg = gc->fg; } static void tty_colours_bg(struct tty *tty, const struct grid_cell *gc) { struct grid_cell *tc = &tty->cell; char s[32]; /* Is this a 24-bit or 256-colour colour? */ if (gc->bg & COLOUR_FLAG_RGB || gc->bg & COLOUR_FLAG_256) { if (tty_try_colour(tty, gc->bg, "48") == 0) goto save_bg; /* Should not get here, already converted in tty_check_bg. */ return; } /* Is this an aixterm bright colour? */ if (gc->bg >= 90 && gc->bg <= 97) { if (tty->term_flags & TERM_256COLOURS) { xsnprintf(s, sizeof s, "\033[%dm", gc->bg + 10); tty_puts(tty, s); } else tty_putcode1(tty, TTYC_SETAB, gc->bg - 90 + 8); goto save_bg; } /* Otherwise set the background colour. */ tty_putcode1(tty, TTYC_SETAB, gc->bg); save_bg: /* Save the new values in the terminal current cell. */ tc->bg = gc->bg; } static void tty_colours_us(struct tty *tty, const struct grid_cell *gc) { struct grid_cell *tc = &tty->cell; u_int c; u_char r, g, b; /* Must be an RGB colour - this should never happen. */ if (~gc->us & COLOUR_FLAG_RGB) return; /* * Setulc follows the ncurses(3) one argument "direct colour" * capability format. Calculate the colour value. */ colour_split_rgb(gc->us, &r, &g, &b); c = (65536 * r) + (256 * g) + b; /* Write the colour. */ tty_putcode1(tty, TTYC_SETULC, c); /* Save the new values in the terminal current cell. */ tc->us = gc->us; } static int tty_try_colour(struct tty *tty, int colour, const char *type) { u_char r, g, b; char s[32]; if (colour & COLOUR_FLAG_256) { /* * If the user has specified -2 to the client (meaning * TERM_256COLOURS is set), setaf and setab may not work (or * they may not want to use them), so send the usual sequence. * * Also if RGB is set, setaf and setab do not support the 256 * colour palette so use the sequences directly there too. */ if ((tty->term_flags & TERM_256COLOURS) || tty_term_has(tty->term, TTYC_RGB)) goto fallback_256; /* * If the terminfo entry has 256 colours and setaf and setab * exist, assume that they work correctly. */ if (tty->term->flags & TERM_256COLOURS) { if (*type == '3') { if (!tty_term_has(tty->term, TTYC_SETAF)) goto fallback_256; tty_putcode1(tty, TTYC_SETAF, colour & 0xff); } else { if (!tty_term_has(tty->term, TTYC_SETAB)) goto fallback_256; tty_putcode1(tty, TTYC_SETAB, colour & 0xff); } return (0); } goto fallback_256; } if (colour & COLOUR_FLAG_RGB) { if (*type == '3') { if (!tty_term_has(tty->term, TTYC_SETRGBF)) return (-1); colour_split_rgb(colour & 0xffffff, &r, &g, &b); tty_putcode3(tty, TTYC_SETRGBF, r, g, b); } else { if (!tty_term_has(tty->term, TTYC_SETRGBB)) return (-1); colour_split_rgb(colour & 0xffffff, &r, &g, &b); tty_putcode3(tty, TTYC_SETRGBB, r, g, b); } return (0); } return (-1); fallback_256: xsnprintf(s, sizeof s, "\033[%s;5;%dm", type, colour & 0xff); log_debug("%s: 256 colour fallback: %s", tty->client->name, s); tty_puts(tty, s); return (0); } static void tty_default_colours(struct grid_cell *gc, struct window_pane *wp) { struct options *oo = wp->options; struct style *style, *active_style; int c; if (wp->flags & PANE_STYLECHANGED) { wp->flags &= ~PANE_STYLECHANGED; active_style = options_get_style(oo, "window-active-style"); style = options_get_style(oo, "window-style"); style_copy(&wp->cached_active_style, active_style); style_copy(&wp->cached_style, style); } else { active_style = &wp->cached_active_style; style = &wp->cached_style; } if (gc->fg == 8) { if (wp == wp->window->active && active_style->gc.fg != 8) gc->fg = active_style->gc.fg; else gc->fg = style->gc.fg; if (gc->fg != 8) { c = window_pane_get_palette(wp, gc->fg); if (c != -1) gc->fg = c; } } if (gc->bg == 8) { if (wp == wp->window->active && active_style->gc.bg != 8) gc->bg = active_style->gc.bg; else gc->bg = style->gc.bg; if (gc->bg != 8) { c = window_pane_get_palette(wp, gc->bg); if (c != -1) gc->bg = c; } } } static void tty_default_attributes(struct tty *tty, struct window_pane *wp, u_int bg) { static struct grid_cell gc; memcpy(&gc, &grid_default_cell, sizeof gc); gc.bg = bg; tty_attributes(tty, &gc, wp); } tmux-3.0a/utf8.c100644 001750 001750 00000022450 13570677043 0007265/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "tmux.h" static int utf8_width(wchar_t); /* Set a single character. */ void utf8_set(struct utf8_data *ud, u_char ch) { static const struct utf8_data empty = { { 0 }, 1, 1, 1 }; memcpy(ud, &empty, sizeof *ud); *ud->data = ch; } /* Copy UTF-8 character. */ void utf8_copy(struct utf8_data *to, const struct utf8_data *from) { u_int i; memcpy(to, from, sizeof *to); for (i = to->size; i < sizeof to->data; i++) to->data[i] = '\0'; } /* * 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 */ enum utf8_state utf8_open(struct utf8_data *ud, u_char ch) { memset(ud, 0, sizeof *ud); if (ch >= 0xc2 && ch <= 0xdf) ud->size = 2; else if (ch >= 0xe0 && ch <= 0xef) ud->size = 3; else if (ch >= 0xf0 && ch <= 0xf4) ud->size = 4; else return (UTF8_ERROR); utf8_append(ud, ch); return (UTF8_MORE); } /* Append character to UTF-8, closing if finished. */ enum utf8_state utf8_append(struct utf8_data *ud, u_char ch) { wchar_t wc; int width; if (ud->have >= ud->size) fatalx("UTF-8 character overflow"); if (ud->size > sizeof ud->data) fatalx("UTF-8 character size too large"); if (ud->have != 0 && (ch & 0xc0) != 0x80) ud->width = 0xff; ud->data[ud->have++] = ch; if (ud->have != ud->size) return (UTF8_MORE); if (ud->width == 0xff) return (UTF8_ERROR); if (utf8_combine(ud, &wc) != UTF8_DONE) return (UTF8_ERROR); if ((width = utf8_width(wc)) < 0) return (UTF8_ERROR); ud->width = width; return (UTF8_DONE); } /* Get width of Unicode character. */ static int utf8_width(wchar_t wc) { int width; #ifdef HAVE_UTF8PROC width = utf8proc_wcwidth(wc); #else width = wcwidth(wc); #endif if (width < 0 || width > 0xff) { log_debug("Unicode %04lx, wcwidth() %d", (long)wc, width); #ifndef __OpenBSD__ /* * Many platforms (particularly and inevitably OS X) have no * width for relatively common characters (wcwidth() returns * -1); assume width 1 in this case. This will be wrong for * genuinely nonprintable characters, but they should be * rare. We may pass through stuff that ideally we would block, * but this is no worse than sending the same to the terminal * without tmux. */ if (width < 0) return (1); #endif return (-1); } return (width); } /* Combine UTF-8 into Unicode. */ enum utf8_state utf8_combine(const struct utf8_data *ud, wchar_t *wc) { #ifdef HAVE_UTF8PROC switch (utf8proc_mbtowc(wc, ud->data, ud->size)) { #else switch (mbtowc(wc, ud->data, ud->size)) { #endif case -1: log_debug("UTF-8 %.*s, mbtowc() %d", (int)ud->size, ud->data, errno); mbtowc(NULL, NULL, MB_CUR_MAX); return (UTF8_ERROR); case 0: return (UTF8_ERROR); default: return (UTF8_DONE); } } /* Split Unicode into UTF-8. */ enum utf8_state utf8_split(wchar_t wc, struct utf8_data *ud) { char s[MB_LEN_MAX]; int slen; #ifdef HAVE_UTF8PROC slen = utf8proc_wctomb(s, wc); #else slen = wctomb(s, wc); #endif if (slen <= 0 || slen > (int)sizeof ud->data) return (UTF8_ERROR); memcpy(ud->data, s, slen); ud->size = slen; ud->width = utf8_width(wc); return (UTF8_DONE); } /* * Encode len characters from src into dst, which is guaranteed to have four * bytes available for each character from src (for \abc or UTF-8) plus space * for \0. */ int utf8_strvis(char *dst, const char *src, size_t len, int flag) { struct utf8_data ud; const char *start, *end; enum utf8_state more; size_t i; start = dst; end = src + len; while (src < end) { if ((more = utf8_open(&ud, *src)) == UTF8_MORE) { while (++src < end && more == UTF8_MORE) more = utf8_append(&ud, *src); if (more == UTF8_DONE) { /* UTF-8 character finished. */ for (i = 0; i < ud.size; i++) *dst++ = ud.data[i]; continue; } /* Not a complete, valid UTF-8 character. */ src -= ud.have; } if (src[0] == '$' && src < end - 1) { if (isalpha((u_char)src[1]) || src[1] == '_' || src[1] == '{') *dst++ = '\\'; *dst++ = '$'; } else if (src < end - 1) dst = vis(dst, src[0], flag, src[1]); else if (src < end) dst = vis(dst, src[0], flag, '\0'); src++; } *dst = '\0'; return (dst - start); } /* Same as utf8_strvis but allocate the buffer. */ int utf8_stravis(char **dst, const char *src, int flag) { char *buf; int len; buf = xreallocarray(NULL, 4, strlen(src) + 1); len = utf8_strvis(buf, src, strlen(src), flag); *dst = xrealloc(buf, len + 1); return (len); } /* Does this string contain anything that isn't valid UTF-8? */ int utf8_isvalid(const char *s) { struct utf8_data ud; const char *end; enum utf8_state more; end = s + strlen(s); while (s < end) { if ((more = utf8_open(&ud, *s)) == UTF8_MORE) { while (++s < end && more == UTF8_MORE) more = utf8_append(&ud, *s); if (more == UTF8_DONE) continue; return (0); } if (*s < 0x20 || *s > 0x7e) return (0); s++; } return (1); } /* * Sanitize a string, changing any UTF-8 characters to '_'. Caller should free * the returned string. Anything not valid printable ASCII or UTF-8 is * stripped. */ char * utf8_sanitize(const char *src) { char *dst; size_t n; enum utf8_state more; struct utf8_data ud; u_int i; dst = NULL; n = 0; while (*src != '\0') { dst = xreallocarray(dst, n + 1, sizeof *dst); if ((more = utf8_open(&ud, *src)) == UTF8_MORE) { while (*++src != '\0' && more == UTF8_MORE) more = utf8_append(&ud, *src); if (more == UTF8_DONE) { dst = xreallocarray(dst, n + ud.width, sizeof *dst); for (i = 0; i < ud.width; i++) dst[n++] = '_'; continue; } src -= ud.have; } if (*src > 0x1f && *src < 0x7f) dst[n++] = *src; else dst[n++] = '_'; src++; } dst = xreallocarray(dst, n + 1, sizeof *dst); dst[n] = '\0'; return (dst); } /* Get UTF-8 buffer length. */ size_t utf8_strlen(const struct utf8_data *s) { size_t i; for (i = 0; s[i].size != 0; i++) /* nothing */; return (i); } /* Get UTF-8 string width. */ u_int utf8_strwidth(const struct utf8_data *s, ssize_t n) { ssize_t i; u_int width; width = 0; for (i = 0; s[i].size != 0; i++) { if (n != -1 && n == i) break; width += s[i].width; } return (width); } /* * Convert a string into a buffer of UTF-8 characters. Terminated by size == 0. * Caller frees. */ struct utf8_data * utf8_fromcstr(const char *src) { struct utf8_data *dst; size_t n; enum utf8_state more; dst = NULL; n = 0; while (*src != '\0') { dst = xreallocarray(dst, n + 1, sizeof *dst); if ((more = utf8_open(&dst[n], *src)) == UTF8_MORE) { while (*++src != '\0' && more == UTF8_MORE) more = utf8_append(&dst[n], *src); if (more == UTF8_DONE) { n++; continue; } src -= dst[n].have; } utf8_set(&dst[n], *src); n++; src++; } dst = xreallocarray(dst, n + 1, sizeof *dst); dst[n].size = 0; return (dst); } /* Convert from a buffer of UTF-8 characters into a string. Caller frees. */ char * utf8_tocstr(struct utf8_data *src) { char *dst; size_t n; dst = NULL; n = 0; for(; src->size != 0; src++) { dst = xreallocarray(dst, n + src->size, 1); memcpy(dst + n, src->data, src->size); n += src->size; } dst = xreallocarray(dst, n + 1, 1); dst[n] = '\0'; return (dst); } /* Get width of UTF-8 string. */ u_int utf8_cstrwidth(const char *s) { struct utf8_data tmp; u_int width; enum utf8_state more; width = 0; while (*s != '\0') { if ((more = utf8_open(&tmp, *s)) == UTF8_MORE) { while (*++s != '\0' && more == UTF8_MORE) more = utf8_append(&tmp, *s); if (more == UTF8_DONE) { width += tmp.width; continue; } s -= tmp.have; } if (*s > 0x1f && *s != 0x7f) width++; s++; } return (width); } /* Pad UTF-8 string to width. Caller frees. */ char * utf8_padcstr(const char *s, u_int width) { size_t slen; char *out; u_int n, i; n = utf8_cstrwidth(s); if (n >= width) return (xstrdup(s)); slen = strlen(s); out = xmalloc(slen + 1 + (width - n)); memcpy(out, s, slen); for (i = n; i < width; i++) out[slen++] = ' '; out[slen] = '\0'; return (out); } int utf8_cstrhas(const char *s, const struct utf8_data *ud) { struct utf8_data *copy, *loop; int found = 0; copy = utf8_fromcstr(s); for (loop = copy; loop->size != 0; loop++) { if (loop->size != ud->size) continue; if (memcmp(loop->data, ud->data, loop->size) == 0) { found = 1; break; } } free(copy); return (found); } tmux-3.0a/window-buffer.c100644 001750 001750 00000025060 13570677043 0011155/* $OpenBSD$ */ /* * Copyright (c) 2017 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" static struct screen *window_buffer_init(struct window_mode_entry *, struct cmd_find_state *, struct args *); static void window_buffer_free(struct window_mode_entry *); static void window_buffer_resize(struct window_mode_entry *, u_int, u_int); static void window_buffer_key(struct window_mode_entry *, struct client *, struct session *, struct winlink *, key_code, struct mouse_event *); #define WINDOW_BUFFER_DEFAULT_COMMAND "paste-buffer -b '%%'" #define WINDOW_BUFFER_DEFAULT_FORMAT \ "#{buffer_size} bytes (#{t:buffer_created})" static const struct menu_item window_buffer_menu_items[] = { { "Paste", 'p', NULL }, { "Paste Tagged", 'P', NULL }, { "", KEYC_NONE, NULL }, { "Tag", 't', NULL }, { "Tag All", '\024', NULL }, { "Tag None", 'T', NULL }, { "", KEYC_NONE, NULL }, { "Delete", 'd', NULL }, { "Delete Tagged", 'D', NULL }, { "", KEYC_NONE, NULL }, { "Cancel", 'q', NULL }, { NULL, KEYC_NONE, NULL } }; const struct window_mode window_buffer_mode = { .name = "buffer-mode", .default_format = WINDOW_BUFFER_DEFAULT_FORMAT, .init = window_buffer_init, .free = window_buffer_free, .resize = window_buffer_resize, .key = window_buffer_key, }; enum window_buffer_sort_type { WINDOW_BUFFER_BY_TIME, WINDOW_BUFFER_BY_NAME, WINDOW_BUFFER_BY_SIZE, }; static const char *window_buffer_sort_list[] = { "time", "name", "size" }; struct window_buffer_itemdata { const char *name; u_int order; size_t size; }; struct window_buffer_modedata { struct window_pane *wp; struct cmd_find_state fs; struct mode_tree_data *data; char *command; char *format; struct window_buffer_itemdata **item_list; u_int item_size; }; static struct window_buffer_itemdata * window_buffer_add_item(struct window_buffer_modedata *data) { struct window_buffer_itemdata *item; data->item_list = xreallocarray(data->item_list, data->item_size + 1, sizeof *data->item_list); item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item); return (item); } static void window_buffer_free_item(struct window_buffer_itemdata *item) { free((void *)item->name); free(item); } static int window_buffer_cmp_name(const void *a0, const void *b0) { const struct window_buffer_itemdata *const *a = a0; const struct window_buffer_itemdata *const *b = b0; return (strcmp((*a)->name, (*b)->name)); } static int window_buffer_cmp_time(const void *a0, const void *b0) { const struct window_buffer_itemdata *const *a = a0; const struct window_buffer_itemdata *const *b = b0; if ((*a)->order > (*b)->order) return (-1); if ((*a)->order < (*b)->order) return (1); return (strcmp((*a)->name, (*b)->name)); } static int window_buffer_cmp_size(const void *a0, const void *b0) { const struct window_buffer_itemdata *const *a = a0; const struct window_buffer_itemdata *const *b = b0; if ((*a)->size > (*b)->size) return (-1); if ((*a)->size < (*b)->size) return (1); return (strcmp((*a)->name, (*b)->name)); } static void window_buffer_build(void *modedata, u_int sort_type, __unused uint64_t *tag, const char *filter) { struct window_buffer_modedata *data = modedata; struct window_buffer_itemdata *item; u_int i; struct paste_buffer *pb; char *text, *cp; struct format_tree *ft; struct session *s = NULL; struct winlink *wl = NULL; struct window_pane *wp = NULL; for (i = 0; i < data->item_size; i++) window_buffer_free_item(data->item_list[i]); free(data->item_list); data->item_list = NULL; data->item_size = 0; pb = NULL; while ((pb = paste_walk(pb)) != NULL) { item = window_buffer_add_item(data); item->name = xstrdup(paste_buffer_name(pb)); paste_buffer_data(pb, &item->size); item->order = paste_buffer_order(pb); } switch (sort_type) { case WINDOW_BUFFER_BY_NAME: qsort(data->item_list, data->item_size, sizeof *data->item_list, window_buffer_cmp_name); break; case WINDOW_BUFFER_BY_TIME: qsort(data->item_list, data->item_size, sizeof *data->item_list, window_buffer_cmp_time); break; case WINDOW_BUFFER_BY_SIZE: qsort(data->item_list, data->item_size, sizeof *data->item_list, window_buffer_cmp_size); break; } if (cmd_find_valid_state(&data->fs)) { s = data->fs.s; wl = data->fs.wl; wp = data->fs.wp; } for (i = 0; i < data->item_size; i++) { item = data->item_list[i]; pb = paste_get_name(item->name); if (pb == NULL) continue; ft = format_create(NULL, NULL, FORMAT_NONE, 0); format_defaults(ft, NULL, s, wl, wp); format_defaults_paste_buffer(ft, pb); if (filter != NULL) { cp = format_expand(ft, filter); if (!format_true(cp)) { free(cp); format_free(ft); continue; } free(cp); } text = format_expand(ft, data->format); mode_tree_add(data->data, NULL, item, item->order, item->name, text, -1); free(text); format_free(ft); } } static void window_buffer_draw(__unused void *modedata, void *itemdata, struct screen_write_ctx *ctx, u_int sx, u_int sy) { struct window_buffer_itemdata *item = itemdata; struct paste_buffer *pb; char line[1024]; const char *pdata, *end, *cp; size_t psize, at; u_int i, cx = ctx->s->cx, cy = ctx->s->cy; pb = paste_get_name(item->name); if (pb == NULL) return; pdata = end = paste_buffer_data(pb, &psize); for (i = 0; i < sy; i++) { at = 0; while (end != pdata + psize && *end != '\n') { if ((sizeof line) - at > 5) { cp = vis(line + at, *end, VIS_OCTAL|VIS_TAB, 0); at = cp - line; } end++; } if (at > sx) at = sx; line[at] = '\0'; if (*line != '\0') { screen_write_cursormove(ctx, cx, cy + i, 0); screen_write_puts(ctx, &grid_default_cell, "%s", line); } if (end == pdata + psize) break; end++; } } static int window_buffer_search(__unused void *modedata, void *itemdata, const char *ss) { struct window_buffer_itemdata *item = itemdata; struct paste_buffer *pb; const char *bufdata; size_t bufsize; if ((pb = paste_get_name(item->name)) == NULL) return (0); if (strstr(item->name, ss) != NULL) return (1); bufdata = paste_buffer_data(pb, &bufsize); return (memmem(bufdata, bufsize, ss, strlen(ss)) != NULL); } static void window_buffer_menu(void *modedata, struct client *c, key_code key) { struct window_buffer_modedata *data = modedata; struct window_pane *wp = data->wp; struct window_mode_entry *wme; wme = TAILQ_FIRST(&wp->modes); if (wme == NULL || wme->data != modedata) return; window_buffer_key(wme, c, NULL, NULL, key, NULL); } static struct screen * window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs, struct args *args) { struct window_pane *wp = wme->wp; struct window_buffer_modedata *data; struct screen *s; wme->data = data = xcalloc(1, sizeof *data); data->wp = wp; cmd_find_copy_state(&data->fs, fs); if (args == NULL || !args_has(args, 'F')) data->format = xstrdup(WINDOW_BUFFER_DEFAULT_FORMAT); else data->format = xstrdup(args_get(args, 'F')); if (args == NULL || args->argc == 0) data->command = xstrdup(WINDOW_BUFFER_DEFAULT_COMMAND); else data->command = xstrdup(args->argv[0]); data->data = mode_tree_start(wp, args, window_buffer_build, window_buffer_draw, window_buffer_search, window_buffer_menu, data, window_buffer_menu_items, window_buffer_sort_list, nitems(window_buffer_sort_list), &s); mode_tree_zoom(data->data, args); mode_tree_build(data->data); mode_tree_draw(data->data); return (s); } static void window_buffer_free(struct window_mode_entry *wme) { struct window_buffer_modedata *data = wme->data; u_int i; if (data == NULL) return; mode_tree_free(data->data); for (i = 0; i < data->item_size; i++) window_buffer_free_item(data->item_list[i]); free(data->item_list); free(data->format); free(data->command); free(data); } static void window_buffer_resize(struct window_mode_entry *wme, u_int sx, u_int sy) { struct window_buffer_modedata *data = wme->data; mode_tree_resize(data->data, sx, sy); } static void window_buffer_do_delete(void* modedata, void *itemdata, __unused struct client *c, __unused key_code key) { struct window_buffer_modedata *data = modedata; struct window_buffer_itemdata *item = itemdata; struct paste_buffer *pb; if (item == mode_tree_get_current(data->data)) mode_tree_down(data->data, 0); if ((pb = paste_get_name(item->name)) != NULL) paste_free(pb); } static void window_buffer_do_paste(void* modedata, void *itemdata, struct client *c, __unused key_code key) { struct window_buffer_modedata *data = modedata; struct window_buffer_itemdata *item = itemdata; struct paste_buffer *pb; if ((pb = paste_get_name(item->name)) != NULL) mode_tree_run_command(c, NULL, data->command, item->name); } static void window_buffer_key(struct window_mode_entry *wme, struct client *c, __unused struct session *s, __unused struct winlink *wl, key_code key, struct mouse_event *m) { struct window_pane *wp = wme->wp; struct window_buffer_modedata *data = wme->data; struct mode_tree_data *mtd = data->data; struct window_buffer_itemdata *item; int finished; finished = mode_tree_key(mtd, c, &key, m, NULL, NULL); switch (key) { case 'd': item = mode_tree_get_current(mtd); window_buffer_do_delete(data, item, c, key); mode_tree_build(mtd); break; case 'D': mode_tree_each_tagged(mtd, window_buffer_do_delete, c, key, 0); mode_tree_build(mtd); break; case 'P': mode_tree_each_tagged(mtd, window_buffer_do_paste, c, key, 0); finished = 1; break; case 'p': case '\r': item = mode_tree_get_current(mtd); window_buffer_do_paste(data, item, c, key); finished = 1; break; } if (finished || paste_get_top(NULL) == NULL) window_pane_reset_mode(wp); else { mode_tree_draw(mtd); wp->flags |= PANE_REDRAW; } } tmux-3.0a/window-client.c100644 001750 001750 00000024442 13570677043 0011165/* $OpenBSD$ */ /* * Copyright (c) 2017 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" static struct screen *window_client_init(struct window_mode_entry *, struct cmd_find_state *, struct args *); static void window_client_free(struct window_mode_entry *); static void window_client_resize(struct window_mode_entry *, u_int, u_int); static void window_client_key(struct window_mode_entry *, struct client *, struct session *, struct winlink *, key_code, struct mouse_event *); #define WINDOW_CLIENT_DEFAULT_COMMAND "detach-client -t '%%'" #define WINDOW_CLIENT_DEFAULT_FORMAT \ "session #{session_name} " \ "(#{client_width}x#{client_height}, #{t:client_activity})" static const struct menu_item window_client_menu_items[] = { { "Detach", 'd', NULL }, { "Detach Tagged", 'D', NULL }, { "", KEYC_NONE, NULL }, { "Tag", 't', NULL }, { "Tag All", '\024', NULL }, { "Tag None", 'T', NULL }, { "", KEYC_NONE, NULL }, { "Cancel", 'q', NULL }, { NULL, KEYC_NONE, NULL } }; const struct window_mode window_client_mode = { .name = "client-mode", .default_format = WINDOW_CLIENT_DEFAULT_FORMAT, .init = window_client_init, .free = window_client_free, .resize = window_client_resize, .key = window_client_key, }; enum window_client_sort_type { WINDOW_CLIENT_BY_NAME, WINDOW_CLIENT_BY_SIZE, WINDOW_CLIENT_BY_CREATION_TIME, WINDOW_CLIENT_BY_ACTIVITY_TIME, }; static const char *window_client_sort_list[] = { "name", "size", "creation", "activity" }; struct window_client_itemdata { struct client *c; }; struct window_client_modedata { struct window_pane *wp; struct mode_tree_data *data; char *format; char *command; struct window_client_itemdata **item_list; u_int item_size; }; static struct window_client_itemdata * window_client_add_item(struct window_client_modedata *data) { struct window_client_itemdata *item; data->item_list = xreallocarray(data->item_list, data->item_size + 1, sizeof *data->item_list); item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item); return (item); } static void window_client_free_item(struct window_client_itemdata *item) { server_client_unref(item->c); free(item); } static int window_client_cmp_name(const void *a0, const void *b0) { const struct window_client_itemdata *const *a = a0; const struct window_client_itemdata *const *b = b0; return (strcmp((*a)->c->name, (*b)->c->name)); } static int window_client_cmp_size(const void *a0, const void *b0) { const struct window_client_itemdata *const *a = a0; const struct window_client_itemdata *const *b = b0; if ((*a)->c->tty.sx < (*b)->c->tty.sx) return (-1); if ((*a)->c->tty.sx > (*b)->c->tty.sx) return (1); if ((*a)->c->tty.sy < (*b)->c->tty.sy) return (-1); if ((*a)->c->tty.sy > (*b)->c->tty.sy) return (1); return (strcmp((*a)->c->name, (*b)->c->name)); } static int window_client_cmp_creation_time(const void *a0, const void *b0) { const struct window_client_itemdata *const *a = a0; const struct window_client_itemdata *const *b = b0; if (timercmp(&(*a)->c->creation_time, &(*b)->c->creation_time, >)) return (-1); if (timercmp(&(*a)->c->creation_time, &(*b)->c->creation_time, <)) return (1); return (strcmp((*a)->c->name, (*b)->c->name)); } static int window_client_cmp_activity_time(const void *a0, const void *b0) { const struct window_client_itemdata *const *a = a0; const struct window_client_itemdata *const *b = b0; if (timercmp(&(*a)->c->activity_time, &(*b)->c->activity_time, >)) return (-1); if (timercmp(&(*a)->c->activity_time, &(*b)->c->activity_time, <)) return (1); return (strcmp((*a)->c->name, (*b)->c->name)); } static void window_client_build(void *modedata, u_int sort_type, __unused uint64_t *tag, const char *filter) { struct window_client_modedata *data = modedata; struct window_client_itemdata *item; u_int i; struct client *c; char *text, *cp; for (i = 0; i < data->item_size; i++) window_client_free_item(data->item_list[i]); free(data->item_list); data->item_list = NULL; data->item_size = 0; TAILQ_FOREACH(c, &clients, entry) { if (c->session == NULL || (c->flags & (CLIENT_DETACHING))) continue; item = window_client_add_item(data); item->c = c; c->references++; } switch (sort_type) { case WINDOW_CLIENT_BY_NAME: qsort(data->item_list, data->item_size, sizeof *data->item_list, window_client_cmp_name); break; case WINDOW_CLIENT_BY_SIZE: qsort(data->item_list, data->item_size, sizeof *data->item_list, window_client_cmp_size); break; case WINDOW_CLIENT_BY_CREATION_TIME: qsort(data->item_list, data->item_size, sizeof *data->item_list, window_client_cmp_creation_time); break; case WINDOW_CLIENT_BY_ACTIVITY_TIME: qsort(data->item_list, data->item_size, sizeof *data->item_list, window_client_cmp_activity_time); break; } for (i = 0; i < data->item_size; i++) { item = data->item_list[i]; c = item->c; if (filter != NULL) { cp = format_single(NULL, filter, c, NULL, NULL, NULL); if (!format_true(cp)) { free(cp); continue; } free(cp); } text = format_single(NULL, data->format, c, NULL, NULL, NULL); mode_tree_add(data->data, NULL, item, (uint64_t)c, c->name, text, -1); free(text); } } static void window_client_draw(__unused void *modedata, void *itemdata, struct screen_write_ctx *ctx, u_int sx, u_int sy) { struct window_client_itemdata *item = itemdata; struct client *c = item->c; struct screen *s = ctx->s; struct window_pane *wp; u_int cx = s->cx, cy = s->cy, lines, at; if (c->session == NULL || (c->flags & (CLIENT_DEAD|CLIENT_DETACHING))) return; wp = c->session->curw->window->active; lines = status_line_size(c); if (lines >= sy) lines = 0; if (status_at_line(c) == 0) at = lines; else at = 0; screen_write_cursormove(ctx, cx, cy + at, 0); screen_write_preview(ctx, &wp->base, sx, sy - 2 - lines); if (at != 0) screen_write_cursormove(ctx, cx, cy + 2, 0); else screen_write_cursormove(ctx, cx, cy + sy - 1 - lines, 0); screen_write_hline(ctx, sx, 0, 0); if (at != 0) screen_write_cursormove(ctx, cx, cy, 0); else screen_write_cursormove(ctx, cx, cy + sy - lines, 0); screen_write_fast_copy(ctx, &c->status.screen, 0, 0, sx, lines); } static void window_client_menu(void *modedata, struct client *c, key_code key) { struct window_client_modedata *data = modedata; struct window_pane *wp = data->wp; struct window_mode_entry *wme; wme = TAILQ_FIRST(&wp->modes); if (wme == NULL || wme->data != modedata) return; window_client_key(wme, c, NULL, NULL, key, NULL); } static struct screen * window_client_init(struct window_mode_entry *wme, __unused struct cmd_find_state *fs, struct args *args) { struct window_pane *wp = wme->wp; struct window_client_modedata *data; struct screen *s; wme->data = data = xcalloc(1, sizeof *data); data->wp = wp; if (args == NULL || !args_has(args, 'F')) data->format = xstrdup(WINDOW_CLIENT_DEFAULT_FORMAT); else data->format = xstrdup(args_get(args, 'F')); if (args == NULL || args->argc == 0) data->command = xstrdup(WINDOW_CLIENT_DEFAULT_COMMAND); else data->command = xstrdup(args->argv[0]); data->data = mode_tree_start(wp, args, window_client_build, window_client_draw, NULL, window_client_menu, data, window_client_menu_items, window_client_sort_list, nitems(window_client_sort_list), &s); mode_tree_zoom(data->data, args); mode_tree_build(data->data); mode_tree_draw(data->data); return (s); } static void window_client_free(struct window_mode_entry *wme) { struct window_client_modedata *data = wme->data; u_int i; if (data == NULL) return; mode_tree_free(data->data); for (i = 0; i < data->item_size; i++) window_client_free_item(data->item_list[i]); free(data->item_list); free(data->format); free(data->command); free(data); } static void window_client_resize(struct window_mode_entry *wme, u_int sx, u_int sy) { struct window_client_modedata *data = wme->data; mode_tree_resize(data->data, sx, sy); } static void window_client_do_detach(void* modedata, void *itemdata, __unused struct client *c, key_code key) { struct window_client_modedata *data = modedata; struct window_client_itemdata *item = itemdata; if (item == mode_tree_get_current(data->data)) mode_tree_down(data->data, 0); if (key == 'd' || key == 'D') server_client_detach(item->c, MSG_DETACH); else if (key == 'x' || key == 'X') server_client_detach(item->c, MSG_DETACHKILL); else if (key == 'z' || key == 'Z') server_client_suspend(item->c); } static void window_client_key(struct window_mode_entry *wme, struct client *c, __unused struct session *s, __unused struct winlink *wl, key_code key, struct mouse_event *m) { struct window_pane *wp = wme->wp; struct window_client_modedata *data = wme->data; struct mode_tree_data *mtd = data->data; struct window_client_itemdata *item; int finished; finished = mode_tree_key(mtd, c, &key, m, NULL, NULL); switch (key) { case 'd': case 'x': case 'z': item = mode_tree_get_current(mtd); window_client_do_detach(data, item, c, key); mode_tree_build(mtd); break; case 'D': case 'X': case 'Z': mode_tree_each_tagged(mtd, window_client_do_detach, c, key, 0); mode_tree_build(mtd); break; case '\r': item = mode_tree_get_current(mtd); mode_tree_run_command(c, NULL, data->command, item->c->ttyname); finished = 1; break; } if (finished || server_client_how_many() == 0) window_pane_reset_mode(wp); else { mode_tree_draw(mtd); wp->flags |= PANE_REDRAW; } } tmux-3.0a/window-clock.c100644 001750 001750 00000015645 13466241542 0011002/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" static struct screen *window_clock_init(struct window_mode_entry *, struct cmd_find_state *, struct args *); static void window_clock_free(struct window_mode_entry *); static void window_clock_resize(struct window_mode_entry *, u_int, u_int); static void window_clock_key(struct window_mode_entry *, struct client *, struct session *, struct winlink *, key_code, struct mouse_event *); static void window_clock_timer_callback(int, short, void *); static void window_clock_draw_screen(struct window_mode_entry *); const struct window_mode window_clock_mode = { .name = "clock-mode", .init = window_clock_init, .free = window_clock_free, .resize = window_clock_resize, .key = window_clock_key, }; struct window_clock_mode_data { struct screen screen; time_t tim; struct event timer; }; const char window_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 } }, }; static void window_clock_timer_callback(__unused int fd, __unused short events, void *arg) { struct window_mode_entry *wme = arg; struct window_pane *wp = wme->wp; struct window_clock_mode_data *data = wme->data; struct tm now, then; time_t t; struct timeval tv = { .tv_sec = 1 }; evtimer_del(&data->timer); evtimer_add(&data->timer, &tv); if (TAILQ_FIRST(&wp->modes) != wme) return; 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(wme); wp->flags |= PANE_REDRAW; } static struct screen * window_clock_init(struct window_mode_entry *wme, __unused struct cmd_find_state *fs, __unused struct args *args) { struct window_pane *wp = wme->wp; struct window_clock_mode_data *data; struct screen *s; struct timeval tv = { .tv_sec = 1 }; wme->data = data = xmalloc(sizeof *data); data->tim = time(NULL); evtimer_set(&data->timer, window_clock_timer_callback, wme); evtimer_add(&data->timer, &tv); 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(wme); return (s); } static void window_clock_free(struct window_mode_entry *wme) { struct window_clock_mode_data *data = wme->data; evtimer_del(&data->timer); screen_free(&data->screen); free(data); } static void window_clock_resize(struct window_mode_entry *wme, u_int sx, u_int sy) { struct window_clock_mode_data *data = wme->data; struct screen *s = &data->screen; screen_resize(s, sx, sy, 0); window_clock_draw_screen(wme); } static void window_clock_key(struct window_mode_entry *wme, __unused struct client *c, __unused struct session *s, __unused struct winlink *wl, __unused key_code key, __unused struct mouse_event *m) { window_pane_reset_mode(wme->wp); } static void window_clock_draw_screen(struct window_mode_entry *wme) { struct window_pane *wp = wme->wp; struct window_clock_mode_data *data = wme->data; struct screen_write_ctx ctx; int colour, style; struct screen *s = &data->screen; struct grid_cell gc; char tim[64], *ptr; time_t t; struct tm *tm; u_int i, j, x, y, idx; 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, s); t = time(NULL); tm = localtime(&t); if (style == 0) { strftime(tim, sizeof tim, "%l:%M ", localtime(&t)); if (tm->tm_hour >= 12) strlcat(tim, "PM", sizeof tim); else strlcat(tim, "AM", sizeof tim); } else strftime(tim, sizeof tim, "%H:%M", tm); screen_write_clearscreen(&ctx, 8); 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, 0); memcpy(&gc, &grid_default_cell, sizeof gc); gc.flags |= GRID_FLAG_NOPALETTE; gc.fg = colour; screen_write_puts(&ctx, &gc, "%s", tim); } screen_write_stop(&ctx); return; } x = (screen_size_x(s) / 2) - 3 * strlen(tim); y = (screen_size_y(s) / 2) - 3; memcpy(&gc, &grid_default_cell, sizeof gc); gc.flags |= GRID_FLAG_NOPALETTE; gc.bg = 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, 0); if (window_clock_table[idx][j][i]) screen_write_putc(&ctx, &gc, ' '); } } x += 6; } screen_write_stop(&ctx); } tmux-3.0a/window-copy.c100644 001750 001750 00000266363 13570677043 0010673/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" static const char *window_copy_key_table(struct window_mode_entry *); static void window_copy_command(struct window_mode_entry *, struct client *, struct session *, struct winlink *, struct args *, struct mouse_event *); static struct screen *window_copy_init(struct window_mode_entry *, struct cmd_find_state *, struct args *); static struct screen *window_copy_view_init(struct window_mode_entry *, struct cmd_find_state *, struct args *); static void window_copy_free(struct window_mode_entry *); static void window_copy_resize(struct window_mode_entry *, u_int, u_int); static void window_copy_formats(struct window_mode_entry *, struct format_tree *); static void window_copy_pageup1(struct window_mode_entry *, int); static int window_copy_pagedown(struct window_mode_entry *, int, int); static void window_copy_next_paragraph(struct window_mode_entry *); static void window_copy_previous_paragraph(struct window_mode_entry *); static void window_copy_redraw_selection(struct window_mode_entry *, u_int); static void window_copy_redraw_lines(struct window_mode_entry *, u_int, u_int); static void window_copy_redraw_screen(struct window_mode_entry *); static void window_copy_write_line(struct window_mode_entry *, struct screen_write_ctx *, u_int); static void window_copy_write_lines(struct window_mode_entry *, struct screen_write_ctx *, u_int, u_int); static void window_copy_scroll_to(struct window_mode_entry *, u_int, u_int); static int window_copy_search_compare(struct grid *, u_int, u_int, struct grid *, u_int, int); static int window_copy_search_lr(struct grid *, struct grid *, u_int *, u_int, u_int, u_int, int); static int window_copy_search_rl(struct grid *, struct grid *, u_int *, u_int, u_int, u_int, int); static int window_copy_search_marks(struct window_mode_entry *, struct screen *); static void window_copy_clear_marks(struct window_mode_entry *); static void window_copy_move_left(struct screen *, u_int *, u_int *); static void window_copy_move_right(struct screen *, u_int *, u_int *); static int window_copy_is_lowercase(const char *); static int window_copy_search_jump(struct window_mode_entry *, struct grid *, struct grid *, u_int, u_int, u_int, int, int, int); static int window_copy_search(struct window_mode_entry *, int); static int window_copy_search_up(struct window_mode_entry *); static int window_copy_search_down(struct window_mode_entry *); static void window_copy_goto_line(struct window_mode_entry *, const char *); static void window_copy_update_cursor(struct window_mode_entry *, u_int, u_int); static void window_copy_start_selection(struct window_mode_entry *); static int window_copy_adjust_selection(struct window_mode_entry *, u_int *, u_int *); static int window_copy_set_selection(struct window_mode_entry *, int); static int window_copy_update_selection(struct window_mode_entry *, int); static void window_copy_synchronize_cursor(struct window_mode_entry *); static void *window_copy_get_selection(struct window_mode_entry *, size_t *); static void window_copy_copy_buffer(struct window_mode_entry *, const char *, void *, size_t); static void window_copy_copy_pipe(struct window_mode_entry *, struct session *, const char *, const char *); static void window_copy_copy_selection(struct window_mode_entry *, const char *); static void window_copy_append_selection(struct window_mode_entry *); static void window_copy_clear_selection(struct window_mode_entry *); static void window_copy_copy_line(struct window_mode_entry *, char **, size_t *, u_int, u_int, u_int); static int window_copy_in_set(struct window_mode_entry *, u_int, u_int, const char *); static u_int window_copy_find_length(struct window_mode_entry *, u_int); static void window_copy_cursor_start_of_line(struct window_mode_entry *); static void window_copy_cursor_back_to_indentation( struct window_mode_entry *); static void window_copy_cursor_end_of_line(struct window_mode_entry *); static void window_copy_other_end(struct window_mode_entry *); static void window_copy_cursor_left(struct window_mode_entry *); static void window_copy_cursor_right(struct window_mode_entry *); static void window_copy_cursor_up(struct window_mode_entry *, int); static void window_copy_cursor_down(struct window_mode_entry *, int); static void window_copy_cursor_jump(struct window_mode_entry *); static void window_copy_cursor_jump_back(struct window_mode_entry *); static void window_copy_cursor_jump_to(struct window_mode_entry *); static void window_copy_cursor_jump_to_back(struct window_mode_entry *); static void window_copy_cursor_next_word(struct window_mode_entry *, const char *); static void window_copy_cursor_next_word_end(struct window_mode_entry *, const char *); static void window_copy_cursor_previous_word(struct window_mode_entry *, const char *); static void window_copy_scroll_up(struct window_mode_entry *, u_int); static void window_copy_scroll_down(struct window_mode_entry *, u_int); static void window_copy_rectangle_toggle(struct window_mode_entry *); static void window_copy_move_mouse(struct mouse_event *); static void window_copy_drag_update(struct client *, struct mouse_event *); static void window_copy_drag_release(struct client *, struct mouse_event *); const struct window_mode window_copy_mode = { .name = "copy-mode", .init = window_copy_init, .free = window_copy_free, .resize = window_copy_resize, .key_table = window_copy_key_table, .command = window_copy_command, .formats = window_copy_formats, }; const struct window_mode window_view_mode = { .name = "view-mode", .init = window_copy_view_init, .free = window_copy_free, .resize = window_copy_resize, .key_table = window_copy_key_table, .command = window_copy_command, .formats = window_copy_formats, }; enum { WINDOW_COPY_OFF, WINDOW_COPY_SEARCHUP, WINDOW_COPY_SEARCHDOWN, WINDOW_COPY_JUMPFORWARD, WINDOW_COPY_JUMPBACKWARD, WINDOW_COPY_JUMPTOFORWARD, WINDOW_COPY_JUMPTOBACKWARD, }; enum { WINDOW_COPY_REL_POS_ABOVE, WINDOW_COPY_REL_POS_ON_SCREEN, WINDOW_COPY_REL_POS_BELOW, }; enum window_copy_cmd_action { WINDOW_COPY_CMD_NOTHING, WINDOW_COPY_CMD_REDRAW, WINDOW_COPY_CMD_CANCEL, }; struct window_copy_cmd_state { struct window_mode_entry *wme; struct args *args; struct mouse_event *m; struct client *c; struct session *s; struct winlink *wl; }; /* * 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 started */ u_int oy; /* number of lines scrolled up */ u_int selx; /* beginning of selection */ u_int sely; u_int endselx; /* end of selection */ u_int endsely; enum { CURSORDRAG_NONE, /* selection is independent of cursor */ CURSORDRAG_ENDSEL, /* end is synchronized with cursor */ CURSORDRAG_SEL, /* start is synchronized with cursor */ } cursordrag; int modekeys; enum { LINE_SEL_NONE, LINE_SEL_LEFT_RIGHT, LINE_SEL_RIGHT_LEFT, } lineflag; /* line selection mode */ int rectflag; /* in rectangle copy mode? */ int scroll_exit; /* exit on scroll to end? */ u_int cx; u_int cy; u_int lastcx; /* position in last line w/ content */ u_int lastsx; /* size of last line w/ content */ int searchtype; char *searchstr; bitstr_t *searchmark; u_int searchcount; int searchthis; int searchx; int searchy; int searcho; int jumptype; char jumpchar; struct event dragtimer; #define WINDOW_COPY_DRAG_REPEAT_TIME 50000 }; static void window_copy_scroll_timer(__unused int fd, __unused short events, void *arg) { struct window_mode_entry *wme = arg; struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; struct timeval tv = { .tv_usec = WINDOW_COPY_DRAG_REPEAT_TIME }; evtimer_del(&data->dragtimer); if (TAILQ_FIRST(&wp->modes) != wme) return; if (data->cy == 0) { evtimer_add(&data->dragtimer, &tv); window_copy_cursor_up(wme, 1); } else if (data->cy == screen_size_y(&data->screen) - 1) { evtimer_add(&data->dragtimer, &tv); window_copy_cursor_down(wme, 1); } } static struct window_copy_mode_data * window_copy_common_init(struct window_mode_entry *wme) { struct window_pane *wp = wme->wp; struct window_copy_mode_data *data; struct screen *base = &wp->base; wme->data = data = xcalloc(1, sizeof *data); data->cursordrag = CURSORDRAG_NONE; data->lineflag = LINE_SEL_NONE; if (wp->searchstr != NULL) { data->searchtype = WINDOW_COPY_SEARCHUP; data->searchstr = xstrdup(wp->searchstr); } else { data->searchtype = WINDOW_COPY_OFF; data->searchstr = NULL; } data->searchmark = NULL; data->searchx = data->searchy = data->searcho = -1; data->jumptype = WINDOW_COPY_OFF; data->jumpchar = '\0'; screen_init(&data->screen, screen_size_x(base), screen_size_y(base), 0); data->modekeys = options_get_number(wp->window->options, "mode-keys"); evtimer_set(&data->dragtimer, window_copy_scroll_timer, wme); return (data); } static struct screen * window_copy_init(struct window_mode_entry *wme, __unused struct cmd_find_state *fs, struct args *args) { struct window_pane *wp = wme->wp; struct window_copy_mode_data *data; struct screen_write_ctx ctx; u_int i; data = window_copy_common_init(wme); if (wp->fd != -1 && wp->disabled++ == 0) bufferevent_disable(wp->event, EV_READ|EV_WRITE); data->backing = &wp->base; data->cx = data->backing->cx; data->cy = data->backing->cy; data->scroll_exit = args_has(args, 'e'); data->screen.cx = data->cx; data->screen.cy = data->cy; screen_write_start(&ctx, NULL, &data->screen); for (i = 0; i < screen_size_y(&data->screen); i++) window_copy_write_line(wme, &ctx, i); screen_write_cursormove(&ctx, data->cx, data->cy, 0); screen_write_stop(&ctx); return (&data->screen); } static struct screen * window_copy_view_init(struct window_mode_entry *wme, __unused struct cmd_find_state *fs, __unused struct args *args) { struct window_pane *wp = wme->wp; struct window_copy_mode_data *data; struct screen *base = &wp->base; struct screen *s; data = window_copy_common_init(wme); data->backing = s = xmalloc(sizeof *data->backing); screen_init(s, screen_size_x(base), screen_size_y(base), UINT_MAX); return (&data->screen); } static void window_copy_free(struct window_mode_entry *wme) { struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; evtimer_del(&data->dragtimer); if (wp->fd != -1 && --wp->disabled == 0) bufferevent_enable(wp->event, EV_READ|EV_WRITE); free(data->searchmark); free(data->searchstr); if (data->backing != &wp->base) { screen_free(data->backing); free(data->backing); } screen_free(&data->screen); free(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_mode_entry *wme = TAILQ_FIRST(&wp->modes); struct window_copy_mode_data *data = wme->data; struct screen *backing = data->backing; struct screen_write_ctx back_ctx, ctx; struct grid_cell gc; u_int old_hsize, old_cy; if (backing == &wp->base) return; 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, 8); } else data->backing_written = 1; old_cy = backing->cy; screen_write_vnputs(&back_ctx, 0, &gc, 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(wme, 0, 1); /* Write the new lines. */ window_copy_redraw_lines(wme, old_cy, backing->cy - old_cy + 1); screen_write_stop(&ctx); } void window_copy_pageup(struct window_pane *wp, int half_page) { window_copy_pageup1(TAILQ_FIRST(&wp->modes), half_page); } static void window_copy_pageup1(struct window_mode_entry *wme, int half_page) { struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; u_int n, ox, oy, px, py; oy = screen_hsize(data->backing) + data->cy - data->oy; ox = window_copy_find_length(wme, oy); if (data->cx != ox) { data->lastcx = data->cx; data->lastsx = ox; } data->cx = data->lastcx; n = 1; if (screen_size_y(s) > 2) { if (half_page) n = screen_size_y(s) / 2; else n = screen_size_y(s) - 2; } if (data->oy + n > screen_hsize(data->backing)) { data->oy = screen_hsize(data->backing); if (data->cy < n) data->cy = 0; else data->cy -= n; } else data->oy += n; if (data->screen.sel == NULL || !data->rectflag) { py = screen_hsize(data->backing) + data->cy - data->oy; px = window_copy_find_length(wme, py); if ((data->cx >= data->lastsx && data->cx != px) || data->cx > px) window_copy_cursor_end_of_line(wme); } window_copy_update_selection(wme, 1); window_copy_redraw_screen(wme); } static int window_copy_pagedown(struct window_mode_entry *wme, int half_page, int scroll_exit) { struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; u_int n, ox, oy, px, py; oy = screen_hsize(data->backing) + data->cy - data->oy; ox = window_copy_find_length(wme, oy); if (data->cx != ox) { data->lastcx = data->cx; data->lastsx = ox; } data->cx = data->lastcx; n = 1; if (screen_size_y(s) > 2) { if (half_page) n = screen_size_y(s) / 2; else n = screen_size_y(s) - 2; } if (data->oy < n) { data->oy = 0; if (data->cy + (n - data->oy) >= screen_size_y(data->backing)) data->cy = screen_size_y(data->backing) - 1; else data->cy += n - data->oy; } else data->oy -= n; if (data->screen.sel == NULL || !data->rectflag) { py = screen_hsize(data->backing) + data->cy - data->oy; px = window_copy_find_length(wme, py); if ((data->cx >= data->lastsx && data->cx != px) || data->cx > px) window_copy_cursor_end_of_line(wme); } if (scroll_exit && data->oy == 0) return (1); window_copy_update_selection(wme, 1); window_copy_redraw_screen(wme); return (0); } static void window_copy_previous_paragraph(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; u_int oy; oy = screen_hsize(data->backing) + data->cy - data->oy; while (oy > 0 && window_copy_find_length(wme, oy) == 0) oy--; while (oy > 0 && window_copy_find_length(wme, oy) > 0) oy--; window_copy_scroll_to(wme, 0, oy); } static void window_copy_next_paragraph(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; u_int maxy, ox, oy; oy = screen_hsize(data->backing) + data->cy - data->oy; maxy = screen_hsize(data->backing) + screen_size_y(s) - 1; while (oy < maxy && window_copy_find_length(wme, oy) == 0) oy++; while (oy < maxy && window_copy_find_length(wme, oy) > 0) oy++; ox = window_copy_find_length(wme, oy); window_copy_scroll_to(wme, ox, oy); } static void window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft) { struct window_copy_mode_data *data = wme->data; format_add(ft, "selection_present", "%d", data->screen.sel != NULL); format_add(ft, "scroll_position", "%d", data->oy); format_add(ft, "rectangle_toggle", "%d", data->rectflag); } static void window_copy_resize(struct window_mode_entry *wme, u_int sx, u_int sy) { struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; struct screen_write_ctx ctx; int search; screen_resize(s, sx, sy, 1); if (data->backing != &wp->base) screen_resize(data->backing, sx, sy, 1); 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); search = (data->searchmark != NULL); window_copy_clear_selection(wme); window_copy_clear_marks(wme); screen_write_start(&ctx, NULL, s); window_copy_write_lines(wme, &ctx, 0, screen_size_y(s) - 1); screen_write_stop(&ctx); if (search) window_copy_search_marks(wme, NULL); data->searchx = data->cx; data->searchy = data->cy; data->searcho = data->oy; window_copy_redraw_screen(wme); } static const char * window_copy_key_table(struct window_mode_entry *wme) { struct window_pane *wp = wme->wp; if (options_get_number(wp->window->options, "mode-keys") == MODEKEY_VI) return ("copy-mode-vi"); return ("copy-mode"); } static enum window_copy_cmd_action window_copy_cmd_append_selection(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct session *s = cs->s; if (s != NULL) window_copy_append_selection(wme); window_copy_clear_selection(wme); return (WINDOW_COPY_CMD_REDRAW); } static enum window_copy_cmd_action window_copy_cmd_append_selection_and_cancel(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct session *s = cs->s; if (s != NULL) window_copy_append_selection(wme); window_copy_clear_selection(wme); return (WINDOW_COPY_CMD_CANCEL); } static enum window_copy_cmd_action window_copy_cmd_back_to_indentation(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; window_copy_cursor_back_to_indentation(wme); return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_begin_selection(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct client *c = cs->c; struct mouse_event *m = cs->m; struct window_copy_mode_data *data = wme->data; if (m != NULL) { window_copy_start_drag(c, m); return (WINDOW_COPY_CMD_NOTHING); } data->lineflag = LINE_SEL_NONE; window_copy_start_selection(wme); return (WINDOW_COPY_CMD_REDRAW); } static enum window_copy_cmd_action window_copy_cmd_stop_selection(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; data->cursordrag = CURSORDRAG_NONE; data->lineflag = LINE_SEL_NONE; return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_bottom_line(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; data->cx = 0; data->cy = screen_size_y(&data->screen) - 1; window_copy_update_selection(wme, 1); return (WINDOW_COPY_CMD_REDRAW); } static enum window_copy_cmd_action window_copy_cmd_cancel(__unused struct window_copy_cmd_state *cs) { return (WINDOW_COPY_CMD_CANCEL); } static enum window_copy_cmd_action window_copy_cmd_clear_selection(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; window_copy_clear_selection(wme); return (WINDOW_COPY_CMD_REDRAW); } static enum window_copy_cmd_action window_copy_cmd_copy_end_of_line(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct client *c = cs->c; struct session *s = cs->s; struct winlink *wl = cs->wl; struct window_pane *wp = wme->wp; u_int np = wme->prefix; char *prefix = NULL; if (cs->args->argc == 2) prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp); window_copy_start_selection(wme); for (; np > 1; np--) window_copy_cursor_down(wme, 0); window_copy_cursor_end_of_line(wme); if (s != NULL) { window_copy_copy_selection(wme, prefix); free(prefix); return (WINDOW_COPY_CMD_CANCEL); } free(prefix); return (WINDOW_COPY_CMD_REDRAW); } static enum window_copy_cmd_action window_copy_cmd_copy_line(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct client *c = cs->c; struct session *s = cs->s; struct winlink *wl = cs->wl; struct window_pane *wp = wme->wp; u_int np = wme->prefix; char *prefix = NULL; if (cs->args->argc == 2) prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp); window_copy_cursor_start_of_line(wme); window_copy_start_selection(wme); for (; np > 1; np--) window_copy_cursor_down(wme, 0); window_copy_cursor_end_of_line(wme); if (s != NULL) { window_copy_copy_selection(wme, prefix); free(prefix); return (WINDOW_COPY_CMD_CANCEL); } free(prefix); return (WINDOW_COPY_CMD_REDRAW); } static enum window_copy_cmd_action window_copy_cmd_copy_selection_no_clear(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct client *c = cs->c; struct session *s = cs->s; struct winlink *wl = cs->wl; struct window_pane *wp = wme->wp; char *prefix = NULL; if (cs->args->argc == 2) prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp); if (s != NULL) window_copy_copy_selection(wme, prefix); free(prefix); return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_copy_selection(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; window_copy_cmd_copy_selection_no_clear(cs); window_copy_clear_selection(wme); return (WINDOW_COPY_CMD_REDRAW); } static enum window_copy_cmd_action window_copy_cmd_copy_selection_and_cancel(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; window_copy_cmd_copy_selection_no_clear(cs); window_copy_clear_selection(wme); return (WINDOW_COPY_CMD_CANCEL); } static enum window_copy_cmd_action window_copy_cmd_cursor_down(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; u_int np = wme->prefix; for (; np != 0; np--) window_copy_cursor_down(wme, 0); return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_cursor_left(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; u_int np = wme->prefix; for (; np != 0; np--) window_copy_cursor_left(wme); return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_cursor_right(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; u_int np = wme->prefix; for (; np != 0; np--) window_copy_cursor_right(wme); return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_cursor_up(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; u_int np = wme->prefix; for (; np != 0; np--) window_copy_cursor_up(wme, 0); return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_end_of_line(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; window_copy_cursor_end_of_line(wme); return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_halfpage_down(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; for (; np != 0; np--) { if (window_copy_pagedown(wme, 1, data->scroll_exit)) return (WINDOW_COPY_CMD_CANCEL); } return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_halfpage_down_and_cancel(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; u_int np = wme->prefix; for (; np != 0; np--) { if (window_copy_pagedown(wme, 1, 1)) return (WINDOW_COPY_CMD_CANCEL); } return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_halfpage_up(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; u_int np = wme->prefix; for (; np != 0; np--) window_copy_pageup1(wme, 1); return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_history_bottom(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; u_int oy; oy = screen_hsize(data->backing) + data->cy - data->oy; if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely) window_copy_other_end(wme); data->cy = screen_size_y(&data->screen) - 1; data->cx = window_copy_find_length(wme, data->cy); data->oy = 0; window_copy_update_selection(wme, 1); return (WINDOW_COPY_CMD_REDRAW); } static enum window_copy_cmd_action window_copy_cmd_history_top(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; u_int oy; oy = screen_hsize(data->backing) + data->cy - data->oy; if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely) window_copy_other_end(wme); data->cy = 0; data->cx = 0; data->oy = screen_hsize(data->backing); window_copy_update_selection(wme, 1); return (WINDOW_COPY_CMD_REDRAW); } static enum window_copy_cmd_action window_copy_cmd_jump_again(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; switch (data->jumptype) { case WINDOW_COPY_JUMPFORWARD: for (; np != 0; np--) window_copy_cursor_jump(wme); break; case WINDOW_COPY_JUMPBACKWARD: for (; np != 0; np--) window_copy_cursor_jump_back(wme); break; case WINDOW_COPY_JUMPTOFORWARD: for (; np != 0; np--) window_copy_cursor_jump_to(wme); break; case WINDOW_COPY_JUMPTOBACKWARD: for (; np != 0; np--) window_copy_cursor_jump_to_back(wme); break; } return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_jump_reverse(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; switch (data->jumptype) { case WINDOW_COPY_JUMPFORWARD: for (; np != 0; np--) window_copy_cursor_jump_back(wme); break; case WINDOW_COPY_JUMPBACKWARD: for (; np != 0; np--) window_copy_cursor_jump(wme); break; case WINDOW_COPY_JUMPTOFORWARD: for (; np != 0; np--) window_copy_cursor_jump_to_back(wme); break; case WINDOW_COPY_JUMPTOBACKWARD: for (; np != 0; np--) window_copy_cursor_jump_to(wme); break; } return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_middle_line(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; data->cx = 0; data->cy = (screen_size_y(&data->screen) - 1) / 2; window_copy_update_selection(wme, 1); return (WINDOW_COPY_CMD_REDRAW); } static enum window_copy_cmd_action window_copy_cmd_previous_matching_bracket(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; u_int np = wme->prefix; struct window_copy_mode_data *data = wme->data; struct screen *s = data->backing; char open[] = "{[(", close[] = "}])"; char tried, found, start, *cp; u_int px, py, xx, n; struct grid_cell gc; int failed; for (; np != 0; np--) { /* Get cursor position and line length. */ px = data->cx; py = screen_hsize(s) + data->cy - data->oy; xx = window_copy_find_length(wme, py); if (xx == 0) break; /* * Get the current character. If not on a bracket, try the * previous. If still not, then behave like previous-word. */ tried = 0; retry: grid_get_cell(s->grid, px, py, &gc); if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING)) cp = NULL; else { found = *gc.data.data; cp = strchr(close, found); } if (cp == NULL) { if (data->modekeys == MODEKEY_EMACS) { if (!tried && px > 0) { px--; tried = 1; goto retry; } window_copy_cursor_previous_word(wme, "}]) "); } continue; } start = open[cp - close]; /* Walk backward until the matching bracket is reached. */ n = 1; failed = 0; do { if (px == 0) { if (py == 0) { failed = 1; break; } do { py--; xx = window_copy_find_length(wme, py); } while (xx == 0 && py > 0); if (xx == 0 && py == 0) { failed = 1; break; } px = xx - 1; } else px--; grid_get_cell(s->grid, px, py, &gc); if (gc.data.size == 1 && (~gc.flags & GRID_FLAG_PADDING)) { if (*gc.data.data == found) n++; else if (*gc.data.data == start) n--; } } while (n != 0); /* Move the cursor to the found location if any. */ if (!failed) window_copy_scroll_to(wme, px, py); } return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_next_matching_bracket(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; u_int np = wme->prefix; struct window_copy_mode_data *data = wme->data; struct screen *s = data->backing; char open[] = "{[(", close[] = "}])"; char tried, found, end, *cp; u_int px, py, xx, yy, sx, sy, n; struct grid_cell gc; int failed; struct grid_line *gl; for (; np != 0; np--) { /* Get cursor position and line length. */ px = data->cx; py = screen_hsize(s) + data->cy - data->oy; xx = window_copy_find_length(wme, py); yy = screen_hsize(s) + screen_size_y(s) - 1; if (xx == 0) break; /* * Get the current character. If not on a bracket, try the * next. If still not, then behave like next-word. */ tried = 0; retry: grid_get_cell(s->grid, px, py, &gc); if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING)) cp = NULL; else { found = *gc.data.data; /* * In vi mode, attempt to move to previous bracket if a * closing bracket is found first. If this fails, * return to the original cursor position. */ cp = strchr(close, found); if (cp != NULL && data->modekeys == MODEKEY_VI) { sx = data->cx; sy = screen_hsize(s) + data->cy - data->oy; window_copy_scroll_to(wme, px, py); window_copy_cmd_previous_matching_bracket(cs); px = data->cx; py = screen_hsize(s) + data->cy - data->oy; grid_get_cell(s->grid, px, py, &gc); if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING) || strchr(close, *gc.data.data) == NULL) window_copy_scroll_to(wme, sx, sy); break; } cp = strchr(open, found); } if (cp == NULL) { if (data->modekeys == MODEKEY_EMACS) { if (!tried && px <= xx) { px++; tried = 1; goto retry; } window_copy_cursor_next_word_end(wme, "{[( "); continue; } /* For vi, continue searching for bracket until EOL. */ if (px > xx) { if (py == yy) continue; gl = grid_get_line(s->grid, py); if (~gl->flags & GRID_LINE_WRAPPED) continue; if (gl->cellsize > s->grid->sx) continue; px = 0; py++; xx = window_copy_find_length(wme, py); } else px++; goto retry; } end = close[cp - open]; /* Walk forward until the matching bracket is reached. */ n = 1; failed = 0; do { if (px > xx) { if (py == yy) { failed = 1; break; } px = 0; py++; xx = window_copy_find_length(wme, py); } else px++; grid_get_cell(s->grid, px, py, &gc); if (gc.data.size == 1 && (~gc.flags & GRID_FLAG_PADDING)) { if (*gc.data.data == found) n++; else if (*gc.data.data == end) n--; } } while (n != 0); /* Move the cursor to the found location if any. */ if (!failed) window_copy_scroll_to(wme, px, py); } return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_next_paragraph(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; u_int np = wme->prefix; for (; np != 0; np--) window_copy_next_paragraph(wme); return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_next_space(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; u_int np = wme->prefix; for (; np != 0; np--) window_copy_cursor_next_word(wme, " "); return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_next_space_end(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; u_int np = wme->prefix; for (; np != 0; np--) window_copy_cursor_next_word_end(wme, " "); return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_next_word(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct session *s = cs->s; u_int np = wme->prefix; const char *ws; ws = options_get_string(s->options, "word-separators"); for (; np != 0; np--) window_copy_cursor_next_word(wme, ws); return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_next_word_end(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct session *s = cs->s; u_int np = wme->prefix; const char *ws; ws = options_get_string(s->options, "word-separators"); for (; np != 0; np--) window_copy_cursor_next_word_end(wme, ws); return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_other_end(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; u_int np = wme->prefix; if ((np % 2) != 0) window_copy_other_end(wme); return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_page_down(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; for (; np != 0; np--) { if (window_copy_pagedown(wme, 0, data->scroll_exit)) return (WINDOW_COPY_CMD_CANCEL); } return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_page_down_and_cancel(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; u_int np = wme->prefix; for (; np != 0; np--) { if (window_copy_pagedown(wme, 0, 1)) return (WINDOW_COPY_CMD_CANCEL); } return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_page_up(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; u_int np = wme->prefix; for (; np != 0; np--) window_copy_pageup1(wme, 0); return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_previous_paragraph(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; u_int np = wme->prefix; for (; np != 0; np--) window_copy_previous_paragraph(wme); return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_previous_space(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; u_int np = wme->prefix; for (; np != 0; np--) window_copy_cursor_previous_word(wme, " "); return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_previous_word(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct session *s = cs->s; u_int np = wme->prefix; const char *ws; ws = options_get_string(s->options, "word-separators"); for (; np != 0; np--) window_copy_cursor_previous_word(wme, ws); return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_rectangle_toggle(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; data->lineflag = LINE_SEL_NONE; window_copy_rectangle_toggle(wme); return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_scroll_down(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; for (; np != 0; np--) window_copy_cursor_down(wme, 1); if (data->scroll_exit && data->oy == 0) return (WINDOW_COPY_CMD_CANCEL); return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_scroll_down_and_cancel(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; for (; np != 0; np--) window_copy_cursor_down(wme, 1); if (data->oy == 0) return (WINDOW_COPY_CMD_CANCEL); return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_scroll_up(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; u_int np = wme->prefix; for (; np != 0; np--) window_copy_cursor_up(wme, 1); return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_search_again(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; if (data->searchtype == WINDOW_COPY_SEARCHUP) { for (; np != 0; np--) window_copy_search_up(wme); } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { for (; np != 0; np--) window_copy_search_down(wme); } return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_search_reverse(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; if (data->searchtype == WINDOW_COPY_SEARCHUP) { for (; np != 0; np--) window_copy_search_down(wme); } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { for (; np != 0; np--) window_copy_search_up(wme); } return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_select_line(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; data->lineflag = LINE_SEL_LEFT_RIGHT; data->rectflag = 0; window_copy_cursor_start_of_line(wme); window_copy_start_selection(wme); for (; np > 1; np--) window_copy_cursor_down(wme, 0); window_copy_cursor_end_of_line(wme); return (WINDOW_COPY_CMD_REDRAW); } static enum window_copy_cmd_action window_copy_cmd_select_word(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct session *s = cs->s; struct window_copy_mode_data *data = wme->data; const char *ws; data->lineflag = LINE_SEL_LEFT_RIGHT; data->rectflag = 0; ws = options_get_string(s->options, "word-separators"); window_copy_cursor_previous_word(wme, ws); window_copy_start_selection(wme); window_copy_cursor_next_word_end(wme, ws); return (WINDOW_COPY_CMD_REDRAW); } static enum window_copy_cmd_action window_copy_cmd_start_of_line(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; window_copy_cursor_start_of_line(wme); return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_top_line(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; data->cx = 0; data->cy = 0; window_copy_update_selection(wme, 1); return (WINDOW_COPY_CMD_REDRAW); } static enum window_copy_cmd_action window_copy_cmd_copy_pipe_no_clear(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct client *c = cs->c; struct session *s = cs->s; struct winlink *wl = cs->wl; struct window_pane *wp = wme->wp; char *command = NULL; char *prefix = NULL; if (cs->args->argc == 3) prefix = format_single(NULL, cs->args->argv[2], c, s, wl, wp); if (s != NULL && *cs->args->argv[1] != '\0') { command = format_single(NULL, cs->args->argv[1], c, s, wl, wp); window_copy_copy_pipe(wme, s, prefix, command); free(command); } free(prefix); return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_copy_pipe(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; window_copy_cmd_copy_pipe_no_clear(cs); window_copy_clear_selection(wme); return (WINDOW_COPY_CMD_REDRAW); } static enum window_copy_cmd_action window_copy_cmd_copy_pipe_and_cancel(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; window_copy_cmd_copy_pipe_no_clear(cs); window_copy_clear_selection(wme); return (WINDOW_COPY_CMD_CANCEL); } static enum window_copy_cmd_action window_copy_cmd_goto_line(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; const char *argument = cs->args->argv[1]; if (*argument != '\0') window_copy_goto_line(wme, argument); return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_jump_backward(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; const char *argument = cs->args->argv[1]; if (*argument != '\0') { data->jumptype = WINDOW_COPY_JUMPBACKWARD; data->jumpchar = *argument; for (; np != 0; np--) window_copy_cursor_jump_back(wme); } return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_jump_forward(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; const char *argument = cs->args->argv[1]; if (*argument != '\0') { data->jumptype = WINDOW_COPY_JUMPFORWARD; data->jumpchar = *argument; for (; np != 0; np--) window_copy_cursor_jump(wme); } return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_jump_to_backward(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; const char *argument = cs->args->argv[1]; if (*argument != '\0') { data->jumptype = WINDOW_COPY_JUMPTOBACKWARD; data->jumpchar = *argument; for (; np != 0; np--) window_copy_cursor_jump_to_back(wme); } return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_jump_to_forward(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; const char *argument = cs->args->argv[1]; if (*argument != '\0') { data->jumptype = WINDOW_COPY_JUMPTOFORWARD; data->jumpchar = *argument; for (; np != 0; np--) window_copy_cursor_jump_to(wme); } return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_search_backward(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; const char *argument = cs->args->argv[1]; if (*argument != '\0') { data->searchtype = WINDOW_COPY_SEARCHUP; free(data->searchstr); data->searchstr = xstrdup(argument); for (; np != 0; np--) window_copy_search_up(wme); } return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_search_forward(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; u_int np = wme->prefix; const char *argument = cs->args->argv[1]; if (*argument != '\0') { data->searchtype = WINDOW_COPY_SEARCHDOWN; free(data->searchstr); data->searchstr = xstrdup(argument); for (; np != 0; np--) window_copy_search_down(wme); } return (WINDOW_COPY_CMD_NOTHING); } static enum window_copy_cmd_action window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; const char *argument = cs->args->argv[1]; const char *ss = data->searchstr; char prefix; enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING; prefix = *argument++; if (data->searchx == -1 || data->searchy == -1) { data->searchx = data->cx; data->searchy = data->cy; data->searcho = data->oy; } else if (ss != NULL && strcmp(argument, ss) != 0) { data->cx = data->searchx; data->cy = data->searchy; data->oy = data->searcho; action = WINDOW_COPY_CMD_REDRAW; } if (*argument == '\0') { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } switch (prefix) { case '=': case '-': data->searchtype = WINDOW_COPY_SEARCHUP; free(data->searchstr); data->searchstr = xstrdup(argument); if (!window_copy_search_up(wme)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } break; case '+': data->searchtype = WINDOW_COPY_SEARCHDOWN; free(data->searchstr); data->searchstr = xstrdup(argument); if (!window_copy_search_down(wme)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } break; } return (action); } static enum window_copy_cmd_action window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; struct window_copy_mode_data *data = wme->data; const char *argument = cs->args->argv[1]; const char *ss = data->searchstr; char prefix; enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING; prefix = *argument++; if (data->searchx == -1 || data->searchy == -1) { data->searchx = data->cx; data->searchy = data->cy; data->searcho = data->oy; } else if (ss != NULL && strcmp(argument, ss) != 0) { data->cx = data->searchx; data->cy = data->searchy; data->oy = data->searcho; action = WINDOW_COPY_CMD_REDRAW; } if (*argument == '\0') { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } switch (prefix) { case '=': case '+': data->searchtype = WINDOW_COPY_SEARCHDOWN; free(data->searchstr); data->searchstr = xstrdup(argument); if (!window_copy_search_down(wme)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } break; case '-': data->searchtype = WINDOW_COPY_SEARCHUP; free(data->searchstr); data->searchstr = xstrdup(argument); if (!window_copy_search_up(wme)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } } return (action); } static const struct { const char *command; int minargs; int maxargs; enum window_copy_cmd_action (*f)(struct window_copy_cmd_state *); } window_copy_cmd_table[] = { { "append-selection", 0, 0, window_copy_cmd_append_selection }, { "append-selection-and-cancel", 0, 0, window_copy_cmd_append_selection_and_cancel }, { "back-to-indentation", 0, 0, window_copy_cmd_back_to_indentation }, { "begin-selection", 0, 0, window_copy_cmd_begin_selection }, { "bottom-line", 0, 0, window_copy_cmd_bottom_line }, { "cancel", 0, 0, window_copy_cmd_cancel }, { "clear-selection", 0, 0, window_copy_cmd_clear_selection }, { "copy-end-of-line", 0, 1, window_copy_cmd_copy_end_of_line }, { "copy-line", 0, 1, window_copy_cmd_copy_line }, { "copy-pipe-no-clear", 1, 2, window_copy_cmd_copy_pipe_no_clear }, { "copy-pipe", 1, 2, window_copy_cmd_copy_pipe }, { "copy-pipe-and-cancel", 1, 2, window_copy_cmd_copy_pipe_and_cancel }, { "copy-selection-no-clear", 0, 1, window_copy_cmd_copy_selection_no_clear }, { "copy-selection", 0, 1, window_copy_cmd_copy_selection }, { "copy-selection-and-cancel", 0, 1, window_copy_cmd_copy_selection_and_cancel }, { "cursor-down", 0, 0, window_copy_cmd_cursor_down }, { "cursor-left", 0, 0, window_copy_cmd_cursor_left }, { "cursor-right", 0, 0, window_copy_cmd_cursor_right }, { "cursor-up", 0, 0, window_copy_cmd_cursor_up }, { "end-of-line", 0, 0, window_copy_cmd_end_of_line }, { "goto-line", 1, 1, window_copy_cmd_goto_line }, { "halfpage-down", 0, 0, window_copy_cmd_halfpage_down }, { "halfpage-down-and-cancel", 0, 0, window_copy_cmd_halfpage_down_and_cancel }, { "halfpage-up", 0, 0, window_copy_cmd_halfpage_up }, { "history-bottom", 0, 0, window_copy_cmd_history_bottom }, { "history-top", 0, 0, window_copy_cmd_history_top }, { "jump-again", 0, 0, window_copy_cmd_jump_again }, { "jump-backward", 1, 1, window_copy_cmd_jump_backward }, { "jump-forward", 1, 1, window_copy_cmd_jump_forward }, { "jump-reverse", 0, 0, window_copy_cmd_jump_reverse }, { "jump-to-backward", 1, 1, window_copy_cmd_jump_to_backward }, { "jump-to-forward", 1, 1, window_copy_cmd_jump_to_forward }, { "middle-line", 0, 0, window_copy_cmd_middle_line }, { "next-matching-bracket", 0, 0, window_copy_cmd_next_matching_bracket }, { "next-paragraph", 0, 0, window_copy_cmd_next_paragraph }, { "next-space", 0, 0, window_copy_cmd_next_space }, { "next-space-end", 0, 0, window_copy_cmd_next_space_end }, { "next-word", 0, 0, window_copy_cmd_next_word }, { "next-word-end", 0, 0, window_copy_cmd_next_word_end }, { "other-end", 0, 0, window_copy_cmd_other_end }, { "page-down", 0, 0, window_copy_cmd_page_down }, { "page-down-and-cancel", 0, 0, window_copy_cmd_page_down_and_cancel }, { "page-up", 0, 0, window_copy_cmd_page_up }, { "previous-matching-bracket", 0, 0, window_copy_cmd_previous_matching_bracket }, { "previous-paragraph", 0, 0, window_copy_cmd_previous_paragraph }, { "previous-space", 0, 0, window_copy_cmd_previous_space }, { "previous-word", 0, 0, window_copy_cmd_previous_word }, { "rectangle-toggle", 0, 0, window_copy_cmd_rectangle_toggle }, { "scroll-down", 0, 0, window_copy_cmd_scroll_down }, { "scroll-down-and-cancel", 0, 0, window_copy_cmd_scroll_down_and_cancel }, { "scroll-up", 0, 0, window_copy_cmd_scroll_up }, { "search-again", 0, 0, window_copy_cmd_search_again }, { "search-backward", 1, 1, window_copy_cmd_search_backward }, { "search-backward-incremental", 1, 1, window_copy_cmd_search_backward_incremental }, { "search-forward", 1, 1, window_copy_cmd_search_forward }, { "search-forward-incremental", 1, 1, window_copy_cmd_search_forward_incremental }, { "search-reverse", 0, 0, window_copy_cmd_search_reverse }, { "select-line", 0, 0, window_copy_cmd_select_line }, { "select-word", 0, 0, window_copy_cmd_select_word }, { "start-of-line", 0, 0, window_copy_cmd_start_of_line }, { "stop-selection", 0, 0, window_copy_cmd_stop_selection }, { "top-line", 0, 0, window_copy_cmd_top_line }, }; static void window_copy_command(struct window_mode_entry *wme, struct client *c, struct session *s, struct winlink *wl, struct args *args, struct mouse_event *m) { struct window_copy_mode_data *data = wme->data; struct window_copy_cmd_state cs; enum window_copy_cmd_action action; const char *command; u_int i; if (args->argc == 0) return; command = args->argv[0]; if (m != NULL && m->valid && !MOUSE_WHEEL(m->b)) window_copy_move_mouse(m); cs.wme = wme; cs.args = args; cs.m = m; cs.c = c; cs.s = s; cs.wl = wl; action = WINDOW_COPY_CMD_NOTHING; for (i = 0; i < nitems(window_copy_cmd_table); i++) { if (strcmp(window_copy_cmd_table[i].command, command) == 0) { if (args->argc - 1 < window_copy_cmd_table[i].minargs || args->argc - 1 > window_copy_cmd_table[i].maxargs) break; action = window_copy_cmd_table[i].f (&cs); break; } } if (strncmp(command, "search-", 7) != 0 && data->searchmark != NULL) { window_copy_clear_marks(wme); if (action == WINDOW_COPY_CMD_NOTHING) action = WINDOW_COPY_CMD_REDRAW; data->searchx = data->searchy = -1; } wme->prefix = 1; if (action == WINDOW_COPY_CMD_CANCEL) window_pane_reset_mode(wme->wp); else if (action == WINDOW_COPY_CMD_REDRAW) window_copy_redraw_screen(wme); } static void window_copy_scroll_to(struct window_mode_entry *wme, u_int px, u_int py) { struct window_copy_mode_data *data = wme->data; struct grid *gd = data->backing->grid; u_int offset, gap; data->cx = px; if (py >= gd->hsize - data->oy && py < gd->hsize - data->oy + gd->sy) data->cy = py - (gd->hsize - data->oy); else { 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(wme, 1); window_copy_redraw_screen(wme); } static int window_copy_search_compare(struct grid *gd, u_int px, u_int py, struct grid *sgd, u_int spx, int cis) { struct grid_cell gc, sgc; const struct utf8_data *ud, *sud; grid_get_cell(gd, px, py, &gc); ud = &gc.data; grid_get_cell(sgd, spx, 0, &sgc); sud = &sgc.data; if (ud->size != sud->size || ud->width != sud->width) return (0); if (cis && ud->size == 1) return (tolower(ud->data[0]) == sud->data[0]); return (memcmp(ud->data, sud->data, ud->size) == 0); } static int window_copy_search_lr(struct grid *gd, struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis) { u_int ax, bx, px; int matched; for (ax = first; ax < last; ax++) { if (ax + sgd->sx > gd->sx) break; for (bx = 0; bx < sgd->sx; bx++) { px = ax + bx; matched = window_copy_search_compare(gd, px, py, sgd, bx, cis); if (!matched) break; } if (bx == sgd->sx) { *ppx = ax; return (1); } } return (0); } static int window_copy_search_rl(struct grid *gd, struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis) { u_int ax, bx, px; int matched; 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; matched = window_copy_search_compare(gd, px, py, sgd, bx, cis); if (!matched) break; } if (bx == sgd->sx) { *ppx = ax - 1; return (1); } } return (0); } static void window_copy_move_left(struct screen *s, u_int *fx, u_int *fy) { if (*fx == 0) { /* left */ if (*fy == 0) /* top */ return; *fx = screen_size_x(s) - 1; *fy = *fy - 1; } else *fx = *fx - 1; } static void window_copy_move_right(struct screen *s, u_int *fx, u_int *fy) { if (*fx == screen_size_x(s) - 1) { /* right */ if (*fy == screen_hsize(s) + screen_size_y(s)) /* bottom */ return; *fx = 0; *fy = *fy + 1; } else *fx = *fx + 1; } static int window_copy_is_lowercase(const char *ptr) { while (*ptr != '\0') { if (*ptr != tolower((u_char)*ptr)) return (0); ++ptr; } return (1); } /* * Search for text stored in sgd starting from position fx,fy up to endline. If * found, jump to it. If cis then ignore case. The direction is 0 for searching * up, down otherwise. If wrap then go to begin/end of grid and try again if * not found. */ static int window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, struct grid *sgd, u_int fx, u_int fy, u_int endline, int cis, int wrap, int direction) { u_int i, px; int found; found = 0; if (direction) { for (i = fy; i <= endline; i++) { found = window_copy_search_lr(gd, sgd, &px, i, fx, gd->sx, cis); if (found) break; fx = 0; } } else { for (i = fy + 1; endline < i; i--) { found = window_copy_search_rl(gd, sgd, &px, i - 1, 0, fx, cis); if (found) { i--; break; } fx = gd->sx; } } if (found) { window_copy_scroll_to(wme, px, i); return (1); } if (wrap) { return (window_copy_search_jump(wme, gd, sgd, direction ? 0 : gd->sx - 1, direction ? 0 : gd->hsize + gd->sy - 1, fy, cis, 0, direction)); } return (0); } /* * Search in for text searchstr. If direction is 0 then search up, otherwise * down. */ static int window_copy_search(struct window_mode_entry *wme, int direction) { struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; struct screen *s = data->backing, ss; struct screen_write_ctx ctx; struct grid *gd = s->grid; u_int fx, fy, endline; int wrapflag, cis, found; free(wp->searchstr); wp->searchstr = xstrdup(data->searchstr); fx = data->cx; fy = screen_hsize(data->backing) - data->oy + data->cy; screen_init(&ss, screen_write_strlen("%s", data->searchstr), 1, 0); screen_write_start(&ctx, NULL, &ss); screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", data->searchstr); screen_write_stop(&ctx); if (direction) window_copy_move_right(s, &fx, &fy); else window_copy_move_left(s, &fx, &fy); wrapflag = options_get_number(wp->window->options, "wrap-search"); cis = window_copy_is_lowercase(data->searchstr); if (direction) endline = gd->hsize + gd->sy - 1; else endline = 0; found = window_copy_search_jump(wme, gd, ss.grid, fx, fy, endline, cis, wrapflag, direction); if (window_copy_search_marks(wme, &ss)) window_copy_redraw_screen(wme); screen_free(&ss); return (found); } static int window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp) { struct window_copy_mode_data *data = wme->data; struct screen *s = data->backing, ss; struct screen_write_ctx ctx; struct grid *gd = s->grid; int found, cis, which = -1; u_int px, py, b, nfound = 0, width; if (ssp == NULL) { width = screen_write_strlen("%s", data->searchstr); screen_init(&ss, width, 1, 0); screen_write_start(&ctx, NULL, &ss); screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", data->searchstr); screen_write_stop(&ctx); ssp = &ss; } else width = screen_size_x(ssp); cis = window_copy_is_lowercase(data->searchstr); free(data->searchmark); data->searchmark = bit_alloc((gd->hsize + gd->sy) * gd->sx); for (py = 0; py < gd->hsize + gd->sy; py++) { px = 0; for (;;) { found = window_copy_search_lr(gd, ssp->grid, &px, py, px, gd->sx, cis); if (!found) break; nfound++; if (px == data->cx && py == gd->hsize + data->cy - data->oy) which = nfound; b = (py * gd->sx) + px; bit_nset(data->searchmark, b, b + width - 1); px++; } } if (which != -1) data->searchthis = 1 + nfound - which; else data->searchthis = -1; data->searchcount = nfound; if (ssp == &ss) screen_free(&ss); return (nfound); } static void window_copy_clear_marks(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; free(data->searchmark); data->searchmark = NULL; } static int window_copy_search_up(struct window_mode_entry *wme) { return (window_copy_search(wme, 0)); } static int window_copy_search_down(struct window_mode_entry *wme) { return (window_copy_search(wme, 1)); } static void window_copy_goto_line(struct window_mode_entry *wme, const char *linestr) { struct window_copy_mode_data *data = wme->data; const char *errstr; int lineno; lineno = strtonum(linestr, -1, INT_MAX, &errstr); if (errstr != NULL) return; if (lineno < 0 || (u_int)lineno > screen_hsize(data->backing)) lineno = screen_hsize(data->backing); data->oy = lineno; window_copy_update_selection(wme, 1); window_copy_redraw_screen(wme); } static void window_copy_write_line(struct window_mode_entry *wme, struct screen_write_ctx *ctx, u_int py) { struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; struct options *oo = wp->window->options; struct grid_cell gc; char hdr[512]; size_t size = 0; style_apply(&gc, oo, "mode-style"); gc.flags |= GRID_FLAG_NOPALETTE; if (py == 0 && s->rupper < s->rlower) { if (data->searchmark == NULL) { size = xsnprintf(hdr, sizeof hdr, "[%u/%u]", data->oy, screen_hsize(data->backing)); } else { if (data->searchthis == -1) { size = xsnprintf(hdr, sizeof hdr, "(%u results) [%d/%u]", data->searchcount, data->oy, screen_hsize(data->backing)); } else { size = xsnprintf(hdr, sizeof hdr, "(%u/%u results) [%d/%u]", data->searchthis, data->searchcount, 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, 0); screen_write_puts(ctx, &gc, "%s", hdr); } else size = 0; if (size < screen_size_x(s)) { screen_write_cursormove(ctx, 0, py, 0); screen_write_copy(ctx, data->backing, 0, (screen_hsize(data->backing) - data->oy) + py, screen_size_x(s) - size, 1, data->searchmark, &gc); } 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, 0); screen_write_putc(ctx, &gc, '$'); } } static void window_copy_write_lines(struct window_mode_entry *wme, 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(wme, ctx, py); } static void window_copy_redraw_selection(struct window_mode_entry *wme, u_int old_y) { struct window_copy_mode_data *data = wme->data; u_int new_y, start, end; new_y = data->cy; if (old_y <= new_y) { start = old_y; end = new_y; } else { start = new_y; end = old_y; } window_copy_redraw_lines(wme, start, end - start + 1); } static void window_copy_redraw_lines(struct window_mode_entry *wme, u_int py, u_int ny) { struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; 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(wme, &ctx, i); screen_write_cursormove(&ctx, data->cx, data->cy, 0); screen_write_stop(&ctx); } static void window_copy_redraw_screen(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; window_copy_redraw_lines(wme, 0, screen_size_y(&data->screen)); } static void window_copy_synchronize_cursor(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; u_int xx, yy; xx = data->cx; yy = screen_hsize(data->backing) + data->cy - data->oy; switch (data->cursordrag) { case CURSORDRAG_ENDSEL: data->endselx = xx; data->endsely = yy; break; case CURSORDRAG_SEL: data->selx = xx; data->sely = yy; break; case CURSORDRAG_NONE: break; } } static void window_copy_update_cursor(struct window_mode_entry *wme, u_int cx, u_int cy) { struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; 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(wme, old_cy, 1); if (data->cx == screen_size_x(s)) window_copy_redraw_lines(wme, data->cy, 1); else { screen_write_start(&ctx, wp, NULL); screen_write_cursormove(&ctx, data->cx, data->cy, 0); screen_write_stop(&ctx); } } static void window_copy_start_selection(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; data->selx = data->cx; data->sely = screen_hsize(data->backing) + data->cy - data->oy; data->endselx = data->selx; data->endsely = data->sely; data->cursordrag = CURSORDRAG_ENDSEL; window_copy_set_selection(wme, 1); } static int window_copy_adjust_selection(struct window_mode_entry *wme, u_int *selx, u_int *sely) { struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; u_int sx, sy, ty; int relpos; sx = *selx; sy = *sely; ty = screen_hsize(data->backing) - data->oy; if (sy < ty) { relpos = WINDOW_COPY_REL_POS_ABOVE; if (!data->rectflag) sx = 0; sy = 0; } else if (sy > ty + screen_size_y(s) - 1) { relpos = WINDOW_COPY_REL_POS_BELOW; if (!data->rectflag) sx = screen_size_x(s) - 1; sy = screen_size_y(s) - 1; } else { relpos = WINDOW_COPY_REL_POS_ON_SCREEN; sy -= ty; } *selx = sx; *sely = sy; return (relpos); } static int window_copy_update_selection(struct window_mode_entry *wme, int may_redraw) { struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; if (s->sel == NULL && data->lineflag == LINE_SEL_NONE) return (0); return (window_copy_set_selection(wme, may_redraw)); } static int window_copy_set_selection(struct window_mode_entry *wme, int may_redraw) { struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; struct options *oo = wp->window->options; struct grid_cell gc; u_int sx, sy, cy, endsx, endsy; int startrelpos, endrelpos; window_copy_synchronize_cursor(wme); /* Adjust the selection. */ sx = data->selx; sy = data->sely; startrelpos = window_copy_adjust_selection(wme, &sx, &sy); /* Adjust the end of selection. */ endsx = data->endselx; endsy = data->endsely; endrelpos = window_copy_adjust_selection(wme, &endsx, &endsy); /* Selection is outside of the current screen */ if (startrelpos == endrelpos && startrelpos != WINDOW_COPY_REL_POS_ON_SCREEN) { screen_hide_selection(s); return (0); } /* Set colours and selection. */ style_apply(&gc, oo, "mode-style"); gc.flags |= GRID_FLAG_NOPALETTE; screen_set_selection(s, sx, sy, endsx, endsy, data->rectflag, data->modekeys, &gc); if (data->rectflag && may_redraw) { /* * 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 (data->cursordrag == CURSORDRAG_ENDSEL) { if (sy < cy) window_copy_redraw_lines(wme, sy, cy - sy + 1); else window_copy_redraw_lines(wme, cy, sy - cy + 1); } else { if (endsy < cy) { window_copy_redraw_lines(wme, endsy, cy - endsy + 1); } else { window_copy_redraw_lines(wme, cy, endsy - cy + 1); } } } return (1); } static void * window_copy_get_selection(struct window_mode_entry *wme, size_t *len) { struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; char *buf; size_t off; u_int i, xx, yy, sx, sy, ex, ey, ey_last; u_int firstsx, lastex, restex, restsx, selx; int keys; if (data->screen.sel == NULL && data->lineflag == LINE_SEL_NONE) return (NULL); 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->endselx; yy = data->endsely; 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. */ ey_last = window_copy_find_length(wme, ey); if (ex > ey_last) ex = ey_last; /* * 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->cursordrag == CURSORDRAG_ENDSEL) selx = data->selx; else selx = data->endselx; if (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 = selx; restsx = selx; } else { /* Cursor is on the left. */ lastex = selx + 1; restex = 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. */ for (i = sy; i <= ey; i++) { window_copy_copy_line(wme, &buf, &off, i, (i == sy ? firstsx : restsx), (i == ey ? lastex : restex)); } /* Don't bother if no data. */ if (off == 0) { free(buf); return (NULL); } if (keys == MODEKEY_EMACS || lastex <= ey_last) off -= 1; /* remove final \n (unless at end in vi mode) */ *len = off; return (buf); } static void window_copy_copy_buffer(struct window_mode_entry *wme, const char *prefix, void *buf, size_t len) { struct window_pane *wp = wme->wp; struct screen_write_ctx ctx; if (options_get_number(global_options, "set-clipboard") != 0) { screen_write_start(&ctx, wp, NULL); screen_write_setselection(&ctx, buf, len); screen_write_stop(&ctx); notify_pane("pane-set-clipboard", wp); } paste_add(prefix, buf, len); } static void window_copy_copy_pipe(struct window_mode_entry *wme, struct session *s, const char *prefix, const char *command) { void *buf; size_t len; struct job *job; buf = window_copy_get_selection(wme, &len); if (buf == NULL) return; job = job_run(command, s, NULL, NULL, NULL, NULL, NULL, JOB_NOWAIT); bufferevent_write(job_get_event(job), buf, len); window_copy_copy_buffer(wme, prefix, buf, len); } static void window_copy_copy_selection(struct window_mode_entry *wme, const char *prefix) { char *buf; size_t len; buf = window_copy_get_selection(wme, &len); if (buf != NULL) window_copy_copy_buffer(wme, prefix, buf, len); } static void window_copy_append_selection(struct window_mode_entry *wme) { struct window_pane *wp = wme->wp; char *buf; struct paste_buffer *pb; const char *bufdata, *bufname = NULL; size_t len, bufsize; struct screen_write_ctx ctx; buf = window_copy_get_selection(wme, &len); if (buf == NULL) return; if (options_get_number(global_options, "set-clipboard") != 0) { screen_write_start(&ctx, wp, NULL); screen_write_setselection(&ctx, buf, len); screen_write_stop(&ctx); notify_pane("pane-set-clipboard", wp); } pb = paste_get_top(&bufname); if (pb != NULL) { bufdata = paste_buffer_data(pb, &bufsize); buf = xrealloc(buf, len + bufsize); memmove(buf + bufsize, buf, len); memcpy(buf, bufdata, bufsize); len += bufsize; } if (paste_set(buf, len, bufname, NULL) != 0) free(buf); } static void window_copy_copy_line(struct window_mode_entry *wme, char **buf, size_t *off, u_int sy, u_int sx, u_int ex) { struct window_copy_mode_data *data = wme->data; struct grid *gd = data->backing->grid; struct grid_cell gc; struct grid_line *gl; struct utf8_data ud; u_int i, xx, wrapped = 0; const char *s; if (sx > ex) return; /* * Work out if the line was wrapped at the screen edge and all of it is * on screen. */ gl = grid_get_line(gd, 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(wme, sy); if (ex > xx) ex = xx; if (sx > xx) sx = xx; if (sx < ex) { for (i = sx; i < ex; i++) { grid_get_cell(gd, i, sy, &gc); if (gc.flags & GRID_FLAG_PADDING) continue; utf8_copy(&ud, &gc.data); if (ud.size == 1 && (gc.attr & GRID_ATTR_CHARSET)) { s = tty_acs_get(NULL, ud.data[0]); if (s != NULL && strlen(s) <= sizeof ud.data) { ud.size = strlen(s); memcpy(ud.data, s, ud.size); } } *buf = xrealloc(*buf, (*off) + ud.size); memcpy(*buf + *off, ud.data, ud.size); *off += ud.size; } } /* Only add a newline if the line wasn't wrapped. */ if (!wrapped || ex != xx) { *buf = xrealloc(*buf, (*off) + 1); (*buf)[(*off)++] = '\n'; } } static void window_copy_clear_selection(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; u_int px, py; screen_clear_selection(&data->screen); data->cursordrag = CURSORDRAG_NONE; data->lineflag = LINE_SEL_NONE; py = screen_hsize(data->backing) + data->cy - data->oy; px = window_copy_find_length(wme, py); if (data->cx > px) window_copy_update_cursor(wme, px, data->cy); } static int window_copy_in_set(struct window_mode_entry *wme, u_int px, u_int py, const char *set) { struct window_copy_mode_data *data = wme->data; struct grid_cell gc; grid_get_cell(data->backing->grid, px, py, &gc); if (gc.flags & GRID_FLAG_PADDING) return (0); return (utf8_cstrhas(set, &gc.data)); } static u_int window_copy_find_length(struct window_mode_entry *wme, u_int py) { struct window_copy_mode_data *data = wme->data; return (grid_line_length(data->backing->grid, py)); } static void window_copy_cursor_start_of_line(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; struct screen *back_s = data->backing; struct grid *gd = back_s->grid; u_int py; if (data->cx == 0 && data->lineflag == LINE_SEL_NONE) { py = screen_hsize(back_s) + data->cy - data->oy; while (py > 0 && grid_get_line(gd, py - 1)->flags & GRID_LINE_WRAPPED) { window_copy_cursor_up(wme, 0); py = screen_hsize(back_s) + data->cy - data->oy; } } window_copy_update_cursor(wme, 0, data->cy); if (window_copy_update_selection(wme, 1)) window_copy_redraw_lines(wme, data->cy, 1); } static void window_copy_cursor_back_to_indentation(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; u_int px, py, xx; struct grid_cell gc; px = 0; py = screen_hsize(data->backing) + data->cy - data->oy; xx = window_copy_find_length(wme, py); while (px < xx) { grid_get_cell(data->backing->grid, px, py, &gc); if (gc.data.size != 1 || *gc.data.data != ' ') break; px++; } window_copy_update_cursor(wme, px, data->cy); if (window_copy_update_selection(wme, 1)) window_copy_redraw_lines(wme, data->cy, 1); } static void window_copy_cursor_end_of_line(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; struct screen *back_s = data->backing; struct grid *gd = back_s->grid; struct grid_line *gl; u_int px, py; py = screen_hsize(back_s) + data->cy - data->oy; px = window_copy_find_length(wme, py); if (data->cx == px && data->lineflag == LINE_SEL_NONE) { if (data->screen.sel != NULL && data->rectflag) px = screen_size_x(back_s); gl = grid_get_line(gd, py); if (gl->flags & GRID_LINE_WRAPPED) { while (py < gd->sy + gd->hsize) { gl = grid_get_line(gd, py); if (~gl->flags & GRID_LINE_WRAPPED) break; window_copy_cursor_down(wme, 0); py = screen_hsize(back_s) + data->cy - data->oy; } px = window_copy_find_length(wme, py); } } window_copy_update_cursor(wme, px, data->cy); if (window_copy_update_selection(wme, 1)) window_copy_redraw_lines(wme, data->cy, 1); } static void window_copy_other_end(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; u_int selx, sely, cy, yy, hsize; if (s->sel == NULL && data->lineflag == LINE_SEL_NONE) return; if (data->lineflag == LINE_SEL_LEFT_RIGHT) data->lineflag = LINE_SEL_RIGHT_LEFT; else if (data->lineflag == LINE_SEL_RIGHT_LEFT) data->lineflag = LINE_SEL_LEFT_RIGHT; switch (data->cursordrag) { case CURSORDRAG_NONE: case CURSORDRAG_SEL: data->cursordrag = CURSORDRAG_ENDSEL; break; case CURSORDRAG_ENDSEL: data->cursordrag = CURSORDRAG_SEL; break; } selx = data->endselx; sely = data->endsely; if (data->cursordrag == CURSORDRAG_SEL) { selx = data->selx; sely = data->sely; } cy = data->cy; yy = screen_hsize(data->backing) + data->cy - data->oy; data->cx = selx; hsize = screen_hsize(data->backing); if (sely < hsize - data->oy) { /* above */ data->oy = hsize - sely; data->cy = 0; } else if (sely > hsize - data->oy + screen_size_y(s)) { /* below */ data->oy = hsize - sely + screen_size_y(s) - 1; data->cy = screen_size_y(s) - 1; } else data->cy = cy + sely - yy; window_copy_update_selection(wme, 1); window_copy_redraw_screen(wme); } static void window_copy_cursor_left(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; u_int py, cx; struct grid_cell gc; py = screen_hsize(data->backing) + data->cy - data->oy; cx = data->cx; while (cx > 0) { grid_get_cell(data->backing->grid, cx, py, &gc); if (~gc.flags & GRID_FLAG_PADDING) break; cx--; } if (cx == 0 && py > 0) { window_copy_cursor_up(wme, 0); window_copy_cursor_end_of_line(wme); } else if (cx > 0) { window_copy_update_cursor(wme, cx - 1, data->cy); if (window_copy_update_selection(wme, 1)) window_copy_redraw_lines(wme, data->cy, 1); } } static void window_copy_cursor_right(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; u_int px, py, yy, cx, cy; struct grid_cell gc; py = screen_hsize(data->backing) + data->cy - data->oy; yy = screen_hsize(data->backing) + screen_size_y(data->backing) - 1; if (data->screen.sel != NULL && data->rectflag) px = screen_size_x(&data->screen); else px = window_copy_find_length(wme, py); if (data->cx >= px && py < yy) { window_copy_cursor_start_of_line(wme); window_copy_cursor_down(wme, 0); } else if (data->cx < px) { cx = data->cx + 1; cy = screen_hsize(data->backing) + data->cy - data->oy; while (cx < px) { grid_get_cell(data->backing->grid, cx, cy, &gc); if (~gc.flags & GRID_FLAG_PADDING) break; cx++; } window_copy_update_cursor(wme, cx, data->cy); if (window_copy_update_selection(wme, 1)) window_copy_redraw_lines(wme, data->cy, 1); } } static void window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only) { struct window_copy_mode_data *data = wme->data; 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(wme, oy); if (data->cx != ox) { data->lastcx = data->cx; data->lastsx = ox; } if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely) window_copy_other_end(wme); if (scroll_only || data->cy == 0) { data->cx = data->lastcx; window_copy_scroll_down(wme, 1); if (scroll_only) { if (data->cy == screen_size_y(s) - 1) window_copy_redraw_lines(wme, data->cy, 1); else window_copy_redraw_lines(wme, data->cy, 2); } } else { window_copy_update_cursor(wme, data->lastcx, data->cy - 1); if (window_copy_update_selection(wme, 1)) { if (data->cy == screen_size_y(s) - 1) window_copy_redraw_lines(wme, data->cy, 1); else window_copy_redraw_lines(wme, data->cy, 2); } } if (data->screen.sel == NULL || !data->rectflag) { py = screen_hsize(data->backing) + data->cy - data->oy; px = window_copy_find_length(wme, py); if ((data->cx >= data->lastsx && data->cx != px) || data->cx > px) window_copy_cursor_end_of_line(wme); } if (data->lineflag == LINE_SEL_LEFT_RIGHT) window_copy_cursor_end_of_line(wme); else if (data->lineflag == LINE_SEL_RIGHT_LEFT) window_copy_cursor_start_of_line(wme); } static void window_copy_cursor_down(struct window_mode_entry *wme, int scroll_only) { struct window_copy_mode_data *data = wme->data; 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(wme, oy); if (data->cx != ox) { data->lastcx = data->cx; data->lastsx = ox; } if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely) window_copy_other_end(wme); if (scroll_only || data->cy == screen_size_y(s) - 1) { data->cx = data->lastcx; window_copy_scroll_up(wme, 1); if (scroll_only && data->cy > 0) window_copy_redraw_lines(wme, data->cy - 1, 2); } else { window_copy_update_cursor(wme, data->lastcx, data->cy + 1); if (window_copy_update_selection(wme, 1)) window_copy_redraw_lines(wme, data->cy - 1, 2); } if (data->screen.sel == NULL || !data->rectflag) { py = screen_hsize(data->backing) + data->cy - data->oy; px = window_copy_find_length(wme, py); if ((data->cx >= data->lastsx && data->cx != px) || data->cx > px) window_copy_cursor_end_of_line(wme); } if (data->lineflag == LINE_SEL_LEFT_RIGHT) window_copy_cursor_end_of_line(wme); else if (data->lineflag == LINE_SEL_RIGHT_LEFT) window_copy_cursor_start_of_line(wme); } static void window_copy_cursor_jump(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; struct screen *back_s = data->backing; 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(wme, py); while (px < xx) { grid_get_cell(back_s->grid, px, py, &gc); if (!(gc.flags & GRID_FLAG_PADDING) && gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wme, px, data->cy); if (window_copy_update_selection(wme, 1)) window_copy_redraw_lines(wme, data->cy, 1); return; } px++; } } static void window_copy_cursor_jump_back(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; struct screen *back_s = data->backing; 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 (;;) { grid_get_cell(back_s->grid, px, py, &gc); if (!(gc.flags & GRID_FLAG_PADDING) && gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wme, px, data->cy); if (window_copy_update_selection(wme, 1)) window_copy_redraw_lines(wme, data->cy, 1); return; } if (px == 0) break; px--; } } static void window_copy_cursor_jump_to(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; struct screen *back_s = data->backing; struct grid_cell gc; u_int px, py, xx; px = data->cx + 2; py = screen_hsize(back_s) + data->cy - data->oy; xx = window_copy_find_length(wme, py); while (px < xx) { grid_get_cell(back_s->grid, px, py, &gc); if (!(gc.flags & GRID_FLAG_PADDING) && gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wme, px - 1, data->cy); if (window_copy_update_selection(wme, 1)) window_copy_redraw_lines(wme, data->cy, 1); return; } px++; } } static void window_copy_cursor_jump_to_back(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; struct screen *back_s = data->backing; struct grid_cell gc; u_int px, py; px = data->cx; py = screen_hsize(back_s) + data->cy - data->oy; if (px > 0) px--; if (px > 0) px--; for (;;) { grid_get_cell(back_s->grid, px, py, &gc); if (!(gc.flags & GRID_FLAG_PADDING) && gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wme, px + 1, data->cy); if (window_copy_update_selection(wme, 1)) window_copy_redraw_lines(wme, data->cy, 1); return; } if (px == 0) break; px--; } } static void window_copy_cursor_next_word(struct window_mode_entry *wme, const char *separators) { struct window_copy_mode_data *data = wme->data; 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(wme, 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(wme, 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(wme, 0); px = 0; py = screen_hsize(back_s) + data->cy - data->oy; xx = window_copy_find_length(wme, py); } else px++; } expected = !expected; } while (expected == 1); window_copy_update_cursor(wme, px, data->cy); if (window_copy_update_selection(wme, 1)) window_copy_redraw_lines(wme, data->cy, 1); } static void window_copy_cursor_next_word_end(struct window_mode_entry *wme, const char *separators) { struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; struct options *oo = wp->window->options; struct screen *back_s = data->backing; u_int px, py, xx, yy; int keys, expected = 1; px = data->cx; py = screen_hsize(back_s) + data->cy - data->oy; xx = window_copy_find_length(wme, py); yy = screen_hsize(back_s) + screen_size_y(back_s) - 1; keys = options_get_number(oo, "mode-keys"); if (keys == MODEKEY_VI && !window_copy_in_set(wme, px, py, separators)) px++; /* * 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(wme, 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(wme, 0); px = 0; py = screen_hsize(back_s) + data->cy - data->oy; xx = window_copy_find_length(wme, py); } else px++; } expected = !expected; } while (expected == 0); if (keys == MODEKEY_VI && px != 0) px--; window_copy_update_cursor(wme, px, data->cy); if (window_copy_update_selection(wme, 1)) window_copy_redraw_lines(wme, data->cy, 1); } /* Move to the previous place where a word begins. */ static void window_copy_cursor_previous_word(struct window_mode_entry *wme, const char *separators) { struct window_copy_mode_data *data = wme->data; 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(wme, 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(wme, 0); py = screen_hsize(data->backing) + data->cy - data->oy; px = window_copy_find_length(wme, py); /* Stop if separator at EOL. */ if (px > 0 && window_copy_in_set(wme, px - 1, py, separators)) break; } } /* Move back to the beginning of this word. */ while (px > 0 && !window_copy_in_set(wme, px - 1, py, separators)) px--; out: window_copy_update_cursor(wme, px, data->cy); if (window_copy_update_selection(wme, 1)) window_copy_redraw_lines(wme, data->cy, 1); } static void window_copy_scroll_up(struct window_mode_entry *wme, u_int ny) { struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; struct screen *s = &data->screen; struct screen_write_ctx ctx; if (data->oy < ny) ny = data->oy; if (ny == 0) return; data->oy -= ny; window_copy_update_selection(wme, 0); screen_write_start(&ctx, wp, NULL); screen_write_cursormove(&ctx, 0, 0, 0); screen_write_deleteline(&ctx, ny, 8); window_copy_write_lines(wme, &ctx, screen_size_y(s) - ny, ny); window_copy_write_line(wme, &ctx, 0); if (screen_size_y(s) > 1) window_copy_write_line(wme, &ctx, 1); if (screen_size_y(s) > 3) window_copy_write_line(wme, &ctx, screen_size_y(s) - 2); if (s->sel != NULL && screen_size_y(s) > ny) window_copy_write_line(wme, &ctx, screen_size_y(s) - ny - 1); screen_write_cursormove(&ctx, data->cx, data->cy, 0); screen_write_stop(&ctx); } static void window_copy_scroll_down(struct window_mode_entry *wme, u_int ny) { struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; 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; window_copy_update_selection(wme, 0); screen_write_start(&ctx, wp, NULL); screen_write_cursormove(&ctx, 0, 0, 0); screen_write_insertline(&ctx, ny, 8); window_copy_write_lines(wme, &ctx, 0, ny); if (s->sel != NULL && screen_size_y(s) > ny) window_copy_write_line(wme, &ctx, ny); else if (ny == 1) /* nuke position */ window_copy_write_line(wme, &ctx, 1); screen_write_cursormove(&ctx, data->cx, data->cy, 0); screen_write_stop(&ctx); } static void window_copy_rectangle_toggle(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; u_int px, py; data->rectflag = !data->rectflag; py = screen_hsize(data->backing) + data->cy - data->oy; px = window_copy_find_length(wme, py); if (data->cx > px) window_copy_update_cursor(wme, px, data->cy); window_copy_update_selection(wme, 1); window_copy_redraw_screen(wme); } static void window_copy_move_mouse(struct mouse_event *m) { struct window_pane *wp; struct window_mode_entry *wme; u_int x, y; wp = cmd_mouse_pane(m, NULL, NULL); if (wp == NULL) return; wme = TAILQ_FIRST(&wp->modes); if (wme == NULL) return; if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode) return; if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) return; window_copy_update_cursor(wme, x, y); } void window_copy_start_drag(struct client *c, struct mouse_event *m) { struct window_pane *wp; struct window_mode_entry *wme; u_int x, y; if (c == NULL) return; wp = cmd_mouse_pane(m, NULL, NULL); if (wp == NULL) return; wme = TAILQ_FIRST(&wp->modes); if (wme == NULL) return; if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode) return; if (cmd_mouse_at(wp, m, &x, &y, 1) != 0) return; c->tty.mouse_drag_update = window_copy_drag_update; c->tty.mouse_drag_release = window_copy_drag_release; window_copy_update_cursor(wme, x, y); window_copy_start_selection(wme); window_copy_redraw_screen(wme); window_copy_drag_update(c, m); } static void window_copy_drag_update(struct client *c, struct mouse_event *m) { struct window_pane *wp; struct window_mode_entry *wme; struct window_copy_mode_data *data; u_int x, y, old_cx, old_cy; struct timeval tv = { .tv_usec = WINDOW_COPY_DRAG_REPEAT_TIME }; if (c == NULL) return; wp = cmd_mouse_pane(m, NULL, NULL); if (wp == NULL) return; wme = TAILQ_FIRST(&wp->modes); if (wme == NULL) return; if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode) return; data = wme->data; evtimer_del(&data->dragtimer); if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) return; old_cx = data->cx; old_cy = data->cy; window_copy_update_cursor(wme, x, y); if (window_copy_update_selection(wme, 1)) window_copy_redraw_selection(wme, old_cy); if (old_cy != data->cy || old_cx == data->cx) { if (y == 0) { evtimer_add(&data->dragtimer, &tv); window_copy_cursor_up(wme, 1); } else if (y == screen_size_y(&data->screen) - 1) { evtimer_add(&data->dragtimer, &tv); window_copy_cursor_down(wme, 1); } } } static void window_copy_drag_release(struct client *c, struct mouse_event *m) { struct window_pane *wp; struct window_mode_entry *wme; struct window_copy_mode_data *data; if (c == NULL) return; wp = cmd_mouse_pane(m, NULL, NULL); if (wp == NULL) return; wme = TAILQ_FIRST(&wp->modes); if (wme == NULL) return; if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode) return; data = wme->data; evtimer_del(&data->dragtimer); } tmux-3.0a/window-tree.c100644 001750 001750 00000071543 13570677043 0010652/* $OpenBSD$ */ /* * Copyright (c) 2017 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" static struct screen *window_tree_init(struct window_mode_entry *, struct cmd_find_state *, struct args *); static void window_tree_free(struct window_mode_entry *); static void window_tree_resize(struct window_mode_entry *, u_int, u_int); static void window_tree_key(struct window_mode_entry *, struct client *, struct session *, struct winlink *, key_code, struct mouse_event *); #define WINDOW_TREE_DEFAULT_COMMAND "switch-client -t '%%'" #define WINDOW_TREE_DEFAULT_FORMAT \ "#{?pane_format," \ "#{pane_current_command} \"#{pane_title}\"" \ "," \ "#{?window_format," \ "#{window_name}#{window_flags} " \ "(#{window_panes} panes)" \ "#{?#{==:#{window_panes},1}, \"#{pane_title}\",}" \ "," \ "#{session_windows} windows" \ "#{?session_grouped, " \ "(group #{session_group}: " \ "#{session_group_list})," \ "}" \ "#{?session_attached, (attached),}" \ "}" \ "}" static const struct menu_item window_tree_menu_items[] = { { "Select", 'E', NULL }, { "Expand", 'R', NULL }, { "", KEYC_NONE, NULL }, { "Tag", 't', NULL }, { "Tag All", '\024', NULL }, { "Tag None", 'T', NULL }, { "", KEYC_NONE, NULL }, { "Kill", 'x', NULL }, { "Kill Tagged", 'X', NULL }, { "", KEYC_NONE, NULL }, { "Cancel", 'q', NULL }, { NULL, KEYC_NONE, NULL } }; const struct window_mode window_tree_mode = { .name = "tree-mode", .default_format = WINDOW_TREE_DEFAULT_FORMAT, .init = window_tree_init, .free = window_tree_free, .resize = window_tree_resize, .key = window_tree_key, }; enum window_tree_sort_type { WINDOW_TREE_BY_INDEX, WINDOW_TREE_BY_NAME, WINDOW_TREE_BY_TIME, }; static const char *window_tree_sort_list[] = { "index", "name", "time" }; enum window_tree_type { WINDOW_TREE_NONE, WINDOW_TREE_SESSION, WINDOW_TREE_WINDOW, WINDOW_TREE_PANE, }; struct window_tree_itemdata { enum window_tree_type type; int session; int winlink; int pane; }; struct window_tree_modedata { struct window_pane *wp; int dead; int references; struct mode_tree_data *data; char *format; char *command; int squash_groups; struct window_tree_itemdata **item_list; u_int item_size; const char *entered; struct cmd_find_state fs; enum window_tree_type type; int offset; int left; int right; u_int start; u_int end; u_int each; }; static void window_tree_pull_item(struct window_tree_itemdata *item, struct session **sp, struct winlink **wlp, struct window_pane **wp) { *wp = NULL; *wlp = NULL; *sp = session_find_by_id(item->session); if (*sp == NULL) return; if (item->type == WINDOW_TREE_SESSION) { *wlp = (*sp)->curw; *wp = (*wlp)->window->active; return; } *wlp = winlink_find_by_index(&(*sp)->windows, item->winlink); if (*wlp == NULL) { *sp = NULL; return; } if (item->type == WINDOW_TREE_WINDOW) { *wp = (*wlp)->window->active; return; } *wp = window_pane_find_by_id(item->pane); if (!window_has_pane((*wlp)->window, *wp)) *wp = NULL; if (*wp == NULL) { *sp = NULL; *wlp = NULL; return; } } static struct window_tree_itemdata * window_tree_add_item(struct window_tree_modedata *data) { struct window_tree_itemdata *item; data->item_list = xreallocarray(data->item_list, data->item_size + 1, sizeof *data->item_list); item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item); return (item); } static void window_tree_free_item(struct window_tree_itemdata *item) { free(item); } static int window_tree_cmp_session_name(const void *a0, const void *b0) { const struct session *const *a = a0; const struct session *const *b = b0; return (strcmp((*a)->name, (*b)->name)); } static int window_tree_cmp_session_time(const void *a0, const void *b0) { const struct session *const *a = a0; const struct session *const *b = b0; if (timercmp(&(*a)->activity_time, &(*b)->activity_time, >)) return (-1); if (timercmp(&(*a)->activity_time, &(*b)->activity_time, <)) return (1); return (strcmp((*a)->name, (*b)->name)); } static int window_tree_cmp_window_name(const void *a0, const void *b0) { const struct winlink *const *a = a0; const struct winlink *const *b = b0; return (strcmp((*a)->window->name, (*b)->window->name)); } static int window_tree_cmp_window_time(const void *a0, const void *b0) { const struct winlink *const *a = a0; const struct winlink *const *b = b0; if (timercmp(&(*a)->window->activity_time, &(*b)->window->activity_time, >)) return (-1); if (timercmp(&(*a)->window->activity_time, &(*b)->window->activity_time, <)) return (1); return (strcmp((*a)->window->name, (*b)->window->name)); } static int window_tree_cmp_pane_time(const void *a0, const void *b0) { const struct window_pane *const *a = a0; const struct window_pane *const *b = b0; if ((*a)->active_point < (*b)->active_point) return (-1); if ((*a)->active_point > (*b)->active_point) return (1); return (0); } static void window_tree_build_pane(struct session *s, struct winlink *wl, struct window_pane *wp, void *modedata, struct mode_tree_item *parent) { struct window_tree_modedata *data = modedata; struct window_tree_itemdata *item; char *name, *text; u_int idx; window_pane_index(wp, &idx); item = window_tree_add_item(data); item->type = WINDOW_TREE_PANE; item->session = s->id; item->winlink = wl->idx; item->pane = wp->id; text = format_single(NULL, data->format, NULL, s, wl, wp); xasprintf(&name, "%u", idx); mode_tree_add(data->data, parent, item, (uint64_t)wp, name, text, -1); free(text); free(name); } static int window_tree_filter_pane(struct session *s, struct winlink *wl, struct window_pane *wp, const char *filter) { char *cp; int result; if (filter == NULL) return (1); cp = format_single(NULL, filter, NULL, s, wl, wp); result = format_true(cp); free(cp); return (result); } static int window_tree_build_window(struct session *s, struct winlink *wl, void* modedata, u_int sort_type, struct mode_tree_item *parent, const char *filter) { struct window_tree_modedata *data = modedata; struct window_tree_itemdata *item; struct mode_tree_item *mti; char *name, *text; struct window_pane *wp, **l; u_int n, i; int expanded; item = window_tree_add_item(data); item->type = WINDOW_TREE_WINDOW; item->session = s->id; item->winlink = wl->idx; item->pane = -1; text = format_single(NULL, data->format, NULL, s, wl, NULL); xasprintf(&name, "%u", wl->idx); if (data->type == WINDOW_TREE_SESSION || data->type == WINDOW_TREE_WINDOW) expanded = 0; else expanded = 1; mti = mode_tree_add(data->data, parent, item, (uint64_t)wl, name, text, expanded); free(text); free(name); if ((wp = TAILQ_FIRST(&wl->window->panes)) == NULL) goto empty; if (TAILQ_NEXT(wp, entry) == NULL) { if (!window_tree_filter_pane(s, wl, wp, filter)) goto empty; return (1); } l = NULL; n = 0; TAILQ_FOREACH(wp, &wl->window->panes, entry) { if (!window_tree_filter_pane(s, wl, wp, filter)) continue; l = xreallocarray(l, n + 1, sizeof *l); l[n++] = wp; } if (n == 0) goto empty; switch (sort_type) { case WINDOW_TREE_BY_INDEX: break; case WINDOW_TREE_BY_NAME: /* Panes don't have names, so leave in number order. */ break; case WINDOW_TREE_BY_TIME: qsort(l, n, sizeof *l, window_tree_cmp_pane_time); break; } for (i = 0; i < n; i++) window_tree_build_pane(s, wl, l[i], modedata, mti); free(l); return (1); empty: window_tree_free_item(item); data->item_size--; mode_tree_remove(data->data, mti); return (0); } static void window_tree_build_session(struct session *s, void* modedata, u_int sort_type, const char *filter) { struct window_tree_modedata *data = modedata; struct window_tree_itemdata *item; struct mode_tree_item *mti; char *text; struct winlink *wl, **l; u_int n, i, empty; int expanded; item = window_tree_add_item(data); item->type = WINDOW_TREE_SESSION; item->session = s->id; item->winlink = -1; item->pane = -1; text = format_single(NULL, data->format, NULL, s, NULL, NULL); if (data->type == WINDOW_TREE_SESSION) expanded = 0; else expanded = 1; mti = mode_tree_add(data->data, NULL, item, (uint64_t)s, s->name, text, expanded); free(text); l = NULL; n = 0; RB_FOREACH(wl, winlinks, &s->windows) { l = xreallocarray(l, n + 1, sizeof *l); l[n++] = wl; } switch (sort_type) { case WINDOW_TREE_BY_INDEX: break; case WINDOW_TREE_BY_NAME: qsort(l, n, sizeof *l, window_tree_cmp_window_name); break; case WINDOW_TREE_BY_TIME: qsort(l, n, sizeof *l, window_tree_cmp_window_time); break; } empty = 0; for (i = 0; i < n; i++) { if (!window_tree_build_window(s, l[i], modedata, sort_type, mti, filter)) empty++; } if (empty == n) { window_tree_free_item(item); data->item_size--; mode_tree_remove(data->data, mti); } free(l); } static void window_tree_build(void *modedata, u_int sort_type, uint64_t *tag, const char *filter) { struct window_tree_modedata *data = modedata; struct session *s, **l; struct session_group *sg, *current; u_int n, i; current = session_group_contains(data->fs.s); for (i = 0; i < data->item_size; i++) window_tree_free_item(data->item_list[i]); free(data->item_list); data->item_list = NULL; data->item_size = 0; l = NULL; n = 0; RB_FOREACH(s, sessions, &sessions) { if (data->squash_groups && (sg = session_group_contains(s)) != NULL) { if ((sg == current && s != data->fs.s) || (sg != current && s != TAILQ_FIRST(&sg->sessions))) continue; } l = xreallocarray(l, n + 1, sizeof *l); l[n++] = s; } switch (sort_type) { case WINDOW_TREE_BY_INDEX: break; case WINDOW_TREE_BY_NAME: qsort(l, n, sizeof *l, window_tree_cmp_session_name); break; case WINDOW_TREE_BY_TIME: qsort(l, n, sizeof *l, window_tree_cmp_session_time); break; } for (i = 0; i < n; i++) window_tree_build_session(l[i], modedata, sort_type, filter); free(l); switch (data->type) { case WINDOW_TREE_NONE: break; case WINDOW_TREE_SESSION: *tag = (uint64_t)data->fs.s; break; case WINDOW_TREE_WINDOW: *tag = (uint64_t)data->fs.wl; break; case WINDOW_TREE_PANE: if (window_count_panes(data->fs.wl->window) == 1) *tag = (uint64_t)data->fs.wl; else *tag = (uint64_t)data->fs.wp; break; } } static void window_tree_draw_label(struct screen_write_ctx *ctx, u_int px, u_int py, u_int sx, u_int sy, const struct grid_cell *gc, const char *label) { size_t len; u_int ox, oy; len = strlen(label); if (sx == 0 || sy == 1 || len > sx) return; ox = (sx - len + 1) / 2; oy = (sy + 1) / 2; if (ox > 1 && ox + len < sx - 1 && sy >= 3) { screen_write_cursormove(ctx, px + ox - 1, py + oy - 1, 0); screen_write_box(ctx, len + 2, 3); } screen_write_cursormove(ctx, px + ox, py + oy, 0); screen_write_puts(ctx, gc, "%s", label); } static void window_tree_draw_session(struct window_tree_modedata *data, struct session *s, struct screen_write_ctx *ctx, u_int sx, u_int sy) { struct options *oo = s->options; struct winlink *wl; struct window *w; u_int cx = ctx->s->cx, cy = ctx->s->cy; u_int loop, total, visible, each, width, offset; u_int current, start, end, remaining, i; struct grid_cell gc; int colour, active_colour, left, right; char *label; total = winlink_count(&s->windows); memcpy(&gc, &grid_default_cell, sizeof gc); colour = options_get_number(oo, "display-panes-colour"); active_colour = options_get_number(oo, "display-panes-active-colour"); if (sx / total < 24) { visible = sx / 24; if (visible == 0) visible = 1; } else visible = total; current = 0; RB_FOREACH(wl, winlinks, &s->windows) { if (wl == s->curw) break; current++; } if (current < visible) { start = 0; end = visible; } else if (current >= total - visible) { start = total - visible; end = total; } else { start = current - (visible / 2); end = start + visible; } if (data->offset < -(int)start) data->offset = -(int)start; if (data->offset > (int)(total - end)) data->offset = (int)(total - end); start += data->offset; end += data->offset; left = (start != 0); right = (end != total); if (((left && right) && sx <= 6) || ((left || right) && sx <= 3)) left = right = 0; if (left && right) { each = (sx - 6) / visible; remaining = (sx - 6) - (visible * each); } else if (left || right) { each = (sx - 3) / visible; remaining = (sx - 3) - (visible * each); } else { each = sx / visible; remaining = sx - (visible * each); } if (each == 0) return; if (left) { data->left = cx + 2; screen_write_cursormove(ctx, cx + 2, cy, 0); screen_write_vline(ctx, sy, 0, 0); screen_write_cursormove(ctx, cx, cy + sy / 2, 0); screen_write_puts(ctx, &grid_default_cell, "<"); } else data->left = -1; if (right) { data->right = cx + sx - 3; screen_write_cursormove(ctx, cx + sx - 3, cy, 0); screen_write_vline(ctx, sy, 0, 0); screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2, 0); screen_write_puts(ctx, &grid_default_cell, ">"); } else data->right = -1; data->start = start; data->end = end; data->each = each; i = loop = 0; RB_FOREACH(wl, winlinks, &s->windows) { if (loop == end) break; if (loop < start) { loop++; continue; } w = wl->window; if (wl == s->curw) gc.fg = active_colour; else gc.fg = colour; if (left) offset = 3 + (i * each); else offset = (i * each); if (loop == end - 1) width = each + remaining; else width = each - 1; screen_write_cursormove(ctx, cx + offset, cy, 0); screen_write_preview(ctx, &w->active->base, width, sy); xasprintf(&label, " %u:%s ", wl->idx, w->name); if (strlen(label) > width) xasprintf(&label, " %u ", wl->idx); window_tree_draw_label(ctx, cx + offset, cy, width, sy, &gc, label); free(label); if (loop != end - 1) { screen_write_cursormove(ctx, cx + offset + width, cy, 0); screen_write_vline(ctx, sy, 0, 0); } loop++; i++; } } static void window_tree_draw_window(struct window_tree_modedata *data, struct session *s, struct window *w, struct screen_write_ctx *ctx, u_int sx, u_int sy) { struct options *oo = s->options; struct window_pane *wp; u_int cx = ctx->s->cx, cy = ctx->s->cy; u_int loop, total, visible, each, width, offset; u_int current, start, end, remaining, i; struct grid_cell gc; int colour, active_colour, left, right, pane_idx; char *label; total = window_count_panes(w); memcpy(&gc, &grid_default_cell, sizeof gc); colour = options_get_number(oo, "display-panes-colour"); active_colour = options_get_number(oo, "display-panes-active-colour"); if (sx / total < 24) { visible = sx / 24; if (visible == 0) visible = 1; } else visible = total; current = 0; TAILQ_FOREACH(wp, &w->panes, entry) { if (wp == w->active) break; current++; } if (current < visible) { start = 0; end = visible; } else if (current >= total - visible) { start = total - visible; end = total; } else { start = current - (visible / 2); end = start + visible; } if (data->offset < -(int)start) data->offset = -(int)start; if (data->offset > (int)(total - end)) data->offset = (int)(total - end); start += data->offset; end += data->offset; left = (start != 0); right = (end != total); if (((left && right) && sx <= 6) || ((left || right) && sx <= 3)) left = right = 0; if (left && right) { each = (sx - 6) / visible; remaining = (sx - 6) - (visible * each); } else if (left || right) { each = (sx - 3) / visible; remaining = (sx - 3) - (visible * each); } else { each = sx / visible; remaining = sx - (visible * each); } if (each == 0) return; if (left) { data->left = cx + 2; screen_write_cursormove(ctx, cx + 2, cy, 0); screen_write_vline(ctx, sy, 0, 0); screen_write_cursormove(ctx, cx, cy + sy / 2, 0); screen_write_puts(ctx, &grid_default_cell, "<"); } else data->left = -1; if (right) { data->right = cx + sx - 3; screen_write_cursormove(ctx, cx + sx - 3, cy, 0); screen_write_vline(ctx, sy, 0, 0); screen_write_cursormove(ctx, cx + sx - 1, cy + sy / 2, 0); screen_write_puts(ctx, &grid_default_cell, ">"); } else data->right = -1; data->start = start; data->end = end; data->each = each; i = loop = 0; TAILQ_FOREACH(wp, &w->panes, entry) { if (loop == end) break; if (loop < start) { loop++; continue; } if (wp == w->active) gc.fg = active_colour; else gc.fg = colour; if (left) offset = 3 + (i * each); else offset = (i * each); if (loop == end - 1) width = each + remaining; else width = each - 1; screen_write_cursormove(ctx, cx + offset, cy, 0); screen_write_preview(ctx, &wp->base, width, sy); if (window_pane_index(wp, &pane_idx) != 0) pane_idx = loop; xasprintf(&label, " %u ", pane_idx); window_tree_draw_label(ctx, cx + offset, cy, each, sy, &gc, label); free(label); if (loop != end - 1) { screen_write_cursormove(ctx, cx + offset + width, cy, 0); screen_write_vline(ctx, sy, 0, 0); } loop++; i++; } } static void window_tree_draw(void *modedata, void *itemdata, struct screen_write_ctx *ctx, u_int sx, u_int sy) { struct window_tree_itemdata *item = itemdata; struct session *sp; struct winlink *wlp; struct window_pane *wp; window_tree_pull_item(item, &sp, &wlp, &wp); if (wp == NULL) return; switch (item->type) { case WINDOW_TREE_NONE: break; case WINDOW_TREE_SESSION: window_tree_draw_session(modedata, sp, ctx, sx, sy); break; case WINDOW_TREE_WINDOW: window_tree_draw_window(modedata, sp, wlp->window, ctx, sx, sy); break; case WINDOW_TREE_PANE: screen_write_preview(ctx, &wp->base, sx, sy); break; } } static int window_tree_search(__unused void *modedata, void *itemdata, const char *ss) { struct window_tree_itemdata *item = itemdata; struct session *s; struct winlink *wl; struct window_pane *wp; char *cmd; int retval; window_tree_pull_item(item, &s, &wl, &wp); switch (item->type) { case WINDOW_TREE_NONE: return (0); case WINDOW_TREE_SESSION: if (s == NULL) return (0); return (strstr(s->name, ss) != NULL); case WINDOW_TREE_WINDOW: if (s == NULL || wl == NULL) return (0); return (strstr(wl->window->name, ss) != NULL); case WINDOW_TREE_PANE: if (s == NULL || wl == NULL || wp == NULL) break; cmd = osdep_get_name(wp->fd, wp->tty); if (cmd == NULL || *cmd == '\0') return (0); retval = (strstr(cmd, ss) != NULL); free(cmd); return retval; } return (0); } static void window_tree_menu(void *modedata, struct client *c, key_code key) { struct window_tree_modedata *data = modedata; struct window_pane *wp = data->wp; struct window_mode_entry *wme; wme = TAILQ_FIRST(&wp->modes); if (wme == NULL || wme->data != modedata) return; window_tree_key(wme, c, NULL, NULL, key, NULL); } static struct screen * window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs, struct args *args) { struct window_pane *wp = wme->wp; struct window_tree_modedata *data; struct screen *s; wme->data = data = xcalloc(1, sizeof *data); data->wp = wp; data->references = 1; if (args_has(args, 's')) data->type = WINDOW_TREE_SESSION; else if (args_has(args, 'w')) data->type = WINDOW_TREE_WINDOW; else data->type = WINDOW_TREE_PANE; memcpy(&data->fs, fs, sizeof data->fs); if (args == NULL || !args_has(args, 'F')) data->format = xstrdup(WINDOW_TREE_DEFAULT_FORMAT); else data->format = xstrdup(args_get(args, 'F')); if (args == NULL || args->argc == 0) data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND); else data->command = xstrdup(args->argv[0]); data->squash_groups = !args_has(args, 'G'); data->data = mode_tree_start(wp, args, window_tree_build, window_tree_draw, window_tree_search, window_tree_menu, data, window_tree_menu_items, window_tree_sort_list, nitems(window_tree_sort_list), &s); mode_tree_zoom(data->data, args); mode_tree_build(data->data); mode_tree_draw(data->data); data->type = WINDOW_TREE_NONE; return (s); } static void window_tree_destroy(struct window_tree_modedata *data) { u_int i; if (--data->references != 0) return; for (i = 0; i < data->item_size; i++) window_tree_free_item(data->item_list[i]); free(data->item_list); free(data->format); free(data->command); free(data); } static void window_tree_free(struct window_mode_entry *wme) { struct window_tree_modedata *data = wme->data; if (data == NULL) return; data->dead = 1; mode_tree_free(data->data); window_tree_destroy(data); } static void window_tree_resize(struct window_mode_entry *wme, u_int sx, u_int sy) { struct window_tree_modedata *data = wme->data; mode_tree_resize(data->data, sx, sy); } static char * window_tree_get_target(struct window_tree_itemdata *item, struct cmd_find_state *fs) { struct session *s; struct winlink *wl; struct window_pane *wp; char *target; window_tree_pull_item(item, &s, &wl, &wp); target = NULL; switch (item->type) { case WINDOW_TREE_NONE: break; case WINDOW_TREE_SESSION: if (s == NULL) break; xasprintf(&target, "=%s:", s->name); break; case WINDOW_TREE_WINDOW: if (s == NULL || wl == NULL) break; xasprintf(&target, "=%s:%u.", s->name, wl->idx); break; case WINDOW_TREE_PANE: if (s == NULL || wl == NULL || wp == NULL) break; xasprintf(&target, "=%s:%u.%%%u", s->name, wl->idx, wp->id); break; } if (target == NULL) cmd_find_clear_state(fs, 0); else cmd_find_from_winlink_pane(fs, wl, wp, 0); return (target); } static void window_tree_command_each(void* modedata, void* itemdata, struct client *c, __unused key_code key) { struct window_tree_modedata *data = modedata; struct window_tree_itemdata *item = itemdata; char *name; struct cmd_find_state fs; name = window_tree_get_target(item, &fs); if (name != NULL) mode_tree_run_command(c, &fs, data->entered, name); free(name); } static enum cmd_retval window_tree_command_done(__unused struct cmdq_item *item, void *modedata) { struct window_tree_modedata *data = modedata; if (!data->dead) { mode_tree_build(data->data); mode_tree_draw(data->data); data->wp->flags |= PANE_REDRAW; } window_tree_destroy(data); return (CMD_RETURN_NORMAL); } static int window_tree_command_callback(struct client *c, void *modedata, const char *s, __unused int done) { struct window_tree_modedata *data = modedata; if (s == NULL || *s == '\0' || data->dead) return (0); data->entered = s; mode_tree_each_tagged(data->data, window_tree_command_each, c, KEYC_NONE, 1); data->entered = NULL; data->references++; cmdq_append(c, cmdq_get_callback(window_tree_command_done, data)); return (0); } static void window_tree_command_free(void *modedata) { struct window_tree_modedata *data = modedata; window_tree_destroy(data); } static void window_tree_kill_each(__unused void* modedata, void* itemdata, __unused struct client *c, __unused key_code key) { struct window_tree_itemdata *item = itemdata; struct session *s; struct winlink *wl; struct window_pane *wp; window_tree_pull_item(item, &s, &wl, &wp); switch (item->type) { case WINDOW_TREE_NONE: break; case WINDOW_TREE_SESSION: if (s != NULL) { server_destroy_session(s); session_destroy(s, 1, __func__); } break; case WINDOW_TREE_WINDOW: if (wl != NULL) server_kill_window(wl->window); break; case WINDOW_TREE_PANE: if (wp != NULL) server_kill_pane(wp); break; } } static int window_tree_kill_current_callback(struct client *c, void *modedata, const char *s, __unused int done) { struct window_tree_modedata *data = modedata; struct mode_tree_data *mtd = data->data; if (s == NULL || *s == '\0' || data->dead) return (0); if (tolower((u_char) s[0]) != 'y' || s[1] != '\0') return (0); window_tree_kill_each(data, mode_tree_get_current(mtd), c, KEYC_NONE); data->references++; cmdq_append(c, cmdq_get_callback(window_tree_command_done, data)); return (0); } static int window_tree_kill_tagged_callback(struct client *c, void *modedata, const char *s, __unused int done) { struct window_tree_modedata *data = modedata; struct mode_tree_data *mtd = data->data; if (s == NULL || *s == '\0' || data->dead) return (0); if (tolower((u_char) s[0]) != 'y' || s[1] != '\0') return (0); mode_tree_each_tagged(mtd, window_tree_kill_each, c, KEYC_NONE, 1); data->references++; cmdq_append(c, cmdq_get_callback(window_tree_command_done, data)); return (0); } static key_code window_tree_mouse(struct window_tree_modedata *data, key_code key, u_int x, struct window_tree_itemdata *item) { struct session *s; struct winlink *wl; struct window_pane *wp; u_int loop; if (key != KEYC_MOUSEDOWN1_PANE) return (KEYC_NONE); if (data->left != -1 && x <= (u_int)data->left) return ('<'); if (data->right != -1 && x >= (u_int)data->right) return ('>'); if (data->left != -1) x -= data->left; else if (x != 0) x--; if (x == 0 || data->end == 0) x = 0; else { x = x / data->each; if (data->start + x >= data->end) x = data->end - 1; } window_tree_pull_item(item, &s, &wl, &wp); if (item->type == WINDOW_TREE_SESSION) { if (s == NULL) return (KEYC_NONE); mode_tree_expand_current(data->data); loop = 0; RB_FOREACH(wl, winlinks, &s->windows) { if (loop == data->start + x) break; loop++; } if (wl != NULL) mode_tree_set_current(data->data, (uint64_t)wl); return ('\r'); } if (item->type == WINDOW_TREE_WINDOW) { if (wl == NULL) return (KEYC_NONE); mode_tree_expand_current(data->data); loop = 0; TAILQ_FOREACH(wp, &wl->window->panes, entry) { if (loop == data->start + x) break; loop++; } if (wp != NULL) mode_tree_set_current(data->data, (uint64_t)wp); return ('\r'); } return (KEYC_NONE); } static void window_tree_key(struct window_mode_entry *wme, struct client *c, __unused struct session *s, __unused struct winlink *wl, key_code key, struct mouse_event *m) { struct window_pane *wp = wme->wp; struct window_tree_modedata *data = wme->data; struct window_tree_itemdata *item, *new_item; char *name, *prompt = NULL; struct cmd_find_state fs; int finished; u_int tagged, x, y, idx; struct session *ns; struct winlink *nwl; struct window_pane *nwp; item = mode_tree_get_current(data->data); finished = mode_tree_key(data->data, c, &key, m, &x, &y); if (item != (new_item = mode_tree_get_current(data->data))) { item = new_item; data->offset = 0; } if (KEYC_IS_MOUSE(key) && m != NULL) key = window_tree_mouse(data, key, x, item); switch (key) { case '<': data->offset--; break; case '>': data->offset++; break; case 'x': window_tree_pull_item(item, &ns, &nwl, &nwp); switch (item->type) { case WINDOW_TREE_NONE: break; case WINDOW_TREE_SESSION: if (ns == NULL) break; xasprintf(&prompt, "Kill session %s? ", ns->name); break; case WINDOW_TREE_WINDOW: if (nwl == NULL) break; xasprintf(&prompt, "Kill window %u? ", nwl->idx); break; case WINDOW_TREE_PANE: if (nwp == NULL || window_pane_index(nwp, &idx) != 0) break; xasprintf(&prompt, "Kill pane %u? ", idx); break; } if (prompt == NULL) break; data->references++; status_prompt_set(c, prompt, "", window_tree_kill_current_callback, window_tree_command_free, data, PROMPT_SINGLE|PROMPT_NOFORMAT); free(prompt); break; case 'X': tagged = mode_tree_count_tagged(data->data); if (tagged == 0) break; xasprintf(&prompt, "Kill %u tagged? ", tagged); data->references++; status_prompt_set(c, prompt, "", window_tree_kill_tagged_callback, window_tree_command_free, data, PROMPT_SINGLE|PROMPT_NOFORMAT); free(prompt); break; case ':': tagged = mode_tree_count_tagged(data->data); if (tagged != 0) xasprintf(&prompt, "(%u tagged) ", tagged); else xasprintf(&prompt, "(current) "); data->references++; status_prompt_set(c, prompt, "", window_tree_command_callback, window_tree_command_free, data, PROMPT_NOFORMAT); free(prompt); break; case '\r': name = window_tree_get_target(item, &fs); if (name != NULL) mode_tree_run_command(c, NULL, data->command, name); finished = 1; free(name); break; } if (finished) window_pane_reset_mode(wp); else { mode_tree_draw(data->data); wp->flags |= PANE_REDRAW; } } tmux-3.0a/window.c100644 001750 001750 00000100376 13570677043 0007712/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tmux.h" /* * 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; static u_int next_window_pane_id; static u_int next_window_id; static u_int next_active_point; /* List of window modes. */ const struct window_mode *all_window_modes[] = { &window_buffer_mode, &window_client_mode, &window_clock_mode, &window_copy_mode, &window_tree_mode, &window_view_mode, NULL }; struct window_pane_input_data { struct cmdq_item *item; u_int wp; }; static struct window_pane *window_pane_create(struct window *, u_int, u_int, u_int); static void window_pane_destroy(struct window_pane *); RB_GENERATE(windows, window, entry, window_cmp); RB_GENERATE(winlinks, winlink, entry, winlink_cmp); RB_GENERATE(window_pane_tree, window_pane, tree_entry, window_pane_cmp); int window_cmp(struct window *w1, struct window *w2) { return (w1->id - w2->id); } int winlink_cmp(struct winlink *wl1, struct winlink *wl2) { return (wl1->idx - wl2->idx); } 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)); } struct winlink * winlink_find_by_window_id(struct winlinks *wwl, u_int id) { struct winlink *wl; RB_FOREACH(wl, winlinks, wwl) { if (wl->window->id == id) return (wl); } return (NULL); } static 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) { if (wl->window != NULL) { TAILQ_REMOVE(&wl->window->winlinks, wl, wentry); window_remove_ref(wl->window, __func__); } TAILQ_INSERT_TAIL(&w->winlinks, wl, wentry); wl->window = w; window_add_ref(w, __func__); } void winlink_remove(struct winlinks *wwl, struct winlink *wl) { struct window *w = wl->window; if (w != NULL) { TAILQ_REMOVE(&w->winlinks, wl, wentry); window_remove_ref(w, __func__); } RB_REMOVE(winlinks, wwl, wl); free(wl); } 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; } } } struct window * window_find_by_id_str(const char *s) { const char *errstr; u_int id; if (*s != '@') return (NULL); id = strtonum(s + 1, 0, UINT_MAX, &errstr); if (errstr != NULL) return (NULL); return (window_find_by_id(id)); } struct window * window_find_by_id(u_int id) { struct window w; w.id = id; return (RB_FIND(windows, &windows, &w)); } void window_update_activity(struct window *w) { gettimeofday(&w->activity_time, NULL); alerts_queue(w, WINDOW_ACTIVITY); } struct window * window_create(u_int sx, u_int sy) { struct window *w; 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; w->options = options_create(global_w_options); w->references = 0; TAILQ_INIT(&w->winlinks); w->id = next_window_id++; RB_INSERT(windows, &windows, w); window_update_activity(w); return (w); } static void window_destroy(struct window *w) { log_debug("window @%u destroyed (%d references)", w->id, w->references); RB_REMOVE(windows, &windows, w); if (w->layout_root != NULL) layout_free_cell(w->layout_root); if (w->saved_layout_root != NULL) layout_free_cell(w->saved_layout_root); free(w->old_layout); window_destroy_panes(w); if (event_initialized(&w->name_event)) evtimer_del(&w->name_event); if (event_initialized(&w->alerts_timer)) evtimer_del(&w->alerts_timer); if (event_initialized(&w->offset_timer)) event_del(&w->offset_timer); options_free(w->options); free(w->name); free(w); } int window_pane_destroy_ready(struct window_pane *wp) { int n; if (wp->pipe_fd != -1) { if (EVBUFFER_LENGTH(wp->pipe_event->output) != 0) return (0); if (ioctl(wp->fd, FIONREAD, &n) != -1 && n > 0) return (0); } if (~wp->flags & PANE_EXITED) return (0); return (1); } void window_add_ref(struct window *w, const char *from) { w->references++; log_debug("%s: @%u %s, now %d", __func__, w->id, from, w->references); } void window_remove_ref(struct window *w, const char *from) { w->references--; log_debug("%s: @%u %s, now %d", __func__, w->id, from, w->references); if (w->references == 0) window_destroy(w); } void window_set_name(struct window *w, const char *new_name) { free(w->name); utf8_stravis(&w->name, new_name, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); notify_window("window-renamed", w); } void window_resize(struct window *w, u_int sx, u_int sy) { log_debug("%s: @%u resize %ux%u", __func__, w->id, sx, sy); w->sx = sx; w->sy = sy; } int window_has_pane(struct window *w, struct window_pane *wp) { struct window_pane *wp1; TAILQ_FOREACH(wp1, &w->panes, entry) { if (wp1 == wp) return (1); } return (0); } int window_set_active_pane(struct window *w, struct window_pane *wp, int notify) { log_debug("%s: pane %%%u", __func__, wp->id); if (wp == w->active) return (0); w->last = w->active; w->active = wp; w->active->active_point = next_active_point++; w->active->flags |= PANE_CHANGED; tty_update_window_offset(w); if (notify) notify_window("window-pane-changed", w); return (1); } void window_redraw_active_switch(struct window *w, struct window_pane *wp) { struct style *sy1, *sy2; int c1, c2; if (wp == w->active) return; for (;;) { /* * If the active and inactive styles or palettes are different, * need to redraw the panes. */ sy1 = &wp->cached_style; sy2 = &wp->cached_active_style; if (!style_equal(sy1, sy2)) wp->flags |= PANE_REDRAW; else { c1 = window_pane_get_palette(wp, sy1->gc.fg); c2 = window_pane_get_palette(wp, sy2->gc.fg); if (c1 != c2) wp->flags |= PANE_REDRAW; else { c1 = window_pane_get_palette(wp, sy1->gc.bg); c2 = window_pane_get_palette(wp, sy2->gc.bg); if (c1 != c2) wp->flags |= PANE_REDRAW; } } if (wp == w->active) break; wp = w->active; } } 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); } 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)); } int window_zoom(struct window_pane *wp) { struct window *w = wp->window; struct window_pane *wp1; if (w->flags & WINDOW_ZOOMED) return (-1); if (window_count_panes(w) == 1) return (-1); if (w->active != wp) window_set_active_pane(w, wp, 1); TAILQ_FOREACH(wp1, &w->panes, entry) { wp1->saved_layout_cell = wp1->layout_cell; wp1->layout_cell = NULL; } w->saved_layout_root = w->layout_root; layout_init(w, wp); w->flags |= WINDOW_ZOOMED; notify_window("window-layout-changed", w); return (0); } int window_unzoom(struct window *w) { struct window_pane *wp; if (!(w->flags & WINDOW_ZOOMED)) return (-1); w->flags &= ~WINDOW_ZOOMED; layout_free(w); w->layout_root = w->saved_layout_root; w->saved_layout_root = NULL; TAILQ_FOREACH(wp, &w->panes, entry) { wp->layout_cell = wp->saved_layout_cell; wp->saved_layout_cell = NULL; } layout_fix_panes(w); notify_window("window-layout-changed", w); return (0); } struct window_pane * window_add_pane(struct window *w, struct window_pane *other, u_int hlimit, int flags) { struct window_pane *wp; if (other == NULL) other = w->active; wp = window_pane_create(w, w->sx, w->sy, hlimit); if (TAILQ_EMPTY(&w->panes)) { log_debug("%s: @%u at start", __func__, w->id); TAILQ_INSERT_HEAD(&w->panes, wp, entry); } else if (flags & SPAWN_BEFORE) { log_debug("%s: @%u before %%%u", __func__, w->id, wp->id); if (flags & SPAWN_FULLSIZE) TAILQ_INSERT_HEAD(&w->panes, wp, entry); else TAILQ_INSERT_BEFORE(other, wp, entry); } else { log_debug("%s: @%u after %%%u", __func__, w->id, wp->id); if (flags & SPAWN_FULLSIZE) TAILQ_INSERT_TAIL(&w->panes, wp, entry); else TAILQ_INSERT_AFTER(&w->panes, other, wp, entry); } return (wp); } void window_lost_pane(struct window *w, struct window_pane *wp) { log_debug("%s: @%u pane %%%u", __func__, w->id, wp->id); if (wp == marked_pane.wp) server_clear_marked(); 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); } if (w->active != NULL) { w->active->flags |= PANE_CHANGED; notify_window("window-pane-changed", w); } } else if (wp == w->last) w->last = NULL; } void window_remove_pane(struct window *w, struct window_pane *wp) { window_lost_pane(w, wp); 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 = options_get_number(w->options, "pane-base-index"); 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); } int window_pane_index(struct window_pane *wp, u_int *i) { struct window_pane *wq; struct window *w = wp->window; *i = options_get_number(w->options, "pane-base-index"); TAILQ_FOREACH(wq, &w->panes, entry) { if (wp == wq) { return (0); } (*i)++; } return (-1); } 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); } } const char * window_printable_flags(struct winlink *wl) { struct session *s = wl->session; static char flags[32]; int pos; pos = 0; if (wl->flags & WINLINK_ACTIVITY) flags[pos++] = '#'; if (wl->flags & WINLINK_BELL) flags[pos++] = '!'; if (wl->flags & WINLINK_SILENCE) flags[pos++] = '~'; if (wl == s->curw) flags[pos++] = '*'; if (wl == TAILQ_FIRST(&s->lastw)) flags[pos++] = '-'; if (server_check_marked() && wl == marked_pane.wl) flags[pos++] = 'M'; if (wl->window->flags & WINDOW_ZOOMED) flags[pos++] = 'Z'; flags[pos] = '\0'; return (flags); } struct window_pane * window_pane_find_by_id_str(const char *s) { const char *errstr; u_int id; if (*s != '%') return (NULL); id = strtonum(s + 1, 0, UINT_MAX, &errstr); if (errstr != NULL) return (NULL); return (window_pane_find_by_id(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)); } static struct window_pane * window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) { struct window_pane *wp; char host[HOST_NAME_MAX + 1]; wp = xcalloc(1, sizeof *wp); wp->window = w; wp->options = options_create(w->options); wp->flags = PANE_STYLECHANGED; wp->id = next_window_pane_id++; RB_INSERT(window_pane_tree, &all_window_panes, wp); wp->argc = 0; wp->argv = NULL; wp->shell = NULL; wp->cwd = NULL; wp->fd = -1; wp->event = NULL; TAILQ_INIT(&wp->modes); wp->layout_cell = NULL; wp->xoff = 0; wp->yoff = 0; wp->sx = wp->osx = sx; wp->sy = wp->osx = sy; wp->pipe_fd = -1; wp->pipe_off = 0; wp->pipe_event = NULL; wp->saved_grid = NULL; wp->saved_cx = UINT_MAX; wp->saved_cy = UINT_MAX; screen_init(&wp->base, sx, sy, hlimit); wp->screen = &wp->base; screen_init(&wp->status_screen, 1, 1, 0); if (gethostname(host, sizeof host) == 0) screen_set_title(&wp->base, host); input_init(wp); return (wp); } static void window_pane_destroy(struct window_pane *wp) { window_pane_reset_mode_all(wp); free(wp->searchstr); if (wp->fd != -1) { #ifdef HAVE_UTEMPTER utempter_remove_record(wp->fd); #endif bufferevent_free(wp->event); close(wp->fd); } input_free(wp); screen_free(&wp->status_screen); screen_free(&wp->base); if (wp->saved_grid != NULL) grid_destroy(wp->saved_grid); if (wp->pipe_fd != -1) { bufferevent_free(wp->pipe_event); close(wp->pipe_fd); } if (event_initialized(&wp->resize_timer)) event_del(&wp->resize_timer); RB_REMOVE(window_pane_tree, &all_window_panes, wp); options_free(wp->options); free((void *)wp->cwd); free(wp->shell); cmd_free_argv(wp->argc, wp->argv); free(wp->palette); free(wp); } static void window_pane_read_callback(__unused struct bufferevent *bufev, void *data) { struct window_pane *wp = data; struct evbuffer *evb = wp->event->input; size_t size = EVBUFFER_LENGTH(evb); char *new_data; size_t new_size; new_size = size - wp->pipe_off; if (wp->pipe_fd != -1 && new_size > 0) { new_data = EVBUFFER_DATA(evb) + wp->pipe_off; bufferevent_write(wp->pipe_event, new_data, new_size); } log_debug("%%%u has %zu bytes", wp->id, size); input_parse(wp); wp->pipe_off = EVBUFFER_LENGTH(evb); } static void window_pane_error_callback(__unused struct bufferevent *bufev, __unused short what, void *data) { struct window_pane *wp = data; log_debug("%%%u error", wp->id); wp->flags |= PANE_EXITED; if (window_pane_destroy_ready(wp)) server_destroy_pane(wp, 1); } void window_pane_set_event(struct window_pane *wp) { setblocking(wp->fd, 0); wp->event = bufferevent_new(wp->fd, window_pane_read_callback, NULL, window_pane_error_callback, wp); bufferevent_setwatermark(wp->event, EV_READ, 0, READ_SIZE); bufferevent_enable(wp->event, EV_READ|EV_WRITE); } void window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) { struct window_mode_entry *wme; if (sx == wp->sx && sy == wp->sy) return; wp->sx = sx; wp->sy = sy; log_debug("%s: %%%u resize %ux%u", __func__, wp->id, sx, sy); screen_resize(&wp->base, sx, sy, wp->saved_grid == NULL); wme = TAILQ_FIRST(&wp->modes); if (wme != NULL && wme->mode->resize != NULL) wme->mode->resize(wme, sx, sy); wp->flags |= PANE_RESIZE; } /* * 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, int cursor) { struct screen *s = &wp->base; u_int sx, sy; if (wp->saved_grid != NULL) return; if (!options_get_number(wp->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); if (cursor) { 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, 8); 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, int cursor) { struct screen *s = &wp->base; u_int sx, sy; if (!options_get_number(wp->options, "alternate-screen")) return; /* * Restore the cursor position and cell. This happens even if not * currently in the alternate screen. */ if (cursor && wp->saved_cx != UINT_MAX && wp->saved_cy != UINT_MAX) { 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); } if (wp->saved_grid == NULL) 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, 1); /* Restore the saved grid. */ grid_duplicate_lines(s->grid, screen_hsize(s), wp->saved_grid, 0, sy); /* * 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 || sx != wp->saved_grid->sx) screen_resize(s, sx, sy, 1); grid_destroy(wp->saved_grid); wp->saved_grid = NULL; wp->flags |= PANE_REDRAW; } void window_pane_set_palette(struct window_pane *wp, u_int n, int colour) { if (n > 0xff) return; if (wp->palette == NULL) wp->palette = xcalloc(0x100, sizeof *wp->palette); wp->palette[n] = colour; wp->flags |= PANE_REDRAW; } void window_pane_unset_palette(struct window_pane *wp, u_int n) { if (n > 0xff || wp->palette == NULL) return; wp->palette[n] = 0; wp->flags |= PANE_REDRAW; } void window_pane_reset_palette(struct window_pane *wp) { if (wp->palette == NULL) return; free(wp->palette); wp->palette = NULL; wp->flags |= PANE_REDRAW; } int window_pane_get_palette(struct window_pane *wp, int c) { int new; if (wp == NULL || wp->palette == NULL) return (-1); new = -1; if (c < 8) new = wp->palette[c]; else if (c >= 90 && c <= 97) new = wp->palette[8 + c - 90]; else if (c & COLOUR_FLAG_256) new = wp->palette[c & ~COLOUR_FLAG_256]; if (new == 0) return (-1); return (new); } static void window_pane_mode_timer(__unused int fd, __unused short events, void *arg) { struct window_pane *wp = arg; struct timeval tv = { .tv_sec = 10 }; int n = 0; evtimer_del(&wp->modetimer); evtimer_add(&wp->modetimer, &tv); log_debug("%%%u in mode: last=%ld", wp->id, (long)wp->modelast); if (wp->modelast < time(NULL) - WINDOW_MODE_TIMEOUT) { if (ioctl(wp->fd, FIONREAD, &n) == -1 || n > 0) window_pane_reset_mode_all(wp); } } int window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode, struct cmd_find_state *fs, struct args *args) { struct timeval tv = { .tv_sec = 10 }; struct window_mode_entry *wme; if (!TAILQ_EMPTY(&wp->modes) && TAILQ_FIRST(&wp->modes)->mode == mode) return (1); wp->modelast = time(NULL); if (TAILQ_EMPTY(&wp->modes)) { evtimer_set(&wp->modetimer, window_pane_mode_timer, wp); evtimer_add(&wp->modetimer, &tv); } TAILQ_FOREACH(wme, &wp->modes, entry) { if (wme->mode == mode) break; } if (wme != NULL) { TAILQ_REMOVE(&wp->modes, wme, entry); TAILQ_INSERT_HEAD(&wp->modes, wme, entry); } else { wme = xcalloc(1, sizeof *wme); wme->wp = wp; wme->mode = mode; wme->prefix = 1; TAILQ_INSERT_HEAD(&wp->modes, wme, entry); wme->screen = wme->mode->init(wme, fs, args); } wp->screen = wme->screen; wp->flags |= (PANE_REDRAW|PANE_CHANGED); server_status_window(wp->window); notify_pane("pane-mode-changed", wp); return (0); } void window_pane_reset_mode(struct window_pane *wp) { struct window_mode_entry *wme, *next; if (TAILQ_EMPTY(&wp->modes)) return; wme = TAILQ_FIRST(&wp->modes); TAILQ_REMOVE(&wp->modes, wme, entry); wme->mode->free(wme); free(wme); next = TAILQ_FIRST(&wp->modes); if (next == NULL) { log_debug("%s: no next mode", __func__); evtimer_del(&wp->modetimer); wp->screen = &wp->base; } else { log_debug("%s: next mode is %s", __func__, next->mode->name); wp->screen = next->screen; if (next->mode->resize != NULL) next->mode->resize(next, wp->sx, wp->sy); } wp->flags |= (PANE_REDRAW|PANE_CHANGED); server_status_window(wp->window); notify_pane("pane-mode-changed", wp); } void window_pane_reset_mode_all(struct window_pane *wp) { while (!TAILQ_EMPTY(&wp->modes)) window_pane_reset_mode(wp); } void window_pane_key(struct window_pane *wp, struct client *c, struct session *s, struct winlink *wl, key_code key, struct mouse_event *m) { struct window_mode_entry *wme; struct window_pane *wp2; if (KEYC_IS_MOUSE(key) && m == NULL) return; wme = TAILQ_FIRST(&wp->modes); if (wme != NULL) { wp->modelast = time(NULL); if (wme->mode->key != NULL) wme->mode->key(wme, c, s, wl, (key & ~KEYC_XTERM), m); return; } if (wp->fd == -1 || wp->flags & PANE_INPUTOFF) return; input_key(wp, key, m); if (KEYC_IS_MOUSE(key)) return; if (options_get_number(wp->window->options, "synchronize-panes")) { TAILQ_FOREACH(wp2, &wp->window->panes, entry) { if (wp2 != wp && TAILQ_EMPTY(&wp2->modes) && wp2->fd != -1 && (~wp2->flags & PANE_INPUTOFF) && window_pane_visible(wp2)) input_key(wp2, key, NULL); } } } int window_pane_visible(struct window_pane *wp) { if (~wp->window->flags & WINDOW_ZOOMED) return (1); return (wp == wp->window->active); } u_int window_pane_search(struct window_pane *wp, const char *term, int regex, int ignore) { struct screen *s = &wp->base; regex_t r; char *new = NULL, *line; u_int i; int flags = 0, found; size_t n; if (!regex) { if (ignore) flags |= FNM_CASEFOLD; xasprintf(&new, "*%s*", term); } else { if (ignore) flags |= REG_ICASE; if (regcomp(&r, term, flags|REG_EXTENDED) != 0) return (0); } for (i = 0; i < screen_size_y(s); i++) { line = grid_view_string_cells(s->grid, 0, i, screen_size_x(s)); for (n = strlen(line); n > 0; n--) { if (!isspace((u_char)line[n - 1])) break; line[n - 1] = '\0'; } log_debug("%s: %s", __func__, line); if (!regex) found = (fnmatch(new, line, 0) == 0); else found = (regexec(&r, line, 0, NULL, 0) == 0); free(line); if (found) break; } if (!regex) free(new); else regfree(&r); if (i == screen_size_y(s)) return (0); return (i + 1); } /* Get MRU pane from a list. */ static struct window_pane * window_pane_choose_best(struct window_pane **list, u_int size) { struct window_pane *next, *best; u_int i; if (size == 0) return (NULL); best = list[0]; for (i = 1; i < size; i++) { next = list[i]; if (next->active_point > best->active_point) best = next; } return (best); } /* * Find the pane directly above another. We build a list of those adjacent to * top edge and then choose the best. */ struct window_pane * window_pane_find_up(struct window_pane *wp) { struct window *w; struct window_pane *next, *best, **list; u_int edge, left, right, end, size; int status, found; if (wp == NULL) return (NULL); w = wp->window; status = options_get_number(w->options, "pane-border-status"); list = NULL; size = 0; edge = wp->yoff; if (status == PANE_STATUS_TOP) { if (edge == 1) edge = w->sy + 1; } else if (status == PANE_STATUS_BOTTOM) { if (edge == 0) edge = w->sy; } else { if (edge == 0) edge = w->sy + 1; } left = wp->xoff; right = wp->xoff + wp->sx; TAILQ_FOREACH(next, &w->panes, entry) { if (next == wp) continue; if (next->yoff + next->sy + 1 != edge) continue; end = next->xoff + next->sx - 1; found = 0; if (next->xoff < left && end > right) found = 1; else if (next->xoff >= left && next->xoff <= right) found = 1; else if (end >= left && end <= right) found = 1; if (!found) continue; list = xreallocarray(list, size + 1, sizeof *list); list[size++] = next; } best = window_pane_choose_best(list, size); free(list); return (best); } /* Find the pane directly below another. */ struct window_pane * window_pane_find_down(struct window_pane *wp) { struct window *w; struct window_pane *next, *best, **list; u_int edge, left, right, end, size; int status, found; if (wp == NULL) return (NULL); w = wp->window; status = options_get_number(w->options, "pane-border-status"); list = NULL; size = 0; edge = wp->yoff + wp->sy + 1; if (status == PANE_STATUS_TOP) { if (edge >= w->sy) edge = 1; } else if (status == PANE_STATUS_BOTTOM) { if (edge >= w->sy - 1) edge = 0; } else { if (edge >= w->sy) edge = 0; } left = wp->xoff; right = wp->xoff + wp->sx; TAILQ_FOREACH(next, &w->panes, entry) { if (next == wp) continue; if (next->yoff != edge) continue; end = next->xoff + next->sx - 1; found = 0; if (next->xoff < left && end > right) found = 1; else if (next->xoff >= left && next->xoff <= right) found = 1; else if (end >= left && end <= right) found = 1; if (!found) continue; list = xreallocarray(list, size + 1, sizeof *list); list[size++] = next; } best = window_pane_choose_best(list, size); free(list); return (best); } /* Find the pane directly to the left of another. */ struct window_pane * window_pane_find_left(struct window_pane *wp) { struct window *w; struct window_pane *next, *best, **list; u_int edge, top, bottom, end, size; int found; if (wp == NULL) return (NULL); w = wp->window; list = NULL; size = 0; edge = wp->xoff; if (edge == 0) edge = w->sx + 1; top = wp->yoff; bottom = wp->yoff + wp->sy; TAILQ_FOREACH(next, &w->panes, entry) { if (next == wp) continue; if (next->xoff + next->sx + 1 != edge) continue; end = next->yoff + next->sy - 1; found = 0; if (next->yoff < top && end > bottom) found = 1; else if (next->yoff >= top && next->yoff <= bottom) found = 1; else if (end >= top && end <= bottom) found = 1; if (!found) continue; list = xreallocarray(list, size + 1, sizeof *list); list[size++] = next; } best = window_pane_choose_best(list, size); free(list); return (best); } /* Find the pane directly to the right of another. */ struct window_pane * window_pane_find_right(struct window_pane *wp) { struct window *w; struct window_pane *next, *best, **list; u_int edge, top, bottom, end, size; int found; if (wp == NULL) return (NULL); w = wp->window; list = NULL; size = 0; edge = wp->xoff + wp->sx + 1; if (edge >= w->sx) edge = 0; top = wp->yoff; bottom = wp->yoff + wp->sy; TAILQ_FOREACH(next, &w->panes, entry) { if (next == wp) continue; if (next->xoff != edge) continue; end = next->yoff + next->sy - 1; found = 0; if (next->yoff < top && end > bottom) found = 1; else if (next->yoff >= top && next->yoff <= bottom) found = 1; else if (end >= top && end <= bottom) found = 1; if (!found) continue; list = xreallocarray(list, size + 1, sizeof *list); list[size++] = next; } best = window_pane_choose_best(list, size); free(list); return (best); } /* Clear alert flags for a winlink */ void winlink_clear_flags(struct winlink *wl) { struct winlink *loop; wl->window->flags &= ~WINDOW_ALERTFLAGS; TAILQ_FOREACH(loop, &wl->window->winlinks, wentry) { if ((loop->flags & WINLINK_ALERTFLAGS) != 0) { loop->flags &= ~WINLINK_ALERTFLAGS; server_status_session(loop->session); } } } /* Shuffle window indexes up. */ int winlink_shuffle_up(struct session *s, struct winlink *wl) { int idx, last; 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) 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); } return (idx); } static void window_pane_input_callback(struct client *c, int closed, void *data) { struct window_pane_input_data *cdata = data; struct window_pane *wp; struct evbuffer *evb = c->stdin_data; u_char *buf = EVBUFFER_DATA(evb); size_t len = EVBUFFER_LENGTH(evb); wp = window_pane_find_by_id(cdata->wp); if (wp == NULL || closed || c->flags & CLIENT_DEAD) { if (wp == NULL) c->flags |= CLIENT_EXIT; evbuffer_drain(evb, len); c->stdin_callback = NULL; server_client_unref(c); cmdq_continue(cdata->item); free(cdata); return; } input_parse_buffer(wp, buf, len); evbuffer_drain(evb, len); } int window_pane_start_input(struct window_pane *wp, struct cmdq_item *item, char **cause) { struct client *c = item->client; struct window_pane_input_data *cdata; if (~wp->flags & PANE_EMPTY) { *cause = xstrdup("pane is not empty"); return (-1); } cdata = xmalloc(sizeof *cdata); cdata->item = item; cdata->wp = wp->id; return (server_set_stdin_callback(c, window_pane_input_callback, cdata, cause)); } tmux-3.0a/xmalloc.c100644 001750 001750 00000005245 13570677043 0010041/* $OpenBSD$ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Versions of malloc and friends that check their results, and never return * failure (they call fatalx if they encounter an error). * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #include #include #include #include #include #include #include "tmux.h" void * xmalloc(size_t size) { void *ptr; if (size == 0) fatalx("xmalloc: zero size"); ptr = malloc(size); if (ptr == NULL) fatalx("xmalloc: allocating %zu bytes: %s", size, strerror(errno)); return ptr; } void * xcalloc(size_t nmemb, size_t size) { void *ptr; if (size == 0 || nmemb == 0) fatalx("xcalloc: zero size"); ptr = calloc(nmemb, size); if (ptr == NULL) fatalx("xcalloc: allocating %zu * %zu bytes: %s", nmemb, size, strerror(errno)); return ptr; } void * xrealloc(void *ptr, size_t size) { return xreallocarray(ptr, 1, size); } void * xreallocarray(void *ptr, size_t nmemb, size_t size) { void *new_ptr; if (nmemb == 0 || size == 0) fatalx("xreallocarray: zero size"); new_ptr = reallocarray(ptr, nmemb, size); if (new_ptr == NULL) fatalx("xreallocarray: allocating %zu * %zu bytes: %s", nmemb, size, strerror(errno)); return new_ptr; } char * xstrdup(const char *str) { char *cp; if ((cp = strdup(str)) == NULL) fatalx("xstrdup: %s", strerror(errno)); return cp; } char * xstrndup(const char *str, size_t maxlen) { char *cp; if ((cp = strndup(str, maxlen)) == NULL) fatalx("xstrndup: %s", strerror(errno)); return cp; } int 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 == -1) fatalx("xasprintf: %s", strerror(errno)); return i; } int xsnprintf(char *str, size_t len, const char *fmt, ...) { va_list ap; int i; va_start(ap, fmt); i = xvsnprintf(str, len, fmt, ap); va_end(ap); return i; } int xvsnprintf(char *str, size_t len, const char *fmt, va_list ap) { int i; if (len > INT_MAX) fatalx("xsnprintf: len > INT_MAX"); i = vsnprintf(str, len, fmt, ap); if (i < 0 || i >= (int)len) fatalx("xsnprintf: overflow"); return i; } tmux-3.0a/xmalloc.h100644 001750 001750 00000003012 13570677043 0010034/* $OpenBSD$ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Created: Mon Mar 20 22:09:17 1995 ylo * * Versions of malloc and friends that check their results, and never return * failure (they call fatal if they encounter an error). * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #ifndef XMALLOC_H #define XMALLOC_H #if !defined(__bounded__) #define __bounded__(x, y, z) #endif void *xmalloc(size_t); void *xcalloc(size_t, size_t); void *xrealloc(void *, size_t); void *xreallocarray(void *, size_t, size_t); char *xstrdup(const char *); char *xstrndup(const char *, size_t); int xasprintf(char **, const char *, ...) __attribute__((__format__ (printf, 2, 3))) __attribute__((__nonnull__ (2))); int xvasprintf(char **, const char *, va_list) __attribute__((__nonnull__ (2))); int xsnprintf(char *, size_t, const char *, ...) __attribute__((__format__ (printf, 3, 4))) __attribute__((__nonnull__ (3))) __attribute__((__bounded__ (__string__, 1, 2))); int xvsnprintf(char *, size_t, const char *, va_list) __attribute__((__nonnull__ (3))) __attribute__((__bounded__ (__string__, 1, 2))); #endif /* XMALLOC_H */ tmux-3.0a/xterm-keys.c100644 001750 001750 00000014151 13462323664 0010503/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * 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). */ static int xterm_keys_match(const char *, const char *, size_t, size_t *, key_code *); static int xterm_keys_modifiers(const char *, size_t, size_t *, key_code *); struct xterm_keys_entry { key_code key; const char *template; }; static 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_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;_~" }, { '!', "\033[27;_;33~" }, { '#', "\033[27;_;35~" }, { '(', "\033[27;_;40~" }, { ')', "\033[27;_;41~" }, { '+', "\033[27;_;43~" }, { ',', "\033[27;_;44~" }, { '-', "\033[27;_;45~" }, { '.', "\033[27;_;46~" }, { '0', "\033[27;_;48~" }, { '1', "\033[27;_;49~" }, { '2', "\033[27;_;50~" }, { '3', "\033[27;_;51~" }, { '4', "\033[27;_;52~" }, { '5', "\033[27;_;53~" }, { '6', "\033[27;_;54~" }, { '7', "\033[27;_;55~" }, { '8', "\033[27;_;56~" }, { '9', "\033[27;_;57~" }, { ':', "\033[27;_;58~" }, { ';', "\033[27;_;59~" }, { '<', "\033[27;_;60~" }, { '=', "\033[27;_;61~" }, { '>', "\033[27;_;62~" }, { '?', "\033[27;_;63~" }, { '\'', "\033[27;_;39~" }, { '\r', "\033[27;_;13~" }, { '\t', "\033[27;_;9~" }, }; /* * 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). */ static int xterm_keys_match(const char *template, const char *buf, size_t len, size_t *size, key_code *modifiers) { size_t pos; int retval; *modifiers = 0; if (len == 0) return (0); pos = 0; do { if (*template == '_') { retval = xterm_keys_modifiers(buf, len, &pos, modifiers); if (retval != 0) return (retval); continue; } if (buf[pos] != *template) return (-1); pos++; } while (*++template != '\0' && pos != len); if (*template != '\0') /* partial */ return (1); *size = pos; return (0); } /* Find modifiers from buffer. */ static int xterm_keys_modifiers(const char *buf, size_t len, size_t *pos, key_code *modifiers) { u_int flags; if (len - *pos < 2) return (1); if (buf[*pos] < '0' || buf[*pos] > '9') return (-1); flags = buf[(*pos)++] - '0'; if (buf[*pos] >= '0' && buf[*pos] <= '9') flags = (flags * 10) + (buf[(*pos)++] - '0'); flags -= 1; *modifiers = 0; if (flags & 1) *modifiers |= KEYC_SHIFT; if (flags & 2) *modifiers |= KEYC_ESCAPE; if (flags & 4) *modifiers |= KEYC_CTRL; if (flags & 8) *modifiers |= KEYC_ESCAPE; return (0); } /* * 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, key_code *key) { const struct xterm_keys_entry *entry; u_int i; int matched; key_code modifiers; for (i = 0; i < nitems(xterm_keys_table); i++) { entry = &xterm_keys_table[i]; matched = xterm_keys_match(entry->template, buf, len, size, &modifiers); if (matched == -1) continue; if (matched == 0) *key = (entry->key|modifiers|KEYC_XTERM); return (matched); } return (-1); } /* Lookup a key number from the table. */ char * xterm_keys_lookup(key_code key) { const struct xterm_keys_entry *entry; u_int i; key_code 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); /* * If this has the escape modifier, but was not originally an xterm * key, it may be a genuine escape + key. So don't pass it through as * an xterm key or programs like vi may be confused. */ if ((key & (KEYC_ESCAPE|KEYC_XTERM)) == KEYC_ESCAPE) return (NULL); /* Otherwise, find the key in the table. */ key &= KEYC_MASK_KEY; 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); } tmux-3.0a/CHANGES100644 001750 001750 00000333075 13570677562 0007244CHANGES FROM 3.0 to 3.0a * Do not require REG_STARTEND. * Respawn panes or windows correctly if default-command is set. * Add missing option for after-kill-pane hook. * Fix for crash with a format variable that doesn't exist. * Do not truncate list-keys output on some platforms. * Do not crash when restoring a layout with only one pane. CHANGES FROM 2.9 to 3.0 * Workaround invalid layout strings generated by older tmux versions and add some additional sanity checks * xterm 348 now disables margins when resized, so send DECLRMM again after resize. * Add support for the SD (scroll down) escape sequence. * Expand arguments to C and s format modifiers to match the m modifier. * Add support for underscore colours (Setulc capability must be added with terminal-overrides as described in tmux(1)). * Add a "fill" style attribute for the fill colour of the drawing area (where appropriate). * New -H flag to send-keys to send literal keys. * Format variables for pane mouse modes (mouse_utf8_flag and mouse_sgr_flag) and for origin mode (origin_flag). * Add -F to refresh-client for flags for control mode clients, only one flag (no-output) supported at the moment. * Add a few vi(1) keys for menus. * Add pane options, set with set-option -p and displayed with show-options -p. Pane options inherit from window options (so every pane option is also a window option). The pane style is now configured by setting window-style and window-active-style in the pane options; select-pane -P and -g now change the option but are no longer documented. * Do not document set-window-option and show-window-options. set-option -w and show-options -w should be used instead. * Add a -A flag to show-options to show parent options as well (they are marked with a *). * Resize panes lazily - do not resize unless they are in an attached, active window. * Add regular expression support for the format search, match and substitute modifiers and make them able to ignore case. find-window now accepts -r to use regular expressions. * Do not use $TMUX to find the session because for windows in multiple sessions it is wrong as often as it is right, and for windows in one session it is pointless. Instead use TMUX_PANE if it is present. * Do not always resize the window back to its original size after applying a layout, keep it at the layout size until it must be resized (for example when attached and window-size is not manual). * Add new-session -X and attach-session -x to send SIGHUP to parent when detaching (like detach-client -P). * Support for octal escapes in strings (such as \007) and improve list-keys output so it parses correctly if copied into a configuration file. * INCOMPATIBLE: Add a new {} syntax to the configuration file. This is a string similar to single quotes but also includes newlines and allows commands that take other commands as string arguments to be expressed more clearly and without additional escaping. A literal { and } or a string containing { or } must now be escaped or quoted, for example '{' and '}' instead of { or }, or 'X#{foo}' instead of X#{foo}. * New <, >, <= and >= comparison operators for formats. * Improve escaping of special characters in list-keys output. * INCOMPATIBLE: tmux's configuration parsing has changed to use yacc(1). There is one incompatible change: a \ on its own must be escaped or quoted as either \\ or '\' (the latter works on older tmux versions). Entirely the same parser is now used for parsing the configuration file and for string commands. This means that constructs previously only available in .tmux.conf, such as %if, can now be used in string commands (for example, those given to if-shell - not commands invoked from the shell, they are still parsed by the shell itself). * Add support for the overline attribute (SGR 53). The Smol capability is needed in terminal-overrides. * Add the ability to create simple menus. Introduces new command display-menu. Default menus are bound to MouseDown3 on the status line; MouseDown3 or M-MouseDown3 on panes; MouseDown3 in tree, client and buffer modes; and C-b C-m and C-b M-m. * Allow panes to be empty (no command). They can be created either by piping to split-window -I, or by passing an empty command ('') to split-window. Output can be sent to an existing empty window with display-message -I. * Add keys to jump between matching brackets (emacs C-M-f and C-M-b, vi %). * Add a -e flag to new-window, split-window, respawn-window, respawn-pane to pass environment variables into the newly created process. * Hooks are now stored in the options tree as array options, allowing them to have multiple separate commands. set-hook and show-hooks remain but set-option and show-options can now also be used (show-options will only show hooks if given the -H flag). Hooks with multiple commands are run in index order. * Automatically scroll if dragging to create a selection with the mouse and the cursor reaches the top or bottom line. * Add -no-clear variants of copy-selection and copy-pipe which do not clear the selection after copying. Make copy-pipe clear the selection by default to be consistent with copy-selection. * Add an argument to copy commands to set the prefix for the buffer name, this (for example) allows buffers for different sessions to be named separately. * Update session activity on focus event. * Pass target from source-file into the config file parser so formats in %if and %endif have access to more useful variables. * Add the ability to infer an option type (server, session, window) from its name to show-options (it was already present in set-option). CHANGES FROM 2.9 to 2.9a * Fix bugs in select-pane and the main-horizontal and main-vertical layouts. CHANGES FROM 2.8 to 2.9 * Attempt to preserve horizontal cursor position as well as vertical with reflow. * Rewrite main-vertical and horizontal and change layouts to better handle the case where all panes won't fit into the window size, reduce problems with pane border status lines and fix other bugs mostly found by Thomas Sattler. * Add format variables for the default formats in the various modes (tree_mode_format and so on) and add a -a flag to display-message to list variables with values. * Add a -v flag to display-message to show verbose messages as the format is parsed, this allows formats to be debugged * Add support for HPA (\033[`). * Add support for origin mode (\033[?6h). * No longer clear history on RIS. * Extend the #[] style syntax and use that together with previous format changes to allow the status line to be entirely configured with a single option. Now that it is possible to configure their content, enable the existing code that lets the status line be multiple lines in height. The status option can now take a value of 2, 3, 4 or 5 (as well as the previous on or off) to configure more than one line. The new status-format array option configures the format of each line, the default just references the existing status-* options, although some of the more obscure status options may be eliminated in time. Additions to the #[] syntax are: "align" to specify alignment (left, centre, right), "list" for the window list and "range" to configure ranges of text for the mouse bindings. The "align" keyword can also be used to specify alignment of entries in tree mode and the pane status lines. * Add E: and T: format modifiers to expand a format twice (useful to expand the value of an option). * The individual -fg, -bg and -attr options have been removed; they were superseded by -style options in tmux 1.9. * Allow more than one mode to be opened in a pane. Modes are kept on a stack and retrieved if the same mode is entered again. Exiting the active mode goes back to the previous one. * When showing command output in copy mode, call it view mode instead (affects pane_mode format). * Add -b to display-panes like run-shell. * Handle UTF-8 in word-separators option. * New "terminal" colour allowing options to use the terminal default colour rather than inheriting the default from a parent option. * Do not move the cursor in copy mode when the mouse wheel is used. * Use the same working directory rules for jobs as new windows rather than always starting in the user's home. * Allow panes to be one line or column in size. * Go to last line when goto-line number is out of range in copy mode. * Yank previously cut text if any with C-y in the command prompt, only use the buffer if no text has been cut. * Add q: format modifier to quote shell special characters. * Add StatusLeft and StatusRight mouse locations (keys such as MouseDown1StatusLeft) for the status-left and status-right areas of the status line. * Add -Z to find-window. * Support for windows larger than the client. This adds two new options, window-size and default-size, and a new command, resize-window. The force-width and force-height options and the session_width and session_height formats have been removed. The new window-size option tells tmux how to work out the size of windows: largest means it picks the size of the largest session, smallest the smallest session (similar to the old behaviour) and manual means that it does not automatically resize windows. aggressive-resize modifies the choice of session for largest and smallest as it did before. If a window is in a session attached to a client that is too small, only part of the window is shown. tmux attempts to keep the cursor visible, so the part of the window displayed is changed as the cursor moves (with a small delay, to try and avoid excess redrawing when applications redraw status lines or similar that are not currently visible). Drawing windows which are larger than the client is not as efficient as those which fit, particularly when the cursor moves, so it is recommended to avoid using this on slow machines or networks (set window-size to smallest or manual). The resize-window command can be used to resize a window manually. If it is used, the window-size option is automatically set to manual for the window (undo this with "setw -u window-size"). resize-window works in a similar way to resize-pane (-U -D -L -R -x -y flags) but also has -a and -A flags. -a sets the window to the size of the smallest client (what it would be if window-size was smallest) and -A the largest. For the same behaviour as force-width or force-height, use resize-window -x or -y. If the global window-size option is set to manual, the default-size option is used for new windows. If -x or -y is used with new-session, that sets the default-size option for the new session. The maximum size of a window is 10000x10000. But expect applications to complain and higher memory use if making a window that big. The minimum size is the size required for the current layout including borders. The refresh-client command can be used to pan around a window, -U -D -L -R moves up, down, left or right and -c returns to automatic cursor tracking. The position is reset when the current window is changed. CHANGES FROM 2.7 to 2.8 * Make display-panes block the client until a pane is chosen or it times out. * Clear history on RIS like most other terminals do. * Add an "Any" key to run a command if a key is pressed that is not bound in the current key table. * Expand formats in load-buffer and save-buffer. * Add a rectangle_toggle format. * Add set-hook -R to run a hook immediately. * Add README.ja. * Add pane focus hooks. * Allow any punctuation as separator for s/x/y not only /. * Improve resizing with the mouse (fix resizing the wrong pane in some layouts, and allow resizing multiple panes at the same time). * Allow , and } to be escaped in formats as #, and #}. * Add KRB5CCNAME to update-environment. * Change meaning of -c to display-message so the client is used if it matches the session given to -t. * Fixes to : form of SGR. * Add x and X to choose-tree to kill sessions, windows or panes. CHANGES FROM 2.6 TO 2.7 * Remove EVENT_* variables from environment on platforms where tmux uses them so they do not pass on to panes. * Fixes for hooks at server exit. * Remove SGR 10 (was equivalent to SGR 0 but no other terminal seems to do this). * Expand formats in window and session names. * Add -Z flag to choose-tree, choose-client, choose-buffer to automatically zoom the pane when the mode is entered and unzoom when it exits, assuming the pane is not already zoomed. This is now part of the default key bindings. * Add C-g to exit modes with emacs keys. * Add exit-empty option to exit server if no sessions (defaults to on). * Show if a filter is present in choose modes. * Add pipe-pane -I to to connect stdin of the child process. * Performance improvements for reflow. * Use RGB terminfo(5) capability to detect RGB colour terminals (the existing Tc extension remains unchanged). * Support for ISO colon-separated SGR sequences. * Add select-layout -E to spread panes out evenly (bound to E key). * Support wide characters properly when reflowing. * Pass PWD to new panes as a hint to shells, as well as calling chdir(). * Performance improvements for the various choose modes. * Only show first member of session groups in tree mode (-G flag to choose-tree to show all). * Support %else in config files to match %if; from Brad Town in GitHub issue 1071. * Fix "kind" terminfo(5) capability to be S-Down not S-Up. * Add a box around the preview label in tree mode. * Show exit status and time in the remain-on-exit pane text; from Timo Boettcher in GitHub issue 1103. * Correctly use pane-base-index in tree mode. * Change the allow-rename option default to off. * Support for xterm(1) title stack escape sequences (GitHub issue 1075 from Brad Town). * Correctly remove padding cells to fix a UTF-8 display problem (GitHub issue 1090). CHANGES FROM 2.5 TO 2.6, 05 October 2017 * Add select-pane -T to set pane title. * Fix memory leak when lines with BCE are removed from history. * Fix (again) the "prefer unattached" behaviour of attach-session. * Reorder how keys are checked to allow keys to be specified that have a leading escape. GitHub issue 1048. * Support REP escape sequence (\033[b). * Run alert hooks based on options rather than always, and allow further bells even if there is an existing bell. * Add -d flag to display-panes to override display-panes-time. * Add selection_present format when in copy mode (allows key bindings that do something different if there is a selection). * Add pane_at_left, pane_at_right, pane_at_top and pane_at_bottom formats. * Make bell, activity and silence alerting more consistent by: removing the bell-on-alert option; adding activity-action and silence-action options with the same possible values as the existing bell-action; adding a "both" value for the visual-bell, visual-activity and visual-silence options to trigger both a bell and a message. * Add a pane_pipe format to show if pipe-pane is active. * Block signals between forking and resetting signal handlers so that the libevent signal handler doesn't get called in the child and incorrectly write into the signal pipe that it still shares with the parent. GitHub issue 1001. * Allow punctuation in pane_current_command. * Add -c for respawn-pane and respawn-window. * Wait for any remaining data to flush when a pane is closed while pipe-pane is in use. * Fix working out current client with no target. GitHub issue 995. * Try to fallback to C.UTF-8 as well as en_US.UTF-8 when looking for a UTF-8 locale. * Add user-keys option for user-defined key escape sequences (mapped to User0 to User999 keys). * Add pane-set-clipboard hook. * FAQ file has moved out of repository to online. * Fix problem with high CPU usage when a client dies unexpectedly. GitHub issue 941. * Do a dance on OS X 10.10 and above to return tmux to the user namespace, allowing access to the clipboard. * Do not allow escape sequences which expect a specific terminator (APC, DSC, OSC) to wait for forever - use a small timeout. This reduces the chance of the pane locking up completely when sent garbage (cat /dev/random or similar). * Support SIGUSR2 to toggle logging on a running server, also generate the "out" log file with -vv not -vvvv. * Make set-clipboard a three state option: on (tmux both sends to outside terminal and accepts from applications inside); external (tmux sends outside but does not accept inside); and off. * Fix OSC 4 palette setting for bright foreground colours. GitHub issue 954. * Use setrgbf and setrgbb terminfo(5) capabilities to set RGB colours, if they are available. (Tc is still supported as well.) * Fix redrawing panes when they are resized several times but end up with the size unchanged (for example, splitw/resizep -Z/breakp). * Major rewrite of choose mode. Now includes preview, sorting, searching and tagging; commands that can be executed directly from the mode (for example, to delete one or more buffers); and filtering in tree mode. * choose-window and choose-session are now aliases of choose-tree (in the command-alias option). * Support OSC 10 and OSC 11 to set foreground and background colours. * Check the U8 capability to determine whether to use UTF-8 line drawing characters for ACS. * Some missing notifications for layout changes. * Control mode clients now do not affect session sizes until they issue refresh-client -C. new-session -x and -y works with control clients even if the session is not detached. * All new sessions that are unattached (whether with -d or started with no terminal) are now created with size 80 x 24. Whether the status line is on or off does not affect the size of new sessions until they are attached. * Expand formats in option names and add -F flag to expand them in option values. * Remember the search string for a pane even if copy mode is exited and entered again. * Some further BCE fixes (scroll up, reverse index). * Improvements to how terminals are cleared (entirely or partially). CHANGES FROM 2.4 TO 2.5, 09 May 2017 * Reset updated flag when restarting #() command so that new output is properly recognised. GitHub issue 922. * Fix ECH with a background colour. * Do not rely on the terminal not moving the cursor after DL or EL. * Fix send-keys and send-prefix in copy-mode (so C-b C-b works). GitHub issue 905. * Set the current pane for rotate-window so it works in command sequences. * Add pane_mode format. * Differentiate M-Up from Escape+Up when possible (that is, in terminals with xterm(1) style function keys). GitHub issue 907. * Add session_stack and window_stack_index formats. * Some new control mode notifications and corresponding hooks: pane-mode-changed, window-pane-changed, client-session-changed, session-window-changed. * Format pane_search_string for last search term while in copy mode (useful with command-prompt -I). * Fix a problem with high CPU usage and multiple clients with #(). GitHub issue 889. * Fix UTF-8 combining characters in column 0. * Fix reference counting so that panes are properly destroyed and their processes killed. * Clamp SU (CSI S) parameter to work around a bug in Konsole. * Tweak line wrapping in full width panes to play more nicely with terminal copy and paste. * Fix when we emit SGR 0 in capture-pane -e. * Do not change TERM until after config file parsing has finished, so that commands run inside the config file can use it to make decisions (typically about default-terminal). * Make the initial client wait until config file parsing has finished to avoid racing with commands. * Fix core when if-shell fails. * Only use ED to clear screen if the pane is at the bottom. * Fix multibyte UTF-8 output. * Code improvements around target (-t) resolution. * Change how the default target (for commands without -t) is managed across command sequences: now it is set up at the start and commands are required to update it if needed. Fixes binding command sequences to mouse keys. * Make if-shell from the config file work correctly. * Change to always check the root key table if no binding is found in the current table (prefix table or copy-mode table or whatever). This means that root key bindings will take effect even in copy mode, if not overridden by a copy mode key binding. * Fix so that the history file works again. * Run config file without a client rather than using the first client, restores previous behaviour. * If a #() command doesn't exit, continue to read from it and use its last full line of output. * Handle slow terminals and fast output better: when the amount of data outstanding gets too large, discard output until it is drained and we are able to do a full redraw. Prevents tmux sitting on a huge buffer that the terminal will take forever to consume. * Do not redraw a client unless we realistically think it can accept the data - defer redraws until the client has nothing else waiting to write. CHANGES FROM 2.3 TO 2.4, 20 April 2017 Incompatible Changes ==================== * Key tables have undergone major changes. Mode key tables are no longer separate from the main key tables. All mode key tables have been removed, together with the -t flag to bind-key and unbind-key. The emacs-edit, vi-edit, emacs-choose and vi-choose tables have been replaced by fixed key bindings in the command prompt and choose modes. The mode-keys and status-keys options remain. The emacs-copy and vi-copy tables have been replaced by the copy-mode and copy-mode-vi tables. Commands are sent using the -X and -N flags to send-keys. So the following: bind -temacs-copy C-Up scroll-up bind -temacs-copy -R5 WheelUpPane scroll-up Becomes: bind -Tcopy-mode C-Up send -X scroll-up bind -Tcopy-mode WheelUpPane send -N5 -X scroll-up These changes allows the full command parser (including command sequences) and command set to be used - for example, the normal command prompt with editing and history is now used for searching, jumping, and so on instead of a custom one. The default C-r binding is now: bind -Tcopy-mode C-r command-prompt -i -p'search up' "send -X search-backward-incremental '%%'" There are also some new commmands available with send -X, such as copy-pipe-and-cancel. * set-remain-on-exit has gone -- can be achieved with hooks instead. * Hooks: before hooks have been removed and only a selection of commands now have after hooks (they are no longer automatic). Additional hooks have been added. * The xterm-keys option now defaults to on. Normal Changes ============== * Support for mouse double and triple clicks. * BCE (Background Colour Erase) is now supported. * All occurrences of a search string in copy mode are now highlighted; additionally, the number of search results is displayed. The highlighting updates interactively with the default emacs key bindings (incremental search). * source-file now understands glob patterns. * Formats now have simple comparisons: #{==:a,b} #{!=:a,b} * There are the following new formats: - #{version} -- the tmux server version; - #{client_termtype} -- the terminal type of the client; - #{client_name} -- the name of a client; - #{client_written} -- the number of bytes written to the client. * The configuration file now accepts %if/%endif conditional blocks which are processed when it is parsed; the argument is a format string (useful with the new format comparison options). * detach-client now has -E to execute a command replacing the client instead of exiting. * Add support for custom command aliases, this is an array option which contains items of the form "alias=command". This is consulted when an unknown command is parsed. * break-pane now has -n to specify the new window name. * OSC 52 support has been added for programs inside tmux to set a tmux buffer. * The mouse "all event" mode (1003) is now supported. * Palette setting is now possible (OSC 4 and 104). * Strikethrough support (a recent terminfo is required). * Grouped sessions can now be named (new -t). * terminal-overrides and update-environment are now array options (the previous set -ag syntax should work without change). * There have been substantial performance improvements. CHANGES FROM 2.2 TO 2.3, 29 September 2016 Incompatible Changes ==================== None. Normal Changes ============== * New option 'pane-border-status' to add text in the pane borders. * Support for hooks on commands: 'after' and 'before' hooks. * 'source-file' understands '-q' to suppress errors for nonexistent files. * Lots of UTF8 improvements, especially on MacOS. * 'window-status-separator' understands #[] expansions. * 'split-window' understands '-f' for performing a full-width split. * Allow report count to be specified when using 'bind-key -R'. * 'set -a' for appending to user options (@foo) is now supported. * 'display-panes' can now accept a command to run, rather than always selecting the pane. CHANGES FROM 2.1 TO 2.2, 10 April 2016 Incompatible Changes ==================== * The format strings which referenced time have been removed. Instead: #{t:window_activity} can be used. * Support for TMPDIR has been removed. Use TMUX_TMPDIR instead. * UTF8 detection now happens automatically if the client supports it, hence the: mouse-utf8 utf8 options has been removed. * The: mouse_utf8_flag format string has been removed. * The -I option to show-messages has been removed. See: #{t:start_time} format option instead. Normal Changes ============== * Panes are unzoomed with selectp -LRUD * New formats added: #{scroll_position} #{socket_path} #{=10:...} -- limit to N characters (from the start) #{=-10:...} -- limit to N characters (from the end) #{t:...} -- used to format time-based formats #{b:...} -- used to ascertain basename from string #{d:...} -- used to ascertain dirname from string #{s:...} -- used to perform substitutions on a string * Job output is run via the format system, so formats work again * If display-time is set to 0, then the indicators wait for a key to be pressed. * list-keys and list-commands can be run without starting the tmux server. * kill-session learns -C to clear all alerts in all windows of the session. * Support for hooks (internal for now), but hooks for the following have been implemented: alert-bell alert-silence alert-activity client-attached client-detached client-resized pane-died pane-exited * RGB (24bit) colour support. The 'Tc' flag must be set in the external TERM entry (using terminal-overrides or a custom terminfo entry). CHANGES FROM 2.0 TO 2.1, 18 October 2015 Incompatible Changes ==================== * Mouse-mode has been rewritten. There's now no longer options for: - mouse-resize-pane - mouse-select-pane - mouse-select-window - mode-mouse Instead there is just one option: 'mouse' which turns on mouse support entirely. * 'default-terminal' is now a session option. Furthermore, if this is set to 'screen-*' then emulate what screen does. If italics are wanted, this can be set to 'tmux' but this is still new and not necessarily supported on all platforms with older ncurses installs. * The c0-* options for rate-limiting have been removed. Instead, a backoff approach is used. Normal Changes ============== * New formats: - session_activity - window_linked - window_activity_format - session_alerts - session_last_attached - client_pid - pid * 'copy-selection', 'append-selection', 'start-named-buffer' now understand an '-x' flag to prevent it exiting copying mode. * 'select-pane' now understands '-P' to set window/pane background colours. * 'renumber-windows' now understands windows which are unlinked. * 'bind' now understands multiple key tables. Allows for key-chaining. * 'select-layout' understands '-o' to undo the last layout change. * The environment is updated when switching sessions as well as attaching. * 'select-pane' now understands '-M' for marking a pane. This marked pane can then be used with commands which understand src-pane specifiers automatically. * If a session/window target is prefixed with '=' then only an exact match is considered. * 'move-window' understands '-a'. * 'update-environment' understands '-E' when attach-session is used on an already attached client. * 'show-environment' understands '-s' to output Bourne-compatible commands. * New option: 'history-file' to save/restore command prompt history. * Copy mode is exited if the history is cleared whilst in copy-mode. * 'copy-mode' learned '-e' to exit copy-mode when scrolling to end. CHANGES FROM 1.9a TO 2.0, 06 March 2015 Incompatible Changes ==================== * The choose-list command has been removed. * 'terminal-overrides' is now a server option, not a session option. * 'message-limit' is now a server option, not a session option. * 'monitor-content' option has been removed. * 'pane_start_path' option has been removed. * The "info" mechanism which used to (for some commands) provide feedback has been removed, and like other commands, they now produce nothing on success. Normal Changes ============== * tmux can now write an entry to utmp if the library 'utempter' is present at compile time. * set-buffer learned append mode (-a), and a corresponding 'append-selection' command has been added to copy-mode. * choose-mode now has the following commands which can be bound: - start-of-list - end-of-list - top-line - bottom-line * choose-buffer now understands UTF-8. * Pane navigation has changed: - The old way of always using the top or left if the choice is ambiguous. - The new way of remembering the last used pane is annoying if the layout is balanced and the leftmost is obvious to the user (because clearly if we go right from the top-left in a tiled set of four we want to end up in top-right, even if we were last using the bottom-right). So instead, use a combination of both: if there is only one possible pane alongside the current pane, move to it, otherwise choose the most recently used of the choice. * 'set-buffer' can now be told to give names to buffers. * The 'new-session', 'new-window', 'split-window', and 'respawn-pane' commands now understand multiple arguments and handle quoting problems correctly. * 'capture-pane' understands '-S-' to mean the start of the pane, and '-E-' to mean the end of the pane. * Support for function keys beyond F12 has changed. The following explains: - F13-F24 are S-F1 to S-F12 - F25-F36 are C-F1 to C-F12 - F37-F48 are C-S-F1 to C-S-F12 - F49-F60 are M-F1 to M-F12 - F61-F63 are M-S-F1 to M-S-F3 Therefore, F13 becomes a binding of S-F1, etc. * Support using pane id as part of session or window specifier (so % means session-of-%1 or window-of-%1) and window id as part of session (so @1 means session-of-@1). * 'copy-pipe' command now understands formats via -F * 'if-shell' command now understands formats via -F * 'split-window' and 'join-window' understand -b to create the pane to the left or above the target pane. CHANGES FROM 1.9 TO 1.9a, 22 February 2014 NOTE: This is a bug-fix release to address some important bugs which just missed the 1.9 deadline, but were found afterwards. Normal Changes ============== * Fix crash due to uninitialized lastwp member of layout_cell * Fix -fg/-bg/-style with 256 colour terminals. CHANGES FROM 1.8 TO 1.9, 20 February 2014 NOTE: This release has bumped the tmux protocol version. It is therefore advised that the prior tmux server is restarted when this version of tmux is installed, to avoid protocol mismatch errors for newer clients trying to talk to an older running tmux server. Incompatible Changes ==================== * 88 colour support has been removed. * 'default-path' has been removed. The new-window command accepts '-c' to cater for this. The previous value of "." can be replaced with: 'neww -c $PWD', the previous value of '' which meant current path of the pane can be specified as: 'neww -c "#{pane_current_path}"' Deprecated Changes ================== * The single format specifiers: #A -> #Z (where defined) have been deprecated and replaced with longer-named equivalents, as listed in the FORMATS section of the tmux manpage. * The various foo-{fg,bg,attr} commands have been deprecated and replaced with equivalent foo-style option instead. Currently this is still backwards-compatible, but will be removed over time. Normal Changes ============== * A new environment variable TMUX_TMPDIR is now honoured, allowing the socket directory to be set outside of TMPDIR (/tmp/ if not set). * If -s not given to swap-pane the current pane is assumed. * A #{pane_synchronized} format specifier has been added to be a conditional format if a pane is in a synchronised mode (c.f. synchronize-panes) * Tmux now runs under Cygwin natively. * Formats can now be nested within each other and expanded accordingly. * Added 'automatic-rename-format' option to allow the automatic rename mechanism to use something other than the default of #{pane_current_command}. * new-session learnt '-c' to specify the starting directory for that session and all subsequent windows therein. * The session name is now shown in the message printed to the terminal when a session is detached. * Lots more format specifiers have been added. * Server race conditions have been fixed; in particular commands are not run until after the configuration file is read completely. * Case insensitive searching in tmux's copy-mode is now possible. * attach-session and switch-client learnt the '-t' option to accept a window and/or a pane to use. * Copy-mode is only exited if no selection is in progress. * Paste key in copy-mode is now possible to enter text from the clipboard. * status-interval set to '0' now works as intended. * tmux now supports 256 colours running under fbterm. * Many bug fixes! CHANGES FROM 1.7 TO 1.8, 26 March 2013 Incompatible Changes ==================== * layout redo/undo has been removed. Normal Changes ============== * Add halfpage up/down bindings to copy mode. * Session choosing fixed to work with unattached sessions. * New window options window-status-last-{attr,bg,fg} to denote the last window which was active. * Scrolling in copy-mode now scrolls the region without moving the mouse cursor. * run-shell learnt '-t' to specify the pane to use when displaying output. * Support for middle-click pasting. * choose-tree learns '-u' to start uncollapsed. * select-window learnt '-T' to toggle to the last window if it's already current. * New session option 'assume-paste-time' for pasting text versus key-binding actions. * choose-* commands now work outside of an attached client. * Aliases are now shown for list-commands command. * Status learns about formats. * Free-form options can be set with set-option if prepended with an '@' sign. * capture-pane learnt '-p' to send to stdout, and '-e' for capturing escape sequences, and '-a' to capture the alternate screen, and '-P' to dump pending output. * Many new formats added (client_session, client_last_session, etc.) * Control mode, which is a way for a client to send tmux commands. Currently more useful to users of iterm2. * resize-pane learnt '-x' and '-y' for absolute pane sizing. * Config file loading now reports errors from all files which are loaded via the 'source-file' command. * 'copy-pipe' mode command to copy selection and pipe the selection to a command. * Panes can now emit focus notifications for certain applications which use those. * run-shell and if-shell now accept formats. * resize-pane learnt '-Z' for zooming a pane temporarily. * new-session learnt '-A' to make it behave like attach-session. * set-option learnt '-o' to prevent setting an option which is already set. * capture-pane and show-options learns '-q' to silence errors. * New command 'wait-for' which blocks a client until woken up again. * Resizing panes will now reflow the text inside them. * Lots and lots of bug fixes, fixing memory-leaks, etc. * Various manpage improvements. CHANGES FROM 1.6 TO 1.7, 13 October 2012 * tmux configuration files now support line-continuation with a "\" at the end of a line. * New option status-position to move the status line to the top or bottom of the screen. * Enforce history-limit option when clearing the screen. * Give each window a unique id, like panes but prefixed with @. * Add pane id to each pane in layout description (while still accepting the old form). * Provide defined ways to set the various default-path possibilities: ~ for home directory, . for server start directory, - for session start directory and empty for the pane's working directory (the default). All can also be used as part of a relative path (eg -/foo). Also provide -c flags to neww and splitw to override default-path setting. * Add -l flag to send-keys to send input literally (without translating key names). * Allow a single option to be specified to show-options to show just that option. * New command "move-pane" (like join-pane but allows the same window). * join-pane and move-pane commands learn "-b" option to place the pane to the left or above. * Support for bracketed-paste mode. * Allow send-keys command to accept hex values. * Add locking around "start-server" to avoid race-conditions. * break-pane learns -P/-F arguments for display formatting. * set-option learns "-q" to make it quiet, and not print out anything. * copy mode learns "wrap-search" option. * Add a simple form of output rate limiting by counting the number of certain C0 sequences (linefeeds, backspaces, carriage returns) and if it exceeds a threshold (current default 250/millisecond), start to redraw the pane every 100 milliseconds instead of making each change as it comes. Two configuration options - c0-change-trigger and c0-change-interval. * find-window learns new flags: "-C", "-N", "-T" to match against either or all of a window's content, name, or title. Defaults to all three options if none specified. * find-window automatically selects the appropriate pane for the found matches. * show-environment can now accept one option to show that environment value. * Exit mouse mode when end-of-screen reached when scrolling with the mouse wheel. * select-layout learns -u and -U for layout history stacks. * kill-window, detach-client, kill-session all learn "-a" option for killing all but the current thing specified. * move-window learns "-r" option to renumber window sequentially in a session. * New session option "renumber-windows" to automatically renumber windows in a session when a window is closed. (see "move-window -r"). * Only enter copy-mode on scroll up. * choose-* and list-* commands all use "-F" for format specifiers. * When spawning external commands, the value from the "default-shell" option is now used, rather than assuming /bin/sh. * New choose-tree command to render window/sessions as a tree for selection. * display-message learns new format options. * For linked-windows across sessions, all flags for that window are now cleared across sessions. * Lots and lots of bug fixes, fixing memory-leaks, etc. * Various manpage improvements. CHANGES FROM 1.5 TO 1.6, 23 January 2012 * Extend the mode-mouse option to add a third choice which means the mouse does not enter copy mode. * Add a -r flag to switch-client to toggle the client read-only flag. * Add pane-base-index option. * Support \ for line continuation in the configuration file. * Framework for more powerful formatting of command output and use it for list-{panes,windows,sessions}. This allows more descriptive replacements (such as #{session_name}) and conditionals. * Mark dead panes with some text saying they are dead. * Reject $SHELL if it is not a full path. * Add -S option to refresh-client to redraw status line. * Add an else clause for if-shell. * Try to resolve relative paths for loadb and saveb (first, using client working directory, if any, then default-path or session working directory). * Support for \e[3J to clear the history and send the corresponding terminfo code (E3) before locking. * When in copy mode, make repeat count indicate buffer to replace, if used. * Add screen*:XT to terminal-overrides for tmux-in-tmux. * Status-line message attributes added. * Move word-separators to be a session rather than window option. * Change the way the working directory for new processes is discovered. If default-path isn't empty, it is used. Otherwise, if a new window is created from the command-line, the working directory of the client is used. If not, platform specific code is used to retrieve the current working directory of the process in the active pane. If that fails, the directory where the session was created is used, instead. * Do not change the current pane if both mouse-select-{pane,window} are enabled. * Add \033[s and \033[u to save and restore cursor position. * Allow $HOME to be used as default-path. * Add CNL and CPL escape sequences. * Calculate last position correctly for UTF-8 wide characters. * Add an option allow-rename to disable the window rename escape sequence. * Attributes for each type of status-line alert (ie bell, content and activity) added. Therefore, remove the superfluous options window-status-alert-{attr,bg,fg}. * Add a -R flag to send-keys to reset the terminal. * Add strings to allow the aixterm bright colours to be used when configuring colours. * Drop the ability to have a list of keys in the prefix in favour of two separate options, prefix and prefix2. * Flag -2 added to send-prefix to send the secondary prefix key. * Show pane size in top right of display panes mode. * Some memory leaks plugged. * More command-prompt editing improvements. * Various manpage improvements. * More Vi mode improvements. CHANGES FROM 1.4 TO 1.5, 09 July 2011 * Support xterm mouse modes 1002 and 1003. * Change from a per-session stack of buffers to one global stack. This renders copy-buffer useless and makes buffer-limit now a server option. * Fix most-recently-used choice by avoiding reset the activity timer for unattached sessions every second. * Add a -P option to new-window and split-window to print the new window or pane index in target form (useful to pass it into other commands). * Handle a # at the end of a replacement string (such as status-left) correctly. * Support for UTF-8 mouse input (\033[1005h) which was added in xterm 262. If the new mouse-utf8 option is on, UTF-8 mouse input is enabled for all UTF-8 terminals. The option defaults to on if LANG etc are set in the same manner as the utf8 option. * Support for HP-UX. * Accept colours of the hex form #ffffff and translate to the nearest from the xterm(1) 256-colour set. * Clear the non-blocking IO flag (O_NONBLOCK) on the stdio file descriptors before closing them (fixes things like "tmux ls && cat"). * Use TMPDIR if set. * Fix next and previous session functions to actually work. * Support -x and -y for new-session to specify the initial size of the window if created detached with -d. * Make bind-key accept characters with the top-bit-set and print them as octal. * Set $TMUX without the session when background jobs are run. * Simplify the way jobs work and drop the persist type, so all jobs are fire-and-forget. * Accept tcgetattr/tcsetattr(3) failure, fixes problems with fatal() if the terminal disappears while locked. * Add a -P option to detach to HUP the client's parent process (usually causing it to exit as well). * Support passing through escape sequences to the underlying terminal by using DCS with a "tmux;" prefix. * Prevent tiled producing a corrupt layout when only one column is needed. * Give each pane created in a tmux server a unique id (starting from 0), put it in the TMUX_PANE environment variable and accept it as a target. * Allow a start and end line to be specified for capture-pane which may be negative to capture part of the history. * Add -a and -s options to lsp to list all panes in the server or session respectively. Likewise add -s to lsw. * Change -t on display-message to be target-pane for the #[A-Z] replacements and add -c as target-client. * The attach-session command now prefers the most recently used unattached session. * Add -s option to detach-client to detach all clients attached to a session. * Add -t to list-clients. * Change window with mouse wheel over status line if mouse-select-window is on. * When mode-mouse is on, automatically enter copy mode when the mouse is dragged or the mouse wheel is used. Also exit copy mode when the mouse wheel is scrolled off the bottom. * Provide #h character pair for short hostname (no domain). * Don't use strnvis(3) for the title as it breaks UTF-8. * Use the tsl and fsl terminfo(5) capabilities to update terminal title and automatically fill them in on terminals with the XT capability (which means their title setting is xterm-compatible). * Add a new option, mouse-resize-pane. When on, panes may be resized by dragging their borders. * Fix crash by resetting last pane on {break,swap}-pane across windows. * Add three new copy-mode commands - select-line, copy-line, copy-end-of-line. * Support setting the xterm clipboard when copying from copy mode using the xterm escape sequence for the purpose (if xterm is configured to allow it). * Support xterm(1) cursor colour change sequences through terminfo(5) Cc (set) and Cr (reset) extensions. * Support DECSCUSR sequence to set the cursor style with two new terminfo(5) extensions, Cs and Csr. * Make the command-prompt custom prompts recognize the status-left option character pairs. * Add a respawn-pane command. * Add a couple of extra xterm-style keys that gnome terminal provides. * Allow the initial context on prompts to be set with the new -I option to command-prompt. Include the current window and session name in the prompt when renaming and add a new key binding ($) for rename session. * Option bell-on-alert added to trigger the terminal bell when there is an alert. * Change the list-keys format so that it shows the keys using actual tmux commands which should be able to be directly copied into the config file. * Show full targets for lsp/lsw -a. * Make confirm-before prompt customizable with -p option like command-prompt and add the character pairs #W and #P to the default kill-{pane,window} prompts. * Avoid sending data to suspended/locked clients. * Small memory leaks in error paths plugged. * Vi mode improvements. CHANGES FROM 1.3 TO 1.4, 27 December 2010 * Window bell reporting fixed. * Show which pane is active in the list-panes output. * Backoff reworked. * Prevent the server from dying when switching into copy mode when already in a different mode. * Reset running jobs when the status line is enabled or disabled. * Simplify xterm modifier detection. * Avoid crashing in copy mode if the screen size is too small for the indicator. * Flags -n and -p added to switch-client. * Use UTF-8 line drawing characters on UTF-8 terminals, thus fixing some terminals (eg putty) which disable the vt100 ACS mode switching sequences in UTF-8 mode. On terminals without ACS, use ASCII equivalents. * New server option exit-unattached added. * New session option destroy-unattached added. * Fall back on normal session choice method if $TMUX exists but is invalid rather than rejecting. * Mark repeating keys with "(repeat)" in the key list. * When removing a pane, don't change the active pane unless the active pane is actually the one being removed. * New command last-pane added. * AIX fixes. * Flag -a added to unbind-key. * Add XAUTHORITY to update-environment. * More info regarding window and pane flags is now shown in list-*. * If VISUAL or EDITOR contains "vi" configure mode-keys and status-key to vi. * New window option monitor-silence and session option visual-silence added. * In the built-in layouts distribute the panes more evenly. * Set the default value of main-pane-width to 80 instead of 81. * Command-line flag -V added. * Instead of keeping a per-client prompt history make it global. * Fix rectangle copy to behave like emacs (the cursor is not part of the selection on the right edge but on the left it is). * Flag -l added to switch-client. * Retrieve environment variables from the global environment rather than getenv(3), thus allowing them to be updated during the configuration file. * New window options other-pane-{height,width} added. * More minor bugs fixed and manpage improvements. CHANGES FROM 1.2 TO 1.3, 18 July 2010 * New input parser. * Flags to move through panes -UDLR added to select-pane. * Commands up-pane, and down-pane removed, since equivalent behaviour is now available through the target flag (-t:+ and -t:-). * Jump-forward/backward in copy move (based on vi's F, and f commands). * Make paste-buffer accept a pane as a target. * Flag -a added to new-window to insert a window after an existing one, moving windows up if necessary. * Merge more mode into copy mode. * Run job commands explicitly in the global environment (which can be modified with setenv -g), rather than with the environment tmux started with. * Use the machine's hostname as the default title, instead of an empty string. * Prevent double free if the window option remain-on-exit is set. * Key string conversions rewritten. * Mark zombie windows as dead in the choose-window list. * Tiled layout added. * Signal handling reworked. * Reset SIGCHLD after fork to fix problems with some shells. * Select-prompt command removed. Therefore, bound ' to command-prompt -p index "select-window -t:%%" by default. * Catch SIGHUP and terminate if running as a client, thus avoiding clients from being left hanging around when, for instance, a SSH session is disconnected. * Solaris 9 fixes (such as adding compat {get,set}env(3) code). * Accept none instead of default for attributes. * Window options window-status-alert-{alert,bg,fg} added. * Flag -s added to the paste-buffer command to specify a custom separator. * Allow dragging to make a selection in copy mode if the mode-mouse option is set. * Support the mouse scroll wheel. * Make pipe-pane accept special character sequences (eg #I). * Fix problems with window sizing when starting tmux from .xinitrc. * Give tmux sockets (but not the containing folder) group permissions. * Extend the target flags (ie -t) to accept an offset (for example -t:+2), and make it wrap windows, and panes. * New command choose-buffer added. * New server option detach-on-destroy to set what happens to a client when the session it is attached to is destroyed. If on (default), the client is detached. Otherwise, the client is switched to the most recently active of the remaining sessions. * The commands load-buffer, and save-buffer now accept a dash (-) as the file to read from stdin, or write to stdout. * Custom layouts added. * Additional code reduction, bug fixes, and manpage enhancements. CHANGES FROM 1.1 TO 1.2, 10 March 2010 * Switch to libevent. * Emulate the ri (reverse index) capability, ergo allowing tmux to at least start on Sun consoles (TERM=sun, or sun-color). * Assign each entry a number, or lowercase letter in choose mode, and accept that as a shortcut key. * Permit top-bit-set characters to be entered in the status line. * Mark no-prefix keys with (no prefix), rather than [] in list-keys. * New command show-messages (alias showmsgs), and new session option message-limit, to show a per-client log of status lines messages up to the number defined by message-limit. * Do not interpret #() for display-message to avoid leaking commands. * New window options window-status-format, and window-status-current-format to control the format of each window in the status line. * Add a -p flag to display-message to print the output, instead of displaying it in the status line. * Emulate il1, dl1, ich1 to run with vt100 feature set. * New command capture-pane (alias capturep) to copy the entire pane contents to a paste buffer. * Avoid duplicating code by adding a -w flag to set-option, and show-options to set, and show window options. The commands set-window-option, and show-window-options are now aliases. * Panes can now be referred to as top, bottom, top-left, etc. * Add server-wide options, which can be set with set-option -s, and shown with show-options -s. * New server option quiet (like -q from the command line). * New server option escape-time to set the timeout used to detect if escapes are alone, part of a function key, or meta sequence. * New session options pane-active-border-bg, pane-active-border-fg, pane-border-bg, and pane-border-fg to set pane colours. * Make split-window accept a pane target, instead of a window. * New command join-pane (alias joinp) to split, and move an existing pane into the space (the opposite of break-pane), thus simplifying calls to split-window, followed by move-window. * Permit S- prefix on keys for shift when the terminal/terminfo supports them. * Window targets (-t flag) can now refer to the last window (!), next (+), and previous (-) window by number. * Mode keys to jump to the bottom/top of history, end of the next word, scroll up/down, and reverse search in copy mode. * New session option display-panes-active-colour to display the active pane in a different colour with the display-panes command. * Read the socket path from $TMUX if it's present, and -L, and -S are not given. * Vi-style mode keys B, W, and E to navigate between words in copy mode. * Start in more mode when configuration file errors are detected. * Rectangle copy support added. * If attach-session was specified with the -r flag, make the client read-only. * Per-window alternate-screen option. * Make load-buffer work with FIFOs. * New window option word-separators to set the characters considered as word separators in copy mode. * Permit keys in copy mode to be prefixed by a repeat count, entered with [1-9] in vi mode, or M-[1-9] in emacs mode. * utf8 improvements. * As usual, additional code reduction, bug fixes, and manpage enhancements. CHANGES FROM 1.0 TO 1.1, 05 November 2009 * New run-shell (alias run) command to run an external command without a window, capture it's stdout, and send it to output mode. * Ability to define multiple prefix keys. * Internal locking mechanism removed. Instead, detach each client and run the external command specified in the new session option lock-command (by default lock -np), thus allowing the system password to be used. * set-password command, and -U command line flag removed per the above change. * Add support for -c command line flag to execute a shell command. * New lock-client (alias lockc), and lock-session (alias locks) commands to lock a particular client, or all clients attached to a session. * Support C-n/C-p/C-v/M-v with emacs keys in choice mode. * Use : for goto line rather than g in vi mode. * Try to guess which client to use when no target client was specified. Finds the current session, and if only one client is present, use it. Otherwise, return the most recently used client. * Make C-Down/C-Up in copy mode scroll the screen down/up one line without moving the cursor. * Scroll mode superseded by copy mode. * New synchronize-panes window option to send all input to all other panes in the same window. * New lock-server session option to lock, when off (on by default), each session when it has been idle for the lock-after-time setting. When on, the entire server locks when all sessions have been idle for their individual lock-after-time setting. * Add support for grouped sessions which have independent name, options, current window, but where the linked windows are synchronized (ie creating, killing windows are mirrored between the sessions). A grouped session may be created by passing -t to new-session. * New mouse-select-pane session option to select the current pane with the mouse. * Queue, and run commands in the background for if-shell, status-left, status-right, and #() by starting each once every status-interval. Adds the capability to call some programs which would previously cause the server to hang (eg sleep/tmux). It also avoids running commands excessively (ie if used multiple times, it will be run only once). * When a window is zombified and automatic-rename is on, append [dead] to the name. * Split list-panes (alias lsp) off from list-windows. * New pipe-pane (alias pipep) to redirect a pane output to an external command. * Support for automatic-renames for Solaris. * Permit attributes to be turned off in #[] by prefixing with no (eg nobright). * Add H/M/L in vi mode, and M-R/M-r in emacs to move the cursor to the top, middle, and bottom of the screen. * -a option added to kill-pane to kill all except current pane. * The -d command line flag is now gone (can be replaced by terminal-overrides). Just use op/AX to detect default colours. * input/tty/utf8 improvements. * xterm-keys rewrite. * Additional code reduction, and bug fixes. CHANGES FROM 0.9 TO 1.0, 20 September 2009 * Option to alter the format of the window title set by tmux. * Backoff for a while after multiple incorrect password attempts. * Quick display of pane numbers (C-b q). * Better choose-window, choose-session commands and a new choose-client command. * Option to request multiple responses when using command-prompt. * Improved environment handling. * Combine wrapped lines when pasting. * Option to override terminal settings (terminal-overrides). * Use the full range of ACS characters for drawing pane separator lines. * Customisable mode keys. * Status line colour options, with embedded colours in status-left/right, and an option to centre the window list. * Much improved layouts, including both horizontal and vertical splitting. * Optional visual bell, activity and content indications. * Set the utf8 and status-utf8 options when the server is started with -u. * display-message command to show a message in the status line, by default some information about the current window. * Improved current process detection on NetBSD. * unlink-window -k is now the same as kill-window. * attach-session now works from inside tmux. * A system-wide configuration file, /etc/tmux.conf. * A number of new commands in copy mode, including searching. * Panes are now specified using the target (-t) notation. * -t now accepts fnmatch(3) patterns and looks for prefixes. * Translate \r into \n when pasting. * Support for binding commands to keys without the prefix key * Support for alternate screen (terminfo smcup/rmcup). * Maintain data that goes off screen after reducing the window size, so it can be restored when the size is increased again. * New if-shell command to test a shell command before running a tmux command. * tmux now works as the shell. * Man page reorganisation. * Many minor additions, much code tidying and several bug fixes. CHANGES FROM 0.8 TO 0.9, 01 July 2009 * Major changes to build infrastructure: cleanup of makefiles and addition of a configure script. * monitor-content window option to monitor a window for a specific fnmatch(3) pattern. The find-window command also now accepts fnmatch(3) patterns. * previous-layout and select-layout commands, and a main-horizontal layout. * Recreate the server socket on SIGUSR1. * clear-history command. * Use ACS line drawing characters for pane separator lines. * UTF-8 improvements, and code to detect UTF-8 support by looking at environment variables. * The resize-pane-up and resize-pane-down commands are now merged together into a new resize-pane command with -U and -D flags. * confirm-before command to request a yes/no answer before executing dangerous commands. * Status line bug fixes, support for UTF-8 (status-utf8 option), and a key to paste from the paste buffer. * Support for some additional escape sequences and terminal features, including better support for insert mode and tab stops. * Improved window resizing behaviour, modelled after xterm. * Some code reduction and a number of miscellaneous bug fixes. ================================================================================ On 01 June 2009, tmux was imported into the OpenBSD base system. From this date onward changes are logged as part of the normal CVS commit message to either OpenBSD or SourceForge CVS. This file will be updated to contain a summary of major changes with each release, and to mention important configuration or command syntax changes during development. The list of older changes is below. ================================================================================ 21 May 2009 * stat(2) files before trying to load them to avoid problems, for example with "source-file /dev/zero". 19 May 2009 * Try to guess if the window is UTF-8 by outputting a three-byte UTF-8 wide character and seeing how much the cursor moves. Currently tries to figure out if this works by some stupid checks on the terminal, these need to be rethought. Also might be better using a width 1 character rather than width 2. * If LANG contains "UTF-8", assume the terminal supports UTF-8, on the grounds that anyone who configures it probably wants UTF-8. Not certain if this is a perfect idea but let's see if it causes any problems. * New window option: monitor-content. Searches for a string in a window and if it matches, highlight the status line. 18 May 2009 * main-horizontal layout and main-pane-height option to match vertical. * New window option main-pane-width to set the width of the large left pane with main-vertical (was left-vertical) layout. * Lots of layout cleanup. manual layout is now manual-vertical. 16 May 2009 * select-layout command and a few default key bindings (M-0, M-1, M-2, M-9) to select layouts. * Recreate server socket on SIGUSR1, per SF feature request 2792533. 14 May 2009 * Keys in status line (p in vi mode, M-y in emacs) to paste the first line of the upper paste buffer. Suggested by Dan Colish. * clear-history command to clear a pane's history. * Don't force wrapping with \n when asked, let the cursor code figure it out. Should fix terminals which use this to detect line breaks. * Major cleanup and restructuring of build infrastructure. Still separate files for GNU and BSD make, but they are now hugely simplified at the expense of adding a configure script which must be run before make. Now build and install with: $ ./configure && make && sudo make install 04 May 2009 * Use ACS line drawing characters for pane separator lines. 30 April 2009 * Support command sequences without a space before the semicolon, for example "neww; neww" now works as well as "neww ; neww". "neww;neww" is still an error. * previous-layout command. * Display the layout name in window lists. * Merge resize-pane-up and resize-pane-down into resize-pane with -U and -D flags. 29 April 2009 * Get rid of compat/vis.* - only one function was used which is easily replaced,and less compat code == good. 27 April 2009 * Avoid using the prompt history when the server is locked, and prevent any input entered from being added to the client's prompt history. * New command, confirm-before (alias confirm), which asks for confirmation before executing a command. Bound "&" and "x" by default to confirm-before "kill-window" and confirm-before "kill-pane", respectively. 23 April 2009 * Support NEL, yet another way of making newline. Fixes the output from some Gentoo packaging thing. Reported by someone on SF then logs that allowed a fix sent by tcunha. * Use the xenl terminfo flag to detect early-wrap terminals like the FreeBSD console. Many thanks for a very informative email from Christian Weisgerber. 21 April 2009 * tmux 0.8 released. 17 April 2009 * Remove the right number of characters from the buffer when escape then a cursor key (or other key prefixed by \033) is pressed. Reported by Stuart Henderson. 03 April 2009 * rotate-window command. -U flag (default) for up, -D flag for down. 02 April 2009 * Change scroll/pane redraws to only redraw the single pane affected rather than the entire window. * If redrawing the region would mean redrawing > half the pane, just schedule to redraw the entire window. Also add a flag to skip updating the window any further if it is scheduled to be redrawn. This has the effect of batching multiple redraws together. 01 April 2009 * Basic horizontal splitting and layout management. Still some redraw and other issues - particularly, don't mix with manual pane resizing, be careful when viewing from multiple clients and don't expect shell windows to redraw very well after the layout is changed; generally cycling the layout a few times will fix most problems. Getting this in for testing while I think about how to deal with manual mode. Split window as normal and cycle the layouts with C-b space. Some of the layouts will work better when swap-pane comes along. 31 March 2009 * AIX port, thanks to cmihai for access to a box. Only tested on 6.1 with xlc 10.1 (make sure CC is set). Needs GNU make and probably ncurses (didn't try plain curses). Also won't build with DEBUG, so comment the FDEBUG=1 line in GNUmakefile. * Draw a vertical line on the right when the window size is less than the terminal size. This is partly to shake out any horizontal limit bugs on the way to horizontal splitting/pane tiling. Currently a bit slow since it has to do a lot of redrawing but hopefully that will improve as I get some better ideas for how to do it. * Fix remaining problems with copy and paste and UTF-8. 28 March 2009 * Better UTF-8 support, including combined characters. Unicode data is now stored as UTF-8 in a separate array, the code does a lookup into this every time it gets to a UTF-8 cell. Zero width characters are just appended onto the UTF-8 data for the previous cell. This also means that almost no bytes extra are wasted non-Unicode data (yay). Still some oddities, such as copy mode skips over wide characters in a strange way, and the code could do with some tidying. * Key repeating is now a property of the key binding not of the command. Repeat is turned on when the key is bound with the -r flag to bind-key. next/previous-window no longer repeat by default as it turned out to annoy me. 27 March 2009 * Clear using ED when redrawing the screen. I foolishly assumed using spaces would be equivalent and terminals would pick up on this, but apparently not. This fixes copy and paste in xterm/rxvt. * Sockets in /tmp are now created in a subdirectory named, tmux-UID, eg tmux-1000. The default socket is thus /tmp/tmux-UID/default. To start a separate server, the new -L command line option should be used: this creates a socket in the same directory with a different name ("-L main" will create socket called "main"). -S should only be used to place the socket outside /tmp. This makes sockets a little more secure and a bit more convenient to use multiple servers. 21 March 2009 * New session flag "set-remain-on-exit" to set remain-on-exit flag for new windows created in that session (like "remain-by-default" used to do). Not perfectly happy about this, but until I can think of a good way to introduce it generically (maybe a set of options in the session) this will do. Fixes SF request 2527847. 07 March 2009 * Support for 88 colour terminals. * break-pane command to create a new window using an existing pane. 02 March 2009 * Make escape key timer work properly so escape+key can be used without lightning fast key presses. 13 February 2009 * Redo mode keys slightly more cleanly and apply them to command prompt editing. vi or emacs mode is controlled by the session option status-keys. 12 February 2009 * Looking up argv[0] is expensive, so just use p_comm for the window name which is good enough. Also increase name update time to 500 ms. 11 February 2009 * Only use ri when actually at the top of the screen; just move the cursor up otherwise. * FreeBSD's console wraps lines at $COLUMNS - 1 rather than $COLUMNS (the cursor can never be beyond $COLUMNS - 1) and does not appear to support changing this behaviour, or any of the obvious possibilities (turning off right margin wrapping, insert mode). This is irritating, most notably because it impossible to write to the very bottom-right of the screen without scrolling. To work around this, if built on FreeBSD and run with a "cons" $TERM, the bottom-right cell on the screen is omitted. * Emulate scroll regions (slowly) to support the few terminals which don't have it (some of which don't really have any excuse). 10 February 2009 * No longer redraw the status line every status-interval unless it has actually changed. 08 February 2009 * Don't treat empty arguments ("") differently when parsing configuration file/command prompt rather than command line. * tmux 0.7 released. 03 February 2009 * New command, copy-buffer (alias copyb), to copy a session paste buffer to another session. 01 February 2009 * The character pair #(command) may now contain (escaped) right parenthesis. 30 January 2009 * . now bound to "command-prompt 'move-window %%'" by default, from joshe. 29 January 2009 * Window options to set status line fg, bg and attributes for a single window. Options are: window-status-fg, window-status-bg, window-status-attr. Set to "default" to use the session status colours. This allows quite neat things like: $ cat ~/bin/xssh #!/bin/sh if [ ! -z "$TMUX" ]; then case "$1" in natalya) tmux setw window-status-fg red >/dev/null ;; natasha) tmux setw window-status-fg yellow >/dev/null ;; esac fi ssh "$@" [ ! -z "$TMUX" ] && tmux setw -u window-status-fg >/dev/null $ alias ssh="~/bin/xssh" * Support #(command) in status-left, and status-right, which is displayed as the first line of command's output (e.g. set -g status-right "#(whoami)@#(hostname -s)"). Commands with )s aren't supported. 28 January 2009 * Support mouse in copy mode to move cursor. Can't do anything else at the moment until other mouse modes are handled. * Better support for at least the most common variant of mouse input: parse it and adjust for different panes. Also support mouse in window/session choice mode. 27 January 2009 * Bring back the fancy window titles with session/window names: it is easy to work around problems with elinks (see FAQ). * -u flag to scroll-mode and copy-mode to start scrolled one page up. scroll-mode -u is bound to prefix,page-up (ppage) by default. * Allow status, mode and message attributes to be changed by three new options: status-attr, mode-attr, message-attr. A comma-separated list is accepted containing: bright, dim, underscore, blink, reverse, hidden, italics, for example: set -g status-attr bright,blink From Josh Elsasser, thanks! 26 January 2009 * Be more clever about picking the right process to create the window name. * Don't balls up the terminal on UTF-8 combined characters. Don't support them properly either - they are just discarded for the moment. 25 January 2009 * load-buffer command 23 January 2009 * Use reverse colours rather than swapping fg and bg for message, mode and status line. This makes these usable on black and white terminals. * Better error messages when creating a session or window fails. * Oops. Return non-zero on error. Reported by Will Maier. 21 January 2009 * Handle SIGTERM (and kill-server which uses it), a bit more neatly - tidy up properly and print a nicer message. Same effect though :-). * new-window now supports -k to kill target window if it exists. * Bring back split-window -p and -l options to specify the height a percentage or as a number of lines. * Make window and session choice modes allow you to choose items in vi keys mode (doh!). As a side-effect, this makes enter copy selection (as well as C-w/M-w) when using emacs keys in copy mode. Reported by merdely. 20 January 2009 * Darwin support for automatic-rename from joshe; Darwin doesn't seem to have a sane method of getting argv[0] and searching for the precise insane way is too frustrating, so this just uses the executable name. * Try to change the window title to match the command running it in. This is done by reading argv[0] from the process group leader of the group that owns the tty (tcgetpgrp()). This can't be done portably so some OS-dependent code is introduced (ugh); OpenBSD, FreeBSD and Linux are supported at the moment. A new window flag, automatic-rename, is available: if this is set to off, the window name is not changed. Specifying a name with the new-window, new-session or rename-window commands will automatically set this flag to off for the window in question. To disable it entirely set the option to off globally (setw -g automatic-rename off). 19 January 2009 * Fix various stupid issues when the status line is turned off. Grr. * Use reverse attributes for clock and cursor, otherwise they do not appear on black and white terminals. * An error in a command sequence now stops execution of that sequence. Internally, each command code now passes a return code back rather than talking to the calling client (if any) directly. * attach-session now tries to start the server if it isn't already started - if no sessions are created in .tmux.conf this will cause an error. * Clean up starting server by making initial client get a special socketpair. 18 January 2009 * Unbreak UTF-8. * -a flag to next-window and previous-window to select the next or previous window with activity or bell. Bound to M-n and M-p. * find-window command to search window names, titles and visible content (but not history) for a string. If only one is found, the window is selected otherwise a choice list is shown. This (as with the other choice commands) only works from a key. Bound to "f" by default. * Cleaned up command printing code, also enclose arguments with spaces in "s. * Added command sequences. These are entered by separating each argument by a ; argument (spaces on both sides), for example: lsk ; lsc To use a literal ; as the argument prefix it with \, for example: bind x lsk \; lsc Commands are executed from left to right. Also note that command sequences do not support repeat-time repetition unless all commands making up the sequence support it. * suspend-client command to suspend a client. Don't try to background it though... * Mark attached sessions in sessions lists. Suggested by Simon Kuhnle. 17 January 2009 * tmux 0.6 released. 15 January 2009 * Support #H for hostname and #S for session name in status-left/right. * Two new commands, choose-window and choose-session which work only when bound to a key and allow the window or session to be selected from a list. These are now bound to "w" and "s" instead of the list commands. 14 January 2009 * Rework the prefix-time stuff. The option is now called repeat-time and defaults to 500 ms. It only applies to a small subset of commands, currently: up-pane, down-pane, next-window, previous-window, resize-pane-up, resize-pane-down. These are the commands for which it is obviously useful, having it for everything else was just bloody annoying. * The alt-up and alt-down keys now resize a pane by five lines at a time. * switch-pane is now select-pane and requires -p to select a pane. The "o" key binding is changed to down-pane. * up-pane and down-pane commands, bound to arrow up and down by default. * Multiple vertical window splitting. Minimum pane size is four lines, an (unhelpful) error will be shown if attempting to split a window with less that eight lines. If the window is resized, as many panes are shown as can fit without reducing them below four lines. There is (currently!) not a way to show a hidden pane without making the window larger. Note the -p and -l options to split-window are now gone, these may reappear once I think them through again. * Server locking on inactivity (lock-after-time) is now disabled by default. 13 January 2009 * kill-pane command. 12 January 2009 * command-prompt now accepts a single argument, a template string. Any occurrences of %% in this string are replaced by whatever is entered at the prompt and the result is executed as a command. This allows things like (now bound by default): bind , command-prompt "rename-window %%" Or my favourite: bind x command-prompt "split-window 'man %%'" * Option to set prefix time, allowing multiple commands to be entered without pressing the prefix key again, so long as they each typed within this time of each other. * Yet more hacks for key handling. Think it is just about working now. * Two commands, resize-pane-up and resize-pane-down to resize a pane. * Make the window pane code handle panes of different sizes, and add a -l and -p arguments to split-window to specify the new window size in lines or as a percentage. 11 January 2009 * Vertical window splitting. Currently can only split a window into two panes. New split-window command splits (bound to ") and switch-pane command (bound to o) switches between panes. close-pane, swap-pane commands are to follow. Also to come are pane resizing, >2 panes, the ability to break a pane out to a full window and vice versa and possibly horizontal splitting. Panes are subelements of windows rather than being windows in their own right. I tried to make them windows (so the splitting was at the session or client level) but this rapidly became very complex and invasive. So in the interests of having something working, I just made it so each window can have two child processes instead of one (and it still took me 12 hours straight coding). Now the concept is proven and much of the support code is there, this may change in future if more flexibility is needed. * save-buffer command, from Tiago Cunha. 10 January 2009 * New option, lock-after-time. If there is no activity in the period specified by this option (in seconds), tmux will lock the server. Default is 1800 (30 minutes), set to 0 to disable. * Server locking. Two new commands: set-password to set a password (a preencrypted password may be specified with -c); and lock-server to lock the server until the password is entered. Also an additional command line flag, -U, to unlock from the shell. The default password is blank (any password accepted). If specifying an encrypted password from encrypt(1) in .tmux.conf with -c, don't forget to enclose it in single-quotes (') to prevent shell variable expansion. * If a window is created from the command line, tmux will now use the same current working directory for the new process. A new default-path option to sets the working directory for processes created from keys or interactively from the prompt. * New mode to display a large clock. Entered with clock-mode command (bound to C-b t by default); two window options: clock-mode-colour and clock-mode-style (12 or 24). This will probably be used as the basis for window locking. * New command, server-info, to show some server information and terminal details. 09 January 2009 * Stop using ncurses variables and instead build a table of the codes we want into an array for each terminal type. This makes the code a little more untidy in places but gets rid of the awful global variables and calling setterm all the time, and shoves all the ncurses-dependent mess into a single file, tty-term.c. It also allows overriding single terminal codes, this is used to fix rxvt on some platforms (where it is missing dch) and in future may allow user customisation a la vim. * Update key handling code. Simplify, support ctrl properly and add a new window option (xterm-keys) to output xterm key codes including ctrl and, if available, alt and shift. 08 January 2009 * If built without DEBUG (the release versions), don't cause a fatal error if the grid functions notice an input error, just log and ignore the request. This might mean me getting shouted at less often when bugs kill long-running sessions, at least in release versions. * Hopefully fix cursor out-of-bounds checking when writing to grid. When I wrote the code I must have forgotten that the cursor can be one cell off the right of the screen (yes, I know), so there were number of out-of-bounds/ overflow problems. 07 January 2009 * New flag to set and setw, -u, to unset an option (allowing it to inherit from) the global options again. * Added more info messages for options changes. * A bit of tidying and reorganisation of options code. 06 January 2009 * Don't crash when backspacing if cursor is off the right of the screen, reported by David Chisnall. * Complete words at any point inside command in prompt, also use option name as well as command names. * Per-client prompt history of up to 100 items. * Use a splay tree for key bindings instead of an array. As a side-effect this sorts them when listed. 22 December 2008 * Use the right keys for home and end. 20 December 2008 * Add vim mode for tmux configuration file to examples/, from Tiago Cunha. 15 December 2008 * New command, source-file (alias source), to load a configuration file. Written by Tiago Cunha, many thanks. 13 December 2008 * Work around lack of dch. On Linux, the rxvt termcap doesn't have it (it is lying, but we can't really start disbelieving termcaps...). This is a bit horrible - I can see no way to do it without pretty much redrawing the whole line, but it works... 10 December 2008 * glibc's getopt(3) is useless: it is not POSIX compliant without jumping through non-portable hoops, and the method of resetting it is unclear (the man page on my system says set optind to 1, but other sources say 0). So, import OpenBSD's getopt_long.c into compat/ for use on Linux and use the clearly documented optreset = optind = 1 method. This fixes some strange issues with command parsing (getting the syntax wrong would prevent any further commands being parsed). 06 December 2008 * Bring set/setw/show/showw into line with other commands. This means that by default they now affect the current window (if any); the new -g flag must be passed to set the global options. This changes the behaviour of set/show and WILL BREAK CURRENT CONFIGURATIONS. In summary, whether in the configuration file, the command prompt, or a key binding, use -g to set a global option, use -t to specify a particular window or session, or omit both to try and use the current window or session. This makes set/show a bit of a pain but is the correct behaviour for setw/showw and is the same as every other command, so we can put up with a bit of pain for consistency. * Redo window options. They now work in the same way to session options with a global options set. showw/setw commands now have similar syntax to show/set (including the ability to use abbreviations). PLEASE NOTE this includes the following configuration-breaking changes: - remain-by-default is now GONE, use "setw -g remain-on-exit" to apply the global window option instead; - mode-keys is now a window option rather than session - use "setw [-g] mode-keys" instead of set. There are also some additions: - message-fg and message-bg session options to control status line message colours; - mode-fg and mode-bg window options to set colours in window modes such as copy mode. The options code still a mess and now there is twice as much of it :-(. 02 December 2008 * Add support for including the window title in status-left or status-right strings by including the character pair "#T". This may be prefixed with a number to specify a maximum length, for example "#24T" to use at most 24 characters of the title. * Introduce two new options, status-left-length and status-right-length, control the maximum length of left and right components of the status bar. * elinks (and possibly others) bypass the terminal and talk directly to X to restore the window title when exiting. tmux can't know about this particular bit of stupidity so the title ends up strange - the prefix isn't terribly important and elinks is quite useful so just get rid of it. 27 November 2008 * Tweaks to support Dragonfly. 17 November 2008 * tmux 0.5 released. 16 November 2008 * New window option: "utf8"; this must be on (it is off by default) for UTF-8 to be parsed. The global/session option "utf8-default" controls the setting for new windows. This means that by default tmux does not handle UTF-8. To use UTF-8 by default it is necessary to a) "set utf8-default on" in .tmux.conf b) start tmux with -u on any terminal which support UTF-8. It seems a bit unnecessary for this to be a per-window option but that is the easiest way to do it, and it can't do any harm... * Enable default colours if op contains \033[39;49m, based on a report from fulvio ciriaco. 12 November 2008 * Keep stack of last windows rather than just most recent; based on a diff from joshe. 04 November 2008 * Don't try to redraw status line when showing a prompt or message; if it does, the status timer is never reset so it redraws on every loop. Spotted by joshe. 09 October 2008 * Translate 256 colours into 16 if 256 is not available, same as screen does. * Better support for OSC command (only to set window title now), and also support using APC for the same purpose (some Linux default shell profiles do this). 25 September 2008 * Large internal rewrite to better support 256 colours and UTF-8. Screen data is now stored as single two-way array of structures rather than as multiple separate arrays. Also simplified a lot of code. Only external changes are three new flags, -2, -d and -u, which force tmux to assume the terminal supports 256 colours, default colours (useful for xterm-256color which lacks the AX flag), or UTF-8 respectively. 10 September 2008 * Split off colour conversion code from screen code. 09 September 2008 * Initial UTF-8 support. A bit ugly and with a limit of 4096 UTF-8 characters per window. 08 September 2008 * 256 colour support. tmux attempts to autodetect the terminal by looking both at what ncurses reports (usually wrong for xterm) and checking if the TERM contains "256col". For xterm TERM=xterm-256color is needed (as well as a build that support 256 colours); this seems to work for rxvt as well. On non-256 colour terminals, high colours are translated to white foreground and black background. 28 August 2008 * Support OS X/Darwin thanks to bsd-poll.c from OpenSSH. Also convert from clock_gettime(2) to gettimeofday(2) as OS X doesn't support the former; microsecond accuracy will have to be sufficient ;-). 07 August 2008 * Lose some unused/useless wrapper functions. 25 July 2008 * Shell variables may now be defined and used in configuration file. Define variables with: VAR=1 And use with: renamew ${VAR} renamew "x${VAR}x" Also some other fixes to make, for example, "abc""abc" work similarly to the shell. 24 July 2008 * Finally lose inconsistently-used SCREEN_DEF* defines. * If cursor mode is on, switch the arrow keys from \033[A to \033OA. * Support the numeric keypad in both application and numbers mode. This is different from screen which always keeps it in application mode. 19 July 2008 * Unbreak "set status" - tmux thought it was ambiguous, reported by rivo nurges. 02 July 2008 * Split vi and emacs mode keys into two tables and add an option (mode-keys) to select between them. Default is emacs, use, tmux set mode-keys vi to change to vi. vi mode uses space to start selection, enter to copy selection and escape to clear selection. 01 July 2008 * Protocol versioning. Clients which identify as a different version from the server will be rejected. * tmux 0.4 released. 29 June 2008 * Zombie windows. These are not closed when the child process dies. May be set for a window with the new "remain-on-exit" option; the default setting of this flag for new windows may be set with the "remain-by-default" session option. A window may be restarted with the respawn-window command: respawn-window [-k] [command] If -k is given, any existing process running in the window is killed; if command is omitted, the same command as when the window was first created is used. 27 June 2008 * Handle nonexistent session or client to -t properly. 25 June 2008 * select-prompt command to allow a window to be selected at a prompt. Only windows in the current session may be selected. Bound to ' by default. Suggested by merdely. * move-window command. Requested by merdely. * Support binding alt keys (prefixed with M-). Change default to use C- for ctrl keys (^ is still accepted as an alternative). * Slim down default key bindings: support lowercase only. * Handle escaped keys properly (parse eg \033b into a single key code) and use this to change copy mode next/previous work to M-f and M-b to match emacs. 24 June 2008 * Next word (C-n/w) and previous word (C-b/b) in copy mode. 23 June 2008 * list-commands command (alias lscm). * Split information about options into a table and use it to parse options on input (allowing abbreviations) and to print them with show-options (meaning that bell-action gets a proper string). This turned out a bit ugly though :-/. 22 June 2008 * Do not translate black and white into default if the terminal supports default colours. This was nice to force programs which didn't use default colours to be properly transparent in rxvt/aterm windows with a background image, but it causes trouble if someone redefines the default foreground and background (to have black on white or something). 21 June 2008 * Naive tab completion in the command prompt. This only completes command names if a) they are at the start of the text b) the cursor is at the end of the text c) the text contains no spaces. * Only attempt to set the title where TERM looks like an xterm (contains "xterm", "rxvt" or is "screen"). I hate this but I don't see a better way: setting the title actually kills some other terminals pretty much dead. * Strip padding out of terminfo(5) strings. Currently the padding is just ignored, this may need to be altered if there are any software terminals out there that actually need it. 20 June 2008 * buffer-limit option to set maximum size of buffer stack. Default is 9. * Initial buffer improvements. Each session has a stack of buffers and each buffer command takes a -b option to manipulate items on the stack. If -b is omitted, the top entry is used. The following commands are currently available: set-buffer [-b index] [-t target-session] string paste-buffer [-d] [-b index] [-t target-window] delete-buffer [-b index] [-t target-session] show-buffers [-t target-session] show-buffer [-b index] [-t target-session] -d to paste-buffer deletes the buffer after pasting it. * New option, display-time, sets the time status line messages stay on screen (unless a key is pressed). Set in milliseconds, default is 750 (0.75 seconds). The timer is only checked every 100 ms or so. 19 June 2008 * Use "status" consistently for status line option, and prefix for "prefix" key option. * Allow commands to be entered at a prompt. This is triggered with the command-prompt command, bound to : by default. * Show status messages properly, without blocking the server. 18 June 2008 * New option, set-titles. On by default, this attempts to set the window title using the \e]2;...\007 xterm code. Note that elinks requires the STY environment variable (used by screen) to be set before it will set the window title. So, if you want window titles set by elinks, set STY before running it (any value will do). I can't do this for all windows since setting it to an invalid value breaks screen. * Show arrows at either end of status line when scrolled if more windows exist. Highlight the arrow if a hidden window has activity or bell. * Scroll the status line to show the current window if necessary. Also handle windows smaller than needed better (show a blank status line instead of hanging or crashing). 17 June 2008 * tmux 0.3 released. 16 June 2008 * Add some information messages when window options are changed, suggested by Mike Erdely. Also add a -q command-line option to suppress them. * show-window-options (showw) command. 15 June 2008 * show-options (show) command to show one or all options. 14 June 2008 * New window options: force-width and force-height. This will force a window to an arbitrary width and height (0 for the default unlimited). This is neat for emacs which doesn't have a sensible way to force hard wrapping at 80 columns. Also, don't try to be clever and use clr_eol when redrawing the whole screen, it causes trouble since the redraw functions are used to draw the blank areas too. * Clear the blank area below windows properly when they are smaller than client, also add an indicator line to show the vertical limit. * Don't die on empty strings in config file, reported by Will Maier. 08 June 2008 * Set socket mode +x if any sessions are attached and -x if not. 07 June 2008 * Make status-interval actually changeable. 06 June 2008 * New window option: aggressive-resize. Normally, windows are resized to the size of the smallest attached session to which they are linked. This means a window only changes size when sessions are detached or attached, or they are linked or unlinked from a session. This flag changes a window to be the size of the smallest attached session for which it is the current window - it is resized every time a session changes to it or away from it. This is nice for things that handle SIGWINCH well (like irssi) and bad for things like shells. * The server now exits when no sessions remain. * Fix bug with inserting characters with TERM=xterm-color. 05 June 2008 * Completely reorganise command parsing. Much more common code in cmd-generic.c and a new way of specifying windows, clients or sessions. Now, most commands take a -t argument, which specifies a client, a session, or a window target. Clients and sessions are given alone (sessions are fnmatch(3)d and clients currently not), windows are give by (client|session):index. For example, if a user is in session "1" window 0 on /dev/ttypi, these should all be equivalent: tmux renamew newname (current session and window) tmux renamew -t: newname (current session and window) tmux renamew -t:0 newname (current session, window 0) tmux renamew -t0 newname (current session, window 0) tmux renamew -t1:0 newname (session 1, window 0) tmux renamew -t1: newname (session 1's current window) tmux renamew -t/dev/ttypi newname (client /dev/ttypi's current session and window) tmux renamew -t/dev/ttypi: newname (client /dev/ttypi's current session and window) tmux renamew -t/dev/ttypi:0 newname (client /dev/ttypi's current session, window 0) This does have some downsides, for example, having to use -t on selectw, tmux selectw -t7 is annoying. But then using non-flagged arguments would mean renaming the current window would need to be something like: tmux renamew : newname It might be better not to try and be so consistent; comments to the usual address ;-). * Infrastructure for printing arguments in list-keys output. Easy ones only for now. 04 June 2008 * Add some vi(1) key bindings in copy mode, and support binding ^[, ^\, ^] ^^ and ^_. Both from/prompted by Will Maier. * setw monitor-activity and set status without arguments now toggle the current value; suggested by merdely. * New command set-window-option (alias setw) to set the single current window option: monitor-activity to determine whether window activity is shown in the status bar for that window (default off). * Change so active/bell windows are inverted in status line. * Activity monitoring - window with activity are marked in status line. No way to disable this/filter windows yet. * Brought select-window command into line with everything else; it now uses -i for the window index. * Strings to display on the left and right of the status bar may now be set with the status-left and status-right options. These are passed through strftime(3) before being displayed. The status bar is automatically updated at an interval set by the status-interval option. The default is to display nothing on the left and the date and time on the left; the default update interval is 15 seconds. 03 June 2008 * Per session options. Setting options without specifying a session sets the global options as normal (global options are inherited by all sessions); passing -c or -s will set the option only for that session. * Because a client has a session attached, any command needing a session can take a client and use its session. So, anything that used to accept -s now accepts -c as well. * -s to specify session name now supports fnmatch(3) wildcards; if multiple sessions are found, or if no -s is specified, the most newly created is used. * If no command is specified, assume new-session. As a byproduct, clean up command default values into separate init functions. * kill-server command. 02 June 2008 * New command, start-server (alias "start"), to start the tmux server and do nothing else. This is good if you have a configuration file which creates windows or sessions (like me): in that case, starting the server the first time tmux new is run is bad since it creates a new session and window (as it is supposed to - starting the server is a side-effect). Instead, I have a little script which does the equivalent of: tmux has -s0 2>/dev/null || tmux start tmux attach -d -s0 And I use it to start the server if necessary and attach to my primary session. * Basic configuration file in ~/.tmux.conf or specified with -f. This is file contains a set of tmux commands that are run the first time the server is started. The configuration commands are executed before any others, so if you have a configuration file that contains: new -d neww -s0 And you do the following without an existing server running: tmux new You will end up with two sessions, session 0 with two windows (created by the configuration file) and your client attached to session 1 with one window (created by the command-line command). I'm not completely happy with this, it seems a little non-obvious, but I haven't yet decided what to do about it. There is no environment variable handling or other special stuff yet. In the future, it might be nice to be able to have per-session configuration settings, probably by having conditionals in the file (so you could, for example, have commands to define a particular window layout that would only be invoked if you called tmux new -smysession and mysession did not already exist). * BIG CHANGE: -s and -c to specify session name and client name are now passed after the command rather than before it. So, for example: tmux -s0 neww Becomes: tmux neww -s0 This is to allow them to be used in the (forthcoming) configuration file THIS WILL BREAK ANY CURRENT SCRIPTS OR ALIASES USING -s OR -c. 01 June 2008 * Bug fix: don't die if -k passed to link-window and the destination doesn't exist. * New command, send-keys, will send a set of keys to a window. 31 May 2008 * Fix so tmux doesn't hang if the initial window fails for some reason. This was highlighted by problems on Darwin, thanks to Elias Pipping for the report and access to a test account. (tmux still won't work on Darwin since its poll(2) is broken.) 02 January 2008 * Don't attempt to reset the tty on exit if it has been closed externally. 06 December 2007 * Restore checks for required termcap entries and add a few more obvious emulations. * Another major reorganisation, this time of screen handling. A new set of functions, screen_write_*, are now used to write to a screen and a tty simultaneously. These are used by the input parser to update the base window screen and also by the different modes which now interpose their own screen. 30 November 2007 * Support \ek...\e\ to set window name. 27 November 2007 * Enable/disable mouse when asked, if terminal claims to support it. Mouse sequences are just passed through unaltered for the moment. * Big internal reorganisation. Rather than leaving control of the tty solely in the client and piping all data through a socket to it, change so that the server opens the tty again and reads and writes to it directly. This avoids a lot of buffering and copying. Also reorganise the redrawing stuff so that everything goes through screen_draw_* - this makes the code simpler, but still needs broken up more, and all the ways of writing to screens should be more consistent. 26 November 2007 * Rather than shifting up one line at a time once the history is full, shift by 10% of the history each time. This is faster. * Add ^A and ^E to copy mode to move to start-of-line/end-of-line. 24 November 2007 * Support for alt charset mode (VT100 graphics characters). 23 November 2007 * Mostly complete copy & paste. Copy mode entered with C-b [ (copy-mode command). In copy mode, arrow keys/page up/page down/hjkl/C-u/C-f navigate, space or C-space starts selection, and enter or C-w copies and (important!) exits copy mode. C-b ] (paste-buffer) pastes into current window. No extra utility keys (bol/eol/clear selection/etc), only one single buffer, and no buffer manipulation commands (clear/view/etc) yet. The code is also fugly :-(. * history-limit option to set maximum history. Does not apply retroactively to existing windows! Lines take up a variable amount of space, but a reasonable guess for an 80-column terminal is 250 KB per 1000 lines (of history used, an empty history takes no space). 21 November 2007 * Create every line as zero length and only expand it as data is written, rather than creating at full size immediately. * Make command output (eg list-keys) go to a scrollable window similar to scroll mode. * Redo screen redrawing so it is a) readable b) split into utility functions that can be used outside screen.c. Use these to make scroll mode only redraw what it has to which gets rid of irritating flickering status box and makes it much faster. * Full line width memory and horizontal scrolling in history. * Initial support for scroll history. = to enter scrolling mode, and then vi keys or up/down/pgup/pgdown to navigate. Q to exit. No horizontal history yet (need per-line sizes) and a few kinks to be worked out (resizing while in history mode will probably cause trouble). 20 November 2007 * Fix format string error with "must specify a client" message. Also sprinkle some printflike tags. * tmux 0.1 released. 17 November 2007 * (nicm) Add -k option to link-window to kill target window if it exists. 16 November 2007 * (nicm) Split in-client display into two columns. This is a hack but not a lot more so than that bit is already and it helps with lots of keys. * (nicm) switch-client command to switch client between different sessions. This is pretty cool: $ tmux bind q switch 0 $ tmux bind w switch 1 Then you can switch between sessions 0 and 1 with a key :-). * (nicm) Accept "-c client-tty" on command line to allow client manipulation commands, and change detach-/refresh-session to detach-/refresh-client (this loses the -a behaviour, but at some point -session versions may return, and -c will allow fnmatch(3)). * (nicm) List available commands on ambiguous command. 12 November 2007 * (nicm) If the terminal supports default colours (AX present), force black background and white foreground to default. This is useful on transparent *terms for programs which don't do it themselves (like most(1)). * (nicm) Fill in the rest of the man page. * (nicm) kill-session command. 09 November 2007 * (nicm) C-space is now "^ " not "^@". * (nicm) Support tab (\011). * (nicm) Initial man page outline. * (nicm) -V to show version. * (nicm) rename-session command. 08 November 2007 * (nicm) Check for required terminal capabilities on start. 31 October 2007 * (nicm) Linux port. 30 October 2007 * (nicm) swap-window command. Same as link-window but swaps windows. 26 October 2007 * (nicm) Saving scroll region on \e7 causes problems with ncmpc so I guess it is not required. * (nicm) unlink-window command. * (nicm) link-window command to link an existing window into another session (or another index in the same session). Syntax: tmux -s dstname link-window [-i dstidx] srcname srcidx * (nicm) Redo window data structures. The global array remains, but each per- session list is now a RB tree of winlink structures. This disassociates the window index from the array size (allowing arbitrary indexes) which still allowing windows to have multiple indexes. 25 October 2007 * (nicm) has-session command: checks if session exists. 24 October 2007 * (nicm) Support for \e6n to request cursor position. resize(1) now works. * (nicm) Support for \e7, \e8 save/restore cursor and attribute sequences. Currently don't save mode (probably should). Also change some cases where out-of-bound values are ignored to limit them to within range (there are others than need to be checked too). 23 October 2007 * (nicm) Lift limit on session name passed with -s. * (nicm) Show size in session/window lists. * (nicm) Pass tty up to server when client identifies and add a list-clients command to list connected clients. 20 October 2007 * (nicm) Add default-command option and change default to be $SHELL rather than $SHELL -l. Also try to read shell from passwd db if $SHELL isn't present. 19 October 2007 * (nicm) -n on new-session is now -s, and -n is now the initial window name. This was documented but not implemented :-/. * (nicm) kill-window command, bound to & by default (because it should be hard to hit accidently). * (nicm) bell-style option with three choices: "none" completely ignore bell; "any" pass through a bell in any window to current; "current" ignore bells except in current window. This applies only to the bell terminal signal, the status bar always reflects any bells. * (nicm) Refresh session command. 12 October 2007 * (nicm) Add a warning if $TMUX exists on new/attach. * (nicm) send-prefix command. Bound to C-b by default. * (nicm) set status, status-fg, status-bg commands. fg and bg are as a number from 0 to 8 or a string ("red", "blue", etc). status may be 1/0, on/off, yes/no. * (nicm) Make status line mark window in yellow on bell. 04 October 2007 * (nicm) -d option to attach to detach all other clients on the same session. * (nicm) Partial resizing support. Still buggy. A C-b S and back sometimes fixes it when it goes wonky. * (mxey) Added my tmux start script as an example (examples/start-tmux.sh). * (mxey) New sessions can now be given a command for their first window. * (mxey) Fixed usage statement for new-window. * (nicm) attach-session (can't believe I forgot it until now!) and list-windows commands. * (nicm) rename-window and select-window commands. * (nicm) set-option command (alias set): "tmux set-option prefix ^A". * (nicm) Key binding and unbinding is back. 03 October 2007 * (nicm) {new,next,last,previous}-window. * (nicm) Rewrite command handling so commands are much more generic and the same commands are used for command line and keys (although most will probably need to check how they are called). Currently incomplete (only new/detach/ls implemented). Change: -s is now passed before command again! * (nicm) String number arguments. So you can do: tmux bind ^Q create "blah". * (nicm) Key binding. tmux bind key command [argument] and tmux unbind key. Key names are in a table in key-string.c, plus A is A, ^A is ctrl-A. Possible commands are in cmd.c (look at cmd_bind_table). * (nicm) Move command parsing into the client. Also rename some messages and tidy up a few bits. Lots more tidying up needed :-/. 02 October 2007 * (nicm) Redraw client status lines on rename. * (nicm) Error on ambiguous command. 01 October 2007 * (nicm) Restore window title handling. * (nicm) Simple uncustomisable status line with window list. 30 September 2007 * (nicm) Window info command for debugging, C-b I. 29 September 2007 * (nicm) Deleting/inserting lines should follow scrolling region. Fix. * (nicm) Allow creation of detached sessions: "tmux new-session -d". * (nicm) Permit error messages to be passed back for transient clients like rename. Also make rename -i work. * (nicm) Pass through bell in any window to current. 28 September 2007 * (nicm) Major rewrite of input parser: - Lose the old weirdness in favour of a state machine. - Merge in parsing from screen.c. - Split key parsing off into a separate file. This is step one towards hopefully allowing a status line. It requires that we output data as if the terminal had one line less than it really does - a serious problem when it comes to things like scrolling. This change consolidates all the range checking and limiting together which should make it easier. * (mxey) Added window renaming, like "tmux rename [-s session] [-i index] name" 27 September 2007 * Split "tmux list" into "tmux list-sessions" (ls) and "list-windows" (lsw). * New command session selection: - if name is specified, look for it and use it if it exists, otherwise error - if no name specified, try the current session from $TMUX - if $TMUX doesn't exist, and there is only one session, use it, otherwise error 26 September 2007 * Add command aliases, so "ls" is an alias for "list". * Rename some commands and alter syntax to take options after a la CVS. Also change some flags. So: tmux -s/socket -nabc new Becomes: tmux -S/socket new -sabc * Major tidy and split of client/server code. 22 September 2007 * Window list command (C-b W). Started by Maximilian Gass, finished by me. 20 September 2007 * Specify meta via environment variable (META). * Record last window and ^L key to switch to it. Largely from Maximilian Gass. * Reset ignored signals in child after forkpty, makes ^C work. * Wrap on next/previous. From Maximilian Gass. 19 September 2007 * Don't renumber windows on close. 28 August 2007 * Scrolling region (\e[r) support. 27 August 2007 * Change screen.c to work more logically and hopefully fix heap corruption. 09 July 2007 * Initial import to CVS. Basic functions are working, albeit with a couple of showstopper memory bugs and many missing features. Detaching, reattaching, creating new sessions, listing sessions work acceptably for using with shells. Simple curses programs (top, systat, tetris) and more complicated ones (mutt, emacs) that don't require scrolling regions (ESC[r) mostly work fine (including mutt, emacs). No status bar yet and no key remapping or other customisation. tmux-3.0a/README.ja100644 001750 001750 00000005331 13466230030 0007464tmuxへようこそ! tmuxはターミナルマルチプレクサーです。複数のターミナルを一つのスクリーン内に作成し、操作することができます。 バックグラウンドで処理を実行中に一度スクリーンから離れて後から復帰することも可能です。 OpenBSD、FreeBSD、NetBSD、Linux、OS X、Solarisで実行できます。 tmuxはlibevent 2.x.に依存します。 下記からダウンロードしてください。 http://libevent.org また、ncursesも必要です。こちらからどうぞ。 http://invisible-island.net/ncurses/ tarballでのtmuxのビルドとインストール方法。 $ ./configure && make $ sudo make install tmuxはutmp(5)をアップデートするためにutempterを使うことができます。もしインストール済みであればオプション「--enable-utempter」をつけて実行してください。 リポジトリから最新バージョンを手に入れるためには下記を実行。 $ git clone https://github.com/tmux/tmux.git $ cd tmux $ sh autogen.sh $ ./configure && make (ビルドのためにはlibevent、ncurses libraries、headersに加えて、C compiler、make、autoconf、automake、pkg-configが必要です。) 詳しい情報はhttp://git-scm.comをご覧ください。修正はメール宛、もしくはhttps://github.com/tmux/tmux/issuesにて受け付けています。 tmuxのドキュメントについてはtmux.1マニュアルをご覧ください。こちらのコマンドで参照可能です。 $ nroff -mdoc tmux.1|less サンプル設定は本リポジトリのexample_tmux.confに また、bash-completionファイルは下記にあります。 https://github.com/imomaliev/tmux-bash-completion 「-v」や「-vv」を指定することでデバッグモードでの起動が可能です。カレントディレクトリにサーバーやクライアントのログファイルが生成されます。 議論やバグレポート用のメーリングリストにはこちらから参加可能です。 https://groups.google.com/forum/#!forum/tmux-users gitコミットについての連絡先 https://groups.google.com/forum/#!forum/tmux-git 購読はまでメールをお願いします。 バグレポートや機能追加(特にコードへの貢献)は大歓迎です。こちらにご連絡ください。 tmux-users@googlegroups.com 本ファイル、CHANGES、 FAQ、SYNCINGそしてTODOはISC licenseで保護されています。 その他のファイルのライセンスや著作権については、ファイルの上部に明記されています。 -- Nicholas Marriott tmux-3.0a/example_tmux.conf100644 001750 001750 00000003434 13504653146 0011606# # Example .tmux.conf # # By Nicholas Marriott. Public domain. # # Some tweaks to the status line set -g status-right "%H:%M" set -g window-status-current-style "underscore" # If running inside tmux ($TMUX is set), then change the status line to red %if #{TMUX} set -g status-bg red %endif # Enable RGB colour if running in xterm(1) set-option -sa terminal-overrides ",xterm*:Tc" # Change the default $TERM to tmux-256color set -g default-terminal "tmux-256color" # No bells at all set -g bell-action none # Keep windows around after they exit set -g remain-on-exit on # Change the prefix key to C-a set -g prefix C-a unbind C-b bind C-a send-prefix # Turn the mouse on, but without copy mode dragging set -g mouse on unbind -n MouseDrag1Pane unbind -Tcopy-mode MouseDrag1Pane # Some extra key bindings to select higher numbered windows 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 # A key to toggle between smallest and largest sizes if a window is visible in # multiple places bind F set -w window-size # Keys to toggle monitoring activity in a window and the synchronize-panes option bind m set monitor-activity bind y set synchronize-panes\; display 'synchronize-panes #{?synchronize-panes,on,off}' # Create a single default session - because a session is created here, tmux # should be started with "tmux attach" rather than "tmux new" new -d -s0 -nirssi 'exec irssi' set -t0:0 monitor-activity on set -t0:0 aggressive-resize on neww -d -ntodo 'exec emacs ~/TODO' setw -t0:1 aggressive-resize on neww -d -nmutt 'exec mutt' setw -t0:2 aggressive-resize on neww -d neww -d neww -d tmux-3.0a/osdep-aix.c100644 001750 001750 00000004341 13466230031 0010251/* $OpenBSD$ */ /* * 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 #include #include #include "tmux.h" char * osdep_get_name(__unused int fd, char *tty) { struct psinfo p; char *path; ssize_t bytes; int f, ttyfd, retval; pid_t pgrp; if ((ttyfd = open(tty, O_RDONLY|O_NOCTTY)) == -1) return (NULL); retval = ioctl(ttyfd, TIOCGPGRP, &pgrp); close(ttyfd); if (retval == -1) return (NULL); xasprintf(&path, "/proc/%u/psinfo", (u_int) pgrp); f = open(path, O_RDONLY); free(path); if (f < 0) return (NULL); bytes = read(f, &p, sizeof(p)); close(f); if (bytes != sizeof(p)) return (NULL); return (xstrdup(p.pr_fname)); } char * osdep_get_cwd(int fd) { static char target[MAXPATHLEN + 1]; char *path; const char *ttypath; ssize_t n; pid_t pgrp; int len, retval, ttyfd; if ((ttypath = ptsname(fd)) == NULL) return (NULL); if ((ttyfd = open(ttypath, O_RDONLY|O_NOCTTY)) == -1) return (NULL); retval = ioctl(ttyfd, TIOCGPGRP, &pgrp); close(ttyfd); if (retval == -1) return (NULL); xasprintf(&path, "/proc/%u/cwd", (u_int) pgrp); n = readlink(path, target, MAXPATHLEN); free(path); if (n > 0) { target[n] = '\0'; if ((len = strlen(target)) > 1 && target[len - 1] == '/') target[len - 1] = '\0'; return (target); } return (NULL); } struct event_base * osdep_event_init(void) { return (event_init()); } tmux-3.0a/osdep-cygwin.c100644 001750 001750 00000003601 13466230031 0010766/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #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) { free(path); return (NULL); } free(path); len = 0; buf = NULL; while ((ch = fgetc(f)) != EOF) { if (ch == '\0') break; buf = xrealloc(buf, len + 2); buf[len++] = ch; } if (buf != NULL) buf[len] = '\0'; fclose(f); return (buf); } char * osdep_get_cwd(int fd) { static char target[MAXPATHLEN + 1]; char *path; pid_t pgrp; ssize_t n; if ((pgrp = tcgetpgrp(fd)) == -1) return (NULL); xasprintf(&path, "/proc/%lld/cwd", (long long) pgrp); n = readlink(path, target, MAXPATHLEN); free(path); if (n > 0) { target[n] = '\0'; return (target); } return (NULL); } struct event_base * osdep_event_init(void) { return (event_init()); } tmux-3.0a/osdep-darwin.c100644 001750 001750 00000005145 13504653146 0010770/* $OpenBSD$ */ /* * Copyright (c) 2009 Joshua Elsasser * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include char *osdep_get_name(int, char *); char *osdep_get_cwd(int); struct event_base *osdep_event_init(void); #ifndef __unused #define __unused __attribute__ ((__unused__)) #endif char * osdep_get_name(int fd, __unused char *tty) { #ifdef __MAC_10_7 struct proc_bsdshortinfo bsdinfo; pid_t pgrp; int ret; if ((pgrp = tcgetpgrp(fd)) == -1) return (NULL); ret = proc_pidinfo(pgrp, PROC_PIDT_SHORTBSDINFO, 0, &bsdinfo, sizeof bsdinfo); if (ret == sizeof bsdinfo && *bsdinfo.pbsi_comm != '\0') return (strdup(bsdinfo.pbsi_comm)); return (NULL); #else 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)); #endif } char * osdep_get_cwd(int fd) { static char wd[PATH_MAX]; struct proc_vnodepathinfo pathinfo; pid_t pgrp; int ret; if ((pgrp = tcgetpgrp(fd)) == -1) return (NULL); ret = proc_pidinfo(pgrp, PROC_PIDVNODEPATHINFO, 0, &pathinfo, sizeof pathinfo); if (ret == sizeof pathinfo) { strlcpy(wd, pathinfo.pvi_cdir.vip_path, sizeof wd); return (wd); } return (NULL); } struct event_base * osdep_event_init(void) { struct event_base *base; /* * 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); base = event_init(); unsetenv("EVENT_NOKQUEUE"); unsetenv("EVENT_NOPOLL"); return (base); } tmux-3.0a/osdep-dragonfly.c100644 001750 001750 00000005723 13466230031 0011462/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include struct kinfo_proc *cmp_procs(struct kinfo_proc *, struct kinfo_proc *); char *osdep_get_name(int, char *); char *osdep_get_cwd(int); struct event_base *osdep_event_init(void); #ifndef nitems #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) #endif #define is_runnable(p) \ ((p)->kp_stat == SACTIVE || (p)->kp_stat == SIDL) #define is_stopped(p) \ ((p)->kp_stat == SSTOP || (p)->kp_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 (strcmp(p1->kp_comm, p2->kp_comm) < 0) return (p1); if (strcmp(p1->kp_comm, p2->kp_comm) > 0) return (p2); if (p1->kp_pid > p2->kp_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].kp_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->kp_comm); free(buf); return (name); error: free(buf); return (NULL); } char * osdep_get_cwd(int fd) { return (NULL); } struct event_base * osdep_event_init(void) { return (event_init()); } tmux-3.0a/osdep-freebsd.c100644 001750 001750 00000010734 13466241542 0011116/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "compat.h" struct kinfo_proc *cmp_procs(struct kinfo_proc *, struct kinfo_proc *); char *osdep_get_name(int, char *); char *osdep_get_cwd(int); 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); } static char * osdep_get_cwd_fallback(int fd) { static char wd[PATH_MAX]; struct kinfo_file *info = NULL; pid_t pgrp; int nrecords, i; if ((pgrp = tcgetpgrp(fd)) == -1) return (NULL); if ((info = kinfo_getfile(pgrp, &nrecords)) == NULL) return (NULL); for (i = 0; i < nrecords; i++) { if (info[i].kf_fd == KF_FD_TYPE_CWD) { strlcpy(wd, info[i].kf_path, sizeof wd); free(info); return (wd); } } free(info); return (NULL); } #ifdef KERN_PROC_CWD char * osdep_get_cwd(int fd) { static struct kinfo_file info; static int fallback; int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_CWD, 0 }; size_t len = sizeof info; if (fallback) return (osdep_get_cwd_fallback(fd)); if ((name[3] = tcgetpgrp(fd)) == -1) return (NULL); if (sysctl(name, 4, &info, &len, NULL, 0) == -1) { if (errno == ENOENT) { fallback = 1; return (osdep_get_cwd_fallback(fd)); } return (NULL); } return (info.kf_path); } #else /* !KERN_PROC_CWD */ char * osdep_get_cwd(int fd) { return (osdep_get_cwd_fallback(fd)); } #endif /* KERN_PROC_CWD */ struct event_base * osdep_event_init(void) { struct event_base *base; /* * 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); base = event_init(); unsetenv("EVENT_NOKQUEUE"); return (base); } tmux-3.0a/osdep-hpux.c100644 001750 001750 00000002117 13466230031 0010453/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" char * osdep_get_name(__unused int fd, __unused char *tty) { return (NULL); } char * osdep_get_cwd(__unused int fd) { return (NULL); } struct event_base * osdep_event_init(void) { return (event_init()); } tmux-3.0a/osdep-linux.c100644 001750 001750 00000004354 13466230031 0010633/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #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) { free(path); return (NULL); } free(path); len = 0; buf = NULL; while ((ch = fgetc(f)) != EOF) { if (ch == '\0') break; buf = xrealloc(buf, len + 2); buf[len++] = ch; } if (buf != NULL) buf[len] = '\0'; fclose(f); return (buf); } char * osdep_get_cwd(int fd) { static char target[MAXPATHLEN + 1]; char *path; pid_t pgrp, sid; ssize_t n; if ((pgrp = tcgetpgrp(fd)) == -1) return (NULL); xasprintf(&path, "/proc/%lld/cwd", (long long) pgrp); n = readlink(path, target, MAXPATHLEN); free(path); if (n == -1 && ioctl(fd, TIOCGSID, &sid) != -1) { xasprintf(&path, "/proc/%lld/cwd", (long long) sid); n = readlink(path, target, MAXPATHLEN); free(path); } if (n > 0) { target[n] = '\0'; return (target); } return (NULL); } struct event_base * osdep_event_init(void) { struct event_base *base; /* On Linux, epoll doesn't work on /dev/null (yes, really). */ setenv("EVENT_NOEPOLL", "1", 1); base = event_init(); unsetenv("EVENT_NOEPOLL"); return (base); } tmux-3.0a/osdep-netbsd.c100644 001750 001750 00000007210 13517540563 0010760/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include "tmux.h" #define is_runnable(p) \ ((p)->p_stat == LSRUN || (p)->p_stat == SIDL) #define is_stopped(p) \ ((p)->p_stat == SSTOP || (p)->p_stat == SZOMB) struct kinfo_proc2 *cmp_procs(struct kinfo_proc2 *, struct kinfo_proc2 *); char *osdep_get_name(int, char *); char *osdep_get_cwd(int); 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; retry: mib[5] = 0; if (sysctl(mib, __arraycount(mib), NULL, &len, NULL, 0) == -1) return (NULL); if ((newbuf = realloc(buf, len)) == 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; 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); } char * osdep_get_cwd(int fd) { static char target[PATH_MAX + 1]; pid_t pgrp; #ifdef KERN_PROC_CWD int mib[4]; size_t len; #else char *path; ssize_t n; #endif if ((pgrp = tcgetpgrp(fd)) == -1) return (NULL); #ifdef KERN_PROC_CWD mib[0] = CTL_KERN; mib[1] = KERN_PROC_ARGS; mib[2] = pgrp; mib[3] = KERN_PROC_CWD; len = sizeof(target); if (sysctl(mib, __arraycount(mib), target, &len, NULL, 0) == 0) return (target); #else xasprintf(&path, "/proc/%lld/cwd", (long long) pgrp); n = readlink(path, target, sizeof(target) - 1); free(path); if (n > 0) { target[n] = '\0'; return (target); } #endif return (NULL); } struct event_base * osdep_event_init(void) { return (event_init()); } tmux-3.0a/osdep-openbsd.c100644 001750 001750 00000007221 13466230031 0011122/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include /* MAXCOMLEN */ #include #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 == SDEAD) static struct kinfo_proc *cmp_procs(struct kinfo_proc *, struct kinfo_proc *); char *osdep_get_name(int, char *); char *osdep_get_cwd(int); struct event_base *osdep_event_init(void); static 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) goto error; 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); } char * osdep_get_cwd(int fd) { int name[] = { CTL_KERN, KERN_PROC_CWD, 0 }; static char path[MAXPATHLEN]; size_t pathlen = sizeof path; if ((name[2] = tcgetpgrp(fd)) == -1) return (NULL); if (sysctl(name, 3, path, &pathlen, NULL, 0) != 0) return (NULL); return (path); } struct event_base * osdep_event_init(void) { return (event_init()); } tmux-3.0a/osdep-sunos.c100644 001750 001750 00000004266 13466230031 0010645/* $OpenBSD$ */ /* * 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(__unused 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/%u/psinfo", (u_int) pgrp); f = open(path, O_RDONLY); free(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)); } char * osdep_get_cwd(int fd) { static char target[MAXPATHLEN + 1]; char *path; const char *ttypath; ssize_t n; pid_t pgrp; int retval, ttyfd; if ((ttypath = ptsname(fd)) == NULL) return (NULL); if ((ttyfd = open(ttypath, O_RDONLY|O_NOCTTY)) == -1) return (NULL); retval = ioctl(ttyfd, TIOCGPGRP, &pgrp); close(ttyfd); if (retval == -1) return (NULL); xasprintf(&path, "/proc/%u/path/cwd", (u_int) pgrp); n = readlink(path, target, MAXPATHLEN); free(path); if (n > 0) { target[n] = '\0'; return (target); } return (NULL); } struct event_base * osdep_event_init(void) { return (event_init()); } tmux-3.0a/osdep-unknown.c100644 001750 001750 00000002106 13466230031 0011164/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" char * osdep_get_name(__unused int fd, __unused char *tty) { return (NULL); } char * osdep_get_cwd(int fd) { return (NULL); } struct event_base * osdep_event_init(void) { return (event_init()); } tmux-3.0a/mdoc2man.awk100644 001750 001750 00000020640 13466230031 0010420#!/usr/bin/awk # # $Id: mdoc2man.awk,v 1.9 2009/10/24 00:52:42 dtucker Exp $ # # Version history: # v4+ Adapted for OpenSSH Portable (see cvs Id and history) # v3, I put the program under a proper license # Dan Nelson added .An, .Aq and fixed a typo # v2, fixed to work on GNU awk --posix and MacOS X # v1, first attempt, didn't work on MacOS X # # Copyright (c) 2003 Peter Stuge # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. 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. BEGIN { optlist=0 oldoptlist=0 nospace=0 synopsis=0 reference=0 block=0 ext=0 extopt=0 literal=0 prenl=0 breakw=0 line="" } function wtail() { retval="" while(w0;i--) { add(refauthors[i]) if(i>1) add(", ") } if(nrefauthors>1) add(" and ") if(nrefauthors>0) add(refauthors[0] ", ") add("\\fI" reftitle "\\fP") if(length(refissue)) add(", " refissue) if(length(refreport)) { add(", " refreport) } if(length(refdate)) add(", " refdate) if(length(refopt)) add(", " refopt) add(".") reference=0 } else if(reference) { if(match(words[w],"^%A$")) { refauthors[nrefauthors++]=wtail() } if(match(words[w],"^%T$")) { reftitle=wtail() sub("^\"","",reftitle) sub("\"$","",reftitle) } if(match(words[w],"^%N$")) { refissue=wtail() } if(match(words[w],"^%D$")) { refdate=wtail() } if(match(words[w],"^%O$")) { refopt=wtail() } if(match(words[w],"^%R$")) { refreport=wtail() } } else if(match(words[w],"^Nm$")) { if(synopsis) { add(".br") prenl++ } n=words[++w] if(!length(name)) name=n if(!length(n)) n=name add("\\fB" n "\\fP") if(!nospace&&match(words[w+1],"^[\\.,]")) nospace=1 } else if(match(words[w],"^Nd$")) { add("\\- " wtail()) } else if(match(words[w],"^Fl$")) { add("\\fB\\-" words[++w] "\\fP") if(!nospace&&match(words[w+1],"^[\\.,]")) nospace=1 } else if(match(words[w],"^Ar$")) { add("\\fI") if(w==nwords) add("file ...\\fP") else { add(words[++w] "\\fP") while(match(words[w+1],"^\\|$")) add(OFS words[++w] " \\fI" words[++w] "\\fP") } if(!nospace&&match(words[w+1],"^[\\.,]")) nospace=1 } else if(match(words[w],"^Cm$")) { add("\\fB" words[++w] "\\fP") while(w") if(option) add("]") if(ext&&!extopt&&!match(line," $")) add(OFS) if(!ext&&!extopt&&length(line)) { print line prenl=0 line="" } } tmux-3.0a/tmux.1100644 001750 001750 00000403643 13570677043 0007321.\" $OpenBSD$ .\" .\" Copyright (c) 2007 Nicholas Marriott .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER .\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING .\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .Dd $Mdocdate$ .Dt TMUX 1 .Os .Sh NAME .Nm tmux .Nd terminal multiplexer .Sh SYNOPSIS .Nm tmux .Bk -words .Op Fl 2CluvV .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 C Start in control mode (see the .Sx CONTROL MODE section). Given twice .Xo ( Fl CC ) Xc disables echo. .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 @SYSCONFDIR@/tmux.conf , if present, then looks for a user configuration file at .Pa ~/.tmux.conf . .Pp The configuration file is a set of .Nm commands which are executed in sequence when the server is first started. .Nm loads configuration files once when the server process has started. The .Ic source-file command may be used to load a file later. .Pp .Nm shows any error messages from commands in configuration files in the first session created, and continues to process the rest of the configuration file. .It Fl L Ar socket-name .Nm stores the server socket in a directory under .Ev TMUX_TMPDIR or .Pa /tmp if it is unset. 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 (note that this will fail if any parent directories are missing). .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 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 Write UTF-8 output to the terminal even if the first environment variable of .Ev LC_ALL , .Ev LC_CTYPE , or .Ev LANG that is set does not contain .Qq UTF-8 or .Qq UTF8 . .It Fl v Request verbose logging. 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. If .Fl v is specified twice, an additional .Pa tmux-out-PID.log file is generated with a copy of everything .Nm writes to the terminal. .Pp The .Dv SIGUSR2 signal may be sent to the .Nm server process to toggle logging between on (as if .Fl v was given) and off. .It Fl V Report the .Nm version. .Pp .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 DEFAULT 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 \&( Switch the attached client to the previous session. .It \&) Switch the attached client to the next session. .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 L Switch the attached client back to the last session. .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 m Mark the current pane (see .Ic select-pane .Fl m ) . .It M Clear the marked pane. .It s Select a new session for the attached client interactively. .It t Show the time. .It w Choose the current window interactively. .It x Kill the current pane. .It z Toggle zoom state of 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 Space Arrange the current window in the next preset layout. .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 COMMAND PARSING AND EXECUTION .Nm supports a large number of commands which can be used to control its behaviour. Each command is named and can accept zero or more flags and arguments. They may be bound to a key with the .Ic bind-key command or run from the shell prompt, a shell script, a configuration file or the command prompt. For example, the same .Ic set-option command run from the shell prompt, from .Pa ~/.tmux.conf and bound to a key may look like: .Bd -literal -offset indent $ tmux set-option -g status-style bg=cyan set-option -g status-style bg=cyan bind-key C set-option -g status-style bg=cyan .Ed .Pp Here, the command name is .Ql set-option , .Ql Fl g is a flag and .Ql status-style and .Ql bg=cyan are arguments. .Pp .Nm distinguishes between command parsing and execution. In order to execute a command, .Nm needs it to be split up into its name and arguments. This is command parsing. If a command is run from the shell, the shell parses it; from inside .Nm or from a configuration file, .Nm does. Examples of when .Nm parses commands are: .Bl -dash -offset indent .It in a configuration file; .It typed at the command prompt (see .Ic command-prompt ) ; .It given to .Ic bind-key ; .It passed as arguments to .Ic if-shell or .Ic confirm-before . .El .Pp To execute commands, each client has a .Ql command queue . A global command queue not attached to any client is used on startup for configuration files like .Pa ~/.tmux.conf . Parsed commands added to the queue are executed in order. Some commands, like .Ic if-shell and .Ic confirm-before , parse their argument to create a new command which is inserted immediately after themselves. This means that arguments can be parsed twice or more - once when the parent command (such as .Ic if-shell ) is parsed and again when it parses and executes its command. Commands like .Ic if-shell , .Ic run-shell and .Ic display-panes stop execution of subsequent commands on the queue until something happens - .Ic if-shell and .Ic run-shell until a shell command finishes and .Ic display-panes until a key is pressed. For example, the following commands: .Bd -literal -offset indent new-session; new-window if-shell "true" "split-window" kill-session .Ed .Pp Will execute .Ic new-session , .Ic new-window , .Ic if-shell , the shell command .Xr true 1 , .Ic split-window and .Ic kill-session in that order. .Pp The .Sx COMMANDS section lists the .Nm commands and their arguments. .Sh PARSING SYNTAX This section describes the syntax of commands parsed by .Nm , for example in a configuration file or at the command prompt. Note that when commands are entered into the shell, they are parsed by the shell - see for example .Xr ksh 1 or .Xr csh 1 . .Pp Each command is terminated by a newline or a semicolon (;). Commands separated by semicolons together form a .Ql command sequence - if a command in the sequence encounters an error, no subsequent commands are executed. .Pp Comments are marked by the unquoted # character - any remaining text after a comment is ignored until the end of the line. .Pp If the last character of a line is \e, the line is joined with the following line (the \e and the newline are completely removed). This is called line continuation and applies both inside and outside quoted strings and in comments, but not inside braces. .Pp Command arguments may be specified as strings surrounded by single (') quotes, double quotes (") or braces ({}). .\" " This is required when the argument contains any special character. Single and double quoted strings cannot span multiple lines except with line continuation. Braces can span multiple lines. .Pp Outside of quotes and inside double quotes, these replacements are performed: .Bl -dash -offset indent .It Environment variables preceded by $ are replaced with their value from the global environment (see the .Sx GLOBAL AND SESSION ENVIRONMENT section). .It A leading ~ or ~user is expanded to the home directory of the current or specified user. .It \euXXXX or \euXXXXXXXX is replaced by the Unicode codepoint corresponding to the given four or eight digit hexadecimal number. .It When preceded (escaped) by a \e, the following characters are replaced: \ee by the escape character; \er by a carriage return; \en by a newline; and \et by a tab. .It \eooo is replaced by a character of the octal value ooo. Three octal digits are required, for example \e001. The largest valid character is \e377. .It Any other characters preceded by \e are replaced by themselves (that is, the \e is removed) and are not treated as having any special meaning - so for example \e; will not mark a command sequence and \e$ will not expand an environment variable. .El .Pp Braces are similar to single quotes in that the text inside is taken literally without any replacements but this also includes line continuation. Braces can span multiple lines in which case a literal newline is included in the string. They are designed to avoid the need for additional escaping when passing a group of .Nm or shell commands as an argument (for example to .Ic if-shell or .Ic pipe-pane ) . These two examples produce an identical command - note that no escaping is needed when using {}: .Bd -literal -offset indent if-shell true { display -p 'brace-dollar-foo: }$foo' } if-shell true "\en display -p 'brace-dollar-foo: }\e$foo'\en" .Ed .Pp Braces may be enclosed inside braces, for example: .Bd -literal -offset indent bind x if-shell "true" { if-shell "true" { display "true!" } } .Ed .Pp Environment variables may be set by using the syntax .Ql name=value , for example .Ql HOME=/home/user . Variables set during parsing are added to the global environment. .Pp Commands may be parsed conditionally by surrounding them with .Ql %if , .Ql %elif , .Ql %else and .Ql %endif . The argument to .Ql %if and .Ql %elif is expanded as a format (see .Sx FORMATS ) and if it evaluates to false (zero or empty), subsequent text is ignored until the closing .Ql %elif , .Ql %else or .Ql %endif . For example: .Bd -literal -offset indent %if "#{==:#{host},myhost}" set -g status-style bg=red %elif "#{==:#{host},myotherhost}" set -g status-style bg=green %else set -g status-style bg=blue %endif .Ed .Pp Will change the status line to red if running on .Ql myhost , green if running on .Ql myotherhost , or blue if running on another host. Conditionals may be given on one line, for example: .Bd -literal -offset indent %if #{==:#{host},myhost} set -g status-style bg=red %endif .Ed .Sh COMMANDS This section describes the commands supported by .Nm . Most commands accept the optional .Fl t (and sometimes .Fl s ) 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. .Pp .Ar target-client should be the name of the client, typically 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, .Nm attempts to work out the client currently in use; if that fails, an error is reported. Clients may be listed with the .Ic list-clients command. .Pp .Ar target-session is tried as, in order: .Bl -enum -offset Ds .It A session ID prefixed with a $. .It An exact name of a session (as listed by the .Ic list-sessions command). .It The start of a session name, for example .Ql mysess would match a session named .Ql mysession . .It An .Xr fnmatch 3 pattern which is matched against the session name. .El .Pp If the session name is prefixed with an .Ql = , only an exact match is accepted (so .Ql =mysess will only match exactly .Ql mysess , not .Ql mysession ) . .Pp If a single session 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 (or .Ar src-window or .Ar dst-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: .Bl -enum -offset Ds .It A special token, listed below. .It A window index, for example .Ql mysession:1 is window 1 in session .Ql mysession . .It A window ID, such as @1. .It An exact window name, such as .Ql mysession:mywindow . .It The start of a window name, such as .Ql mysession:mywin . .It As an .Xr fnmatch 3 pattern matched against the window name. .El .Pp Like sessions, a .Ql = prefix will do an exact match only. 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. .Pp The following special tokens are available to indicate particular windows. Each has a single-character alternative form. .Bl -column "XXXXXXXXXX" "X" .It Sy "Token" Ta Sy "" Ta Sy "Meaning" .It Li "{start}" Ta "^" Ta "The lowest-numbered window" .It Li "{end}" Ta "$" Ta "The highest-numbered window" .It Li "{last}" Ta "!" Ta "The last (previously current) window" .It Li "{next}" Ta "+" Ta "The next window by number" .It Li "{previous}" Ta "-" Ta "The previous window by number" .El .Pp .Ar target-pane (or .Ar src-pane or .Ar dst-pane ) may be a pane ID or takes a similar form to .Ar target-window but with the optional addition of a period followed by a pane index or pane ID, for example: .Ql mysession:mywindow.1 . If the pane index is omitted, the currently active pane in the specified window is used. The following special tokens are available for the pane index: .Bl -column "XXXXXXXXXXXXXX" "X" .It Sy "Token" Ta Sy "" Ta Sy "Meaning" .It Li "{last}" Ta "!" Ta "The last (previously active) pane" .It Li "{next}" Ta "+" Ta "The next pane by number" .It Li "{previous}" Ta "-" Ta "The previous pane by number" .It Li "{top}" Ta "" Ta "The top pane" .It Li "{bottom}" Ta "" Ta "The bottom pane" .It Li "{left}" Ta "" Ta "The leftmost pane" .It Li "{right}" Ta "" Ta "The rightmost pane" .It Li "{top-left}" Ta "" Ta "The top-left pane" .It Li "{top-right}" Ta "" Ta "The top-right pane" .It Li "{bottom-left}" Ta "" Ta "The bottom-left pane" .It Li "{bottom-right}" Ta "" Ta "The bottom-right pane" .It Li "{up-of}" Ta "" Ta "The pane above the active pane" .It Li "{down-of}" Ta "" Ta "The pane below the active pane" .It Li "{left-of}" Ta "" Ta "The pane to the left of the active pane" .It Li "{right-of}" Ta "" Ta "The pane to the right of the active pane" .El .Pp The tokens .Ql + and .Ql - may be followed by an offset, for example: .Bd -literal -offset indent select-window -t:+2 .Ed .Pp In addition, .Em target-session , .Em target-window or .Em target-pane may consist entirely of the token .Ql {mouse} (alternative form .Ql = ) to specify the session, window or pane where the most recent mouse event occurred (see the .Sx MOUSE SUPPORT section) or .Ql {marked} (alternative form .Ql ~ ) to specify the marked pane (see .Ic select-pane .Fl m ) . .Pp Sessions, window and panes are each numbered with a unique ID; session IDs are prefixed with a .Ql $ , windows with a .Ql @ , and panes with a .Ql % . These are unique and are unchanged for the life of the session, window or pane in the .Nm server. The pane ID is passed to the child process of the pane in the .Ev TMUX_PANE environment variable. IDs may be displayed using the .Ql session_id , .Ql window_id , or .Ql pane_id formats (see the .Sx FORMATS section) and the .Ic display-message , .Ic list-sessions , .Ic list-windows or .Ic list-panes commands. .Pp .Ar shell-command arguments are .Xr sh 1 commands. This may be a single argument passed to the shell, for example: .Bd -literal -offset indent new-window 'vi /etc/passwd' .Ed .Pp Will run: .Bd -literal -offset indent /bin/sh -c 'vi /etc/passwd' .Ed .Pp Additionally, the .Ic new-window , .Ic new-session , .Ic split-window , .Ic respawn-window and .Ic respawn-pane commands allow .Ar shell-command to be given as multiple arguments and executed directly (without .Ql sh -c ) . This can avoid issues with shell quoting. For example: .Bd -literal -offset indent $ tmux new-window vi /etc/passwd .Ed .Pp Will run .Xr vi 1 directly without invoking the shell. .Pp .Ar command .Op Ar arguments refers to a .Nm command, either passed with the command and arguments separately, for example: .Bd -literal -offset indent bind-key F1 set-option status off .Ed .Pp Or passed as a single string argument in .Pa .tmux.conf , for example: .Bd -literal -offset indent bind-key F1 { set-option status off } .Ed .Pp Example .Nm commands include: .Bd -literal -offset indent refresh-client -t/dev/ttyp2 rename-session -tfirst newname set-option -wt:0 monitor-activity on new-window ; split-window -d bind-key R source-file ~/.tmux.conf \e; \e display-message "source-file done" .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 dErx .Op Fl c Ar working-directory .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. If .Fl x is given, send SIGHUP to the parent process of the client as well as detaching the client, typically causing it to exit. .Fl r signifies the client is read-only (only keys bound to the .Ic detach-client or .Ic switch-client commands 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. .Pp .Fl c will set the session working directory (used for new windows) to .Ar working-directory . .Pp If .Fl E is used, the .Ic update-environment option will not be applied. .It Xo Ic detach-client .Op Fl aP .Op Fl E Ar shell-command .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 the session specified by .Fl s . The .Fl a option kills all but the client given with .Fl t . If .Fl P is given, send SIGHUP to the parent process of the client, typically causing it to exit. With .Fl E , run .Ar shell-command to replace the client. .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 Xo Ic kill-session .Op Fl aC .Op Fl t Ar target-session .Xc Destroy the given session, closing any windows linked to it and no other sessions, and detaching all clients attached to it. If .Fl a is given, all sessions but the specified one is killed. The .Fl C flag clears alerts (bell, activity, or silence) in all windows linked to the session. .It Xo Ic list-clients .Op Fl F Ar format .Op Fl t Ar target-session .Xc .D1 (alias: Ic lsc ) List all clients attached to the server. For the meaning of the .Fl F flag, see the .Sx FORMATS section. If .Ar target-session is specified, list only clients connected to that session. .It Xo Ic list-commands .Op Fl F Ar format .Xc .D1 (alias: Ic lscm ) List the syntax of all commands supported by .Nm . .It Ic list-sessions Op Fl F Ar format .D1 (alias: Ic ls ) List all sessions managed by the server. For the meaning of the .Fl F flag, see the .Sx FORMATS section. .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 AdDEPX .Op Fl c Ar start-directory .Op Fl F Ar format .Op Fl n Ar window-name .Op Fl s Ar session-name .Op Fl t Ar group-name .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. With .Fl d , the initial size comes from the global .Ic default-size option; .Fl x and .Fl y can be used to specify a different size. .Ql - uses the size of the current client if any. If .Fl x or .Fl y is given, the .Ic default-size option is set for the session. .Pp If run from a terminal, any .Xr termios 4 special characters are saved and used for new windows in the new session. .Pp The .Fl A flag makes .Ic new-session behave like .Ic attach-session if .Ar session-name already exists; in this case, .Fl D behaves like .Fl d to .Ic attach-session , and .Fl X behaves like .Fl x to .Ic attach-session . .Pp If .Fl t is given, it specifies a .Ic session group . Sessions in the same group share the same set of windows - new windows are linked to all sessions in the group and any windows closed removed from all sessions. The current and previous window and any session options remain independent and any session in a group may be killed without affecting the others. The .Ar group-name argument may be: .Bl -enum -width Ds .It the name of an existing group, in which case the new session is added to that group; .It the name of an existing session - the new session is added to the same group as that session, creating a new group if necessary; .It the name for a new group containing only the new session. .El .Pp .Fl n and .Ar shell-command are invalid if .Fl t is used. .Pp The .Fl P option prints information about the new session after it has been created. By default, it uses the format .Ql #{session_name}: but a different format may be specified with .Fl F . .Pp If .Fl E is used, the .Ic update-environment option will not be applied. .It Xo Ic refresh-client .Op Fl cDlLRSU .Op Fl C Ar XxY .Op Fl F Ar flags .Op Fl t Ar target-client .Op Ar adjustment .Xc .D1 (alias: Ic refresh ) Refresh the current client if bound to a key, or a single client if one is given with .Fl t . If .Fl S is specified, only update the client's status line. .Pp The .Fl U , .Fl D , .Fl L .Fl R , and .Fl c flags allow the visible portion of a window which is larger than the client to be changed. .Fl U moves the visible part up by .Ar adjustment rows and .Fl D down, .Fl L left by .Ar adjustment columns and .Fl R right. .Fl c returns to tracking the cursor automatically. If .Ar adjustment is omitted, 1 is used. Note that the visible position is a property of the client not of the window, changing the current window in the attached session will reset it. .Pp .Fl C sets the width and height of a control client and .Fl F sets a comma-separated list of flags. Currently the only flag available is .Ql no-output to disable receiving pane output. .Pp .Fl l requests the clipboard from the client using the .Xr xterm 1 escape sequence and stores it in a new paste buffer. .Pp .Fl L , .Fl R , .Fl U and .Fl D move the visible portion of the window left, right, up or down by .Ar adjustment , if the window is larger than the client. .Fl c resets so that the position follows the cursor. See the .Ic window-size option. .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 JT .Op Fl t Ar target-client .Xc .D1 (alias: Ic showmsgs ) Show client messages or server information. 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 server option. With .Fl t , display the log for .Ar target-client . .Fl J and .Fl T show debugging information about jobs and terminals. .It Xo Ic source-file .Op Fl nqv .Ar path .Ar ... .Xc .D1 (alias: Ic source ) Execute commands from one or more files specified by .Ar path (which may be .Xr glob 7 patterns). If .Fl q is given, no error will be returned if .Ar path does not exist. With .Fl n , the file is parsed but no commands are executed. .Fl v shows the parsed commands and line numbers if possible. .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 Elnpr .Op Fl c Ar target-client .Op Fl t Ar target-session .Op Fl T Ar key-table .Xc .D1 (alias: Ic switchc ) Switch the current session for client .Ar target-client to .Ar target-session . As a special case, .Fl t may refer to a pane (a target that contains .Ql \&: , .Ql \&. or .Ql % ) , in which case the session, window and pane are all changed. If .Fl l , .Fl n or .Fl p is used, the client is moved to the last, next or previous session respectively. .Fl r toggles whether a client is read-only (see the .Ic attach-session command). .Pp If .Fl E is used, .Ic update-environment option will not be applied. .Pp .Fl T sets the client's key table; the next key from the client will be interpreted from .Ar key-table . This may be used to configure multiple prefix keys, or to bind commands to sequences of keys. For example, to make typing .Ql abc run the .Ic list-keys command: .Bd -literal -offset indent bind-key -Ttable2 c list-keys bind-key -Ttable1 b switch-client -Ttable2 bind-key -Troot a switch-client -Ttable1 .Ed .El .Sh WINDOWS AND PANES A .Nm window may be in one of two 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 In copy mode an indicator is displayed in the top-right corner of the pane with the current position and the number of lines in the history. .Pp Commands are sent to copy mode using the .Fl X flag to the .Ic send-keys command. When a key is pressed, copy mode automatically uses one of two key tables, depending on the .Ic mode-keys option: .Ic copy-mode for emacs, or .Ic copy-mode-vi for vi. Key tables may be viewed with the .Ic list-keys command. .Pp The following commands are supported in copy mode: .Bl -column "CommandXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" "viXXXXXXXXXX" "emacs" -offset indent .It Sy "Command" Ta Sy "vi" Ta Sy "emacs" .It Li "append-selection" Ta "" Ta "" .It Li "append-selection-and-cancel" Ta "A" Ta "" .It Li "back-to-indentation" Ta "^" Ta "M-m" .It Li "begin-selection" Ta "Space" Ta "C-Space" .It Li "bottom-line" Ta "L" Ta "" .It Li "cancel" Ta "q" Ta "Escape" .It Li "clear-selection" Ta "Escape" Ta "C-g" .It Li "copy-end-of-line []" Ta "D" Ta "C-k" .It Li "copy-line []" Ta "" Ta "" .It Li "copy-pipe []" Ta "" Ta "" .It Li "copy-pipe-no-clear []" Ta "" Ta "" .It Li "copy-pipe-and-cancel []" Ta "" Ta "" .It Li "copy-selection []" Ta "" Ta "" .It Li "copy-selection-no-clear []" Ta "" Ta "" .It Li "copy-selection-and-cancel []" 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-up" Ta "k" Ta "Up" .It Li "end-of-line" Ta "$" Ta "C-e" .It Li "goto-line " Ta ":" Ta "g" .It Li "halfpage-down" Ta "C-d" Ta "M-Down" .It Li "halfpage-down-and-cancel" Ta "" Ta "" .It Li "halfpage-up" Ta "C-u" Ta "M-Up" .It Li "history-bottom" Ta "G" Ta "M->" .It Li "history-top" Ta "g" Ta "M-<" .It Li "jump-again" Ta ";" Ta ";" .It Li "jump-backward " Ta "F" Ta "F" .It Li "jump-forward " Ta "f" Ta "f" .It Li "jump-reverse" Ta "," Ta "," .It Li "jump-to-backward " Ta "T" Ta "" .It Li "jump-to-forward " Ta "t" Ta "" .It Li "middle-line" Ta "M" Ta "M-r" .It Li "next-matching-bracket" Ta "%" Ta "M-C-f" .It Li "next-paragraph" Ta "}" Ta "M-}" .It Li "next-space" Ta "W" Ta "" .It Li "next-space-end" Ta "E" Ta "" .It Li "next-word" Ta "w" Ta "" .It Li "next-word-end" Ta "e" Ta "M-f" .It Li "other-end" Ta "o" Ta "" .It Li "page-down" Ta "C-f" Ta "PageDown" .It Li "page-down-and-cancel" Ta "" Ta "" .It Li "page-up" Ta "C-b" Ta "PageUp" .It Li "previous-matching-bracket" Ta "" Ta "M-C-b" .It Li "previous-paragraph" Ta "{" Ta "M-{" .It Li "previous-space" Ta "B" Ta "" .It Li "previous-word" Ta "b" Ta "M-b" .It Li "rectangle-toggle" Ta "v" Ta "R" .It Li "scroll-down" Ta "C-e" Ta "C-Down" .It Li "scroll-down-and-cancel" Ta "" Ta "" .It Li "scroll-up" Ta "C-y" Ta "C-Up" .It Li "search-again" Ta "n" Ta "n" .It Li "search-backward " Ta "?" Ta "" .It Li "search-forward " Ta "/" Ta "" .It Li "search-backward-incremental " Ta "" Ta "C-r" .It Li "search-forward-incremental " Ta "" Ta "C-s" .It Li "search-reverse" Ta "N" Ta "N" .It Li "select-line" Ta "V" Ta "" .It Li "select-word" Ta "" Ta "" .It Li "start-of-line" Ta "0" Ta "C-a" .It Li "stop-selection" Ta "" Ta "" .It Li "top-line" Ta "H" Ta "M-R" .El .Pp Copy commands may take an optional buffer prefix argument which is used to generate the buffer name (the default is .Ql buffer so buffers are named .Ql buffer0 , .Ql buffer1 and so on). Pipe commands take a command argument which is the command to which the copied text is piped. The .Ql -and-cancel variants of some commands exit copy mode after they have completed (for copy commands) or when the cursor reaches the bottom (for scrolling commands). .Ql -no-clear variants do not clear the selection. .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 session 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. .Pp The synopsis for the .Ic copy-mode command is: .Bl -tag -width Ds .It Xo Ic copy-mode .Op Fl Meu .Op Fl t Ar target-pane .Xc Enter copy mode. The .Fl u option scrolls one page up. .Fl M begins a mouse drag (only valid if bound to a mouse key binding, see .Sx MOUSE SUPPORT ) . .Fl e specifies that scrolling to the bottom of the history (to the visible screen) should exit copy mode. While in copy mode, pressing a key other than those used for scrolling will disable this behaviour. This is intended to allow fast scrolling through a pane's history, for example with: .Bd -literal -offset indent bind PageUp copy-mode -eu .Ed .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 dP .Op Fl F Ar format .Op Fl n Ar window-name .Op Fl s Ar src-pane .Op Fl t Ar dst-window .Xc .D1 (alias: Ic breakp ) Break .Ar src-pane off from its containing window to make it the only pane in .Ar dst-window . If .Fl d is given, the new window does not become the current window. The .Fl P option prints information about the new window after it has been created. By default, it uses the format .Ql #{session_name}:#{window_index} but a different format may be specified with .Fl F . .It Xo Ic capture-pane .Op Fl aepPqCJ .Op Fl b Ar buffer-name .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. If .Fl p is given, the output goes to stdout, otherwise to the buffer specified with .Fl b or a new buffer if omitted. If .Fl a is given, the alternate screen is used, and the history is not accessible. If no alternate screen exists, an error will be returned unless .Fl q is given. If .Fl e is given, the output includes escape sequences for text and background attributes. .Fl C also escapes non-printable characters as octal \exxx. .Fl J joins wrapped lines and preserves trailing spaces at each line's end. .Fl P captures only any output that the pane has received that is the beginning of an as-yet incomplete escape sequence. .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. .Ql - to .Fl S is the start of the history and to .Fl E the end of the visible pane. The default is to capture only the visible contents of the pane. .It Xo .Ic choose-client .Op Fl NZ .Op Fl F Ar format .Op Fl f Ar filter .Op Fl O Ar sort-order .Op Fl t Ar target-pane .Op Ar template .Xc Put a pane into client mode, allowing a client to be selected interactively from a list. .Fl Z zooms the pane. The following keys may be used in client mode: .Bl -column "Key" "Function" -offset indent .It Sy "Key" Ta Sy "Function" .It Li "Enter" Ta "Choose selected client" .It Li "Up" Ta "Select previous client" .It Li "Down" Ta "Select next client" .It Li "C-s" Ta "Search by name" .It Li "n" Ta "Repeat last search" .It Li "t" Ta "Toggle if client is tagged" .It Li "T" Ta "Tag no clients" .It Li "C-t" Ta "Tag all clients" .It Li "d" Ta "Detach selected client" .It Li "D" Ta "Detach tagged clients" .It Li "x" Ta "Detach and HUP selected client" .It Li "X" Ta "Detach and HUP tagged clients" .It Li "z" Ta "Suspend selected client" .It Li "Z" Ta "Suspend tagged clients" .It Li "f" Ta "Enter a format to filter items" .It Li "O" Ta "Change sort order" .It Li "v" Ta "Toggle preview" .It Li "q" Ta "Exit mode" .El .Pp After a client is chosen, .Ql %% is replaced by the client name in .Ar template and the result executed as a command. If .Ar template is not given, "detach-client -t '%%'" is used. .Pp .Fl O specifies the initial sort order: one of .Ql name , .Ql size , .Ql creation , or .Ql activity . .Fl f specifies an initial filter: the filter is a format - if it evaluates to zero, the item in the list is not shown, otherwise it is shown. If a filter would lead to an empty list, it is ignored. .Fl F specifies the format for each item in the list. .Fl N starts without the preview. This command works only if at least one client is attached. .It Xo .Ic choose-tree .Op Fl GNswZ .Op Fl F Ar format .Op Fl f Ar filter .Op Fl O Ar sort-order .Op Fl t Ar target-pane .Op Ar template .Xc Put a pane into tree mode, where a session, window or pane may be chosen interactively from a list. .Fl s starts with sessions collapsed and .Fl w with windows collapsed. .Fl Z zooms the pane. The following keys may be used in tree mode: .Bl -column "Key" "Function" -offset indent .It Sy "Key" Ta Sy "Function" .It Li "Enter" Ta "Choose selected item" .It Li "Up" Ta "Select previous item" .It Li "Down" Ta "Select next item" .It Li "x" Ta "Kill selected item" .It Li "X" Ta "Kill tagged items" .It Li "<" Ta "Scroll list of previews left" .It Li ">" Ta "Scroll list of previews right" .It Li "C-s" Ta "Search by name" .It Li "n" Ta "Repeat last search" .It Li "t" Ta "Toggle if item is tagged" .It Li "T" Ta "Tag no items" .It Li "C-t" Ta "Tag all items" .It Li "\&:" Ta "Run a command for each tagged item" .It Li "f" Ta "Enter a format to filter items" .It Li "O" Ta "Change sort order" .It Li "v" Ta "Toggle preview" .It Li "q" Ta "Exit mode" .El .Pp After a session, window or pane is chosen, .Ql %% is replaced by the target in .Ar template and the result executed as a command. If .Ar template is not given, "switch-client -t '%%'" is used. .Pp .Fl O specifies the initial sort order: one of .Ql index , .Ql name , or .Ql time . .Fl f specifies an initial filter: the filter is a format - if it evaluates to zero, the item in the list is not shown, otherwise it is shown. If a filter would lead to an empty list, it is ignored. .Fl F specifies the format for each item in the tree. .Fl N starts without the preview. .Fl G includes all sessions in any session groups in the tree rather than only the first. This command works only if at least one client is attached. .It Xo .Ic display-panes .Op Fl b .Op Fl d Ar duration .Op Fl t Ar target-client .Op Ar template .Xc .D1 (alias: Ic displayp ) Display a visible indicator of each pane shown by .Ar target-client . See the .Ic display-panes-colour and .Ic display-panes-active-colour session options. The indicator is closed when a key is pressed or .Ar duration milliseconds have passed. If .Fl d is not given, .Ic display-panes-time is used. A duration of zero means the indicator stays until a key is pressed. While the indicator is on screen, a pane may be chosen with the .Ql 0 to .Ql 9 keys, which will cause .Ar template to be executed as a command with .Ql %% substituted by the pane ID. The default .Ar template is "select-pane -t '%%'". With .Fl b , other commands are not blocked from running until the indicator is closed. .It Xo Ic find-window .Op Fl rCNTZ .Op Fl t Ar target-pane .Ar match-string .Xc .D1 (alias: Ic findw ) Search for a .Xr fnmatch 3 pattern or, with .Fl r , regular expression .Ar match-string in window names, titles, and visible content (but not history). The flags control matching behavior: .Fl C matches only visible window contents, .Fl N matches only the window name and .Fl T matches only the window title. The default is .Fl CNT . .Fl Z zooms the pane. .Pp This command works only if at least one client is attached. .It Xo Ic join-pane .Op Fl bdhv .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 . The .Fl b option causes .Ar src-pane to be joined to left of or above .Ar dst-pane . .Pp If .Fl s is omitted and a marked pane is present (see .Ic select-pane .Fl m ) , the marked pane is used rather than the current 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 Xo Ic kill-window .Op Fl a .Op Fl t Ar target-window .Xc .D1 (alias: Ic killw ) Kill the current window or the window at .Ar target-window , removing it from any sessions to which it is linked. The .Fl a option kills all but the window given with .Fl t . .It Xo Ic last-pane .Op Fl de .Op Fl t Ar target-window .Xc .D1 (alias: Ic lastp ) Select the last (previously selected) pane. .Fl e enables or .Fl d disables input to the 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 adk .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. With .Fl a , the window is moved to the next index up (following windows are moved if necessary). 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 F Ar format .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). For the meaning of the .Fl F flag, see the .Sx FORMATS section. .It Xo Ic list-windows .Op Fl a .Op Fl F Ar format .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 . For the meaning of the .Fl F flag, see the .Sx FORMATS section. .It Xo Ic move-pane .Op Fl bdhv .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 movep ) Like .Ic join-pane , but .Ar src-pane and .Ar dst-pane may belong to the same window. .It Xo Ic move-window .Op Fl ardk .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 . With .Fl r , all windows in the session are renumbered in sequential order, respecting the .Ic base-index option. .It Xo Ic new-window .Op Fl adkP .Op Fl c Ar start-directory .Op Fl e Ar environment .Op Fl F Ar format .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. .Fl c specifies the working directory in which the new window is created. .Pp When the shell command completes, the window closes. See the .Ic remain-on-exit option to change this behaviour. .Pp .Fl e takes the form .Ql VARIABLE=value and sets an environment variable for the newly created window; it may be specified multiple times. .Pp The .Ev TERM environment variable must be set to .Ql screen or .Ql tmux for all programs running .Em inside .Nm . New windows will automatically have .Ql TERM=screen added to their environment, but care must be taken not to reset this in shell start-up files or by the .Fl e option. .Pp The .Fl P option prints information about the new window after it has been created. By default, it uses the format .Ql #{session_name}:#{window_index} but a different format may be specified with .Fl F . .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 an alert. .It Xo Ic pipe-pane .Op Fl IOo .Op Fl t Ar target-pane .Op Ar shell-command .Xc .D1 (alias: Ic pipep ) Pipe output sent by the program in .Ar target-pane to a shell command or vice versa. A pane may only be connected 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 .Fl I and .Fl O specify which of the .Ar shell-command output streams are connected to the pane: with .Fl I stdout is connected (so anything .Ar shell-command prints is written to the pane as if it were typed); with .Fl O stdin is connected (so any output in the pane is piped to .Ar shell-command ) . Both may be used together and if neither are specified, .Fl O is used. .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 an 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 DLMRUZ .Op Fl t Ar target-pane .Op Fl x Ar width .Op Fl y Ar height .Op Ar adjustment .Xc .D1 (alias: Ic resizep ) Resize a pane, up, down, left or right by .Ar adjustment with .Fl U , .Fl D , .Fl L or .Fl R , or to an absolute size with .Fl x or .Fl y . The .Ar adjustment is given in lines or cells (the default is 1). .Pp With .Fl Z , the active pane is toggled between zoomed (occupying the whole of the window) and unzoomed (its normal position in the layout). .Pp .Fl M begins mouse resizing (only valid if bound to a mouse key binding, see .Sx MOUSE SUPPORT ) . .It Xo Ic resize-window .Op Fl aADLRU .Op Fl t Ar target-window .Op Fl x Ar width .Op Fl y Ar height .Op Ar adjustment .Xc .D1 (alias: Ic resizew ) Resize a window, up, down, left or right by .Ar adjustment with .Fl U , .Fl D , .Fl L or .Fl R , or to an absolute size with .Fl x or .Fl y . The .Ar adjustment is given in lines or cells (the default is 1). .Fl A sets the size of the largest session containing the window; .Fl a the size of the smallest. This command will automatically set .Ic window-size to manual in the window options. .It Xo Ic respawn-pane .Op Fl k .Op Fl c Ar start-directory .Op Fl e Ar environment .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. .Fl c specifies a new working directory for the pane. The .Fl e option has the same meaning as for the .Ic new-window command. .It Xo Ic respawn-window .Op Fl k .Op Fl c Ar start-directory .Op Fl e Ar environment .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. .Fl c specifies a new working directory for the window. The .Fl e option has the same meaning as for the .Ic new-window command. .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 Enop .Op Fl t Ar target-pane .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. .Fl o applies the last set layout if possible (undoes the most recent layout change). .Fl E spreads the current pane and any panes next to it out evenly. .It Xo Ic select-pane .Op Fl DdeLlMmRU .Op Fl T Ar title .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. .Fl e enables or .Fl d disables input to the pane. .Fl T sets the pane title. .Pp .Fl m and .Fl M are used to set and clear the .Em marked pane . There is one marked pane at a time, setting a new marked pane clears the last. The marked pane is the default target for .Fl s to .Ic join-pane , .Ic swap-pane and .Ic swap-window . .It Xo Ic select-window .Op Fl lnpT .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. If .Fl T is given and the selected window is already the current window, the command behaves like .Ic last-window . .It Xo Ic split-window .Op Fl bdfhIvP .Op Fl c Ar start-directory .Op Fl e Ar environment .Oo Fl l .Ar size | .Fl p Ar percentage Oc .Op Fl t Ar target-pane .Op Ar shell-command .Op Fl F Ar format .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. The .Fl b option causes the new pane to be created to the left of or above .Ar target-pane . The .Fl f option creates a new pane spanning the full window height (with .Fl h ) or full window width (with .Fl v ) , instead of splitting the active pane. .Pp An empty .Ar shell-command ('') will create a pane with no command running in it. Output can be sent to such a pane with the .Ic display-message command. The .Fl I flag (if .Ar shell-command is not specified or empty) will create an empty pane and forward any output from stdin to it. For example: .Bd -literal -offset indent $ make 2>&1|tmux splitw -dI & .Ed .Pp 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. .Pp If .Fl s is omitted and a marked pane is present (see .Ic select-pane .Fl m ) , the marked pane is used rather than the current 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 . .Pp Like .Ic swap-pane , if .Fl s is omitted and a marked pane is present (see .Ic select-pane .Fl m ) , the window containing the marked pane is used rather than the current 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 F12 , .Em Home , .Em IC (Insert), .Em NPage/PageDown/PgDn , .Em PPage/PageUp/PgUp , .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 nr .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 . Keys are bound in a key table. By default (without -T), the key is bound in the .Em prefix key table. This table is used for keys pressed after the prefix key (for example, by default .Ql c is bound to .Ic new-window in the .Em prefix table, so .Ql C-b c creates a new window). The .Em root table is used for keys pressed without the prefix key: binding .Ql c to .Ic new-window in the .Em root table (not recommended) means a plain .Ql c will create a new window. .Fl n is an alias for .Fl T Ar root . Keys may also be bound in custom key tables and the .Ic switch-client .Fl T command used to switch to them from a key binding. The .Fl r flag indicates this key may repeat, see the .Ic repeat-time option. .Pp To view the default bindings and possible commands, see the .Ic list-keys command. .It Xo Ic list-keys .Op Fl T Ar key-table .Xc .D1 (alias: Ic lsk ) List all key bindings. Without .Fl T all key tables are printed. With .Fl T only .Ar key-table . .It Xo Ic send-keys .Op Fl HlMRX .Op Fl N Ar repeat-count .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. .Pp The .Fl l flag disables key name lookup and processes the keys as literal UTF-8 characters. The .Fl H flag expects each key to be a hexadecimal number for an ASCII character. .Pp The .Fl R flag causes the terminal state to be reset. .Pp .Fl M passes through a mouse event (only valid if bound to a mouse key binding, see .Sx MOUSE SUPPORT ) . .Pp .Fl X is used to send a command into copy mode - see the .Sx WINDOWS AND PANES section. .Fl N specifies a repeat count. .It Xo Ic send-prefix .Op Fl 2 .Op Fl t Ar target-pane .Xc Send the prefix key, or with .Fl 2 the secondary prefix key, to a window as if it was pressed. .It Xo Ic unbind-key .Op Fl an .Op Fl T Ar key-table .Ar key .Xc .D1 (alias: Ic unbind ) Unbind the command bound to .Ar key . .Fl n and .Fl T are the same as for .Ic bind-key . If .Fl a is present, all key bindings are removed. .El .Sh OPTIONS The appearance and behaviour of .Nm may be modified by changing the value of various options. There are four types of option: .Em server options , .Em session options .Em window options and .Em pane options . .Pp The .Nm server has a set of global options which do not apply to any particular window or session or pane. 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 a set of pane options to each pane. Pane options inherit from window options. This means any pane option may be set as a window option to apply the option to all panes in the window without the option set, for example these commands will set the background colour to red for all panes except pane 0: .Bd -literal -offset indent set -w window-style bg=red set -pt:.0 window-style bg=blue .Ed .Pp There is also a set of global window options from which any unset window or pane options are inherited. Window and pane options are altered with .Ic set-option .Fl w and .Fl p commands and displayed with .Ic show-option .Fl w and .Fl p . .Pp .Nm also supports user options which are prefixed with a .Ql \&@ . User options may have any name, so long as they are prefixed with .Ql \&@ , and be set to any string. For example: .Bd -literal -offset indent $ tmux setw -q @foo "abc123" $ tmux showw -v @foo abc123 .Ed .Pp Commands which set options are as follows: .Bl -tag -width Ds .It Xo Ic set-option .Op Fl aFgopqsuw .Op Fl t Ar target-pane .Ar option Ar value .Xc .D1 (alias: Ic set ) Set a pane option with .Fl p , a window option with .Fl w , a server option with .Fl s , otherwise a session option. If the option is not a user option, .Fl w or .Fl s may be unnecessary - .Nm will infer the type from the option name, assuming .Fl w for pane options. If .Fl g is given, the global session or window option is set. .Pp .Fl F expands formats in the option value. The .Fl u flag unsets an option, so a session inherits the option from the global options (or with .Fl g , restores a global option to the default). .Pp The .Fl o flag prevents setting an option that is already set and the .Fl q flag suppresses errors about unknown or ambiguous options. .Pp With .Fl a , and if the option expects a string or a style, .Ar value is appended to the existing setting. For example: .Bd -literal -offset indent set -g status-left "foo" set -ag status-left "bar" .Ed .Pp Will result in .Ql foobar . And: .Bd -literal -offset indent set -g status-style "bg=red" set -ag status-style "fg=blue" .Ed .Pp Will result in a red background .Em and blue foreground. Without .Fl a , the result would be the default background and a blue foreground. .It Xo Ic show-options .Op Fl AgHpqsvw .Op Fl t Ar target-pane .Op Ar option .Xc .D1 (alias: Ic show ) Show the pane options (or a single option if .Ar option is provided) with .Fl p , the window options with .Fl w , the server options with .Fl s , otherwise the session options. If the option is not a user option, .Fl w or .Fl s may be unnecessary - .Nm will infer the type from the option name, assuming .Fl w for pane options. Global session or window options are listed if .Fl g is used. .Fl v shows only the option value, not the name. If .Fl q is set, no error will be returned if .Ar option is unset. .Fl H includes hooks (omitted by default). .Fl A includes options inherited from a parent set of options, such options are marked with an asterisk. .Ar value depends on the option and may be a number, a string, or a flag (on, off, or omitted to toggle). .El .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 command-alias[] .Ar name=value .Xc This is an array of custom aliases for commands. If an unknown command matches .Ar name , it is replaced with .Ar value . For example, after: .Pp .Dl set -s command-alias[100] zoom='resize-pane -Z' .Pp Using: .Pp .Dl zoom -t:.1 .Pp Is equivalent to: .Pp .Dl resize-pane -Z -t:.1 .Pp Note that aliases are expanded when a command is parsed rather than when it is executed, so binding an alias with .Ic bind-key will bind the expanded form. .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 , .Ql tmux or a derivative of them. .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-empty .Op Ic on | off .Xc If enabled (the default), the server will exit when there are no active sessions. .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 focus-events .Op Ic on | off .Xc When enabled, focus events are requested from the terminal if supported and passed through to applications running in .Nm . Attached clients should be detached and attached again after changing this option. .It Ic history-file Ar path If not empty, a file to which .Nm will write command prompt history on exit and load it from on start. .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 100. .It Xo Ic set-clipboard .Op Ic on | external | off .Xc Attempt to set the terminal clipboard content using the .Xr xterm 1 escape sequence, if there is an .Em \&Ms entry in the .Xr terminfo 5 description (see the .Sx TERMINFO EXTENSIONS section). .Pp If set to .Ic on , .Nm will both accept the escape sequence to create a buffer and attempt to set the terminal clipboard. If set to .Ic external , .Nm will attempt to set the terminal clipboard but ignore attempts by applications to set .Nm buffers. If .Ic off , .Nm will neither accept the clipboard escape sequence nor attempt to set the clipboard. .Pp 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 terminal-overrides[] Ar string Allow terminal descriptions read using .Xr terminfo 5 to be overridden. Each entry is 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 matching .Ql rxvt* : .Pp .Dl "rxvt*:clear=\ee[H\ee[2J" .Pp The terminal entry value is passed through .Xr strunvis 3 before interpretation. .It Ic user-keys[] Ar key Set list of user-defined key escape sequences. Each item is associated with a key named .Ql User0 , .Ql User1 , and so on. .Pp For example: .Bd -literal -offset indent set -s user-keys[0] "\ee[5;30012~" bind User0 resize-pane -L 3 .Ed .El .Pp Available session options are: .Bl -tag -width Ds .It Xo Ic activity-action .Op Ic any | none | current | other .Xc Set action on window activity when .Ic monitor-activity is on. .Ic any means activity in any window linked to a session causes a bell or message (depending on .Ic visual-activity ) in the current window of that session, .Ic none means all activity is ignored (equivalent to .Ic monitor-activity being off), .Ic current means only activity in windows other than the current window are ignored and .Ic other means activity in the current window is ignored but not those in other windows. .It Ic assume-paste-time Ar milliseconds If keys are entered faster than one in .Ar milliseconds , they are assumed to have been pasted rather than typed and .Nm key bindings are not processed. The default is one millisecond and zero disables. .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 | other .Xc Set action on a bell in a window when .Ic monitor-bell is on. The values are the same as those for .Ic activity-action . .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-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-size Ar XxY Set the default size of new windows when the .Ic window-size option is set to manual or when a session is created with .Ic new-session .Fl d . The value is the width and height separated by an .Ql x character. The default is 80x24. .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. If set to 0, messages and indicators are displayed until a key is pressed. .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 key-table Ar key-table Set the default key table to .Ar key-table instead of .Em root . .It Ic lock-after-time Ar number Lock the session (like the .Ic lock-session command) after .Ar number seconds of inactivity. 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 Ic message-command-style Ar style Set status line message command style. For how to specify .Ar style , see the .Sx STYLES section. .It Ic message-style Ar style Set status line message style. For how to specify .Ar style , see the .Sx STYLES section. .It Xo Ic mouse .Op Ic on | off .Xc If on, .Nm captures the mouse and allows mouse events to be bound as key bindings. See the .Sx MOUSE SUPPORT section for details. .It Ic prefix Ar key Set the key accepted as a prefix key. In addition to the standard keys described under .Sx KEY BINDINGS , .Ic prefix can be set to the special key .Ql None to set no prefix. .It Ic prefix2 Ar key Set a secondary key accepted as a prefix key. Like .Ic prefix , .Ic prefix2 can be set to .Ql None . .It Xo Ic renumber-windows .Op Ic on | off .Xc If on, when a window is closed in a session, automatically renumber the other windows in numerical order. This respects the .Ic base-index option if it has been set. If off, do not renumber the windows. .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 set-titles .Op Ic on | off .Xc Attempt to set the client terminal title using the .Em tsl and .Em fsl .Xr terminfo 5 entries if they exist. .Nm automatically sets these to the \ee]0;...\e007 sequence if the terminal appears to be .Xr xterm 1 . This option is off by default. .It Ic set-titles-string Ar string String used to set the client terminal title if .Ic set-titles is on. Formats are expanded, see the .Sx FORMATS section. .It Xo Ic silence-action .Op Ic any | none | current | other .Xc Set action on window silence when .Ic monitor-silence is on. The values are the same as those for .Ic activity-action . .It Xo Ic status .Op Ic off | on | 2 | 3 | 4 | 5 .Xc Show or hide the status line or specify its size. Using .Ic on gives a status line one row in height; .Ic 2 , .Ic 3 , .Ic 4 or .Ic 5 more rows. .It Ic status-format[] Ar format Specify the format to be used for each line of the status line. The default builds the top status line from the various individual status options below. .It Ic status-interval Ar interval Update the status line 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 (by default the session name) to the left of the status line. .Ar string will be passed through .Xr strftime 3 . Also see the .Sx FORMATS and .Sx STYLES sections. .Pp For details on how the names and titles can be set see the .Sx "NAMES AND TITLES" section. .Pp Examples are: .Bd -literal -offset indent #(sysctl vm.loadavg) #[fg=yellow,bold]#(apm -l)%%#[default] [#S] .Ed .Pp The default is .Ql "[#S] " . .It Ic status-left-length Ar length Set the maximum .Ar length of the left component of the status line. The default is 10. .It Ic status-left-style Ar style Set the style of the left part of the status line. For how to specify .Ar style , see the .Sx STYLES section. .It Xo Ic status-position .Op Ic top | bottom .Xc Set the position of the status line. .It Ic status-right Ar string Display .Ar string to the right of the status line. By default, the current pane 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 and character pairs are replaced. .It Ic status-right-length Ar length Set the maximum .Ar length of the right component of the status line. The default is 40. .It Ic status-right-style Ar style Set the style of the right part of the status line. For how to specify .Ar style , see the .Sx STYLES section. .It Ic status-style Ar style Set status line style. For how to specify .Ar style , see the .Sx STYLES section. .It Ic update-environment[] Ar variable Set 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). .It Xo Ic visual-activity .Op Ic on | off | both .Xc If on, display a message instead of sending a bell when activity occurs in a window for which the .Ic monitor-activity window option is enabled. If set to both, a bell and a message are produced. .It Xo Ic visual-bell .Op Ic on | off | both .Xc If on, a message is shown on a bell in a window for which the .Ic monitor-bell window option is enabled instead of it being passed through to the terminal (which normally makes a sound). If set to both, a bell and a message are produced. Also see the .Ic bell-action option. .It Xo Ic visual-silence .Op Ic on | off | both .Xc If .Ic monitor-silence is enabled, prints a message after the interval has expired on a given window instead of sending a bell. If set to both, a bell and a message are produced. .It Ic word-separators Ar string Sets the session'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 \ -_@ . .El .Pp Available 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 or largest session (see the .Ic window-size option) for which it is the current window, rather than the session to which it is attached. The window may resize when the current window is changed on another session; this option is good for full-screen programs which support .Dv SIGWINCH and poor for interactive programs such as shells. .Pp .It Xo Ic automatic-rename .Op Ic on | off .Xc Control automatic window renaming. When this setting is enabled, .Nm will rename the window automatically using the format specified by .Ic automatic-rename-format . 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 , or with a terminal escape sequence. It may be switched off globally with: .Bd -literal -offset indent set-option -wg automatic-rename off .Ed .Pp .It Ic automatic-rename-format Ar format The format (see .Sx FORMATS ) used when the .Ic automatic-rename option is enabled. .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 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 Xo Ic mode-keys .Op Ic vi | emacs .Xc Use vi or emacs-style key bindings in copy mode. The default is emacs, unless .Ev VISUAL or .Ev EDITOR contains .Ql vi . .Pp .It Ic mode-style Ar style Set window modes style. For how to specify .Ar style , see the .Sx STYLES section. .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 Xo Ic monitor-bell .Op Ic on | off .Xc Monitor for a bell in the window. Windows with a bell are 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 Ic pane-active-border-style Ar style Set the pane border style for the currently active pane. For how to specify .Ar style , see the .Sx STYLES section. Attributes are ignored. .Pp .It Ic pane-base-index Ar index Like .Ic base-index , but set the starting index for pane numbers. .Pp .It Ic pane-border-format Ar format Set the text shown in pane border status lines. .Pp .It Xo Ic pane-border-status .Op Ic off | top | bottom .Xc Turn pane border status lines off or set their position. .Pp .It Ic pane-border-style Ar style Set the pane border style for panes aside from the active pane. For how to specify .Ar style , see the .Sx STYLES section. Attributes are ignored. .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 Ic window-status-activity-style Ar style Set status line style for windows with an activity alert. For how to specify .Ar style , see the .Sx STYLES section. .Pp .It Ic window-status-bell-style Ar style Set status line style for windows with a bell alert. For how to specify .Ar style , see the .Sx STYLES section. .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 window-status-current-style Ar style Set status line style for the currently active window. For how to specify .Ar style , see the .Sx STYLES section. .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 .Sx FORMATS and .Sx STYLES sections. .Pp .It Ic window-status-last-style Ar style Set status line style for the last active window. For how to specify .Ar style , see the .Sx STYLES section. .Pp .It Ic window-status-separator Ar string Sets the separator drawn between windows in the status line. The default is a single space character. .Pp .It Ic window-status-style Ar style Set status line style for a single window. For how to specify .Ar style , see the .Sx STYLES section. .Pp .It Xo Ic window-size .Ar largest | Ar smallest | Ar manual .Xc Configure how .Nm determines the window size. If set to .Ar largest , the size of the largest attached session is used; if .Ar smallest , the size of the smallest. If .Ar manual , the size of a new window is set from the .Ic default-size option and windows are resized automatically. See also the .Ic resize-window command and the .Ic aggressive-resize option. .Pp .It Xo Ic wrap-search .Op Ic on | off .Xc If this option is set, searches will wrap around the end of the pane contents. The default is on. .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. .El .Pp Available pane options are: .Pp .Bl -tag -width Ds -compact .It Xo Ic allow-rename .Op Ic on | off .Xc Allow programs in the pane to change the window name using a terminal escape sequence (\eek...\ee\e\e). .Pp .It Xo Ic alternate-screen .Op Ic on | off .Xc This option configures whether programs running inside the pane 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. .Pp .It Xo Ic remain-on-exit .Op Ic on | off .Xc A pane with this flag set is not destroyed when the program running in it exits. The pane may be reactivated with the .Ic respawn-pane command. .Pp .It Ic window-active-style Ar style Set the pane style when it is the active pane. For how to specify .Ar style , see the .Sx STYLES section. .Pp .It Ic window-style Ar style Set the pane style. For how to specify .Ar style , see the .Sx STYLES section. .El .Sh HOOKS .Nm allows commands to run on various triggers, called .Em hooks . Most .Nm commands have an .Em after hook and there are a number of hooks not associated with commands. .Pp Hooks are stored as array options, members of the array are executed in order when the hook is triggered. Hooks may be configured with the .Ic set-hook or .Ic set-option commands and displayed with .Ic show-hooks or .Ic show-options .Fl H . The following two commands are equivalent: .Bd -literal -offset indent. set-hook -g pane-mode-changed[42] 'set -g status-left-style bg=red' set-option -g pane-mode-changed[42] 'set -g status-left-style bg=red' .Ed .Pp Setting a hook without specifying an array index clears the hook and sets the first member of the array. .Pp A command's after hook is run after it completes, except when the command is run as part of a hook itself. They are named with an .Ql after- prefix. For example, the following command adds a hook to select the even-vertical layout after every .Ic split-window : .Bd -literal -offset indent set-hook -g after-split-window "selectl even-vertical" .Ed .Pp All the notifications listed in the .Sx CONTROL MODE section are hooks (without any arguments), except .Ic %exit . The following additional hooks are available: .Bl -tag -width "XXXXXXXXXXXXXXXXXXXXXX" .It alert-activity Run when a window has activity. See .Ic monitor-activity . .It alert-bell Run when a window has received a bell. See .Ic monitor-bell . .It alert-silence Run when a window has been silent. See .Ic monitor-silence . .It client-attached Run when a client is attached. .It client-detached Run when a client is detached .It client-resized Run when a client is resized. .It client-session-changed Run when a client's attached session is changed. .It pane-died Run when the program running in a pane exits, but .Ic remain-on-exit is on so the pane has not closed. .It pane-exited Run when the program running in a pane exits. .It pane-focus-in Run when the focus enters a pane, if the .Ic focus-events option is on. .It pane-focus-out Run when the focus exits a pane, if the .Ic focus-events option is on. .It pane-set-clipboard Run when the terminal clipboard is set using the .Xr xterm 1 escape sequence. .It session-created Run when a new session created. .It session-closed Run when a session closed. .It session-renamed Run when a session is renamed. .It window-linked Run when a window is linked into a session. .It window-renamed Run when a window is renamed. .It window-unlinked Run when a window is unlinked from a session. .El .Pp Hooks are managed with these commands: .Bl -tag -width Ds .It Xo Ic set-hook .Op Fl agRu .Op Fl t Ar target-session .Ar hook-name .Ar command .Xc Without .Fl R , sets (or with .Fl u unsets) hook .Ar hook-name to .Ar command . If .Fl g is given, .Em hook-name is added to the global list of hooks, otherwise it is added to the session hooks (for .Ar target-session with .Fl t ) . .Fl a appends to a hook. Like options, session hooks inherit from the global ones. .Pp With .Fl R , run .Ar hook-name immediately. .It Xo Ic show-hooks .Op Fl g .Op Fl t Ar target-session .Xc Shows the global list of hooks with .Fl g , otherwise the session hooks. .El .Sh MOUSE SUPPORT If the .Ic mouse option is on (the default is off), .Nm allows mouse events to be bound as keys. The name of each key is made up of a mouse event (such as .Ql MouseUp1 ) and a location suffix, one of the following: .Bl -column "XXXXXXXXXXXXX" -offset indent .It Li "Pane" Ta "the contents of a pane" .It Li "Border" Ta "a pane border" .It Li "Status" Ta "the status line window list" .It Li "StatusLeft" Ta "the left part of the status line" .It Li "StatusRight" Ta "the right part of the status line" .It Li "StatusDefault" Ta "any other part of the status line" .El .Pp The following mouse events are available: .Bl -column "MouseDown1" "MouseDrag1" "WheelDown" -offset indent .It Li "WheelUp" Ta "WheelDown" Ta "" .It Li "MouseDown1" Ta "MouseUp1" Ta "MouseDrag1" Ta "MouseDragEnd1" .It Li "MouseDown2" Ta "MouseUp2" Ta "MouseDrag2" Ta "MouseDragEnd2" .It Li "MouseDown3" Ta "MouseUp3" Ta "MouseDrag3" Ta "MouseDragEnd3" .It Li "DoubleClick1" Ta "DoubleClick2" Ta "DoubleClick3" .It Li "TripleClick1" Ta "TripleClick2" Ta "TripleClick3" .El .Pp Each should be suffixed with a location, for example .Ql MouseDown1Status . .Pp The special token .Ql {mouse} or .Ql = may be used as .Ar target-window or .Ar target-pane in commands bound to mouse key bindings. It resolves to the window or pane over which the mouse event took place (for example, the window in the status line over which button 1 was released for a .Ql MouseUp1Status binding, or the pane over which the wheel was scrolled for a .Ql WheelDownPane binding). .Pp The .Ic send-keys .Fl M flag may be used to forward a mouse event to a pane. .Pp The default key bindings allow the mouse to be used to select and resize panes, to copy text and to change window using the status line. These take effect if the .Ic mouse option is turned on. .Sh FORMATS Certain commands accept the .Fl F flag with a .Ar format argument. This is a string which controls the output format of the command. Format variables are enclosed in .Ql #{ and .Ql } , for example .Ql #{session_name} . The possible variables are listed in the table below, or the name of a .Nm option may be used for an option's value. Some variables have a shorter alias such as .Ql #S ; .Ql ## is replaced by a single .Ql # , .Ql #, by a .Ql \&, and .Ql #} by a .Ql } . .Pp Conditionals are available by prefixing with .Ql \&? and separating two alternatives with a comma; if the specified variable exists and is not zero, the first alternative is chosen, otherwise the second is used. For example .Ql #{?session_attached,attached,not attached} will include the string .Ql attached if the session is attached and the string .Ql not attached if it is unattached, or .Ql #{?automatic-rename,yes,no} will include .Ql yes if .Ic automatic-rename is enabled, or .Ql no if not. Conditionals can be nested arbitrarily. Inside a conditional, .Ql \&, and .Ql } must be escaped as .Ql #, and .Ql #} , unless they are part of a .Ql #{...} replacement. For example: .Bd -literal -offset indent #{?pane_in_mode,#[fg=white#,bg=red],#[fg=red#,bg=white]}#W . .Ed .Pp String comparisons may be expressed by prefixing two comma-separated alternatives by .Ql == , .Ql != , .Ql < , .Ql > , .Ql <= or .Ql >= and a colon. For example .Ql #{==:#{host},myhost} will be replaced by .Ql 1 if running on .Ql myhost , otherwise by .Ql 0 . .Ql || and .Ql && evaluate to true if either or both of two comma-separated alternatives are true, for example .Ql #{||:#{pane_in_mode},#{alternate_on}} . .Pp An .Ql m specifies an .Xr fnmatch 3 or regular expression comparison. The first argument is the pattern and the second the string to compare. An optional third argument specifies flags: .Ql r means the pattern is a regular expression instead of the default .Xr fnmatch 3 pattern, and .Ql i means to ignore case. For example: .Ql #{m:*foo*,#{host}} or .Ql #{m/ri:^A,MYVAR} . A .Ql C performs a search for an .Xr fnmatch 3 pattern or regular expression in the pane content and evaluates to zero if not found, or a line number if found. Like .Ql m , an .Ql r flag means search for a regular expression and .Ql i ignores case. For example: .Ql #{C/r:^Start} .Pp A limit may be placed on the length of the resultant string by prefixing it by an .Ql = , a number and a colon. Positive numbers count from the start of the string and negative from the end, so .Ql #{=5:pane_title} will include at most the first five characters of the pane title, or .Ql #{=-5:pane_title} the last five characters. A suffix or prefix may be given as a second argument - if provided then it is appended or prepended to the string if the length has been trimmed, for example .Ql #{=/5/...:pane_title} will append .Ql ... if the pane title is more than five characters. .Pp Prefixing a time variable with .Ql t: will convert it to a string, so if .Ql #{window_activity} gives .Ql 1445765102 , .Ql #{t:window_activity} gives .Ql Sun Oct 25 09:25:02 2015 . The .Ql b: and .Ql d: prefixes are .Xr basename 3 and .Xr dirname 3 of the variable respectively. .Ql q: will escape .Xr sh 1 special characters. .Ql E: will expand the format twice, for example .Ql #{E:status-left} is the result of expanding the content of the .Ic status-left option rather than the option itself. .Ql T: is like .Ql E: but also expands .Xr strftime 3 specifiers. .Ql S: , .Ql W: or .Ql P: will loop over each session, window or pane and insert the format once for each. For windows and panes, two comma-separated formats may be given: the second is used for the current window or active pane. For example, to get a list of windows formatted like the status line: .Bd -literal -offset indent #{W:#{E:window-status-format} ,#{E:window-status-current-format} } .Ed .Pp A prefix of the form .Ql s/foo/bar/: will substitute .Ql foo with .Ql bar throughout. The first argument may be an extended regular expression and a final argument may be .Ql i to ignore case, for example .Ql s/a(.)/\e1x/i: would change .Ql abABab into .Ql bxBxbx . .Pp In addition, the last line of a shell command's output may be inserted using .Ql #() . For example, .Ql #(uptime) will insert the system's uptime. When constructing formats, .Nm does not wait for .Ql #() commands to finish; instead, the previous result from running the same command is used, or a placeholder if the command has not been run before. If the command hasn't exited, the most recent line of output will be used, but the status line will not be updated more than once a second. Commands are executed with the .Nm global environment set (see the .Sx GLOBAL AND SESSION ENVIRONMENT section). .Pp An .Ql l specifies that a string should be interpreted literally and not expanded. For example .Ql #{l:#{?pane_in_mode,yes,no}} will be replaced by .Ql #{?pane_in_mode,yes,no} . .Pp The following variables are available, where appropriate: .Bl -column "XXXXXXXXXXXXXXXXXXX" "XXXXX" .It Sy "Variable name" Ta Sy "Alias" Ta Sy "Replaced with" .It Li "alternate_on" Ta "" Ta "1 if pane is in alternate screen" .It Li "alternate_saved_x" Ta "" Ta "Saved cursor X in alternate screen" .It Li "alternate_saved_y" Ta "" Ta "Saved cursor Y in alternate screen" .It Li "buffer_created" Ta "" Ta "Time buffer created" .It Li "buffer_name" Ta "" Ta "Name of buffer" .It Li "buffer_sample" Ta "" Ta "Sample of start of buffer" .It Li "buffer_size" Ta "" Ta "Size of the specified buffer in bytes" .It Li "client_activity" Ta "" Ta "Time client last had activity" .It Li "client_control_mode" Ta "" Ta "1 if client is in control mode" .It Li "client_created" Ta "" Ta "Time client created" .It Li "client_discarded" Ta "" Ta "Bytes discarded when client behind" .It Li "client_height" Ta "" Ta "Height of client" .It Li "client_key_table" Ta "" Ta "Current key table" .It Li "client_last_session" Ta "" Ta "Name of the client's last session" .It Li "client_name" Ta "" Ta "Name of client" .It Li "client_pid" Ta "" Ta "PID of client process" .It Li "client_prefix" Ta "" Ta "1 if prefix key has been pressed" .It Li "client_readonly" Ta "" Ta "1 if client is readonly" .It Li "client_session" Ta "" Ta "Name of the client's session" .It Li "client_termname" Ta "" Ta "Terminal name of client" .It Li "client_termtype" Ta "" Ta "Terminal type of client" .It Li "client_tty" Ta "" Ta "Pseudo terminal of client" .It Li "client_utf8" Ta "" Ta "1 if client supports utf8" .It Li "client_width" Ta "" Ta "Width of client" .It Li "client_written" Ta "" Ta "Bytes written to client" .It Li "command" Ta "" Ta "Name of command in use, if any" .It Li "command_list_alias" Ta "" Ta "Command alias if listing commands" .It Li "command_list_name" Ta "" Ta "Command name if listing commands" .It Li "command_list_usage" Ta "" Ta "Command usage if listing commands" .It Li "cursor_character" Ta "" Ta "Character at cursor in pane" .It Li "cursor_flag" Ta "" Ta "Pane cursor flag" .It Li "cursor_x" Ta "" Ta "Cursor X position in pane" .It Li "cursor_y" Ta "" Ta "Cursor Y position in pane" .It Li "history_bytes" Ta "" Ta "Number of bytes in window history" .It Li "history_limit" Ta "" Ta "Maximum window history lines" .It Li "history_size" Ta "" Ta "Size of history in lines" .It Li "hook" Ta "" Ta "Name of running hook, if any" .It Li "hook_pane" Ta "" Ta "ID of pane where hook was run, if any" .It Li "hook_session" Ta "" Ta "ID of session where hook was run, if any" .It Li "hook_session_name" Ta "" Ta "Name of session where hook was run, if any" .It Li "hook_window" Ta "" Ta "ID of window where hook was run, if any" .It Li "hook_window_name" Ta "" Ta "Name of window where hook was run, if any" .It Li "host" Ta "#H" Ta "Hostname of local host" .It Li "host_short" Ta "#h" Ta "Hostname of local host (no domain name)" .It Li "insert_flag" Ta "" Ta "Pane insert flag" .It Li "keypad_cursor_flag" Ta "" Ta "Pane keypad cursor flag" .It Li "keypad_flag" Ta "" Ta "Pane keypad flag" .It Li "line" Ta "" Ta "Line number in the list" .It Li "mouse_all_flag" Ta "" Ta "Pane mouse all flag" .It Li "mouse_any_flag" Ta "" Ta "Pane mouse any flag" .It Li "mouse_button_flag" Ta "" Ta "Pane mouse button flag" .It Li "mouse_line" Ta "" Ta "Line under mouse, if any" .It Li "mouse_sgr_flag" Ta "" Ta "Pane mouse SGR flag" .It Li "mouse_standard_flag" Ta "" Ta "Pane mouse standard flag" .It Li "mouse_utf8_flag" Ta "" Ta "Pane mouse UTF-8 flag" .It Li "mouse_word" Ta "" Ta "Word under mouse, if any" .It Li "mouse_x" Ta "" Ta "Mouse X position, if any" .It Li "mouse_y" Ta "" Ta "Mouse Y position, if any" .It Li "origin_flag" Ta "" Ta "Pane origin flag" .It Li "pane_active" Ta "" Ta "1 if active pane" .It Li "pane_at_bottom" Ta "" Ta "1 if pane is at the bottom of window" .It Li "pane_at_left" Ta "" Ta "1 if pane is at the left of window" .It Li "pane_at_right" Ta "" Ta "1 if pane is at the right of window" .It Li "pane_at_top" Ta "" Ta "1 if pane is at the top of window" .It Li "pane_bottom" Ta "" Ta "Bottom of pane" .It Li "pane_current_command" Ta "" Ta "Current command if available" .It Li "pane_current_path" Ta "" Ta "Current path if available" .It Li "pane_dead" Ta "" Ta "1 if pane is dead" .It Li "pane_dead_status" Ta "" Ta "Exit status of process in dead pane" .It Li "pane_format" Ta "" Ta "1 if format is for a pane (not assuming the current)" .It Li "pane_height" Ta "" Ta "Height of pane" .It Li "pane_id" Ta "#D" Ta "Unique pane ID" .It Li "pane_in_mode" Ta "" Ta "1 if pane is in a mode" .It Li "pane_index" Ta "#P" Ta "Index of pane" .It Li "pane_input_off" Ta "" Ta "1 if input to pane is disabled" .It Li "pane_left" Ta "" Ta "Left of pane" .It Li "pane_marked" Ta "" Ta "1 if this is the marked pane" .It Li "pane_marked_set" Ta "" Ta "1 if a marked pane is set" .It Li "pane_mode" Ta "" Ta "Name of pane mode, if any" .It Li "pane_pid" Ta "" Ta "PID of first process in pane" .It Li "pane_pipe" Ta "" Ta "1 if pane is being piped" .It Li "pane_right" Ta "" Ta "Right of pane" .It Li "pane_search_string" Ta "" Ta "Last search string in copy mode" .It Li "pane_start_command" Ta "" Ta "Command pane started with" .It Li "pane_synchronized" Ta "" Ta "1 if pane is synchronized" .It Li "pane_tabs" Ta "" Ta "Pane tab positions" .It Li "pane_title" Ta "#T" Ta "Title of pane" .It Li "pane_top" Ta "" Ta "Top of pane" .It Li "pane_tty" Ta "" Ta "Pseudo terminal of pane" .It Li "pane_width" Ta "" Ta "Width of pane" .It Li "pid" Ta "" Ta "Server PID" .It Li "rectangle_toggle" Ta "" Ta "1 if rectangle selection is activated" .It Li "scroll_position" Ta "" Ta "Scroll position in copy mode" .It Li "scroll_region_lower" Ta "" Ta "Bottom of scroll region in pane" .It Li "scroll_region_upper" Ta "" Ta "Top of scroll region in pane" .It Li "selection_present" Ta "" Ta "1 if selection started in copy mode" .It Li "session_activity" Ta "" Ta "Time of session last activity" .It Li "session_alerts" Ta "" Ta "List of window indexes with alerts" .It Li "session_attached" Ta "" Ta "Number of clients session is attached to" .It Li "session_created" Ta "" Ta "Time session created" .It Li "session_format" Ta "" Ta "1 if format is for a session (not assuming the current)" .It Li "session_group" Ta "" Ta "Name of session group" .It Li "session_group_list" Ta "" Ta "List of sessions in group" .It Li "session_group_size" Ta "" Ta "Size of session group" .It Li "session_grouped" Ta "" Ta "1 if session in a group" .It Li "session_id" Ta "" Ta "Unique session ID" .It Li "session_last_attached" Ta "" Ta "Time session last attached" .It Li "session_many_attached" Ta "" Ta "1 if multiple clients attached" .It Li "session_name" Ta "#S" Ta "Name of session" .It Li "session_stack" Ta "" Ta "Window indexes in most recent order" .It Li "session_windows" Ta "" Ta "Number of windows in session" .It Li "socket_path" Ta "" Ta "Server socket path" .It Li "start_time" Ta "" Ta "Server start time" .It Li "version" Ta "" Ta "Server version" .It Li "window_active" Ta "" Ta "1 if window active" .It Li "window_activity" Ta "" Ta "Time of window last activity" .It Li "window_activity_flag" Ta "" Ta "1 if window has activity" .It Li "window_bell_flag" Ta "" Ta "1 if window has bell" .It Li "window_bigger" Ta "" Ta "1 if window is larger than client" .It Li "window_end_flag" Ta "" Ta "1 if window has the highest index" .It Li "window_flags" Ta "#F" Ta "Window flags" .It Li "window_format" Ta "" Ta "1 if format is for a window (not assuming the current)" .It Li "window_height" Ta "" Ta "Height of window" .It Li "window_id" Ta "" Ta "Unique window ID" .It Li "window_index" Ta "#I" Ta "Index of window" .It Li "window_last_flag" Ta "" Ta "1 if window is the last used" .It Li "window_layout" Ta "" Ta "Window layout description, ignoring zoomed window panes" .It Li "window_linked" Ta "" Ta "1 if window is linked across sessions" .It Li "window_name" Ta "#W" Ta "Name of window" .It Li "window_offset_x" Ta "" Ta "X offset into window if larger than client" .It Li "window_offset_y" Ta "" Ta "Y offset into window if larger than client" .It Li "window_panes" Ta "" Ta "Number of panes in window" .It Li "window_silence_flag" Ta "" Ta "1 if window has silence alert" .It Li "window_stack_index" Ta "" Ta "Index in session most recent stack" .It Li "window_start_flag" Ta "" Ta "1 if window has the lowest index" .It Li "window_visible_layout" Ta "" Ta "Window layout description, respecting zoomed window panes" .It Li "window_width" Ta "" Ta "Width of window" .It Li "window_zoomed_flag" Ta "" Ta "1 if window is zoomed" .It Li "wrap_flag" Ta "" Ta "Pane wrap flag" .El .Sh STYLES .Nm offers various options to specify the colour and attributes of aspects of the interface, for example .Ic status-style for the status line. In addition, embedded styles may be specified in format options, such as .Ic status-left-format , by enclosing them in .Ql #[ and .Ql \&] . .Pp A style may be the single term .Ql default to specify the default style (which may inherit from another option) or a space or comma separated list of the following: .Bl -tag -width Ds .It Ic fg=colour Set the foreground colour. The colour is one of: .Ic black , .Ic red , .Ic green , .Ic yellow , .Ic blue , .Ic magenta , .Ic cyan , .Ic white ; if supported the bright variants .Ic brightred , .Ic brightgreen , .Ic brightyellow ; .Ic colour0 to .Ic colour255 from the 256-colour set; .Ic default for the default colour; .Ic terminal for the terminal default colour; or a hexadecimal RGB string such as .Ql #ffffff . .It Ic bg=colour Set the background colour. .It Ic none Set no attributes (turn off any active attributes). .It Xo Ic bright (or .Ic bold ) , .Ic dim , .Ic underscore , .Ic blink , .Ic reverse , .Ic hidden , .Ic italics , .Ic overline , .Ic strikethrough , .Ic double-underscore , .Ic curly-underscore , .Ic dotted-underscore , .Ic dashed-underscore .Xc Set an attribute. Any of the attributes may be prefixed with .Ql no to unset. .It Xo Ic align=left (or .Ic noalign ) , .Ic align=centre , .Ic align=right .Xc Align text to the left, centre or right of the available space if appropriate. .It Ic fill=colour Fill the available space with a background colour if appropriate. .It Xo Ic list=on , .Ic list=focus , .Ic list=left-marker , .Ic list=right-marker , .Ic nolist .Xc Mark the position of the various window list components in the .Ic status-format option: .Ic list=on marks the start of the list; .Ic list=focus is the part of the list that should be kept in focus if the entire list won't fit in the available space (typically the current window); .Ic list=left-marker and .Ic list=right-marker mark the text to be used to mark that text has been trimmed from the left or right of the list if there is not enough space. .It Xo Ic range=left , .Ic range=right , .Ic range=window|X , .Ic norange .Xc Mark a range in the .Ic status-format option. .Ic range=left and .Ic range=right are the text used for the .Ql StatusLeft and .Ql StatusRight mouse keys. .Ic range=window|X is the range for a window passed to the .Ql Status mouse key, where .Ql X is a window index. .El .Pp Examples are: .Bd -literal -offset indent fg=yellow bold underscore blink bg=black,fg=default,noreverse .Ed .Sh NAMES AND TITLES .Nm distinguishes between names and titles. Windows and sessions have names, which may be used to specify them in targets and are displayed in the status line and various lists: the name is the .Nm identifier for a window or session. Only panes have titles. A pane's title is typically set by the program running inside the pane using an escape sequence (like it would set the .Xr xterm 1 window title in .Xr X 7 ) . Windows themselves do not have titles - a window's title is the title of its active pane. .Nm itself may set the title of the terminal in which the client is running, see the .Ic set-titles option. .Pp A session's name is set with the .Ic new-session and .Ic rename-session commands. A window's name is set with one of: .Bl -enum -width Ds .It A command argument (such as .Fl n for .Ic new-window or .Ic new-session ) . .It An escape sequence (if the .Ic allow-rename option is turned on): .Bd -literal -offset indent $ printf '\e033kWINDOW_NAME\e033\e\e' .Ed .It Automatic renaming, which sets the name to the active command in the window's active pane. See the .Ic automatic-rename option. .El .Pp When a pane is first created, its title is the hostname. A pane's title can be set via the title setting escape sequence, for example: .Bd -literal -offset indent $ printf '\e033]2;My Title\e033\e\e' .Ed .Pp It can also be modified with the .Ic select-pane .Fl T command. .Sh GLOBAL AND SESSION 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 gs .Op Fl t Ar target-session .Op Ar variable .Xc .D1 (alias: Ic showenv ) Display the environment for .Ar target-session or the global environment with .Fl g . If .Ar variable is omitted, all variables are shown. Variables removed from the environment are prefixed with .Ql - . If .Fl s is used, the output is formatted as a set of Bourne shell commands. .El .Sh STATUS LINE .Nm includes an optional status line which is displayed in the bottom line of each terminal. .Pp By default, the status line is enabled and one line in height (it may be disabled or made multiple lines 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 title of the active pane in double quotes; and the time and date. .Pp Each line of the status line is configured with the .Ic status-format option. The default 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 activity is monitored and activity has been detected." .It Li "\&!" Ta "Window bells are monitored and a bell has occurred in the window." .It Li "~" Ta "The window has been silent for the monitor-silence interval." .It Li "M" Ta "The window contains the marked pane." .It Li "Z" Ta "The window's active pane is zoomed." .El .Pp The # symbol relates to the .Ic monitor-activity window option. The window name is printed in inverted colours if an alert (bell, activity or silence) is present. .Pp The colour and attributes of the status line may be configured, the entire status line using the .Ic status-style session option and individual windows using the .Ic window-status-style window option. .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 1Ni .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 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, 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 . .Ql %%% is like .Ql %% but any quotation marks are escaped. .Pp .Fl 1 makes the prompt only accept one key press, in this case the resulting input is a single character. .Fl N makes the prompt only accept numeric key presses. .Fl i executes the command every time the prompt input changes instead of when the user exits the command prompt. .Pp The following keys have a special meaning in the command prompt, depending on the value of the .Ic status-keys option: .Bl -column "FunctionXXXXXXXXXXXXXXXXXXXXXXXXX" "viXXXX" "emacsX" -offset indent .It Sy "Function" Ta Sy "vi" Ta Sy "emacs" .It Li "Cancel command prompt" Ta "Escape" Ta "Escape" .It Li "Delete from cursor to start of word" Ta "" Ta "C-w" .It Li "Delete entire command" Ta "d" Ta "C-u" .It Li "Delete from cursor to end" Ta "D" Ta "C-k" .It Li "Execute command" Ta "Enter" Ta "Enter" .It Li "Get next command from history" Ta "" Ta "Down" .It Li "Get previous command from history" Ta "" Ta "Up" .It Li "Insert top paste buffer" Ta "p" Ta "C-y" .It Li "Look for completions" Ta "Tab" Ta "Tab" .It Li "Move cursor left" Ta "h" Ta "Left" .It Li "Move cursor right" Ta "l" Ta "Right" .It Li "Move cursor to end" Ta "$" Ta "C-e" .It Li "Move cursor to next word" Ta "w" Ta "M-f" .It Li "Move cursor to previous word" Ta "b" Ta "M-b" .It Li "Move cursor to start" Ta "0" Ta "C-a" .It Li "Transpose characters" Ta "" Ta "C-t" .El .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-menu .Op Fl c Ar target-client .Op Fl t Ar target-pane .Op Fl T Ar title .Op Fl x Ar position .Op Fl y Ar position .Ar name .Ar key .Ar command .Ar ... .Xc .D1 (alias: Ic menu ) Display a menu on .Ar target-client . .Ar target-pane gives the target for any commands run from the menu. .Pp A menu is passed as a series of arguments: first the menu item name, second the key shortcut (or empty for none) and third the command to run when the menu item is chosen. The name and command are formats, see the .Sx FORMATS and .Sx STYLES sections. If the name begins with a hyphen (-), then the item is disabled (shown dim) and may not be chosen. The name may be empty for a separator line, in which case both the key and command should be omitted. .Pp .Fl T is a format for the menu title (see .Sx FORMATS ) . .Pp .Fl x and .Fl y give the position of the menu. Both may be a row or column number, or one of the following special values: .Bl -column "XXXXX" "XXXX" -offset indent .It Sy "Value" Ta Sy "Flag" Ta Sy "Meaning" .It Li "R" Ta Fl x Ta "The right side of the terminal" .It Li "P" Ta "Both" Ta "The bottom left of the pane" .It Li "M" Ta "Both" Ta "The mouse position" .It Li "W" Ta Fl x Ta "The window position on the status line" .It Li "S" Ta Fl y Ta "The line above or below the status line" .El .Pp Each menu consists of items followed by a key shortcut shown in brackets. If the menu is too large to fit on the terminal, it is not displayed. Pressing the key shortcut chooses the corresponding item. If the mouse is enabled and the menu is opened from a mouse key binding, releasing the mouse button with an item selected will choose that item. The following keys are also available: .Bl -column "Key" "Function" -offset indent .It Sy "Key" Ta Sy "Function" .It Li "Enter" Ta "Choose selected item" .It Li "Up" Ta "Select previous item" .It Li "Down" Ta "Select next item" .It Li "q" Ta "Exit menu" .El .It Xo Ic display-message .Op Fl aIpv .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 described in the .Sx FORMATS section; information is taken from .Ar target-pane if .Fl t is given, otherwise the active pane. .Pp .Fl v prints verbose logging as the format is parsed and .Fl a lists the format variables and their values. .Pp .Fl I forwards any input read from stdin to the empty pane given by .Ar target-pane . .El .Sh BUFFERS .Nm maintains a set of named .Em paste buffers . Each buffer may be either explicitly or automatically named. Explicitly named buffers are named when created with the .Ic set-buffer or .Ic load-buffer commands, or by renaming an automatically named buffer with .Ic set-buffer .Fl n . Automatically named buffers are given a name such as .Ql buffer0001 , .Ql buffer0002 and so on. When the .Ic buffer-limit option is reached, the oldest automatically named buffer is deleted. Explicitly named buffers are not subject to .Ic buffer-limit and may be deleted with .Ic delete-buffer command. .Pp Buffers may be added using .Ic copy-mode or the .Ic set-buffer and .Ic load-buffer commands, and pasted into a window using the .Ic paste-buffer command. If a buffer command is used and no buffer is specified, the most recently added automatically named buffer is assumed. .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 NZ .Op Fl F Ar format .Op Fl f Ar filter .Op Fl O Ar sort-order .Op Fl t Ar target-pane .Op Ar template .Xc Put a pane into buffer mode, where a buffer may be chosen interactively from a list. .Fl Z zooms the pane. The following keys may be used in buffer mode: .Bl -column "Key" "Function" -offset indent .It Sy "Key" Ta Sy "Function" .It Li "Enter" Ta "Paste selected buffer" .It Li "Up" Ta "Select previous buffer" .It Li "Down" Ta "Select next buffer" .It Li "C-s" Ta "Search by name or content" .It Li "n" Ta "Repeat last search" .It Li "t" Ta "Toggle if buffer is tagged" .It Li "T" Ta "Tag no buffers" .It Li "C-t" Ta "Tag all buffers" .It Li "p" Ta "Paste selected buffer" .It Li "P" Ta "Paste tagged buffers" .It Li "d" Ta "Delete selected buffer" .It Li "D" Ta "Delete tagged buffers" .It Li "f" Ta "Enter a format to filter items" .It Li "O" Ta "Change sort order" .It Li "v" Ta "Toggle preview" .It Li "q" Ta "Exit mode" .El .Pp After a buffer is chosen, .Ql %% is replaced by the buffer name in .Ar template and the result executed as a command. If .Ar template is not given, "paste-buffer -b '%%'" is used. .Pp .Fl O specifies the initial sort order: one of .Ql time , .Ql name or .Ql size . .Fl f specifies an initial filter: the filter is a format - if it evaluates to zero, the item in the list is not shown, otherwise it is shown. If a filter would lead to an empty list, it is ignored. .Fl F specifies the format for each item in the list. .Fl N starts without the preview. This command works only if at least one client is attached. .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-name .D1 (alias: Ic deleteb ) Delete the buffer named .Ar buffer-name , or the most recently added automatically named buffer if not specified. .It Xo Ic list-buffers .Op Fl F Ar format .Xc .D1 (alias: Ic lsb ) List the global buffers. For the meaning of the .Fl F flag, see the .Sx FORMATS section. .It Xo Ic load-buffer .Op Fl b Ar buffer-name .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 dpr .Op Fl b Ar buffer-name .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. 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). If .Fl p is specified, paste bracket control codes are inserted around the buffer if the application has requested bracketed paste mode. .It Xo Ic save-buffer .Op Fl a .Op Fl b Ar buffer-name .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 a .Op Fl b Ar buffer-name .Op Fl n Ar new-buffer-name .Ar data .Xc .D1 (alias: Ic setb ) Set the contents of the specified buffer to .Ar data . The .Fl a option appends to rather than overwriting the buffer. The .Fl n option renames the buffer to .Ar new-buffer-name . .It Xo Ic show-buffer .Op Fl b Ar buffer-name .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 Xo Ic if-shell .Op Fl bF .Op Fl t Ar target-pane .Ar shell-command command .Op Ar command .Xc .D1 (alias: Ic if ) Execute the first .Ar command if .Ar shell-command returns success or the second .Ar command otherwise. Before being executed, .Ar shell-command is expanded using the rules specified in the .Sx FORMATS section, including those relevant to .Ar target-pane . With .Fl b , .Ar shell-command is run in the background. .Pp If .Fl F is given, .Ar shell-command is not executed but considered success if neither empty nor zero (after formats are expanded). .It Ic lock-server .D1 (alias: Ic lock ) Lock each client individually by running the command specified by the .Ic lock-command option. .It Xo Ic run-shell .Op Fl b .Op Fl t Ar target-pane .Ar shell-command .Xc .D1 (alias: Ic run ) Execute .Ar shell-command in the background without creating a window. Before being executed, shell-command is expanded using the rules specified in the .Sx FORMATS section. With .Fl b , the command is run in the background. After it finishes, any output to stdout is displayed in copy mode (in the pane specified by .Fl t or the current pane if omitted). If the command doesn't return success, the exit status is also displayed. .It Xo Ic wait-for .Op Fl L | S | U .Ar channel .Xc .D1 (alias: Ic wait ) When used without options, prevents the client from exiting until woken using .Ic wait-for .Fl S with the same channel. When .Fl L is used, the channel is locked and any clients that try to lock the same channel are made to wait until the channel is unlocked with .Ic wait-for .Fl U . .El .Sh TERMINFO EXTENSIONS .Nm understands some unofficial extensions to .Xr terminfo 5 : .Bl -tag -width Ds .It Em Cs , 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 \&Smol Enable the overline attribute. The capability is usually SGR 53 and can be added to .Ic terminal-overrides as: .Bd -literal -offset indent Smol=\eE[53m .Ed .It Em \&Smulx Set a styled underscore. The single parameter is one of: 0 for no underscore, 1 for normal underscore, 2 for double underscore, 3 for curly underscore, 4 for dotted underscore and 5 for dashed underscore. The capability can typically be added to .Ic terminal-overrides as: .Bd -literal -offset indent Smulx=\eE[4::%p1%dm .Ed .It Em \&Setulc Set the underscore colour. The argument is (red * 65536) + (green * 256) + blue where each is between 0 and 255. The capability can typically be added to .Ic terminal-overrides as: .Bd -literal -offset indent Setulc=\eE[58::2::%p1%{65536}%/%d::%p1%{256}%/%{255}%&%d::%p1%{255}%&%d%;m .Ed .It Em \&Ss , Se Set or reset 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 Se is not set, \&Ss with argument 0 will be used to reset the cursor style instead. .It Em \&Tc Indicate that the terminal supports the .Ql direct colour RGB escape sequence (for example, \ee[38;2;255;255;255m). .Pp If supported, this is used for the initialize colour escape sequence (which may be enabled by adding the .Ql initc and .Ql ccc capabilities to the .Nm .Xr terminfo 5 entry). .It Em \&Ms 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 CONTROL MODE .Nm offers a textual interface called .Em control mode . This allows applications to communicate with .Nm using a simple text-only protocol. .Pp In control mode, a client sends .Nm commands or command sequences terminated by newlines on standard input. Each command will produce one block of output on standard output. An output block consists of a .Em %begin line followed by the output (which may be empty). The output block ends with a .Em %end or .Em %error . .Em %begin and matching .Em %end or .Em %error have two arguments: an integer time (as seconds from epoch) and command number. For example: .Bd -literal -offset indent %begin 1363006971 2 0: ksh* (1 panes) [80x24] [layout b25f,80x24,0,0,2] @2 (active) %end 1363006971 2 .Ed .Pp The .Ic refresh-client .Fl C command may be used to set the size of a client in control mode. .Pp In control mode, .Nm outputs notifications. A notification will never occur inside an output block. .Pp The following notifications are defined: .Bl -tag -width Ds .It Ic %client-session-changed Ar client Ar session-id Ar name The client is now attached to the session with ID .Ar session-id , which is named .Ar name . .It Ic %exit Op Ar reason The .Nm client is exiting immediately, either because it is not attached to any session or an error occurred. If present, .Ar reason describes why the client exited. .It Ic %layout-change Ar window-id Ar window-layout Ar window-visible-layout Ar window-flags The layout of a window with ID .Ar window-id changed. The new layout is .Ar window-layout . The window's visible layout is .Ar window-visible-layout and the window flags are .Ar window-flags . .It Ic %output Ar pane-id Ar value A window pane produced output. .Ar value escapes non-printable characters and backslash as octal \\xxx. .It Ic %pane-mode-changed Ar pane-id The pane with ID .Ar pane-id has changed mode. .It Ic %session-changed Ar session-id Ar name The client is now attached to the session with ID .Ar session-id , which is named .Ar name . .It Ic %session-renamed Ar name The current session was renamed to .Ar name . .It Ic %session-window-changed Ar session-id Ar window-id The session with ID .Ar session-id changed its active window to the window with ID .Ar window-id . .It Ic %sessions-changed A session was created or destroyed. .It Ic %unlinked-window-add Ar window-id The window with ID .Ar window-id was created but is not linked to the current session. .It Ic %window-add Ar window-id The window with ID .Ar window-id was linked to the current session. .It Ic %window-close Ar window-id The window with ID .Ar window-id closed. .It Ic %window-pane-changed Ar window-id Ar pane-id The active pane in the window with ID .Ar window-id changed to the pane with ID .Ar pane-id . .It Ic %window-renamed Ar window-id Ar name The window with ID .Ar window-id was renamed to .Ar name . .El .Sh ENVIRONMENT When .Nm is started, it inspects the following environment variables: .Bl -tag -width LC_CTYPE .It Ev EDITOR If the command specified in this variable contains the string .Ql vi and .Ev VISUAL is unset, use vi-style key bindings. Overridden by the .Ic mode-keys and .Ic status-keys options. .It Ev HOME The user's login directory. If unset, the .Xr passwd 5 database is consulted. .It Ev LC_CTYPE The character encoding .Xr locale 1 . It is used for two separate purposes. For output to the terminal, UTF-8 is used if the .Fl u option is given or if .Ev LC_CTYPE contains .Qq UTF-8 or .Qq UTF8 . Otherwise, only ASCII characters are written and non-ASCII characters are replaced with underscores .Pq Ql _ . For input, .Nm always runs with a UTF-8 locale. If en_US.UTF-8 is provided by the operating system it is used and .Ev LC_CTYPE is ignored for input. Otherwise, .Ev LC_CTYPE tells .Nm what the UTF-8 locale is called on the current system. If the locale specified by .Ev LC_CTYPE is not available or is not a UTF-8 locale, .Nm exits with an error message. .It Ev LC_TIME The date and time format .Xr locale 1 . It is used for locale-dependent .Xr strftime 3 format specifiers. .It Ev PWD The current working directory to be set in the global environment. This may be useful if it contains symbolic links. If the value of the variable does not match the current working directory, the variable is ignored and the result of .Xr getcwd 3 is used instead. .It Ev SHELL The absolute path to the default shell for new windows. See the .Ic default-shell option for details. .It Ev TMUX_TMPDIR The parent directory of the directory containing the server sockets. See the .Fl L option for details. .It Ev VISUAL If the command specified in this variable contains the string .Ql vi , use vi-style key bindings. Overridden by the .Ic mode-keys and .Ic status-keys options. .El .Sh FILES .Bl -tag -width "@SYSCONFDIR@/tmux.confXXX" -compact .It Pa ~/.tmux.conf Default .Nm configuration file. .It Pa @SYSCONFDIR@/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-style 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 nicholas.marriott@gmail.com