tmate-1.8.10/000077500000000000000000000000001242461015400127125ustar00rootroot00000000000000tmate-1.8.10/.gitignore000066400000000000000000000002461242461015400147040ustar00rootroot00000000000000*.o *~ *.diff *.patch *.core core tags .deps/ aclocal.m4 autom4te.cache/ config.log config.status etc/ tmux Makefile Makefile.in configure tmate cscope.* ctags *.log tmate-1.8.10/.mailmap000066400000000000000000000023301242461015400143310ustar00rootroot00000000000000Bob Beck beck Igor Sobrado sobrado Jacek Masiulaniec jacekm Jason McIntyre jcm Joel Sing jsing Marc Espie espie Matthew Dempsky matthew Matthias Kilian kili Matthieu Herrb matthieu Miod Vallat miod Nicholas Marriott nicm Nicholas Marriott no_author Okan Demirmen okan Philip Guenther guenther Pierre-Yves Ritschard pyr Ray Lai ray Ryan McBride mcbride Stefan Sperling stsp Stuart Henderson sthen Ted Unangst tedu Theo Deraadt deraadt Thomas Adam Thomas William Yodlowsky william tmate-1.8.10/CHANGES000066400000000000000000002304471242461015400137170ustar00rootroot00000000000000CHANGES 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. * Changes panes can now emit focus notifications for certain applications which use those. * run-shell and if-shell now accept format placeholders. * 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 Sept 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-separataed 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. $Id$ LocalWords: showw utf UTF fulvio ciriaco joshe OSC APC gettime abc DEF OA clr LocalWords: rivo nurges lscm Erdely eol smysession mysession ek dstname RB ms LocalWords: dstidx srcname srcidx winlink lsw nabc sabc Exp Tiago Cunha dch LocalWords: setw Chisnall renamew merdely eg Maier newname selectw neww Gass tmate-1.8.10/FAQ000066400000000000000000000411141242461015400132450ustar00rootroot00000000000000tmux frequently asked questions ****************************************************************************** * PLEASE NOTE: most display problems are due to incorrect TERM! Before * * reporting problems make SURE that TERM settings are correct inside and * * outside tmux. * * * * Inside tmux TERM must be "screen" or similar (such as "screen-256color"). * * Don't bother reporting problems where it isn't! * * * * Outside, it must match your terminal: particularly, use "rxvt" for rxvt * * and derivatives. * ****************************************************************************** * How is tmux different from GNU screen? tmux and GNU screen have many similarities. Some of the main differences I am aware of are (bearing in mind I haven't used screen for a few years now): - tmux uses a client-server model. Each server has single Unix domain socket in /tmp and within one server there are multiple sessions which may be attached to multiple clients (terminals). This has advantages, notably: windows may be linked simultaneously to multiple sessions; windows may be moved freely between sessions; and a client may be switched between sessions easily (C-b D). There is one major disadvantage: if the server crashes, game over, all sessions die. In practice, however, tmux is quite stable and gets more so as people report any bugs they hit :-). This model is different from screen, where typically each new screen instance is independent. tmux supports the same behaviour by using multiple servers with the -L option but it is not typically recommended. - Different command interfaces. One of the goals of tmux is that the shell should be easily usable as a scripting language - almost all tmux commands can be used from the shell and behave identically whether used from the shell, from a key binding or from the command prompt. Personally I also find tmux's command interface much more consistent and clearer, but this is subjective. - tmux calls window names (what you see in the status line) "names", screen calls them "titles". - tmux has a multiple paste buffers. Not a major one but comes in handy quite a lot. - tmux supports automatically renaming windows to the running application without gross hacks using escape sequences. Its even on by default. - tmux has a choice of vi or emacs key layouts. Again, not major, but I use emacs so if tmux did support only one key set it would be emacs and then all the vi users would get humpy. Key bindings may be completely reconfigured in any case. - tmux has an option to limit the window size. - tmux has search in windows (C-b f). - The window split (pane) model is different. tmux has two objects, windows and panes; screen has just windows. This difference has several implications: * In screen you can have a window appear in several layouts, in tmux a pane can only be in one window (fixing this is a big todo item but quite invasive). * tmux layouts are immutable and do not get changed unless you modify them. * In tmux, all panes are closed when you kill a window. * tmux panes do not have individual names, titles and so on. I think tmux's model is much easier to manage and navigate within a window, but breaking panes off from and joining them to windows is more clumsy. tmux also has support for preset pane layouts. - tmux's status line syntax is more readable and easier to use. I think it'd be hard for anyone to argue with this. tmux doesn't support running a command constantly and always using the last line of its output, commands must be run again each time. - tmux has modern, easily extended code. Again hard to argue screen is better if you have looked at the code. - tmux depends on libevent. I don't see this as a disadvantage: libevent is small and portable, and on modern systems with current package management systems dependencies are not an issue. libevent brings advantages in code simplicity and performance. - screen allows the window to be bigger than the terminal and can pan around it. tmux limits the size to the largest attached client. This is a big todo item for tmux but it is not trivial. - screen has builtin serial and telnet support; this is bloat and is unlikely to be added to tmux. - screen has support for updating utmp. Nobody has really come up with a clean, portable way to do this without making tmux setuid or setgid yet. - Environment handling is different. - tmux tends to be more demanding on the terminal so tends to show up terminal and application bugs which screen does not. - screen has wider platform support, for example IRIX, and for odd terminals. * I found a bug! What do I do? Please send bug reports by email to nicm@users.sourceforge.net or tmux-users@lists.sourceforge.net. Please include as much of the following information as possible: - the version of tmux you are running; - the operating system you are using and its version; - the terminal emulator you are using and the TERM setting when tmux was started; - a description of the problem; - if the problem is repeatable, the steps to repeat the problem; - for screen corruption issues, a screenshot and the output of "infocmp $TERM" from outside tmux are often very useful. * Why doesn't tmux do $x? Please send feature requests by email to nicm@users.sourceforge.net. * Why do you use the screen terminal description inside tmux? It sucks. It is already widely available. It is planned to change to something else such as xterm-xfree86 at some point, if possible. * I don't see any colour in my terminal! Help! On some platforms, common terminal descriptions such as xterm do not include colour. screen ignores this, tmux does not. If the terminal emulator in use supports colour, use a value for TERM which correctly lists this, such as xterm-color. * tmux freezes my terminal when I attach to a session. I even have to kill -9 the shell it was started from to recover! Some consoles really really don't like attempts to set the window title. Tell tmux not to do this by turning off the "set-titles" option (you can do this in .tmux.conf): set -g set-titles off If this doesn't fix it, send a bug report. * Why is C-b the prefix key? How do I change it? The default key is C-b because the prototype of tmux was originally developed inside screen and C-b was chosen not to clash with the screen meta key. It also has the advantage of not interfering with the use of C-a for start-of-line in emacs and the shell (although it does interfere with previous-character). Changing is simple: change the "prefix-key" option, and - if required - move the binding of the "send-prefix" command from C-b (C-b C-b sends C-b by default) to the new key. For example: set -g prefix C-a unbind C-b bind C-a send-prefix * How do I use UTF-8? When running tmux in a UTF-8 capable terminal, UTF-8 must be turned on in tmux; as of release 0.9, tmux attempts to autodetect a UTF-8-capable terminal by checking the LC_ALL, LC_CTYPE and LANG environment variables. list-clients may be used to check if this is detected correctly; if not, the -u command-line flag may be specified when creating or attaching a client to a tmux session: $ tmux -u new Since the 1.0 release, tmux will turn on UTF-8 related options automatically (ie status-utf8, and utf8) if the above conditions are met. * How do I use a 256 colour terminal? Provided the underlying terminal supports 256 colours, it is usually sufficient to add the following to ~/.tmux.conf: set -g default-terminal "screen-256color" Note that some platforms do not support "screen-256color" ("infocmp screen-256color" will return an error) - in this case see the next entry in this FAQ. tmux attempts to detect a 256 colour terminal both by looking at the colors terminfo entry and by looking for the string "256col" in the TERM environment variable. If both these methods fail, the -2 flag may be passed to tmux when attaching to a session to indicate the terminal supports 256 colours. * vim or $otherprogram doesn't display 256 colours. What's up? Some programs attempt to detect the number of colours a terminal is capable of by checking the colors terminfo or Co termcap entry. However, this is not reliable, and in any case is missing from the "screen" terminal description used inside tmux. There are two options (aside from using "screen-256color") to allow programs to recognise they are running on a 256-colour terminal inside tmux: - Manually force the application to use 256 colours always or if TERM is set to screen. For vim, you can do this by overriding the t_Co option, see http://vim.wikia.com/wiki/256_colors_in_vim. - Creating a custom terminfo file that includes colors#256 in ~/.terminfo and using it instead. These may be compiled with tic(1). * How do I make Ctrl-PgUp and Ctrl-PgDn work in vim? tmux supports passing through ctrl (and where supported by the client terminal, alt and shift) modifiers to function keys using xterm(1)-style key sequences. This may be enabled per window, or globally with the tmux command: setw -g xterm-keys on Because the TERM variable inside tmux must be set to "screen", vim will not automatically detect these keys are available; however, the appropriate key sequences can be overridden in .vimrc using the following: if &term == "screen" set t_kN=^[[6;*~ set t_kP=^[[5;*~ endif And similarly for any other keys for which modifiers are desired. Please note that the "xterm-keys" setting may affect other programs, in the same way as running them in a standard xterm; for example most shells do not expect to receive xterm(1)-style key sequences so this setting may prevent keys such as ctrl-left and ctrl-right working correctly. tmux also passes through the ctrl (bit 5 set, for example ^[[5~ to ^[[5^) modifier in non-xterm(1) mode; it may be possible to configure vim to accept these, an example of how to do so would be welcome. vim users may also want to set the "ttyfast" option inside tmux. * Why doesn't elinks set the window title inside tmux? There isn't a way to detect if a terminal supports setting the window title, so elinks attempts to guess by looking at the environment. Rather than looking for TERM=screen, it uses the STY variable to detect if it is running in screen; tmux does not use this so the check fails. A workaround is to set STY before running elinks. The following shell function does this, and also clears the window title on exit (elinks, for some strange reason, sets it to the value of TERM): elinks() { STY= `which elinks` $* echo -ne \\033]0\;\\007; } * What is the proper way to escape characters with #(command)? When using the #(command) construction to include the output from a command in the status line, the command will be parsed twice. First, when it's read by the configuration file or the command-prompt parser, and second when the status line is being drawn and the command is passed to the shell. For example, to echo the string "(test)" to the status line, either single or double quotes could be used: set -g status-right "#(echo \\\\(test\\\\))" set -g status-right '#(echo \\\(test\\\))' In both cases, the status-right option will be set to the string "#(echo \\(test\\))" and the command executed will be "echo \(test\)". * tmux uses too much CPU. What do I do? Automatic window renaming may use a lot of CPU, particularly on slow computers: if this is a problem, turn it off with "setw -g automatic-rename off". If this doesn't fix it, please report the problem. * I use PuTTY and my tmux window pane separators are all qqqqqqqqq's! PuTTY is using a character set translation that doesn't support ACS line drawing. With a Unicode font, try setting PuTTY to use a different translation on the Window -> Translation configuration page. For example, change UTF-8 to ISO-8859-1 or CP437. It may also be necessary to adjust the way PuTTY treats line drawing characters in the lower part of the same configuration page. * What is the best way to display the load average? Why no #L? It isn't possible to get the load average portably in code and it is preferable not to add portability goop. The following works on at least Linux, *BSD and OS X: uptime|awk '{split(substr($0, index($0, "load")), a, ":"); print a[2]}' * How do I attach the same session to multiple clients but with a different current window, like screen -x? One or more of the windows can be linked into multiple sessions manually with link-window, or a grouped session with all the windows can be created with new-session -t. * Ctrl and arrow keys doesn't work in putty! What do I do? putty inverts the sense of the cursor key mode on ctrl, which is a bit hard for tmux to detect properly. To get ctrl keys right, change the terminfo settings so kUP5 (Ctrl-Up etc) are the adjusted versions, and disable smkx/rmkx so tmux doesn't change the mode. For example with this line in .tmux.conf (assuming you have TERM set to xterm): set -g terminal-overrides "xterm*:kLFT5=\eOD:kRIT5=\eOC:kUP5=\eOA:kDN5=\eOB:smkx@:rmkx@" Note that this will only work in tmux 1.2 and above. * How can I blank the tmux window? GNU screen has a feature whereby it will blank the screen after a period of inactivity. To do the same thing in tmux, use the lock-command setting, for example (with GNU bash): set -g lock-command 'tput civis && read -s -n1' This will remove the cursor and tell the shell to quit once a key has been pressed. For zsh, use "read -s -k1". In addition, it's possible to have both blanking and locking (for instance via lock(1) or vlock(1)) by using the following: bind x set lock-command '/usr/bin/vlock' \; lock-client \; set lock-command 'tput civis && read -s -n1' * vim displays reverse video instead of italics, while less displays italics (or just regular text) instead of reverse. What's wrong? Screen's terminfo description lacks italics mode and has standout mode in its place, but using the same escape sequence that urxvt uses for italics. This means applications (like vim) looking for italics will not find it and might turn to reverse in its place, while applications (like less) asking for standout will end up with italics instead of reverse. To make applications aware that tmux supports italics and to use a proper escape sequence for standout, you'll need to create a new terminfo file with modified sgr, smso, rmso, sitm and ritm entries: $ mkdir $HOME/.terminfo/ $ screen_terminfo="screen" $ infocmp "$screen_terminfo" | sed \ -e 's/^screen[^|]*|[^,]*,/screen-it|screen with italics support,/' \ -e 's/%?%p1%t;3%/%?%p1%t;7%/' \ -e 's/smso=[^,]*,/smso=\\E[7m,/' \ -e 's/rmso=[^,]*,/rmso=\\E[27m,/' \ -e '$s/$/ sitm=\\E[3m, ritm=\\E[23m,/' > /tmp/screen.terminfo $ tic /tmp/screen.terminfo And tell tmux to use it in ~/.tmux.conf: set -g default-terminal "screen-it" If your terminal supports 256 colors, use: $ screen_terminfo="screen-256color" instead of "screen". See the FAQ entry about 256 colors support for more info. Also note that tmux will still display reverse video on terminals that do not support italics. If your urxvt cannot display italics at all, make sure you have an italics capable font enabled, for example, add to ~/.Xdefaults: urxvt.italicFont: xft:Bitstream Vera Sans Mono:italic:autohint=true * How can I make tmux use my terminal's scrollback buffer? Normally, tmux enables the terminal's "alternate screen". Most terminals (such as xterm) do not save scrollback for the alternate screen. You might prefer tmux to use the normal screen, so it uses your terminal's scrollback buffer. This way, you can access the scrollback buffer as usual, for example using the mouse wheel - although there is no guarantee output inside tmux will always (or ever) be added to the scrollback. You can make tmux use the normal screen by telling it that your terminal does not have an alternate screen. Put the following in ~/.tmux.conf: set -g terminal-overrides 'xterm*:smcup@:rmcup@' Adjust if your $TERM does not start with xterm. tmux will still emulate the alternate screen for applications run under tmux, so you don't really lose anything with this setting. The only disadvantage is that when you exit tmux, it will not restore whatever was there before you started. * How do I see the default configuration? Show the default session options by starting a new tmux server with no configuration file: $ tmux -Lfoo -f/dev/null start\; show -g Or the default window options: $ tmux -Lfoo -f/dev/null start\; show -gw $Id$ tmate-1.8.10/Makefile.am000066400000000000000000000124561242461015400147560ustar00rootroot00000000000000# $Id$ # Obvious program stuff. bin_PROGRAMS = tmate dist_man1_MANS = tmate.1 # Distribution tarball options. EXTRA_DIST = \ CHANGES FAQ README TODO examples compat \ array.h compat.h tmux.h osdep-*.c dist-hook: grep "^#found_debug=" configure find $(distdir) -name .svn -type d|xargs rm -Rf # Preprocessor flags. CPPFLAGS += @XOPEN_DEFINES@ # glibc as usual does things ass-backwards and hides useful things by default, # so everyone has to add this. if IS_GLIBC CFLAGS += -D_GNU_SOURCE endif CFLAGS += -Wno-unused-parameter -Wno-unused-variable CFLAGS += -Ilibssh/include/ -Imsgpack/src CFLAGS += -rdynamic # for stack traces # Set flags for gcc. gcc4 whines abouts silly stuff so it needs slightly # different flags. if IS_GCC CFLAGS += -std=gnu99 if IS_DEBUG CFLAGS += -O0 -g CFLAGS += -Wno-long-long -Wall -W -Wnested-externs -Wformat=2 CFLAGS += -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations CFLAGS += -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare CFLAGS += -Wbad-function-cast -Winline -Wcast-align CPPFLAGS += -DDEBUG else CFLAGS += -O2 endif if IS_GCC4 CPPFLAGS += -iquote. -I/usr/local/include if IS_DEBUG CFLAGS += -Wno-pointer-sign endif else CPPFLAGS += -I. -I- -I/usr/local/include endif endif # Set flags for Solaris. if IS_SUNOS CPPFLAGS += -D_XPG4_2 -D__EXTENSIONS__ -D_POSIX_PTHREAD_SEMANTICS endif # Set flags for Sun CC. if IS_SUNCC CFLAGS += -erroff=E_EMPTY_DECLARATION endif # List of sources. dist_tmate_SOURCES = \ arguments.c \ attributes.c \ cfg.c \ client.c \ clock.c \ cmd-attach-session.c \ cmd-bind-key.c \ cmd-break-pane.c \ cmd-capture-pane.c \ cmd-choose-buffer.c \ cmd-choose-client.c \ cmd-choose-list.c \ cmd-choose-tree.c \ cmd-clear-history.c \ cmd-clock-mode.c \ cmd-command-prompt.c \ cmd-confirm-before.c \ cmd-copy-mode.c \ cmd-delete-buffer.c \ cmd-detach-client.c \ cmd-display-message.c \ cmd-display-panes.c \ cmd-find-window.c \ cmd-has-session.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-link-window.c \ cmd-list-buffers.c \ cmd-list-clients.c \ cmd-list-commands.c \ cmd-list-keys.c \ cmd-list-panes.c \ cmd-list-sessions.c \ cmd-list-windows.c \ cmd-list.c \ cmd-load-buffer.c \ cmd-lock-server.c \ cmd-move-window.c \ cmd-new-session.c \ cmd-new-window.c \ cmd-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-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-server-info.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-start-server.c \ cmd-string.c \ cmd-suspend-client.c \ cmd-swap-pane.c \ cmd-swap-window.c \ cmd-switch-client.c \ cmd-unbind-key.c \ cmd-unlink-window.c \ cmd-wait-for.c \ cmd.c \ colour.c \ control.c \ control-notify.c \ environ.c \ format.c \ grid-cell.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 \ mode-key.c \ names.c \ notify.c \ options-table.c \ options.c \ paste.c \ resize.c \ screen-redraw.c \ screen-write.c \ screen.c \ server-client.c \ server-fn.c \ server-window.c \ server.c \ session.c \ signal.c \ status.c \ tmate-debug.c \ tmate-ssh-client.c \ tmate-encoder.c \ tmate-decoder.c \ tmate-env.c \ tmate-msg.c \ tmate-session.c \ tmux.c \ tty-acs.c \ tty-keys.c \ tty-term.c \ tty.c \ utf8.c \ window-choose.c \ window-clock.c \ window-copy.c \ window.c \ xmalloc.c \ xterm-keys.c nodist_tmate_SOURCES = osdep-@PLATFORM@.c # Pile in all the compat/ stuff that is needed. if NO_FORKPTY nodist_tmate_SOURCES += compat/forkpty-@PLATFORM@.c endif if NO_IMSG nodist_tmate_SOURCES += compat/imsg.c compat/imsg-buffer.c endif if NO_CLOSEFROM nodist_tmate_SOURCES += compat/closefrom.c endif if NO_DAEMON nodist_tmate_SOURCES += compat/daemon.c endif if NO_SETENV nodist_tmate_SOURCES += compat/setenv.c endif if NO_STRLCAT nodist_tmate_SOURCES += compat/strlcat.c endif if NO_STRLCPY nodist_tmate_SOURCES += compat/strlcpy.c endif if NO_ASPRINTF nodist_tmate_SOURCES += compat/asprintf.c endif if NO_FGETLN nodist_tmate_SOURCES += compat/fgetln.c endif if NO_GETOPT nodist_tmate_SOURCES += compat/getopt.c endif if NO_STRCASESTR nodist_tmate_SOURCES += compat/strcasestr.c endif if NO_STRSEP nodist_tmate_SOURCES += compat/strsep.c endif if NO_VIS nodist_tmate_SOURCES += compat/vis.c compat/unvis.c endif if NO_STRTONUM nodist_tmate_SOURCES += compat/strtonum.c endif if NO_B64_NTOP nodist_tmate_SOURCES += compat/b64_ntop.c endif tmate_LDADD = \ libssh/build/src/libssh.a \ msgpack/src/.libs/libmsgpackc.a *.c: $(tmate_LDADD) libssh/build/src/libssh.a: cd libssh/build; ([ -f Makefile ] || cmake .. -DWITH_SFTP=OFF -DWITH_SERVER=OFF -DWITH_PCAP=OFF -DWITH_STATIC_LIB=ON -DWITH_GSSAPI=OFF) +make -C libssh/build ssh_static msgpack/src/.libs/libmsgpackc.a: cd msgpack; ([ -f Makefile ] || (./bootstrap && ./configure)) +make -C msgpack/src libmsgpackc.la .PHONY: libssh/build/src/libssh.a msgpack/src/.libs/libmsgpackc.a tmate-1.8.10/README000077700000000000000000000000001242461015400150442README.mdustar00rootroot00000000000000tmate-1.8.10/README-tmux000066400000000000000000000040441242461015400145670ustar00rootroot00000000000000Welcome to tmux! tmux is a "terminal multiplexer", it enables a number of terminals (or windows) to be accessed and controlled from a single terminal. tmux is intended to be a simple, modern, BSD-licensed alternative to programs such as GNU screen. This release runs on OpenBSD, FreeBSD, NetBSD, Linux and OS X and may still run on Solaris and AIX (although they haven't been tested in a while). Since the 1.2 release tmux depends on libevent. Download it from: http://www.monkey.org/~provos/libevent/ To build tmux from a release tarball, do: $ ./configure && make $ sudo make install To get and build the latest from version control: $ git clone git://git.code.sf.net/p/tmux/tmux-code tmux $ cd tmux $ sh autogen.sh $ ./configure && make For more information see https://sourceforge.net/scm/?type=git&group_id=200378 and http://git-scm.com. Patches should be sent by email to the mailing list at tmux-users@lists.sourceforge.net. For documentation on using tmux, see the tmux.1 manpage. It can be viewed from the source tree with: $ nroff -mdoc tmux.1|less Some common questions are answered in the FAQ file and a more extensive (but slightly out of date) guide is available in the OpenBSD FAQ at http://www.openbsd.org/faq/faq7.html#tmux. A rough todo list is in the TODO file and some example configurations and a Vim syntax file are in the examples directory. For debugging, running tmux with -v or -vv will generate server and client log files in the current directory. tmux mailing lists are available. Visit: https://sourceforge.net/mail/?group_id=200378 Bug reports, feature suggestions and especially code contributions are most welcome. Please send by email to: tmux-users@lists.sourceforge.net This file and the CHANGES, FAQ and TODO files are licensed under the ISC license. Files under examples/ remain copyright their authors unless otherwise stated in the file but permission has been received to distribute them with tmux. All other files have a license and copyright notice at their start. -- Nicholas Marriott $Id$ tmate-1.8.10/README.md000066400000000000000000000024261242461015400141750ustar00rootroot00000000000000tmate ===== What is it? ----------- Tmate is a fork of tmux. It provides an instant pairing solution. License ------- tmate is built on top of tmux, libssh and msgpack. Their respective licenses are in the sources. tmate is MIT licensed. Copyright (c) 2013 Nicolas Viennot 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. tmate-1.8.10/SYNCING000066400000000000000000000134251242461015400137540ustar00rootroot00000000000000Preamble ======== Tmux on SourceForge has two git repositories [1] "tmux-code" and "tmux-openbsd". Here's a description of them: * "tmux-code" is the portable version, the one which contains code for other operating systems, and autotools, etc., which isn't found or needed in the OpenBSD base system. * "tmux-openbsd" is the version of tmux in OpenBSD base system which provides the basis of the portable tmux version. Note: The "tmux-openbsd" repository is actually handled by "git cvsimport" running at 15 minute intervals, so a commit made to OpenBSD's tmux CVS repository will take at least that long to appear in this git repository. (It might take longer, depending on the CVS mirror used to import the OpenBSD code). It is assumed that the person doing the sync has read/write access to the tmux-code repository on SourceForge already. If you've never used git before, git tracks meta-data about the committer and the author, as part of a commit, hence: % git config [--global] user.name "Your name" % git config [--global] user.email "you@yourdomain.com" Note that, if you already have this in the global ~/.gitconfig option, then this will be used. Setting this per-repository would involve not using the "--global" flag above. If you wish to use the same credentials always, pass the "--global" option, as shown. This is a one-off operation once the repository has been cloned, assuming this information has ever been set before. Cloning repositories ==================== This involves having both tmux-code and tmux-openbsd cloned, as in: % cd /some/where/useful % git clone ssh://${USER}@git.code.sf.net/p/tmux/tmux % git clone ssh://${USER}@git.code.sf.net/p/tmux/tmux-openbsd Note that you do not need additional checkouts to manage the sync -- an existing clone of either repositories will suffice. So if you already have these checkouts existing, skip that. Adding in git-remotes ===================== Because the portable "tmux-code" git repository and the "tmux-openbsd" repository do not inherently share any history between each other, the history has been faked between them. This "faking of history" is something which has to be told to git for the purposes of comparing the "tmux" and "tmux-openbsd" repositories for syncing. To do this, we must reference the clone of the "tmux-openbsd" repository from the "tmux-code" repository, as shown by the following command: % cd /path/to/tmux-code % git remote add obsd-tmux file:///path/to/tmux-openbsd So that now, the remote "obsd-tmux" can be used to reference branches and commits from the "tmux-openbsd" repository, but from the context of the portable "tmux-code" repository, which makes sense because it's the "tmux" repository which will have the updates applied to them. Fetching updates ================ To ensure the latest commits from "tmux-openbsd" can be found from within "tmux-code", we have to ensure the "master" branch from "tmux-openbsd" is up-to-date first, and then reference that update in "tmux-code", as in: % cd /path/to/tmux-openbsd % git checkout master % git pull Then back in "tmux-code": % cd /path/to/tmux-code % git fetch obsd-tmux-code Creating the necessary branches =============================== Now that "tmux-code" can see commits and branches from "tmux-openbsd" by way of the remote name "obsd-tmux", we can now create the master branch from "tmux-openbsd" in the "tmux-code" repository: % git checkout -b obsd-master obsd-tmux/master Adding in the fake history points ================================= To tie both the "master" branch from "tmux-code" and the "obsd-master" branch from "tmux-openbsd" together, the fake history points added to the "tmux-code" repository need to be added. To do this, we must add an additional refspec line, as in: % cd /path/to/tmux-code % git config --add remote.origin.fetch '+refs/replace/*:refs/replace/*' % git fetch origin Performing the Sync =================== Make sure the "master" branch is checked out: % git checkout master The following will show commits on OpenBSD not yet synched with "tmux-code": % git log master..obsd-master From there, merge the result in, fixing up any conflicts which might arise. % git merge obsd-master Then ensure things look correct by BULDING the result of that sync: % make clean && ./autogen.sh && ./configure && make Compare the git merge result with what's on origin/master -- that is, check which commits you're about to push: % git log origin/master..master And if happy: % git push origin master Release tmux for next version ============================= 1. Comment the "found_debug=yes" line in configure.ac, since releases don't have debugging enabled, otherwise make(1) aborts when preparing the distribution. 2. Update and commit README and CHANGES. The former should be checked for anything outdated and updated with a list of things that might break upgrades and the latter should mention all the major changes since the last version. 3. Tag with: % git tag -a 1.X Where "1.X" is the next version. Push the tag out with: % git push 1.X 4. Build the tarball with make dist. Now that it's using autoconf there shouldn't be any weird files (such as the original and rejection files from patch(1)) but it doesn't hurt taking a quick look at it. 5. Split the release changes into a new file. This should be named tmux-$VERSION-readme to make sourceforge show it automagically in specific parts of the project page. 6. Upload the tarball and the above file. Make the tarball the default download by selecting all operating systems under the file details. 7. Run make update-index.html upload-index.html to replace %%VERSION%%. 8. Bump version in configure.ac and uncomment "found_debug=yes" to create a debug build by default. 9. Update freshmeat. [1] https://sourceforge.net/p/tmux/_list/git tmate-1.8.10/TODO000066400000000000000000000173701242461015400134120ustar00rootroot00000000000000NOTES ===== This file describes rough notes regarding ideas for potential future tmux development. It's not necessarily guaranteed that items in this TODO file will ever get implemented. It is asked therefore, that anyone thinking of undertaking a task in this TODO file, email tmux-users@lists.sf.net to discuss the feature. Thie file is split up between tmux user interface (UI) issues, and terminal compatibility issues. TMUX UI ISSUES ============== - implicitly add exec to the commands for new windows (switch to disable it)? - bring back detach-session to detach all clients on a session? - allow fnmatch for -c, so that you can, eg, detach all clients - garbage collect window history (100 lines at a time?) if it hasn't been used in $x time - flags to centre screen in window - activity/bell should be per-window not per-link? what if it is cur win in session not being watched? - should be able to move to a hidden pane and it would be moved into view. pane number in status line/top-right would be cool for this - support other mouse modes (highlight etc) and use it in copy mode - set-remain-on-exit is a bit of a hack, some way to do it generically? - would be nice to be able to use "--" to mark start of command w/ neww etc to avoid quoting - make command sequences more usable: don't require space after ;, handle errors better - choice and more mode would be better per client than per window? - hooks to which commands may be attached, for example: tmux add-hook "new-session" if-shell "[ -e $HOME/.tmux-session.conf ]" source-file $HOME/.tmux-session.conf - way to set socket path from config file - warts on current naming: - display-time but message-fg/bg/attr - list-* vs show-* - server-info - up-pane/down-pane/swap-pane -U/swap-pane -D vs next-*/previous-* - split-window -> split-pane?? - some way to force a screen to use the entire terminal even if it is forced to be smaller by other clients. pan smaller terminal? (like screen F) -- idea of a "view" onto a window, need base x/y offsets for redraw - commands should be able to succeed or fail and have || or && for command lists - some way to keep a command running continually and just use its last line of output - UTF-8 to a non-UTF-8 terminal should not be able to balls up the terminal - www/ruby-addressable; make regress - support esc-esc to quit in modes - fix ctrl+F1-F4 output. to what? - better utf8 support: window names, prompt input, message display - option to move copy mode indicator into status line - selection behaviour closer to vi in vi mode - live update: server started with -U connects to server, requests sessions and windows, receives fds - sort out inheriting config from shell on new sessions/windows: should pick up default-path/termios/etc from client if possible, else leave empty/default - link panes into multiple windows - bells should be passed between sessions with visual-bell etc sequence until its shell exits, to allow them to be used from the config file - better session sharing: create-socket command to create socket somewhere (-r flag for readonly) - multiline status line (no?) - support title stack, both internally and externally http://docs.freebsd.org/cgi/getmsg.cgi?fetch=1149299+0+archive/2010/freebsd-questions/20100207.freebsd-questions - some way to pad # stuff with spaces, #!2T maybe - a binding to "scroll down and exit at bottom" copy mode - some way to pass keystrokes in copy mode through to underlying window. why? - last window update time and # replacement for it for display-message - find-window across sessions - other ways to make session handling easier? - ' and " should be parsed the same (eg "\e" vs '\e') in config and command prompt? - command to toggle selection not to move it in copy-mode - audit of escape sequence support vs xterm - support binding keys to mouse (mouse-select-pane -> mouse-keys or something, mouse click == select-pane -t %%, mouse scroll up == copy-mode) - bind commands to key sequences? -- make it so ALL keys go through a table, first an implicit table in which C-b is the only default binding to a command that says "next key from $othertable" and so on. means -n can go away as well - monitor, bell etc should monitor /all/ panes in the window not just one - a history of commands that can be reversed (reverse member of each command, and a buffer) - info() when changing to same window - way to add dest for break-pane; maybe some easier way to unbreak-pane - case insensitive searching - incremental searching in copy mode. - configurable borders and empty space filler for when panes < window? - mouse-select-pane will screw up with !MODE_MOUSE_STANDARD (it sets the flag on w/o checking the others before calling tty_update_mode) - pass shell commands as argv rather than strings, allow them to be specified in commands without quotes - named buffers and allow gaps in the stack - monitor-activity is broken in several ways with multiple clients - monitor-activity should be more powerful (eg set a region) - maybe a way to put pane names instead of window names in status line - support for borderless panes - wait-for command 20130222153957.GY6782@yelena.nicm.ath.cx - last-pane across sessions - panes should have names like windows - command-prompt doesn't work if made read-only. why? - option to quote format eg #{session_name:quoted} - formats need conditions for >0 (for #P) - fetch full command line on !Linux, and add option to strip prefixes such as "sh " "/bin/sh " etc etc - synchronize-windows option - append to buffer in copy mode - way to paste w/o trailing whitespace - flag to switch-client to switch all clients - history of layouts and undo/redo flags to selectl - way to tag a layout as a number/name - optimize pane redraws, 20120318184853.GK10965@yelena.nicm.ath.cx - support multibyte key strings - allow commands to be executed when certain patterns in a screen are clicked on with the mouse - flag to make next/previous commands skip a window - way to do tmux command/run-shell from mode keys - send command to all windows - choose-pane command (augment choose-tree to do this?) - choose-mode and copy-mode are very similar. Perhaps make choose-mode a subset of copy-mode in that it inherits key-bindings and other traits but not all - add -c for new-session like new-window - flag to choose-* for sort order (eg sort windows/sessions/clients by last used time) - perhaps using formats (but what about numeric sort)? - instead of separate window and session options, just one master options list with each option having a type (window or session), then options on window, on session, and global. for window options we look window->session->global, and for session we look session->global - maybe keep last layout + size around and if size reverts just put it back - way to set hints/limits about pane size for resizing - revamp layouts: they are too complicated, should be more closely integrated, should support hints, layout sets should just be a special case of custom layouts, and we should support panes that are not attached to a cell at all. this could be the time to introduce panelink to replace layout_cell - run-shell/if-shell should support formats - attach should take a pane and select it as well as attaching - attach should have a flag to create session if it doesn't exist. or better new a flag to attach it TERMINAL ISSUES ================ - use a better termcap internally instead of screen, perhaps xterm - clear window title on exit (see using xterm title stack) - get it passing all the vttest tests that don't require resizing the terminal - support for bce - use screen-256color when started on 256 colour terminal? * We need a tmux terminfo entry to document the extensions we are using in upstream terminfo. Must NOT change (only add or remove) anything from TERM=screen so we can fallback! tmate-1.8.10/arguments.c000066400000000000000000000111231242461015400150610ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2010 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* Create an arguments set with no flags. */ struct args * args_create(int argc, ...) { struct args *args; va_list ap; int i; args = xcalloc(1, sizeof *args); if ((args->flags = bit_alloc(SCHAR_MAX)) == NULL) fatal("bit_alloc failed"); args->argc = argc; if (argc == 0) args->argv = NULL; else args->argv = xcalloc(argc, sizeof *args->argv); va_start(ap, argc); for (i = 0; i < argc; i++) args->argv[i] = xstrdup(va_arg(ap, char *)); va_end(ap); return (args); } /* Parse an argv and argc into a new argument set. */ struct args * args_parse(const char *template, int argc, char **argv) { struct args *args; char *ptr; int opt; args = xcalloc(1, sizeof *args); if ((args->flags = bit_alloc(SCHAR_MAX)) == NULL) fatal("bit_alloc failed"); optreset = 1; optind = 1; while ((opt = getopt(argc, argv, template)) != -1) { if (opt < 0 || opt >= SCHAR_MAX) continue; if (opt == '?' || (ptr = strchr(template, opt)) == NULL) { free(args->flags); free(args); return (NULL); } bit_set(args->flags, opt); if (ptr[1] == ':') { free(args->values[opt]); args->values[opt] = xstrdup(optarg); } } argc -= optind; argv += optind; args->argc = argc; args->argv = cmd_copy_argv(argc, argv); return (args); } /* Free an arguments set. */ void args_free(struct args *args) { u_int i; cmd_free_argv(args->argc, args->argv); for (i = 0; i < SCHAR_MAX; i++) free(args->values[i]); free(args->flags); free(args); } /* Print a set of arguments. */ size_t args_print(struct args *args, char *buf, size_t len) { size_t off; int i; const char *quotes; /* There must be at least one byte at the start. */ if (len == 0) return (0); off = 0; /* Process the flags first. */ buf[off++] = '-'; for (i = 0; i < SCHAR_MAX; i++) { if (!bit_test(args->flags, i) || args->values[i] != NULL) continue; if (off == len - 1) { buf[off] = '\0'; return (len); } buf[off++] = i; buf[off] = '\0'; } if (off == 1) buf[--off] = '\0'; /* Then the flags with arguments. */ for (i = 0; i < SCHAR_MAX; i++) { if (!bit_test(args->flags, i) || args->values[i] == NULL) continue; if (off >= len) { /* snprintf will have zero terminated. */ return (len); } if (strchr(args->values[i], ' ') != NULL) quotes = "\""; else quotes = ""; off += xsnprintf(buf + off, len - off, "%s-%c %s%s%s", off != 0 ? " " : "", i, quotes, args->values[i], quotes); } /* And finally the argument vector. */ for (i = 0; i < args->argc; i++) { if (off >= len) { /* snprintf will have zero terminated. */ return (len); } if (strchr(args->argv[i], ' ') != NULL) quotes = "\""; else quotes = ""; off += xsnprintf(buf + off, len - off, "%s%s%s%s", off != 0 ? " " : "", quotes, args->argv[i], quotes); } return (off); } /* Return if an argument is present. */ int args_has(struct args *args, u_char ch) { return (bit_test(args->flags, ch)); } /* Set argument value. */ void args_set(struct args *args, u_char ch, const char *value) { free(args->values[ch]); if (value != NULL) args->values[ch] = xstrdup(value); else args->values[ch] = NULL; bit_set(args->flags, ch); } /* Get argument value. Will be NULL if it isn't present. */ const char * args_get(struct args *args, u_char ch) { return (args->values[ch]); } /* Convert an argument value to a number. */ long long args_strtonum(struct args *args, u_char ch, long long minval, long long maxval, char **cause) { const char *errstr; long long ll; if (!args_has(args, ch)) { *cause = xstrdup("missing"); return (0); } ll = strtonum(args->values[ch], minval, maxval, &errstr); if (errstr != NULL) { *cause = xstrdup(errstr); return (0); } *cause = NULL; return (ll); } tmate-1.8.10/array.h000066400000000000000000000066211242461015400142060ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2006 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef ARRAY_H #define ARRAY_H #define ARRAY_INITIALIZER { NULL, 0, 0 } #define ARRAY_DECL(n, c) \ struct n { \ c *list; \ u_int num; \ size_t space; \ } #define ARRAY_ITEM(a, i) ((a)->list[i]) #define ARRAY_ITEMSIZE(a) (sizeof *(a)->list) #define ARRAY_INITIALSPACE(a) (10 * ARRAY_ITEMSIZE(a)) #define ARRAY_ENSURE(a, n) do { \ if (UINT_MAX - (n) < (a)->num) \ fatalx("number too big"); \ if (SIZE_MAX / ((a)->num + (n)) < ARRAY_ITEMSIZE(a)) \ fatalx("size too big"); \ if ((a)->space == 0) { \ (a)->space = ARRAY_INITIALSPACE(a); \ (a)->list = xrealloc((a)->list, 1, (a)->space); \ } \ while ((a)->space <= ((a)->num + (n)) * ARRAY_ITEMSIZE(a)) { \ (a)->list = xrealloc((a)->list, 2, (a)->space); \ (a)->space *= 2; \ } \ } while (0) #define ARRAY_EMPTY(a) (((void *) (a)) == NULL || (a)->num == 0) #define ARRAY_LENGTH(a) ((a)->num) #define ARRAY_DATA(a) ((a)->list) #define ARRAY_FIRST(a) ARRAY_ITEM(a, 0) #define ARRAY_LAST(a) ARRAY_ITEM(a, (a)->num - 1) #define ARRAY_INIT(a) do { \ (a)->num = 0; \ (a)->list = NULL; \ (a)->space = 0; \ } while (0) #define ARRAY_CLEAR(a) do { \ (a)->num = 0; \ } while (0) #define ARRAY_SET(a, i, s) do { \ (a)->list[i] = s; \ } while (0) #define ARRAY_ADD(a, s) do { \ ARRAY_ENSURE(a, 1); \ (a)->list[(a)->num] = s; \ (a)->num++; \ } while (0) #define ARRAY_INSERT(a, i, s) do { \ ARRAY_ENSURE(a, 1); \ if ((i) < (a)->num) { \ memmove((a)->list + (i) + 1, (a)->list + (i), \ ARRAY_ITEMSIZE(a) * ((a)->num - (i))); \ } \ (a)->list[i] = s; \ (a)->num++; \ } while (0) #define ARRAY_REMOVE(a, i) do { \ if ((i) < (a)->num - 1) { \ memmove((a)->list + (i), (a)->list + (i) + 1, \ ARRAY_ITEMSIZE(a) * ((a)->num - (i) - 1)); \ } \ (a)->num--; \ if ((a)->num == 0) \ ARRAY_FREE(a); \ } while (0) #define ARRAY_EXPAND(a, n) do { \ ARRAY_ENSURE(a, n); \ (a)->num += n; \ } while (0) #define ARRAY_TRUNC(a, n) do { \ if ((a)->num > n) \ (a)->num -= n; \ else \ ARRAY_FREE(a); \ } while (0) #define ARRAY_CONCAT(a, b) do { \ ARRAY_ENSURE(a, (b)->num); \ memcpy((a)->list + (a)->num, (b)->list, (b)->num * ARRAY_ITEMSIZE(a)); \ (a)->num += (b)->num; \ } while (0) #define ARRAY_FREE(a) do { \ free((a)->list); \ ARRAY_INIT(a); \ } while (0) #define ARRAY_FREEALL(a) do { \ ARRAY_FREE(a); \ free(a); \ } while (0) #endif tmate-1.8.10/attributes.c000066400000000000000000000050561242461015400152520ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2009 Joshua Elsasser * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" const char * attributes_tostring(u_char attr) { static char buf[128]; size_t len; if (attr == 0) return ("none"); len = xsnprintf(buf, sizeof buf, "%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," : ""); if (len > 0) buf[len - 1] = '\0'; return (buf); } int attributes_fromstring(const char *str) { const char delimiters[] = " ,|"; u_char attr; size_t end; if (*str == '\0' || strcspn(str, delimiters) == 0) return (-1); if (strchr(delimiters, str[strlen(str) - 1]) != NULL) return (-1); if (strcasecmp(str, "default") == 0 || strcasecmp(str, "none") == 0) return (0); attr = 0; do { end = strcspn(str, delimiters); if ((end == 6 && strncasecmp(str, "bright", end) == 0) || (end == 4 && strncasecmp(str, "bold", end) == 0)) attr |= GRID_ATTR_BRIGHT; else if (end == 3 && strncasecmp(str, "dim", end) == 0) attr |= GRID_ATTR_DIM; else if (end == 10 && strncasecmp(str, "underscore", end) == 0) attr |= GRID_ATTR_UNDERSCORE; else if (end == 5 && strncasecmp(str, "blink", end) == 0) attr |= GRID_ATTR_BLINK; else if (end == 7 && strncasecmp(str, "reverse", end) == 0) attr |= GRID_ATTR_REVERSE; else if (end == 6 && strncasecmp(str, "hidden", end) == 0) attr |= GRID_ATTR_HIDDEN; else if (end == 7 && strncasecmp(str, "italics", end) == 0) attr |= GRID_ATTR_ITALICS; else return (-1); str += end + strspn(str + end, delimiters); } while (*str != '\0'); return (attr); } tmate-1.8.10/autogen.sh000077500000000000000000000006001242461015400147070ustar00rootroot00000000000000#!/bin/sh # $Id$ if [ "x$(uname)" = "xOpenBSD" ]; then [ -z "$AUTOMAKE_VERSION" ] && export AUTOMAKE_VERSION=1.10 [ -z "$AUTOCONF_VERSION" ] && export AUTOCONF_VERSION=2.65 fi die() { echo "$@" >&2 exit 1 } mkdir -p etc aclocal || die "aclocal failed" automake --add-missing --force-missing --copy --foreign || die "automake failed" autoreconf || die "autoreconf failed" tmate-1.8.10/cfg.c000066400000000000000000000065541242461015400136270ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include "tmux.h" struct cmd_q *cfg_cmd_q; int cfg_finished; int cfg_references; struct causelist cfg_causes; int load_cfg(const char *path, struct cmd_q *cmdq, char **cause) { FILE *f; u_int n, found; char *buf, *copy, *line, *cause1, *msg; size_t len, oldlen; struct cmd_list *cmdlist; log_debug("loading %s", path); if ((f = fopen(path, "rb")) == NULL) { xasprintf(cause, "%s: %s", path, strerror(errno)); return (-1); } n = found = 0; line = NULL; while ((buf = fgetln(f, &len))) { /* Trim \n. */ if (buf[len - 1] == '\n') len--; log_debug("%s: %.*s", path, (int)len, buf); /* Current line is the continuation of the previous one. */ if (line != NULL) { oldlen = strlen(line); line = xrealloc(line, 1, oldlen + len + 1); } else { oldlen = 0; line = xmalloc(len + 1); } /* Append current line to the previous. */ memcpy(line + oldlen, buf, len); line[oldlen + len] = '\0'; n++; /* Continuation: get next line? */ len = strlen(line); if (len > 0 && line[len - 1] == '\\') { line[len - 1] = '\0'; /* Ignore escaped backslash at EOL. */ if (len > 1 && line[len - 2] != '\\') continue; } copy = line; line = NULL; /* Skip empty lines. */ buf = copy; while (isspace((u_char)*buf)) buf++; if (*buf == '\0') { free(copy); continue; } /* Parse and run the command. */ if (cmd_string_parse(buf, &cmdlist, path, n, &cause1) != 0) { free(copy); if (cause1 == NULL) continue; xasprintf(&msg, "%s:%u: %s", path, n, cause1); ARRAY_ADD(&cfg_causes, msg); free(cause1); continue; } free(copy); if (cmdlist == NULL) continue; cmdq_append(cmdq, cmdlist); cmd_list_free(cmdlist); found++; } if (line != NULL) free(line); fclose(f); return (found); } void cfg_default_done(unused struct cmd_q *cmdq) { if (--cfg_references != 0) return; cfg_finished = 1; if (!RB_EMPTY(&sessions)) cfg_show_causes(RB_MIN(sessions, &sessions)); cmdq_free(cfg_cmd_q); cfg_cmd_q = NULL; } void cfg_show_causes(struct session *s) { struct window_pane *wp; char *cause; u_int i; if (s == NULL || ARRAY_EMPTY(&cfg_causes)) return; wp = s->curw->window->active; window_pane_set_mode(wp, &window_copy_mode); window_copy_init_for_output(wp); for (i = 0; i < ARRAY_LENGTH(&cfg_causes); i++) { cause = ARRAY_ITEM(&cfg_causes, i); window_copy_add(wp, "%s", cause); free(cause); } ARRAY_FREE(&cfg_causes); } tmate-1.8.10/client.c000066400000000000000000000377611242461015400143520ustar00rootroot00000000000000/* $Id$ */ /* * 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" struct imsgbuf client_ibuf; struct event client_event; struct event client_stdin; 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; int client_exitval; enum msgtype client_exittype; int client_attached; int client_get_lock(char *); int client_connect(char *, int); void client_send_identify(int); void client_send_environ(void); void client_write_server(enum msgtype, void *, size_t); void client_update_event(void); void client_signal(int, short, void *); void client_stdin_callback(int, short, void *); void client_write(int, const char *, size_t); void client_callback(int, short, void *); int client_dispatch_attached(void); int client_dispatch_wait(void *); 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 -1 to * retry. Ignore other errors - just continue and start the server without the * lock. */ int client_get_lock(char *lockfile) { int lockfd; if ((lockfd = open(lockfile, O_WRONLY|O_CREAT, 0600)) == -1) fatal("open failed"); if (flock(lockfd, LOCK_EX|LOCK_NB) == -1 && errno == EWOULDBLOCK) { while (flock(lockfd, LOCK_EX) == -1 && errno == EINTR) /* nothing */; close(lockfd); return (-1); } return (lockfd); } /* Connect client to server. */ int client_connect(char *path, int start_server) { struct sockaddr_un sa; size_t size; int fd, lockfd; char *lockfile; 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); } retry: if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) fatal("socket failed"); if (connect(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) { if (errno != ECONNREFUSED && errno != ENOENT) goto failed; if (!start_server) goto failed; close(fd); xasprintf(&lockfile, "%s.lock", path); if ((lockfd = client_get_lock(lockfile)) == -1) goto retry; if (unlink(path) != 0 && errno != ENOENT) return (-1); fd = server_start(lockfd, lockfile); free(lockfile); close(lockfd); } setblocking(fd, 0); return (fd); failed: close(fd); return (-1); } /* Get exit string from reason number. */ const char * client_exit_message(void) { switch (client_exitreason) { case CLIENT_EXIT_NONE: break; case CLIENT_EXIT_DETACHED: return ("detached"); case CLIENT_EXIT_DETACHED_HUP: return ("detached and SIGHUP"); case CLIENT_EXIT_LOST_TTY: return ("lost tty"); case CLIENT_EXIT_TERMINATED: return ("terminated"); case CLIENT_EXIT_LOST_SERVER: return ("lost server"); case CLIENT_EXIT_EXITED: return ("exited"); case CLIENT_EXIT_SERVER_EXITED: return ("server exited"); } return ("unknown reason"); } /* Client main loop. */ int client_main(int argc, char **argv, int flags) { struct cmd *cmd; struct cmd_list *cmdlist; struct msg_command_data cmddata; int cmdflags, fd; pid_t ppid; enum msgtype msg; char *cause; struct termios tio, saved_tio; /* Set up the initial command. */ cmdflags = 0; if (shell_cmd != NULL) { msg = MSG_SHELL; cmdflags = CMD_STARTSERVER; } else if (argc == 0) { msg = MSG_COMMAND; cmdflags = CMD_STARTSERVER|CMD_SENDENVIRON|CMD_CANTNEST; } else { msg = MSG_COMMAND; /* * It sucks parsing the command string twice (in client and * later in server) but it is necessary to get the start server * flag. */ cmdlist = cmd_list_parse(argc, argv, NULL, 0, &cause); if (cmdlist == NULL) { fprintf(stderr, "%s\n", cause); return (1); } cmdflags &= ~CMD_STARTSERVER; TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { if (cmd->entry->flags & CMD_STARTSERVER) cmdflags |= CMD_STARTSERVER; if (cmd->entry->flags & CMD_SENDENVIRON) cmdflags |= CMD_SENDENVIRON; if (cmd->entry->flags & CMD_CANTNEST) cmdflags |= CMD_CANTNEST; } cmd_list_free(cmdlist); } /* * Check if this could be a nested session, if the command can't nest: * if the socket path matches $TMUX, this is probably the same server. */ if (shell_cmd == NULL && environ_path != NULL && (cmdflags & CMD_CANTNEST) && strcmp(socket_path, environ_path) == 0) { fprintf(stderr, "sessions should be nested with care, " "unset $TMUX to force\n"); return (1); } /* Initialise the client socket and start the server. */ fd = client_connect(socket_path, cmdflags & CMD_STARTSERVER); if (fd == -1) { fprintf(stderr, "failed to connect to server\n"); return (1); } /* Set process title, log and signals now this is the client. */ #ifdef HAVE_SETPROCTITLE setproctitle("client (%s)", socket_path); #endif logfile("client"); /* Create imsg. */ imsg_init(&client_ibuf, fd); event_set(&client_event, fd, EV_READ, client_callback, shell_cmd); /* Create stdin handler. */ setblocking(STDIN_FILENO, 0); event_set(&client_stdin, STDIN_FILENO, EV_READ|EV_PERSIST, client_stdin_callback, NULL); if (flags & IDENTIFY_TERMIOS) { 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); } /* Establish signal handlers. */ set_signals(client_signal); /* Send initial environment. */ if (cmdflags & CMD_SENDENVIRON) client_send_environ(); client_send_identify(flags); /* Send first command. */ if (msg == MSG_COMMAND) { /* Fill in command line arguments. */ cmddata.pid = environ_pid; cmddata.session_id = environ_session_id; /* Prepare command for server. */ cmddata.argc = argc; if (cmd_pack_argv( argc, argv, cmddata.argv, sizeof cmddata.argv) != 0) { fprintf(stderr, "command too long\n"); return (1); } client_write_server(msg, &cmddata, sizeof cmddata); } else if (msg == MSG_SHELL) client_write_server(msg, NULL, 0); /* Set the event and dispatch. */ client_update_event(); event_dispatch(); /* Print the exit message, if any, and exit. */ if (client_attached) { if (client_exitreason != CLIENT_EXIT_NONE && !login_shell) printf("[%s]\n", client_exit_message()); ppid = getppid(); if (client_exittype == MSG_DETACHKILL && ppid > 1) kill(ppid, SIGHUP); } else if (flags & IDENTIFY_TERMIOS) { if (flags & IDENTIFY_CONTROL) { 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); } setblocking(STDIN_FILENO, 1); return (client_exitval); } /* Send identify message to server with the file descriptors. */ void client_send_identify(int flags) { struct msg_identify_data data; char *term; int fd; data.flags = flags; if (getcwd(data.cwd, sizeof data.cwd) == NULL) *data.cwd = '\0'; term = getenv("TERM"); if (term == NULL || strlcpy(data.term, term, sizeof data.term) >= sizeof data.term) *data.term = '\0'; if ((fd = dup(STDIN_FILENO)) == -1) fatal("dup failed"); imsg_compose(&client_ibuf, MSG_IDENTIFY, PROTOCOL_VERSION, -1, fd, &data, sizeof data); client_update_event(); } /* Forward entire environment to server. */ void client_send_environ(void) { struct msg_environ_data data; char **var; for (var = environ; *var != NULL; var++) { if (strlcpy(data.var, *var, sizeof data.var) >= sizeof data.var) continue; client_write_server(MSG_ENVIRON, &data, sizeof data); } } /* Write a message to the server without a file descriptor. */ void client_write_server(enum msgtype type, void *buf, size_t len) { imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, -1, buf, len); client_update_event(); } /* Update client event based on whether it needs to read or read and write. */ void client_update_event(void) { short events; event_del(&client_event); events = EV_READ; if (client_ibuf.w.queued > 0) events |= EV_WRITE; event_set( &client_event, client_ibuf.fd, events, client_callback, shell_cmd); event_add(&client_event, NULL); } /* Callback to handle signals in the client. */ void client_signal(int sig, unused short events, unused void *data) { struct sigaction sigact; int status; if (!client_attached) { switch (sig) { case SIGCHLD: waitpid(WAIT_ANY, &status, WNOHANG); break; case SIGTERM: event_loopexit(NULL); break; } } else { switch (sig) { case SIGHUP: client_exitreason = CLIENT_EXIT_LOST_TTY; client_exitval = 1; client_write_server(MSG_EXITING, NULL, 0); break; case SIGTERM: client_exitreason = CLIENT_EXIT_TERMINATED; client_exitval = 1; client_write_server(MSG_EXITING, NULL, 0); break; case SIGWINCH: client_write_server(MSG_RESIZE, NULL, 0); break; case SIGCONT: memset(&sigact, 0, sizeof sigact); sigemptyset(&sigact.sa_mask); sigact.sa_flags = SA_RESTART; sigact.sa_handler = SIG_IGN; if (sigaction(SIGTSTP, &sigact, NULL) != 0) fatal("sigaction failed"); client_write_server(MSG_WAKEUP, NULL, 0); break; } } client_update_event(); } /* Callback for client imsg read events. */ void client_callback(unused int fd, short events, void *data) { ssize_t n; int retval; if (events & EV_READ) { if ((n = imsg_read(&client_ibuf)) == -1 || n == 0) goto lost_server; if (client_attached) retval = client_dispatch_attached(); else retval = client_dispatch_wait(data); if (retval != 0) { event_loopexit(NULL); return; } } if (events & EV_WRITE) { if (msgbuf_write(&client_ibuf.w) < 0) goto lost_server; } client_update_event(); return; lost_server: client_exitreason = CLIENT_EXIT_LOST_SERVER; client_exitval = 1; event_loopexit(NULL); } /* Callback for client stdin read events. */ void client_stdin_callback(unused int fd, unused short events, unused void *data1) { struct msg_stdin_data data; data.size = read(STDIN_FILENO, data.data, sizeof data.data); if (data.size < 0 && (errno == EINTR || errno == EAGAIN)) return; client_write_server(MSG_STDIN, &data, sizeof data); if (data.size <= 0) event_del(&client_stdin); client_update_event(); } /* Force write to file descriptor. */ void client_write(int fd, const char *data, size_t size) { ssize_t used; while (size != 0) { used = write(fd, data, size); if (used == -1) { if (errno == EINTR || errno == EAGAIN) continue; break; } data += used; size -= used; } } /* Dispatch imsgs when in wait state (before MSG_READY). */ int client_dispatch_wait(void *data) { struct imsg imsg; ssize_t n, datalen; struct msg_shell_data shelldata; struct msg_exit_data exitdata; struct msg_stdout_data stdoutdata; struct msg_stderr_data stderrdata; const char *shellcmd = data; for (;;) { if ((n = imsg_get(&client_ibuf, &imsg)) == -1) fatalx("imsg_get failed"); if (n == 0) return (0); datalen = imsg.hdr.len - IMSG_HEADER_SIZE; log_debug("got %d from server", imsg.hdr.type); switch (imsg.hdr.type) { case MSG_EXIT: case MSG_SHUTDOWN: if (datalen != sizeof exitdata) { if (datalen != 0) fatalx("bad MSG_EXIT size"); } else { memcpy(&exitdata, imsg.data, sizeof exitdata); client_exitval = exitdata.retcode; } imsg_free(&imsg); return (-1); case MSG_READY: if (datalen != 0) fatalx("bad MSG_READY size"); event_del(&client_stdin); client_attached = 1; client_write_server(MSG_RESIZE, 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"); memcpy(&stdoutdata, imsg.data, sizeof stdoutdata); client_write(STDOUT_FILENO, stdoutdata.data, stdoutdata.size); break; case MSG_STDERR: if (datalen != sizeof stderrdata) fatalx("bad MSG_STDERR"); memcpy(&stderrdata, imsg.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 %u, server %u)\n", PROTOCOL_VERSION, imsg.hdr.peerid); client_exitval = 1; imsg_free(&imsg); return (-1); case MSG_SHELL: if (datalen != sizeof shelldata) fatalx("bad MSG_SHELL size"); memcpy(&shelldata, imsg.data, sizeof shelldata); shelldata.shell[(sizeof shelldata.shell) - 1] = '\0'; clear_signals(0); shell_exec(shelldata.shell, shellcmd); /* NOTREACHED */ case MSG_DETACH: client_write_server(MSG_EXITING, NULL, 0); break; case MSG_EXITED: imsg_free(&imsg); return (-1); default: fatalx("unexpected message"); } imsg_free(&imsg); } } /* Dispatch imsgs in attached state (after MSG_READY). */ int client_dispatch_attached(void) { struct imsg imsg; struct msg_lock_data lockdata; struct sigaction sigact; ssize_t n, datalen; for (;;) { if ((n = imsg_get(&client_ibuf, &imsg)) == -1) fatalx("imsg_get failed"); if (n == 0) return (0); datalen = imsg.hdr.len - IMSG_HEADER_SIZE; log_debug("got %d from server", imsg.hdr.type); switch (imsg.hdr.type) { case MSG_DETACHKILL: case MSG_DETACH: if (datalen != 0) fatalx("bad MSG_DETACH size"); client_exittype = imsg.hdr.type; if (imsg.hdr.type == MSG_DETACHKILL) client_exitreason = CLIENT_EXIT_DETACHED_HUP; else client_exitreason = CLIENT_EXIT_DETACHED; client_write_server(MSG_EXITING, NULL, 0); break; case MSG_EXIT: if (datalen != 0 && datalen != sizeof (struct msg_exit_data)) fatalx("bad MSG_EXIT size"); client_write_server(MSG_EXITING, NULL, 0); client_exitreason = CLIENT_EXIT_EXITED; break; case MSG_EXITED: if (datalen != 0) fatalx("bad MSG_EXITED size"); imsg_free(&imsg); return (-1); case MSG_SHUTDOWN: if (datalen != 0) fatalx("bad MSG_SHUTDOWN size"); client_write_server(MSG_EXITING, NULL, 0); client_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 != sizeof lockdata) fatalx("bad MSG_LOCK size"); memcpy(&lockdata, imsg.data, sizeof lockdata); lockdata.cmd[(sizeof lockdata.cmd) - 1] = '\0'; system(lockdata.cmd); client_write_server(MSG_UNLOCK, NULL, 0); break; default: fatalx("unexpected message"); } imsg_free(&imsg); } } tmate-1.8.10/clock.c000066400000000000000000000072431242461015400141570ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" const char clock_table[14][5][5] = { { { 1,1,1,1,1 }, /* 0 */ { 1,0,0,0,1 }, { 1,0,0,0,1 }, { 1,0,0,0,1 }, { 1,1,1,1,1 } }, { { 0,0,0,0,1 }, /* 1 */ { 0,0,0,0,1 }, { 0,0,0,0,1 }, { 0,0,0,0,1 }, { 0,0,0,0,1 } }, { { 1,1,1,1,1 }, /* 2 */ { 0,0,0,0,1 }, { 1,1,1,1,1 }, { 1,0,0,0,0 }, { 1,1,1,1,1 } }, { { 1,1,1,1,1 }, /* 3 */ { 0,0,0,0,1 }, { 1,1,1,1,1 }, { 0,0,0,0,1 }, { 1,1,1,1,1 } }, { { 1,0,0,0,1 }, /* 4 */ { 1,0,0,0,1 }, { 1,1,1,1,1 }, { 0,0,0,0,1 }, { 0,0,0,0,1 } }, { { 1,1,1,1,1 }, /* 5 */ { 1,0,0,0,0 }, { 1,1,1,1,1 }, { 0,0,0,0,1 }, { 1,1,1,1,1 } }, { { 1,1,1,1,1 }, /* 6 */ { 1,0,0,0,0 }, { 1,1,1,1,1 }, { 1,0,0,0,1 }, { 1,1,1,1,1 } }, { { 1,1,1,1,1 }, /* 7 */ { 0,0,0,0,1 }, { 0,0,0,0,1 }, { 0,0,0,0,1 }, { 0,0,0,0,1 } }, { { 1,1,1,1,1 }, /* 8 */ { 1,0,0,0,1 }, { 1,1,1,1,1 }, { 1,0,0,0,1 }, { 1,1,1,1,1 } }, { { 1,1,1,1,1 }, /* 9 */ { 1,0,0,0,1 }, { 1,1,1,1,1 }, { 0,0,0,0,1 }, { 1,1,1,1,1 } }, { { 0,0,0,0,0 }, /* : */ { 0,0,1,0,0 }, { 0,0,0,0,0 }, { 0,0,1,0,0 }, { 0,0,0,0,0 } }, { { 1,1,1,1,1 }, /* A */ { 1,0,0,0,1 }, { 1,1,1,1,1 }, { 1,0,0,0,1 }, { 1,0,0,0,1 } }, { { 1,1,1,1,1 }, /* P */ { 1,0,0,0,1 }, { 1,1,1,1,1 }, { 1,0,0,0,0 }, { 1,0,0,0,0 } }, { { 1,0,0,0,1 }, /* M */ { 1,1,0,1,1 }, { 1,0,1,0,1 }, { 1,0,0,0,1 }, { 1,0,0,0,1 } }, }; void clock_draw(struct screen_write_ctx *ctx, int colour, int style) { struct screen *s = ctx->s; struct grid_cell gc; char tim[64], *ptr; time_t t; u_int i, j, x, y, idx; t = time(NULL); if (style == 0) strftime(tim, sizeof tim, "%l:%M %p", localtime(&t)); else strftime(tim, sizeof tim, "%H:%M", localtime(&t)); screen_write_clearscreen(ctx); if (screen_size_x(s) < 6 * strlen(tim) || screen_size_y(s) < 6) { if (screen_size_x(s) >= strlen(tim) && screen_size_y(s) != 0) { x = (screen_size_x(s) / 2) - (strlen(tim) / 2); y = screen_size_y(s) / 2; screen_write_cursormove(ctx, x, y); memcpy(&gc, &grid_default_cell, sizeof gc); colour_set_fg(&gc, colour); screen_write_puts(ctx, &gc, "%s", tim); } return; } x = (screen_size_x(s) / 2) - 3 * strlen(tim); y = (screen_size_y(s) / 2) - 3; memcpy(&gc, &grid_default_cell, sizeof gc); colour_set_bg(&gc, colour); for (ptr = tim; *ptr != '\0'; ptr++) { if (*ptr >= '0' && *ptr <= '9') idx = *ptr - '0'; else if (*ptr == ':') idx = 10; else if (*ptr == 'A') idx = 11; else if (*ptr == 'P') idx = 12; else if (*ptr == 'M') idx = 13; else { x += 6; continue; } for (j = 0; j < 5; j++) { for (i = 0; i < 5; i++) { screen_write_cursormove(ctx, x + i, y + j); if (clock_table[idx][j][i]) screen_write_putc(ctx, &gc, ' '); } } x += 6; } } tmate-1.8.10/cmd-attach-session.c000066400000000000000000000062471242461015400165550ustar00rootroot00000000000000/* $Id$ */ /* * 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" /* * Attach existing session to the current terminal. */ enum cmd_retval cmd_attach_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_attach_session_entry = { "attach-session", "attach", "drt:", 0, 0, "[-dr] " CMD_TARGET_SESSION_USAGE, CMD_CANTNEST|CMD_STARTSERVER|CMD_SENDENVIRON, NULL, NULL, cmd_attach_session_exec }; enum cmd_retval cmd_attach_session(struct cmd_q *cmdq, const char* tflag, int dflag, int rflag) { struct session *s; struct client *c; const char *update; char *cause; u_int i; if (RB_EMPTY(&sessions)) { cmdq_error(cmdq, "no sessions"); return (CMD_RETURN_ERROR); } if ((s = cmd_find_session(cmdq, tflag, 1)) == NULL) return (CMD_RETURN_ERROR); if (cmdq->client == NULL) return (CMD_RETURN_NORMAL); if (cmdq->client->session != NULL) { if (dflag) { /* * Can't use server_write_session in case attaching to * the same session as currently attached to. */ for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c == NULL || c->session != s) continue; if (c == cmdq->client) continue; server_write_client(c, MSG_DETACH, NULL, 0); } } cmdq->client->session = s; notify_attached_session_changed(cmdq->client); session_update_activity(s); server_redraw_client(cmdq->client); s->curw->flags &= ~WINLINK_ALERTFLAGS; } else { if (server_client_open(cmdq->client, s, &cause) != 0) { cmdq_error(cmdq, "open terminal failed: %s", cause); free(cause); return (CMD_RETURN_ERROR); } if (rflag) cmdq->client->flags |= CLIENT_READONLY; if (dflag) server_write_session(s, MSG_DETACH, NULL, 0); update = options_get_string(&s->options, "update-environment"); environ_update(update, &cmdq->client->environ, &s->environ); cmdq->client->session = s; notify_attached_session_changed(cmdq->client); session_update_activity(s); server_redraw_client(cmdq->client); s->curw->flags &= ~WINLINK_ALERTFLAGS; server_write_ready(cmdq->client); cmdq->client_exit = 0; } recalculate_sizes(); server_update_socket(); return (CMD_RETURN_NORMAL); } enum cmd_retval cmd_attach_session_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; return (cmd_attach_session(cmdq, args_get(args, 't'), args_has(args, 'd'), args_has(args, 'r'))); } tmate-1.8.10/cmd-bind-key.c000066400000000000000000000070151242461015400153240ustar00rootroot00000000000000/* $Id$ */ /* * 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, this recurses through cmd_*. */ enum cmd_retval cmd_bind_key_check(struct args *); enum cmd_retval cmd_bind_key_exec(struct cmd *, struct cmd_q *); enum cmd_retval cmd_bind_key_table(struct cmd *, struct cmd_q *, int); const struct cmd_entry cmd_bind_key_entry = { "bind-key", "bind", "cnrt:", 1, -1, "[-cnr] [-t key-table] key command [arguments]", 0, NULL, cmd_bind_key_check, cmd_bind_key_exec }; enum cmd_retval cmd_bind_key_check(struct args *args) { if (args_has(args, 't')) { if (args->argc != 2 && args->argc != 3) return (CMD_RETURN_ERROR); } else { if (args->argc < 2) return (CMD_RETURN_ERROR); } return (CMD_RETURN_NORMAL); } enum cmd_retval cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; char *cause; struct cmd_list *cmdlist; int key; key = key_string_lookup_string(args->argv[0]); if (key == KEYC_NONE) { cmdq_error(cmdq, "unknown key: %s", args->argv[0]); return (CMD_RETURN_ERROR); } if (args_has(args, 't')) return (cmd_bind_key_table(self, cmdq, key)); cmdlist = cmd_list_parse(args->argc - 1, args->argv + 1, NULL, 0, &cause); if (cmdlist == NULL) { cmdq_error(cmdq, "%s", cause); free(cause); return (CMD_RETURN_ERROR); } if (!args_has(args, 'n')) key |= KEYC_PREFIX; key_bindings_add(key, args_has(args, 'r'), cmdlist); return (CMD_RETURN_NORMAL); } enum cmd_retval cmd_bind_key_table(struct cmd *self, struct cmd_q *cmdq, int key) { struct args *args = self->args; const char *tablename; const struct mode_key_table *mtab; struct mode_key_binding *mbind, mtmp; enum mode_key_cmd cmd; const char *arg; tablename = args_get(args, 't'); if ((mtab = mode_key_findtable(tablename)) == NULL) { cmdq_error(cmdq, "unknown key table: %s", tablename); return (CMD_RETURN_ERROR); } cmd = mode_key_fromstring(mtab->cmdstr, args->argv[1]); if (cmd == MODEKEY_NONE) { cmdq_error(cmdq, "unknown command: %s", args->argv[1]); return (CMD_RETURN_ERROR); } if (cmd != MODEKEYCOPY_COPYPIPE) { if (args->argc != 2) { cmdq_error(cmdq, "no argument allowed"); return (CMD_RETURN_ERROR); } arg = NULL; } else { if (args->argc != 3) { cmdq_error(cmdq, "no argument given"); return (CMD_RETURN_ERROR); } arg = args->argv[2]; } mtmp.key = key; mtmp.mode = !!args_has(args, 'c'); if ((mbind = RB_FIND(mode_key_tree, mtab->tree, &mtmp)) == NULL) { mbind = xmalloc(sizeof *mbind); mbind->key = mtmp.key; mbind->mode = mtmp.mode; RB_INSERT(mode_key_tree, mtab->tree, mbind); } mbind->cmd = cmd; mbind->arg = arg != NULL ? xstrdup(arg) : NULL; return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-break-pane.c000066400000000000000000000060341242461015400156270ustar00rootroot00000000000000/* $Id$ */ /* * 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. */ enum cmd_retval cmd_break_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_break_pane_entry = { "break-pane", "breakp", "dPF:t:", 0, 0, "[-dP] [-F format] " CMD_TARGET_PANE_USAGE, 0, NULL, NULL, cmd_break_pane_exec }; enum cmd_retval cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq) { #ifdef TMATE cmdq_error(cmdq, "break pane is not supported with tmate"); return (CMD_RETURN_ERROR); #else struct args *args = self->args; struct winlink *wl; struct session *s; struct window_pane *wp; struct window *w; char *name; char *cause; int base_idx; struct client *c; struct format_tree *ft; const char *template; char *cp; if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) return (CMD_RETURN_ERROR); if (window_count_panes(wl->window) == 1) { cmdq_error(cmdq, "can't break with only one pane"); return (CMD_RETURN_ERROR); } w = wl->window; server_unzoom_window(w); TAILQ_REMOVE(&w->panes, wp, entry); if (wp == w->active) { w->active = w->last; w->last = NULL; if (w->active == NULL) { w->active = TAILQ_PREV(wp, window_panes, entry); if (w->active == NULL) w->active = TAILQ_NEXT(wp, entry); } } else if (wp == w->last) w->last = NULL; layout_close_pane(wp); w = wp->window = window_create1(s->sx, s->sy); TAILQ_INSERT_HEAD(&w->panes, wp, entry); w->active = wp; name = default_window_name(w); window_set_name(w, name); free(name); layout_init(w, wp); base_idx = options_get_number(&s->options, "base-index"); wl = session_attach(s, w, -1 - base_idx, &cause); /* can't fail */ if (!args_has(self->args, 'd')) session_select(s, wl->idx); server_redraw_session(s); server_status_session_group(s); if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) template = BREAK_PANE_TEMPLATE; ft = format_create(); if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL) format_client(ft, c); format_session(ft, s); format_winlink(ft, s, wl); format_window_pane(ft, wp); cp = format_expand(ft, template); cmdq_print(cmdq, "%s", cp); free(cp); format_free(ft); } return (CMD_RETURN_NORMAL); #endif } tmate-1.8.10/cmd-capture-pane.c000066400000000000000000000126701242461015400162110ustar00rootroot00000000000000/* $Id$ */ /* * 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. */ enum cmd_retval cmd_capture_pane_exec(struct cmd *, struct cmd_q *); char *cmd_capture_pane_append(char *, size_t *, char *, size_t); char *cmd_capture_pane_pending(struct args *, struct window_pane *, size_t *); char *cmd_capture_pane_history(struct args *, struct cmd_q *, struct window_pane *, size_t *); const struct cmd_entry cmd_capture_pane_entry = { "capture-pane", "capturep", "ab:CeE:JpPqS:t:", 0, 0, "[-aCeJpPq] [-b buffer-index] [-E end-line] [-S start-line]" CMD_TARGET_PANE_USAGE, 0, NULL, NULL, cmd_capture_pane_exec }; char * cmd_capture_pane_append(char *buf, size_t *len, char *line, size_t linelen) { buf = xrealloc(buf, 1, *len + linelen + 1); memcpy(buf + *len, line, linelen); *len += linelen; return (buf); } char * cmd_capture_pane_pending(struct args *args, struct window_pane *wp, size_t *len) { char *buf, *line, tmp[5]; size_t linelen; u_int i; if (wp->ictx.since_ground == NULL) return (xstrdup("")); line = EVBUFFER_DATA(wp->ictx.since_ground); linelen = EVBUFFER_LENGTH(wp->ictx.since_ground); buf = xstrdup(""); if (args_has(args, 'C')) { for (i = 0; i < linelen; i++) { if (line[i] >= ' ') { tmp[0] = line[i]; tmp[1] = '\0'; } else xsnprintf(tmp, sizeof tmp, "\\%03o", line[i]); buf = cmd_capture_pane_append(buf, len, tmp, strlen(tmp)); } } else buf = cmd_capture_pane_append(buf, len, line, linelen); return (buf); } char * cmd_capture_pane_history(struct args *args, struct cmd_q *cmdq, 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; 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(cmdq, "no alternate screen"); return (NULL); } return (xstrdup("")); } } else gd = wp->base.grid; 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; 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); } enum cmd_retval cmd_capture_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c; struct window_pane *wp; char *buf, *cause; int buffer; u_int limit; size_t len; if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL) return (CMD_RETURN_ERROR); len = 0; if (args_has(args, 'P')) buf = cmd_capture_pane_pending(args, wp, &len); else buf = cmd_capture_pane_history(args, cmdq, wp, &len); if (buf == NULL) return (CMD_RETURN_ERROR); if (args_has(args, 'p')) { c = cmdq->client; if (c == NULL || (c->session != NULL && !(c->flags & CLIENT_CONTROL))) { cmdq_error(cmdq, "can't write to stdout"); return (CMD_RETURN_ERROR); } evbuffer_add(c->stdout_data, buf, len); if (args_has(args, 'P') && len > 0) evbuffer_add(c->stdout_data, "\n", 1); server_push_stdout(c); } else { limit = options_get_number(&global_options, "buffer-limit"); if (!args_has(args, 'b')) { paste_add(&global_buffers, buf, len, limit); return (CMD_RETURN_NORMAL); } buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); if (cause != NULL) { cmdq_error(cmdq, "buffer %s", cause); free(buf); free(cause); return (CMD_RETURN_ERROR); } if (paste_replace(&global_buffers, buffer, buf, len) != 0) { cmdq_error(cmdq, "no buffer %d", buffer); free(buf); return (CMD_RETURN_ERROR); } } return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-choose-buffer.c000066400000000000000000000051671242461015400163570ustar00rootroot00000000000000/* $Id$ */ /* * 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" /* * Enter choice mode to choose a buffer. */ enum cmd_retval cmd_choose_buffer_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_choose_buffer_entry = { "choose-buffer", NULL, "F:t:", 0, 1, CMD_TARGET_WINDOW_USAGE " [-F format] [template]", 0, NULL, NULL, cmd_choose_buffer_exec }; enum cmd_retval cmd_choose_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c; struct window_choose_data *cdata; struct winlink *wl; struct paste_buffer *pb; char *action, *action_data; const char *template; u_int idx; if ((c = cmd_current_client(cmdq)) == NULL) { cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); } if ((template = args_get(args, 'F')) == NULL) template = CHOOSE_BUFFER_TEMPLATE; if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) return (CMD_RETURN_ERROR); if (paste_get_top(&global_buffers) == NULL) return (CMD_RETURN_NORMAL); if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) return (CMD_RETURN_NORMAL); if (args->argc != 0) action = xstrdup(args->argv[0]); else action = xstrdup("paste-buffer -b '%%'"); idx = 0; while ((pb = paste_walk_stack(&global_buffers, &idx)) != NULL) { cdata = window_choose_data_create(TREE_OTHER, c, c->session); cdata->idx = idx - 1; cdata->ft_template = xstrdup(template); format_add(cdata->ft, "line", "%u", idx - 1); format_paste_buffer(cdata->ft, pb); xasprintf(&action_data, "%u", idx - 1); cdata->command = cmd_template_replace(action, action_data, 1); free(action_data); window_choose_add(wl->window->active, cdata); } free(action); window_choose_ready(wl->window->active, 0, NULL); return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-choose-client.c000066400000000000000000000062151242461015400163570ustar00rootroot00000000000000/* $Id$ */ /* * 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" /* * Enter choice mode to choose a client. */ enum cmd_retval cmd_choose_client_exec(struct cmd *, struct cmd_q *); void cmd_choose_client_callback(struct window_choose_data *); const struct cmd_entry cmd_choose_client_entry = { "choose-client", NULL, "F:t:", 0, 1, CMD_TARGET_WINDOW_USAGE " [-F format] [template]", 0, NULL, NULL, cmd_choose_client_exec }; struct cmd_choose_client_data { struct client *client; }; enum cmd_retval cmd_choose_client_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c; struct client *c1; struct window_choose_data *cdata; struct winlink *wl; const char *template; char *action; u_int i, idx, cur; if ((c = cmd_current_client(cmdq)) == NULL) { cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); } if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) return (CMD_RETURN_ERROR); if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) return (CMD_RETURN_NORMAL); if ((template = args_get(args, 'F')) == NULL) template = CHOOSE_CLIENT_TEMPLATE; if (args->argc != 0) action = xstrdup(args->argv[0]); else action = xstrdup("detach-client -t '%%'"); cur = idx = 0; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c1 = ARRAY_ITEM(&clients, i); if (c1 == NULL || c1->session == NULL || c1->tty.path == NULL) continue; if (c1 == cmdq->client) cur = idx; idx++; cdata = window_choose_data_create(TREE_OTHER, c, c->session); cdata->idx = i; cdata->ft_template = xstrdup(template); format_add(cdata->ft, "line", "%u", i); format_session(cdata->ft, c1->session); format_client(cdata->ft, c1); cdata->command = cmd_template_replace(action, c1->tty.path, 1); window_choose_add(wl->window->active, cdata); } free(action); window_choose_ready(wl->window->active, cur, cmd_choose_client_callback); return (CMD_RETURN_NORMAL); } void cmd_choose_client_callback(struct window_choose_data *cdata) { struct client *c; if (cdata == NULL) return; if (cdata->start_client->flags & CLIENT_DEAD) return; if (cdata->idx > ARRAY_LENGTH(&clients) - 1) return; c = ARRAY_ITEM(&clients, cdata->idx); if (c == NULL || c->session == NULL) return; window_choose_data_run(cdata); } tmate-1.8.10/cmd-choose-list.c000066400000000000000000000047101242461015400160520ustar00rootroot00000000000000/* $Id$ */ /* * 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 #include #include #include "tmux.h" #define CMD_CHOOSE_LIST_DEFAULT_TEMPLATE "run-shell '%%'" /* * Enter choose mode to choose a custom list. */ enum cmd_retval cmd_choose_list_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_choose_list_entry = { "choose-list", NULL, "l:t:", 0, 1, "[-l items] " CMD_TARGET_WINDOW_USAGE "[template]", 0, NULL, NULL, cmd_choose_list_exec }; enum cmd_retval cmd_choose_list_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c; struct winlink *wl; const char *list1; char *template, *item, *copy, *list; u_int idx; if ((c = cmd_current_client(cmdq)) == NULL) { cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); } if ((list1 = args_get(args, 'l')) == NULL) return (CMD_RETURN_ERROR); if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) return (CMD_RETURN_ERROR); if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) return (CMD_RETURN_NORMAL); if (args->argc != 0) template = xstrdup(args->argv[0]); else template = xstrdup(CMD_CHOOSE_LIST_DEFAULT_TEMPLATE); copy = list = xstrdup(list1); idx = 0; while ((item = strsep(&list, ",")) != NULL) { if (*item == '\0') /* no empty entries */ continue; window_choose_add_item(wl->window->active, c, wl, item, template, idx); idx++; } free(copy); if (idx == 0) { free(template); window_pane_reset_mode(wl->window->active); return (CMD_RETURN_ERROR); } window_choose_ready(wl->window->active, 0, NULL); free(template); return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-choose-tree.c000066400000000000000000000144561242461015400160460ustar00rootroot00000000000000/* $Id$ */ /* * 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 #include #include #include "tmux.h" #define CMD_CHOOSE_TREE_WINDOW_ACTION "select-window -t '%%'" #define CMD_CHOOSE_TREE_SESSION_ACTION "switch-client -t '%%'" /* * Enter choice mode to choose a session and/or window. */ enum cmd_retval cmd_choose_tree_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_choose_tree_entry = { "choose-tree", NULL, "S:W:swub:c:t:", 0, 1, "[-suw] [-b session-template] [-c window template] [-S format] " \ "[-W format] " CMD_TARGET_WINDOW_USAGE, 0, NULL, NULL, cmd_choose_tree_exec }; const struct cmd_entry cmd_choose_session_entry = { "choose-session", NULL, "F:t:", 0, 1, CMD_TARGET_WINDOW_USAGE " [-F format] [template]", 0, NULL, NULL, cmd_choose_tree_exec }; const struct cmd_entry cmd_choose_window_entry = { "choose-window", NULL, "F:t:", 0, 1, CMD_TARGET_WINDOW_USAGE "[-F format] [template]", 0, NULL, NULL, cmd_choose_tree_exec }; enum cmd_retval cmd_choose_tree_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl, *wm; struct session *s, *s2; struct client *c; struct window_choose_data *wcd = NULL; const char *ses_template, *win_template; char *final_win_action, *cur_win_template; char *final_win_template_middle; char *final_win_template_last; const char *ses_action, *win_action; u_int cur_win, idx_ses, win_ses, win_max; u_int wflag, sflag; ses_template = win_template = NULL; ses_action = win_action = NULL; if ((c = cmd_current_client(cmdq)) == NULL) { cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); } if ((s = c->session) == NULL) return (CMD_RETURN_ERROR); if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) return (CMD_RETURN_ERROR); if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) return (CMD_RETURN_NORMAL); /* Sort out which command this is. */ wflag = sflag = 0; if (self->entry == &cmd_choose_session_entry) { sflag = 1; if ((ses_template = args_get(args, 'F')) == NULL) ses_template = CHOOSE_TREE_SESSION_TEMPLATE; if (args->argc != 0) ses_action = args->argv[0]; else ses_action = CMD_CHOOSE_TREE_SESSION_ACTION; } else if (self->entry == &cmd_choose_window_entry) { wflag = 1; if ((win_template = args_get(args, 'F')) == NULL) win_template = CHOOSE_TREE_WINDOW_TEMPLATE; if (args->argc != 0) win_action = args->argv[0]; else win_action = CMD_CHOOSE_TREE_WINDOW_ACTION; } else { wflag = args_has(args, 'w'); sflag = args_has(args, 's'); if ((ses_action = args_get(args, 'b')) == NULL) ses_action = CMD_CHOOSE_TREE_SESSION_ACTION; if ((win_action = args_get(args, 'c')) == NULL) win_action = CMD_CHOOSE_TREE_WINDOW_ACTION; if ((ses_template = args_get(args, 'S')) == NULL) ses_template = CHOOSE_TREE_SESSION_TEMPLATE; if ((win_template = args_get(args, 'W')) == NULL) win_template = CHOOSE_TREE_WINDOW_TEMPLATE; } /* * If not asking for windows and sessions, assume no "-ws" given and * hence display the entire tree outright. */ if (!wflag && !sflag) wflag = sflag = 1; /* * If we're drawing in tree mode, including sessions, then pad the * window template, otherwise just render the windows as a flat list * without any padding. */ if (wflag && sflag) { xasprintf(&final_win_template_middle, " \001tq\001> %s", win_template); xasprintf(&final_win_template_last, " \001mq\001> %s", win_template); } else if (wflag) { final_win_template_middle = xstrdup(win_template); final_win_template_last = xstrdup(win_template); } else final_win_template_middle = final_win_template_last = NULL; idx_ses = cur_win = -1; RB_FOREACH(s2, sessions, &sessions) { idx_ses++; /* * If we're just choosing windows, jump straight there. Note * that this implies the current session, so only choose * windows when the session matches this one. */ if (wflag && !sflag) { if (s != s2) continue; goto windows_only; } wcd = window_choose_add_session(wl->window->active, c, s2, ses_template, ses_action, idx_ses); /* If we're just choosing sessions, skip choosing windows. */ if (sflag && !wflag) { if (s == s2) cur_win = idx_ses; continue; } windows_only: win_ses = win_max = -1; RB_FOREACH(wm, winlinks, &s2->windows) win_max++; RB_FOREACH(wm, winlinks, &s2->windows) { win_ses++; if (sflag && wflag) idx_ses++; if (wm == s2->curw && s == s2) { if (wflag && !sflag) { /* * Then we're only counting windows. * So remember which is the current * window in the list. */ cur_win = win_ses; } else cur_win = idx_ses; } xasprintf(&final_win_action, "%s %s %s", wcd != NULL ? wcd->command : "", wcd != NULL ? ";" : "", win_action); if (win_ses != win_max) cur_win_template = final_win_template_middle; else cur_win_template = final_win_template_last; window_choose_add_window(wl->window->active, c, s2, wm, cur_win_template, final_win_action, (wflag && !sflag) ? win_ses : idx_ses); free(final_win_action); } /* * If we're just drawing windows, don't consider moving on to * other sessions as we only list windows in this session. */ if (wflag && !sflag) break; } free(final_win_template_middle); free(final_win_template_last); window_choose_ready(wl->window->active, cur_win, NULL); if (args_has(args, 'u')) window_choose_expand_all(wl->window->active); return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-clear-history.c000066400000000000000000000027361242461015400164140ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "tmux.h" /* * Clear pane history. */ enum cmd_retval cmd_clear_history_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_clear_history_entry = { "clear-history", "clearhist", "t:", 0, 0, CMD_TARGET_PANE_USAGE, 0, NULL, NULL, cmd_clear_history_exec }; enum cmd_retval cmd_clear_history_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct window_pane *wp; struct grid *gd; if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL) return (CMD_RETURN_ERROR); gd = wp->base.grid; grid_move_lines(gd, 0, gd->hsize, gd->sy); gd->hsize = 0; return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-clock-mode.c000066400000000000000000000026211242461015400156350ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "tmux.h" /* * Enter clock mode. */ enum cmd_retval cmd_clock_mode_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_clock_mode_entry = { "clock-mode", NULL, "t:", 0, 0, CMD_TARGET_PANE_USAGE, 0, NULL, NULL, cmd_clock_mode_exec }; enum cmd_retval cmd_clock_mode_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct window_pane *wp; if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL) return (CMD_RETURN_ERROR); window_pane_set_mode(wp, &window_clock_mode); return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-command-prompt.c000066400000000000000000000117161242461015400165620ustar00rootroot00000000000000/* $Id$ */ /* * 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. */ void cmd_command_prompt_key_binding(struct cmd *, int); int cmd_command_prompt_check(struct args *); enum cmd_retval cmd_command_prompt_exec(struct cmd *, struct cmd_q *); int cmd_command_prompt_callback(void *, const char *); void cmd_command_prompt_free(void *); const struct cmd_entry cmd_command_prompt_entry = { "command-prompt", NULL, "I:p:t:", 0, 1, "[-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " [template]", 0, cmd_command_prompt_key_binding, NULL, cmd_command_prompt_exec }; struct cmd_command_prompt_cdata { struct client *c; char *inputs; char *next_input; char *next_prompt; char *prompts; char *template; int idx; }; void cmd_command_prompt_key_binding(struct cmd *self, int key) { switch (key) { case '$': self->args = args_create(1, "rename-session '%%'"); args_set(self->args, 'I', "#S"); break; case ',': self->args = args_create(1, "rename-window '%%'"); args_set(self->args, 'I', "#W"); break; case '.': self->args = args_create(1, "move-window -t '%%'"); break; case 'f': self->args = args_create(1, "find-window '%%'"); break; case '\'': self->args = args_create(1, "select-window -t ':%%'"); args_set(self->args, 'p', "index"); break; default: self->args = args_create(0); break; } } enum cmd_retval cmd_command_prompt_exec(struct cmd *self, struct cmd_q *cmdq) { 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(cmdq, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); if (c->prompt_string != NULL) return (CMD_RETURN_NORMAL); cdata = xmalloc(sizeof *cdata); cdata->c = c; cdata->idx = 1; cdata->inputs = NULL; cdata->next_input = NULL; cdata->next_prompt = NULL; cdata->prompts = NULL; cdata->template = NULL; if (args->argc != 0) cdata->template = xstrdup(args->argv[0]); else cdata->template = xstrdup("%1"); if ((prompts = args_get(args, 'p')) != NULL) cdata->prompts = xstrdup(prompts); else if (args->argc != 0) { n = strcspn(cdata->template, " ,"); xasprintf(&cdata->prompts, "(%.*s) ", (int) n, cdata->template); } else cdata->prompts = xstrdup(":"); /* Get first prompt. */ cdata->next_prompt = cdata->prompts; ptr = strsep(&cdata->next_prompt, ","); if (prompts == NULL) prompt = xstrdup(ptr); else xasprintf(&prompt, "%s ", ptr); /* Get initial prompt input. */ if ((inputs = args_get(args, 'I')) != NULL) { cdata->inputs = xstrdup(inputs); cdata->next_input = cdata->inputs; input = strsep(&cdata->next_input, ","); } status_prompt_set(c, prompt, input, cmd_command_prompt_callback, cmd_command_prompt_free, cdata, 0); free(prompt); return (CMD_RETURN_NORMAL); } int cmd_command_prompt_callback(void *data, const char *s) { struct cmd_command_prompt_cdata *cdata = data; struct client *c = cdata->c; struct cmd_list *cmdlist; char *cause, *new_template, *prompt, *ptr; char *input = NULL; if (s == NULL) return (0); new_template = cmd_template_replace(cdata->template, s, cdata->idx); 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 ((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); } if (cmd_string_parse(new_template, &cmdlist, NULL, 0, &cause) != 0) { if (cause != NULL) { *cause = toupper((u_char) *cause); status_message_set(c, "%s", cause); free(cause); } return (0); } cmdq_run(c->cmdq, cmdlist); cmd_list_free(cmdlist); if (c->prompt_callbackfn != (void *) &cmd_command_prompt_callback) return (1); return (0); } void cmd_command_prompt_free(void *data) { struct cmd_command_prompt_cdata *cdata = data; free(cdata->inputs); free(cdata->prompts); free(cdata->template); free(cdata); } tmate-1.8.10/cmd-confirm-before.c000066400000000000000000000066401242461015400165220ustar00rootroot00000000000000/* $Id$ */ /* * 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" /* * Asks for confirmation before executing a command. */ void cmd_confirm_before_key_binding(struct cmd *, int); enum cmd_retval cmd_confirm_before_exec(struct cmd *, struct cmd_q *); int cmd_confirm_before_callback(void *, const char *); void cmd_confirm_before_free(void *); const struct cmd_entry cmd_confirm_before_entry = { "confirm-before", "confirm", "p:t:", 1, 1, "[-p prompt] " CMD_TARGET_CLIENT_USAGE " command", 0, cmd_confirm_before_key_binding, NULL, cmd_confirm_before_exec }; struct cmd_confirm_before_data { char *cmd; struct client *client; }; void cmd_confirm_before_key_binding(struct cmd *self, int key) { switch (key) { case '&': self->args = args_create(1, "kill-window"); args_set(self->args, 'p', "kill-window #W? (y/n)"); break; case 'x': self->args = args_create(1, "kill-pane"); args_set(self->args, 'p', "kill-pane #P? (y/n)"); break; default: self->args = args_create(0); break; } } enum cmd_retval cmd_confirm_before_exec(struct cmd *self, struct cmd_q *cmdq) { 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(cmdq, 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]); cdata->client = c; cdata->client->references++; 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); } int cmd_confirm_before_callback(void *data, const char *s) { struct cmd_confirm_before_data *cdata = data; struct client *c = cdata->client; struct cmd_list *cmdlist; char *cause; 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); if (cmd_string_parse(cdata->cmd, &cmdlist, NULL, 0, &cause) != 0) { if (cause != NULL) { cmdq_error(c->cmdq, "%s", cause); free(cause); } return (0); } cmdq_run(c->cmdq, cmdlist); cmd_list_free(cmdlist); return (0); } void cmd_confirm_before_free(void *data) { struct cmd_confirm_before_data *cdata = data; struct client *c = cdata->client; c->references--; free(cdata->cmd); free(cdata); } tmate-1.8.10/cmd-copy-mode.c000066400000000000000000000034301242461015400155130ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "tmux.h" /* * Enter copy mode. */ void cmd_copy_mode_key_binding(struct cmd *, int); enum cmd_retval cmd_copy_mode_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_copy_mode_entry = { "copy-mode", NULL, "t:u", 0, 0, "[-u] " CMD_TARGET_PANE_USAGE, 0, cmd_copy_mode_key_binding, NULL, cmd_copy_mode_exec }; void cmd_copy_mode_key_binding(struct cmd *self, int key) { self->args = args_create(0); if (key == KEYC_PPAGE) args_set(self->args, 'u', NULL); } enum cmd_retval cmd_copy_mode_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct window_pane *wp; if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL) return (CMD_RETURN_ERROR); if (window_pane_set_mode(wp, &window_copy_mode) != 0) return (CMD_RETURN_NORMAL); window_copy_init_from_pane(wp); if (wp->mode == &window_copy_mode && args_has(self->args, 'u')) window_copy_pageup(wp); return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-delete-buffer.c000066400000000000000000000032721242461015400163340ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Delete a paste buffer. */ enum cmd_retval cmd_delete_buffer_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_delete_buffer_entry = { "delete-buffer", "deleteb", "b:", 0, 0, CMD_BUFFER_USAGE, 0, NULL, NULL, cmd_delete_buffer_exec }; enum cmd_retval cmd_delete_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; char *cause; int buffer; if (!args_has(args, 'b')) { paste_free_top(&global_buffers); return (CMD_RETURN_NORMAL); } buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); if (cause != NULL) { cmdq_error(cmdq, "buffer %s", cause); free(cause); return (CMD_RETURN_ERROR); } if (paste_free_index(&global_buffers, buffer) != 0) { cmdq_error(cmdq, "no buffer %d", buffer); return (CMD_RETURN_ERROR); } return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-detach-client.c000066400000000000000000000041711242461015400163260ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "tmux.h" /* * Detach a client. */ enum cmd_retval cmd_detach_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_detach_client_entry = { "detach-client", "detach", "as:t:P", 0, 0, "[-P] [-a] [-s target-session] " CMD_TARGET_CLIENT_USAGE, CMD_READONLY, NULL, NULL, cmd_detach_client_exec }; enum cmd_retval cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c, *c2; struct session *s; enum msgtype msgtype; u_int i; if (args_has(args, 'P')) msgtype = MSG_DETACHKILL; else msgtype = MSG_DETACH; if (args_has(args, 's')) { s = cmd_find_session(cmdq, args_get(args, 's'), 0); if (s == NULL) return (CMD_RETURN_ERROR); for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c != NULL && c->session == s) server_write_client(c, msgtype, NULL, 0); } } else { c = cmd_find_client(cmdq, args_get(args, 't'), 0); if (c == NULL) return (CMD_RETURN_ERROR); if (args_has(args, 'a')) { for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c2 = ARRAY_ITEM(&clients, i); if (c2 == NULL || c == c2) continue; server_write_client(c2, msgtype, NULL, 0); } } else server_write_client(c, msgtype, NULL, 0); } return (CMD_RETURN_STOP); } tmate-1.8.10/cmd-display-message.c000066400000000000000000000054221242461015400167110ustar00rootroot00000000000000/* $Id$ */ /* * 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. */ enum cmd_retval cmd_display_message_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_display_message_entry = { "display-message", "display", "c:pt:F:", 0, 1, "[-p] [-c target-client] [-F format] " CMD_TARGET_PANE_USAGE " [message]", 0, NULL, NULL, cmd_display_message_exec }; enum cmd_retval cmd_display_message_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c; struct session *s; struct winlink *wl; struct window_pane *wp; const char *template; char *msg; struct format_tree *ft; char out[BUFSIZ]; time_t t; size_t len; if (args_has(args, 't')) { wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); if (wl == NULL) return (CMD_RETURN_ERROR); } else { wl = cmd_find_pane(cmdq, NULL, &s, &wp); if (wl == NULL) return (CMD_RETURN_ERROR); } if (args_has(args, 'F') && args->argc != 0) { cmdq_error(cmdq, "only one of -F or argument must be given"); return (CMD_RETURN_ERROR); } if (args_has(args, 'c')) { c = cmd_find_client(cmdq, args_get(args, 'c'), 0); if (c == NULL) return (CMD_RETURN_ERROR); } else { c = cmd_current_client(cmdq); if (c == NULL && !args_has(self->args, 'p')) { cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); } } template = args_get(args, 'F'); if (args->argc != 0) template = args->argv[0]; if (template == NULL) template = DISPLAY_MESSAGE_TEMPLATE; ft = format_create(); if (c != NULL) format_client(ft, c); format_session(ft, s); format_winlink(ft, s, wl); format_window_pane(ft, wp); t = time(NULL); len = strftime(out, sizeof out, template, localtime(&t)); out[len] = '\0'; msg = format_expand(ft, out); if (args_has(self->args, 'p')) cmdq_print(cmdq, "%s", msg); else status_message_set(c, "%s", msg); free(msg); format_free(ft); return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-display-panes.c000066400000000000000000000026241242461015400163740ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "tmux.h" /* * Display panes on a client. */ enum cmd_retval cmd_display_panes_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_display_panes_entry = { "display-panes", "displayp", "t:", 0, 0, CMD_TARGET_CLIENT_USAGE, 0, NULL, NULL, cmd_display_panes_exec }; enum cmd_retval cmd_display_panes_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c; if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); server_set_identify(c); return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-find-window.c000066400000000000000000000136201242461015400160460ustar00rootroot00000000000000/* $Id$ */ /* * 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" /* * Find window containing text. */ enum cmd_retval cmd_find_window_exec(struct cmd *, struct cmd_q *); void cmd_find_window_callback(struct window_choose_data *); /* Flags for determining matching behavior. */ #define CMD_FIND_WINDOW_BY_TITLE 0x1 #define CMD_FIND_WINDOW_BY_CONTENT 0x2 #define CMD_FIND_WINDOW_BY_NAME 0x4 #define CMD_FIND_WINDOW_ALL \ (CMD_FIND_WINDOW_BY_TITLE | \ CMD_FIND_WINDOW_BY_CONTENT | \ CMD_FIND_WINDOW_BY_NAME) const struct cmd_entry cmd_find_window_entry = { "find-window", "findw", "F:CNt:T", 1, 4, "[-CNT] [-F format] " CMD_TARGET_WINDOW_USAGE " match-string", 0, NULL, NULL, cmd_find_window_exec }; struct cmd_find_window_data { struct winlink *wl; char *list_ctx; u_int pane_id; }; ARRAY_DECL(cmd_find_window_data_list, struct cmd_find_window_data); u_int cmd_find_window_match_flags(struct args *); void cmd_find_window_match(struct cmd_find_window_data_list *, int, struct winlink *, const char *, const char *); u_int cmd_find_window_match_flags(struct args *args) { u_int match_flags = 0; /* Turn on flags based on the options. */ if (args_has(args, 'T')) match_flags |= CMD_FIND_WINDOW_BY_TITLE; if (args_has(args, 'C')) match_flags |= CMD_FIND_WINDOW_BY_CONTENT; if (args_has(args, 'N')) match_flags |= CMD_FIND_WINDOW_BY_NAME; /* If none of the flags were set, default to matching anything. */ if (match_flags == 0) match_flags = CMD_FIND_WINDOW_ALL; return (match_flags); } void cmd_find_window_match(struct cmd_find_window_data_list *find_list, int match_flags, struct winlink *wl, const char *str, const char *searchstr) { struct cmd_find_window_data find_data; struct window_pane *wp; u_int i, line; char *sres; memset(&find_data, 0, sizeof find_data); i = 0; TAILQ_FOREACH(wp, &wl->window->panes, entry) { i++; if ((match_flags & CMD_FIND_WINDOW_BY_NAME) && fnmatch(searchstr, wl->window->name, 0) == 0) { find_data.list_ctx = xstrdup(""); break; } if ((match_flags & CMD_FIND_WINDOW_BY_TITLE) && fnmatch(searchstr, wp->base.title, 0) == 0) { xasprintf(&find_data.list_ctx, "pane %u title: \"%s\"", i - 1, wp->base.title); break; } if (match_flags & CMD_FIND_WINDOW_BY_CONTENT && (sres = window_pane_search(wp, str, &line)) != NULL) { xasprintf(&find_data.list_ctx, "pane %u line %u: \"%s\"", i - 1, line + 1, sres); free(sres); break; } } if (find_data.list_ctx != NULL) { find_data.wl = wl; find_data.pane_id = i - 1; ARRAY_ADD(find_list, find_data); } } enum cmd_retval cmd_find_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c; struct window_choose_data *cdata; struct session *s; struct winlink *wl, *wm; struct cmd_find_window_data_list find_list; char *str, *searchstr; const char *template; u_int i, match_flags; if ((c = cmd_current_client(cmdq)) == NULL) { cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); } s = c->session; if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) return (CMD_RETURN_ERROR); if ((template = args_get(args, 'F')) == NULL) template = FIND_WINDOW_TEMPLATE; match_flags = cmd_find_window_match_flags(args); str = args->argv[0]; ARRAY_INIT(&find_list); xasprintf(&searchstr, "*%s*", str); RB_FOREACH(wm, winlinks, &s->windows) cmd_find_window_match (&find_list, match_flags, wm, str, searchstr); free(searchstr); if (ARRAY_LENGTH(&find_list) == 0) { cmdq_error(cmdq, "no windows matching: %s", str); ARRAY_FREE(&find_list); return (CMD_RETURN_ERROR); } if (ARRAY_LENGTH(&find_list) == 1) { if (session_select(s, ARRAY_FIRST(&find_list).wl->idx) == 0) server_redraw_session(s); recalculate_sizes(); goto out; } if (window_pane_set_mode(wl->window->active, &window_choose_mode) != 0) goto out; for (i = 0; i < ARRAY_LENGTH(&find_list); i++) { wm = ARRAY_ITEM(&find_list, i).wl; cdata = window_choose_data_create(TREE_OTHER, c, c->session); cdata->idx = wm->idx; cdata->wl = wm; cdata->ft_template = xstrdup(template); cdata->pane_id = ARRAY_ITEM(&find_list, i).pane_id; format_add(cdata->ft, "line", "%u", i); format_add(cdata->ft, "window_find_matches", "%s", ARRAY_ITEM(&find_list, i).list_ctx); format_session(cdata->ft, s); format_winlink(cdata->ft, s, wm); format_window_pane(cdata->ft, wm->window->active); window_choose_add(wl->window->active, cdata); } window_choose_ready(wl->window->active, 0, cmd_find_window_callback); out: ARRAY_FREE(&find_list); return (CMD_RETURN_NORMAL); } void cmd_find_window_callback(struct window_choose_data *cdata) { struct session *s; struct window_pane *wp; if (cdata == NULL) return; s = cdata->start_session; if (!session_alive(s)) return; wp = window_pane_at_index(cdata->wl->window, cdata->pane_id); if (wp != NULL && window_pane_visible(wp)) window_set_active_pane(cdata->wl->window, wp); if (session_select(s, cdata->idx) == 0) { server_redraw_session(s); recalculate_sizes(); } } tmate-1.8.10/cmd-has-session.c000066400000000000000000000026031242461015400160540ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "tmux.h" /* * Cause client to report an error and exit with 1 if session doesn't exist. */ enum cmd_retval cmd_has_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_has_session_entry = { "has-session", "has", "t:", 0, 0, CMD_TARGET_SESSION_USAGE, 0, NULL, NULL, cmd_has_session_exec }; enum cmd_retval cmd_has_session_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; if (cmd_find_session(cmdq, args_get(args, 't'), 0) == NULL) return (CMD_RETURN_ERROR); return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-if-shell.c000066400000000000000000000076401242461015400153310ustar00rootroot00000000000000/* $Id$ */ /* * 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. */ enum cmd_retval cmd_if_shell_exec(struct cmd *, struct cmd_q *); void cmd_if_shell_callback(struct job *); void cmd_if_shell_done(struct cmd_q *); void cmd_if_shell_free(void *); const struct cmd_entry cmd_if_shell_entry = { "if-shell", "if", "bt:", 2, 3, "[-b] " CMD_TARGET_PANE_USAGE " shell-command command [command]", 0, NULL, NULL, cmd_if_shell_exec }; struct cmd_if_shell_data { char *cmd_if; char *cmd_else; struct cmd_q *cmdq; int bflag; int started; }; enum cmd_retval cmd_if_shell_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct cmd_if_shell_data *cdata; char *shellcmd; struct client *c; struct session *s = NULL; struct winlink *wl = NULL; struct window_pane *wp = NULL; struct format_tree *ft; if (args_has(args, 't')) wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); else { c = cmd_find_client(cmdq, NULL, 1); if (c != NULL && c->session != NULL) { s = c->session; wl = s->curw; wp = wl->window->active; } } ft = format_create(); if (s != NULL) format_session(ft, s); if (s != NULL && wl != NULL) format_winlink(ft, s, wl); if (wp != NULL) format_window_pane(ft, wp); shellcmd = format_expand(ft, args->argv[0]); format_free(ft); cdata = xmalloc(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; cdata->bflag = args_has(args, 'b'); cdata->started = 0; cdata->cmdq = cmdq; cmdq->references++; job_run(shellcmd, s, cmd_if_shell_callback, cmd_if_shell_free, cdata); free(shellcmd); if (cdata->bflag) return (CMD_RETURN_NORMAL); return (CMD_RETURN_WAIT); } void cmd_if_shell_callback(struct job *job) { struct cmd_if_shell_data *cdata = job->data; struct cmd_q *cmdq = cdata->cmdq, *cmdq1; struct cmd_list *cmdlist; char *cause, *cmd; if (cmdq->dead) return; if (!WIFEXITED(job->status) || WEXITSTATUS(job->status) != 0) cmd = cdata->cmd_else; else cmd = cdata->cmd_if; if (cmd == NULL) return; if (cmd_string_parse(cmd, &cmdlist, NULL, 0, &cause) != 0) { if (cause != NULL) { cmdq_error(cmdq, "%s", cause); free(cause); } return; } cdata->started = 1; cmdq1 = cmdq_new(cmdq->client); cmdq1->emptyfn = cmd_if_shell_done; cmdq1->data = cdata; cmdq_run(cmdq1, cmdlist); cmd_list_free(cmdlist); } void cmd_if_shell_done(struct cmd_q *cmdq1) { struct cmd_if_shell_data *cdata = cmdq1->data; struct cmd_q *cmdq = cdata->cmdq; if (!cmdq_free(cmdq) && !cdata->bflag) cmdq_continue(cmdq); cmdq_free(cmdq1); free(cdata->cmd_else); free(cdata->cmd_if); free(cdata); } void cmd_if_shell_free(void *data) { struct cmd_if_shell_data *cdata = data; struct cmd_q *cmdq = cdata->cmdq; if (cdata->started) return; if (!cmdq_free(cmdq) && !cdata->bflag) cmdq_continue(cmdq); free(cdata->cmd_else); free(cdata->cmd_if); free(cdata); } tmate-1.8.10/cmd-join-pane.c000066400000000000000000000111651242461015400155030ustar00rootroot00000000000000/* $Id$ */ /* * 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). */ void cmd_join_pane_key_binding(struct cmd *, int); enum cmd_retval cmd_join_pane_exec(struct cmd *, struct cmd_q *); enum cmd_retval join_pane(struct cmd *, struct cmd_q *, int); const struct cmd_entry cmd_join_pane_entry = { "join-pane", "joinp", "bdhvp:l:s:t:", 0, 0, "[-bdhv] [-p percentage|-l size] [-s src-pane] [-t dst-pane]", 0, cmd_join_pane_key_binding, NULL, cmd_join_pane_exec }; const struct cmd_entry cmd_move_pane_entry = { "move-pane", "movep", "bdhvp:l:s:t:", 0, 0, "[-bdhv] [-p percentage|-l size] [-s src-pane] [-t dst-pane]", 0, NULL, NULL, cmd_join_pane_exec }; void cmd_join_pane_key_binding(struct cmd *self, int key) { switch (key) { case '%': self->args = args_create(0); args_set(self->args, 'h', NULL); break; default: self->args = args_create(0); break; } } enum cmd_retval cmd_join_pane_exec(struct cmd *self, struct cmd_q *cmdq) { return (join_pane(self, cmdq, self->entry == &cmd_join_pane_entry)); } enum cmd_retval join_pane(struct cmd *self, struct cmd_q *cmdq, int not_same_window) { #ifdef TMATE cmdq_error(cmdq, "join pane is not supported with tmate"); return (CMD_RETURN_ERROR); #else struct args *args = self->args; struct session *dst_s; struct winlink *src_wl, *dst_wl; struct window *src_w, *dst_w; struct window_pane *src_wp, *dst_wp; char *cause; int size, percentage, dst_idx; enum layout_type type; struct layout_cell *lc; dst_wl = cmd_find_pane(cmdq, args_get(args, 't'), &dst_s, &dst_wp); if (dst_wl == NULL) return (CMD_RETURN_ERROR); dst_w = dst_wl->window; dst_idx = dst_wl->idx; server_unzoom_window(dst_w); src_wl = cmd_find_pane(cmdq, args_get(args, 's'), NULL, &src_wp); if (src_wl == NULL) return (CMD_RETURN_ERROR); src_w = src_wl->window; server_unzoom_window(src_w); if (not_same_window && src_w == dst_w) { cmdq_error(cmdq, "can't join a pane to its own window"); return (CMD_RETURN_ERROR); } if (!not_same_window && src_wp == dst_wp) { cmdq_error(cmdq, "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(cmdq, "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(cmdq, "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; } lc = layout_split_pane(dst_wp, type, size, args_has(args, 'b')); if (lc == NULL) { cmdq_error(cmdq, "create pane failed: pane too small"); return (CMD_RETURN_ERROR); } layout_close_pane(src_wp); if (src_w->active == src_wp) { src_w->active = TAILQ_PREV(src_wp, window_panes, entry); if (src_w->active == NULL) src_w->active = TAILQ_NEXT(src_wp, entry); } TAILQ_REMOVE(&src_w->panes, src_wp, entry); if (window_count_panes(src_w) == 0) server_kill_window(src_w); else notify_window_layout_changed(src_w); src_wp->window = dst_w; TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry); layout_assign_pane(lc, src_wp); recalculate_sizes(); server_redraw_window(src_w); server_redraw_window(dst_w); if (!args_has(args, 'd')) { window_set_active_pane(dst_w, src_wp); session_select(dst_s, dst_idx); server_redraw_session(dst_s); } else server_status_session(dst_s); notify_window_layout_changed(dst_w); return (CMD_RETURN_NORMAL); #endif } tmate-1.8.10/cmd-kill-pane.c000066400000000000000000000036711242461015400155020ustar00rootroot00000000000000/* $Id$ */ /* * 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. */ enum cmd_retval cmd_kill_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_kill_pane_entry = { "kill-pane", "killp", "at:", 0, 0, "[-a] " CMD_TARGET_PANE_USAGE, 0, NULL, NULL, cmd_kill_pane_exec }; enum cmd_retval cmd_kill_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl; struct window_pane *loopwp, *tmpwp, *wp; if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp)) == NULL) return (CMD_RETURN_ERROR); server_unzoom_window(wl->window); if (window_count_panes(wl->window) == 1) { /* Only one pane, kill the window. */ server_kill_window(wl->window); recalculate_sizes(); return (CMD_RETURN_NORMAL); } if (args_has(self->args, 'a')) { TAILQ_FOREACH_SAFE(loopwp, &wl->window->panes, entry, tmpwp) { if (loopwp == wp) continue; layout_close_pane(loopwp); window_remove_pane(wl->window, loopwp); } } else { layout_close_pane(wp); window_remove_pane(wl->window, wp); } server_redraw_window(wl->window); return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-kill-server.c000066400000000000000000000024321242461015400160570ustar00rootroot00000000000000/* $Id$ */ /* * 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. */ enum cmd_retval cmd_kill_server_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_kill_server_entry = { "kill-server", NULL, "", 0, 0, "", 0, NULL, NULL, cmd_kill_server_exec }; enum cmd_retval cmd_kill_server_exec(unused struct cmd *self, unused struct cmd_q *cmdq) { kill(getpid(), SIGTERM); return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-kill-session.c000066400000000000000000000034121242461015400162330ustar00rootroot00000000000000/* $Id$ */ /* * 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. */ enum cmd_retval cmd_kill_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_kill_session_entry = { "kill-session", NULL, "at:", 0, 0, "[-a] " CMD_TARGET_SESSION_USAGE, 0, NULL, NULL, cmd_kill_session_exec }; enum cmd_retval cmd_kill_session_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct session *s, *s2, *s3; if ((s = cmd_find_session(cmdq, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); if (args_has(args, 'a')) { RB_FOREACH_SAFE(s2, sessions, &sessions, s3) { if (s != s2) { server_destroy_session(s2); session_destroy(s2); } } } else { server_destroy_session(s); session_destroy(s); } return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-kill-window.c000066400000000000000000000031341242461015400160600ustar00rootroot00000000000000/* $Id$ */ /* * 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. */ enum cmd_retval cmd_kill_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_kill_window_entry = { "kill-window", "killw", "at:", 0, 0, "[-a] " CMD_TARGET_WINDOW_USAGE, 0, NULL, NULL, cmd_kill_window_exec }; enum cmd_retval cmd_kill_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl, *wl2, *wl3; struct session *s; if ((wl = cmd_find_window(cmdq, args_get(args, 't'), &s)) == NULL) return (CMD_RETURN_ERROR); 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); } tmate-1.8.10/cmd-link-window.c000066400000000000000000000037041242461015400160650ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Link a window into another session. */ enum cmd_retval cmd_link_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_link_window_entry = { "link-window", "linkw", "dks:t:", 0, 0, "[-dk] " CMD_SRCDST_WINDOW_USAGE, 0, NULL, NULL, cmd_link_window_exec }; enum cmd_retval cmd_link_window_exec(struct cmd *self, struct cmd_q *cmdq) { #ifdef TMATE cmdq_error(cmdq, "link window is not supported with tmate"); return (CMD_RETURN_ERROR); #else struct args *args = self->args; struct session *src, *dst; struct winlink *wl; char *cause; int idx, kflag, dflag; if ((wl = cmd_find_window(cmdq, args_get(args, 's'), &src)) == NULL) return (CMD_RETURN_ERROR); if ((idx = cmd_find_index(cmdq, args_get(args, 't'), &dst)) == -2) return (CMD_RETURN_ERROR); kflag = args_has(self->args, 'k'); dflag = args_has(self->args, 'd'); if (server_link_window(src, wl, dst, idx, kflag, !dflag, &cause) != 0) { cmdq_error(cmdq, "can't link window: %s", cause); free(cause); return (CMD_RETURN_ERROR); } recalculate_sizes(); return (CMD_RETURN_NORMAL); #endif } tmate-1.8.10/cmd-list-buffers.c000066400000000000000000000033631242461015400162310ustar00rootroot00000000000000/* $Id$ */ /* * 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. */ enum cmd_retval cmd_list_buffers_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_list_buffers_entry = { "list-buffers", "lsb", "F:", 0, 0, "[-F format]", 0, NULL, NULL, cmd_list_buffers_exec }; enum cmd_retval cmd_list_buffers_exec(unused struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct paste_buffer *pb; struct format_tree *ft; u_int idx; char *line; const char *template; if ((template = args_get(args, 'F')) == NULL) template = LIST_BUFFERS_TEMPLATE; idx = 0; while ((pb = paste_walk_stack(&global_buffers, &idx)) != NULL) { ft = format_create(); format_add(ft, "line", "%u", idx - 1); format_paste_buffer(ft, pb); line = format_expand(ft, template); cmdq_print(cmdq, "%s", line); free(line); format_free(ft); } return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-list-clients.c000066400000000000000000000041051242461015400162310ustar00rootroot00000000000000/* $Id$ */ /* * 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. */ enum cmd_retval cmd_list_clients_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_list_clients_entry = { "list-clients", "lsc", "F:t:", 0, 0, "[-F format] " CMD_TARGET_SESSION_USAGE, CMD_READONLY, NULL, NULL, cmd_list_clients_exec }; enum cmd_retval cmd_list_clients_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c; struct session *s; struct format_tree *ft; const char *template; u_int i; char *line; if (args_has(args, 't')) { s = cmd_find_session(cmdq, args_get(args, 't'), 0); if (s == NULL) return (CMD_RETURN_ERROR); } else s = NULL; if ((template = args_get(args, 'F')) == NULL) template = LIST_CLIENTS_TEMPLATE; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c == NULL || c->session == NULL) continue; if (s != NULL && s != c->session) continue; ft = format_create(); format_add(ft, "line", "%u", i); format_session(ft, c->session); format_client(ft, c); line = format_expand(ft, template); cmdq_print(cmdq, "%s", line); free(line); format_free(ft); } return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-list-commands.c000066400000000000000000000030221242461015400163660ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "tmux.h" /* * List all commands with usages. */ enum cmd_retval cmd_list_commands_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_list_commands_entry = { "list-commands", "lscm", "", 0, 0, "", 0, NULL, NULL, cmd_list_commands_exec }; enum cmd_retval cmd_list_commands_exec(unused struct cmd *self, struct cmd_q *cmdq) { const struct cmd_entry **entryp; for (entryp = cmd_table; *entryp != NULL; entryp++) { if ((*entryp)->alias != NULL) { cmdq_print(cmdq, "%s (%s) %s", (*entryp)->name, (*entryp)->alias, (*entryp)->usage); } else { cmdq_print(cmdq, "%s %s", (*entryp)->name, (*entryp)->usage); } } return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-list-keys.c000066400000000000000000000077611242461015400155560ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * List key bindings. */ enum cmd_retval cmd_list_keys_exec(struct cmd *, struct cmd_q *); enum cmd_retval cmd_list_keys_table(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_list_keys_entry = { "list-keys", "lsk", "t:", 0, 0, "[-t key-table]", 0, NULL, NULL, cmd_list_keys_exec }; enum cmd_retval cmd_list_keys_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct key_binding *bd; const char *key; char tmp[BUFSIZ], flags[8]; size_t used; int width, keywidth; #ifdef TMATE /* XXX TODO Really nasty hack, we really need our own client instance... */ struct client fake_client; if (!cmdq->client) { cmdq->client = &fake_client; cmdq->client->flags = 0; cmdq->client->session = RB_MIN(sessions, &sessions); } #endif if (args_has(args, 't')) return (cmd_list_keys_table(self, cmdq)); width = 0; RB_FOREACH(bd, key_bindings, &key_bindings) { key = key_string_lookup_key(bd->key & ~KEYC_PREFIX); if (key == NULL) continue; keywidth = strlen(key); if (!(bd->key & KEYC_PREFIX)) { if (bd->can_repeat) keywidth += 4; else keywidth += 3; } else if (bd->can_repeat) keywidth += 3; if (keywidth > width) width = keywidth; } RB_FOREACH(bd, key_bindings, &key_bindings) { key = key_string_lookup_key(bd->key & ~KEYC_PREFIX); if (key == NULL) continue; *flags = '\0'; if (!(bd->key & KEYC_PREFIX)) { if (bd->can_repeat) xsnprintf(flags, sizeof flags, "-rn "); else xsnprintf(flags, sizeof flags, "-n "); } else if (bd->can_repeat) xsnprintf(flags, sizeof flags, "-r "); used = xsnprintf(tmp, sizeof tmp, "%s%*s ", flags, (int) (width - strlen(flags)), key); if (used >= sizeof tmp) continue; cmd_list_print(bd->cmdlist, tmp + used, (sizeof tmp) - used); cmdq_print(cmdq, "bind-key %s", tmp); } return (CMD_RETURN_NORMAL); } enum cmd_retval cmd_list_keys_table(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; const char *tablename; const struct mode_key_table *mtab; struct mode_key_binding *mbind; const char *key, *cmdstr, *mode; int width, keywidth, any_mode; tablename = args_get(args, 't'); if ((mtab = mode_key_findtable(tablename)) == NULL) { cmdq_error(cmdq, "unknown key table: %s", tablename); return (CMD_RETURN_ERROR); } width = 0; any_mode = 0; RB_FOREACH(mbind, mode_key_tree, mtab->tree) { key = key_string_lookup_key(mbind->key); if (key == NULL) continue; if (mbind->mode != 0) any_mode = 1; keywidth = strlen(key); if (keywidth > width) width = keywidth; } RB_FOREACH(mbind, mode_key_tree, mtab->tree) { key = key_string_lookup_key(mbind->key); if (key == NULL) continue; mode = ""; if (mbind->mode != 0) mode = "c"; cmdstr = mode_key_tostring(mtab->cmdstr, mbind->cmd); if (cmdstr != NULL) { cmdq_print(cmdq, "bind-key -%st %s%s %*s %s%s%s%s", mode, any_mode && *mode == '\0' ? " " : "", mtab->name, (int) width, key, cmdstr, mbind->arg != NULL ? " \"" : "", mbind->arg != NULL ? mbind->arg : "", mbind->arg != NULL ? "\"": ""); } } return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-list-panes.c000066400000000000000000000074371242461015400157110ustar00rootroot00000000000000/* $Id$ */ /* * 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" /* * List panes on given window. */ enum cmd_retval cmd_list_panes_exec(struct cmd *, struct cmd_q *); void cmd_list_panes_server(struct cmd *, struct cmd_q *); void cmd_list_panes_session( struct cmd *, struct session *, struct cmd_q *, int); void cmd_list_panes_window(struct cmd *, struct session *, struct winlink *, struct cmd_q *, int); const struct cmd_entry cmd_list_panes_entry = { "list-panes", "lsp", "asF:t:", 0, 0, "[-as] [-F format] " CMD_TARGET_WINDOW_USAGE, 0, NULL, NULL, cmd_list_panes_exec }; enum cmd_retval cmd_list_panes_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct session *s; struct winlink *wl; if (args_has(args, 'a')) cmd_list_panes_server(self, cmdq); else if (args_has(args, 's')) { s = cmd_find_session(cmdq, args_get(args, 't'), 0); if (s == NULL) return (CMD_RETURN_ERROR); cmd_list_panes_session(self, s, cmdq, 1); } else { wl = cmd_find_window(cmdq, args_get(args, 't'), &s); if (wl == NULL) return (CMD_RETURN_ERROR); cmd_list_panes_window(self, s, wl, cmdq, 0); } return (CMD_RETURN_NORMAL); } void cmd_list_panes_server(struct cmd *self, struct cmd_q *cmdq) { struct session *s; RB_FOREACH(s, sessions, &sessions) cmd_list_panes_session(self, s, cmdq, 2); } void cmd_list_panes_session( struct cmd *self, struct session *s, struct cmd_q *cmdq, int type) { struct winlink *wl; RB_FOREACH(wl, winlinks, &s->windows) cmd_list_panes_window(self, s, wl, cmdq, type); } void cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl, struct cmd_q *cmdq, 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(); format_add(ft, "line", "%u", n); format_session(ft, s); format_winlink(ft, s, wl); format_window_pane(ft, wp); line = format_expand(ft, template); cmdq_print(cmdq, "%s", line); free(line); format_free(ft); n++; } } tmate-1.8.10/cmd-list-sessions.c000066400000000000000000000033311242461015400164360ustar00rootroot00000000000000/* $Id$ */ /* * 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. */ enum cmd_retval cmd_list_sessions_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_list_sessions_entry = { "list-sessions", "ls", "F:", 0, 0, "[-F format]", 0, NULL, NULL, cmd_list_sessions_exec }; enum cmd_retval cmd_list_sessions_exec(struct cmd *self, struct cmd_q *cmdq) { 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(); format_add(ft, "line", "%u", n); format_session(ft, s); line = format_expand(ft, template); cmdq_print(cmdq, "%s", line); free(line); format_free(ft); n++; } return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-list-windows.c000066400000000000000000000052021242461015400162610ustar00rootroot00000000000000/* $Id$ */ /* * 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. */ enum cmd_retval cmd_list_windows_exec(struct cmd *, struct cmd_q *); void cmd_list_windows_server(struct cmd *, struct cmd_q *); void cmd_list_windows_session( struct cmd *, struct session *, struct cmd_q *, int); const struct cmd_entry cmd_list_windows_entry = { "list-windows", "lsw", "F:at:", 0, 0, "[-a] [-F format] " CMD_TARGET_SESSION_USAGE, 0, NULL, NULL, cmd_list_windows_exec }; enum cmd_retval cmd_list_windows_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct session *s; if (args_has(args, 'a')) cmd_list_windows_server(self, cmdq); else { s = cmd_find_session(cmdq, args_get(args, 't'), 0); if (s == NULL) return (CMD_RETURN_ERROR); cmd_list_windows_session(self, s, cmdq, 0); } return (CMD_RETURN_NORMAL); } void cmd_list_windows_server(struct cmd *self, struct cmd_q *cmdq) { struct session *s; RB_FOREACH(s, sessions, &sessions) cmd_list_windows_session(self, s, cmdq, 1); } void cmd_list_windows_session( struct cmd *self, struct session *s, struct cmd_q *cmdq, 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(); format_add(ft, "line", "%u", n); format_session(ft, s); format_winlink(ft, s, wl); format_window_pane(ft, wl->window->active); line = format_expand(ft, template); cmdq_print(cmdq, "%s", line); free(line); format_free(ft); n++; } } tmate-1.8.10/cmd-list.c000066400000000000000000000054351242461015400146010ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" struct cmd_list * cmd_list_parse(int argc, char **argv, const char* file, u_int line, char **cause) { struct cmd_list *cmdlist; struct cmd *cmd; int i, lastsplit; size_t arglen, new_argc; char **copy_argv, **new_argv; copy_argv = cmd_copy_argv(argc, argv); cmdlist = xcalloc(1, sizeof *cmdlist); cmdlist->references = 1; TAILQ_INIT(&cmdlist->list); lastsplit = 0; for (i = 0; i < argc; i++) { arglen = strlen(copy_argv[i]); if (arglen == 0 || copy_argv[i][arglen - 1] != ';') continue; copy_argv[i][arglen - 1] = '\0'; if (arglen > 1 && copy_argv[i][arglen - 2] == '\\') { copy_argv[i][arglen - 2] = ';'; continue; } new_argc = i - lastsplit; new_argv = copy_argv + lastsplit; if (arglen != 1) new_argc++; cmd = cmd_parse(new_argc, new_argv, file, line, cause); if (cmd == NULL) goto bad; TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry); lastsplit = i + 1; } if (lastsplit != argc) { cmd = cmd_parse(argc - lastsplit, copy_argv + lastsplit, file, line, cause); if (cmd == NULL) goto bad; TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry); } cmd_free_argv(argc, copy_argv); return (cmdlist); bad: cmd_list_free(cmdlist); cmd_free_argv(argc, copy_argv); return (NULL); } 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); args_free(cmd->args); free(cmd->file); free(cmd); } free(cmdlist); } size_t cmd_list_print(struct cmd_list *cmdlist, char *buf, size_t len) { struct cmd *cmd; size_t off; off = 0; TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { if (off >= len) break; off += cmd_print(cmd, buf + off, len - off); if (off >= len) break; if (TAILQ_NEXT(cmd, qentry) != NULL) off += xsnprintf(buf + off, len - off, " ; "); } return (off); } tmate-1.8.10/cmd-load-buffer.c000066400000000000000000000104641242461015400160120ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2009 Tiago Cunha * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "tmux.h" /* * Loads a paste buffer from a file. */ enum cmd_retval cmd_load_buffer_exec(struct cmd *, struct cmd_q *); void cmd_load_buffer_callback(struct client *, int, void *); const struct cmd_entry cmd_load_buffer_entry = { "load-buffer", "loadb", "b:", 1, 1, CMD_BUFFER_USAGE " path", 0, NULL, NULL, cmd_load_buffer_exec }; enum cmd_retval cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c = cmdq->client; struct session *s; FILE *f; const char *path, *newpath, *wd; char *pdata, *new_pdata, *cause; size_t psize; u_int limit; int ch, error, buffer, *buffer_ptr; if (!args_has(args, 'b')) buffer = -1; else { buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); if (cause != NULL) { cmdq_error(cmdq, "buffer %s", cause); free(cause); return (CMD_RETURN_ERROR); } } path = args->argv[0]; if (strcmp(path, "-") == 0) { buffer_ptr = xmalloc(sizeof *buffer_ptr); *buffer_ptr = buffer; error = server_set_stdin_callback (c, cmd_load_buffer_callback, buffer_ptr, &cause); if (error != 0) { cmdq_error(cmdq, "%s: %s", path, cause); free(cause); return (CMD_RETURN_ERROR); } return (CMD_RETURN_WAIT); } if (c != NULL) wd = c->cwd; else if ((s = cmd_current_session(cmdq, 0)) != NULL) { wd = options_get_string(&s->options, "default-path"); if (*wd == '\0') wd = s->cwd; } else wd = NULL; if (wd != NULL && *wd != '\0') { newpath = get_full_path(wd, path); if (newpath != NULL) path = newpath; } if ((f = fopen(path, "rb")) == NULL) { cmdq_error(cmdq, "%s: %s", path, strerror(errno)); return (CMD_RETURN_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(cmdq, "realloc error: %s", strerror(errno)); goto error; } pdata = new_pdata; pdata[psize++] = ch; } if (ferror(f)) { cmdq_error(cmdq, "%s: read error", path); goto error; } if (pdata != NULL) pdata[psize] = '\0'; fclose(f); limit = options_get_number(&global_options, "buffer-limit"); if (buffer == -1) { paste_add(&global_buffers, pdata, psize, limit); return (CMD_RETURN_NORMAL); } if (paste_replace(&global_buffers, buffer, pdata, psize) != 0) { cmdq_error(cmdq, "no buffer %d", buffer); free(pdata); return (CMD_RETURN_ERROR); } return (CMD_RETURN_NORMAL); error: free(pdata); if (f != NULL) fclose(f); return (CMD_RETURN_ERROR); } void cmd_load_buffer_callback(struct client *c, int closed, void *data) { int *buffer = data; char *pdata; size_t psize; u_int limit; if (!closed) return; c->stdin_callback = NULL; c->references--; if (c->flags & CLIENT_DEAD) return; psize = EVBUFFER_LENGTH(c->stdin_data); if (psize == 0 || (pdata = malloc(psize + 1)) == NULL) { free(data); goto out; } memcpy(pdata, EVBUFFER_DATA(c->stdin_data), psize); pdata[psize] = '\0'; evbuffer_drain(c->stdin_data, psize); limit = options_get_number(&global_options, "buffer-limit"); if (*buffer == -1) paste_add(&global_buffers, pdata, psize, limit); else if (paste_replace(&global_buffers, *buffer, pdata, psize) != 0) { /* No context so can't use server_client_msg_error. */ evbuffer_add_printf(c->stderr_data, "no buffer %d\n", *buffer); server_push_stderr(c); } free(data); out: cmdq_continue(c->cmdq); } tmate-1.8.10/cmd-lock-server.c000066400000000000000000000040141242461015400160520ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" /* * Lock commands. */ enum cmd_retval cmd_lock_server_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_lock_server_entry = { "lock-server", "lock", "", 0, 0, "", 0, NULL, NULL, cmd_lock_server_exec }; const struct cmd_entry cmd_lock_session_entry = { "lock-session", "locks", "t:", 0, 0, CMD_TARGET_SESSION_USAGE, 0, NULL, NULL, cmd_lock_server_exec }; const struct cmd_entry cmd_lock_client_entry = { "lock-client", "lockc", "t:", 0, 0, CMD_TARGET_CLIENT_USAGE, 0, NULL, NULL, cmd_lock_server_exec }; enum cmd_retval cmd_lock_server_exec(struct cmd *self, unused struct cmd_q *cmdq) { struct args *args = self->args; struct client *c; struct session *s; if (self->entry == &cmd_lock_server_entry) server_lock(); else if (self->entry == &cmd_lock_session_entry) { s = cmd_find_session(cmdq, args_get(args, 't'), 0); if (s == NULL) return (CMD_RETURN_ERROR); server_lock_session(s); } else { c = cmd_find_client(cmdq, args_get(args, 't'), 0); if (c == NULL) return (CMD_RETURN_ERROR); server_lock_client(c); } recalculate_sizes(); return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-move-window.c000066400000000000000000000042551242461015400161000ustar00rootroot00000000000000/* $Id$ */ /* * 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. */ enum cmd_retval cmd_move_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_move_window_entry = { "move-window", "movew", "dkrs:t:", 0, 0, "[-dkr] " CMD_SRCDST_WINDOW_USAGE, 0, NULL, NULL, cmd_move_window_exec }; enum cmd_retval cmd_move_window_exec(struct cmd *self, struct cmd_q *cmdq) { #ifdef TMATE cmdq_error(cmdq, "move window is not supported with tmate"); return (CMD_RETURN_ERROR); #else struct args *args = self->args; struct session *src, *dst, *s; struct winlink *wl; char *cause; int idx, kflag, dflag; if (args_has(args, 'r')) { if ((s = cmd_find_session(cmdq, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); session_renumber_windows(s); recalculate_sizes(); return (CMD_RETURN_NORMAL); } if ((wl = cmd_find_window(cmdq, args_get(args, 's'), &src)) == NULL) return (CMD_RETURN_ERROR); if ((idx = cmd_find_index(cmdq, args_get(args, 't'), &dst)) == -2) return (CMD_RETURN_ERROR); kflag = args_has(self->args, 'k'); dflag = args_has(self->args, 'd'); if (server_link_window(src, wl, dst, idx, kflag, !dflag, &cause) != 0) { cmdq_error(cmdq, "can't move window: %s", cause); free(cause); return (CMD_RETURN_ERROR); } server_unlink_window(src, wl); recalculate_sizes(); return (CMD_RETURN_NORMAL); #endif } tmate-1.8.10/cmd-new-session.c000066400000000000000000000153661242461015400161040ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "tmux.h" /* * Create a new session and attach to the current terminal unless -d is given. */ enum cmd_retval cmd_new_session_check(struct args *); enum cmd_retval cmd_new_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_new_session_entry = { "new-session", "new", "AdDF:n:Ps:t:x:y:", 0, 1, "[-AdDP] [-F format] [-n window-name] [-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] [-y height] [command]", CMD_STARTSERVER|CMD_CANTNEST|CMD_SENDENVIRON, NULL, cmd_new_session_check, cmd_new_session_exec }; enum cmd_retval cmd_new_session_check(struct args *args) { if (args_has(args, 't') && (args->argc != 0 || args_has(args, 'n'))) return (CMD_RETURN_ERROR); return (CMD_RETURN_NORMAL); } enum cmd_retval cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c = cmdq->client; struct session *s, *groupwith; struct window *w; struct environ env; struct termios tio, *tiop; struct passwd *pw; const char *newname, *target, *update, *cwd, *errstr; const char *template; char *cmd, *cause, *cp; int detached, idx; u_int sx, sy; int already_attached; struct format_tree *ft; newname = args_get(args, 's'); if (newname != NULL) { if (!session_check_name(newname)) { cmdq_error(cmdq, "bad session name: %s", newname); return (CMD_RETURN_ERROR); } if (session_find(newname) != NULL) { if (args_has(args, 'A')) { return (cmd_attach_session(cmdq, newname, args_has(args, 'D'), 0)); } cmdq_error(cmdq, "duplicate session: %s", newname); return (CMD_RETURN_ERROR); } } target = args_get(args, 't'); if (target != NULL) { groupwith = cmd_find_session(cmdq, target, 0); if (groupwith == NULL) return (CMD_RETURN_ERROR); } else groupwith = NULL; /* Set -d if no client. */ detached = args_has(args, 'd'); if (c == NULL) detached = 1; /* Is this client already attached? */ already_attached = 0; if (c != NULL && c->session != NULL) already_attached = 1; /* * Save the termios settings, part of which is used for new windows in * this session. * * This is read again with tcgetattr() rather than using tty.tio as if * detached, tty_open won't be called. Because of this, it must be done * before opening the terminal as that calls tcsetattr() to prepare for * tmux taking over. */ if (!detached && !already_attached && c->tty.fd != -1) { 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, NULL, &cause) != 0) { cmdq_error(cmdq, "open terminal failed: %s", cause); free(cause); return (CMD_RETURN_ERROR); } } /* Get the new session working directory. */ if (c != NULL && c->cwd != NULL) cwd = c->cwd; else { pw = getpwuid(getuid()); if (pw->pw_dir != NULL && *pw->pw_dir != '\0') cwd = pw->pw_dir; else cwd = "/"; } /* Find new session size. */ if (c != NULL) { sx = c->tty.sx; sy = c->tty.sy; } else { sx = 80; sy = 24; } if (detached && args_has(args, 'x')) { sx = strtonum(args_get(args, 'x'), 1, USHRT_MAX, &errstr); if (errstr != NULL) { cmdq_error(cmdq, "width %s", errstr); return (CMD_RETURN_ERROR); } } if (detached && args_has(args, 'y')) { sy = strtonum(args_get(args, 'y'), 1, USHRT_MAX, &errstr); if (errstr != NULL) { cmdq_error(cmdq, "height %s", errstr); return (CMD_RETURN_ERROR); } } if (sy > 0 && options_get_number(&global_s_options, "status")) sy--; if (sx == 0) sx = 1; if (sy == 0) sy = 1; /* Figure out the command for the new window. */ if (target != NULL) cmd = NULL; else if (args->argc != 0) cmd = args->argv[0]; else cmd = options_get_string(&global_s_options, "default-command"); /* Construct the environment. */ environ_init(&env); update = options_get_string(&global_s_options, "update-environment"); if (c != NULL) environ_update(update, &c->environ, &env); /* Create the new session. */ idx = -1 - options_get_number(&global_s_options, "base-index"); s = session_create(newname, cmd, cwd, &env, tiop, idx, sx, sy, &cause); if (s == NULL) { cmdq_error(cmdq, "create session failed: %s", cause); free(cause); return (CMD_RETURN_ERROR); } environ_free(&env); /* Set the initial window name if one given. */ if (cmd != NULL && args_has(args, 'n')) { w = s->curw->window; window_set_name(w, args_get(args, 'n')); options_set_number(&w->options, "automatic-rename", 0); } /* * If a target session is given, this is to be part of a session group, * so add it to the group and synchronize. */ if (groupwith != NULL) { session_group_add(groupwith, s); session_group_synchronize_to(s); session_select(s, RB_ROOT(&s->windows)->idx); } /* * Set the client to the new session. If a command client exists, it is * taking this session and needs to get MSG_READY and stay around. */ if (!detached) { if (!already_attached) server_write_ready(c); else if (c->session != NULL) c->last_session = c->session; c->session = s; notify_attached_session_changed(c); session_update_activity(s); 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; ft = format_create(); if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL) format_client(ft, c); format_session(ft, s); cp = format_expand(ft, template); cmdq_print(cmdq, "%s", cp); free(cp); format_free(ft); } if (!detached) cmdq->client_exit = 0; return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-new-window.c000066400000000000000000000072721242461015400157250ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Create a new window. */ enum cmd_retval cmd_new_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_new_window_entry = { "new-window", "neww", "ac:dF:kn:Pt:", 0, 1, "[-adkP] [-c start-directory] [-F format] [-n window-name] " CMD_TARGET_WINDOW_USAGE " [command]", 0, NULL, NULL, cmd_new_window_exec }; enum cmd_retval cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct session *s; struct winlink *wl; struct client *c; const char *cmd, *cwd, *template; char *cause, *cp; int idx, last, detached; struct format_tree *ft; if (args_has(args, 'a')) { wl = cmd_find_window(cmdq, args_get(args, 't'), &s); if (wl == NULL) return (CMD_RETURN_ERROR); 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) { cmdq_error(cmdq, "no free window indexes"); return (CMD_RETURN_ERROR); } /* Move everything from last - 1 to idx up a bit. */ for (; last > idx; last--) { wl = winlink_find_by_index(&s->windows, last - 1); server_link_window(s, wl, s, last, 0, 0, NULL); server_unlink_window(s, wl); } } else { if ((idx = cmd_find_index(cmdq, args_get(args, 't'), &s)) == -2) return (CMD_RETURN_ERROR); } detached = args_has(args, 'd'); wl = NULL; if (idx != -1) wl = winlink_find_by_index(&s->windows, idx); if (wl != NULL && args_has(args, 'k')) { /* * Can't use session_detach as it will destroy session if this * makes it empty. */ notify_window_unlinked(s, wl->window); wl->flags &= ~WINLINK_ALERTFLAGS; winlink_stack_remove(&s->lastw, wl); winlink_remove(&s->windows, wl); /* Force select/redraw if current. */ if (wl == s->curw) { detached = 0; s->curw = NULL; } } if (args->argc == 0) cmd = options_get_string(&s->options, "default-command"); else cmd = args->argv[0]; cwd = cmd_get_default_path(cmdq, args_get(args, 'c')); if (idx == -1) idx = -1 - options_get_number(&s->options, "base-index"); wl = session_new(s, args_get(args, 'n'), cmd, cwd, idx, &cause); if (wl == NULL) { cmdq_error(cmdq, "create window failed: %s", cause); free(cause); return (CMD_RETURN_ERROR); } if (!detached) { session_select(s, wl->idx); 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; ft = format_create(); if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL) format_client(ft, c); format_session(ft, s); format_winlink(ft, s, wl); format_window_pane(ft, wl->window->active); cp = format_expand(ft, template); cmdq_print(cmdq, "%s", cp); free(cp); format_free(ft); } return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-paste-buffer.c000066400000000000000000000050441242461015400162050ustar00rootroot00000000000000/* $Id$ */ /* * 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. */ enum cmd_retval cmd_paste_buffer_exec(struct cmd *, struct cmd_q *); void cmd_paste_buffer_filter(struct window_pane *, const char *, size_t, const char *, int); const struct cmd_entry cmd_paste_buffer_entry = { "paste-buffer", "pasteb", "db:prs:t:", 0, 0, "[-dpr] [-s separator] [-b buffer-index] " CMD_TARGET_PANE_USAGE, 0, NULL, NULL, cmd_paste_buffer_exec }; enum cmd_retval cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct window_pane *wp; struct session *s; struct paste_buffer *pb; const char *sepstr; char *cause; int buffer; int pflag; if (cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp) == NULL) return (CMD_RETURN_ERROR); if (!args_has(args, 'b')) buffer = -1; else { buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); if (cause != NULL) { cmdq_error(cmdq, "buffer %s", cause); free(cause); return (CMD_RETURN_ERROR); } } if (buffer == -1) pb = paste_get_top(&global_buffers); else { pb = paste_get_index(&global_buffers, buffer); if (pb == NULL) { cmdq_error(cmdq, "no buffer %d", buffer); return (CMD_RETURN_ERROR); } } if (pb != NULL) { sepstr = args_get(args, 's'); if (sepstr == NULL) { if (args_has(args, 'r')) sepstr = "\n"; else sepstr = "\r"; } pflag = (wp->screen->mode & MODE_BRACKETPASTE); paste_send_pane(pb, wp, sepstr, args_has(args, 'p') && pflag); } /* Delete the buffer if -d. */ if (args_has(args, 'd')) { if (buffer == -1) paste_free_top(&global_buffers); else paste_free_index(&global_buffers, buffer); } return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-pipe-pane.c000066400000000000000000000072741242461015400155070ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include "tmux.h" /* * Open pipe to redirect pane output. If already open, close first. */ enum cmd_retval cmd_pipe_pane_exec(struct cmd *, struct cmd_q *); void cmd_pipe_pane_error_callback(struct bufferevent *, short, void *); const struct cmd_entry cmd_pipe_pane_entry = { "pipe-pane", "pipep", "ot:", 0, 1, "[-o] " CMD_TARGET_PANE_USAGE " [command]", 0, NULL, NULL, cmd_pipe_pane_exec }; enum cmd_retval cmd_pipe_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c; struct window_pane *wp; char *command; int old_fd, pipe_fd[2], null_fd; if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL) return (CMD_RETURN_ERROR); c = cmd_find_client(cmdq, NULL, 1); /* Destroy the old pipe. */ old_fd = wp->pipe_fd; if (wp->pipe_fd != -1) { bufferevent_free(wp->pipe_event); close(wp->pipe_fd); wp->pipe_fd = -1; } /* If no pipe command, that is enough. */ if (args->argc == 0 || *args->argv[0] == '\0') return (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); /* Open the new pipe. */ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_fd) != 0) { cmdq_error(cmdq, "socketpair error: %s", strerror(errno)); return (CMD_RETURN_ERROR); } /* Fork the child. */ switch (fork()) { case -1: cmdq_error(cmdq, "fork error: %s", strerror(errno)); return (CMD_RETURN_ERROR); case 0: /* Child process. */ close(pipe_fd[0]); clear_signals(1); if (dup2(pipe_fd[1], STDIN_FILENO) == -1) _exit(1); if (pipe_fd[1] != STDIN_FILENO) close(pipe_fd[1]); null_fd = open(_PATH_DEVNULL, O_WRONLY, 0); if (dup2(null_fd, STDOUT_FILENO) == -1) _exit(1); if (dup2(null_fd, STDERR_FILENO) == -1) _exit(1); if (null_fd != STDOUT_FILENO && null_fd != STDERR_FILENO) close(null_fd); closefrom(STDERR_FILENO + 1); command = status_replace( c, NULL, NULL, NULL, args->argv[0], time(NULL), 0); execl(_PATH_BSHELL, "sh", "-c", command, (char *) NULL); _exit(1); default: /* Parent process. */ close(pipe_fd[1]); wp->pipe_fd = pipe_fd[0]; wp->pipe_off = EVBUFFER_LENGTH(wp->event->input); wp->pipe_event = bufferevent_new(wp->pipe_fd, NULL, NULL, cmd_pipe_pane_error_callback, wp); bufferevent_enable(wp->pipe_event, EV_WRITE); setblocking(wp->pipe_fd, 0); return (CMD_RETURN_NORMAL); } } void cmd_pipe_pane_error_callback( unused struct bufferevent *bufev, unused short what, void *data) { struct window_pane *wp = data; bufferevent_free(wp->pipe_event); close(wp->pipe_fd); wp->pipe_fd = -1; } tmate-1.8.10/cmd-queue.c000066400000000000000000000145571242461015400147570ustar00rootroot00000000000000/* $Id$ */ /* * 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 "tmux.h" #include "tmate.h" /* Create new command queue. */ struct cmd_q * cmdq_new(struct client *c) { struct cmd_q *cmdq; cmdq = xcalloc(1, sizeof *cmdq); cmdq->references = 1; cmdq->dead = 0; cmdq->client = c; cmdq->client_exit = 0; TAILQ_INIT(&cmdq->queue); cmdq->item = NULL; cmdq->cmd = NULL; return (cmdq); } /* Free command queue */ int cmdq_free(struct cmd_q *cmdq) { if (--cmdq->references != 0) return (cmdq->dead); cmdq_flush(cmdq); free(cmdq); return (1); } /* Show message from command. */ void printflike2 cmdq_print(struct cmd_q *cmdq, const char *fmt, ...) { struct client *c = cmdq->client; struct window *w; va_list ap; va_start(ap, fmt); if (c == NULL) /* nothing */; else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { va_start(ap, fmt); evbuffer_add_vprintf(c->stdout_data, fmt, ap); va_end(ap); evbuffer_add(c->stdout_data, "\n", 1); server_push_stdout(c); } else { w = c->session->curw->window; if (w->active->mode != &window_copy_mode) { window_pane_reset_mode(w->active); window_pane_set_mode(w->active, &window_copy_mode); window_copy_init_for_output(w->active); tmate_sync_copy_mode(w->active); } window_copy_vadd(w->active, fmt, ap); } va_end(ap); } /* Show info from command. */ void printflike2 cmdq_info(struct cmd_q *cmdq, const char *fmt, ...) { struct client *c = cmdq->client; va_list ap; char *msg; if (options_get_number(&global_options, "quiet")) return; va_start(ap, fmt); if (c == NULL) /* nothing */; else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { va_start(ap, fmt); evbuffer_add_vprintf(c->stdout_data, fmt, ap); va_end(ap); evbuffer_add(c->stdout_data, "\n", 1); server_push_stdout(c); } else { xvasprintf(&msg, fmt, ap); *msg = toupper((u_char) *msg); status_message_set(c, "%s", msg); free(msg); } va_end(ap); } /* Show error from command. */ void printflike2 cmdq_error(struct cmd_q *cmdq, const char *fmt, ...) { struct client *c = cmdq->client; struct cmd *cmd = cmdq->cmd; va_list ap; char *msg, *cause; size_t msglen; va_start(ap, fmt); msglen = xvasprintf(&msg, fmt, ap); va_end(ap); if (c == NULL) { #ifdef TMATE if (cmd->file && cmd->line) xasprintf(&cause, "%s:%u: %s", cmd->file, cmd->line, msg); else xasprintf(&cause, "%s", msg); #else xasprintf(&cause, "%s:%u: %s", cmd->file, cmd->line, msg); #endif ARRAY_ADD(&cfg_causes, cause); } else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { evbuffer_add(c->stderr_data, msg, msglen); evbuffer_add(c->stderr_data, "\n", 1); server_push_stderr(c); c->retcode = 1; } else { *msg = toupper((u_char) *msg); status_message_set(c, "%s", msg); } free(msg); } /* Print a guard line. */ int cmdq_guard(struct cmd_q *cmdq, const char *guard) { struct client *c = cmdq->client; if (c == NULL || c->session == NULL) return 0; if (!(c->flags & CLIENT_CONTROL)) return 0; evbuffer_add_printf(c->stdout_data, "%%%s %ld %u\n", guard, (long) cmdq->time, cmdq->number); server_push_stdout(c); return 1; } /* Add command list to queue and begin processing if needed. */ void cmdq_run(struct cmd_q *cmdq, struct cmd_list *cmdlist) { cmdq_append(cmdq, cmdlist); if (cmdq->item == NULL) { cmdq->cmd = NULL; cmdq_continue(cmdq); } } /* Add command list to queue. */ void cmdq_append(struct cmd_q *cmdq, struct cmd_list *cmdlist) { struct cmd_q_item *item; item = xcalloc(1, sizeof *item); item->cmdlist = cmdlist; TAILQ_INSERT_TAIL(&cmdq->queue, item, qentry); cmdlist->references++; } /* Continue processing command queue. Returns 1 if finishes empty. */ int cmdq_continue(struct cmd_q *cmdq) { struct cmd_q_item *next; enum cmd_retval retval; int empty, guard; char s[1024]; notify_disable(); empty = TAILQ_EMPTY(&cmdq->queue); if (empty) goto empty; if (cmdq->item == NULL) { cmdq->item = TAILQ_FIRST(&cmdq->queue); cmdq->cmd = TAILQ_FIRST(&cmdq->item->cmdlist->list); } else cmdq->cmd = TAILQ_NEXT(cmdq->cmd, qentry); do { next = TAILQ_NEXT(cmdq->item, qentry); while (cmdq->cmd != NULL) { cmd_print(cmdq->cmd, s, sizeof s); log_debug("cmdq %p: %s (client %d)", cmdq, s, cmdq->client != NULL ? cmdq->client->ibuf.fd : -1); cmdq->time = time(NULL); cmdq->number++; #ifdef TMATE if (tmate_should_replicate_cmd(cmdq->cmd->entry)) tmate_exec_cmd(s); #endif guard = cmdq_guard(cmdq, "begin"); retval = cmdq->cmd->entry->exec(cmdq->cmd, cmdq); if (guard) { if (retval == CMD_RETURN_ERROR) cmdq_guard(cmdq, "error"); else cmdq_guard(cmdq, "end"); } if (retval == CMD_RETURN_ERROR) break; if (retval == CMD_RETURN_WAIT) goto out; if (retval == CMD_RETURN_STOP) { cmdq_flush(cmdq); goto empty; } cmdq->cmd = TAILQ_NEXT(cmdq->cmd, qentry); } TAILQ_REMOVE(&cmdq->queue, cmdq->item, qentry); cmd_list_free(cmdq->item->cmdlist); free(cmdq->item); cmdq->item = next; if (cmdq->item != NULL) cmdq->cmd = TAILQ_FIRST(&cmdq->item->cmdlist->list); } while (cmdq->item != NULL); empty: if (cmdq->client_exit) cmdq->client->flags |= CLIENT_EXIT; if (cmdq->emptyfn != NULL) cmdq->emptyfn(cmdq); /* may free cmdq */ empty = 1; out: notify_enable(); return (empty); } /* Flush command queue. */ void cmdq_flush(struct cmd_q *cmdq) { struct cmd_q_item *item, *item1; TAILQ_FOREACH_SAFE(item, &cmdq->queue, qentry, item1) { TAILQ_REMOVE(&cmdq->queue, item, qentry); cmd_list_free(item->cmdlist); free(item); } cmdq->item = NULL; } tmate-1.8.10/cmd-refresh-client.c000066400000000000000000000041751242461015400165400ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "tmux.h" /* * Refresh client. */ enum cmd_retval cmd_refresh_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_refresh_client_entry = { "refresh-client", "refresh", "C:St:", 0, 0, "[-S] [-C size]" CMD_TARGET_CLIENT_USAGE, 0, NULL, NULL, cmd_refresh_client_exec }; enum cmd_retval cmd_refresh_client_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c; const char *size; u_int w, h; if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); if (args_has(args, 'C')) { if ((size = args_get(args, 'C')) == NULL) { cmdq_error(cmdq, "missing size"); return (CMD_RETURN_ERROR); } if (sscanf(size, "%u,%u", &w, &h) != 2) { cmdq_error(cmdq, "bad size argument"); return (CMD_RETURN_ERROR); } if (w < PANE_MINIMUM || w > 5000 || h < PANE_MINIMUM || h > 5000) { cmdq_error(cmdq, "size too small or too big"); return (CMD_RETURN_ERROR); } if (!(c->flags & CLIENT_CONTROL)) { cmdq_error(cmdq, "not a control client"); return (CMD_RETURN_ERROR); } if (tty_set_size(&c->tty, w, h)) recalculate_sizes(); } else if (args_has(args, 'S')) { status_update_jobs(c); server_status_client(c); } else server_redraw_client(c); return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-rename-session.c000066400000000000000000000035601242461015400165530ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Change session name. */ enum cmd_retval cmd_rename_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_rename_session_entry = { "rename-session", "rename", "t:", 1, 1, CMD_TARGET_SESSION_USAGE " new-name", 0, NULL, NULL, cmd_rename_session_exec }; enum cmd_retval cmd_rename_session_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct session *s; const char *newname; newname = args->argv[0]; if (!session_check_name(newname)) { cmdq_error(cmdq, "bad session name: %s", newname); return (CMD_RETURN_ERROR); } if (session_find(newname) != NULL) { cmdq_error(cmdq, "duplicate session: %s", newname); return (CMD_RETURN_ERROR); } if ((s = cmd_find_session(cmdq, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); RB_REMOVE(sessions, &sessions, s); free(s->name); s->name = xstrdup(newname); RB_INSERT(sessions, &sessions, s); server_status_session(s); notify_session_renamed(s); return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-rename-window.c000066400000000000000000000030741242461015400163770ustar00rootroot00000000000000/* $Id$ */ /* * 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. */ enum cmd_retval cmd_rename_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_rename_window_entry = { "rename-window", "renamew", "t:", 1, 1, CMD_TARGET_WINDOW_USAGE " new-name", 0, NULL, NULL, cmd_rename_window_exec }; enum cmd_retval cmd_rename_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct session *s; struct winlink *wl; if ((wl = cmd_find_window(cmdq, args_get(args, 't'), &s)) == NULL) return (CMD_RETURN_ERROR); window_set_name(wl->window, args->argv[0]); options_set_number(&wl->window->options, "automatic-rename", 0); server_status_window(wl->window); return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-resize-pane.c000066400000000000000000000100711242461015400160400ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Increase or decrease pane size. */ void cmd_resize_pane_key_binding(struct cmd *, int); enum cmd_retval cmd_resize_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_resize_pane_entry = { "resize-pane", "resizep", "DLRt:Ux:y:Z", 0, 1, "[-DLRUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE " [adjustment]", 0, cmd_resize_pane_key_binding, NULL, cmd_resize_pane_exec }; void cmd_resize_pane_key_binding(struct cmd *self, int key) { switch (key) { case KEYC_UP | KEYC_CTRL: self->args = args_create(0); args_set(self->args, 'U', NULL); break; case KEYC_DOWN | KEYC_CTRL: self->args = args_create(0); args_set(self->args, 'D', NULL); break; case KEYC_LEFT | KEYC_CTRL: self->args = args_create(0); args_set(self->args, 'L', NULL); break; case KEYC_RIGHT | KEYC_CTRL: self->args = args_create(0); args_set(self->args, 'R', NULL); break; case KEYC_UP | KEYC_ESCAPE: self->args = args_create(1, "5"); args_set(self->args, 'U', NULL); break; case KEYC_DOWN | KEYC_ESCAPE: self->args = args_create(1, "5"); args_set(self->args, 'D', NULL); break; case KEYC_LEFT | KEYC_ESCAPE: self->args = args_create(1, "5"); args_set(self->args, 'L', NULL); break; case KEYC_RIGHT | KEYC_ESCAPE: self->args = args_create(1, "5"); args_set(self->args, 'R', NULL); break; case 'z': self->args = args_create(0); args_set(self->args, 'Z', NULL); break; default: self->args = args_create(0); break; } } enum cmd_retval cmd_resize_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl; struct window *w; const char *errstr; char *cause; struct window_pane *wp; u_int adjust; int x, y; if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp)) == NULL) return (CMD_RETURN_ERROR); w = wl->window; 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(cmdq, "adjustment %s", errstr); return (CMD_RETURN_ERROR); } } if (args_has(self->args, 'x')) { x = args_strtonum(self->args, 'x', PANE_MINIMUM, INT_MAX, &cause); if (cause != NULL) { cmdq_error(cmdq, "width %s", cause); free(cause); return (CMD_RETURN_ERROR); } layout_resize_pane_to(wp, LAYOUT_LEFTRIGHT, x); } if (args_has(self->args, 'y')) { y = args_strtonum(self->args, 'y', PANE_MINIMUM, INT_MAX, &cause); if (cause != NULL) { cmdq_error(cmdq, "height %s", cause); free(cause); return (CMD_RETURN_ERROR); } layout_resize_pane_to(wp, LAYOUT_TOPBOTTOM, y); } if (args_has(self->args, 'L')) layout_resize_pane(wp, LAYOUT_LEFTRIGHT, -adjust); else if (args_has(self->args, 'R')) layout_resize_pane(wp, LAYOUT_LEFTRIGHT, adjust); else if (args_has(self->args, 'U')) layout_resize_pane(wp, LAYOUT_TOPBOTTOM, -adjust); else if (args_has(self->args, 'D')) layout_resize_pane(wp, LAYOUT_TOPBOTTOM, adjust); server_redraw_window(wl->window); return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-respawn-pane.c000066400000000000000000000046701242461015400162260ustar00rootroot00000000000000/* $Id$ */ /* * 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. */ enum cmd_retval cmd_respawn_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_respawn_pane_entry = { "respawn-pane", "respawnp", "kt:", 0, 1, "[-k] " CMD_TARGET_PANE_USAGE " [command]", 0, NULL, NULL, cmd_respawn_pane_exec }; enum cmd_retval cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl; struct window *w; struct window_pane *wp; struct session *s; struct environ env; const char *cmd; char *cause; u_int idx; if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) return (CMD_RETURN_ERROR); w = wl->window; if (!args_has(self->args, 'k') && wp->fd != -1) { if (window_pane_index(wp, &idx) != 0) fatalx("index not found"); cmdq_error(cmdq, "pane still active: %s:%u.%u", s->name, wl->idx, idx); return (CMD_RETURN_ERROR); } environ_init(&env); environ_copy(&global_environ, &env); environ_copy(&s->environ, &env); server_fill_environ(s, &env); window_pane_reset_mode(wp); screen_reinit(&wp->base); input_init(wp); if (args->argc != 0) cmd = args->argv[0]; else cmd = NULL; if (window_pane_spawn(wp, cmd, NULL, NULL, &env, s->tio, &cause) != 0) { cmdq_error(cmdq, "respawn pane failed: %s", cause); free(cause); environ_free(&env); return (CMD_RETURN_ERROR); } wp->flags |= PANE_REDRAW; server_status_window(w); environ_free(&env); return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-respawn-window.c000066400000000000000000000051761242461015400166140ustar00rootroot00000000000000/* $Id$ */ /* * 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. */ enum cmd_retval cmd_respawn_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_respawn_window_entry = { "respawn-window", "respawnw", "kt:", 0, 1, "[-k] " CMD_TARGET_WINDOW_USAGE " [command]", 0, NULL, NULL, cmd_respawn_window_exec }; enum cmd_retval cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl; struct window *w; struct window_pane *wp; struct session *s; struct environ env; const char *cmd; char *cause; if ((wl = cmd_find_window(cmdq, args_get(args, 't'), &s)) == NULL) return (CMD_RETURN_ERROR); w = wl->window; if (!args_has(self->args, 'k')) { TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->fd == -1) continue; cmdq_error(cmdq, "window still active: %s:%d", s->name, wl->idx); return (CMD_RETURN_ERROR); } } environ_init(&env); environ_copy(&global_environ, &env); environ_copy(&s->environ, &env); server_fill_environ(s, &env); wp = TAILQ_FIRST(&w->panes); TAILQ_REMOVE(&w->panes, wp, entry); layout_free(w); window_destroy_panes(w); TAILQ_INSERT_HEAD(&w->panes, wp, entry); window_pane_resize(wp, w->sx, w->sy); if (args->argc != 0) cmd = args->argv[0]; else cmd = NULL; if (window_pane_spawn(wp, cmd, NULL, NULL, &env, s->tio, &cause) != 0) { cmdq_error(cmdq, "respawn window failed: %s", cause); free(cause); environ_free(&env); server_destroy_pane(wp); return (CMD_RETURN_ERROR); } layout_init(w, wp); window_pane_reset_mode(wp); screen_reinit(&wp->base); input_init(wp); window_set_active_pane(w, wp); recalculate_sizes(); server_redraw_window(w); environ_free(&env); return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-rotate-window.c000066400000000000000000000066571242461015400164400ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "tmux.h" /* * Rotate the panes in a window. */ void cmd_rotate_window_key_binding(struct cmd *, int); enum cmd_retval cmd_rotate_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_rotate_window_entry = { "rotate-window", "rotatew", "Dt:U", 0, 0, "[-DU] " CMD_TARGET_WINDOW_USAGE, 0, cmd_rotate_window_key_binding, NULL, cmd_rotate_window_exec }; void cmd_rotate_window_key_binding(struct cmd *self, int key) { self->args = args_create(0); if (key == ('o' | KEYC_ESCAPE)) args_set(self->args, 'D', NULL); } enum cmd_retval cmd_rotate_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl; struct window *w; struct window_pane *wp, *wp2; struct layout_cell *lc; u_int sx, sy, xoff, yoff; if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) return (CMD_RETURN_ERROR); w = wl->window; if (args_has(self->args, 'D')) { wp = TAILQ_LAST(&w->panes, window_panes); TAILQ_REMOVE(&w->panes, wp, entry); TAILQ_INSERT_HEAD(&w->panes, wp, entry); lc = wp->layout_cell; xoff = wp->xoff; yoff = wp->yoff; sx = wp->sx; sy = wp->sy; TAILQ_FOREACH(wp, &w->panes, entry) { if ((wp2 = TAILQ_NEXT(wp, entry)) == NULL) break; wp->layout_cell = wp2->layout_cell; if (wp->layout_cell != NULL) wp->layout_cell->wp = wp; wp->xoff = wp2->xoff; wp->yoff = wp2->yoff; window_pane_resize(wp, wp2->sx, wp2->sy); } wp->layout_cell = lc; if (wp->layout_cell != NULL) wp->layout_cell->wp = wp; wp->xoff = xoff; wp->yoff = yoff; window_pane_resize(wp, sx, sy); if ((wp = TAILQ_PREV(w->active, window_panes, entry)) == NULL) wp = TAILQ_LAST(&w->panes, window_panes); window_set_active_pane(w, wp); server_redraw_window(w); } else { wp = TAILQ_FIRST(&w->panes); TAILQ_REMOVE(&w->panes, wp, entry); TAILQ_INSERT_TAIL(&w->panes, wp, entry); lc = wp->layout_cell; xoff = wp->xoff; yoff = wp->yoff; sx = wp->sx; sy = wp->sy; TAILQ_FOREACH_REVERSE(wp, &w->panes, window_panes, entry) { if ((wp2 = TAILQ_PREV(wp, window_panes, entry)) == NULL) break; wp->layout_cell = wp2->layout_cell; if (wp->layout_cell != NULL) wp->layout_cell->wp = wp; wp->xoff = wp2->xoff; wp->yoff = wp2->yoff; window_pane_resize(wp, wp2->sx, wp2->sy); } wp->layout_cell = lc; if (wp->layout_cell != NULL) wp->layout_cell->wp = wp; wp->xoff = xoff; wp->yoff = yoff; window_pane_resize(wp, sx, sy); if ((wp = TAILQ_NEXT(w->active, entry)) == NULL) wp = TAILQ_FIRST(&w->panes); window_set_active_pane(w, wp); server_redraw_window(w); } return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-run-shell.c000066400000000000000000000104031242461015400155260ustar00rootroot00000000000000/* $Id$ */ /* * 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. */ enum cmd_retval cmd_run_shell_exec(struct cmd *, struct cmd_q *); void cmd_run_shell_callback(struct job *); void cmd_run_shell_free(void *); void cmd_run_shell_print(struct job *, const char *); const struct cmd_entry cmd_run_shell_entry = { "run-shell", "run", "bt:", 1, 1, "[-b] " CMD_TARGET_PANE_USAGE " shell-command", 0, NULL, NULL, cmd_run_shell_exec }; struct cmd_run_shell_data { char *cmd; struct cmd_q *cmdq; int bflag; int wp_id; }; void cmd_run_shell_print(struct job *job, const char *msg) { struct cmd_run_shell_data *cdata = job->data; struct window_pane *wp = NULL; if (cdata->wp_id != -1) wp = window_pane_find_by_id(cdata->wp_id); if (wp == NULL) { cmdq_print(cdata->cmdq, "%s", msg); return; } if (window_pane_set_mode(wp, &window_copy_mode) == 0) window_copy_init_for_output(wp); if (wp->mode == &window_copy_mode) window_copy_add(wp, "%s", msg); } enum cmd_retval cmd_run_shell_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct cmd_run_shell_data *cdata; char *shellcmd; struct client *c; struct session *s = NULL; struct winlink *wl = NULL; struct window_pane *wp = NULL; struct format_tree *ft; if (args_has(args, 't')) wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp); else { c = cmd_find_client(cmdq, NULL, 1); if (c != NULL && c->session != NULL) { s = c->session; wl = s->curw; wp = wl->window->active; } } ft = format_create(); if (s != NULL) format_session(ft, s); if (s != NULL && wl != NULL) format_winlink(ft, s, wl); if (wp != NULL) format_window_pane(ft, wp); shellcmd = format_expand(ft, args->argv[0]); format_free(ft); cdata = xmalloc(sizeof *cdata); cdata->cmd = shellcmd; cdata->bflag = args_has(args, 'b'); cdata->wp_id = wp != NULL ? (int) wp->id : -1; cdata->cmdq = cmdq; cmdq->references++; job_run(shellcmd, s, cmd_run_shell_callback, cmd_run_shell_free, cdata); if (cdata->bflag) return (CMD_RETURN_NORMAL); return (CMD_RETURN_WAIT); } void cmd_run_shell_callback(struct job *job) { struct cmd_run_shell_data *cdata = job->data; struct cmd_q *cmdq = cdata->cmdq; char *cmd, *msg, *line; size_t size; int retcode; u_int lines; if (cmdq->dead) return; cmd = cdata->cmd; lines = 0; do { if ((line = evbuffer_readline(job->event->input)) != NULL) { cmd_run_shell_print(job, line); free(line); lines++; } } while (line != NULL); size = EVBUFFER_LENGTH(job->event->input); if (size != 0) { line = xmalloc(size + 1); memcpy(line, EVBUFFER_DATA(job->event->input), size); line[size] = '\0'; cmd_run_shell_print(job, line); lines++; free(line); } msg = NULL; if (WIFEXITED(job->status)) { if ((retcode = WEXITSTATUS(job->status)) != 0) xasprintf(&msg, "'%s' returned %d", cmd, retcode); } else if (WIFSIGNALED(job->status)) { retcode = WTERMSIG(job->status); xasprintf(&msg, "'%s' terminated by signal %d", cmd, retcode); } if (msg != NULL) { if (lines == 0) cmdq_info(cmdq, "%s", msg); else cmd_run_shell_print(job, msg); free(msg); } } void cmd_run_shell_free(void *data) { struct cmd_run_shell_data *cdata = data; struct cmd_q *cmdq = cdata->cmdq; if (!cmdq_free(cmdq) && !cdata->bflag) cmdq_continue(cmdq); free(cdata->cmd); free(cdata); } tmate-1.8.10/cmd-save-buffer.c000066400000000000000000000076001242461015400160270ustar00rootroot00000000000000/* $Id$ */ /* * 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 "tmux.h" /* * Saves a paste buffer to a file. */ enum cmd_retval cmd_save_buffer_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_save_buffer_entry = { "save-buffer", "saveb", "ab:", 1, 1, "[-a] " CMD_BUFFER_USAGE " path", 0, NULL, NULL, cmd_save_buffer_exec }; const struct cmd_entry cmd_show_buffer_entry = { "show-buffer", "showb", "b:", 0, 0, CMD_BUFFER_USAGE, 0, NULL, NULL, cmd_save_buffer_exec }; enum cmd_retval cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c; struct session *s; struct paste_buffer *pb; const char *path, *newpath, *wd; char *cause, *start, *end; size_t size, used; int buffer; mode_t mask; FILE *f; char *msg; size_t msglen; if (!args_has(args, 'b')) { if ((pb = paste_get_top(&global_buffers)) == NULL) { cmdq_error(cmdq, "no buffers"); return (CMD_RETURN_ERROR); } } else { buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); if (cause != NULL) { cmdq_error(cmdq, "buffer %s", cause); free(cause); return (CMD_RETURN_ERROR); } pb = paste_get_index(&global_buffers, buffer); if (pb == NULL) { cmdq_error(cmdq, "no buffer %d", buffer); return (CMD_RETURN_ERROR); } } if (self->entry == &cmd_show_buffer_entry) path = "-"; else path = args->argv[0]; if (strcmp(path, "-") == 0) { c = cmdq->client; if (c == NULL) { cmdq_error(cmdq, "can't write to stdout"); return (CMD_RETURN_ERROR); } if (c->session == NULL || (c->flags & CLIENT_CONTROL)) goto do_stdout; goto do_print; } c = cmdq->client; if (c != NULL) wd = c->cwd; else if ((s = cmd_current_session(cmdq, 0)) != NULL) { wd = options_get_string(&s->options, "default-path"); if (*wd == '\0') wd = s->cwd; } else wd = NULL; if (wd != NULL && *wd != '\0') { newpath = get_full_path(wd, path); if (newpath != NULL) path = newpath; } mask = umask(S_IRWXG | S_IRWXO); if (args_has(self->args, 'a')) f = fopen(path, "ab"); else f = fopen(path, "wb"); umask(mask); if (f == NULL) { cmdq_error(cmdq, "%s: %s", path, strerror(errno)); return (CMD_RETURN_ERROR); } if (fwrite(pb->data, 1, pb->size, f) != pb->size) { cmdq_error(cmdq, "%s: fwrite error", path); fclose(f); return (CMD_RETURN_ERROR); } fclose(f); return (CMD_RETURN_NORMAL); do_stdout: evbuffer_add(c->stdout_data, pb->data, pb->size); server_push_stdout(c); return (CMD_RETURN_NORMAL); do_print: if (pb->size > (INT_MAX / 4) - 1) { cmdq_error(cmdq, "buffer too big"); return (CMD_RETURN_ERROR); } msg = NULL; msglen = 0; used = 0; while (used != pb->size) { start = pb->data + used; end = memchr(start, '\n', pb->size - used); if (end != NULL) size = end - start; else size = pb->size - used; msglen = size * 4 + 1; msg = xrealloc(msg, 1, msglen); strvisx(msg, start, size, VIS_OCTAL|VIS_TAB); cmdq_print(cmdq, "%s", msg); used += size + (end != NULL); } free(msg); return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-select-layout.c000066400000000000000000000067261242461015400164240ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "tmux.h" /* * Switch window to selected layout. */ void cmd_select_layout_key_binding(struct cmd *, int); enum cmd_retval cmd_select_layout_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_select_layout_entry = { "select-layout", "selectl", "npt:", 0, 1, "[-np] " CMD_TARGET_WINDOW_USAGE " [layout-name]", 0, cmd_select_layout_key_binding, NULL, cmd_select_layout_exec }; const struct cmd_entry cmd_next_layout_entry = { "next-layout", "nextl", "t:", 0, 0, CMD_TARGET_WINDOW_USAGE, 0, NULL, NULL, cmd_select_layout_exec }; const struct cmd_entry cmd_previous_layout_entry = { "previous-layout", "prevl", "t:", 0, 0, CMD_TARGET_WINDOW_USAGE, 0, NULL, NULL, cmd_select_layout_exec }; void cmd_select_layout_key_binding(struct cmd *self, int key) { switch (key) { case '1' | KEYC_ESCAPE: self->args = args_create(1, "even-horizontal"); break; case '2' | KEYC_ESCAPE: self->args = args_create(1, "even-vertical"); break; case '3' | KEYC_ESCAPE: self->args = args_create(1, "main-horizontal"); break; case '4' | KEYC_ESCAPE: self->args = args_create(1, "main-vertical"); break; case '5' | KEYC_ESCAPE: self->args = args_create(1, "tiled"); break; default: self->args = args_create(0); break; } } enum cmd_retval cmd_select_layout_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl; const char *layoutname; int next, previous, layout; if ((wl = cmd_find_window(cmdq, args_get(args, 't'), NULL)) == NULL) return (CMD_RETURN_ERROR); server_unzoom_window(wl->window); next = self->entry == &cmd_next_layout_entry; if (args_has(self->args, 'n')) next = 1; previous = self->entry == &cmd_previous_layout_entry; if (args_has(self->args, 'p')) previous = 1; if (next || previous) { if (next) layout = layout_set_next(wl->window); else layout = layout_set_previous(wl->window); server_redraw_window(wl->window); cmdq_info(cmdq, "arranging in: %s", layout_set_name(layout)); return (CMD_RETURN_NORMAL); } if (args->argc == 0) layout = wl->window->lastlayout; else layout = layout_set_lookup(args->argv[0]); if (layout != -1) { layout = layout_set_select(wl->window, layout); server_redraw_window(wl->window); cmdq_info(cmdq, "arranging in: %s", layout_set_name(layout)); return (CMD_RETURN_NORMAL); } if (args->argc != 0) { layoutname = args->argv[0]; if (layout_parse(wl->window, layoutname) == -1) { cmdq_error(cmdq, "can't set layout: %s", layoutname); return (CMD_RETURN_ERROR); } server_redraw_window(wl->window); cmdq_info(cmdq, "arranging in: %s", layoutname); } return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-select-pane.c000066400000000000000000000061211242461015400160170ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "tmux.h" /* * Select pane. */ void cmd_select_pane_key_binding(struct cmd *, int); enum cmd_retval cmd_select_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_select_pane_entry = { "select-pane", "selectp", "lDLRt:U", 0, 0, "[-lDLRU] " CMD_TARGET_PANE_USAGE, 0, cmd_select_pane_key_binding, NULL, cmd_select_pane_exec }; const struct cmd_entry cmd_last_pane_entry = { "last-pane", "lastp", "t:", 0, 0, CMD_TARGET_WINDOW_USAGE, 0, NULL, NULL, cmd_select_pane_exec }; void cmd_select_pane_key_binding(struct cmd *self, int key) { self->args = args_create(0); if (key == KEYC_UP) args_set(self->args, 'U', NULL); if (key == KEYC_DOWN) args_set(self->args, 'D', NULL); if (key == KEYC_LEFT) args_set(self->args, 'L', NULL); if (key == KEYC_RIGHT) args_set(self->args, 'R', NULL); if (key == 'o') args_set(self->args, 't', ":.+"); } enum cmd_retval cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl; struct window_pane *wp; if (self->entry == &cmd_last_pane_entry || args_has(args, 'l')) { wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); if (wl == NULL) return (CMD_RETURN_ERROR); if (wl->window->last == NULL) { cmdq_error(cmdq, "no last pane"); return (CMD_RETURN_ERROR); } server_unzoom_window(wl->window); window_set_active_pane(wl->window, wl->window->last); server_status_window(wl->window); server_redraw_window_borders(wl->window); return (CMD_RETURN_NORMAL); } if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp)) == NULL) return (CMD_RETURN_ERROR); server_unzoom_window(wp->window); if (!window_pane_visible(wp)) { cmdq_error(cmdq, "pane not visible"); return (CMD_RETURN_ERROR); } if (args_has(self->args, 'L')) wp = window_pane_find_left(wp); else if (args_has(self->args, 'R')) wp = window_pane_find_right(wp); else if (args_has(self->args, 'U')) wp = window_pane_find_up(wp); else if (args_has(self->args, 'D')) wp = window_pane_find_down(wp); if (wp == NULL) { cmdq_error(cmdq, "pane not found"); return (CMD_RETURN_ERROR); } window_set_active_pane(wl->window, wp); server_status_window(wl->window); server_redraw_window_borders(wl->window); return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-select-window.c000066400000000000000000000072531242461015400164120ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Select window by index. */ void cmd_select_window_key_binding(struct cmd *, int); enum cmd_retval cmd_select_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_select_window_entry = { "select-window", "selectw", "lnpTt:", 0, 0, "[-lnpT] " CMD_TARGET_WINDOW_USAGE, 0, cmd_select_window_key_binding, NULL, cmd_select_window_exec }; const struct cmd_entry cmd_next_window_entry = { "next-window", "next", "at:", 0, 0, "[-a] " CMD_TARGET_SESSION_USAGE, 0, cmd_select_window_key_binding, NULL, cmd_select_window_exec }; const struct cmd_entry cmd_previous_window_entry = { "previous-window", "prev", "at:", 0, 0, "[-a] " CMD_TARGET_SESSION_USAGE, 0, cmd_select_window_key_binding, NULL, cmd_select_window_exec }; const struct cmd_entry cmd_last_window_entry = { "last-window", "last", "t:", 0, 0, CMD_TARGET_SESSION_USAGE, 0, NULL, NULL, cmd_select_window_exec }; void cmd_select_window_key_binding(struct cmd *self, int key) { char tmp[16]; self->args = args_create(0); if (key >= '0' && key <= '9') { xsnprintf(tmp, sizeof tmp, ":%d", key - '0'); args_set(self->args, 't', tmp); } if (key == ('n' | KEYC_ESCAPE) || key == ('p' | KEYC_ESCAPE)) args_set(self->args, 'a', NULL); } enum cmd_retval cmd_select_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl; struct session *s; int next, previous, last, activity; next = self->entry == &cmd_next_window_entry; if (args_has(self->args, 'n')) next = 1; previous = self->entry == &cmd_previous_window_entry; if (args_has(self->args, 'p')) previous = 1; last = self->entry == &cmd_last_window_entry; if (args_has(self->args, 'l')) last = 1; if (next || previous || last) { s = cmd_find_session(cmdq, args_get(args, 't'), 0); if (s == NULL) return (CMD_RETURN_ERROR); activity = args_has(self->args, 'a'); if (next) { if (session_next(s, activity) != 0) { cmdq_error(cmdq, "no next window"); return (CMD_RETURN_ERROR); } } else if (previous) { if (session_previous(s, activity) != 0) { cmdq_error(cmdq, "no previous window"); return (CMD_RETURN_ERROR); } } else { if (session_last(s) != 0) { cmdq_error(cmdq, "no last window"); return (CMD_RETURN_ERROR); } } server_redraw_session(s); } else { wl = cmd_find_window(cmdq, args_get(args, 't'), &s); if (wl == NULL) return (CMD_RETURN_ERROR); /* * 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(cmdq, "no last window"); return (-1); } server_redraw_session(s); } else if (session_select(s, wl->idx) == 0) server_redraw_session(s); } recalculate_sizes(); return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-send-keys.c000066400000000000000000000050751242461015400155300ustar00rootroot00000000000000/* $Id$ */ /* * 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. */ enum cmd_retval cmd_send_keys_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_send_keys_entry = { "send-keys", "send", "lRt:", 0, -1, "[-lR] " CMD_TARGET_PANE_USAGE " key ...", 0, NULL, NULL, cmd_send_keys_exec }; const struct cmd_entry cmd_send_prefix_entry = { "send-prefix", NULL, "2t:", 0, 0, "[-2] " CMD_TARGET_PANE_USAGE, 0, NULL, NULL, cmd_send_keys_exec }; enum cmd_retval cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct window_pane *wp; struct session *s; struct input_ctx *ictx; const char *str; int i, key; if (cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp) == NULL) return (CMD_RETURN_ERROR); 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"); window_pane_key(wp, s, key); return (CMD_RETURN_NORMAL); } if (args_has(args, 'R')) { ictx = &wp->ictx; memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell); memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); ictx->old_cx = 0; ictx->old_cy = 0; if (wp->mode == NULL) screen_write_start(&ictx->ctx, wp, &wp->base); else screen_write_start(&ictx->ctx, NULL, &wp->base); screen_write_reset(&ictx->ctx); screen_write_stop(&ictx->ctx); } for (i = 0; i < args->argc; i++) { str = args->argv[i]; if (!args_has(args, 'l') && (key = key_string_lookup_string(str)) != KEYC_NONE) { window_pane_key(wp, s, key); } else { for (; *str != '\0'; str++) window_pane_key(wp, s, *str); } } return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-server-info.c000066400000000000000000000116541242461015400160650ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "tmux.h" /* * Show various information about server. */ enum cmd_retval cmd_server_info_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_server_info_entry = { "server-info", "info", "", 0, 0, "", 0, NULL, NULL, cmd_server_info_exec }; enum cmd_retval cmd_server_info_exec(unused struct cmd *self, struct cmd_q *cmdq) { struct tty_term *term; struct client *c; struct session *s; struct winlink *wl; struct window *w; struct window_pane *wp; struct tty_code *code; const struct tty_term_code_entry *ent; struct utsname un; struct job *job; struct grid *gd; struct grid_line *gl; u_int i, j, k, lines; size_t size; char out[80]; char *tim; time_t t; tim = ctime(&start_time); *strchr(tim, '\n') = '\0'; cmdq_print(cmdq, "tmux " VERSION ", pid %ld, started %s", (long) getpid(), tim); cmdq_print(cmdq, "socket path %s, debug level %d", socket_path, debug_level); if (uname(&un) >= 0) { cmdq_print(cmdq, "system is %s %s %s %s", un.sysname, un.release, un.version, un.machine); } if (cfg_file != NULL) cmdq_print(cmdq, "configuration file is %s", cfg_file); else cmdq_print(cmdq, "configuration file not specified"); cmdq_print(cmdq, "protocol version is %d", PROTOCOL_VERSION); cmdq_print(cmdq, "%s", ""); cmdq_print(cmdq, "Clients:"); for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c == NULL || c->session == NULL) continue; cmdq_print(cmdq,"%2d: %s (%d, %d): %s [%ux%u %s bs=%hho " "class=%u] [flags=0x%x/0x%x, references=%u]", i, c->tty.path, c->ibuf.fd, c->tty.fd, c->session->name, c->tty.sx, c->tty.sy, c->tty.termname, c->tty.tio.c_cc[VERASE], c->tty.class, c->flags, c->tty.flags, c->references); } cmdq_print(cmdq, "%s", ""); cmdq_print(cmdq, "Sessions: [%zu]", sizeof (struct grid_cell)); RB_FOREACH(s, sessions, &sessions) { t = s->creation_time.tv_sec; tim = ctime(&t); *strchr(tim, '\n') = '\0'; cmdq_print(cmdq, "%2u: %s: %u windows (created %s) [%ux%u] " "[flags=0x%x]", s->id, s->name, winlink_count(&s->windows), tim, s->sx, s->sy, s->flags); RB_FOREACH(wl, winlinks, &s->windows) { w = wl->window; cmdq_print(cmdq, "%4u: %s [%ux%u] [flags=0x%x, " "references=%u, last layout=%d]", wl->idx, w->name, w->sx, w->sy, w->flags, w->references, w->lastlayout); j = 0; TAILQ_FOREACH(wp, &w->panes, entry) { lines = size = 0; gd = wp->base.grid; for (k = 0; k < gd->hsize + gd->sy; k++) { gl = &gd->linedata[k]; if (gl->celldata == NULL) continue; lines++; size += gl->cellsize * sizeof *gl->celldata; } cmdq_print(cmdq, "%6u: %s %lu %d %u/%u, %zu bytes", j, wp->tty, (u_long) wp->pid, wp->fd, lines, gd->hsize + gd->sy, size); j++; } } } cmdq_print(cmdq, "%s", ""); cmdq_print(cmdq, "Terminals:"); LIST_FOREACH(term, &tty_terms, entry) { cmdq_print(cmdq, "%s [references=%u, flags=0x%x]:", term->name, term->references, term->flags); for (i = 0; i < NTTYCODE; i++) { ent = &tty_term_codes[i]; code = &term->codes[ent->code]; switch (code->type) { case TTYCODE_NONE: cmdq_print(cmdq, "%2u: %s: [missing]", ent->code, ent->name); break; case TTYCODE_STRING: strnvis(out, code->value.string, sizeof out, VIS_OCTAL|VIS_TAB|VIS_NL); cmdq_print(cmdq, "%2u: %s: (string) %s", ent->code, ent->name, out); break; case TTYCODE_NUMBER: cmdq_print(cmdq, "%2u: %s: (number) %d", ent->code, ent->name, code->value.number); break; case TTYCODE_FLAG: cmdq_print(cmdq, "%2u: %s: (flag) %s", ent->code, ent->name, code->value.flag ? "true" : "false"); break; } } } cmdq_print(cmdq, "%s", ""); cmdq_print(cmdq, "Jobs:"); LIST_FOREACH(job, &all_jobs, lentry) { cmdq_print(cmdq, "%s [fd=%d, pid=%d, status=%d]", job->cmd, job->fd, job->pid, job->status); } return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-set-buffer.c000066400000000000000000000036431242461015400156670ustar00rootroot00000000000000/* $Id$ */ /* * 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 or set a paste buffer. */ enum cmd_retval cmd_set_buffer_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_set_buffer_entry = { "set-buffer", "setb", "b:", 1, 1, CMD_BUFFER_USAGE " data", 0, NULL, NULL, cmd_set_buffer_exec }; enum cmd_retval cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; u_int limit; char *pdata, *cause; size_t psize; int buffer; limit = options_get_number(&global_options, "buffer-limit"); pdata = xstrdup(args->argv[0]); psize = strlen(pdata); if (!args_has(args, 'b')) { paste_add(&global_buffers, pdata, psize, limit); return (CMD_RETURN_NORMAL); } buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); if (cause != NULL) { cmdq_error(cmdq, "buffer %s", cause); free(cause); free(pdata); return (CMD_RETURN_ERROR); } if (paste_replace(&global_buffers, buffer, pdata, psize) != 0) { cmdq_error(cmdq, "no buffer %d", buffer); free(pdata); return (CMD_RETURN_ERROR); } return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-set-environment.c000066400000000000000000000046011242461015400167550ustar00rootroot00000000000000/* $Id$ */ /* * 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. */ enum cmd_retval cmd_set_environment_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_set_environment_entry = { "set-environment", "setenv", "grt:u", 1, 2, "[-gru] " CMD_TARGET_SESSION_USAGE " name [value]", 0, NULL, NULL, cmd_set_environment_exec }; enum cmd_retval cmd_set_environment_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct session *s; struct environ *env; const char *name, *value; name = args->argv[0]; if (*name == '\0') { cmdq_error(cmdq, "empty variable name"); return (CMD_RETURN_ERROR); } if (strchr(name, '=') != NULL) { cmdq_error(cmdq, "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 ((s = cmd_find_session(cmdq, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); env = &s->environ; } if (args_has(self->args, 'u')) { if (value != NULL) { cmdq_error(cmdq, "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(cmdq, "can't specify a value with -r"); return (CMD_RETURN_ERROR); } environ_set(env, name, NULL); } else { if (value == NULL) { cmdq_error(cmdq, "no value specified"); return (CMD_RETURN_ERROR); } environ_set(env, name, value); } return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-set-option.c000066400000000000000000000301721242461015400157230ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Set an option. */ enum cmd_retval cmd_set_option_exec(struct cmd *, struct cmd_q *); enum cmd_retval cmd_set_option_user(struct cmd *, struct cmd_q *, const char *, const char *); int cmd_set_option_unset(struct cmd *, struct cmd_q *, const struct options_table_entry *, struct options *, const char *); int cmd_set_option_set(struct cmd *, struct cmd_q *, const struct options_table_entry *, struct options *, const char *); struct options_entry *cmd_set_option_string(struct cmd *, struct cmd_q *, const struct options_table_entry *, struct options *, const char *); struct options_entry *cmd_set_option_number(struct cmd *, struct cmd_q *, const struct options_table_entry *, struct options *, const char *); struct options_entry *cmd_set_option_key(struct cmd *, struct cmd_q *, const struct options_table_entry *, struct options *, const char *); struct options_entry *cmd_set_option_colour(struct cmd *, struct cmd_q *, const struct options_table_entry *, struct options *, const char *); struct options_entry *cmd_set_option_attributes(struct cmd *, struct cmd_q *, const struct options_table_entry *, struct options *, const char *); struct options_entry *cmd_set_option_flag(struct cmd *, struct cmd_q *, const struct options_table_entry *, struct options *, const char *); struct options_entry *cmd_set_option_choice(struct cmd *, struct cmd_q *, const struct options_table_entry *, struct options *, const char *); const struct cmd_entry cmd_set_option_entry = { "set-option", "set", "agoqst:uw", 1, 2, "[-agosquw] [-t target-session|target-window] option [value]", 0, NULL, NULL, cmd_set_option_exec }; const struct cmd_entry cmd_set_window_option_entry = { "set-window-option", "setw", "agoqt:u", 1, 2, "[-agoqu] " CMD_TARGET_WINDOW_USAGE " option [value]", 0, NULL, NULL, cmd_set_option_exec }; enum cmd_retval cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; const struct options_table_entry *table, *oe; struct session *s; struct winlink *wl; struct client *c; struct options *oo; struct window *w; const char *optstr, *valstr; u_int i; /* Get the option name and value. */ optstr = args->argv[0]; if (*optstr == '\0') { cmdq_error(cmdq, "invalid option"); return (CMD_RETURN_ERROR); } if (args->argc < 2) valstr = NULL; else valstr = args->argv[1]; /* Is this a user option? */ if (*optstr == '@') return (cmd_set_option_user(self, cmdq, optstr, valstr)); /* Find the option entry, try each table. */ table = oe = NULL; if (options_table_find(optstr, &table, &oe) != 0) { cmdq_error(cmdq, "ambiguous option: %s", optstr); return (CMD_RETURN_ERROR); } if (oe == NULL) { cmdq_error(cmdq, "unknown option: %s", optstr); return (CMD_RETURN_ERROR); } /* Work out the tree from the table. */ if (table == server_options_table) oo = &global_options; else if (table == window_options_table) { if (args_has(self->args, 'g')) oo = &global_w_options; else { wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); if (wl == NULL) return (CMD_RETURN_ERROR); oo = &wl->window->options; } } else if (table == session_options_table) { if (args_has(self->args, 'g')) oo = &global_s_options; else { s = cmd_find_session(cmdq, args_get(args, 't'), 0); if (s == NULL) return (CMD_RETURN_ERROR); oo = &s->options; } } else { cmdq_error(cmdq, "unknown table"); return (CMD_RETURN_ERROR); } /* Unset or set the option. */ if (args_has(args, 'u')) { if (cmd_set_option_unset(self, cmdq, oe, oo, valstr) != 0) return (CMD_RETURN_ERROR); } else { if (args_has(args, 'o') && options_find1(oo, optstr) != NULL) { if (!args_has(args, 'q')) cmdq_print(cmdq, "already set: %s", optstr); return (CMD_RETURN_NORMAL); } if (cmd_set_option_set(self, cmdq, oe, oo, valstr) != 0) return (CMD_RETURN_ERROR); } /* Start or stop timers when automatic-rename changed. */ if (strcmp (oe->name, "automatic-rename") == 0) { for (i = 0; i < ARRAY_LENGTH(&windows); i++) { if ((w = ARRAY_ITEM(&windows, i)) == NULL) continue; if (options_get_number(&w->options, "automatic-rename")) queue_window_name(w); else if (event_initialized(&w->name_timer)) evtimer_del(&w->name_timer); } } /* Update sizes and redraw. May not need it but meh. */ recalculate_sizes(); for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c != NULL && c->session != NULL) server_redraw_client(c); } return (CMD_RETURN_NORMAL); } /* Set user option. */ enum cmd_retval cmd_set_option_user(struct cmd *self, struct cmd_q *cmdq, const char* optstr, const char *valstr) { struct args *args = self->args; struct session *s; struct winlink *wl; struct options *oo; if (args_has(args, 's')) oo = &global_options; else if (args_has(self->args, 'w') || self->entry == &cmd_set_window_option_entry) { if (args_has(self->args, 'g')) oo = &global_w_options; else { wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); if (wl == NULL) return (CMD_RETURN_ERROR); oo = &wl->window->options; } } else { if (args_has(self->args, 'g')) oo = &global_s_options; else { s = cmd_find_session(cmdq, args_get(args, 't'), 0); if (s == NULL) return (CMD_RETURN_ERROR); oo = &s->options; } } if (args_has(args, 'u')) { if (options_find1(oo, optstr) == NULL) { cmdq_error(cmdq, "unknown option: %s", optstr); return (CMD_RETURN_ERROR); } if (valstr != NULL) { cmdq_error(cmdq, "value passed to unset option: %s", optstr); return (CMD_RETURN_ERROR); } options_remove(oo, optstr); } else { if (valstr == NULL) { cmdq_error(cmdq, "empty value"); return (CMD_RETURN_ERROR); } if (args_has(args, 'o') && options_find1(oo, optstr) != NULL) { if (!args_has(args, 'q')) cmdq_print(cmdq, "already set: %s", optstr); return (CMD_RETURN_NORMAL); } options_set_string(oo, optstr, "%s", valstr); if (!args_has(args, 'q')) { cmdq_info(cmdq, "set option: %s -> %s", optstr, valstr); } } return (CMD_RETURN_NORMAL); } /* Unset an option. */ int cmd_set_option_unset(struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { struct args *args = self->args; if (args_has(args, 'g')) { cmdq_error(cmdq, "can't unset global option: %s", oe->name); return (-1); } if (value != NULL) { cmdq_error(cmdq, "value passed to unset option: %s", oe->name); return (-1); } options_remove(oo, oe->name); if (!args_has(args, 'q')) cmdq_info(cmdq, "unset option: %s", oe->name); return (0); } /* Set an option. */ int cmd_set_option_set(struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { struct args *args = self->args; struct options_entry *o; const char *s; if (oe->type != OPTIONS_TABLE_FLAG && value == NULL) { cmdq_error(cmdq, "empty value"); return (-1); } o = NULL; switch (oe->type) { case OPTIONS_TABLE_STRING: o = cmd_set_option_string(self, cmdq, oe, oo, value); break; case OPTIONS_TABLE_NUMBER: o = cmd_set_option_number(self, cmdq, oe, oo, value); break; case OPTIONS_TABLE_KEY: o = cmd_set_option_key(self, cmdq, oe, oo, value); break; case OPTIONS_TABLE_COLOUR: o = cmd_set_option_colour(self, cmdq, oe, oo, value); break; case OPTIONS_TABLE_ATTRIBUTES: o = cmd_set_option_attributes(self, cmdq, oe, oo, value); break; case OPTIONS_TABLE_FLAG: o = cmd_set_option_flag(self, cmdq, oe, oo, value); break; case OPTIONS_TABLE_CHOICE: o = cmd_set_option_choice(self, cmdq, oe, oo, value); break; } if (o == NULL) return (-1); s = options_table_print_entry(oe, o, 0); if (!args_has(args, 'q')) cmdq_info(cmdq, "set option: %s -> %s", oe->name, s); return (0); } /* Set a string option. */ struct options_entry * cmd_set_option_string(struct cmd *self, unused struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { struct args *args = self->args; struct options_entry *o; char *oldval, *newval; if (args_has(args, 'a')) { oldval = options_get_string(oo, oe->name); xasprintf(&newval, "%s%s", oldval, value); } else newval = xstrdup(value); o = options_set_string(oo, oe->name, "%s", newval); free(newval); return (o); } /* Set a number option. */ struct options_entry * cmd_set_option_number(unused struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { long long ll; const char *errstr; ll = strtonum(value, oe->minimum, oe->maximum, &errstr); if (errstr != NULL) { cmdq_error(cmdq, "value is %s: %s", errstr, value); return (NULL); } return (options_set_number(oo, oe->name, ll)); } /* Set a key option. */ struct options_entry * cmd_set_option_key(unused struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { int key; if ((key = key_string_lookup_string(value)) == KEYC_NONE) { cmdq_error(cmdq, "bad key: %s", value); return (NULL); } return (options_set_number(oo, oe->name, key)); } /* Set a colour option. */ struct options_entry * cmd_set_option_colour(unused struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { int colour; if ((colour = colour_fromstring(value)) == -1) { cmdq_error(cmdq, "bad colour: %s", value); return (NULL); } return (options_set_number(oo, oe->name, colour)); } /* Set an attributes option. */ struct options_entry * cmd_set_option_attributes(unused struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { int attr; if ((attr = attributes_fromstring(value)) == -1) { cmdq_error(cmdq, "bad attributes: %s", value); return (NULL); } return (options_set_number(oo, oe->name, attr)); } /* Set a flag option. */ struct options_entry * cmd_set_option_flag(unused struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { int flag; if (value == NULL || *value == '\0') flag = !options_get_number(oo, oe->name); else { if ((value[0] == '1' && value[1] == '\0') || strcasecmp(value, "on") == 0 || strcasecmp(value, "yes") == 0) flag = 1; else if ((value[0] == '0' && value[1] == '\0') || strcasecmp(value, "off") == 0 || strcasecmp(value, "no") == 0) flag = 0; else { cmdq_error(cmdq, "bad value: %s", value); return (NULL); } } return (options_set_number(oo, oe->name, flag)); } /* Set a choice option. */ struct options_entry * cmd_set_option_choice(unused struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *oe, struct options *oo, const char *value) { const char **choicep; int n, choice = -1; n = 0; for (choicep = oe->choices; *choicep != NULL; choicep++) { n++; if (strncmp(*choicep, value, strlen(value)) != 0) continue; if (choice != -1) { cmdq_error(cmdq, "ambiguous value: %s", value); return (NULL); } choice = n - 1; } if (choice == -1) { cmdq_error(cmdq, "unknown value: %s", value); return (NULL); } return (options_set_number(oo, oe->name, choice)); } tmate-1.8.10/cmd-show-environment.c000066400000000000000000000041501242461015400171410ustar00rootroot00000000000000/* $Id$ */ /* * 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. */ enum cmd_retval cmd_show_environment_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_show_environment_entry = { "show-environment", "showenv", "gt:", 0, 1, "[-g] " CMD_TARGET_SESSION_USAGE " [name]", 0, NULL, NULL, cmd_show_environment_exec }; enum cmd_retval cmd_show_environment_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct session *s; struct environ *env; struct environ_entry *envent; if (args_has(self->args, 'g')) env = &global_environ; else { if ((s = cmd_find_session(cmdq, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); env = &s->environ; } if (args->argc != 0) { envent = environ_find(env, args->argv[0]); if (envent == NULL) { cmdq_error(cmdq, "unknown variable: %s", args->argv[0]); return (CMD_RETURN_ERROR); } if (envent->value != NULL) cmdq_print(cmdq, "%s=%s", envent->name, envent->value); else cmdq_print(cmdq, "-%s", envent->name); return (CMD_RETURN_NORMAL); } RB_FOREACH(envent, environ, env) { if (envent->value != NULL) cmdq_print(cmdq, "%s=%s", envent->name, envent->value); else cmdq_print(cmdq, "-%s", envent->name); } return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-show-messages.c000066400000000000000000000032461242461015400164110ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Show client message log. */ enum cmd_retval cmd_show_messages_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_show_messages_entry = { "show-messages", "showmsgs", "t:", 0, 0, CMD_TARGET_CLIENT_USAGE, 0, NULL, NULL, cmd_show_messages_exec }; enum cmd_retval cmd_show_messages_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c; struct message_entry *msg; char *tim; u_int i; if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); for (i = 0; i < ARRAY_LENGTH(&c->message_log); i++) { msg = &ARRAY_ITEM(&c->message_log, i); tim = ctime(&msg->msg_time); *strchr(tim, '\n') = '\0'; cmdq_print(cmdq, "%s %s", tim, msg->msg); } return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-show-options.c000066400000000000000000000112251242461015400162710ustar00rootroot00000000000000/* $Id$ */ /* * 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. */ enum cmd_retval cmd_show_options_exec(struct cmd *, struct cmd_q *); enum cmd_retval cmd_show_options_one(struct cmd *, struct cmd_q *, struct options *, int); enum cmd_retval cmd_show_options_all(struct cmd *, struct cmd_q *, const struct options_table_entry *, struct options *); const struct cmd_entry cmd_show_options_entry = { "show-options", "show", "gqst:vw", 0, 1, "[-gqsvw] [-t target-session|target-window] [option]", 0, NULL, NULL, cmd_show_options_exec }; const struct cmd_entry cmd_show_window_options_entry = { "show-window-options", "showw", "gvt:", 0, 1, "[-gv] " CMD_TARGET_WINDOW_USAGE " [option]", 0, NULL, NULL, cmd_show_options_exec }; enum cmd_retval cmd_show_options_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct session *s; struct winlink *wl; const struct options_table_entry *table; struct options *oo; int quiet; if (args_has(self->args, 's')) { oo = &global_options; table = server_options_table; } else if (args_has(self->args, 'w') || self->entry == &cmd_show_window_options_entry) { table = window_options_table; if (args_has(self->args, 'g')) oo = &global_w_options; else { wl = cmd_find_window(cmdq, args_get(args, 't'), NULL); if (wl == NULL) return (CMD_RETURN_ERROR); oo = &wl->window->options; } } else { table = session_options_table; if (args_has(self->args, 'g')) oo = &global_s_options; else { s = cmd_find_session(cmdq, args_get(args, 't'), 0); if (s == NULL) return (CMD_RETURN_ERROR); oo = &s->options; } } quiet = args_has(self->args, 'q'); if (args->argc == 0) return (cmd_show_options_all(self, cmdq, table, oo)); else return (cmd_show_options_one(self, cmdq, oo, quiet)); } enum cmd_retval cmd_show_options_one(struct cmd *self, struct cmd_q *cmdq, struct options *oo, int quiet) { struct args *args = self->args; const struct options_table_entry *table, *oe; struct options_entry *o; const char *optval; if (*args->argv[0] == '@') { if ((o = options_find1(oo, args->argv[0])) == NULL) { if (quiet) return (CMD_RETURN_NORMAL); cmdq_error(cmdq, "unknown option: %s", args->argv[0]); return (CMD_RETURN_ERROR); } if (args_has(self->args, 'v')) cmdq_print(cmdq, "%s", o->str); else cmdq_print(cmdq, "%s \"%s\"", o->name, o->str); return (CMD_RETURN_NORMAL); } table = oe = NULL; if (options_table_find(args->argv[0], &table, &oe) != 0) { cmdq_error(cmdq, "ambiguous option: %s", args->argv[0]); return (CMD_RETURN_ERROR); } if (oe == NULL) { if (quiet) return (CMD_RETURN_NORMAL); cmdq_error(cmdq, "unknown option: %s", args->argv[0]); return (CMD_RETURN_ERROR); } if ((o = options_find1(oo, oe->name)) == NULL) return (CMD_RETURN_NORMAL); optval = options_table_print_entry(oe, o, args_has(self->args, 'v')); if (args_has(self->args, 'v')) cmdq_print(cmdq, "%s", optval); else cmdq_print(cmdq, "%s %s", oe->name, optval); return (CMD_RETURN_NORMAL); } enum cmd_retval cmd_show_options_all(struct cmd *self, struct cmd_q *cmdq, const struct options_table_entry *table, struct options *oo) { const struct options_table_entry *oe; struct options_entry *o; const char *optval; RB_FOREACH(o, options_tree, &oo->tree) { if (*o->name == '@') { if (args_has(self->args, 'v')) cmdq_print(cmdq, "%s", o->str); else cmdq_print(cmdq, "%s \"%s\"", o->name, o->str); } } for (oe = table; oe->name != NULL; oe++) { if ((o = options_find1(oo, oe->name)) == NULL) continue; optval = options_table_print_entry(oe, o, args_has(self->args, 'v')); if (args_has(self->args, 'v')) cmdq_print(cmdq, "%s", optval); else cmdq_print(cmdq, "%s %s", oe->name, optval); } return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-source-file.c000066400000000000000000000045261242461015400160430ustar00rootroot00000000000000/* $Id$ */ /* * 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 "tmux.h" /* * Sources a configuration file. */ enum cmd_retval cmd_source_file_exec(struct cmd *, struct cmd_q *); void cmd_source_file_show(struct cmd_q *); void cmd_source_file_done(struct cmd_q *); const struct cmd_entry cmd_source_file_entry = { "source-file", "source", "", 1, 1, "path", 0, NULL, NULL, cmd_source_file_exec }; enum cmd_retval cmd_source_file_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct cmd_q *cmdq1; char *cause; cmdq1 = cmdq_new(NULL); cmdq1->emptyfn = cmd_source_file_done; cmdq1->data = cmdq; switch (load_cfg(args->argv[0], cmdq1, &cause)) { case -1: if (cfg_references == 0) { cmdq_free(cmdq1); cmdq_error(cmdq, "%s", cause); free(cause); return (CMD_RETURN_ERROR); } ARRAY_ADD(&cfg_causes, cause); /* FALLTHROUGH */ case 0: if (cfg_references == 0) cmd_source_file_show(cmdq); cmdq_free(cmdq1); return (CMD_RETURN_NORMAL); } cmdq->references++; cfg_references++; cmdq_continue(cmdq1); return (CMD_RETURN_WAIT); } void cmd_source_file_show(struct cmd_q *cmdq) { u_int i; char *cause; for (i = 0; i < ARRAY_LENGTH(&cfg_causes); i++) { cause = ARRAY_ITEM(&cfg_causes, i); cmdq_print(cmdq, "%s", cause); free(cause); } ARRAY_FREE(&cfg_causes); } void cmd_source_file_done(struct cmd_q *cmdq1) { struct cmd_q *cmdq = cmdq1->data; cmdq_free(cmdq1); cfg_references--; if (cmdq_free(cmdq)) return; if (cfg_references == 0) cmd_source_file_show(cmdq); cmdq_continue(cmdq); } tmate-1.8.10/cmd-split-window.c000066400000000000000000000104431242461015400162610ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Split a window (add a new pane). */ void cmd_split_window_key_binding(struct cmd *, int); enum cmd_retval cmd_split_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_split_window_entry = { "split-window", "splitw", "c:dF:l:hp:Pt:v", 0, 1, "[-dhvP] [-c start-directory] [-F format] [-p percentage|-l size] " CMD_TARGET_PANE_USAGE " [command]", 0, cmd_split_window_key_binding, NULL, cmd_split_window_exec }; void cmd_split_window_key_binding(struct cmd *self, int key) { self->args = args_create(0); if (key == '%') args_set(self->args, 'h', NULL); } enum cmd_retval cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct session *s; struct winlink *wl; struct window *w; struct window_pane *wp, *new_wp = NULL; struct environ env; const char *cmd, *cwd, *shell; char *cause, *new_cause; u_int hlimit; int size, percentage; enum layout_type type; struct layout_cell *lc; const char *template; struct client *c; struct format_tree *ft; char *cp; if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) return (CMD_RETURN_ERROR); w = wl->window; server_unzoom_window(w); environ_init(&env); environ_copy(&global_environ, &env); environ_copy(&s->environ, &env); server_fill_environ(s, &env); if (args->argc == 0) cmd = options_get_string(&s->options, "default-command"); else cmd = args->argv[0]; cwd = cmd_get_default_path(cmdq, args_get(args, 'c')); 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) { xasprintf(&new_cause, "size %s", cause); free(cause); cause = new_cause; goto error; } } else if (args_has(args, 'p')) { percentage = args_strtonum(args, 'p', 0, INT_MAX, &cause); if (cause != NULL) { xasprintf(&new_cause, "percentage %s", cause); free(cause); cause = new_cause; goto error; } if (type == LAYOUT_TOPBOTTOM) size = (wp->sy * percentage) / 100; else size = (wp->sx * percentage) / 100; } hlimit = options_get_number(&s->options, "history-limit"); shell = options_get_string(&s->options, "default-shell"); if (*shell == '\0' || areshell(shell)) shell = _PATH_BSHELL; if ((lc = layout_split_pane(wp, type, size, 0)) == NULL) { cause = xstrdup("pane too small"); goto error; } new_wp = window_add_pane(w, hlimit); if (window_pane_spawn( new_wp, cmd, shell, cwd, &env, s->tio, &cause) != 0) goto error; layout_assign_pane(lc, new_wp); server_redraw_window(w); if (!args_has(args, 'd')) { window_set_active_pane(w, new_wp); session_select(s, wl->idx); server_redraw_session(s); } else server_status_session(s); environ_free(&env); if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) template = SPLIT_WINDOW_TEMPLATE; ft = format_create(); if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL) format_client(ft, c); format_session(ft, s); format_winlink(ft, s, wl); format_window_pane(ft, new_wp); cp = format_expand(ft, template); cmdq_print(cmdq, "%s", cp); free(cp); format_free(ft); } notify_window_layout_changed(w); return (CMD_RETURN_NORMAL); error: environ_free(&env); if (new_wp != NULL) window_remove_pane(w, new_wp); cmdq_error(cmdq, "create pane failed: %s", cause); free(cause); return (CMD_RETURN_ERROR); } tmate-1.8.10/cmd-start-server.c000066400000000000000000000023551242461015400162650ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "tmux.h" /* * Start the server and do nothing else. */ enum cmd_retval cmd_start_server_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_start_server_entry = { "start-server", "start", "", 0, 0, "", CMD_STARTSERVER, NULL, NULL, cmd_start_server_exec }; enum cmd_retval cmd_start_server_exec(unused struct cmd *self, unused struct cmd_q *cmdq) { return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-string.c000066400000000000000000000152451242461015400151340ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include "tmux.h" /* * Parse a command from a string. */ int cmd_string_getc(const char *, size_t *); void cmd_string_ungetc(size_t *); void cmd_string_copy(char **, char *, size_t *); char *cmd_string_string(const char *, size_t *, char, int); char *cmd_string_variable(const char *, size_t *); char *cmd_string_expand_tilde(const char *, size_t *); int cmd_string_getc(const char *s, size_t *p) { const u_char *ucs = s; if (ucs[*p] == '\0') return (EOF); return (ucs[(*p)++]); } void cmd_string_ungetc(size_t *p) { (*p)--; } /* * Parse command string. Returns -1 on error. If returning -1, cause is error * string, or NULL for empty command. */ int cmd_string_parse(const char *s, struct cmd_list **cmdlist, const char *file, u_int line, char **cause) { size_t p; int ch, i, argc, rval; char **argv, *buf, *t; const char *whitespace, *equals; size_t len; argv = NULL; argc = 0; buf = NULL; len = 0; *cause = NULL; *cmdlist = NULL; rval = -1; p = 0; for (;;) { ch = cmd_string_getc(s, &p); switch (ch) { case '\'': if ((t = cmd_string_string(s, &p, '\'', 0)) == NULL) goto error; cmd_string_copy(&buf, t, &len); break; case '"': if ((t = cmd_string_string(s, &p, '"', 1)) == NULL) goto error; cmd_string_copy(&buf, t, &len); break; case '$': if ((t = cmd_string_variable(s, &p)) == NULL) goto error; cmd_string_copy(&buf, t, &len); break; case '#': /* Comment: discard rest of line. */ while ((ch = cmd_string_getc(s, &p)) != EOF) ; /* FALLTHROUGH */ case EOF: case ' ': case '\t': if (buf != NULL) { buf = xrealloc(buf, 1, len + 1); buf[len] = '\0'; argv = xrealloc(argv, argc + 1, sizeof *argv); argv[argc++] = buf; buf = NULL; len = 0; } if (ch != EOF) break; while (argc != 0) { equals = strchr(argv[0], '='); whitespace = argv[0] + strcspn(argv[0], " \t"); if (equals == NULL || equals > whitespace) break; environ_put(&global_environ, argv[0]); argc--; memmove(argv, argv + 1, argc * (sizeof *argv)); } if (argc == 0) goto out; *cmdlist = cmd_list_parse(argc, argv, file, line, cause); if (*cmdlist == NULL) goto out; rval = 0; goto out; case '~': if (buf == NULL) { t = cmd_string_expand_tilde(s, &p); if (t == NULL) goto error; cmd_string_copy(&buf, t, &len); break; } /* FALLTHROUGH */ default: if (len >= SIZE_MAX - 2) goto error; buf = xrealloc(buf, 1, len + 1); buf[len++] = ch; break; } } error: xasprintf(cause, "invalid or unknown command: %s", s); out: free(buf); if (argv != NULL) { for (i = 0; i < argc; i++) free(argv[i]); free(argv); } return (rval); } void cmd_string_copy(char **dst, char *src, size_t *len) { size_t srclen; srclen = strlen(src); *dst = xrealloc(*dst, 1, *len + srclen + 1); strlcpy(*dst + *len, src, srclen + 1); *len += srclen; free(src); } char * cmd_string_string(const char *s, size_t *p, char endch, int esc) { int ch; char *buf, *t; size_t len; buf = NULL; len = 0; while ((ch = cmd_string_getc(s, p)) != endch) { switch (ch) { case EOF: goto error; case '\\': if (!esc) break; switch (ch = cmd_string_getc(s, p)) { case EOF: goto error; case 'e': ch = '\033'; break; case 'r': ch = '\r'; break; case 'n': ch = '\n'; break; case 't': ch = '\t'; break; } break; case '$': if (!esc) break; if ((t = cmd_string_variable(s, p)) == NULL) goto error; cmd_string_copy(&buf, t, &len); continue; } if (len >= SIZE_MAX - 2) goto error; buf = xrealloc(buf, 1, len + 1); buf[len++] = ch; } buf = xrealloc(buf, 1, len + 1); buf[len] = '\0'; return (buf); error: free(buf); return (NULL); } char * cmd_string_variable(const char *s, size_t *p) { int ch, fch; char *buf, *t; size_t len; struct environ_entry *envent; #define cmd_string_first(ch) ((ch) == '_' || \ ((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z')) #define cmd_string_other(ch) ((ch) == '_' || \ ((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z') || \ ((ch) >= '0' && (ch) <= '9')) buf = NULL; len = 0; fch = EOF; switch (ch = cmd_string_getc(s, p)) { case EOF: goto error; case '{': fch = '{'; ch = cmd_string_getc(s, p); if (!cmd_string_first(ch)) goto error; /* FALLTHROUGH */ default: if (!cmd_string_first(ch)) { xasprintf(&t, "$%c", ch); return (t); } buf = xrealloc(buf, 1, len + 1); buf[len++] = ch; for (;;) { ch = cmd_string_getc(s, p); if (ch == EOF || !cmd_string_other(ch)) break; else { if (len >= SIZE_MAX - 3) goto error; buf = xrealloc(buf, 1, len + 1); buf[len++] = ch; } } } if (fch == '{' && ch != '}') goto error; if (ch != EOF && fch != '{') cmd_string_ungetc(p); /* ch */ buf = xrealloc(buf, 1, len + 1); buf[len] = '\0'; envent = environ_find(&global_environ, buf); free(buf); if (envent == NULL) return (xstrdup("")); return (xstrdup(envent->value)); error: free(buf); return (NULL); } char * cmd_string_expand_tilde(const char *s, size_t *p) { struct passwd *pw; struct environ_entry *envent; char *home, *path, *username; home = NULL; if (cmd_string_getc(s, p) == '/') { envent = environ_find(&global_environ, "HOME"); if (envent != NULL && *envent->value != '\0') home = envent->value; else if ((pw = getpwuid(getuid())) != NULL) home = pw->pw_dir; } else { cmd_string_ungetc(p); if ((username = cmd_string_string(s, p, '/', 0)) == NULL) return (NULL); if ((pw = getpwnam(username)) != NULL) home = pw->pw_dir; free(username); } if (home == NULL) return (NULL); xasprintf(&path, "%s/", home); return (path); } tmate-1.8.10/cmd-suspend-client.c000066400000000000000000000030211242461015400165500ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Suspend client with SIGTSTP. */ enum cmd_retval cmd_suspend_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_suspend_client_entry = { "suspend-client", "suspendc", "t:", 0, 0, CMD_TARGET_CLIENT_USAGE, 0, NULL, NULL, cmd_suspend_client_exec }; enum cmd_retval cmd_suspend_client_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c; if ((c = cmd_find_client(cmdq, args_get(args, 't'), 0)) == NULL) return (CMD_RETURN_ERROR); tty_stop_tty(&c->tty); c->flags |= CLIENT_SUSPENDED; server_write_client(c, MSG_SUSPEND, NULL, 0); return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-swap-pane.c000066400000000000000000000101061242461015400155100ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Swap two panes. */ void cmd_swap_pane_key_binding(struct cmd *, int); enum cmd_retval cmd_swap_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_swap_pane_entry = { "swap-pane", "swapp", "dDs:t:U", 0, 0, "[-dDU] " CMD_SRCDST_PANE_USAGE, 0, cmd_swap_pane_key_binding, NULL, cmd_swap_pane_exec }; void cmd_swap_pane_key_binding(struct cmd *self, int key) { self->args = args_create(0); if (key == '{') args_set(self->args, 'U', NULL); else if (key == '}') args_set(self->args, 'D', NULL); } enum cmd_retval cmd_swap_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *src_wl, *dst_wl; struct window *src_w, *dst_w; struct window_pane *tmp_wp, *src_wp, *dst_wp; struct layout_cell *src_lc, *dst_lc; u_int sx, sy, xoff, yoff; dst_wl = cmd_find_pane(cmdq, args_get(args, 't'), NULL, &dst_wp); if (dst_wl == NULL) return (CMD_RETURN_ERROR); dst_w = dst_wl->window; server_unzoom_window(dst_w); if (!args_has(args, 's')) { src_w = dst_w; if (args_has(self->args, 'D')) { src_wp = TAILQ_NEXT(dst_wp, entry); if (src_wp == NULL) src_wp = TAILQ_FIRST(&dst_w->panes); } else if (args_has(self->args, 'U')) { src_wp = TAILQ_PREV(dst_wp, window_panes, entry); if (src_wp == NULL) src_wp = TAILQ_LAST(&dst_w->panes, window_panes); } else return (CMD_RETURN_NORMAL); } else { src_wl = cmd_find_pane(cmdq, args_get(args, 's'), NULL, &src_wp); if (src_wl == NULL) return (CMD_RETURN_ERROR); src_w = src_wl->window; } server_unzoom_window(src_w); #ifdef TMATE if (src_w != dst_w) { cmdq_error(cmdq, "swap pane on different window is not supported with tmate"); return (CMD_RETURN_ERROR); } #endif 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; dst_wp->window = src_w; sx = src_wp->sx; sy = src_wp->sy; xoff = src_wp->xoff; yoff = src_wp->yoff; src_wp->xoff = dst_wp->xoff; src_wp->yoff = dst_wp->yoff; window_pane_resize(src_wp, dst_wp->sx, dst_wp->sy); dst_wp->xoff = xoff; dst_wp->yoff = yoff; window_pane_resize(dst_wp, sx, sy); if (!args_has(self->args, 'd')) { if (src_w != dst_w) { window_set_active_pane(src_w, dst_wp); window_set_active_pane(dst_w, src_wp); } else { tmp_wp = dst_wp; if (!window_pane_visible(tmp_wp)) tmp_wp = src_wp; window_set_active_pane(src_w, tmp_wp); } } else { if (src_w->active == src_wp) window_set_active_pane(src_w, dst_wp); if (dst_w->active == dst_wp) window_set_active_pane(dst_w, src_wp); } if (src_w != dst_w) { if (src_w->last == src_wp) src_w->last = NULL; if (dst_w->last == dst_wp) dst_w->last = NULL; } server_redraw_window(src_w); server_redraw_window(dst_w); return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-swap-window.c000066400000000000000000000050021242461015400160730ustar00rootroot00000000000000/* $Id$ */ /* * 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. */ enum cmd_retval cmd_swap_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_swap_window_entry = { "swap-window", "swapw", "ds:t:", 0, 0, "[-d] " CMD_SRCDST_WINDOW_USAGE, 0, NULL, NULL, cmd_swap_window_exec }; enum cmd_retval cmd_swap_window_exec(struct cmd *self, struct cmd_q *cmdq) { #ifdef TMATE cmdq_error(cmdq, "swap window is not supported with tmate"); return (CMD_RETURN_ERROR); #else struct args *args = self->args; const char *target_src, *target_dst; struct session *src, *dst; struct session_group *sg_src, *sg_dst; struct winlink *wl_src, *wl_dst; struct window *w; target_src = args_get(args, 's'); if ((wl_src = cmd_find_window(cmdq, target_src, &src)) == NULL) return (CMD_RETURN_ERROR); target_dst = args_get(args, 't'); if ((wl_dst = cmd_find_window(cmdq, target_dst, &dst)) == NULL) return (CMD_RETURN_ERROR); sg_src = session_group_find(src); sg_dst = session_group_find(dst); if (src != dst && sg_src != NULL && sg_dst != NULL && sg_src == sg_dst) { cmdq_error(cmdq, "can't move window, sessions are grouped"); return (CMD_RETURN_ERROR); } if (wl_dst->window == wl_src->window) return (CMD_RETURN_NORMAL); w = wl_dst->window; wl_dst->window = wl_src->window; wl_src->window = w; if (!args_has(self->args, 'd')) { session_select(dst, wl_dst->idx); if (src != dst) session_select(src, wl_src->idx); } session_group_synchronize_from(src); server_redraw_session_group(src); if (src != dst) { session_group_synchronize_from(dst); server_redraw_session_group(dst); } recalculate_sizes(); return (CMD_RETURN_NORMAL); #endif } tmate-1.8.10/cmd-switch-client.c000066400000000000000000000057231242461015400164030ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Switch client to a different session. */ void cmd_switch_client_key_binding(struct cmd *, int); enum cmd_retval cmd_switch_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_switch_client_entry = { "switch-client", "switchc", "lc:npt:r", 0, 0, "[-lnpr] [-c target-client] [-t target-session]", CMD_READONLY, cmd_switch_client_key_binding, NULL, cmd_switch_client_exec }; void cmd_switch_client_key_binding(struct cmd *self, int key) { self->args = args_create(0); switch (key) { case '(': args_set(self->args, 'p', NULL); break; case ')': args_set(self->args, 'n', NULL); break; case 'L': args_set(self->args, 'l', NULL); break; } } enum cmd_retval cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c; struct session *s; if ((c = cmd_find_client(cmdq, args_get(args, 'c'), 0)) == NULL) return (CMD_RETURN_ERROR); if (args_has(args, 'r')) { if (c->flags & CLIENT_READONLY) { c->flags &= ~CLIENT_READONLY; cmdq_info(cmdq, "made client writable"); } else { c->flags |= CLIENT_READONLY; cmdq_info(cmdq, "made client read-only"); } } s = NULL; if (args_has(args, 'n')) { if ((s = session_next_session(c->session)) == NULL) { cmdq_error(cmdq, "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(cmdq, "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; if (s == NULL) { cmdq_error(cmdq, "can't find last session"); return (CMD_RETURN_ERROR); } } else s = cmd_find_session(cmdq, args_get(args, 't'), 0); if (s == NULL) return (CMD_RETURN_ERROR); if (c->session != NULL) c->last_session = c->session; c->session = s; session_update_activity(s); recalculate_sizes(); server_check_unattached(); server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-unbind-key.c000066400000000000000000000057761242461015400157030ustar00rootroot00000000000000/* $Id$ */ /* * 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. */ enum cmd_retval cmd_unbind_key_check(struct args *); enum cmd_retval cmd_unbind_key_exec(struct cmd *, struct cmd_q *); enum cmd_retval cmd_unbind_key_table(struct cmd *, struct cmd_q *, int); const struct cmd_entry cmd_unbind_key_entry = { "unbind-key", "unbind", "acnt:", 0, 1, "[-acn] [-t key-table] key", 0, NULL, cmd_unbind_key_check, cmd_unbind_key_exec }; enum cmd_retval cmd_unbind_key_check(struct args *args) { if (args_has(args, 'a') && args->argc != 0) return (CMD_RETURN_ERROR); if (!args_has(args, 'a') && args->argc != 1) return (CMD_RETURN_ERROR); return (CMD_RETURN_NORMAL); } enum cmd_retval cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct key_binding *bd; int key; if (!args_has(args, 'a')) { key = key_string_lookup_string(args->argv[0]); if (key == KEYC_NONE) { cmdq_error(cmdq, "unknown key: %s", args->argv[0]); return (CMD_RETURN_ERROR); } } else key = KEYC_NONE; if (args_has(args, 't')) return (cmd_unbind_key_table(self, cmdq, key)); if (key == KEYC_NONE) { while (!RB_EMPTY(&key_bindings)) { bd = RB_ROOT(&key_bindings); key_bindings_remove(bd->key); } return (CMD_RETURN_NORMAL); } if (!args_has(args, 'n')) key |= KEYC_PREFIX; key_bindings_remove(key); return (CMD_RETURN_NORMAL); } enum cmd_retval cmd_unbind_key_table(struct cmd *self, struct cmd_q *cmdq, int key) { struct args *args = self->args; const char *tablename; const struct mode_key_table *mtab; struct mode_key_binding *mbind, mtmp; tablename = args_get(args, 't'); if ((mtab = mode_key_findtable(tablename)) == NULL) { cmdq_error(cmdq, "unknown key table: %s", tablename); return (CMD_RETURN_ERROR); } if (key == KEYC_NONE) { while (!RB_EMPTY(mtab->tree)) { mbind = RB_ROOT(mtab->tree); RB_REMOVE(mode_key_tree, mtab->tree, mbind); free(mbind); } return (CMD_RETURN_NORMAL); } mtmp.key = key; mtmp.mode = !!args_has(args, 'c'); if ((mbind = RB_FIND(mode_key_tree, mtab->tree, &mtmp)) != NULL) { RB_REMOVE(mode_key_tree, mtab->tree, mbind); free(mbind); } return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd-unlink-window.c000066400000000000000000000040131242461015400164220ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "tmux.h" /* * Unlink a window, unless it would be destroyed by doing so (only one link). */ enum cmd_retval cmd_unlink_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_unlink_window_entry = { "unlink-window", "unlinkw", "kt:", 0, 0, "[-k] " CMD_TARGET_WINDOW_USAGE, 0, NULL, NULL, cmd_unlink_window_exec }; enum cmd_retval cmd_unlink_window_exec(struct cmd *self, struct cmd_q *cmdq) { #ifdef TMATE cmdq_error(cmdq, "unlink window is not supported with tmate"); return (CMD_RETURN_ERROR); #else struct args *args = self->args; struct winlink *wl; struct window *w; struct session *s, *s2; struct session_group *sg; u_int references; if ((wl = cmd_find_window(cmdq, args_get(args, 't'), &s)) == NULL) return (CMD_RETURN_ERROR); w = wl->window; sg = session_group_find(s); if (sg != NULL) { references = 0; TAILQ_FOREACH(s2, &sg->sessions, gentry) references++; } else references = 1; if (!args_has(self->args, 'k') && w->references == references) { cmdq_error(cmdq, "window is only linked to one session"); return (CMD_RETURN_ERROR); } server_unlink_window(s, wl); recalculate_sizes(); return (CMD_RETURN_NORMAL); #endif } tmate-1.8.10/cmd-wait-for.c000066400000000000000000000130421242461015400153470ustar00rootroot00000000000000/* $Id$ */ /* * 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" #include "tmate.h" /* * Block or wake a client on a named wait channel. */ enum cmd_retval cmd_wait_for_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_wait_for_entry = { "wait-for", "wait", "LSU", 1, 1, "[-LSU] channel", 0, NULL, NULL, cmd_wait_for_exec }; struct wait_channel { const char *name; int locked; TAILQ_HEAD(, cmd_q) waiters; TAILQ_HEAD(, cmd_q) lockers; RB_ENTRY(wait_channel) entry; }; RB_HEAD(wait_channels, wait_channel); struct wait_channels wait_channels = RB_INITIALIZER(wait_channels); int wait_channel_cmp(struct wait_channel *, struct wait_channel *); RB_PROTOTYPE(wait_channels, wait_channel, entry, wait_channel_cmp); RB_GENERATE(wait_channels, wait_channel, entry, wait_channel_cmp); int wait_channel_cmp(struct wait_channel *wc1, struct wait_channel *wc2) { return (strcmp(wc1->name, wc2->name)); } enum cmd_retval cmd_wait_for_signal(struct cmd_q *, const char *, struct wait_channel *); enum cmd_retval cmd_wait_for_wait(struct cmd_q *, const char *, struct wait_channel *); enum cmd_retval cmd_wait_for_lock(struct cmd_q *, const char *, struct wait_channel *); enum cmd_retval cmd_wait_for_unlock(struct cmd_q *, const char *, struct wait_channel *); enum cmd_retval cmd_wait_for_exec(struct cmd *self, struct cmd_q *cmdq) { 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(cmdq, name, wc)); if (args_has(args, 'L')) return (cmd_wait_for_lock(cmdq, name, wc)); if (args_has(args, 'U')) return (cmd_wait_for_unlock(cmdq, name, wc)); return (cmd_wait_for_wait(cmdq, name, wc)); } enum cmd_retval cmd_wait_for_signal(struct cmd_q *cmdq, const char *name, struct wait_channel *wc) { struct cmd_q *wq, *wq1; if (wc == NULL || TAILQ_EMPTY(&wc->waiters)) { cmdq_error(cmdq, "no waiting clients on %s", name); return (CMD_RETURN_ERROR); } TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) { TAILQ_REMOVE(&wc->waiters, wq, waitentry); if (!cmdq_free(wq)) cmdq_continue(wq); } if (!wc->locked) { RB_REMOVE(wait_channels, &wait_channels, wc); free((void*) wc->name); free(wc); } return (CMD_RETURN_NORMAL); } #ifdef TMATE void signal_waiting_clients(const char *name) { struct wait_channel *wc, wc0; struct cmd_q *wq, *wq1; wc0.name = name; wc = RB_FIND(wait_channels, &wait_channels, &wc0); if (wc == NULL || TAILQ_EMPTY(&wc->waiters)) { return; } TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) { TAILQ_REMOVE(&wc->waiters, wq, waitentry); if (!cmdq_free(wq)) cmdq_continue(wq); } if (!wc->locked) { RB_REMOVE(wait_channels, &wait_channels, wc); free((void*) wc->name); free(wc); } } #endif enum cmd_retval cmd_wait_for_wait(struct cmd_q *cmdq, const char *name, struct wait_channel *wc) { #ifdef TMATE if (!strcmp(name, "tmate-ready") && tmate_session.decoder.ready) return (CMD_RETURN_NORMAL); #endif if (cmdq->client == NULL || cmdq->client->session != NULL) { cmdq_error(cmdq, "not able to wait"); return (CMD_RETURN_ERROR); } if (wc == NULL) { wc = xmalloc(sizeof *wc); wc->name = xstrdup(name); wc->locked = 0; TAILQ_INIT(&wc->waiters); TAILQ_INIT(&wc->lockers); RB_INSERT(wait_channels, &wait_channels, wc); } TAILQ_INSERT_TAIL(&wc->waiters, cmdq, waitentry); cmdq->references++; return (CMD_RETURN_WAIT); } enum cmd_retval cmd_wait_for_lock(struct cmd_q *cmdq, const char *name, struct wait_channel *wc) { if (cmdq->client == NULL || cmdq->client->session != NULL) { cmdq_error(cmdq, "not able to lock"); return (CMD_RETURN_ERROR); } if (wc == NULL) { wc = xmalloc(sizeof *wc); wc->name = xstrdup(name); wc->locked = 0; TAILQ_INIT(&wc->waiters); TAILQ_INIT(&wc->lockers); RB_INSERT(wait_channels, &wait_channels, wc); } if (wc->locked) { TAILQ_INSERT_TAIL(&wc->lockers, cmdq, waitentry); cmdq->references++; return (CMD_RETURN_WAIT); } wc->locked = 1; return (CMD_RETURN_NORMAL); } enum cmd_retval cmd_wait_for_unlock(struct cmd_q *cmdq, const char *name, struct wait_channel *wc) { struct cmd_q *wq; if (wc == NULL || !wc->locked) { cmdq_error(cmdq, "channel %s not locked", name); return (CMD_RETURN_ERROR); } if ((wq = TAILQ_FIRST(&wc->lockers)) != NULL) { TAILQ_REMOVE(&wc->lockers, wq, waitentry); if (!cmdq_free(wq)) cmdq_continue(wq); } else { wc->locked = 0; if (TAILQ_EMPTY(&wc->waiters)) { RB_REMOVE(wait_channels, &wait_channels, wc); free((void*) wc->name); free(wc); } } return (CMD_RETURN_NORMAL); } tmate-1.8.10/cmd.c000066400000000000000000000774661242461015400136450ustar00rootroot00000000000000/* $Id$ */ /* * 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" 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_list_entry, &cmd_choose_session_entry, &cmd_choose_tree_entry, &cmd_choose_window_entry, &cmd_clear_history_entry, &cmd_clock_mode_entry, &cmd_command_prompt_entry, &cmd_confirm_before_entry, &cmd_copy_mode_entry, &cmd_delete_buffer_entry, &cmd_detach_client_entry, &cmd_display_message_entry, &cmd_display_panes_entry, &cmd_find_window_entry, &cmd_has_session_entry, &cmd_if_shell_entry, &cmd_join_pane_entry, &cmd_kill_pane_entry, &cmd_kill_server_entry, &cmd_kill_session_entry, &cmd_kill_window_entry, &cmd_last_pane_entry, &cmd_last_window_entry, &cmd_link_window_entry, &cmd_list_buffers_entry, &cmd_list_clients_entry, &cmd_list_commands_entry, &cmd_list_keys_entry, &cmd_list_panes_entry, &cmd_list_sessions_entry, &cmd_list_windows_entry, &cmd_load_buffer_entry, &cmd_lock_client_entry, &cmd_lock_server_entry, &cmd_lock_session_entry, &cmd_move_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_respawn_pane_entry, &cmd_respawn_window_entry, &cmd_rotate_window_entry, &cmd_run_shell_entry, &cmd_save_buffer_entry, &cmd_select_layout_entry, &cmd_select_pane_entry, &cmd_select_window_entry, &cmd_send_keys_entry, &cmd_send_prefix_entry, &cmd_server_info_entry, &cmd_set_buffer_entry, &cmd_set_environment_entry, &cmd_set_option_entry, &cmd_set_window_option_entry, &cmd_show_buffer_entry, &cmd_show_environment_entry, &cmd_show_messages_entry, &cmd_show_options_entry, &cmd_show_window_options_entry, &cmd_source_file_entry, &cmd_split_window_entry, &cmd_start_server_entry, &cmd_suspend_client_entry, &cmd_swap_pane_entry, &cmd_swap_window_entry, &cmd_switch_client_entry, &cmd_unbind_key_entry, &cmd_unlink_window_entry, &cmd_wait_for_entry, NULL }; int cmd_session_better(struct session *, struct session *, int); struct session *cmd_choose_session_list(struct sessionslist *); struct session *cmd_choose_session(int); struct client *cmd_choose_client(struct clients *); struct client *cmd_lookup_client(const char *); struct session *cmd_lookup_session(const char *, int *); struct session *cmd_lookup_session_id(const char *); struct winlink *cmd_lookup_window(struct session *, const char *, int *); int cmd_lookup_index(struct session *, const char *, int *); struct window_pane *cmd_lookup_paneid(const char *); struct winlink *cmd_lookup_winlink_windowid(struct session *, const char *); struct window *cmd_lookup_windowid(const char *); struct session *cmd_window_session(struct cmd_q *, struct window *, struct winlink **); struct winlink *cmd_find_window_offset(const char *, struct session *, int *); int cmd_find_index_offset(const char *, struct session *, int *); struct window_pane *cmd_find_pane_offset(const char *, struct winlink *); int cmd_pack_argv(int argc, char **argv, char *buf, size_t len) { size_t arglen; int i; *buf = '\0'; for (i = 0; i < argc; i++) { if (strlcpy(buf, argv[i], len) >= len) return (-1); arglen = strlen(argv[i]) + 1; buf += arglen; len -= arglen; } return (0); } int cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv) { int i; size_t arglen; if (argc == 0) return (0); *argv = xcalloc(argc, sizeof **argv); buf[len - 1] = '\0'; for (i = 0; i < argc; i++) { if (len == 0) { cmd_free_argv(argc, *argv); return (-1); } arglen = strlen(buf) + 1; (*argv)[i] = xstrdup(buf); buf += arglen; len -= arglen; } return (0); } char ** cmd_copy_argv(int argc, char *const *argv) { char **new_argv; int i; if (argc == 0) return (NULL); new_argv = xcalloc(argc, sizeof *new_argv); for (i = 0; i < argc; i++) { if (argv[i] != NULL) new_argv[i] = xstrdup(argv[i]); } return (new_argv); } void cmd_free_argv(int argc, char **argv) { int i; if (argc == 0) return; for (i = 0; i < argc; i++) free(argv[i]); free(argv); } struct cmd * cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause) { const struct cmd_entry **entryp, *entry; struct cmd *cmd; struct args *args; char s[BUFSIZ]; int ambiguous = 0; *cause = NULL; if (argc == 0) { xasprintf(cause, "no command"); return (NULL); } entry = NULL; for (entryp = cmd_table; *entryp != NULL; entryp++) { if ((*entryp)->alias != NULL && strcmp((*entryp)->alias, argv[0]) == 0) { ambiguous = 0; entry = *entryp; break; } if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0) continue; if (entry != NULL) ambiguous = 1; entry = *entryp; /* Bail now if an exact match. */ if (strcmp(entry->name, argv[0]) == 0) break; } if (ambiguous) goto ambiguous; if (entry == NULL) { xasprintf(cause, "unknown command: %s", argv[0]); return (NULL); } args = args_parse(entry->args_template, argc, argv); if (args == NULL) goto usage; if (entry->args_lower != -1 && args->argc < entry->args_lower) goto usage; if (entry->args_upper != -1 && args->argc > entry->args_upper) goto usage; if (entry->check != NULL && entry->check(args) != 0) goto usage; cmd = xcalloc(1, sizeof *cmd); cmd->entry = entry; cmd->args = args; if (file != NULL) cmd->file = xstrdup(file); cmd->line = line; return (cmd); ambiguous: *s = '\0'; for (entryp = cmd_table; *entryp != NULL; entryp++) { if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0) continue; if (strlcat(s, (*entryp)->name, sizeof s) >= sizeof s) break; if (strlcat(s, ", ", sizeof s) >= sizeof s) break; } s[strlen(s) - 2] = '\0'; xasprintf(cause, "ambiguous command: %s, could be: %s", argv[0], s); return (NULL); usage: if (args != NULL) args_free(args); xasprintf(cause, "usage: %s %s", entry->name, entry->usage); return (NULL); } size_t cmd_print(struct cmd *cmd, char *buf, size_t len) { size_t off, used; off = xsnprintf(buf, len, "%s ", cmd->entry->name); if (off < len) { used = args_print(cmd->args, buf + off, len - off); if (used == 0) off--; else off += used; buf[off] = '\0'; } return (off); } /* * Figure out the current session. Use: 1) the current session, if the command * context has one; 2) the most recently used session containing the pty of the * calling client, if any; 3) the session specified in the TMUX variable from * the environment (as passed from the client); 4) the most recently used * session from all sessions. */ struct session * cmd_current_session(struct cmd_q *cmdq, int prefer_unattached) { struct msg_command_data *data = cmdq->msgdata; struct client *c = cmdq->client; struct session *s; struct sessionslist ss; struct winlink *wl; struct window_pane *wp; const char *path; int found; if (c != NULL && c->session != NULL) return (c->session); /* * If the name of the calling client's pty is known, build a list of * the sessions that contain it and if any choose either the first or * the newest. */ path = c == NULL ? NULL : c->tty.path; if (path != NULL) { ARRAY_INIT(&ss); RB_FOREACH(s, sessions, &sessions) { found = 0; RB_FOREACH(wl, winlinks, &s->windows) { TAILQ_FOREACH(wp, &wl->window->panes, entry) { if (strcmp(wp->tty, path) == 0) { found = 1; break; } } if (found) break; } if (found) ARRAY_ADD(&ss, s); } s = cmd_choose_session_list(&ss); ARRAY_FREE(&ss); if (s != NULL) return (s); } /* Use the session from the TMUX environment variable. */ if (data != NULL && data->pid == getpid() && data->session_id != -1) { s = session_find_by_id(data->session_id); if (s != NULL) return (s); } return (cmd_choose_session(prefer_unattached)); } /* Is this session better? */ int cmd_session_better(struct session *s, struct session *best, int prefer_unattached) { if (best == NULL) return (1); if (prefer_unattached) { if (!(best->flags & SESSION_UNATTACHED) && (s->flags & SESSION_UNATTACHED)) return (1); else if ((best->flags & SESSION_UNATTACHED) && !(s->flags & SESSION_UNATTACHED)) return (0); } return (timercmp(&s->activity_time, &best->activity_time, >)); } /* * Find the most recently used session, preferring unattached if the flag is * set. */ struct session * cmd_choose_session(int prefer_unattached) { struct session *s, *best; best = NULL; RB_FOREACH(s, sessions, &sessions) { if (cmd_session_better(s, best, prefer_unattached)) best = s; } return (best); } /* Find the most recently used session from a list. */ struct session * cmd_choose_session_list(struct sessionslist *ss) { struct session *s, *sbest; struct timeval *tv = NULL; u_int i; sbest = NULL; for (i = 0; i < ARRAY_LENGTH(ss); i++) { if ((s = ARRAY_ITEM(ss, i)) == NULL) continue; if (tv == NULL || timercmp(&s->activity_time, tv, >)) { sbest = s; tv = &s->activity_time; } } return (sbest); } /* * Find the current client. First try the current client if set, then pick the * most recently used of the clients attached to the current session if any, * then of all clients. */ struct client * cmd_current_client(struct cmd_q *cmdq) { struct session *s; struct client *c; struct clients cc; u_int i; if (cmdq->client != NULL && cmdq->client->session != NULL) return (cmdq->client); /* * No current client set. Find the current session and return the * newest of its clients. */ s = cmd_current_session(cmdq, 0); if (s != NULL && !(s->flags & SESSION_UNATTACHED)) { ARRAY_INIT(&cc); for (i = 0; i < ARRAY_LENGTH(&clients); i++) { if ((c = ARRAY_ITEM(&clients, i)) == NULL) continue; if (s == c->session) ARRAY_ADD(&cc, c); } c = cmd_choose_client(&cc); ARRAY_FREE(&cc); if (c != NULL) return (c); } return (cmd_choose_client(&clients)); } /* Choose the most recently used client from a list. */ struct client * cmd_choose_client(struct clients *cc) { struct client *c, *cbest; struct timeval *tv = NULL; u_int i; cbest = NULL; for (i = 0; i < ARRAY_LENGTH(cc); i++) { if ((c = ARRAY_ITEM(cc, i)) == NULL) continue; if (c->session == NULL) continue; if (tv == NULL || timercmp(&c->activity_time, tv, >)) { cbest = c; tv = &c->activity_time; } } return (cbest); } /* Find the target client or report an error and return NULL. */ struct client * cmd_find_client(struct cmd_q *cmdq, const char *arg, int quiet) { struct client *c; char *tmparg; size_t arglen; /* A NULL argument means the current client. */ if (arg == NULL) { c = cmd_current_client(cmdq); if (c == NULL && !quiet) cmdq_error(cmdq, "no clients"); return (c); } tmparg = xstrdup(arg); /* Trim a single trailing colon if any. */ arglen = strlen(tmparg); if (arglen != 0 && tmparg[arglen - 1] == ':') tmparg[arglen - 1] = '\0'; /* Find the client, if any. */ c = cmd_lookup_client(tmparg); /* If no client found, report an error. */ if (c == NULL && !quiet) cmdq_error(cmdq, "client not found: %s", tmparg); free(tmparg); return (c); } /* * Lookup a client by device path. Either of a full match and a match without a * leading _PATH_DEV ("/dev/") is accepted. */ struct client * cmd_lookup_client(const char *name) { struct client *c; const char *path; u_int i; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c == NULL || c->session == NULL || c->tty.path == NULL) continue; path = c->tty.path; /* Check for exact matches. */ if (strcmp(name, path) == 0) return (c); /* Check without leading /dev if present. */ if (strncmp(path, _PATH_DEV, (sizeof _PATH_DEV) - 1) != 0) continue; if (strcmp(name, path + (sizeof _PATH_DEV) - 1) == 0) return (c); } return (NULL); } /* Find the target session or report an error and return NULL. */ struct session * cmd_lookup_session_id(const char *arg) { char *endptr; long id; if (arg[0] != '$') return (NULL); id = strtol(arg + 1, &endptr, 10); if (arg[1] != '\0' && *endptr == '\0') return (session_find_by_id(id)); return (NULL); } /* Lookup a session by name. If no session is found, NULL is returned. */ struct session * cmd_lookup_session(const char *name, int *ambiguous) { struct session *s, *sfound; *ambiguous = 0; /* Look for $id first. */ if ((s = cmd_lookup_session_id(name)) != NULL) return (s); /* * Look for matches. First look for exact matches - session names must * be unique so an exact match can't be ambigious and can just be * returned. */ if ((s = session_find(name)) != NULL) return (s); /* * Otherwise look for partial matches, returning early if it is found to * be ambiguous. */ sfound = NULL; RB_FOREACH(s, sessions, &sessions) { if (strncmp(name, s->name, strlen(name)) == 0 || fnmatch(name, s->name, 0) == 0) { if (sfound != NULL) { *ambiguous = 1; return (NULL); } sfound = s; } } return (sfound); } /* * Lookup a window or return -1 if not found or ambigious. First try as an * index and if invalid, use fnmatch or leading prefix. Return NULL but fill in * idx if the window index is a valid number but there is no window with that * index. */ struct winlink * cmd_lookup_window(struct session *s, const char *name, int *ambiguous) { struct winlink *wl, *wlfound; const char *errstr; u_int idx; *ambiguous = 0; /* Try as a window id. */ if ((wl = cmd_lookup_winlink_windowid(s, name)) != NULL) return (wl); /* First see if this is a valid window index in this session. */ idx = strtonum(name, 0, INT_MAX, &errstr); if (errstr == NULL) { if ((wl = winlink_find_by_index(&s->windows, idx)) != NULL) return (wl); } /* Look for exact matches, error if more than one. */ wlfound = NULL; RB_FOREACH(wl, winlinks, &s->windows) { if (strcmp(name, wl->window->name) == 0) { if (wlfound != NULL) { *ambiguous = 1; return (NULL); } wlfound = wl; } } if (wlfound != NULL) return (wlfound); /* Now look for pattern matches, again error if multiple. */ wlfound = NULL; RB_FOREACH(wl, winlinks, &s->windows) { if (strncmp(name, wl->window->name, strlen(name)) == 0 || fnmatch(name, wl->window->name, 0) == 0) { if (wlfound != NULL) { *ambiguous = 1; return (NULL); } wlfound = wl; } } if (wlfound != NULL) return (wlfound); return (NULL); } /* * Find a window index - if the window doesn't exist, check if it is a * potential index and return it anyway. */ int cmd_lookup_index(struct session *s, const char *name, int *ambiguous) { struct winlink *wl; const char *errstr; u_int idx; if ((wl = cmd_lookup_window(s, name, ambiguous)) != NULL) return (wl->idx); if (*ambiguous) return (-1); idx = strtonum(name, 0, INT_MAX, &errstr); if (errstr == NULL) return (idx); return (-1); } /* Lookup pane id. An initial % means a pane id. */ struct window_pane * cmd_lookup_paneid(const char *arg) { const char *errstr; u_int paneid; if (*arg != '%') return (NULL); paneid = strtonum(arg + 1, 0, UINT_MAX, &errstr); if (errstr != NULL) return (NULL); return (window_pane_find_by_id(paneid)); } /* Lookup window id in a session. An initial @ means a window id. */ struct winlink * cmd_lookup_winlink_windowid(struct session *s, const char *arg) { const char *errstr; u_int windowid; if (*arg != '@') return (NULL); windowid = strtonum(arg + 1, 0, UINT_MAX, &errstr); if (errstr != NULL) return (NULL); return (winlink_find_by_window_id(&s->windows, windowid)); } /* Lookup window id. An initial @ means a window id. */ struct window * cmd_lookup_windowid(const char *arg) { const char *errstr; u_int windowid; if (*arg != '@') return (NULL); windowid = strtonum(arg + 1, 0, UINT_MAX, &errstr); if (errstr != NULL) return (NULL); return (window_find_by_id(windowid)); } /* Find session and winlink for window. */ struct session * cmd_window_session(struct cmd_q *cmdq, struct window *w, struct winlink **wlp) { struct session *s; struct sessionslist ss; struct winlink *wl; /* If this window is in the current session, return that winlink. */ s = cmd_current_session(cmdq, 0); if (s != NULL) { wl = winlink_find_by_window(&s->windows, w); if (wl != NULL) { if (wlp != NULL) *wlp = wl; return (s); } } /* Otherwise choose from all sessions with this window. */ ARRAY_INIT(&ss); RB_FOREACH(s, sessions, &sessions) { if (winlink_find_by_window(&s->windows, w) != NULL) ARRAY_ADD(&ss, s); } s = cmd_choose_session_list(&ss); ARRAY_FREE(&ss); if (wlp != NULL) *wlp = winlink_find_by_window(&s->windows, w); return (s); } /* Find the target session or report an error and return NULL. */ struct session * cmd_find_session(struct cmd_q *cmdq, const char *arg, int prefer_unattached) { struct session *s; struct window_pane *wp; struct window *w; struct client *c; char *tmparg; size_t arglen; int ambiguous; /* A NULL argument means the current session. */ if (arg == NULL) return (cmd_current_session(cmdq, prefer_unattached)); /* Lookup as pane id or window id. */ if ((wp = cmd_lookup_paneid(arg)) != NULL) return (cmd_window_session(cmdq, wp->window, NULL)); if ((w = cmd_lookup_windowid(arg)) != NULL) return (cmd_window_session(cmdq, w, NULL)); /* Trim a single trailing colon if any. */ tmparg = xstrdup(arg); arglen = strlen(tmparg); if (arglen != 0 && tmparg[arglen - 1] == ':') tmparg[arglen - 1] = '\0'; /* An empty session name is the current session. */ if (*tmparg == '\0') { free(tmparg); return (cmd_current_session(cmdq, prefer_unattached)); } /* Find the session, if any. */ s = cmd_lookup_session(tmparg, &ambiguous); /* If it doesn't, try to match it as a client. */ if (s == NULL && (c = cmd_lookup_client(tmparg)) != NULL) s = c->session; /* If no session found, report an error. */ if (s == NULL) { if (ambiguous) cmdq_error(cmdq, "more than one session: %s", tmparg); else cmdq_error(cmdq, "session not found: %s", tmparg); } free(tmparg); return (s); } /* Find the target session and window or report an error and return NULL. */ struct winlink * cmd_find_window(struct cmd_q *cmdq, const char *arg, struct session **sp) { struct session *s; struct winlink *wl; struct window_pane *wp; const char *winptr; char *sessptr = NULL; int ambiguous = 0; /* * Find the current session. There must always be a current session, if * it can't be found, report an error. */ if ((s = cmd_current_session(cmdq, 0)) == NULL) { cmdq_error(cmdq, "can't establish current session"); return (NULL); } /* A NULL argument means the current session and window. */ if (arg == NULL) { if (sp != NULL) *sp = s; return (s->curw); } /* Lookup as pane id. */ if ((wp = cmd_lookup_paneid(arg)) != NULL) { s = cmd_window_session(cmdq, wp->window, &wl); if (sp != NULL) *sp = s; return (wl); } /* Time to look at the argument. If it is empty, that is an error. */ if (*arg == '\0') goto not_found; /* Find the separating colon and split into window and session. */ winptr = strchr(arg, ':'); if (winptr == NULL) goto no_colon; winptr++; /* skip : */ sessptr = xstrdup(arg); *strchr(sessptr, ':') = '\0'; /* Try to lookup the session if present. */ if (*sessptr != '\0') { if ((s = cmd_lookup_session(sessptr, &ambiguous)) == NULL) goto no_session; } if (sp != NULL) *sp = s; /* * Then work out the window. An empty string is the current window, * otherwise try special cases then to look it up in the session. */ if (*winptr == '\0') wl = s->curw; else if (winptr[0] == '!' && winptr[1] == '\0') wl = TAILQ_FIRST(&s->lastw); else if (winptr[0] == '^' && winptr[1] == '\0') wl = RB_MIN(winlinks, &s->windows); else if (winptr[0] == '$' && winptr[1] == '\0') wl = RB_MAX(winlinks, &s->windows); else if (winptr[0] == '+' || winptr[0] == '-') wl = cmd_find_window_offset(winptr, s, &ambiguous); else wl = cmd_lookup_window(s, winptr, &ambiguous); if (wl == NULL) goto not_found; if (sessptr != NULL) free(sessptr); return (wl); no_colon: /* * No colon in the string, first try special cases, then as a window * and lastly as a session. */ if (arg[0] == '!' && arg[1] == '\0') { if ((wl = TAILQ_FIRST(&s->lastw)) == NULL) goto not_found; } else if (arg[0] == '+' || arg[0] == '-') { if ((wl = cmd_find_window_offset(arg, s, &ambiguous)) == NULL) goto lookup_session; } else if ((wl = cmd_lookup_window(s, arg, &ambiguous)) == NULL) goto lookup_session; if (sp != NULL) *sp = s; return (wl); lookup_session: if (ambiguous) goto not_found; if (*arg != '\0' && (s = cmd_lookup_session(arg, &ambiguous)) == NULL) goto no_session; if (sp != NULL) *sp = s; return (s->curw); no_session: if (ambiguous) cmdq_error(cmdq, "multiple sessions: %s", arg); else cmdq_error(cmdq, "session not found: %s", arg); free(sessptr); return (NULL); not_found: if (ambiguous) cmdq_error(cmdq, "multiple windows: %s", arg); else cmdq_error(cmdq, "window not found: %s", arg); free(sessptr); return (NULL); } struct winlink * cmd_find_window_offset(const char *winptr, struct session *s, int *ambiguous) { struct winlink *wl; int offset = 1; if (winptr[1] != '\0') offset = strtonum(winptr + 1, 1, INT_MAX, NULL); if (offset == 0) wl = cmd_lookup_window(s, winptr, ambiguous); else { if (winptr[0] == '+') wl = winlink_next_by_number(s->curw, s, offset); else wl = winlink_previous_by_number(s->curw, s, offset); } return (wl); } /* * Find the target session and window index, whether or not it exists in the * session. Return -2 on error or -1 if no window index is specified. This is * used when parsing an argument for a window target that may not exist (for * example if it is going to be created). */ int cmd_find_index(struct cmd_q *cmdq, const char *arg, struct session **sp) { struct session *s; struct winlink *wl; const char *winptr; char *sessptr = NULL; int idx, ambiguous = 0; /* * Find the current session. There must always be a current session, if * it can't be found, report an error. */ if ((s = cmd_current_session(cmdq, 0)) == NULL) { cmdq_error(cmdq, "can't establish current session"); return (-2); } /* A NULL argument means the current session and "no window" (-1). */ if (arg == NULL) { if (sp != NULL) *sp = s; return (-1); } /* Time to look at the argument. If it is empty, that is an error. */ if (*arg == '\0') goto not_found; /* Find the separating colon. If none, assume the current session. */ winptr = strchr(arg, ':'); if (winptr == NULL) goto no_colon; winptr++; /* skip : */ sessptr = xstrdup(arg); *strchr(sessptr, ':') = '\0'; /* Try to lookup the session if present. */ if (sessptr != NULL && *sessptr != '\0') { if ((s = cmd_lookup_session(sessptr, &ambiguous)) == NULL) goto no_session; } if (sp != NULL) *sp = s; /* * Then work out the window. An empty string is a new window otherwise * try to look it up in the session. */ if (*winptr == '\0') idx = -1; else if (winptr[0] == '!' && winptr[1] == '\0') { if ((wl = TAILQ_FIRST(&s->lastw)) == NULL) goto not_found; idx = wl->idx; } else if (winptr[0] == '+' || winptr[0] == '-') { if ((idx = cmd_find_index_offset(winptr, s, &ambiguous)) < 0) goto invalid_index; } else if ((idx = cmd_lookup_index(s, winptr, &ambiguous)) == -1) goto invalid_index; free(sessptr); return (idx); no_colon: /* * No colon in the string, first try special cases, then as a window * and lastly as a session. */ if (arg[0] == '!' && arg[1] == '\0') { if ((wl = TAILQ_FIRST(&s->lastw)) == NULL) goto not_found; idx = wl->idx; } else if (arg[0] == '+' || arg[0] == '-') { if ((idx = cmd_find_index_offset(arg, s, &ambiguous)) < 0) goto lookup_session; } else if ((idx = cmd_lookup_index(s, arg, &ambiguous)) == -1) goto lookup_session; if (sp != NULL) *sp = s; return (idx); lookup_session: if (ambiguous) goto not_found; if (*arg != '\0' && (s = cmd_lookup_session(arg, &ambiguous)) == NULL) goto no_session; if (sp != NULL) *sp = s; return (-1); no_session: if (ambiguous) cmdq_error(cmdq, "multiple sessions: %s", arg); else cmdq_error(cmdq, "session not found: %s", arg); free(sessptr); return (-2); invalid_index: if (ambiguous) goto not_found; cmdq_error(cmdq, "invalid index: %s", arg); free(sessptr); return (-2); not_found: if (ambiguous) cmdq_error(cmdq, "multiple windows: %s", arg); else cmdq_error(cmdq, "window not found: %s", arg); free(sessptr); return (-2); } int cmd_find_index_offset(const char *winptr, struct session *s, int *ambiguous) { int idx, offset = 1; if (winptr[1] != '\0') offset = strtonum(winptr + 1, 1, INT_MAX, NULL); if (offset == 0) idx = cmd_lookup_index(s, winptr, ambiguous); else { if (winptr[0] == '+') { if (s->curw->idx == INT_MAX) idx = cmd_lookup_index(s, winptr, ambiguous); else idx = s->curw->idx + offset; } else { if (s->curw->idx == 0) idx = cmd_lookup_index(s, winptr, ambiguous); else idx = s->curw->idx - offset; } } return (idx); } /* * Find the target session, window and pane number or report an error and * return NULL. The pane number is separated from the session:window by a ., * such as mysession:mywindow.0. */ struct winlink * cmd_find_pane(struct cmd_q *cmdq, const char *arg, struct session **sp, struct window_pane **wpp) { struct session *s; struct winlink *wl; const char *period, *errstr; char *winptr, *paneptr; u_int idx; /* Get the current session. */ if ((s = cmd_current_session(cmdq, 0)) == NULL) { cmdq_error(cmdq, "can't establish current session"); return (NULL); } if (sp != NULL) *sp = s; /* A NULL argument means the current session, window and pane. */ if (arg == NULL) { *wpp = s->curw->window->active; return (s->curw); } /* Lookup as pane id. */ if ((*wpp = cmd_lookup_paneid(arg)) != NULL) { s = cmd_window_session(cmdq, (*wpp)->window, &wl); if (sp != NULL) *sp = s; return (wl); } /* Look for a separating period. */ if ((period = strrchr(arg, '.')) == NULL) goto no_period; /* Pull out the window part and parse it. */ winptr = xstrdup(arg); winptr[period - arg] = '\0'; if (*winptr == '\0') wl = s->curw; else if ((wl = cmd_find_window(cmdq, winptr, sp)) == NULL) goto error; /* Find the pane section and look it up. */ paneptr = winptr + (period - arg) + 1; if (*paneptr == '\0') *wpp = wl->window->active; else if (paneptr[0] == '+' || paneptr[0] == '-') *wpp = cmd_find_pane_offset(paneptr, wl); else { idx = strtonum(paneptr, 0, INT_MAX, &errstr); if (errstr != NULL) goto lookup_string; *wpp = window_pane_at_index(wl->window, idx); if (*wpp == NULL) goto lookup_string; } free(winptr); return (wl); lookup_string: /* Try pane string description. */ if ((*wpp = window_find_string(wl->window, paneptr)) == NULL) { cmdq_error(cmdq, "can't find pane: %s", paneptr); goto error; } free(winptr); return (wl); no_period: /* Try as a pane number alone. */ idx = strtonum(arg, 0, INT_MAX, &errstr); if (errstr != NULL) goto lookup_window; /* Try index in the current session and window. */ if ((*wpp = window_pane_at_index(s->curw->window, idx)) == NULL) goto lookup_window; return (s->curw); lookup_window: /* Try pane string description. */ if ((*wpp = window_find_string(s->curw->window, arg)) != NULL) return (s->curw); /* Try as a window and use the active pane. */ if ((wl = cmd_find_window(cmdq, arg, sp)) != NULL) *wpp = wl->window->active; return (wl); error: free(winptr); return (NULL); } struct window_pane * cmd_find_pane_offset(const char *paneptr, struct winlink *wl) { struct window *w = wl->window; struct window_pane *wp = w->active; u_int offset = 1; if (paneptr[1] != '\0') offset = strtonum(paneptr + 1, 1, INT_MAX, NULL); if (offset > 0) { if (paneptr[0] == '+') wp = window_pane_next_by_number(w, wp, offset); else wp = window_pane_previous_by_number(w, wp, offset); } return (wp); } /* Replace the first %% or %idx in template by s. */ char * cmd_template_replace(const char *template, const char *s, int idx) { char ch, *buf; const char *ptr; int replaced; 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++; len += strlen(s); buf = xrealloc(buf, 1, len + 1); strlcat(buf, s, len + 1); continue; } buf = xrealloc(buf, 1, len + 2); buf[len++] = ch; buf[len] = '\0'; } return (buf); } /* * Return the default path for a new pane, using the given path or the * default-path option if it is NULL. Several special values are accepted: the * empty string or relative path for the current pane's working directory, ~ * for the user's home, - for the session working directory, . for the tmux * server's working directory. The default on failure is the session's working * directory. */ const char * cmd_get_default_path(struct cmd_q *cmdq, const char *cwd) { struct client *c = cmdq->client; struct session *s; struct environ_entry *envent; const char *root; char tmp[MAXPATHLEN]; struct passwd *pw; int n; size_t skip; static char path[MAXPATHLEN]; if ((s = cmd_current_session(cmdq, 0)) == NULL) return (NULL); if (cwd == NULL) cwd = options_get_string(&s->options, "default-path"); skip = 1; if (strcmp(cwd, "$HOME") == 0 || strncmp(cwd, "$HOME/", 6) == 0) { /* User's home directory - $HOME. */ skip = 5; goto find_home; } else if (cwd[0] == '~' && (cwd[1] == '\0' || cwd[1] == '/')) { /* User's home directory - ~. */ goto find_home; } else if (cwd[0] == '-' && (cwd[1] == '\0' || cwd[1] == '/')) { /* Session working directory. */ root = s->cwd; goto complete_path; } else if (cwd[0] == '.' && (cwd[1] == '\0' || cwd[1] == '/')) { /* Server working directory. */ if (getcwd(tmp, sizeof tmp) != NULL) { root = tmp; goto complete_path; } return (s->cwd); } else if (*cwd == '/') { /* Absolute path. */ return (cwd); } else { /* Empty or relative path. */ if (c != NULL && c->session == NULL && c->cwd != NULL) root = c->cwd; else if (s->curw != NULL) root = osdep_get_cwd(s->curw->window->active->fd); else return (s->cwd); skip = 0; if (root != NULL) goto complete_path; } return (s->cwd); find_home: envent = environ_find(&global_environ, "HOME"); if (envent != NULL && *envent->value != '\0') root = envent->value; else if ((pw = getpwuid(getuid())) != NULL) root = pw->pw_dir; else return (s->cwd); complete_path: if (root[skip] == '\0') { strlcpy(path, root, sizeof path); return (path); } n = snprintf(path, sizeof path, "%s/%s", root, cwd + skip); if (n > 0 && (size_t)n < sizeof path) return (path); return (s->cwd); } tmate-1.8.10/colour.c000066400000000000000000000204361242461015400143660ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" /* * Colour to string conversion functions. Bit 8 of the colour means it is one * of the 256 colour palette. */ /* An RGB colour. */ struct colour_rgb { u_char r; u_char g; u_char b; }; /* 256 colour RGB table, generated on first use. */ struct colour_rgb *colour_rgb_256; void colour_rgb_generate256(void); u_int colour_rgb_distance(struct colour_rgb *, struct colour_rgb *); int colour_rgb_find(struct colour_rgb *); /* Generate 256 colour RGB table. */ void colour_rgb_generate256(void) { struct colour_rgb *rgb; u_int i, r, g, b; /* * Allocate the table. The first 16 colours are often changed by users * and terminals so don't include them. */ colour_rgb_256 = xcalloc(240, sizeof *colour_rgb_256); /* Add the colours first. */ r = g = b = 0; for (i = 240; i > 24; i--) { rgb = &colour_rgb_256[240 - i]; if (r != 0) rgb->r = (r * 40) + 55; if (g != 0) rgb->g = (g * 40) + 55; if (b != 0) rgb->b = (b * 40) + 55; b++; if (b > 5) { b = 0; g++; } if (g > 5) { g = 0; r++; } } /* Then add the greys. */ for (i = 24; i > 0; i--) { rgb = &colour_rgb_256[240 - i]; rgb->r = 8 + (24 - i) * 10; rgb->g = 8 + (24 - i) * 10; rgb->b = 8 + (24 - i) * 10; } } /* Get colour RGB distance. */ u_int colour_rgb_distance(struct colour_rgb *rgb1, struct colour_rgb *rgb2) { int r, g, b; r = rgb1->r - rgb2->r; g = rgb1->g - rgb2->g; b = rgb1->b - rgb2->b; return (r * r + g * g + b * b); } /* Work out the nearest colour from the 256 colour set. */ int colour_rgb_find(struct colour_rgb *rgb) { u_int distance, lowest, colour, i; if (colour_rgb_256 == NULL) colour_rgb_generate256(); colour = 16; lowest = UINT_MAX; for (i = 0; i < 240; i++) { distance = colour_rgb_distance(&colour_rgb_256[i], rgb); if (distance < lowest) { lowest = distance; colour = 16 + i; } } return (colour); } /* Set grid cell foreground colour. */ void colour_set_fg(struct grid_cell *gc, int c) { if (c & 0x100) gc->flags |= GRID_FLAG_FG256; gc->fg = c; } /* Set grid cell background colour. */ void colour_set_bg(struct grid_cell *gc, int c) { if (c & 0x100) gc->flags |= GRID_FLAG_BG256; gc->bg = c; } /* Convert colour to a string. */ const char * colour_tostring(int c) { static char s[32]; if (c & 0x100) { xsnprintf(s, sizeof s, "colour%u", c & ~0x100); return (s); } switch (c) { case 0: return ("black"); case 1: return ("red"); case 2: return ("green"); case 3: return ("yellow"); case 4: return ("blue"); case 5: return ("magenta"); case 6: return ("cyan"); case 7: return ("white"); case 8: return ("default"); 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 (NULL); } /* Convert colour from string. */ int colour_fromstring(const char *s) { const char *errstr; const char *cp; struct colour_rgb rgb; int n; if (*s == '#' && strlen(s) == 7) { for (cp = s + 1; isxdigit((u_char) *cp); cp++) ; if (*cp != '\0') return (-1); n = sscanf(s + 1, "%2hhx%2hhx%2hhx", &rgb.r, &rgb.g, &rgb.b); if (n != 3) return (-1); return (colour_rgb_find(&rgb) | 0x100); } if (strncasecmp(s, "colour", (sizeof "colour") - 1) == 0) { n = strtonum(s + (sizeof "colour") - 1, 0, 255, &errstr); if (errstr != NULL) return (-1); return (n | 0x100); } if (strcasecmp(s, "black") == 0 || (s[0] == '0' && s[1] == '\0')) return (0); if (strcasecmp(s, "red") == 0 || (s[0] == '1' && s[1] == '\0')) return (1); if (strcasecmp(s, "green") == 0 || (s[0] == '2' && s[1] == '\0')) return (2); if (strcasecmp(s, "yellow") == 0 || (s[0] == '3' && s[1] == '\0')) return (3); if (strcasecmp(s, "blue") == 0 || (s[0] == '4' && s[1] == '\0')) return (4); if (strcasecmp(s, "magenta") == 0 || (s[0] == '5' && s[1] == '\0')) return (5); if (strcasecmp(s, "cyan") == 0 || (s[0] == '6' && s[1] == '\0')) return (6); if (strcasecmp(s, "white") == 0 || (s[0] == '7' && s[1] == '\0')) return (7); if (strcasecmp(s, "default") == 0 || (s[0] == '8' && s[1] == '\0')) return (8); if (strcasecmp(s, "brightblack") == 0 || (s[0] == '9' && s[1] == '0' && s[1] == '\0')) return (90); if (strcasecmp(s, "brightred") == 0 || (s[0] == '9' && s[1] == '1' && s[1] == '\0')) return (91); if (strcasecmp(s, "brightgreen") == 0 || (s[0] == '9' && s[1] == '2' && s[1] == '\0')) return (92); if (strcasecmp(s, "brightyellow") == 0 || (s[0] == '9' && s[1] == '3' && s[1] == '\0')) return (93); if (strcasecmp(s, "brightblue") == 0 || (s[0] == '9' && s[1] == '4' && s[1] == '\0')) return (94); if (strcasecmp(s, "brightmagenta") == 0 || (s[0] == '9' && s[1] == '5' && s[1] == '\0')) return (95); if (strcasecmp(s, "brightcyan") == 0 || (s[0] == '9' && s[1] == '6' && s[1] == '\0')) return (96); if (strcasecmp(s, "brightwhite") == 0 || (s[0] == '9' && s[1] == '7' && s[1] == '\0')) return (97); return (-1); } /* Convert 256 colour palette to 16. */ u_char colour_256to16(u_char c) { static const u_char table[256] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 4, 4, 4, 12, 12, 2, 6, 4, 4, 12, 12, 2, 2, 6, 4, 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14, 1, 5, 4, 4, 12, 12, 3, 8, 4, 4, 12, 12, 2, 2, 6, 4, 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 5, 4, 12, 12, 1, 1, 5, 4, 12, 12, 3, 3, 8, 4, 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 1, 5, 12, 12, 1, 1, 1, 5, 12, 12, 1, 1, 1, 5, 12, 12, 3, 3, 3, 7, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14, 9, 9, 9, 9, 13, 12, 9, 9, 9, 9, 13, 12, 9, 9, 9, 9, 13, 12, 9, 9, 9, 9, 13, 12, 11, 11, 11, 11, 7, 12, 10, 10, 10, 10, 10, 14, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13, 11, 11, 11, 11, 11, 15, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15 }; return (table[c]); } /* Convert 256 colour palette to 88. */ u_char colour_256to88(u_char c) { static const u_char table[256] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 17, 18, 18, 19, 20, 21, 21, 22, 22, 23, 20, 21, 21, 22, 22, 23, 24, 25, 25, 26, 26, 27, 24, 25, 25, 26, 26, 27, 28, 29, 29, 30, 30, 31, 32, 33, 33, 34, 34, 35, 36, 37, 37, 38, 38, 39, 36, 37, 37, 38, 38, 39, 40, 41, 41, 42, 42, 43, 40, 41, 41, 42, 42, 43, 44, 45, 45, 46, 46, 47, 32, 33, 33, 34, 34, 35, 36, 37, 37, 38, 38, 39, 36, 37, 37, 38, 38, 39, 40, 41, 41, 42, 42, 43, 40, 41, 41, 42, 42, 43, 44, 45, 45, 46, 46, 47, 48, 49, 49, 50, 50, 51, 52, 53, 53, 54, 54, 55, 52, 53, 53, 54, 54, 55, 56, 57, 57, 58, 58, 59, 56, 57, 57, 58, 58, 59, 60, 61, 61, 62, 62, 63, 48, 49, 49, 50, 50, 51, 52, 53, 53, 54, 54, 55, 52, 53, 53, 54, 54, 55, 56, 57, 57, 58, 58, 59, 56, 57, 57, 58, 58, 59, 60, 61, 61, 62, 62, 63, 64, 65, 65, 66, 66, 67, 68, 69, 69, 70, 70, 71, 68, 69, 69, 70, 70, 71, 72, 73, 73, 74, 74, 75, 72, 73, 73, 74, 74, 75, 76, 77, 77, 78, 78, 79, 0, 0, 80, 80, 80, 81, 81, 81, 82, 82, 82, 83, 83, 83, 84, 84, 84, 85, 85, 85, 86, 86, 86, 87 }; return (table[c]); } tmate-1.8.10/compat.h000066400000000000000000000120111242461015400143410ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef COMPAT_H #define COMPAT_H #ifndef __GNUC__ #define __attribute__(a) #endif #ifndef __dead #define __dead __attribute__ ((__noreturn__)) #endif #ifndef __packed #define __packed __attribute__ ((__packed__)) #endif #ifndef HAVE_BSD_TYPES typedef uint8_t u_int8_t; typedef uint16_t u_int16_t; typedef uint32_t u_int32_t; typedef uint64_t u_int64_t; #endif #ifndef HAVE_PATHS_H #define _PATH_BSHELL "/bin/sh" #define _PATH_TMP "/tmp/" #define _PATH_DEVNULL "/dev/null" #define _PATH_TTY "/dev/tty" #define _PATH_DEV "/dev/" #endif #ifdef HAVE_QUEUE_H #include #else #include "compat/queue.h" #endif #ifdef HAVE_TREE_H #include #else #include "compat/tree.h" #endif #ifdef HAVE_BITSTRING_H #include #else #include "compat/bitstring.h" #endif #ifdef HAVE_PATHS_H #include #endif #ifdef HAVE_FORKPTY #ifdef HAVE_LIBUTIL_H #include #endif #ifdef HAVE_PTY_H #include #endif #ifdef HAVE_UTIL_H #include #endif #endif #ifdef HAVE_VIS #include #else #include "compat/vis.h" #endif #ifdef HAVE_IMSG #include #else #include "compat/imsg.h" #endif #ifdef HAVE_STDINT_H #include #else #include #endif #ifdef BROKEN_CMSG_FIRSTHDR #undef CMSG_FIRSTHDR #define CMSG_FIRSTHDR(mhdr) \ ((mhdr)->msg_controllen >= sizeof(struct cmsghdr) ? \ (struct cmsghdr *)(mhdr)->msg_control : \ (struct cmsghdr *)NULL) #endif #ifndef CMSG_ALIGN #ifdef _CMSG_DATA_ALIGN #define CMSG_ALIGN _CMSG_DATA_ALIGN #else #define CMSG_ALIGN(len) (((len) + sizeof(long) - 1) & ~(sizeof(long) - 1)) #endif #endif #ifndef CMSG_SPACE #define CMSG_SPACE(len) (CMSG_ALIGN(sizeof(struct cmsghdr)) + CMSG_ALIGN(len)) #endif #ifndef CMSG_LEN #define CMSG_LEN(len) (CMSG_ALIGN(sizeof(struct cmsghdr)) + (len)) #endif #ifndef INFTIM #define INFTIM -1 #endif #ifndef WAIT_ANY #define WAIT_ANY -1 #endif #ifndef SUN_LEN #define SUN_LEN(sun) (sizeof (sun)->sun_path) #endif #ifndef timercmp #define timercmp(tvp, uvp, cmp) \ (((tvp)->tv_sec == (uvp)->tv_sec) ? \ ((tvp)->tv_usec cmp (uvp)->tv_usec) : \ ((tvp)->tv_sec cmp (uvp)->tv_sec)) #endif #ifndef timeradd #define timeradd(tvp, uvp, vvp) \ do { \ (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \ (vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \ if ((vvp)->tv_usec >= 1000000) { \ (vvp)->tv_sec++; \ (vvp)->tv_usec -= 1000000; \ } \ } while (0) #endif #ifndef TTY_NAME_MAX #define TTY_NAME_MAX 32 #endif #ifndef HAVE_BZERO #undef bzero #define bzero(buf, len) memset(buf, 0, len); #endif #ifndef HAVE_CLOSEFROM /* closefrom.c */ void closefrom(int); #endif #ifndef HAVE_STRCASESTR /* strcasestr.c */ char *strcasestr(const char *, const char *); #endif #ifndef HAVE_STRSEP /* strsep.c */ char *strsep(char **, const char *); #endif #ifndef HAVE_STRTONUM /* strtonum.c */ long long strtonum(const char *, long long, long long, const char **); #endif #ifndef HAVE_STRLCPY /* strlcpy.c */ size_t strlcpy(char *, const char *, size_t); #endif #ifndef HAVE_STRLCAT /* strlcat.c */ size_t strlcat(char *, const char *, size_t); #endif #ifndef HAVE_DAEMON /* daemon.c */ int daemon(int, int); #endif #ifndef HAVE_B64_NTOP /* b64_ntop.c */ int b64_ntop(const char *, size_t, char *, size_t); #endif #ifndef HAVE_FORKPTY /* forkpty.c */ #include pid_t forkpty(int *, char *, struct termios *, struct winsize *); #endif #ifndef HAVE_ASPRINTF /* asprintf.c */ int asprintf(char **, const char *, ...); int vasprintf(char **, const char *, va_list); #endif #ifndef HAVE_FGETLN /* fgetln.c */ char *fgetln(FILE *, size_t *); #endif #ifndef HAVE_SETENV /* setenv.c */ int setenv(const char *, const char *, int); int unsetenv(const char *); #endif #ifdef HAVE_GETOPT #include #else /* getopt.c */ extern int BSDopterr; extern int BSDoptind; extern int BSDoptopt; extern int BSDoptreset; extern char *BSDoptarg; int BSDgetopt(int, char *const *, const char *); #define getopt(ac, av, o) BSDgetopt(ac, av, o) #define opterr BSDopterr #define optind BSDoptind #define optopt BSDoptopt #define optreset BSDoptreset #define optarg BSDoptarg #endif #endif /* COMPAT_H */ tmate-1.8.10/compat/000077500000000000000000000000001242461015400141755ustar00rootroot00000000000000tmate-1.8.10/compat/asprintf.c000066400000000000000000000026511242461015400161730ustar00rootroot00000000000000/* $Id$ */ /* * 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 #ifdef HAVE_STDINT_H #include #else #include #endif #include #include "tmux.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; } return (n); error: *ret = NULL; return (-1); } tmate-1.8.10/compat/b64_ntop.c000066400000000000000000000163721242461015400160050ustar00rootroot00000000000000/* * Copyright (c) 1996, 1998 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 #define Assert(Cond) if (!(Cond)) abort() 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(uint8_t const *src, size_t srclength, char *target, size_t targsize) { size_t datalength = 0; uint8_t input[3]; uint8_t output[4]; size_t 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; Assert(output[0] < 64); Assert(output[1] < 64); Assert(output[2] < 64); Assert(output[3] < 64); 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); Assert(output[0] < 64); Assert(output[1] < 64); Assert(output[2] < 64); 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); } tmate-1.8.10/compat/bitstring.h000066400000000000000000000103511242461015400163530ustar00rootroot00000000000000/* $Id$ */ /* $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_ */ tmate-1.8.10/compat/closefrom.c000066400000000000000000000055421242461015400163400ustar00rootroot00000000000000/* $Id$ */ /* * 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. */ #include "tmux.h" #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 #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 */ tmate-1.8.10/compat/daemon.c000066400000000000000000000041761242461015400156140ustar00rootroot00000000000000/* $Id$ */ /* $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 "tmux.h" 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); } return (0); } tmate-1.8.10/compat/fgetln.c000066400000000000000000000050101242461015400156140ustar00rootroot00000000000000/* $Id$ */ /* $NetBSD: fgetln.c,v 1.3 2007/08/07 02:06:58 lukem Exp $ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Christos Zoulas. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include "tmux.h" char * fgetln(FILE *fp, size_t *len) { static char *buf = NULL; static size_t bufsiz = 0; char *ptr; if (buf == NULL) { bufsiz = BUFSIZ; if ((buf = malloc(bufsiz)) == NULL) return NULL; } if (fgets(buf, bufsiz, fp) == NULL) return NULL; *len = 0; while ((ptr = strchr(&buf[*len], '\n')) == NULL) { size_t nbufsiz = bufsiz + BUFSIZ; char *nbuf = realloc(buf, nbufsiz); if (nbuf == NULL) { int oerrno = errno; free(buf); errno = oerrno; buf = NULL; return NULL; } else buf = nbuf; *len = bufsiz; if (fgets(&buf[bufsiz], BUFSIZ, fp) == NULL) return buf; bufsiz = nbufsiz; } *len = (ptr - buf) + 1; return buf; } tmate-1.8.10/compat/forkpty-aix.c000066400000000000000000000042101242461015400166130ustar00rootroot00000000000000/* $Id$ */ /* * 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" pid_t forkpty(int *master, unused char *name, struct termios *tio, struct winsize *ws) { int slave, fd; char *path; pid_t pid; if ((*master = open("/dev/ptc", O_RDWR|O_NOCTTY)) == -1) return (-1); if ((path = ttyname(*master)) == NULL) goto out; if ((slave = open(path, O_RDWR|O_NOCTTY)) == -1) goto out; switch (pid = fork()) { case -1: goto out; case 0: close(*master); 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); return (pid); out: if (*master != -1) close(*master); if (slave != -1) close(slave); return (-1); } tmate-1.8.10/compat/forkpty-hpux.c000066400000000000000000000041221242461015400170200ustar00rootroot00000000000000/* $Id$ */ /* * 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" pid_t forkpty(int *master, char *name, struct termios *tio, struct winsize *ws) { int slave; 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); } tmate-1.8.10/compat/forkpty-sunos.c000066400000000000000000000041221242461015400172030ustar00rootroot00000000000000/* $Id$ */ /* * 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" pid_t forkpty(int *master, char *name, struct termios *tio, struct winsize *ws) { int slave; 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); } tmate-1.8.10/compat/getopt.c000066400000000000000000000071421242461015400156470ustar00rootroot00000000000000/* $Id$ */ /* * 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 "tmux.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 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", __progname, BSDoptopt); return (BADCH); } if (*++oli != ':') { /* don't need argument */ BSDoptarg = NULL; if (!*place) ++BSDoptind; } else { /* need an argument */ if (*place) /* no white space */ BSDoptarg = 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", __progname, BSDoptopt); return (BADCH); } else /* white space */ BSDoptarg = nargv[BSDoptind]; place = EMSG; ++BSDoptind; } return (BSDoptopt); /* dump back option letter */ } tmate-1.8.10/compat/imsg-buffer.c000066400000000000000000000132151242461015400165510ustar00rootroot00000000000000/* $Id$ */ /* $OpenBSD: imsg-buffer.c,v 1.3 2010/05/26 13:56:07 nicm Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include "tmux.h" int ibuf_realloc(struct ibuf *, size_t); void ibuf_enqueue(struct msgbuf *, struct ibuf *); void ibuf_dequeue(struct msgbuf *, struct ibuf *); struct ibuf * ibuf_open(size_t len) { struct ibuf *buf; if ((buf = calloc(1, sizeof(struct ibuf))) == NULL) return (NULL); if ((buf->buf = malloc(len)) == NULL) { free(buf); return (NULL); } buf->size = buf->max = len; buf->fd = -1; return (buf); } struct ibuf * ibuf_dynamic(size_t len, size_t max) { struct ibuf *buf; if (max < len) return (NULL); if ((buf = ibuf_open(len)) == NULL) return (NULL); if (max > 0) buf->max = max; return (buf); } int ibuf_realloc(struct ibuf *buf, size_t len) { u_char *b; /* on static buffers max is eq size and so the following fails */ if (buf->wpos + len > buf->max) { errno = ENOMEM; return (-1); } b = realloc(buf->buf, buf->wpos + len); if (b == NULL) return (-1); buf->buf = b; buf->size = buf->wpos + len; return (0); } int ibuf_add(struct ibuf *buf, const void *data, size_t len) { if (buf->wpos + len > buf->size) if (ibuf_realloc(buf, len) == -1) return (-1); memcpy(buf->buf + buf->wpos, data, len); buf->wpos += len; return (0); } void * ibuf_reserve(struct ibuf *buf, size_t len) { void *b; if (buf->wpos + len > buf->size) if (ibuf_realloc(buf, len) == -1) return (NULL); b = buf->buf + buf->wpos; buf->wpos += len; return (b); } void * ibuf_seek(struct ibuf *buf, size_t pos, size_t len) { /* only allowed to seek in already written parts */ if (pos + len > buf->wpos) return (NULL); return (buf->buf + pos); } size_t ibuf_size(struct ibuf *buf) { return (buf->wpos); } size_t ibuf_left(struct ibuf *buf) { return (buf->max - buf->wpos); } void ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf) { ibuf_enqueue(msgbuf, buf); } int ibuf_write(struct msgbuf *msgbuf) { struct iovec iov[IOV_MAX]; struct ibuf *buf; unsigned int i = 0; ssize_t n; bzero(&iov, sizeof(iov)); TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { if (i >= IOV_MAX) break; iov[i].iov_base = buf->buf + buf->rpos; iov[i].iov_len = buf->wpos - buf->rpos; i++; } if ((n = writev(msgbuf->fd, iov, i)) == -1) { if (errno == EAGAIN || errno == ENOBUFS || errno == EINTR) /* try later */ return (0); else return (-1); } if (n == 0) { /* connection closed */ errno = 0; return (-2); } msgbuf_drain(msgbuf, n); return (0); } void ibuf_free(struct ibuf *buf) { free(buf->buf); free(buf); } void msgbuf_init(struct msgbuf *msgbuf) { msgbuf->queued = 0; msgbuf->fd = -1; TAILQ_INIT(&msgbuf->bufs); } void msgbuf_drain(struct msgbuf *msgbuf, size_t n) { struct ibuf *buf, *next; for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0; buf = next) { next = TAILQ_NEXT(buf, entry); if (buf->rpos + n >= buf->wpos) { n -= buf->wpos - buf->rpos; ibuf_dequeue(msgbuf, buf); } else { buf->rpos += n; n = 0; } } } void msgbuf_clear(struct msgbuf *msgbuf) { struct ibuf *buf; while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL) ibuf_dequeue(msgbuf, buf); } int msgbuf_write(struct msgbuf *msgbuf) { struct iovec iov[IOV_MAX]; struct ibuf *buf; unsigned int i = 0; ssize_t n; struct msghdr msg; struct cmsghdr *cmsg; union { struct cmsghdr hdr; char buf[CMSG_SPACE(sizeof(int))]; } cmsgbuf; bzero(&iov, sizeof(iov)); bzero(&msg, sizeof(msg)); TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { if (i >= IOV_MAX) break; iov[i].iov_base = buf->buf + buf->rpos; iov[i].iov_len = buf->wpos - buf->rpos; i++; if (buf->fd != -1) break; } msg.msg_iov = iov; msg.msg_iovlen = i; if (buf != NULL && buf->fd != -1) { msg.msg_control = (caddr_t)&cmsgbuf.buf; msg.msg_controllen = 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; } if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) { if (errno == EAGAIN || errno == ENOBUFS || errno == EINTR) /* try later */ return (0); else return (-1); } if (n == 0) { /* connection closed */ errno = 0; return (-2); } /* * assumption: fd got sent if sendmsg sent anything * this works because fds are passed one at a time */ if (buf != NULL && buf->fd != -1) { close(buf->fd); buf->fd = -1; } msgbuf_drain(msgbuf, n); return (0); } void ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf) { TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry); msgbuf->queued++; } void ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf) { TAILQ_REMOVE(&msgbuf->bufs, buf, entry); if (buf->fd != -1) close(buf->fd); msgbuf->queued--; ibuf_free(buf); } tmate-1.8.10/compat/imsg.c000066400000000000000000000127261242461015400153100ustar00rootroot00000000000000/* $Id$ */ /* $OpenBSD: imsg.c,v 1.3 2010/05/26 13:56:07 nicm Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include "tmux.h" int imsg_get_fd(struct imsgbuf *); void imsg_init(struct imsgbuf *ibuf, int fd) { msgbuf_init(&ibuf->w); bzero(&ibuf->r, sizeof(ibuf->r)); ibuf->fd = fd; ibuf->w.fd = fd; ibuf->pid = getpid(); TAILQ_INIT(&ibuf->fds); } ssize_t imsg_read(struct imsgbuf *ibuf) { struct msghdr msg; struct cmsghdr *cmsg; union { struct cmsghdr hdr; char buf[CMSG_SPACE(sizeof(int) * 16)]; } cmsgbuf; struct iovec iov; ssize_t n; int fd; struct imsg_fd *ifd; bzero(&msg, sizeof(msg)); iov.iov_base = ibuf->r.buf + ibuf->r.wpos; iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = &cmsgbuf.buf; msg.msg_controllen = sizeof(cmsgbuf.buf); if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) { if (errno != EINTR && errno != EAGAIN) { return (-1); } return (-2); } ibuf->r.wpos += n; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { fd = (*(int *)CMSG_DATA(cmsg)); if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL) { close(fd); return (-1); } ifd->fd = fd; TAILQ_INSERT_TAIL(&ibuf->fds, ifd, entry); } /* we do not handle other ctl data level */ } return (n); } ssize_t imsg_get(struct imsgbuf *ibuf, struct imsg *imsg) { size_t av, left, datalen; av = ibuf->r.wpos; if (IMSG_HEADER_SIZE > av) return (0); memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr)); if (imsg->hdr.len < IMSG_HEADER_SIZE || imsg->hdr.len > MAX_IMSGSIZE) { errno = ERANGE; return (-1); } if (imsg->hdr.len > av) return (0); datalen = imsg->hdr.len - IMSG_HEADER_SIZE; ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE; if ((imsg->data = malloc(datalen)) == NULL && datalen != 0) return (-1); if (imsg->hdr.flags & IMSGF_HASFD) imsg->fd = imsg_get_fd(ibuf); else imsg->fd = -1; memcpy(imsg->data, ibuf->r.rptr, datalen); if (imsg->hdr.len < av) { left = av - imsg->hdr.len; memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left); ibuf->r.wpos = left; } else ibuf->r.wpos = 0; return (datalen + IMSG_HEADER_SIZE); } int imsg_compose(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid, pid_t pid, int fd, void *data, u_int16_t datalen) { struct ibuf *wbuf; if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL) return (-1); if (imsg_add(wbuf, data, datalen) == -1) return (-1); wbuf->fd = fd; imsg_close(ibuf, wbuf); return (1); } int imsg_composev(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid, pid_t pid, int fd, const struct iovec *iov, int iovcnt) { struct ibuf *wbuf; int i, datalen = 0; for (i = 0; i < iovcnt; i++) datalen += iov[i].iov_len; if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL) return (-1); for (i = 0; i < iovcnt; i++) if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1) return (-1); wbuf->fd = fd; imsg_close(ibuf, wbuf); return (1); } /* ARGSUSED */ struct ibuf * imsg_create(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid, pid_t pid, u_int16_t datalen) { struct ibuf *wbuf; struct imsg_hdr hdr; datalen += IMSG_HEADER_SIZE; if (datalen > MAX_IMSGSIZE) { errno = ERANGE; return (NULL); } hdr.type = type; hdr.flags = 0; hdr.peerid = peerid; if ((hdr.pid = pid) == 0) hdr.pid = ibuf->pid; if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) { return (NULL); } if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1) return (NULL); return (wbuf); } int imsg_add(struct ibuf *msg, void *data, u_int16_t datalen) { if (datalen) if (ibuf_add(msg, data, datalen) == -1) { ibuf_free(msg); return (-1); } return (datalen); } void imsg_close(struct imsgbuf *ibuf, struct ibuf *msg) { struct imsg_hdr *hdr; hdr = (struct imsg_hdr *)msg->buf; hdr->flags &= ~IMSGF_HASFD; if (msg->fd != -1) hdr->flags |= IMSGF_HASFD; hdr->len = (u_int16_t)msg->wpos; ibuf_close(&ibuf->w, msg); } void imsg_free(struct imsg *imsg) { free(imsg->data); } int imsg_get_fd(struct imsgbuf *ibuf) { int fd; struct imsg_fd *ifd; if ((ifd = TAILQ_FIRST(&ibuf->fds)) == NULL) return (-1); fd = ifd->fd; TAILQ_REMOVE(&ibuf->fds, ifd, entry); free(ifd); return (fd); } int imsg_flush(struct imsgbuf *ibuf) { while (ibuf->w.queued) if (msgbuf_write(&ibuf->w) < 0) return (-1); return (0); } void imsg_clear(struct imsgbuf *ibuf) { int fd; msgbuf_clear(&ibuf->w); while ((fd = imsg_get_fd(ibuf)) != -1) close(fd); } tmate-1.8.10/compat/imsg.h000066400000000000000000000057461242461015400153210ustar00rootroot00000000000000/* $Id$ */ /* $OpenBSD: imsg.h,v 1.4 2010/05/26 13:56:07 nicm Exp $ */ /* * Copyright (c) 2006, 2007 Pierre-Yves Ritschard * Copyright (c) 2006, 2007, 2008 Reyk Floeter * Copyright (c) 2003, 2004 Henning Brauer * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "tmux.h" #define IBUF_READ_SIZE 65535 #define IMSG_HEADER_SIZE sizeof(struct imsg_hdr) #define MAX_IMSGSIZE 16384 struct ibuf { TAILQ_ENTRY(ibuf) entry; u_char *buf; size_t size; size_t max; size_t wpos; size_t rpos; int fd; }; struct msgbuf { TAILQ_HEAD(, ibuf) bufs; u_int32_t queued; int fd; }; struct ibuf_read { u_char buf[IBUF_READ_SIZE]; u_char *rptr; size_t wpos; }; struct imsg_fd { TAILQ_ENTRY(imsg_fd) entry; int fd; }; struct imsgbuf { TAILQ_HEAD(, imsg_fd) fds; struct ibuf_read r; struct msgbuf w; int fd; pid_t pid; }; #define IMSGF_HASFD 1 struct imsg_hdr { u_int32_t type; u_int16_t len; u_int16_t flags; u_int32_t peerid; u_int32_t pid; }; struct imsg { struct imsg_hdr hdr; int fd; void *data; }; /* buffer.c */ struct ibuf *ibuf_open(size_t); struct ibuf *ibuf_dynamic(size_t, size_t); int ibuf_add(struct ibuf *, const void *, size_t); void *ibuf_reserve(struct ibuf *, size_t); void *ibuf_seek(struct ibuf *, size_t, size_t); size_t ibuf_size(struct ibuf *); size_t ibuf_left(struct ibuf *); void ibuf_close(struct msgbuf *, struct ibuf *); int ibuf_write(struct msgbuf *); void ibuf_free(struct ibuf *); void msgbuf_init(struct msgbuf *); void msgbuf_clear(struct msgbuf *); int msgbuf_write(struct msgbuf *); void msgbuf_drain(struct msgbuf *, size_t); /* imsg.c */ void imsg_init(struct imsgbuf *, int); ssize_t imsg_read(struct imsgbuf *); ssize_t imsg_get(struct imsgbuf *, struct imsg *); int imsg_compose(struct imsgbuf *, u_int32_t, u_int32_t, pid_t, int, void *, u_int16_t); int imsg_composev(struct imsgbuf *, u_int32_t, u_int32_t, pid_t, int, const struct iovec *, int); struct ibuf *imsg_create(struct imsgbuf *, u_int32_t, u_int32_t, pid_t, u_int16_t); int imsg_add(struct ibuf *, void *, u_int16_t); void imsg_close(struct imsgbuf *, struct ibuf *); void imsg_free(struct imsg *); int imsg_flush(struct imsgbuf *); void imsg_clear(struct imsgbuf *); tmate-1.8.10/compat/queue.h000066400000000000000000000461151242461015400155010ustar00rootroot00000000000000/* $OpenBSD: queue.h,v 1.36 2012/04/11 13:29:14 naddy 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 circular 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 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. * * A circle 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 circle queue may be traversed in either direction, but has a more * complex end of list detection. * * 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) /* * 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) /* * Circular queue definitions. */ #define CIRCLEQ_HEAD(name, type) \ struct name { \ struct type *cqh_first; /* first element */ \ struct type *cqh_last; /* last element */ \ } #define CIRCLEQ_HEAD_INITIALIZER(head) \ { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } #define CIRCLEQ_ENTRY(type) \ struct { \ struct type *cqe_next; /* next element */ \ struct type *cqe_prev; /* previous element */ \ } /* * Circular queue access methods */ #define CIRCLEQ_FIRST(head) ((head)->cqh_first) #define CIRCLEQ_LAST(head) ((head)->cqh_last) #define CIRCLEQ_END(head) ((void *)(head)) #define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) #define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) #define CIRCLEQ_EMPTY(head) \ (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head)) #define CIRCLEQ_FOREACH(var, head, field) \ for((var) = CIRCLEQ_FIRST(head); \ (var) != CIRCLEQ_END(head); \ (var) = CIRCLEQ_NEXT(var, field)) #define CIRCLEQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = CIRCLEQ_FIRST(head); \ (var) != CIRCLEQ_END(head) && \ ((tvar) = CIRCLEQ_NEXT(var, field), 1); \ (var) = (tvar)) #define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ for((var) = CIRCLEQ_LAST(head); \ (var) != CIRCLEQ_END(head); \ (var) = CIRCLEQ_PREV(var, field)) #define CIRCLEQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ for ((var) = CIRCLEQ_LAST(head, headname); \ (var) != CIRCLEQ_END(head) && \ ((tvar) = CIRCLEQ_PREV(var, headname, field), 1); \ (var) = (tvar)) /* * Circular queue functions. */ #define CIRCLEQ_INIT(head) do { \ (head)->cqh_first = CIRCLEQ_END(head); \ (head)->cqh_last = CIRCLEQ_END(head); \ } while (0) #define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ (elm)->field.cqe_next = (listelm)->field.cqe_next; \ (elm)->field.cqe_prev = (listelm); \ if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \ (head)->cqh_last = (elm); \ else \ (listelm)->field.cqe_next->field.cqe_prev = (elm); \ (listelm)->field.cqe_next = (elm); \ } while (0) #define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ (elm)->field.cqe_next = (listelm); \ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \ (head)->cqh_first = (elm); \ else \ (listelm)->field.cqe_prev->field.cqe_next = (elm); \ (listelm)->field.cqe_prev = (elm); \ } while (0) #define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ (elm)->field.cqe_next = (head)->cqh_first; \ (elm)->field.cqe_prev = CIRCLEQ_END(head); \ if ((head)->cqh_last == CIRCLEQ_END(head)) \ (head)->cqh_last = (elm); \ else \ (head)->cqh_first->field.cqe_prev = (elm); \ (head)->cqh_first = (elm); \ } while (0) #define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.cqe_next = CIRCLEQ_END(head); \ (elm)->field.cqe_prev = (head)->cqh_last; \ if ((head)->cqh_first == CIRCLEQ_END(head)) \ (head)->cqh_first = (elm); \ else \ (head)->cqh_last->field.cqe_next = (elm); \ (head)->cqh_last = (elm); \ } while (0) #define CIRCLEQ_REMOVE(head, elm, field) do { \ if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \ (head)->cqh_last = (elm)->field.cqe_prev; \ else \ (elm)->field.cqe_next->field.cqe_prev = \ (elm)->field.cqe_prev; \ if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \ (head)->cqh_first = (elm)->field.cqe_next; \ else \ (elm)->field.cqe_prev->field.cqe_next = \ (elm)->field.cqe_next; \ _Q_INVALIDATE((elm)->field.cqe_prev); \ _Q_INVALIDATE((elm)->field.cqe_next); \ } while (0) #define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \ if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \ CIRCLEQ_END(head)) \ (head).cqh_last = (elm2); \ else \ (elm2)->field.cqe_next->field.cqe_prev = (elm2); \ if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \ CIRCLEQ_END(head)) \ (head).cqh_first = (elm2); \ else \ (elm2)->field.cqe_prev->field.cqe_next = (elm2); \ _Q_INVALIDATE((elm)->field.cqe_prev); \ _Q_INVALIDATE((elm)->field.cqe_next); \ } while (0) #endif /* !_SYS_QUEUE_H_ */ tmate-1.8.10/compat/setenv.c000066400000000000000000000026251242461015400156520ustar00rootroot00000000000000/* $Id$ */ /* * 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 "tmux.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); } tmate-1.8.10/compat/strcasestr.c000066400000000000000000000043611242461015400165420ustar00rootroot00000000000000/* $Id$ */ /* $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 /* * 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); } tmate-1.8.10/compat/strlcat.c000066400000000000000000000032611242461015400160170ustar00rootroot00000000000000/* $Id$ */ /* $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 "tmux.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 */ } tmate-1.8.10/compat/strlcpy.c000066400000000000000000000031021242461015400160350ustar00rootroot00000000000000/* $Id$ */ /* $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 "tmux.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 */ } tmate-1.8.10/compat/strsep.c000066400000000000000000000047361242461015400156730ustar00rootroot00000000000000/* $Id$ */ /* $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 /* * 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 */ } tmate-1.8.10/compat/strtonum.c000066400000000000000000000034071242461015400162400ustar00rootroot00000000000000/* $Id$ */ /* $OpenBSD: strtonum.c,v 1.6 2004/08/03 19:38:01 millert Exp $ */ /* * Copyright (c) 2004 Ted Unangst and Todd Miller * All rights reserved. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" #define INVALID 1 #define TOOSMALL 2 #define TOOLARGE 3 long long strtonum(const char *numstr, long long minval, long long maxval, const char **errstrp) { long long ll = 0; char *ep; int error = 0; struct errval { const char *errstr; int err; } ev[4] = { { NULL, 0 }, { "invalid", EINVAL }, { "too small", ERANGE }, { "too large", ERANGE }, }; ev[0].err = errno; errno = 0; if (minval > maxval) error = INVALID; else { ll = strtoll(numstr, &ep, 10); if (numstr == ep || *ep != '\0') error = INVALID; else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval) error = TOOSMALL; else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval) error = TOOLARGE; } if (errstrp != NULL) *errstrp = ev[error].errstr; errno = ev[error].err; if (error) ll = 0; return (ll); } tmate-1.8.10/compat/tree.h000066400000000000000000000610771242461015400153200ustar00rootroot00000000000000/* $Id$ */ /* $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_ */ tmate-1.8.10/compat/unvis.c000066400000000000000000000140361242461015400155110ustar00rootroot00000000000000/* $Id$ */ /* $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 "tmux.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); } tmate-1.8.10/compat/vis.c000066400000000000000000000125271242461015400151510ustar00rootroot00000000000000/* $Id$ */ /* $OpenBSD: vis.c,v 1.19 2005/09/01 17:15:49 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 "tmux.h" #define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7') #define isvisible(c) \ (((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)) { *dst++ = c; if (c == '\\' && (flag & VIS_NOSLASH) == 0) *dst++ = '\\'; *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)) { i = 1; *dst++ = c; if (c == '\\' && (flag & VIS_NOSLASH) == 0) { /* need space for the extra '\\' */ if (dst < end) *dst++ = '\\'; else { dst--; i = 2; break; } } 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 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); } tmate-1.8.10/compat/vis.h000066400000000000000000000061411242461015400151510ustar00rootroot00000000000000/* $Id$ */ /* $OpenBSD: vis.h,v 1.11 2005/08/09 19:38:31 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 */ /* * 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 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_ */ tmate-1.8.10/configure.ac000066400000000000000000000256261242461015400152130ustar00rootroot00000000000000# $Id$ # Miscellaneous autofoo bullshit. AC_INIT(tmate, 1.8.10) AC_CONFIG_AUX_DIR(etc) AM_INIT_AUTOMAKE([foreign]) 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=""} # 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_INSTALL # Check for various headers. Alternatives included from compat.h. AC_CHECK_HEADERS( [ \ bitstring.h \ curses.h \ dirent.h \ fcntl.h \ inttypes.h \ libutil.h \ ncurses.h \ ndir.h \ paths.h \ pty.h \ stdint.h \ sys/dir.h \ sys/ndir.h \ sys/tree.h \ term.h \ util.h \ ] ) # Is this a debug build? #found_debug=yes AC_ARG_ENABLE( debug, AC_HELP_STRING(--enable-debug, create a debug build), found_debug=$enable_debug ) AM_CONDITIONAL(IS_DEBUG, test "x$found_debug" = xyes) # Is this a static build? AC_ARG_ENABLE( static, AC_HELP_STRING(--enable-static, create a static build), found_static=$enable_static ) if test "x$found_static" = xyes; then LDFLAGS="$LDFLAGS -static" fi # Is this gcc? AM_CONDITIONAL(IS_GCC, test "x$GCC" = xyes) AC_MSG_CHECKING(for gcc that whines about -I) AC_EGREP_CPP( yes, [ #if __GNUC__ > 3 yes #endif ], found_gcc4=yes, found_gcc4=no ) AM_CONDITIONAL(IS_GCC4, test "x$found_gcc4" = xyes) AC_MSG_RESULT($found_gcc4) # 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) # Is this glibc? AC_MSG_CHECKING(for glibc) AC_EGREP_CPP( yes, [ #include #ifdef __GLIBC__ yes #endif ], found_glibc=yes, found_glibc=no ) AM_CONDITIONAL(IS_GLIBC, test "x$found_glibc" = xyes) AC_MSG_RESULT($found_glibc) # Look for clock_gettime. Must come before event_init. AC_SEARCH_LIBS(clock_gettime, rt) AC_CHECK_LIB(z, inflate, [], [AC_MSG_ERROR([zlib library required])]) AC_CHECK_LIB(crypto,CRYPTO_new_ex_data, [], [AC_MSG_ERROR([OpenSSL library required])]) AC_CHECK_LIB(ssl, SSL_library_init, [], [AC_MSG_ERROR([OpenSSL library required])]) # Look for libevent. PKG_CHECK_MODULES( LIBEVENT, libevent, [ CPPFLAGS="$LIBEVENT_CFLAGS $CPPFLAGS" LIBS="$LIBEVENT_LIBS $LIBS" found_libevent=yes ], [ AC_SEARCH_LIBS( event_init, [event event-1.4 event2], found_libevent=yes, found_libevent=no ) ] ) if test "x$found_libevent" = xno; then AC_MSG_ERROR("libevent not found") fi # Look for curses. AC_SEARCH_LIBS( setupterm, [terminfo curses ncurses], found_curses=yes, found_curses=no ) if test "x$found_curses" = xno; then AC_MSG_ERROR("curses not found") fi # Check for b64_ntop. 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) 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 AC_MSG_RESULT(no) fi fi if test "x$found_b64_ntop" = xyes; then AC_DEFINE(HAVE_B64_NTOP) AC_MSG_RESULT(yes) fi AM_CONDITIONAL(NO_B64_NTOP, [test "x$found_b64_ntop" = xno]) # 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 imsg in libutil. compat/imsg.c is linked by Makefile.am if missing. 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) fi AM_CONDITIONAL(NO_IMSG, [test "x$found_imsg_init" = xno]) # Look for forkpty in libutil. compat/forkpty-*.c is linked if not found. 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(NO_FORKPTY, [test "x$found_forkpty" = xno]) # Look for closefrom, compat/closefrom.c used if missing. AC_CHECK_FUNC(closefrom, found_closefrom=yes, found_closefrom=no) if test "x$found_closefrom" = xyes; then AC_DEFINE(HAVE_CLOSEFROM) fi AM_CONDITIONAL(NO_CLOSEFROM, [test "x$found_closefrom" = xno]) # Look for daemon, compat/daemon.c used if missing. AC_CHECK_FUNC(daemon, found_daemon=yes, found_daemon=no) if test "x$found_daemon" = xyes; then AC_DEFINE(HAVE_DAEMON) fi AM_CONDITIONAL(NO_DAEMON, [test "x$found_daemon" = xno]) # Look for setenv, compat/setenv.c used if missing. AC_CHECK_FUNC(setenv, found_setenv=yes, found_setenv=no) if test "x$found_setenv" = xyes; then AC_DEFINE(HAVE_SETENV) fi AM_CONDITIONAL(NO_SETENV, [test "x$found_setenv" = xno]) # Look for strlcpy, compat/strlcpy.c used if missing. AC_CHECK_FUNC(strlcpy, found_strlcpy=yes, found_strlcpy=no) if test "x$found_strlcpy" = xyes; then AC_DEFINE(HAVE_STRLCPY) fi AM_CONDITIONAL(NO_STRLCPY, [test "x$found_strlcpy" = xno]) # Look for strlcat, compat/strlcat.c used if missing. AC_CHECK_FUNC(strlcat, found_strlcat=yes, found_strlcat=no) if test "x$found_strlcat" = xyes; then AC_DEFINE(HAVE_STRLCAT) fi AM_CONDITIONAL(NO_STRLCAT, [test "x$found_strlcat" = xno]) # Look for asprintf, compat/asprintf.c used if missing. AC_CHECK_FUNC(asprintf, found_asprintf=yes, found_asprintf=no) if test "x$found_asprintf" = xyes; then AC_DEFINE(HAVE_ASPRINTF) fi AM_CONDITIONAL(NO_ASPRINTF, [test "x$found_asprintf" = xno]) # Look for fgetln, compat/fgetln.c used if missing. AC_CHECK_FUNC(fgetln, found_fgetln=yes, found_fgetln=no) if test "x$found_fgetln" = xyes; then AC_DEFINE(HAVE_FGETLN) fi AM_CONDITIONAL(NO_FGETLN, [test "x$found_fgetln" = xno]) # Look for strcasestr, compat/strcasestr.c used if missing. AC_CHECK_FUNC(strcasestr, found_strcasestr=yes, found_strcasestr=no) if test "x$found_strcasestr" = xyes; then AC_DEFINE(HAVE_STRCASESTR) fi AM_CONDITIONAL(NO_STRCASESTR, [test "x$found_strcasestr" = xno]) # Look for strsep, compat/strsep.c used if missing. AC_CHECK_FUNC(strsep, found_strsep=yes, found_strsep=no) if test "x$found_strsep" = xyes; then AC_DEFINE(HAVE_STRSEP) fi AM_CONDITIONAL(NO_STRSEP, [test "x$found_strsep" = xno]) # Look for strtonum, compat/strtonum.c used if missing. AC_CHECK_FUNC(strtonum, found_strtonum=yes, found_strtonum=no) if test "x$found_strtonum" = xyes; then AC_DEFINE(HAVE_STRTONUM) fi AM_CONDITIONAL(NO_STRTONUM, [test "x$found_strtonum" = xno]) # Look for strnvis, compat/{vis,unvis}.c used if missing. AC_CHECK_FUNC(strnvis, found_strnvis=yes, found_strnvis=no) if test "x$found_strnvis" = xyes; then AC_DEFINE(HAVE_VIS) fi AM_CONDITIONAL(NO_VIS, [test "x$found_strnvis" = xno]) # 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_CHECK_DECLS( [optarg, optind, optreset], , found_getopt=no, [ #include ] ) if test "x$found_getopt" != xno; then AC_MSG_CHECKING(if system getopt should be avoided) if test "x$found_glibc" = xyes; then found_getopt=no AC_MSG_RESULT(yes) else AC_MSG_RESULT(no) AC_DEFINE(HAVE_GETOPT) fi fi fi AM_CONDITIONAL(NO_GETOPT, [test "x$found_getopt" = xno]) # Check for some functions that are replaced or omitted. AC_CHECK_FUNCS( [ \ bzero \ dirfd \ setproctitle \ sysconf \ ] ) # Check for BSD-style integer types. AC_MSG_CHECKING(for BSD-style unsigned types) AC_COMPILE_IFELSE([AC_LANG_SOURCE( [ #include #ifdef HAVE_STDINT_H #include #else #include #endif int main(void) { u_int8_t u8; u_int16_t u16; u_int32_t u32; u_int64_t u64; } ])], [AC_DEFINE(HAVE_BSD_TYPES) AC_MSG_RESULT(yes)], AC_MSG_RESULT(no) ) # Look for a suitable queue.h. 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 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 # Figure out the platform for osdep-*.c and forkpty-*.c. AC_MSG_CHECKING(platform) case "$host_os" in *aix*) AC_MSG_RESULT(aix) PLATFORM=aix ;; *darwin*) AC_MSG_RESULT(darwin) AC_DEFINE(BROKEN_CMSG_FIRSTHDR) PLATFORM=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 ;; *hpux*) AC_MSG_RESULT(hpux) PLATFORM=hpux ;; *) 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) # autoconf should create a Makefile. A shock! AC_OUTPUT(Makefile) tmate-1.8.10/control-notify.c000066400000000000000000000115621242461015400160510ustar00rootroot00000000000000/* $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 "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, struct evbuffer *input) { u_char *buf; size_t len; struct evbuffer *message; u_int i; if (c->session == NULL) return; buf = EVBUFFER_DATA(input); len = EVBUFFER_LENGTH(input); /* * 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(); 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_window_layout_changed(struct window *w) { struct client *c; struct session *s; struct format_tree *ft; struct winlink *wl; u_int i; const char *template; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); 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; template = "%layout-change #{window_id} #{window_layout}"; ft = format_create(); wl = winlink_find_by_window(&s->windows, w); if (wl != NULL) { format_winlink(ft, c->session, wl); control_write(c, "%s", format_expand(ft, template)); } format_free(ft); } } void control_notify_window_unlinked(unused struct session *s, struct window *w) { struct client *c; u_int i; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) continue; control_write(c, "%%window-close @%u", w->id); } } void control_notify_window_linked(unused struct session *s, struct window *w) { struct client *c; struct session *cs; u_int i; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); 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; u_int i; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) continue; control_write(c, "%%window-renamed @%u %s", w->id, w->name); } } void control_notify_attached_session_changed(struct client *c) { struct session *s; if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) return; s = c->session; control_write(c, "%%session-changed $%u %s", s->id, s->name); } void control_notify_session_renamed(struct session *s) { struct client *c; u_int i; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); 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; u_int i; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) continue; control_write(c, "%%sessions-changed"); } } void control_notify_session_close(unused struct session *s) { struct client *c; u_int i; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) continue; control_write(c, "%%sessions-changed"); } } tmate-1.8.10/control.c000066400000000000000000000043361242461015400145440ustar00rootroot00000000000000/* $Id$ */ /* * 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 printflike2 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_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_push_stdout(c); } /* Control input callback. Read lines and fire commands. */ void control_callback(struct client *c, int closed, unused void *data) { char *line, *cause; struct cmd_list *cmdlist; 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 */ c->flags |= CLIENT_EXIT; break; } if (cmd_string_parse(line, &cmdlist, NULL, 0, &cause) != 0) { c->cmdq->time = time(NULL); c->cmdq->number++; cmdq_guard(c->cmdq, "begin"); control_write(c, "parse error: %s", cause); cmdq_guard(c->cmdq, "error"); free(cause); } else { cmdq_run(c->cmdq, cmdlist); cmd_list_free(cmdlist); } free(line); } } tmate-1.8.10/environ.c000066400000000000000000000101051242461015400145330ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Environment - manipulate a set of environment variables. */ RB_GENERATE(environ, environ_entry, entry, environ_cmp); int environ_cmp(struct environ_entry *envent1, struct environ_entry *envent2) { return (strcmp(envent1->name, envent2->name)); } /* Initialise the environment. */ void environ_init(struct environ *env) { RB_INIT(env); } /* Free an environment. */ void environ_free(struct environ *env) { struct environ_entry *envent; while (!RB_EMPTY(env)) { envent = RB_ROOT(env); RB_REMOVE(environ, env, envent); free(envent->name); free(envent->value); free(envent); } } /* Copy one environment into another. */ void environ_copy(struct environ *srcenv, struct environ *dstenv) { struct environ_entry *envent; RB_FOREACH(envent, environ, srcenv) environ_set(dstenv, envent->name, envent->value); } /* Find an environment variable. */ struct environ_entry * environ_find(struct environ *env, const char *name) { struct environ_entry envent; envent.name = (char *) name; return (RB_FIND(environ, env, &envent)); } /* Set an environment variable. */ void environ_set(struct environ *env, const char *name, const char *value) { struct environ_entry *envent; if ((envent = environ_find(env, name)) != NULL) { free(envent->value); if (value != NULL) envent->value = xstrdup(value); else envent->value = NULL; } else { envent = xmalloc(sizeof *envent); envent->name = xstrdup(name); if (value != NULL) envent->value = xstrdup(value); else envent->value = NULL; RB_INSERT(environ, env, envent); } } /* Set an environment variable from a NAME=VALUE string. */ void environ_put(struct environ *env, const char *var) { char *name, *value; value = strchr(var, '='); if (value == NULL) return; value++; name = xstrdup(var); name[strcspn(name, "=")] = '\0'; environ_set(env, name, value); 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 a space-separated list of variables from a destination into a source * environment. */ void environ_update(const char *vars, struct environ *srcenv, struct environ *dstenv) { struct environ_entry *envent; char *copyvars, *var, *next; copyvars = next = xstrdup(vars); while ((var = strsep(&next, " ")) != NULL) { if ((envent = environ_find(srcenv, var)) == NULL) environ_set(dstenv, var, NULL); else environ_set(dstenv, envent->name, envent->value); } free(copyvars); } /* Push environment into the real environment - use after fork(). */ void environ_push(struct environ *env) { ARRAY_DECL(, char *) varlist; struct environ_entry *envent; char **varp, *var; u_int i; ARRAY_INIT(&varlist); for (varp = environ; *varp != NULL; varp++) { var = xstrdup(*varp); var[strcspn(var, "=")] = '\0'; ARRAY_ADD(&varlist, var); } for (i = 0; i < ARRAY_LENGTH(&varlist); i++) unsetenv(ARRAY_ITEM(&varlist, i)); ARRAY_FREE(&varlist); RB_FOREACH(envent, environ, env) { if (envent->value != NULL) setenv(envent->name, envent->value, 1); } } tmate-1.8.10/examples/000077500000000000000000000000001242461015400145305ustar00rootroot00000000000000tmate-1.8.10/examples/bash_completion_tmux.sh000066400000000000000000000037361242461015400213200ustar00rootroot00000000000000# START tmux completion # This file is in the public domain # See: http://www.debian-administration.org/articles/317 for how to write more. # Usage: Put "source bash_completion_tmux.sh" into your .bashrc _tmux() { local cur prev opts COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" opts=" \ attach-session \ bind-key \ break-pane \ capture-pane \ choose-client \ choose-session \ choose-window \ clear-history \ clock-mode \ command-prompt \ confirm-before \ copy-buffer \ copy-mode \ delete-buffer \ detach-client \ display-message \ display-panes \ down-pane \ find-window \ has-session \ if-shell \ join-pane \ kill-pane \ kill-server \ kill-session \ kill-window \ last-window \ link-window \ list-buffers \ list-clients \ list-commands \ list-keys \ list-panes \ list-sessions \ list-windows \ load-buffer \ lock-client \ lock-server \ lock-session \ move-window \ new-session \ new-window \ next-layout \ next-window \ paste-buffer \ pipe-pane \ previous-layout \ previous-window \ refresh-client \ rename-session \ rename-window \ resize-pane \ respawn-window \ rotate-window \ run-shell \ save-buffer \ select-layout \ select-pane \ select-prompt \ select-window \ send-keys \ send-prefix \ server-info \ set-buffer \ set-environment \ set-option \ set-window-option \ show-buffer \ show-environment \ show-messages \ show-options \ show-window-options \ source-file \ split-window \ start-server \ suspend-client \ swap-pane \ swap-window \ switch-client \ unbind-key \ unlink-window \ up-pane" COMPREPLY=($(compgen -W "${opts}" -- ${cur})) return 0 } complete -F _tmux tmux # END tmux completion tmate-1.8.10/examples/h-boetes.conf000066400000000000000000000016211242461015400171050ustar00rootroot00000000000000# $Id: h-boetes.conf,v 1.2 2009-10-25 21:45:26 nicm Exp $ # # From Han Boetes. set -g default-command zsh set -g status-right "#(uptime|awk '{print $11}') #(date)" # Statusbar properties. set -g display-time 3000 set -g status-bg black set -g status-fg cyan set-window-option -g window-status-current-attr bright,reverse set-window-option -g window-status-current-bg cyan set-window-option -g window-status-current-fg black # Use c-t instead of c-b as the prefix unbind C-b set -g prefix C-t bind C-t send-prefix bind t send-prefix # Bind function keys. bind -n F1 select-window -t 1 bind -n F2 select-window -t 2 bind -n F3 select-window -t 3 bind -n F4 select-window -t 4 bind -n F5 select-window -t 5 bind -n F6 select-window -t 6 bind -n F7 select-window -t 7 bind -n F8 select-window -t 8 # All new windows started at startup. new emacs neww irssi neww mutt neww neww neww neww neww select-window -t 1 tmate-1.8.10/examples/n-marriott.conf000066400000000000000000000044421242461015400174770ustar00rootroot00000000000000# $Id: n-marriott.conf,v 1.11 2009-11-24 19:03:59 nicm Exp $ # # By Nicholas Marriott. Public domain. # Default global options. set -g status-bg green set -g status-right "%H:%M" # %d-%b-%y set -g bell-action none set -g lock-after-time 1800 # Default global window options. setw -g remain-on-exit on setw -g window-status-current-attr "underscore" #setw -g xterm-keys on # Prefix key. set -g prefix C-a unbind C-b bind C-a send-prefix # Keys to switch session. bind Q switchc -t0 bind W switchc -t1 bind E switchc -t2 # Other key bindings. bind F1 selectw -t:10 bind F2 selectw -t:11 bind F3 selectw -t:12 bind F4 selectw -t:13 bind F5 selectw -t:14 bind F6 selectw -t:15 bind F7 selectw -t:16 bind F8 selectw -t:17 bind F9 selectw -t:18 bind F10 selectw -t:19 bind F11 selectw -t:20 bind F12 selectw -t:21 bind m setw monitor-activity bind y setw force-width 81 bind u setw force-width 0 bind -n F1 run-shell 'mpc toggle >/dev/null 2>&1' bind -n F2 run-shell 'mpc' bind -n F3 run-shell 'mpc prev >/dev/null 2>&1' bind -n F4 run-shell 'mpc next >/dev/null 2>&1' bind -n F5 run-shell 'mpc volume -5 >/dev/null 2>&1' bind -n F6 run-shell 'mpc volume +5 >/dev/null 2>&1' # Hide and show window name from status line bind '-' setw window-status-format '#I'\; setw window-status-current-format '#I' bind '+' setw window-status-format '#I:#W#F'\; setw window-status-current-format '#I:#W#F' # First session. new -d -s0 -nirssi 'exec ssh -t natalya exec sh ~/bin/tmux-start' setw -t0:0 monitor-activity on setw -t0:0 aggressive-resize on set -t0 status-bg green neww -d -ntodo 'exec emacs ~/TODO' setw -t0:1 aggressive-resize on neww -d -ntodo2 'exec emacs ~/TODO2' setw -t0:2 aggressive-resize on neww -d -nncmpc 'exec ncmpc -f ~/.ncmpc.conf' setw -t0:3 aggressive-resize on neww -d -nmutt 'exec mutt' setw -t0:4 aggressive-resize on neww -d neww -d neww -d neww -d neww -d neww -d neww -d neww -d neww -d neww -d neww -d neww -d # Second session. new -d -s1 set -t1 status-bg cyan linkw -dk -t0 -s0:0 linkw -dk -t1 -s0:1 linkw -dk -t2 -s0:2 linkw -dk -t3 -s0:3 linkw -dk -t4 -s0:4 neww -d neww -d neww -d neww -d neww -d neww -d # Third session. new -d -s2 set -t2 status-bg yellow linkw -dk -t0 -s0:0 linkw -dk -t1 -s0:1 linkw -dk -t2 -s0:2 linkw -dk -t3 -s0:3 linkw -dk -t4 -s0:4 neww -d neww -d neww -d neww -d neww -d neww -d tmate-1.8.10/examples/screen-keys.conf000066400000000000000000000034151242461015400176320ustar00rootroot00000000000000# $Id: screen-keys.conf,v 1.7 2010-07-31 11:39:13 nicm Exp $ # # By Nicholas Marriott. Public domain. # # This configuration file binds many of the common GNU screen key bindings to # appropriate tmux key bindings. Note that for some key bindings there is no # tmux analogue and also that this set omits binding some commands available in # tmux but not in screen. # # Note this is only a selection of key bindings and they are in addition to the # normal tmux key bindings. This is intended as an example not as to be used # as-is. # Set the prefix to ^A. unbind C-b set -g prefix ^A bind a send-prefix # Bind appropriate commands similar to screen. # lockscreen ^X x unbind ^X bind ^X lock-server unbind x bind x lock-server # screen ^C c unbind ^C bind ^C new-window unbind c bind c new-window # detach ^D d unbind ^D bind ^D detach # displays * unbind * bind * list-clients # next ^@ ^N sp n unbind ^@ bind ^@ next-window unbind ^N bind ^N next-window unbind " " bind " " next-window unbind n bind n next-window # title A unbind A bind A command-prompt "rename-window %%" # other ^A unbind ^A bind ^A last-window # prev ^H ^P p ^? unbind ^H bind ^H previous-window unbind ^P bind ^P previous-window unbind p bind p previous-window unbind BSpace bind BSpace previous-window # windows ^W w unbind ^W bind ^W list-windows unbind w bind w list-windows # quit \ unbind '\' bind '\' confirm-before "kill-server" # kill K k unbind K bind K confirm-before "kill-window" unbind k bind k confirm-before "kill-window" # redisplay ^L l unbind ^L bind ^L refresh-client unbind l bind l refresh-client # split -v | unbind | bind | split-window # :kB: focus up unbind Tab bind Tab select-pane -t:.+ unbind BTab bind BTab select-pane -t:.- # " windowlist -b unbind '"' bind '"' choose-window tmate-1.8.10/examples/t-williams.conf000066400000000000000000000053451242461015400174700ustar00rootroot00000000000000# $Id: t-williams.conf,v 1.1 2009-11-02 18:59:28 nicm Exp $ # # ~/.tmux.conf - tmux terminal multiplexer config # Thayer Williams (http://cinderwick.ca) # "Feel free to do whatever you like with it." # I typically start tmux from ~/.xinitrc with the following: # # urxvt -e bash -c "tmux attach -d -t mysession" & # # and recall it any time thereafter with xbindkeys (Mod4+s): # # "urxvt -e bash -c 'tmux attach -d -t mysession'" # m:0x50 + c:39 # set prefix key to ctrl+a until I have time to adapt unbind C-b set -g prefix C-a # send the prefix to client inside window (ala nested sessions) bind-key a send-prefix # toggle last window like screen bind-key C-a last-window # confirm before killing a window or the server bind-key k confirm kill-window bind-key K confirm kill-server # toggle statusbar bind-key b set-option status # ctrl+left/right cycles thru windows bind-key -n C-right next bind-key -n C-left prev # open a man page in new window bind / command-prompt "split-window 'exec man %%'" # quick view of processes bind '~' split-window "exec htop" # scrollback buffer n lines set -g history-limit 5000 # listen for activity on all windows set -g bell-action any # on-screen time for display-panes in ms set -g display-panes-time 2000 # start window indexing at one instead of zero set -g base-index 1 # enable wm window titles set -g set-titles on # wm window title string (uses statusbar variables) set -g set-titles-string "tmux.#I.#W" # session initialization new -s mysession mutt neww -t 2 neww -d -t 3 neww -d -t 5 mocp neww -d -t 6 rtorrent selectw -t 1 # statusbar -------------------------------------------------------------- set -g display-time 2000 # default statusbar colors set -g status-fg white set -g status-bg default set -g status-attr default # default window title colors set-window-option -g window-status-fg cyan set-window-option -g window-status-bg default set-window-option -g window-status-attr dim # active window title colors set-window-option -g window-status-current-fg white set-window-option -g window-status-current-bg default set-window-option -g window-status-current-attr bright # command/message line colors set -g message-fg white set -g message-bg black set -g message-attr bright # center align the window list set -g status-justify centre # show some useful stats but only when tmux is started # outside of Xorg, otherwise dwm statusbar shows these already set -g status-right "" set -g status-left "" if '[ -z "$DISPLAY" ]' 'set -g status-left "[#[fg=green] #H #[default]]"' if '[ -z "$DISPLAY" ]' 'set -g status-right "[ #[fg=magenta]#(cat /proc/loadavg | cut -d \" \" -f 1,2,3)#[default] ][ #[fg=cyan,bright]%a %Y-%m-%d %H:%M #[default]]"' if '[ -z "$DISPLAY" ]' 'set -g status-right-length 50' tmate-1.8.10/examples/tmux.vim000066400000000000000000000124111242461015400162410ustar00rootroot00000000000000" Vim syntax file " Language: tmux(1) configuration file " Maintainer: Tiago Cunha " Last Change: $Date: 2010-07-27 18:29:07 $ " License: This file is placed in the public domain. " " To install this file: " " - Drop the file in the syntax directory into runtimepath (such as " ~/.vim/syntax/tmux.vim). " - Make the filetype recognisable by adding the following to filetype.vim " (~/.vim/filetype.vim): " " augroup filetypedetect " au BufNewFile,BufRead .tmux.conf*,tmux.conf* setf tmux " augroup END " " - Switch on syntax highlighting by adding "syntax enable" to .vimrc. " if version < 600 syntax clear elseif exists("b:current_syntax") finish endif setlocal iskeyword+=- syntax case match syn keyword tmuxAction any current none syn keyword tmuxBoolean off on syn keyword tmuxCmds \ attach[-session] detach[-client] has[-session] kill-server \ kill-session lsc list-clients lscm list-commands ls list-sessions \ lockc lock-client locks lock-session new[-session] refresh[-client] \ rename[-session] showmsgs show-messages source[-file] start[-server] \ suspendc suspend-client switchc switch-client \ copy-mode \ breakp break-pane capturep capture-pane choose-client choose-session \ choose-tree choose-window displayp display-panes findw find-window \ joinp join-pane killp kill-pane killw kill-window lastp last-pane \ last[-window] linkw link-window lsp list-panes lsw list-windows movep \ move-pane movew move-window neww new-window nextl next-layout \ next[-window] pipep pipe-pane prevl previous-layout prev[ious-window] \ renamew rename-window resizep resize-pane respawnp respawn-pane \ respawnw respawn-window rotatew rotate-window selectl select-layout \ selectp select-pane selectw select-window splitw split-window swapp \ swap-pane swapw swap-window unlinkw unlink-window \ bind[-key] lsk list-keys send[-keys] send-prefix unbind[-key] \ set[-option] setw set-window-option show[-options] showw \ show-window-options \ setenv set-environment showenv show-environment \ command-prompt confirm[-before] display[-message] \ choose-buffer clearhist clear-history deleteb delete-buffer lsb \ list-buffers loadb load-buffer pasteb paste-buffer saveb save-buffer \ setb set-buffer showb show-buffer \ clock-mode if[-shell] lock[-server] run[-shell] server-info info \ choose-list syn keyword tmuxOptsSet \ buffer-limit escape-time exit-unattached exit-unattached quiet \ set-clipboard \ base-index bell-action bell-on-alert default-command default-path \ default-shell default-terminal destroy-unattached detach-on-destroy \ display-panes-active-colour display-panes-colour display-panes-time \ display-time history-limit \ lock-after-time lock-command lock-server \ message-command-attr message-attr message-command-bg message-bg \ message-command-fg message-fg message-limit \ mouse-resize-pane mouse-select-pane mouse-select-window mouse-utf8 \ pane-active-border-bg pane-border-bg pane-active-border-fg \ pane-border-fg prefix prefix2 \ renumber-windows repeat-time set-remain-on-exit set-titles \ set-titles-string status status-attr status-bg status-fg \ status-interval status-justify status-keys status-left \ status-left-attr status-left-bg status-left-fg status-left-length \ status-position status-right status-right-attr status-right-bg \ status-right-fg status-right-length status-utf8 terminal-overrides \ update-environment visual-activity visual-bell visual-content \ visual-silence word-separators syn keyword tmuxOptsSetw \ aggressive-resize alternate-screen automatic-rename \ c0-change-interval c0-change-trigger clock-mode-colour \ clock-mode-style force-height force-width layout-history-limit \ main-pane-height main-pane-width mode-attr mode-bg mode-fg move-keys \ mode-mouse monitor-activity monitor-content monitor-silence \ other-pane-height other-pane-width pane-base-index remain-on-exit \ synchronize-panes utf8 window-status-bell-attr window-status-bell-bg \ window-status-bell-fg window-status-content-attr \ window-status-content-bg window-status-content-fg \ window-status-activity-attr window-status-activity-bg \ window-status-activity-fg window-status-attr \ window-status-current-attr window-status-attr window-status-current-bg \ window-status-bg window-status-current-fg window-status-fg \ window-status-current-format window-status-format \ window-status-separator xterm-keys wrap-search syn keyword tmuxTodo FIXME NOTE TODO XXX contained syn match tmuxKey /\(C-\|M-\|\^\)\+\S\+/ display syn match tmuxNumber /\d\+/ display syn match tmuxOptions /\s-\a\+/ display syn match tmuxVariable /\w\+=/ display syn match tmuxVariableExpansion /\${\=\w\+}\=/ display syn region tmuxComment start=/#/ end=/$/ contains=tmuxTodo display oneline syn region tmuxString start=/"/ end=/"/ display oneline syn region tmuxString start=/'/ end=/'/ display oneline hi def link tmuxAction Boolean hi def link tmuxBoolean Boolean hi def link tmuxCmds Keyword hi def link tmuxComment Comment hi def link tmuxKey Special hi def link tmuxNumber Number hi def link tmuxOptions Identifier hi def link tmuxOptsSet Function hi def link tmuxOptsSetw Function hi def link tmuxString String hi def link tmuxTodo Todo hi def link tmuxVariable Constant hi def link tmuxVariableExpansion Constant let b:current_syntax = "tmux" tmate-1.8.10/examples/tmux_backup.sh000066400000000000000000000047211242461015400174120ustar00rootroot00000000000000#!/bin/bash # # By Victor Orlikowski. Public domain. # # This script maintains snapshots of each pane's # history buffer, for each tmux session you are running. # # It is intended to be run by cron, on whatever interval works # for you. # Maximum number of snapshots to keep. max_backups=12 # Names of sessions you may wish to exclude from snapshotting, # space separated. ignore_sessions="" # The directory into which you want your snapshots placed. # The default is probably "good enough." backup_dir=~/.tmux_backup/snapshot ######################################################################## # Rotate previous backups. i=${max_backups} while [[ ${i} != 0 ]] ; do if [ -d ${backup_dir}.${i} ] ; then if [[ ${i} = ${max_backups} ]] ; then rm -r ${backup_dir}.${i} else mv ${backup_dir}.${i} ${backup_dir}.$((${i}+1)) fi fi i=$((${i}-1)) done if [ -d ${backup_dir} ] ; then mv ${backup_dir} ${backup_dir}.1 fi ## Dump hardcopy from all windows in all available tmux sessions. unset TMUX for session in $(tmux list-sessions | cut -d' ' -f1 | sed -e 's/:$//') ; do for ignore_session in ${ignore_sessions} ; do if [ ${session} = ${ignore_session} ] ; then continue 2 fi done # Session name can contain the colon character (":"). # This can screw up addressing of windows within tmux, since # target windows are specified as target-session:target-window. # # We use uuidgen to create a "safe" temporary session name, # which we then use to create a "detached" session that "links" # to the "real" session that we want to back up. tmpsession=$(uuidgen) tmux new-session -d -s "$tmpsession" -t "$session" HISTSIZE=$(tmux show-options -g -t "$tmpsession" | grep "history-limit" | awk '{print $2}') for win in $(tmux list-windows -t "$tmpsession" | grep -v "^\s" | cut -d' ' -f1 | sed -e 's/:$//'); do session_dir=$(echo "$session" | sed -e 's/ /_/g' | sed -e 's%/%|%g') win_spec="$tmpsession":"$win" if [ ! -d ${backup_dir}/${session_dir}/${win} ] ; then mkdir -p ${backup_dir}/${session_dir}/${win} fi for pane in $(tmux list-panes -t "$win_spec" | cut -d' ' -f1 | sed -e 's/:$//'); do pane_path=${backup_dir}/${session_dir}/${win}/${pane} pane_spec="$win_spec"."$pane" tmux capture-pane -t "$pane_spec" -S -${HISTSIZE} tmux save-buffer ${pane_path} if [ ! -s ${pane_path} ] ; then sleep 1 rm ${pane_path} fi done done tmux kill-session -t "$tmpsession" done tmate-1.8.10/examples/vim-keys.conf000066400000000000000000000021001242461015400171340ustar00rootroot00000000000000# $Id: vim-keys.conf,v 1.2 2010-09-18 09:36:15 nicm Exp $ # # vim-keys.conf, v1.2 2010/09/12 # # By Daniel Thau. Public domain. # # This configuration file binds many vi- and vim-like bindings to the # appropriate tmux key bindings. Note that for many key bindings there is no # tmux analogue. This is intended for tmux 1.3, which handles pane selection # differently from the previous versions # split windows like vim # vim's definition of a horizontal/vertical split is reversed from tmux's bind s split-window -v bind v split-window -h # move around panes with hjkl, as one would in vim after pressing ctrl-w bind h select-pane -L bind j select-pane -D bind k select-pane -U bind l select-pane -R # resize panes like vim # feel free to change the "1" to however many lines you want to resize by, only # one at a time can be slow bind < resize-pane -L 1 bind > resize-pane -R 1 bind - resize-pane -D 1 bind + resize-pane -U 1 # bind : to command-prompt like vim # this is the default in tmux already bind : command-prompt # vi-style controls for copy mode setw -g mode-keys vi tmate-1.8.10/format.c000066400000000000000000000276571242461015400143670ustar00rootroot00000000000000/* $Id$ */ /* * 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 "tmux.h" #include "tmate.h" /* * Build a list of key-value pairs and use them to expand #{key} entries in a * string. */ int format_replace(struct format_tree *, const char *, size_t, char **, size_t *, size_t *); void format_window_pane_tabs(struct format_tree *, struct window_pane *); /* Format key-value replacement entry. */ RB_GENERATE(format_tree, format_entry, entry, format_cmp); /* Format tree comparison function. */ int format_cmp(struct format_entry *fe1, struct format_entry *fe2) { return (strcmp(fe1->key, fe2->key)); } /* Single-character aliases. */ const char *format_aliases[26] = { 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 */ }; /* Create a new tree. */ struct format_tree * format_create(void) { struct format_tree *ft; char host[MAXHOSTNAMELEN]; ft = xmalloc(sizeof *ft); RB_INIT(ft); if (gethostname(host, sizeof host) == 0) format_add(ft, "host", "%s", host); return (ft); } /* Free a tree. */ void format_free(struct format_tree *ft) { struct format_entry *fe, *fe_next; fe_next = RB_MIN(format_tree, ft); while (fe_next != NULL) { fe = fe_next; fe_next = RB_NEXT(format_tree, ft, fe); RB_REMOVE(format_tree, ft, fe); free(fe->value); free(fe->key); free(fe); } free (ft); } /* Add a key-value pair. */ void format_add(struct format_tree *ft, const char *key, const char *fmt, ...) { struct format_entry *fe; va_list ap; fe = xmalloc(sizeof *fe); fe->key = xstrdup(key); va_start(ap, fmt); xvasprintf(&fe->value, fmt, ap); va_end(ap); RB_INSERT(format_tree, ft, fe); } /* Find a format entry. */ const char * format_find(struct format_tree *ft, const char *key) { struct format_entry *fe, fe_find; fe_find.key = (char *) key; fe = RB_FIND(format_tree, ft, &fe_find); if (fe == NULL) return (NULL); return (fe->value); } /* * Replace a key/value pair in buffer. #{blah} is expanded directly, * #{?blah,a,b} is replace with a if blah exists and is nonzero else b. */ int format_replace(struct format_tree *ft, const char *key, size_t keylen, char **buf, size_t *len, size_t *off) { char *copy, *ptr; const char *value; size_t valuelen; /* Make a copy of the key. */ copy = xmalloc(keylen + 1); memcpy(copy, key, keylen); copy[keylen] = '\0'; /* * Is this a conditional? If so, check it exists and extract either the * first or second element. If not, look up the key directly. */ if (*copy == '?') { ptr = strchr(copy, ','); if (ptr == NULL) goto fail; *ptr = '\0'; value = format_find(ft, copy + 1); if (value != NULL && (value[0] != '0' || value[1] != '\0')) { value = ptr + 1; ptr = strchr(value, ','); if (ptr == NULL) goto fail; *ptr = '\0'; } else { ptr = strchr(ptr + 1, ','); if (ptr == NULL) goto fail; value = ptr + 1; } } else { value = format_find(ft, copy); if (value == NULL) value = ""; } valuelen = strlen(value); /* Expand the buffer and copy in the value. */ while (*len - *off < valuelen + 1) { *buf = xrealloc(*buf, 2, *len); *len *= 2; } memcpy(*buf + *off, value, valuelen); *off += valuelen; free(copy); return (0); fail: free(copy); return (-1); } /* Expand keys in a template. */ char * format_expand(struct format_tree *ft, const char *fmt) { char *buf, *ptr; const char *s; size_t off, len, n; int ch; #ifdef TMATE tmate_format(ft); #endif len = 64; buf = xmalloc(len); off = 0; while (*fmt != '\0') { if (*fmt != '#') { while (len - off < 2) { buf = xrealloc(buf, 2, len); len *= 2; } buf[off++] = *fmt++; continue; } fmt++; ch = (u_char) *fmt++; switch (ch) { case '{': ptr = strchr(fmt, '}'); if (ptr == NULL) break; n = ptr - fmt; if (format_replace(ft, fmt, n, &buf, &len, &off) != 0) break; fmt += n + 1; continue; default: if (ch >= 'A' && ch <= 'Z') { s = format_aliases[ch - 'A']; if (s != NULL) { n = strlen(s); if (format_replace ( ft, s, n, &buf, &len, &off) != 0) break; continue; } } while (len - off < 3) { buf = xrealloc(buf, 2, len); len *= 2; } buf[off++] = '#'; buf[off++] = ch; continue; } break; } buf[off] = '\0'; return (buf); } /* Set default format keys for a session. */ void format_session(struct format_tree *ft, struct session *s) { struct session_group *sg; char *tim; time_t t; format_add(ft, "session_name", "%s", s->name); format_add(ft, "session_windows", "%u", winlink_count(&s->windows)); format_add(ft, "session_width", "%u", s->sx); format_add(ft, "session_height", "%u", s->sy); format_add(ft, "session_id", "$%u", s->id); sg = session_group_find(s); format_add(ft, "session_grouped", "%d", sg != NULL); if (sg != NULL) format_add(ft, "session_group", "%u", session_group_index(sg)); t = s->creation_time.tv_sec; format_add(ft, "session_created", "%ld", (long) t); tim = ctime(&t); *strchr(tim, '\n') = '\0'; format_add(ft, "session_created_string", "%s", tim); if (s->flags & SESSION_UNATTACHED) format_add(ft, "session_attached", "%d", 0); else format_add(ft, "session_attached", "%d", 1); } /* Set default format keys for a client. */ void format_client(struct format_tree *ft, struct client *c) { char *tim; time_t t; struct session *s; format_add(ft, "client_cwd", "%s", c->cwd); format_add(ft, "client_height", "%u", c->tty.sy); format_add(ft, "client_width", "%u", c->tty.sx); format_add(ft, "client_tty", "%s", c->tty.path); format_add(ft, "client_termname", "%s", c->tty.termname); t = c->creation_time.tv_sec; format_add(ft, "client_created", "%ld", (long) t); tim = ctime(&t); *strchr(tim, '\n') = '\0'; format_add(ft, "client_created_string", "%s", tim); t = c->activity_time.tv_sec; format_add(ft, "client_activity", "%ld", (long) t); tim = ctime(&t); *strchr(tim, '\n') = '\0'; format_add(ft, "client_activity_string", "%s", tim); format_add(ft, "client_prefix", "%d", !!(c->flags & CLIENT_PREFIX)); if (c->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 winlink. */ void format_winlink(struct format_tree *ft, struct session *s, struct winlink *wl) { struct window *w = wl->window; char *layout, *flags; layout = layout_dump(w); flags = window_printable_flags(s, wl); format_add(ft, "window_id", "@%u", w->id); format_add(ft, "window_index", "%d", wl->idx); 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(ft, "window_flags", "%s", flags); format_add(ft, "window_layout", "%s", layout); format_add(ft, "window_active", "%d", wl == s->curw); format_add(ft, "window_panes", "%u", window_count_panes(w)); free(flags); free(layout); } /* Add window pane tabs. */ void format_window_pane_tabs(struct format_tree *ft, struct window_pane *wp) { struct evbuffer *buffer; u_int i; buffer = evbuffer_new(); 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, "%d", i); } format_add(ft, "pane_tabs", "%.*s", (int) EVBUFFER_LENGTH(buffer), EVBUFFER_DATA(buffer)); evbuffer_free(buffer); } /* Set default format keys for a window pane. */ void format_window_pane(struct format_tree *ft, struct window_pane *wp) { struct grid *gd = wp->base.grid; struct grid_line *gl; unsigned long long size; u_int i, idx; const char *cwd; char *cmd; size = 0; for (i = 0; i < gd->hsize; i++) { gl = &gd->linedata[i]; size += gl->cellsize * sizeof *gl->celldata; } size += gd->hsize * sizeof *gd->linedata; format_add(ft, "history_size", "%u", gd->hsize); format_add(ft, "history_limit", "%u", gd->hlimit); format_add(ft, "history_bytes", "%llu", size); 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 == wp->window->active); format_add(ft, "pane_dead", "%d", wp->fd == -1); format_add(ft, "pane_in_mode", "%d", wp->screen != &wp->base); if (wp->tty != NULL) format_add(ft, "pane_tty", "%s", wp->tty); format_add(ft, "pane_pid", "%ld", (long) wp->pid); if (wp->cmd != NULL) format_add(ft, "pane_start_command", "%s", wp->cmd); if (wp->cwd != NULL) format_add(ft, "pane_start_path", "%s", wp->cwd); if ((cwd = osdep_get_cwd(wp->fd)) != NULL) format_add(ft, "pane_current_path", "%s", cwd); if ((cmd = osdep_get_name(wp->fd, wp->tty)) != NULL) { format_add(ft, "pane_current_command", "%s", cmd); free(cmd); } format_add(ft, "cursor_x", "%d", wp->base.cx); format_add(ft, "cursor_y", "%d", wp->base.cy); format_add(ft, "scroll_region_upper", "%d", wp->base.rupper); format_add(ft, "scroll_region_lower", "%d", wp->base.rlower); format_add(ft, "saved_cursor_x", "%d", wp->ictx.old_cx); format_add(ft, "saved_cursor_y", "%d", wp->ictx.old_cy); format_add(ft, "alternate_on", "%d", wp->saved_grid ? 1 : 0); format_add(ft, "alternate_saved_x", "%d", wp->saved_cx); format_add(ft, "alternate_saved_y", "%d", 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, "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_any_flag", "%d", !!(wp->base.mode & MODE_MOUSE_ANY)); format_add(ft, "mouse_utf8_flag", "%d", !!(wp->base.mode & MODE_MOUSE_UTF8)); format_window_pane_tabs(ft, wp); } /* Set default format keys for paste buffer. */ void format_paste_buffer(struct format_tree *ft, struct paste_buffer *pb) { char *pb_print = paste_print(pb, 50); format_add(ft, "buffer_size", "%zu", pb->size); format_add(ft, "buffer_sample", "%s", pb_print); free(pb_print); } tmate-1.8.10/grid-cell.c000066400000000000000000000027531242461015400147270ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2012 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION 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" /* Get cell width. */ u_int grid_cell_width(const struct grid_cell *gc) { return (gc->xstate >> 4); } /* Get cell data. */ void grid_cell_get(const struct grid_cell *gc, struct utf8_data *ud) { ud->size = gc->xstate & 0xf; ud->width = gc->xstate >> 4; memcpy(ud->data, gc->xdata, ud->size); } /* Set cell data. */ void grid_cell_set(struct grid_cell *gc, const struct utf8_data *ud) { memcpy(gc->xdata, ud->data, ud->size); gc->xstate = (ud->width << 4) | ud->size; } /* Set a single character as cell data. */ void grid_cell_one(struct grid_cell *gc, u_char ch) { *gc->xdata = ch; gc->xstate = (1 << 4) | 1; } tmate-1.8.10/grid-view.c000066400000000000000000000126771242461015400147700ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Grid view functions. These work using coordinates relative to the visible * screen area. */ #define grid_view_x(gd, x) (x) #define grid_view_y(gd, y) ((gd)->hsize + (y)) /* Get cell for reading. */ const struct grid_cell * grid_view_peek_cell(struct grid *gd, u_int px, u_int py) { return (grid_peek_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py))); } /* Get cell for writing. */ struct grid_cell * grid_view_get_cell(struct grid *gd, u_int px, u_int py) { return (grid_get_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py))); } /* Set cell. */ void grid_view_set_cell( struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) { grid_set_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py), gc); } /* Clear into history. */ void grid_view_clear_history(struct grid *gd) { struct grid_line *gl; u_int yy, last; GRID_DEBUG(gd, ""); /* Find the last used line. */ last = 0; for (yy = 0; yy < gd->sy; yy++) { gl = &gd->linedata[grid_view_y(gd, yy)]; if (gl->cellsize != 0) last = yy + 1; } if (last == 0) return; /* Scroll the lines into the history. */ for (yy = 0; yy < last; yy++) { grid_collect_history(gd); grid_scroll_history(gd); } } /* Clear area. */ void grid_view_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny) { GRID_DEBUG(gd, "px=%u, py=%u, nx=%u, ny=%u", px, py, nx, ny); px = grid_view_x(gd, px); py = grid_view_y(gd, py); grid_clear(gd, px, py, nx, ny); } /* Scroll region up. */ void grid_view_scroll_region_up(struct grid *gd, u_int rupper, u_int rlower) { GRID_DEBUG(gd, "rupper=%u, rlower=%u", rupper, rlower); if (gd->flags & GRID_HISTORY) { grid_collect_history(gd); if (rupper == 0 && rlower == gd->sy - 1) grid_scroll_history(gd); else { rupper = grid_view_y(gd, rupper); rlower = grid_view_y(gd, rlower); grid_scroll_history_region(gd, rupper, rlower); } } else { rupper = grid_view_y(gd, rupper); rlower = grid_view_y(gd, rlower); grid_move_lines(gd, rupper, rupper + 1, rlower - rupper); } } /* Scroll region down. */ void grid_view_scroll_region_down(struct grid *gd, u_int rupper, u_int rlower) { GRID_DEBUG(gd, "rupper=%u, rlower=%u", rupper, rlower); rupper = grid_view_y(gd, rupper); rlower = grid_view_y(gd, rlower); grid_move_lines(gd, rupper + 1, rupper, rlower - rupper); } /* Insert lines. */ void grid_view_insert_lines(struct grid *gd, u_int py, u_int ny) { u_int sy; GRID_DEBUG(gd, "py=%u, ny=%u", py, ny); py = grid_view_y(gd, py); sy = grid_view_y(gd, gd->sy); grid_move_lines(gd, py + ny, py, sy - py - ny); } /* Insert lines in region. */ void grid_view_insert_lines_region(struct grid *gd, u_int rlower, u_int py, u_int ny) { u_int ny2; GRID_DEBUG(gd, "rlower=%u, py=%u, ny=%u", rlower, py, ny); rlower = grid_view_y(gd, rlower); py = grid_view_y(gd, py); ny2 = rlower + 1 - py - ny; grid_move_lines(gd, rlower + 1 - ny2, py, ny2); grid_clear(gd, 0, py + ny2, gd->sx, ny - ny2); } /* Delete lines. */ void grid_view_delete_lines(struct grid *gd, u_int py, u_int ny) { u_int sy; GRID_DEBUG(gd, "py=%u, ny=%u", py, ny); py = grid_view_y(gd, py); sy = grid_view_y(gd, gd->sy); grid_move_lines(gd, py, py + ny, sy - py - ny); grid_clear(gd, 0, sy - ny, gd->sx, py + ny - (sy - ny)); } /* Delete lines inside scroll region. */ void grid_view_delete_lines_region(struct grid *gd, u_int rlower, u_int py, u_int ny) { u_int ny2; GRID_DEBUG(gd, "rlower=%u, py=%u, ny=%u", rlower, py, ny); rlower = grid_view_y(gd, rlower); py = grid_view_y(gd, py); ny2 = rlower + 1 - py - ny; grid_move_lines(gd, py, py + ny, ny2); grid_clear(gd, 0, py + ny2, gd->sx, ny - ny2); } /* Insert characters. */ void grid_view_insert_cells(struct grid *gd, u_int px, u_int py, u_int nx) { u_int sx; GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); px = grid_view_x(gd, px); py = grid_view_y(gd, py); sx = grid_view_x(gd, gd->sx); if (px == sx - 1) grid_clear(gd, px, py, 1, 1); else grid_move_cells(gd, px + nx, px, py, sx - px - nx); } /* Delete characters. */ void grid_view_delete_cells(struct grid *gd, u_int px, u_int py, u_int nx) { u_int sx; GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); px = grid_view_x(gd, px); py = grid_view_y(gd, py); sx = grid_view_x(gd, gd->sx); grid_move_cells(gd, px, px + nx, py, sx - px - nx); grid_clear(gd, sx - nx, py, px + nx - (sx - nx), 1); } /* Convert cells into a string. */ char * grid_view_string_cells(struct grid *gd, u_int px, u_int py, u_int nx) { GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); px = grid_view_x(gd, px); py = grid_view_y(gd, py); return (grid_string_cells(gd, px, py, nx, NULL, 0, 0, 0)); } tmate-1.8.10/grid.c000066400000000000000000000444371242461015400140170ustar00rootroot00000000000000/* $Id$ */ /* * 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, 0, 8, 8, (1 << 4) | 1, " " }; const struct grid_cell grid_marker_cell = { 0, 0, 8, 8, (1 << 4) | 1, "_" }; #define grid_put_cell(gd, px, py, gc) do { \ memcpy(&gd->linedata[py].celldata[px], \ gc, sizeof gd->linedata[py].celldata[px]); \ } while (0) #define grid_put_utf8(gd, px, py, gc) do { \ memcpy(&gd->linedata[py].utf8data[px], \ gc, sizeof gd->linedata[py].utf8data[px]); \ } while (0) int grid_check_y(struct grid *, u_int); #ifdef DEBUG int grid_check_y(struct grid *gd, u_int py) { if ((py) >= (gd)->hsize + (gd)->sy) log_fatalx("y out of range: %u", py); return (0); } #else int grid_check_y(struct grid *gd, u_int py) { if ((py) >= (gd)->hsize + (gd)->sy) { log_debug("y out of range: %u", py); return (-1); } return (0); } #endif void grid_reflow_join(struct grid *, u_int *, struct grid_line *, u_int); void grid_reflow_split(struct grid *, u_int *, struct grid_line *, u_int, u_int); void grid_reflow_move(struct grid *, u_int *, struct grid_line *); size_t grid_string_cells_fg(const struct grid_cell *, int *); size_t grid_string_cells_bg(const struct grid_cell *, int *); void grid_string_cells_code(const struct grid_cell *, const struct grid_cell *, char *, size_t, int); /* Create a new grid. */ struct grid * grid_create(u_int sx, u_int sy, u_int hlimit) { struct grid *gd; gd = xmalloc(sizeof *gd); gd->sx = sx; gd->sy = sy; gd->flags = GRID_HISTORY; gd->hsize = 0; gd->hlimit = hlimit; gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata); return (gd); } /* Destroy grid. */ void grid_destroy(struct grid *gd) { struct grid_line *gl; u_int yy; for (yy = 0; yy < gd->hsize + gd->sy; yy++) { gl = &gd->linedata[yy]; free(gl->celldata); } 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 != ga->sy) return (1); for (yy = 0; yy < ga->sy; yy++) { gla = &ga->linedata[yy]; glb = &gb->linedata[yy]; if (gla->cellsize != glb->cellsize) return (1); for (xx = 0; xx < ga->sx; xx++) { gca = &gla->celldata[xx]; gcb = &glb->celldata[xx]; if (memcmp(gca, gcb, sizeof (struct grid_cell)) != 0) return (1); } } return (0); } /* * Collect lines from the history if at the limit. Free the top (oldest) 10% * and shift up. */ void grid_collect_history(struct grid *gd) { u_int yy; GRID_DEBUG(gd, ""); if (gd->hsize < gd->hlimit) return; yy = gd->hlimit / 10; if (yy < 1) yy = 1; grid_move_lines(gd, 0, yy, gd->hsize + gd->sy - yy); gd->hsize -= yy; } /* * Scroll the entire visible screen, moving one line into the history. Just * allocate a new line at the bottom and move the history size indicator. */ void grid_scroll_history(struct grid *gd) { u_int yy; GRID_DEBUG(gd, ""); yy = gd->hsize + gd->sy; gd->linedata = xrealloc(gd->linedata, yy + 1, sizeof *gd->linedata); memset(&gd->linedata[yy], 0, sizeof gd->linedata[yy]); gd->hsize++; } /* Scroll a region up, moving the top line into the history. */ void grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower) { struct grid_line *gl_history, *gl_upper, *gl_lower; u_int yy; GRID_DEBUG(gd, "upper=%u, lower=%u", upper, lower); /* Create a space for a new line. */ yy = gd->hsize + gd->sy; gd->linedata = xrealloc(gd->linedata, yy + 1, sizeof *gd->linedata); /* Move the entire screen down to free a space for this line. */ gl_history = &gd->linedata[gd->hsize]; memmove(gl_history + 1, gl_history, gd->sy * sizeof *gl_history); /* Adjust the region and find its start and end. */ upper++; gl_upper = &gd->linedata[upper]; lower++; gl_lower = &gd->linedata[lower]; /* Move the line into the history. */ memcpy(gl_history, gl_upper, sizeof *gl_history); /* Then move the region up and clear the bottom line. */ memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper); memset(gl_lower, 0, sizeof *gl_lower); /* Move the history offset down over the line. */ gd->hsize++; } /* Expand line to fit to cell. */ void grid_expand_line(struct grid *gd, u_int py, u_int sx) { struct grid_line *gl; u_int xx; gl = &gd->linedata[py]; if (sx <= gl->cellsize) return; gl->celldata = xrealloc(gl->celldata, sx, sizeof *gl->celldata); for (xx = gl->cellsize; xx < sx; xx++) grid_put_cell(gd, xx, py, &grid_default_cell); gl->cellsize = sx; } /* Peek at grid line. */ const struct grid_line * grid_peek_line(struct grid *gd, u_int py) { if (grid_check_y(gd, py) != 0) return (NULL); return (&gd->linedata[py]); } /* Get cell for reading. */ const struct grid_cell * grid_peek_cell(struct grid *gd, u_int px, u_int py) { if (grid_check_y(gd, py) != 0) return (&grid_default_cell); if (px >= gd->linedata[py].cellsize) return (&grid_default_cell); return (&gd->linedata[py].celldata[px]); } /* Get cell at relative position (for writing). */ struct grid_cell * grid_get_cell(struct grid *gd, u_int px, u_int py) { if (grid_check_y(gd, py) != 0) return (NULL); grid_expand_line(gd, py, px + 1); return (&gd->linedata[py].celldata[px]); } /* Set cell at relative position. */ void grid_set_cell( struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) { if (grid_check_y(gd, py) != 0) return; grid_expand_line(gd, py, px + 1); grid_put_cell(gd, px, py, gc); } /* Clear area. */ void grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny) { u_int xx, yy; GRID_DEBUG(gd, "px=%u, py=%u, nx=%u, ny=%u", px, py, nx, ny); if (nx == 0 || ny == 0) return; if (px == 0 && nx == gd->sx) { grid_clear_lines(gd, py, ny); return; } if (grid_check_y(gd, py) != 0) return; if (grid_check_y(gd, py + ny - 1) != 0) return; for (yy = py; yy < py + ny; yy++) { if (px >= gd->linedata[yy].cellsize) continue; if (px + nx >= gd->linedata[yy].cellsize) { gd->linedata[yy].cellsize = px; continue; } for (xx = px; xx < px + nx; xx++) { if (xx >= gd->linedata[yy].cellsize) break; grid_put_cell(gd, xx, yy, &grid_default_cell); } } } /* Clear lines. This just frees and truncates the lines. */ void grid_clear_lines(struct grid *gd, u_int py, u_int ny) { struct grid_line *gl; u_int yy; GRID_DEBUG(gd, "py=%u, ny=%u", py, ny); if (ny == 0) return; if (grid_check_y(gd, py) != 0) return; if (grid_check_y(gd, py + ny - 1) != 0) return; for (yy = py; yy < py + ny; yy++) { gl = &gd->linedata[yy]; free(gl->celldata); memset(gl, 0, sizeof *gl); } } /* Move a group of lines. */ void grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny) { u_int yy; GRID_DEBUG(gd, "dy=%u, py=%u, ny=%u", dy, py, ny); if (ny == 0 || py == dy) return; if (grid_check_y(gd, py) != 0) return; if (grid_check_y(gd, py + ny - 1) != 0) return; if (grid_check_y(gd, dy) != 0) return; if (grid_check_y(gd, dy + ny - 1) != 0) return; /* Free any lines which are being replaced. */ for (yy = dy; yy < dy + ny; yy++) { if (yy >= py && yy < py + ny) continue; grid_clear_lines(gd, yy, 1); } memmove( &gd->linedata[dy], &gd->linedata[py], ny * (sizeof *gd->linedata)); /* Wipe any lines that have been moved (without freeing them). */ for (yy = py; yy < py + ny; yy++) { if (yy >= dy && yy < dy + ny) continue; memset(&gd->linedata[yy], 0, sizeof gd->linedata[yy]); } } /* Move a group of cells. */ void grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx) { struct grid_line *gl; u_int xx; GRID_DEBUG(gd, "dx=%u, px=%u, py=%u, nx=%u", dx, px, py, nx); if (nx == 0 || px == dx) return; if (grid_check_y(gd, py) != 0) return; gl = &gd->linedata[py]; grid_expand_line(gd, py, px + nx); grid_expand_line(gd, py, dx + nx); memmove( &gl->celldata[dx], &gl->celldata[px], nx * sizeof *gl->celldata); /* Wipe any cells that have been moved. */ for (xx = px; xx < px + nx; xx++) { if (xx >= dx && xx < dx + nx) continue; grid_put_cell(gd, xx, py, &grid_default_cell); } } /* Get ANSI foreground sequence. */ size_t grid_string_cells_fg(const struct grid_cell *gc, int *values) { size_t n; n = 0; if (gc->flags & GRID_FLAG_FG256) { values[n++] = 38; values[n++] = 5; values[n++] = gc->fg; } 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. */ size_t grid_string_cells_bg(const struct grid_cell *gc, int *values) { size_t n; n = 0; if (gc->flags & GRID_FLAG_BG256) { values[n++] = 48; values[n++] = 5; values[n++] = gc->bg; } 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. The output buffer must be able to hold at least 57 * bytes. */ 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[16], newc[16], s[32]; size_t noldc, nnewc, n, i; u_int attr = gc->attr; u_int 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 } }; 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; } /* If the foreground c changed, append 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) { for (i = 0; i < nnewc; i++) s[n++] = newc[i]; } /* If the background c 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) { for (i = 0; i < nnewc; i++) s[n++] = newc[i]; } /* If there are any parameters, append an SGR code. */ *buf = '\0'; if (n > 0) { if (escape_c0) strlcat(buf, "\\033[", len); else strlcat(buf, "\033[", len); for (i = 0; i < n; i++) { if (i + 1 < n) xsnprintf(tmp, sizeof tmp, "%d;", s[i]); else xsnprintf(tmp, sizeof tmp, "%d", s[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) { const struct grid_cell *gc; static struct grid_cell lastgc1; struct utf8_data ud; const char* data; char *buf, code[128]; size_t len, off, size, codelen; u_int xx; GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); if (lastgc != NULL && *lastgc == NULL) { memcpy(&lastgc1, &grid_default_cell, sizeof lastgc1); *lastgc = &lastgc1; } len = 128; buf = xmalloc(len); off = 0; for (xx = px; xx < px + nx; xx++) { gc = grid_peek_cell(gd, xx, py); if (gc->flags & GRID_FLAG_PADDING) continue; grid_cell_get(gc, &ud); if (with_codes) { grid_string_cells_code(*lastgc, gc, code, sizeof code, escape_c0); codelen = strlen(code); memcpy(*lastgc, gc, sizeof *gc); } else codelen = 0; data = ud.data; size = ud.size; if (escape_c0 && size == 1 && *data == '\\') { data = "\\\\"; size = 2; } while (len < off + size + codelen + 1) { buf = xrealloc(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. If there aren't enough lines in * either source or destination, the number of lines is limited to the number * available. */ void grid_duplicate_lines( struct grid *dst, u_int dy, struct grid *src, u_int sy, u_int ny) { struct grid_line *dstl, *srcl; u_int yy; GRID_DEBUG(src, "dy=%u, sy=%u, ny=%u", dy, sy, ny); if (dy + ny > dst->hsize + dst->sy) ny = dst->hsize + dst->sy - dy; if (sy + ny > src->hsize + src->sy) ny = src->hsize + src->sy - sy; grid_clear_lines(dst, dy, ny); for (yy = 0; yy < ny; yy++) { srcl = &src->linedata[sy]; dstl = &dst->linedata[dy]; memcpy(dstl, srcl, sizeof *dstl); if (srcl->cellsize != 0) { dstl->celldata = xcalloc( srcl->cellsize, sizeof *dstl->celldata); memcpy(dstl->celldata, srcl->celldata, srcl->cellsize * sizeof *dstl->celldata); } sy++; dy++; } } /* Join line data. */ void grid_reflow_join(struct grid *dst, u_int *py, struct grid_line *src_gl, u_int new_x) { struct grid_line *dst_gl = &dst->linedata[(*py) - 1]; u_int left, to_copy, ox, nx; /* How much is left on the old line? */ left = new_x - dst_gl->cellsize; /* Work out how much to append. */ to_copy = src_gl->cellsize; if (to_copy > left) to_copy = left; ox = dst_gl->cellsize; nx = ox + to_copy; /* Resize the destination line. */ dst_gl->celldata = xrealloc(dst_gl->celldata, nx, sizeof *dst_gl->celldata); dst_gl->cellsize = nx; /* Append as much as possible. */ memcpy(&dst_gl->celldata[ox], &src_gl->celldata[0], to_copy * sizeof src_gl->celldata[0]); /* If there is any left in the source, split it. */ if (src_gl->cellsize > to_copy) { dst_gl->flags |= GRID_LINE_WRAPPED; src_gl->cellsize -= to_copy; grid_reflow_split(dst, py, src_gl, new_x, to_copy); } } /* Split line data. */ void grid_reflow_split(struct grid *dst, u_int *py, struct grid_line *src_gl, u_int new_x, u_int offset) { struct grid_line *dst_gl = NULL; u_int to_copy; /* Loop and copy sections of the source line. */ while (src_gl->cellsize > 0) { /* Create new line. */ if (*py >= dst->hsize + dst->sy) grid_scroll_history(dst); dst_gl = &dst->linedata[*py]; (*py)++; /* How much should we copy? */ to_copy = new_x; if (to_copy > src_gl->cellsize) to_copy = src_gl->cellsize; /* Expand destination line. */ dst_gl->celldata = xmalloc(to_copy * sizeof *dst_gl->celldata); dst_gl->cellsize = to_copy; dst_gl->flags |= GRID_LINE_WRAPPED; /* Copy the data. */ memcpy (&dst_gl->celldata[0], &src_gl->celldata[offset], to_copy * sizeof dst_gl->celldata[0]); /* Move offset and reduce old line size. */ offset += to_copy; src_gl->cellsize -= to_copy; } /* Last line is not wrapped. */ if (dst_gl != NULL) dst_gl->flags &= ~GRID_LINE_WRAPPED; } /* Move line data. */ void grid_reflow_move(struct grid *dst, u_int *py, struct grid_line *src_gl) { struct grid_line *dst_gl; /* Create new line. */ if (*py >= dst->hsize + dst->sy) grid_scroll_history(dst); dst_gl = &dst->linedata[*py]; (*py)++; /* Copy the old line. */ memcpy(dst_gl, src_gl, sizeof *dst_gl); dst_gl->flags &= ~GRID_LINE_WRAPPED; /* Clear old line. */ src_gl->celldata = NULL; } /* * Reflow lines from src grid into dst grid of width new_x. Returns number of * lines fewer in the visible area. The source grid is destroyed. */ u_int grid_reflow(struct grid *dst, struct grid *src, u_int new_x) { u_int py, sy, line; int previous_wrapped; struct grid_line *src_gl; py = 0; sy = src->sy; previous_wrapped = 0; for (line = 0; line < sy + src->hsize; line++) { src_gl = src->linedata + line; if (!previous_wrapped) { /* Wasn't wrapped. If smaller, move to destination. */ if (src_gl->cellsize <= new_x) grid_reflow_move(dst, &py, src_gl); else grid_reflow_split(dst, &py, src_gl, new_x, 0); } else { /* Previous was wrapped. Try to join. */ grid_reflow_join(dst, &py, src_gl, new_x); } previous_wrapped = src_gl->flags & GRID_LINE_WRAPPED; } grid_destroy(src); if (py > sy) return (0); return (sy - py); } tmate-1.8.10/input-keys.c000066400000000000000000000172051242461015400151730ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * This file is rather misleadingly named, it contains the code which takes a * key code and translates it into something suitable to be sent to the * application running in a pane (similar to input.c does in the other * direction with output). */ struct input_key_ent { int key; const char *data; int flags; #define INPUTKEY_KEYPAD 0x1 /* keypad key */ #define INPUTKEY_CURSOR 0x2 /* cursor key */ }; const struct input_key_ent input_keys[] = { /* Backspace key. */ { KEYC_BSPACE, "\177", 0 }, /* Function keys. */ { KEYC_F1, "\033OP", 0 }, { KEYC_F2, "\033OQ", 0 }, { KEYC_F3, "\033OR", 0 }, { KEYC_F4, "\033OS", 0 }, { KEYC_F5, "\033[15~", 0 }, { KEYC_F6, "\033[17~", 0 }, { KEYC_F7, "\033[18~", 0 }, { KEYC_F8, "\033[19~", 0 }, { KEYC_F9, "\033[20~", 0 }, { KEYC_F10, "\033[21~", 0 }, { KEYC_F11, "\033[23~", 0 }, { KEYC_F12, "\033[24~", 0 }, { KEYC_F13, "\033[25~", 0 }, { KEYC_F14, "\033[26~", 0 }, { KEYC_F15, "\033[28~", 0 }, { KEYC_F16, "\033[29~", 0 }, { KEYC_F17, "\033[31~", 0 }, { KEYC_F18, "\033[32~", 0 }, { KEYC_F19, "\033[33~", 0 }, { KEYC_F20, "\033[34~", 0 }, { KEYC_IC, "\033[2~", 0 }, { KEYC_DC, "\033[3~", 0 }, { KEYC_HOME, "\033[1~", 0 }, { KEYC_END, "\033[4~", 0 }, { KEYC_NPAGE, "\033[6~", 0 }, { KEYC_PPAGE, "\033[5~", 0 }, { KEYC_BTAB, "\033[Z", 0 }, /* * Arrow keys. Cursor versions must come first. The codes are toggled * between CSI and SS3 versions when ctrl is pressed. */ { KEYC_UP|KEYC_CTRL, "\033[A", INPUTKEY_CURSOR }, { KEYC_DOWN|KEYC_CTRL, "\033[B", INPUTKEY_CURSOR }, { KEYC_RIGHT|KEYC_CTRL, "\033[C", INPUTKEY_CURSOR }, { KEYC_LEFT|KEYC_CTRL, "\033[D", INPUTKEY_CURSOR }, { KEYC_UP, "\033OA", INPUTKEY_CURSOR }, { KEYC_DOWN, "\033OB", INPUTKEY_CURSOR }, { KEYC_RIGHT, "\033OC", INPUTKEY_CURSOR }, { KEYC_LEFT, "\033OD", INPUTKEY_CURSOR }, { KEYC_UP|KEYC_CTRL, "\033OA", 0 }, { KEYC_DOWN|KEYC_CTRL, "\033OB", 0 }, { KEYC_RIGHT|KEYC_CTRL, "\033OC", 0 }, { KEYC_LEFT|KEYC_CTRL, "\033OD", 0 }, { KEYC_UP, "\033[A", 0 }, { KEYC_DOWN, "\033[B", 0 }, { KEYC_RIGHT, "\033[C", 0 }, { KEYC_LEFT, "\033[D", 0 }, /* Keypad keys. Keypad versions must come first. */ { KEYC_KP_SLASH, "\033Oo", INPUTKEY_KEYPAD }, { KEYC_KP_STAR, "\033Oj", INPUTKEY_KEYPAD }, { KEYC_KP_MINUS, "\033Om", INPUTKEY_KEYPAD }, { KEYC_KP_SEVEN, "\033Ow", INPUTKEY_KEYPAD }, { KEYC_KP_EIGHT, "\033Ox", INPUTKEY_KEYPAD }, { KEYC_KP_NINE, "\033Oy", INPUTKEY_KEYPAD }, { KEYC_KP_PLUS, "\033Ok", INPUTKEY_KEYPAD }, { KEYC_KP_FOUR, "\033Ot", INPUTKEY_KEYPAD }, { KEYC_KP_FIVE, "\033Ou", INPUTKEY_KEYPAD }, { KEYC_KP_SIX, "\033Ov", INPUTKEY_KEYPAD }, { KEYC_KP_ONE, "\033Oq", INPUTKEY_KEYPAD }, { KEYC_KP_TWO, "\033Or", INPUTKEY_KEYPAD }, { KEYC_KP_THREE, "\033Os", INPUTKEY_KEYPAD }, { KEYC_KP_ENTER, "\033OM", INPUTKEY_KEYPAD }, { KEYC_KP_ZERO, "\033Op", INPUTKEY_KEYPAD }, { KEYC_KP_PERIOD, "\033On", INPUTKEY_KEYPAD }, { KEYC_KP_SLASH, "/", 0 }, { KEYC_KP_STAR, "*", 0 }, { KEYC_KP_MINUS, "-", 0 }, { KEYC_KP_SEVEN, "7", 0 }, { KEYC_KP_EIGHT, "8", 0 }, { KEYC_KP_NINE, "9", 0 }, { KEYC_KP_PLUS, "+", 0 }, { KEYC_KP_FOUR, "4", 0 }, { KEYC_KP_FIVE, "5", 0 }, { KEYC_KP_SIX, "6", 0 }, { KEYC_KP_ONE, "1", 0 }, { KEYC_KP_TWO, "2", 0 }, { KEYC_KP_THREE, "3", 0 }, { KEYC_KP_ENTER, "\n", 0 }, { KEYC_KP_ZERO, "0", 0 }, { KEYC_KP_PERIOD, ".", 0 }, }; /* Translate a key code into an output key sequence. */ void input_key(struct window_pane *wp, int key) { const struct input_key_ent *ike; u_int i; size_t dlen; char *out; u_char ch; log_debug2("writing key 0x%x", key); /* * If this is a normal 7-bit key, just send it, with a leading escape * if necessary. */ if (key != KEYC_NONE && (key & ~KEYC_ESCAPE) < 0x100) { if (key & KEYC_ESCAPE) bufferevent_write(wp->event, "\033", 1); ch = key & ~KEYC_ESCAPE; bufferevent_write(wp->event, &ch, 1); return; } /* * Then try to look this up as an xterm key, if the flag to output them * is set. */ if (options_get_number(&wp->window->options, "xterm-keys")) { if ((out = xterm_keys_lookup(key)) != NULL) { bufferevent_write(wp->event, out, strlen(out)); free(out); return; } } /* Otherwise look the key up in the table. */ for (i = 0; i < nitems(input_keys); i++) { ike = &input_keys[i]; if ((ike->flags & INPUTKEY_KEYPAD) && !(wp->screen->mode & MODE_KKEYPAD)) continue; if ((ike->flags & INPUTKEY_CURSOR) && !(wp->screen->mode & MODE_KCURSOR)) continue; if ((key & KEYC_ESCAPE) && (ike->key | KEYC_ESCAPE) == key) break; if (ike->key == key) break; } if (i == nitems(input_keys)) { log_debug2("key 0x%x missing", key); return; } dlen = strlen(ike->data); log_debug2("found key 0x%x: \"%s\"", key, ike->data); /* Prefix a \033 for escape. */ if (key & KEYC_ESCAPE) bufferevent_write(wp->event, "\033", 1); bufferevent_write(wp->event, ike->data, dlen); } /* Translate mouse and output. */ void input_mouse(struct window_pane *wp, struct session *s, struct mouse_event *m) { char buf[40]; size_t len; struct paste_buffer *pb; if (wp->screen->mode & ALL_MOUSE_MODES) { /* * 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 && (wp->screen->mode & MODE_MOUSE_SGR)) { len = xsnprintf(buf, sizeof buf, "\033[<%d;%d;%d%c", m->sgr_xb, m->x + 1, m->y + 1, m->sgr_rel ? 'm' : 'M'); } else if (wp->screen->mode & MODE_MOUSE_UTF8) { len = xsnprintf(buf, sizeof buf, "\033[M"); len += utf8_split2(m->xb + 32, &buf[len]); len += utf8_split2(m->x + 33, &buf[len]); len += utf8_split2(m->y + 33, &buf[len]); } else { if (m->xb > 223 || m->x >= 222 || m->y > 222) return; len = xsnprintf(buf, sizeof buf, "\033[M"); buf[len++] = m->xb + 32; buf[len++] = m->x + 33; buf[len++] = m->y + 33; } bufferevent_write(wp->event, buf, len); return; } if (m->button == 1 && (m->event & MOUSE_EVENT_CLICK) && options_get_number(&wp->window->options, "mode-mouse") == 1) { pb = paste_get_top(&global_buffers); if (pb != NULL) { paste_send_pane(pb, wp, "\r", wp->screen->mode & MODE_BRACKETPASTE); } } else if ((m->xb & 3) != 1 && options_get_number(&wp->window->options, "mode-mouse") == 1) { if (window_pane_set_mode(wp, &window_copy_mode) == 0) { window_copy_init_from_pane(wp); if (wp->mode->mouse != NULL) wp->mode->mouse(wp, s, m); } } } tmate-1.8.10/input.c000066400000000000000000001210311242461015400142130ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Based on the description by Paul Williams at: * * http://vt100.net/emu/dec_ansi_parser * * With the following changes: * * - 7-bit only. * * - Support for UTF-8. * * - OSC (but not APC) may be terminated by \007 as well as ST. * * - A state for APC similar to OSC. Some terminals appear to use this to set * the title. * * - A state for the screen \033k...\033\\ sequence to rename a window. This is * pretty stupid but not supporting it is more trouble than it is worth. * * - Special handling for ESC inside a DCS to allow arbitrary byte sequences to * be passed to the underlying teminal(s). */ /* Helper functions. */ struct input_transition; int input_split(struct input_ctx *); int input_get(struct input_ctx *, u_int, int, int); void input_reply(struct input_ctx *, const char *, ...); void input_set_state(struct window_pane *, const struct input_transition *); /* Transition entry/exit handlers. */ void input_clear(struct input_ctx *); void input_enter_osc(struct input_ctx *); void input_exit_osc(struct input_ctx *); void input_enter_apc(struct input_ctx *); void input_exit_apc(struct input_ctx *); void input_enter_rename(struct input_ctx *); void input_exit_rename(struct input_ctx *); /* Input state handlers. */ int input_print(struct input_ctx *); int input_intermediate(struct input_ctx *); int input_parameter(struct input_ctx *); int input_input(struct input_ctx *); int input_c0_dispatch(struct input_ctx *); int input_esc_dispatch(struct input_ctx *); int input_csi_dispatch(struct input_ctx *); void input_csi_dispatch_sgr(struct input_ctx *); int input_dcs_dispatch(struct input_ctx *); int input_utf8_open(struct input_ctx *); int input_utf8_add(struct input_ctx *); int input_utf8_close(struct input_ctx *); /* Command table comparison function. */ int input_table_compare(const void *, const void *); /* Command table entry. */ struct input_table_entry { int ch; const char *interm; int type; }; /* Escape commands. */ enum input_esc_type { INPUT_ESC_DECALN, INPUT_ESC_DECKPAM, INPUT_ESC_DECKPNM, INPUT_ESC_DECRC, INPUT_ESC_DECSC, INPUT_ESC_HTS, INPUT_ESC_IND, INPUT_ESC_NEL, INPUT_ESC_RI, INPUT_ESC_RIS, INPUT_ESC_SCSOFF_G0, INPUT_ESC_SCSON_G0, }; /* Escape command table. */ const struct input_table_entry input_esc_table[] = { { '0', "(", INPUT_ESC_SCSOFF_G0 }, { '7', "", INPUT_ESC_DECSC }, { '8', "", INPUT_ESC_DECRC }, { '8', "#", INPUT_ESC_DECALN }, { '=', "", INPUT_ESC_DECKPAM }, { '>', "", INPUT_ESC_DECKPNM }, { 'B', "(", INPUT_ESC_SCSON_G0 }, { 'D', "", INPUT_ESC_IND }, { 'E', "", INPUT_ESC_NEL }, { 'H', "", INPUT_ESC_HTS }, { 'M', "", INPUT_ESC_RI }, { 'c', "", INPUT_ESC_RIS }, }; /* Control (CSI) commands. */ enum input_csi_type { INPUT_CSI_CBT, INPUT_CSI_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_RM, INPUT_CSI_RM_PRIVATE, INPUT_CSI_SCP, INPUT_CSI_SGR, INPUT_CSI_SM, INPUT_CSI_SM_PRIVATE, INPUT_CSI_TBC, INPUT_CSI_VPA, }; /* Control (CSI) command table. */ const struct input_table_entry input_csi_table[] = { { '@', "", INPUT_CSI_ICH }, { 'A', "", INPUT_CSI_CUU }, { 'B', "", INPUT_CSI_CUD }, { 'C', "", INPUT_CSI_CUF }, { 'D', "", INPUT_CSI_CUB }, { '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 }, { 'X', "", INPUT_CSI_ECH }, { 'Z', "", INPUT_CSI_CBT }, { '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 }, { '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. */ const struct input_transition input_state_ground_table[]; const struct input_transition input_state_esc_enter_table[]; const struct input_transition input_state_esc_intermediate_table[]; const struct input_transition input_state_csi_enter_table[]; const struct input_transition input_state_csi_parameter_table[]; const struct input_transition input_state_csi_intermediate_table[]; const struct input_transition input_state_csi_ignore_table[]; const struct input_transition input_state_dcs_enter_table[]; const struct input_transition input_state_dcs_parameter_table[]; const struct input_transition input_state_dcs_intermediate_table[]; const struct input_transition input_state_dcs_handler_table[]; const struct input_transition input_state_dcs_escape_table[]; const struct input_transition input_state_dcs_ignore_table[]; const struct input_transition input_state_osc_string_table[]; const struct input_transition input_state_apc_string_table[]; const struct input_transition input_state_rename_string_table[]; const struct input_transition input_state_consume_st_table[]; const struct input_transition input_state_utf8_three_table[]; const struct input_transition input_state_utf8_two_table[]; const struct input_transition input_state_utf8_one_table[]; /* ground state definition. */ const struct input_state input_state_ground = { "ground", NULL, NULL, input_state_ground_table }; /* esc_enter state definition. */ const struct input_state input_state_esc_enter = { "esc_enter", input_clear, NULL, input_state_esc_enter_table }; /* esc_intermediate state definition. */ const struct input_state input_state_esc_intermediate = { "esc_intermediate", NULL, NULL, input_state_esc_intermediate_table }; /* csi_enter state definition. */ const struct input_state input_state_csi_enter = { "csi_enter", input_clear, NULL, input_state_csi_enter_table }; /* csi_parameter state definition. */ const struct input_state input_state_csi_parameter = { "csi_parameter", NULL, NULL, input_state_csi_parameter_table }; /* csi_intermediate state definition. */ const struct input_state input_state_csi_intermediate = { "csi_intermediate", NULL, NULL, input_state_csi_intermediate_table }; /* csi_ignore state definition. */ const struct input_state input_state_csi_ignore = { "csi_ignore", NULL, NULL, input_state_csi_ignore_table }; /* dcs_enter state definition. */ const struct input_state input_state_dcs_enter = { "dcs_enter", input_clear, NULL, input_state_dcs_enter_table }; /* dcs_parameter state definition. */ const struct input_state input_state_dcs_parameter = { "dcs_parameter", NULL, NULL, input_state_dcs_parameter_table }; /* dcs_intermediate state definition. */ const struct input_state input_state_dcs_intermediate = { "dcs_intermediate", NULL, NULL, input_state_dcs_intermediate_table }; /* dcs_handler state definition. */ const struct input_state input_state_dcs_handler = { "dcs_handler", NULL, NULL, input_state_dcs_handler_table }; /* dcs_escape state definition. */ const struct input_state input_state_dcs_escape = { "dcs_escape", NULL, NULL, input_state_dcs_escape_table }; /* dcs_ignore state definition. */ const struct input_state input_state_dcs_ignore = { "dcs_ignore", NULL, NULL, input_state_dcs_ignore_table }; /* osc_string state definition. */ const struct input_state input_state_osc_string = { "osc_string", input_enter_osc, input_exit_osc, input_state_osc_string_table }; /* apc_string state definition. */ const struct input_state input_state_apc_string = { "apc_string", input_enter_apc, input_exit_apc, input_state_apc_string_table }; /* rename_string state definition. */ const struct input_state input_state_rename_string = { "rename_string", input_enter_rename, input_exit_rename, input_state_rename_string_table }; /* consume_st state definition. */ const struct input_state input_state_consume_st = { "consume_st", NULL, NULL, input_state_consume_st_table }; /* utf8_three state definition. */ const struct input_state input_state_utf8_three = { "utf8_three", NULL, NULL, input_state_utf8_three_table }; /* utf8_two state definition. */ const struct input_state input_state_utf8_two = { "utf8_two", NULL, NULL, input_state_utf8_two_table }; /* utf8_one state definition. */ const struct input_state input_state_utf8_one = { "utf8_one", NULL, NULL, input_state_utf8_one_table }; /* ground state table. */ const struct input_transition input_state_ground_table[] = { INPUT_STATE_ANYWHERE, { 0x00, 0x17, input_c0_dispatch, NULL }, { 0x19, 0x19, input_c0_dispatch, NULL }, { 0x1c, 0x1f, input_c0_dispatch, NULL }, { 0x20, 0x7e, input_print, NULL }, { 0x7f, 0x7f, NULL, NULL }, { 0x80, 0xc1, input_print, NULL }, { 0xc2, 0xdf, input_utf8_open, &input_state_utf8_one }, { 0xe0, 0xef, input_utf8_open, &input_state_utf8_two }, { 0xf0, 0xf4, input_utf8_open, &input_state_utf8_three }, { 0xf5, 0xff, input_print, NULL }, { -1, -1, NULL, NULL } }; /* esc_enter state table. */ const struct input_transition input_state_esc_enter_table[] = { INPUT_STATE_ANYWHERE, { 0x00, 0x17, input_c0_dispatch, NULL }, { 0x19, 0x19, input_c0_dispatch, NULL }, { 0x1c, 0x1f, input_c0_dispatch, NULL }, { 0x20, 0x2f, input_intermediate, &input_state_esc_intermediate }, { 0x30, 0x4f, input_esc_dispatch, &input_state_ground }, { 0x50, 0x50, NULL, &input_state_dcs_enter }, { 0x51, 0x57, input_esc_dispatch, &input_state_ground }, { 0x58, 0x58, NULL, &input_state_consume_st }, { 0x59, 0x59, input_esc_dispatch, &input_state_ground }, { 0x5a, 0x5a, input_esc_dispatch, &input_state_ground }, { 0x5b, 0x5b, NULL, &input_state_csi_enter }, { 0x5c, 0x5c, input_esc_dispatch, &input_state_ground }, { 0x5d, 0x5d, NULL, &input_state_osc_string }, { 0x5e, 0x5e, NULL, &input_state_consume_st }, { 0x5f, 0x5f, NULL, &input_state_apc_string }, { 0x60, 0x6a, input_esc_dispatch, &input_state_ground }, { 0x6b, 0x6b, NULL, &input_state_rename_string }, { 0x6c, 0x7e, input_esc_dispatch, &input_state_ground }, { 0x7f, 0xff, NULL, NULL }, { -1, -1, NULL, NULL } }; /* esc_interm state table. */ const struct input_transition input_state_esc_intermediate_table[] = { INPUT_STATE_ANYWHERE, { 0x00, 0x17, input_c0_dispatch, NULL }, { 0x19, 0x19, input_c0_dispatch, NULL }, { 0x1c, 0x1f, input_c0_dispatch, NULL }, { 0x20, 0x2f, input_intermediate, NULL }, { 0x30, 0x7e, input_esc_dispatch, &input_state_ground }, { 0x7f, 0xff, NULL, NULL }, { -1, -1, NULL, NULL } }; /* csi_enter state table. */ const struct input_transition input_state_csi_enter_table[] = { INPUT_STATE_ANYWHERE, { 0x00, 0x17, input_c0_dispatch, NULL }, { 0x19, 0x19, input_c0_dispatch, NULL }, { 0x1c, 0x1f, input_c0_dispatch, NULL }, { 0x20, 0x2f, input_intermediate, &input_state_csi_intermediate }, { 0x30, 0x39, input_parameter, &input_state_csi_parameter }, { 0x3a, 0x3a, NULL, &input_state_csi_ignore }, { 0x3b, 0x3b, input_parameter, &input_state_csi_parameter }, { 0x3c, 0x3f, input_intermediate, &input_state_csi_parameter }, { 0x40, 0x7e, input_csi_dispatch, &input_state_ground }, { 0x7f, 0xff, NULL, NULL }, { -1, -1, NULL, NULL } }; /* csi_parameter state table. */ const struct input_transition input_state_csi_parameter_table[] = { INPUT_STATE_ANYWHERE, { 0x00, 0x17, input_c0_dispatch, NULL }, { 0x19, 0x19, input_c0_dispatch, NULL }, { 0x1c, 0x1f, input_c0_dispatch, NULL }, { 0x20, 0x2f, input_intermediate, &input_state_csi_intermediate }, { 0x30, 0x39, input_parameter, NULL }, { 0x3a, 0x3a, NULL, &input_state_csi_ignore }, { 0x3b, 0x3b, input_parameter, NULL }, { 0x3c, 0x3f, NULL, &input_state_csi_ignore }, { 0x40, 0x7e, input_csi_dispatch, &input_state_ground }, { 0x7f, 0xff, NULL, NULL }, { -1, -1, NULL, NULL } }; /* csi_intermediate state table. */ const struct input_transition input_state_csi_intermediate_table[] = { INPUT_STATE_ANYWHERE, { 0x00, 0x17, input_c0_dispatch, NULL }, { 0x19, 0x19, input_c0_dispatch, NULL }, { 0x1c, 0x1f, input_c0_dispatch, NULL }, { 0x20, 0x2f, input_intermediate, NULL }, { 0x30, 0x3f, NULL, &input_state_csi_ignore }, { 0x40, 0x7e, input_csi_dispatch, &input_state_ground }, { 0x7f, 0xff, NULL, NULL }, { -1, -1, NULL, NULL } }; /* csi_ignore state table. */ const struct input_transition input_state_csi_ignore_table[] = { INPUT_STATE_ANYWHERE, { 0x00, 0x17, input_c0_dispatch, NULL }, { 0x19, 0x19, input_c0_dispatch, NULL }, { 0x1c, 0x1f, input_c0_dispatch, NULL }, { 0x20, 0x3f, NULL, NULL }, { 0x40, 0x7e, NULL, &input_state_ground }, { 0x7f, 0xff, NULL, NULL }, { -1, -1, NULL, NULL } }; /* dcs_enter state table. */ const struct input_transition input_state_dcs_enter_table[] = { INPUT_STATE_ANYWHERE, { 0x00, 0x17, NULL, NULL }, { 0x19, 0x19, NULL, NULL }, { 0x1c, 0x1f, NULL, NULL }, { 0x20, 0x2f, input_intermediate, &input_state_dcs_intermediate }, { 0x30, 0x39, input_parameter, &input_state_dcs_parameter }, { 0x3a, 0x3a, NULL, &input_state_dcs_ignore }, { 0x3b, 0x3b, input_parameter, &input_state_dcs_parameter }, { 0x3c, 0x3f, input_intermediate, &input_state_dcs_parameter }, { 0x40, 0x7e, input_input, &input_state_dcs_handler }, { 0x7f, 0xff, NULL, NULL }, { -1, -1, NULL, NULL } }; /* dcs_parameter state table. */ const struct input_transition input_state_dcs_parameter_table[] = { INPUT_STATE_ANYWHERE, { 0x00, 0x17, NULL, NULL }, { 0x19, 0x19, NULL, NULL }, { 0x1c, 0x1f, NULL, NULL }, { 0x20, 0x2f, input_intermediate, &input_state_dcs_intermediate }, { 0x30, 0x39, input_parameter, NULL }, { 0x3a, 0x3a, NULL, &input_state_dcs_ignore }, { 0x3b, 0x3b, input_parameter, NULL }, { 0x3c, 0x3f, NULL, &input_state_dcs_ignore }, { 0x40, 0x7e, input_input, &input_state_dcs_handler }, { 0x7f, 0xff, NULL, NULL }, { -1, -1, NULL, NULL } }; /* dcs_interm state table. */ const struct input_transition input_state_dcs_intermediate_table[] = { INPUT_STATE_ANYWHERE, { 0x00, 0x17, NULL, NULL }, { 0x19, 0x19, NULL, NULL }, { 0x1c, 0x1f, NULL, NULL }, { 0x20, 0x2f, input_intermediate, NULL }, { 0x30, 0x3f, NULL, &input_state_dcs_ignore }, { 0x40, 0x7e, input_input, &input_state_dcs_handler }, { 0x7f, 0xff, NULL, NULL }, { -1, -1, NULL, NULL } }; /* dcs_handler state table. */ const struct input_transition input_state_dcs_handler_table[] = { /* No INPUT_STATE_ANYWHERE */ { 0x00, 0x1a, input_input, NULL }, { 0x1b, 0x1b, NULL, &input_state_dcs_escape }, { 0x1c, 0xff, input_input, NULL }, { -1, -1, NULL, NULL } }; /* dcs_escape state table. */ const struct input_transition input_state_dcs_escape_table[] = { /* No INPUT_STATE_ANYWHERE */ { 0x00, 0x5b, input_input, &input_state_dcs_handler }, { 0x5c, 0x5c, input_dcs_dispatch, &input_state_ground }, { 0x5d, 0xff, input_input, &input_state_dcs_handler }, { -1, -1, NULL, NULL } }; /* dcs_ignore state table. */ const struct input_transition input_state_dcs_ignore_table[] = { INPUT_STATE_ANYWHERE, { 0x00, 0x17, NULL, NULL }, { 0x19, 0x19, NULL, NULL }, { 0x1c, 0x1f, NULL, NULL }, { 0x20, 0xff, NULL, NULL }, { -1, -1, NULL, NULL } }; /* osc_string state table. */ const struct input_transition input_state_osc_string_table[] = { INPUT_STATE_ANYWHERE, { 0x00, 0x06, NULL, NULL }, { 0x07, 0x07, NULL, &input_state_ground }, { 0x08, 0x17, NULL, NULL }, { 0x19, 0x19, NULL, NULL }, { 0x1c, 0x1f, NULL, NULL }, { 0x20, 0xff, input_input, NULL }, { -1, -1, NULL, NULL } }; /* apc_string state table. */ const struct input_transition input_state_apc_string_table[] = { INPUT_STATE_ANYWHERE, { 0x00, 0x17, NULL, NULL }, { 0x19, 0x19, NULL, NULL }, { 0x1c, 0x1f, NULL, NULL }, { 0x20, 0xff, input_input, NULL }, { -1, -1, NULL, NULL } }; /* rename_string state table. */ const struct input_transition input_state_rename_string_table[] = { INPUT_STATE_ANYWHERE, { 0x00, 0x17, NULL, NULL }, { 0x19, 0x19, NULL, NULL }, { 0x1c, 0x1f, NULL, NULL }, { 0x20, 0xff, input_input, NULL }, { -1, -1, NULL, NULL } }; /* consume_st state table. */ const struct input_transition input_state_consume_st_table[] = { INPUT_STATE_ANYWHERE, { 0x00, 0x17, NULL, NULL }, { 0x19, 0x19, NULL, NULL }, { 0x1c, 0x1f, NULL, NULL }, { 0x20, 0xff, NULL, NULL }, { -1, -1, NULL, NULL } }; /* utf8_three state table. */ const struct input_transition input_state_utf8_three_table[] = { /* No INPUT_STATE_ANYWHERE */ { 0x00, 0x7f, NULL, &input_state_ground }, { 0x80, 0xbf, input_utf8_add, &input_state_utf8_two }, { 0xc0, 0xff, NULL, &input_state_ground }, { -1, -1, NULL, NULL } }; /* utf8_two state table. */ const struct input_transition input_state_utf8_two_table[] = { /* No INPUT_STATE_ANYWHERE */ { 0x00, 0x7f, NULL, &input_state_ground }, { 0x80, 0xbf, input_utf8_add, &input_state_utf8_one }, { 0xc0, 0xff, NULL, &input_state_ground }, { -1, -1, NULL, NULL } }; /* utf8_one state table. */ const struct input_transition input_state_utf8_one_table[] = { /* No INPUT_STATE_ANYWHERE */ { 0x00, 0x7f, NULL, &input_state_ground }, { 0x80, 0xbf, input_utf8_close, &input_state_ground }, { 0xc0, 0xff, NULL, &input_state_ground }, { -1, -1, NULL, NULL } }; /* Input table compare. */ int input_table_compare(const void *key, const void *value) { const struct input_ctx *ictx = key; const struct input_table_entry *entry = value; if (ictx->ch != entry->ch) return (ictx->ch - entry->ch); return (strcmp(ictx->interm_buf, entry->interm)); } /* Initialise input parser. */ void input_init(struct window_pane *wp) { struct input_ctx *ictx = &wp->ictx; memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell); memcpy(&ictx->old_cell, &grid_default_cell, sizeof ictx->old_cell); ictx->old_cx = 0; ictx->old_cy = 0; *ictx->interm_buf = '\0'; ictx->interm_len = 0; *ictx->param_buf = '\0'; ictx->param_len = 0; ictx->state = &input_state_ground; ictx->flags = 0; ictx->since_ground = evbuffer_new(); } /* Destroy input parser. */ void input_free(struct window_pane *wp) { if (wp != NULL) evbuffer_free(wp->ictx.since_ground); } /* Change input state. */ void input_set_state(struct window_pane *wp, const struct input_transition *itr) { struct input_ctx *ictx = &wp->ictx; struct evbuffer *ground_evb = ictx->since_ground; if (ictx->state->exit != NULL) ictx->state->exit(ictx); if (itr->state == &input_state_ground) evbuffer_drain(ground_evb, EVBUFFER_LENGTH(ground_evb)); ictx->state = itr->state; if (ictx->state->enter != NULL) ictx->state->enter(ictx); } /* Parse input. */ void input_parse(struct window_pane *wp) { struct input_ctx *ictx = &wp->ictx; const struct input_transition *itr; struct evbuffer *evb = wp->event->input; u_char *buf; size_t len, off; if (EVBUFFER_LENGTH(evb) == 0) return; wp->window->flags |= WINDOW_ACTIVITY; wp->window->flags &= ~WINDOW_SILENCE; /* * Open the screen. Use NULL wp if there is a mode set as don't want to * update the tty. */ if (wp->mode == NULL) screen_write_start(&ictx->ctx, wp, &wp->base); else screen_write_start(&ictx->ctx, NULL, &wp->base); ictx->wp = wp; buf = EVBUFFER_DATA(evb); len = EVBUFFER_LENGTH(evb); notify_input(wp, evb); off = 0; /* Parse the input. */ while (off < len) { ictx->ch = buf[off++]; log_debug("%s: '%c' %s", __func__, ictx->ch, ictx->state->name); /* Find the transition. */ itr = ictx->state->transitions; while (itr->first != -1 && itr->last != -1) { if (ictx->ch >= itr->first && ictx->ch <= itr->last) break; itr++; } if (itr->first == -1 || itr->last == -1) { /* No transition? Eh? */ fatalx("No transition from state!"); } /* * Execute the handler, if any. Don't switch state if it * returns non-zero. */ if (itr->handler != NULL && itr->handler(ictx) != 0) continue; /* And switch state, if necessary. */ if (itr->state != NULL) 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(&ictx->ctx); evbuffer_drain(evb, len); } /* Split the parameter list (if any). */ int input_split(struct input_ctx *ictx) { const char *errstr; char *ptr, *out; int n; ictx->param_list_len = 0; if (ictx->param_len == 0) return (0); ptr = ictx->param_buf; while ((out = strsep(&ptr, ";")) != NULL) { if (*out == '\0') n = -1; else { n = strtonum(out, 0, INT_MAX, &errstr); if (errstr != NULL) return (-1); } ictx->param_list[ictx->param_list_len++] = n; if (ictx->param_list_len == nitems(ictx->param_list)) return (-1); } return (0); } /* Get an argument or return default value. */ int input_get(struct input_ctx *ictx, u_int validx, int minval, int defval) { int retval; if (validx >= ictx->param_list_len) return (defval); retval = ictx->param_list[validx]; if (retval == -1) return (defval); if (retval < minval) return (minval); return (retval); } /* Reply to terminal query. */ void input_reply(struct input_ctx *ictx, const char *fmt, ...) { va_list ap; char *reply; va_start(ap, fmt); vasprintf(&reply, fmt, ap); va_end(ap); bufferevent_write(ictx->wp->event, reply, strlen(reply)); free(reply); } /* Clear saved state. */ void input_clear(struct input_ctx *ictx) { *ictx->interm_buf = '\0'; ictx->interm_len = 0; *ictx->param_buf = '\0'; ictx->param_len = 0; *ictx->input_buf = '\0'; ictx->input_len = 0; ictx->flags &= ~INPUT_DISCARD; } /* Output this character to the screen. */ int input_print(struct input_ctx *ictx) { grid_cell_one(&ictx->cell, ictx->ch); screen_write_cell(&ictx->ctx, &ictx->cell); return (0); } /* Collect intermediate string. */ int input_intermediate(struct input_ctx *ictx) { if (ictx->interm_len == (sizeof ictx->interm_buf) - 1) ictx->flags |= INPUT_DISCARD; else { ictx->interm_buf[ictx->interm_len++] = ictx->ch; ictx->interm_buf[ictx->interm_len] = '\0'; } return (0); } /* Collect parameter string. */ int input_parameter(struct input_ctx *ictx) { if (ictx->param_len == (sizeof ictx->param_buf) - 1) ictx->flags |= INPUT_DISCARD; else { ictx->param_buf[ictx->param_len++] = ictx->ch; ictx->param_buf[ictx->param_len] = '\0'; } return (0); } /* Collect input string. */ int input_input(struct input_ctx *ictx) { if (ictx->input_len == (sizeof ictx->input_buf) - 1) ictx->flags |= INPUT_DISCARD; else { ictx->input_buf[ictx->input_len++] = ictx->ch; ictx->input_buf[ictx->input_len] = '\0'; } return (0); } /* Execute C0 control sequence. */ int input_c0_dispatch(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; struct window_pane *wp = ictx->wp; struct screen *s = sctx->s; u_int trigger; log_debug("%s: '%c", __func__, ictx->ch); switch (ictx->ch) { case '\000': /* NUL */ break; case '\007': /* BEL */ wp->window->flags |= WINDOW_BELL; break; case '\010': /* BS */ screen_write_backspace(sctx); goto count_c0; 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); goto count_c0; case '\015': /* CR */ screen_write_carriagereturn(sctx); goto count_c0; case '\016': /* SO */ ictx->cell.attr |= GRID_ATTR_CHARSET; break; case '\017': /* SI */ ictx->cell.attr &= ~GRID_ATTR_CHARSET; break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } return (0); count_c0: trigger = options_get_number(&wp->window->options, "c0-change-trigger"); if (++wp->changes == trigger) { wp->flags |= PANE_DROP; window_pane_timer_start(wp); } return (0); } /* Execute escape sequence. */ int input_esc_dispatch(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; struct screen *s = sctx->s; struct input_table_entry *entry; if (ictx->flags & INPUT_DISCARD) return (0); log_debug("%s: '%c', %s", __func__, ictx->ch, ictx->interm_buf); entry = bsearch(ictx, input_esc_table, nitems(input_esc_table), sizeof input_esc_table[0], input_table_compare); if (entry == NULL) { log_debug("%s: unknown '%c'", __func__, ictx->ch); return (0); } switch (entry->type) { case INPUT_ESC_RIS: memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell); memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); ictx->old_cx = 0; ictx->old_cy = 0; screen_write_reset(sctx); break; case INPUT_ESC_IND: screen_write_linefeed(sctx, 0); break; case INPUT_ESC_NEL: screen_write_carriagereturn(sctx); screen_write_linefeed(sctx, 0); break; case INPUT_ESC_HTS: if (s->cx < screen_size_x(s)) bit_set(s->tabs, s->cx); break; case INPUT_ESC_RI: screen_write_reverseindex(sctx); break; case INPUT_ESC_DECKPAM: screen_write_mode_set(sctx, MODE_KKEYPAD); break; case INPUT_ESC_DECKPNM: screen_write_mode_clear(sctx, MODE_KKEYPAD); break; case INPUT_ESC_DECSC: memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); ictx->old_cx = s->cx; ictx->old_cy = s->cy; break; case INPUT_ESC_DECRC: memcpy(&ictx->cell, &ictx->old_cell, sizeof ictx->cell); screen_write_cursormove(sctx, ictx->old_cx, ictx->old_cy); break; case INPUT_ESC_DECALN: screen_write_alignmenttest(sctx); break; case INPUT_ESC_SCSON_G0: /* * Not really supported, but fake it up enough for those that * use it to switch character sets (by redefining G0 to * graphics set, rather than switching to G1). */ ictx->cell.attr &= ~GRID_ATTR_CHARSET; break; case INPUT_ESC_SCSOFF_G0: ictx->cell.attr |= GRID_ATTR_CHARSET; break; } return (0); } /* Execute control sequence. */ int input_csi_dispatch(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; struct window_pane *wp = ictx->wp; struct screen *s = sctx->s; struct input_table_entry *entry; int n, m; if (ictx->flags & INPUT_DISCARD) return (0); if (input_split(ictx) != 0) return (0); log_debug("%s: '%c' \"%s\" \"%s\"", __func__, ictx->ch, ictx->interm_buf, ictx->param_buf); entry = bsearch(ictx, input_csi_table, nitems(input_csi_table), sizeof input_csi_table[0], input_table_compare); if (entry == NULL) { log_debug("%s: unknown '%c'", __func__, ictx->ch); return (0); } switch (entry->type) { case INPUT_CSI_CBT: /* Find the previous tab point, n times. */ n = input_get(ictx, 0, 1, 1); while (s->cx > 0 && n-- > 0) { do s->cx--; while (s->cx > 0 && !bit_test(s->tabs, s->cx)); } break; case INPUT_CSI_CUB: screen_write_cursorleft(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_CUD: screen_write_cursordown(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_CUF: screen_write_cursorright(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_CUP: n = input_get(ictx, 0, 1, 1); m = input_get(ictx, 1, 1, 1); screen_write_cursormove(sctx, m - 1, n - 1); break; case INPUT_CSI_CUU: screen_write_cursorup(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_CNL: screen_write_carriagereturn(sctx); screen_write_cursordown(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_CPL: screen_write_carriagereturn(sctx); screen_write_cursorup(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_DA: switch (input_get(ictx, 0, 0, 0)) { case 0: input_reply(ictx, "\033[?1;2c"); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_DA_TWO: switch (input_get(ictx, 0, 0, 0)) { case 0: input_reply(ictx, "\033[>0;95;0c"); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_ECH: screen_write_clearcharacter(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_DCH: screen_write_deletecharacter(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_DECSTBM: n = input_get(ictx, 0, 1, 1); m = input_get(ictx, 1, 1, screen_size_y(s)); screen_write_scrollregion(sctx, n - 1, m - 1); break; case INPUT_CSI_DL: screen_write_deleteline(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_DSR: switch (input_get(ictx, 0, 0, 0)) { case 5: input_reply(ictx, "\033[0n"); break; case 6: input_reply(ictx, "\033[%u;%uR", s->cy + 1, s->cx + 1); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_ED: switch (input_get(ictx, 0, 0, 0)) { case 0: screen_write_clearendofscreen(sctx); break; case 1: screen_write_clearstartofscreen(sctx); break; case 2: screen_write_clearscreen(sctx); break; case 3: switch (input_get(ictx, 1, 0, 0)) { case 0: /* * Linux console extension to clear history * (for example before locking the screen). */ screen_write_clearhistory(sctx); break; } break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_EL: switch (input_get(ictx, 0, 0, 0)) { case 0: screen_write_clearendofline(sctx); break; case 1: screen_write_clearstartofline(sctx); break; case 2: screen_write_clearline(sctx); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_HPA: n = input_get(ictx, 0, 1, 1); screen_write_cursormove(sctx, n - 1, s->cy); break; case INPUT_CSI_ICH: screen_write_insertcharacter(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_IL: screen_write_insertline(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_RCP: memcpy(&ictx->cell, &ictx->old_cell, sizeof ictx->cell); screen_write_cursormove(sctx, ictx->old_cx, ictx->old_cy); break; case INPUT_CSI_RM: switch (input_get(ictx, 0, 0, -1)) { case 4: /* IRM */ screen_write_mode_clear(&ictx->ctx, MODE_INSERT); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_RM_PRIVATE: switch (input_get(ictx, 0, 0, -1)) { case 1: /* GATM */ screen_write_mode_clear(&ictx->ctx, MODE_KCURSOR); break; case 3: /* DECCOLM */ screen_write_cursormove(&ictx->ctx, 0, 0); screen_write_clearscreen(&ictx->ctx); break; case 7: /* DECAWM */ screen_write_mode_clear(&ictx->ctx, MODE_WRAP); break; case 25: /* TCEM */ screen_write_mode_clear(&ictx->ctx, MODE_CURSOR); break; case 1000: case 1001: case 1002: case 1003: screen_write_mode_clear(&ictx->ctx, ALL_MOUSE_MODES); break; case 1004: screen_write_mode_clear(&ictx->ctx, MODE_FOCUSON); break; case 1005: screen_write_mode_clear(&ictx->ctx, MODE_MOUSE_UTF8); break; case 1006: screen_write_mode_clear(&ictx->ctx, MODE_MOUSE_SGR); break; case 47: case 1047: window_pane_alternate_off(wp, &ictx->cell, 0); break; case 1049: window_pane_alternate_off(wp, &ictx->cell, 1); break; case 2004: screen_write_mode_clear(&ictx->ctx, MODE_BRACKETPASTE); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_SCP: memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); ictx->old_cx = s->cx; ictx->old_cy = s->cy; break; case INPUT_CSI_SGR: input_csi_dispatch_sgr(ictx); break; case INPUT_CSI_SM: switch (input_get(ictx, 0, 0, -1)) { case 4: /* IRM */ screen_write_mode_set(&ictx->ctx, MODE_INSERT); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_SM_PRIVATE: switch (input_get(ictx, 0, 0, -1)) { case 1: /* GATM */ screen_write_mode_set(&ictx->ctx, MODE_KCURSOR); break; case 3: /* DECCOLM */ screen_write_cursormove(&ictx->ctx, 0, 0); screen_write_clearscreen(&ictx->ctx); break; case 7: /* DECAWM */ screen_write_mode_set(&ictx->ctx, MODE_WRAP); break; case 25: /* TCEM */ screen_write_mode_set(&ictx->ctx, MODE_CURSOR); break; case 1000: screen_write_mode_clear(&ictx->ctx, ALL_MOUSE_MODES); screen_write_mode_set(&ictx->ctx, MODE_MOUSE_STANDARD); break; case 1002: screen_write_mode_clear(&ictx->ctx, ALL_MOUSE_MODES); screen_write_mode_set(&ictx->ctx, MODE_MOUSE_BUTTON); break; case 1003: screen_write_mode_clear(&ictx->ctx, ALL_MOUSE_MODES); screen_write_mode_set(&ictx->ctx, MODE_MOUSE_ANY); break; case 1004: if (s->mode & MODE_FOCUSON) break; screen_write_mode_set(&ictx->ctx, MODE_FOCUSON); wp->flags &= ~PANE_FOCUSED; /* force update if needed */ break; case 1005: screen_write_mode_set(&ictx->ctx, MODE_MOUSE_UTF8); break; case 1006: screen_write_mode_set(&ictx->ctx, MODE_MOUSE_SGR); break; case 47: case 1047: window_pane_alternate_on(wp, &ictx->cell, 0); break; case 1049: window_pane_alternate_on(wp, &ictx->cell, 1); break; case 2004: screen_write_mode_set(&ictx->ctx, MODE_BRACKETPASTE); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_TBC: switch (input_get(ictx, 0, 0, 0)) { case 0: if (s->cx < screen_size_x(s)) bit_clear(s->tabs, s->cx); break; case 3: bit_nclear(s->tabs, 0, screen_size_x(s) - 1); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_VPA: n = input_get(ictx, 0, 1, 1); screen_write_cursormove(sctx, s->cx, n - 1); break; case INPUT_CSI_DECSCUSR: n = input_get(ictx, 0, 0, 0); screen_set_cursor_style(s, n); break; } return (0); } /* Handle CSI SGR. */ void input_csi_dispatch_sgr(struct input_ctx *ictx) { struct grid_cell *gc = &ictx->cell; u_int i; int n, m; u_char attr; if (ictx->param_list_len == 0) { attr = gc->attr; memcpy(gc, &grid_default_cell, sizeof *gc); gc->attr |= (attr & GRID_ATTR_CHARSET); return; } for (i = 0; i < ictx->param_list_len; i++) { n = input_get(ictx, i, 0, 0); if (n == 38 || n == 48) { i++; if (input_get(ictx, i, 0, -1) != 5) continue; i++; m = input_get(ictx, i, 0, -1); if (m == -1) { if (n == 38) { gc->flags &= ~GRID_FLAG_FG256; gc->fg = 8; } else if (n == 48) { gc->flags &= ~GRID_FLAG_BG256; gc->bg = 8; } } else { if (n == 38) { gc->flags |= GRID_FLAG_FG256; gc->fg = m; } else if (n == 48) { gc->flags |= GRID_FLAG_BG256; gc->bg = m; } } continue; } switch (n) { case 0: case 10: attr = gc->attr; memcpy(gc, &grid_default_cell, sizeof *gc); gc->attr |= (attr & GRID_ATTR_CHARSET); break; case 1: gc->attr |= GRID_ATTR_BRIGHT; break; case 2: gc->attr |= GRID_ATTR_DIM; break; case 3: gc->attr |= GRID_ATTR_ITALICS; break; case 4: gc->attr |= GRID_ATTR_UNDERSCORE; break; case 5: gc->attr |= GRID_ATTR_BLINK; break; case 7: gc->attr |= GRID_ATTR_REVERSE; break; case 8: gc->attr |= GRID_ATTR_HIDDEN; break; case 22: gc->attr &= ~(GRID_ATTR_BRIGHT|GRID_ATTR_DIM); break; case 23: gc->attr &= ~GRID_ATTR_ITALICS; break; case 24: gc->attr &= ~GRID_ATTR_UNDERSCORE; break; case 25: gc->attr &= ~GRID_ATTR_BLINK; break; case 27: gc->attr &= ~GRID_ATTR_REVERSE; break; case 30: case 31: case 32: case 33: case 34: case 35: case 36: case 37: gc->flags &= ~GRID_FLAG_FG256; gc->fg = n - 30; break; case 39: gc->flags &= ~GRID_FLAG_FG256; gc->fg = 8; break; case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47: gc->flags &= ~GRID_FLAG_BG256; gc->bg = n - 40; break; case 49: gc->flags &= ~GRID_FLAG_BG256; gc->bg = 8; break; case 90: case 91: case 92: case 93: case 94: case 95: case 96: case 97: gc->flags &= ~GRID_FLAG_FG256; gc->fg = n; break; case 100: case 101: case 102: case 103: case 104: case 105: case 106: case 107: gc->flags &= ~GRID_FLAG_BG256; gc->bg = n - 10; break; } } } /* DCS terminator (ST) received. */ int input_dcs_dispatch(struct input_ctx *ictx) { const char prefix[] = "tmux;"; const u_int prefix_len = (sizeof prefix) - 1; if (ictx->flags & INPUT_DISCARD) return (0); log_debug("%s: \"%s\"", __func__, ictx->input_buf); /* Check for tmux prefix. */ if (ictx->input_len >= prefix_len && strncmp(ictx->input_buf, prefix, prefix_len) == 0) { screen_write_rawstring(&ictx->ctx, ictx->input_buf + prefix_len, ictx->input_len - prefix_len); } return (0); } /* OSC string started. */ void input_enter_osc(struct input_ctx *ictx) { log_debug("%s", __func__); input_clear(ictx); } /* OSC terminator (ST) received. */ void input_exit_osc(struct input_ctx *ictx) { u_char *p = ictx->input_buf; int option; if (ictx->flags & INPUT_DISCARD) return; if (ictx->input_len < 1 || *p < '0' || *p > '9') return; log_debug("%s: \"%s\"", __func__, p); option = 0; while (*p >= '0' && *p <= '9') option = option * 10 + *p++ - '0'; if (*p == ';') p++; switch (option) { case 0: case 2: screen_set_title(ictx->ctx.s, p); server_status_window(ictx->wp->window); break; case 12: if (*p != '?') /* ? is colour request */ screen_set_cursor_colour(ictx->ctx.s, p); break; case 112: if (*p == '\0') /* no arguments allowed */ screen_set_cursor_colour(ictx->ctx.s, ""); break; default: log_debug("%s: unknown '%u'", __func__, option); break; } } /* APC string started. */ void input_enter_apc(struct input_ctx *ictx) { log_debug("%s", __func__); input_clear(ictx); } /* APC terminator (ST) received. */ void input_exit_apc(struct input_ctx *ictx) { if (ictx->flags & INPUT_DISCARD) return; log_debug("%s: \"%s\"", __func__, ictx->input_buf); screen_set_title(ictx->ctx.s, ictx->input_buf); server_status_window(ictx->wp->window); } /* Rename string started. */ void input_enter_rename(struct input_ctx *ictx) { log_debug("%s", __func__); input_clear(ictx); } /* Rename terminator (ST) received. */ void input_exit_rename(struct input_ctx *ictx) { if (ictx->flags & INPUT_DISCARD) return; if (!options_get_number(&ictx->wp->window->options, "allow-rename")) return; log_debug("%s: \"%s\"", __func__, ictx->input_buf); 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. */ int input_utf8_open(struct input_ctx *ictx) { if (!options_get_number(&ictx->wp->window->options, "utf8")) { /* Print, and do not switch state. */ input_print(ictx); return (-1); } log_debug("%s", __func__); utf8_open(&ictx->utf8data, ictx->ch); return (0); } /* Append to UTF-8 character. */ int input_utf8_add(struct input_ctx *ictx) { log_debug("%s", __func__); utf8_append(&ictx->utf8data, ictx->ch); return (0); } /* Close UTF-8 string. */ int input_utf8_close(struct input_ctx *ictx) { log_debug("%s", __func__); utf8_append(&ictx->utf8data, ictx->ch); grid_cell_set(&ictx->cell, &ictx->utf8data); screen_write_cell(&ictx->ctx, &ictx->cell); return (0); } tmate-1.8.10/job.c000066400000000000000000000106701242461015400136340ustar00rootroot00000000000000/* $Id$ */ /* * 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" /* * Job scheduling. Run queued commands in the background and record their * output. */ void job_callback(struct bufferevent *, short, void *); void job_write_callback(struct bufferevent *, void *); /* All jobs list. */ struct joblist all_jobs = LIST_HEAD_INITIALIZER(all_jobs); /* Start a job running, if it isn't already. */ struct job * job_run(const char *cmd, struct session *s, void (*callbackfn)(struct job *), void (*freefn)(void *), void *data) { struct job *job; struct environ env; pid_t pid; int nullfd, out[2]; if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0) return (NULL); environ_init(&env); environ_copy(&global_environ, &env); if (s != NULL) environ_copy(&s->environ, &env); server_fill_environ(s, &env); switch (pid = fork()) { case -1: environ_free(&env); return (NULL); case 0: /* child */ clear_signals(1); environ_push(&env); environ_free(&env); if (dup2(out[1], 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 < 0) 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"); } /* parent */ environ_free(&env); close(out[1]); job = xmalloc(sizeof *job); job->cmd = xstrdup(cmd); job->pid = pid; job->status = 0; LIST_INSERT_HEAD(&all_jobs, job, lentry); job->callbackfn = callbackfn; job->freefn = freefn; job->data = data; job->fd = out[0]; setblocking(job->fd, 0); job->event = bufferevent_new(job->fd, NULL, job_write_callback, job_callback, job); bufferevent_enable(job->event, EV_READ); log_debug("run job %p: %s, pid %ld", job, job->cmd, (long) job->pid); return (job); } /* Kill and free an individual job. */ void job_free(struct job *job) { log_debug("free job %p: %s", job, job->cmd); LIST_REMOVE(job, lentry); free(job->cmd); if (job->freefn != NULL && job->data != NULL) job->freefn(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); } /* Called when output buffer falls below low watermark (default is 0). */ 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 %lu", job, job->cmd, (long) job->pid, (unsigned long) len); if (len == 0) { shutdown(job->fd, SHUT_WR); bufferevent_disable(job->event, EV_WRITE); } } /* Job buffer error callback. */ void job_callback(unused struct bufferevent *bufev, unused short events, void *data) { struct job *job = data; log_debug("job error %p: %s, pid %ld", job, job->cmd, (long) job->pid); if (job->pid == -1) { if (job->callbackfn != NULL) job->callbackfn(job); job_free(job); } else { bufferevent_disable(job->event, EV_READ); close(job->fd); job->fd = -1; } } /* Job died (waitpid() returned its pid). */ void job_died(struct job *job, int status) { log_debug("job died %p: %s, pid %ld", job, job->cmd, (long) job->pid); job->status = status; if (job->fd == -1) { if (job->callbackfn != NULL) job->callbackfn(job); job_free(job); } else job->pid = -1; } tmate-1.8.10/key-bindings.c000066400000000000000000000153371242461015400154520ustar00rootroot00000000000000/* $Id$ */ /* * 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" RB_GENERATE(key_bindings, key_binding, entry, key_bindings_cmp); struct key_bindings key_bindings; struct key_bindings dead_key_bindings; int key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2) { int key1, key2; key1 = bd1->key & ~KEYC_PREFIX; key2 = bd2->key & ~KEYC_PREFIX; if (key1 != key2) return (key1 - key2); if (bd1->key & KEYC_PREFIX && !(bd2->key & KEYC_PREFIX)) return (-1); if (bd2->key & KEYC_PREFIX && !(bd1->key & KEYC_PREFIX)) return (1); return (0); } struct key_binding * key_bindings_lookup(int key) { struct key_binding bd; bd.key = key; return (RB_FIND(key_bindings, &key_bindings, &bd)); } void key_bindings_add(int key, int can_repeat, struct cmd_list *cmdlist) { struct key_binding *bd; key_bindings_remove(key); bd = xmalloc(sizeof *bd); bd->key = key; RB_INSERT(key_bindings, &key_bindings, bd); bd->can_repeat = can_repeat; bd->cmdlist = cmdlist; } void key_bindings_remove(int key) { struct key_binding *bd; if ((bd = key_bindings_lookup(key)) == NULL) return; RB_REMOVE(key_bindings, &key_bindings, bd); RB_INSERT(key_bindings, &dead_key_bindings, bd); } void key_bindings_clean(void) { struct key_binding *bd; while (!RB_EMPTY(&dead_key_bindings)) { bd = RB_ROOT(&dead_key_bindings); RB_REMOVE(key_bindings, &dead_key_bindings, bd); cmd_list_free(bd->cmdlist); free(bd); } } void key_bindings_init(void) { static const struct { int key; int can_repeat; const struct cmd_entry *entry; } table[] = { { ' ', 0, &cmd_next_layout_entry }, { '!', 0, &cmd_break_pane_entry }, { '"', 0, &cmd_split_window_entry }, { '#', 0, &cmd_list_buffers_entry }, { '$', 0, &cmd_command_prompt_entry }, { '%', 0, &cmd_split_window_entry }, { '&', 0, &cmd_confirm_before_entry }, { '(', 0, &cmd_switch_client_entry }, { ')', 0, &cmd_switch_client_entry }, { ',', 0, &cmd_command_prompt_entry }, { '-', 0, &cmd_delete_buffer_entry }, { '.', 0, &cmd_command_prompt_entry }, { '0', 0, &cmd_select_window_entry }, { '1', 0, &cmd_select_window_entry }, { '2', 0, &cmd_select_window_entry }, { '3', 0, &cmd_select_window_entry }, { '4', 0, &cmd_select_window_entry }, { '5', 0, &cmd_select_window_entry }, { '6', 0, &cmd_select_window_entry }, { '7', 0, &cmd_select_window_entry }, { '8', 0, &cmd_select_window_entry }, { '9', 0, &cmd_select_window_entry }, { ':', 0, &cmd_command_prompt_entry }, { ';', 0, &cmd_last_pane_entry }, { '=', 0, &cmd_choose_buffer_entry }, { '?', 0, &cmd_list_keys_entry }, { 'D', 0, &cmd_choose_client_entry }, { 'L', 0, &cmd_switch_client_entry }, { '[', 0, &cmd_copy_mode_entry }, { '\'', 0, &cmd_command_prompt_entry }, { '\002', /* C-b */ 0, &cmd_send_prefix_entry }, { '\017', /* C-o */ 0, &cmd_rotate_window_entry }, { '\032', /* C-z */ 0, &cmd_suspend_client_entry }, { ']', 0, &cmd_paste_buffer_entry }, { 'c', 0, &cmd_new_window_entry }, { 'd', 0, &cmd_detach_client_entry }, { 'f', 0, &cmd_command_prompt_entry }, { 'i', 0, &cmd_display_message_entry }, { 'l', 0, &cmd_last_window_entry }, { 'n', 0, &cmd_next_window_entry }, { 'o', 0, &cmd_select_pane_entry }, { 'p', 0, &cmd_previous_window_entry }, { 'q', 0, &cmd_display_panes_entry }, { 'r', 0, &cmd_refresh_client_entry }, { 's', 0, &cmd_choose_tree_entry }, { 't', 0, &cmd_clock_mode_entry }, { 'w', 0, &cmd_choose_window_entry }, { 'x', 0, &cmd_confirm_before_entry }, { 'z', 0, &cmd_resize_pane_entry }, { '{', 0, &cmd_swap_pane_entry }, { '}', 0, &cmd_swap_pane_entry }, { '~', 0, &cmd_show_messages_entry }, { '1' | KEYC_ESCAPE, 0, &cmd_select_layout_entry }, { '2' | KEYC_ESCAPE, 0, &cmd_select_layout_entry }, { '3' | KEYC_ESCAPE, 0, &cmd_select_layout_entry }, { '4' | KEYC_ESCAPE, 0, &cmd_select_layout_entry }, { '5' | KEYC_ESCAPE, 0, &cmd_select_layout_entry }, { KEYC_PPAGE, 0, &cmd_copy_mode_entry }, { 'n' | KEYC_ESCAPE, 0, &cmd_next_window_entry }, { 'o' | KEYC_ESCAPE, 0, &cmd_rotate_window_entry }, { 'p' | KEYC_ESCAPE, 0, &cmd_previous_window_entry }, { KEYC_UP, 1, &cmd_select_pane_entry }, { KEYC_DOWN, 1, &cmd_select_pane_entry }, { KEYC_LEFT, 1, &cmd_select_pane_entry }, { KEYC_RIGHT, 1, &cmd_select_pane_entry }, { KEYC_UP | KEYC_ESCAPE, 1, &cmd_resize_pane_entry }, { KEYC_DOWN | KEYC_ESCAPE, 1, &cmd_resize_pane_entry }, { KEYC_LEFT | KEYC_ESCAPE, 1, &cmd_resize_pane_entry }, { KEYC_RIGHT | KEYC_ESCAPE, 1, &cmd_resize_pane_entry }, { KEYC_UP | KEYC_CTRL, 1, &cmd_resize_pane_entry }, { KEYC_DOWN | KEYC_CTRL, 1, &cmd_resize_pane_entry }, { KEYC_LEFT | KEYC_CTRL, 1, &cmd_resize_pane_entry }, { KEYC_RIGHT | KEYC_CTRL, 1, &cmd_resize_pane_entry }, }; u_int i; struct cmd *cmd; struct cmd_list *cmdlist; RB_INIT(&key_bindings); for (i = 0; i < nitems(table); i++) { cmdlist = xcalloc(1, sizeof *cmdlist); cmdlist->references = 1; TAILQ_INIT(&cmdlist->list); cmd = xcalloc(1, sizeof *cmd); cmd->entry = table[i].entry; if (cmd->entry->key_binding != NULL) cmd->entry->key_binding(cmd, table[i].key); else cmd->args = args_create(0); TAILQ_INSERT_HEAD(&cmdlist->list, cmd, qentry); key_bindings_add( table[i].key | KEYC_PREFIX, table[i].can_repeat, cmdlist); } } void key_bindings_dispatch(struct key_binding *bd, struct client *c) { struct cmd *cmd; 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)) { cmdq_info(c->cmdq, "client is read-only"); return; } cmdq_run(c->cmdq, bd->cmdlist); } tmate-1.8.10/key-string.c000066400000000000000000000135611242461015400151600ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" int key_string_search_table(const char *); int key_string_get_modifiers(const char **); const struct { const char *string; int key; } key_string_table[] = { /* Function keys. */ { "F1", KEYC_F1 }, { "F2", KEYC_F2 }, { "F3", KEYC_F3 }, { "F4", KEYC_F4 }, { "F5", KEYC_F5 }, { "F6", KEYC_F6 }, { "F7", KEYC_F7 }, { "F8", KEYC_F8 }, { "F9", KEYC_F9 }, { "F10", KEYC_F10 }, { "F11", KEYC_F11 }, { "F12", KEYC_F12 }, { "F13", KEYC_F13 }, { "F14", KEYC_F14 }, { "F15", KEYC_F15 }, { "F16", KEYC_F16 }, { "F17", KEYC_F17 }, { "F18", KEYC_F18 }, { "F19", KEYC_F19 }, { "F20", KEYC_F20 }, { "IC", KEYC_IC }, { "DC", KEYC_DC }, { "Home", KEYC_HOME }, { "End", KEYC_END }, { "NPage", KEYC_NPAGE }, { "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 }, }; /* Find key string in table. */ int key_string_search_table(const char *string) { u_int i; for (i = 0; i < nitems(key_string_table); i++) { if (strcasecmp(string, key_string_table[i].string) == 0) return (key_string_table[i].key); } return (KEYC_NONE); } /* Find modifiers. */ int key_string_get_modifiers(const char **string) { int modifiers; modifiers = 0; while (((*string)[0] != '\0') && (*string)[1] == '-') { switch ((*string)[0]) { case 'C': case 'c': modifiers |= KEYC_CTRL; break; case 'M': case 'm': modifiers |= KEYC_ESCAPE; break; case 'S': case 's': modifiers |= KEYC_SHIFT; break; } *string += 2; } return (modifiers); } /* Lookup a string and convert to a key value. */ int key_string_lookup_string(const char *string) { static const char *other = "!#()+,-.0123456789:;<=>?'\r\t"; int key, modifiers; u_short u; int size; /* Is this a hexadecimal value? */ if (string[0] == '0' && string[1] == 'x') { if (sscanf(string + 2, "%hx%n", &u, &size) != 1 || size > 4) return (KEYC_NONE); 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[0] == '\0') return (KEYC_NONE); /* Is this a standard ASCII key? */ if (string[1] == '\0') { key = (u_char) string[0]; if (key < 32 || key == 127 || key > 255) return (KEYC_NONE); } else { /* Otherwise look the key up in the table. */ key = key_string_search_table(string); if (key == KEYC_NONE) return (KEYC_NONE); } /* Convert the standard control keys. */ if (key < KEYC_BASE && (modifiers & KEYC_CTRL) && !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_NONE); modifiers &= ~KEYC_CTRL; } return (key | modifiers); } /* Convert a key code into string format, with prefix if necessary. */ const char * key_string_lookup_key(int key) { static char out[24]; char tmp[8]; u_int i; *out = '\0'; /* Handle no key. */ if (key == KEYC_NONE) return ("none"); /* * Special case: display C-@ as C-Space. Could do this below in * the (key >= 0 && key <= 32), but this way we let it be found * in key_string_table, for the unlikely chance that we might * change its name. */ if ((key & KEYC_MASK_KEY) == 0) key = ' ' | KEYC_CTRL | (key & KEYC_MASK_MOD); /* Fill in the modifiers. */ if (key & KEYC_CTRL) strlcat(out, "C-", sizeof out); if (key & KEYC_ESCAPE) strlcat(out, "M-", sizeof out); if (key & KEYC_SHIFT) strlcat(out, "S-", sizeof out); key &= KEYC_MASK_KEY; /* Try the key against the string table. */ for (i = 0; i < nitems(key_string_table); i++) { if (key == key_string_table[i].key) break; } if (i != nitems(key_string_table)) { strlcat(out, key_string_table[i].string, sizeof out); return (out); } /* Invalid keys are errors. */ if (key == 127 || key > 255) return (NULL); /* Check for standard or control key. */ if (key >= 0 && key <= 32) { if (key == 0 || key > 26) xsnprintf(tmp, sizeof tmp, "C-%c", 64 + key); else xsnprintf(tmp, sizeof tmp, "C-%c", 96 + key); } else if (key >= 32 && key <= 126) { tmp[0] = key; tmp[1] = '\0'; } else if (key >= 128) xsnprintf(tmp, sizeof tmp, "\\%o", key); strlcat(out, tmp, sizeof out); return (out); } tmate-1.8.10/layout-custom.c000066400000000000000000000146611242461015400157130ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2010 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" struct layout_cell *layout_find_bottomright(struct layout_cell *); u_short layout_checksum(const char *); int layout_append(struct layout_cell *, char *, size_t); struct layout_cell *layout_construct(struct layout_cell *, const char **); void layout_assign(struct window_pane **, struct layout_cell *); /* Find the bottom-right cell. */ struct layout_cell * layout_find_bottomright(struct layout_cell *lc) { if (lc->type == LAYOUT_WINDOWPANE) return (lc); lc = TAILQ_LAST(&lc->cells, layout_cells); return (layout_find_bottomright(lc)); } /* Calculate layout checksum. */ u_short layout_checksum(const char *layout) { u_short csum; csum = 0; for (; *layout != '\0'; layout++) { csum = (csum >> 1) + ((csum & 1) << 15); csum += *layout; } return (csum); } /* Dump layout as a string. */ char * layout_dump(struct window *w) { char layout[BUFSIZ], *out; *layout = '\0'; if (layout_append(w->layout_root, layout, sizeof layout) != 0) return (NULL); xasprintf(&out, "%04x,%s", layout_checksum(layout), layout); return (out); } /* Append information for a single cell. */ int layout_append(struct layout_cell *lc, char *buf, size_t len) { struct layout_cell *lcchild; char tmp[64]; size_t tmplen; const char *brackets = "]["; if (len == 0) return (-1); 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); } /* Parse a layout string and arrange window as layout. */ int layout_parse(struct window *w, const char *layout) { struct layout_cell *lc, *lcchild; struct window_pane *wp; u_int npanes, ncells, sx, sy; u_short csum; /* Check validity. */ if (sscanf(layout, "%hx,", &csum) != 1) return (-1); layout += 5; if (csum != layout_checksum(layout)) return (-1); /* Build the layout. */ lc = layout_construct(NULL, &layout); if (lc == NULL) return (-1); if (*layout != '\0') goto fail; /* Check this window will fit into the layout. */ for (;;) { npanes = window_count_panes(w); ncells = layout_count_cells(lc); if (npanes > ncells) goto fail; if (npanes == ncells) break; /* Fewer panes than cells - close the bottom right. */ lcchild = layout_find_bottomright(lc); layout_destroy_cell(lcchild, &lc); } /* Save the old window size and resize to the layout size. */ sx = w->sx; sy = w->sy; window_resize(w, lc->sx, lc->sy); /* Destroy the old layout and swap to the new. */ layout_free_cell(w->layout_root); w->layout_root = lc; /* Assign the panes into the cells. */ wp = TAILQ_FIRST(&w->panes); layout_assign(&wp, lc); /* Update pane offsets and sizes. */ layout_fix_offsets(lc); layout_fix_panes(w, lc->sx, lc->sy); /* Then resize the layout back to the original window size. */ layout_resize(w, sx, sy); window_resize(w, sx, sy); layout_print_cell(lc, __func__, 0); notify_window_layout_changed(w); return (0); fail: layout_free_cell(lc); return (-1); } /* Assign panes into cells. */ void layout_assign(struct window_pane **wp, struct layout_cell *lc) { struct layout_cell *lcchild; switch (lc->type) { case LAYOUT_WINDOWPANE: layout_make_leaf(lc, *wp); *wp = TAILQ_NEXT(*wp, entry); return; case LAYOUT_LEFTRIGHT: case LAYOUT_TOPBOTTOM: TAILQ_FOREACH(lcchild, &lc->cells, entry) layout_assign(wp, lcchild); return; } } /* Construct a cell from all or part of a layout tree. */ struct layout_cell * layout_construct(struct layout_cell *lcparent, const char **layout) { struct layout_cell *lc, *lcchild; u_int sx, sy, xoff, yoff; 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); } tmate-1.8.10/layout-set.c000066400000000000000000000353421242461015400151730ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Set window layouts - predefined methods to arrange windows. These are one-off * and generate a layout tree. */ void layout_set_even_h(struct window *); void layout_set_even_v(struct window *); void layout_set_main_h(struct window *); void layout_set_main_v(struct window *); void layout_set_tiled(struct window *); const struct { const char *name; void (*arrange)(struct window *); } layout_sets[] = { { "even-horizontal", layout_set_even_h }, { "even-vertical", layout_set_even_v }, { "main-horizontal", layout_set_main_h }, { "main-vertical", layout_set_main_v }, { "tiled", layout_set_tiled }, }; const char * layout_set_name(u_int layout) { return (layout_sets[layout].name); } int layout_set_lookup(const char *name) { u_int i; int matched = -1; for (i = 0; i < nitems(layout_sets); i++) { if (strncmp(layout_sets[i].name, name, strlen(name)) == 0) { if (matched != -1) /* ambiguous */ return (-1); matched = i; } } return (matched); } u_int layout_set_select(struct window *w, u_int layout) { if (layout > nitems(layout_sets) - 1) layout = nitems(layout_sets) - 1; if (layout_sets[layout].arrange != NULL) layout_sets[layout].arrange(w); w->lastlayout = layout; return (layout); } u_int layout_set_next(struct window *w) { u_int layout; if (w->lastlayout == -1) layout = 0; else { layout = w->lastlayout + 1; if (layout > nitems(layout_sets) - 1) layout = 0; } if (layout_sets[layout].arrange != NULL) layout_sets[layout].arrange(w); w->lastlayout = layout; return (layout); } u_int layout_set_previous(struct window *w) { u_int layout; if (w->lastlayout == -1) layout = nitems(layout_sets) - 1; else { layout = w->lastlayout; if (layout == 0) layout = nitems(layout_sets) - 1; else layout--; } if (layout_sets[layout].arrange != NULL) layout_sets[layout].arrange(w); w->lastlayout = layout; return (layout); } void layout_set_even_h(struct window *w) { struct window_pane *wp; struct layout_cell *lc, *lcnew; u_int i, n, width, xoff; layout_print_cell(w->layout_root, __func__, 1); /* Get number of panes. */ n = window_count_panes(w); if (n <= 1) return; /* How many can we fit? */ width = (w->sx - (n - 1)) / n; if (width < PANE_MINIMUM) width = PANE_MINIMUM; /* Free the old root and construct a new. */ layout_free(w); lc = w->layout_root = layout_create_cell(NULL); layout_set_size(lc, w->sx, w->sy, 0, 0); layout_make_node(lc, LAYOUT_LEFTRIGHT); /* Build new leaf cells. */ i = xoff = 0; TAILQ_FOREACH(wp, &w->panes, entry) { /* Create child cell. */ lcnew = layout_create_cell(lc); layout_set_size(lcnew, width, w->sy, xoff, 0); layout_make_leaf(lcnew, wp); TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry); i++; xoff += width + 1; } /* Allocate any remaining space. */ if (w->sx > xoff - 1) { lc = TAILQ_LAST(&lc->cells, layout_cells); layout_resize_adjust(lc, LAYOUT_LEFTRIGHT, w->sx - (xoff - 1)); } /* Fix cell offsets. */ layout_fix_offsets(lc); layout_fix_panes(w, w->sx, w->sy); layout_print_cell(w->layout_root, __func__, 1); server_redraw_window(w); } void layout_set_even_v(struct window *w) { struct window_pane *wp; struct layout_cell *lc, *lcnew; u_int i, n, height, yoff; layout_print_cell(w->layout_root, __func__, 1); /* Get number of panes. */ n = window_count_panes(w); if (n <= 1) return; /* How many can we fit? */ height = (w->sy - (n - 1)) / n; if (height < PANE_MINIMUM) height = PANE_MINIMUM; /* Free the old root and construct a new. */ layout_free(w); lc = w->layout_root = layout_create_cell(NULL); layout_set_size(lc, w->sx, w->sy, 0, 0); layout_make_node(lc, LAYOUT_TOPBOTTOM); /* Build new leaf cells. */ i = yoff = 0; TAILQ_FOREACH(wp, &w->panes, entry) { /* Create child cell. */ lcnew = layout_create_cell(lc); layout_set_size(lcnew, w->sx, height, 0, yoff); layout_make_leaf(lcnew, wp); TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry); i++; yoff += height + 1; } /* Allocate any remaining space. */ if (w->sy > yoff - 1) { lc = TAILQ_LAST(&lc->cells, layout_cells); layout_resize_adjust(lc, LAYOUT_TOPBOTTOM, w->sy - (yoff - 1)); } /* Fix cell offsets. */ layout_fix_offsets(lc); layout_fix_panes(w, w->sx, w->sy); layout_print_cell(w->layout_root, __func__, 1); server_redraw_window(w); } void layout_set_main_h(struct window *w) { struct window_pane *wp; struct layout_cell *lc, *lcmain, *lcrow, *lcchild; u_int n, mainheight, otherheight, width, height; u_int used, i, j, columns, rows, totalrows; layout_print_cell(w->layout_root, __func__, 1); /* Get number of panes. */ n = window_count_panes(w); if (n <= 1) return; n--; /* take off main pane */ /* How many rows and columns will be needed, not counting main? */ columns = (w->sx + 1) / (PANE_MINIMUM + 1); /* maximum columns */ if (columns == 0) columns = 1; rows = 1 + (n - 1) / columns; columns = 1 + (n - 1) / rows; width = (w->sx - (n - 1)) / columns; /* Get the main pane height and add one for separator line. */ mainheight = options_get_number(&w->options, "main-pane-height") + 1; /* Get the optional other pane height and add one for separator line. */ otherheight = options_get_number(&w->options, "other-pane-height") + 1; /* * If an other pane height was specified, honour it so long as it * doesn't shrink the main height to less than the main-pane-height */ if (otherheight > 1 && w->sy - otherheight > mainheight) mainheight = w->sy - otherheight; if (mainheight < PANE_MINIMUM + 1) mainheight = PANE_MINIMUM + 1; /* Try and make everything fit. */ totalrows = rows * (PANE_MINIMUM + 1) - 1; if (mainheight + totalrows > w->sy) { if (totalrows + PANE_MINIMUM + 1 > w->sy) mainheight = PANE_MINIMUM + 2; else mainheight = w->sy - totalrows; height = PANE_MINIMUM; } else height = (w->sy - mainheight - (rows - 1)) / rows; /* Free old tree and create a new root. */ layout_free(w); lc = w->layout_root = layout_create_cell(NULL); layout_set_size(lc, w->sx, mainheight + rows * (height + 1) - 1, 0, 0); layout_make_node(lc, LAYOUT_TOPBOTTOM); /* Create the main pane. */ lcmain = layout_create_cell(lc); layout_set_size(lcmain, w->sx, mainheight - 1, 0, 0); layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes)); TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry); /* Create a grid of the remaining cells. */ wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry); for (j = 0; j < rows; j++) { /* If this is the last cell, all done. */ if (wp == NULL) break; /* Create the new row. */ lcrow = layout_create_cell(lc); layout_set_size(lcrow, w->sx, height, 0, 0); TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry); /* If only one column, just use the row directly. */ if (columns == 1) { layout_make_leaf(lcrow, wp); wp = TAILQ_NEXT(wp, entry); continue; } /* Add in the columns. */ layout_make_node(lcrow, LAYOUT_LEFTRIGHT); for (i = 0; i < columns; i++) { /* Create and add a pane cell. */ lcchild = layout_create_cell(lcrow); layout_set_size(lcchild, width, height, 0, 0); layout_make_leaf(lcchild, wp); TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry); /* Move to the next cell. */ if ((wp = TAILQ_NEXT(wp, entry)) == NULL) break; } /* Adjust the row to fit the full width if necessary. */ if (i == columns) i--; used = ((i + 1) * (width + 1)) - 1; if (w->sx <= used) continue; lcchild = TAILQ_LAST(&lcrow->cells, layout_cells); layout_resize_adjust(lcchild, LAYOUT_LEFTRIGHT, w->sx - used); } /* Adjust the last row height to fit if necessary. */ used = mainheight + (rows * height) + rows - 1; if (w->sy > used) { lcrow = TAILQ_LAST(&lc->cells, layout_cells); layout_resize_adjust(lcrow, LAYOUT_TOPBOTTOM, w->sy - used); } /* Fix cell offsets. */ layout_fix_offsets(lc); layout_fix_panes(w, w->sx, w->sy); layout_print_cell(w->layout_root, __func__, 1); server_redraw_window(w); } void layout_set_main_v(struct window *w) { struct window_pane *wp; struct layout_cell *lc, *lcmain, *lccolumn, *lcchild; u_int n, mainwidth, otherwidth, width, height; u_int used, i, j, columns, rows, totalcolumns; layout_print_cell(w->layout_root, __func__, 1); /* Get number of panes. */ n = window_count_panes(w); if (n <= 1) return; n--; /* take off main pane */ /* How many rows and columns will be needed, not counting main? */ rows = (w->sy + 1) / (PANE_MINIMUM + 1); /* maximum rows */ if (rows == 0) rows = 1; columns = 1 + (n - 1) / rows; rows = 1 + (n - 1) / columns; height = (w->sy - (n - 1)) / rows; /* Get the main pane width and add one for separator line. */ mainwidth = options_get_number(&w->options, "main-pane-width") + 1; /* Get the optional other pane width and add one for separator line. */ otherwidth = options_get_number(&w->options, "other-pane-width") + 1; /* * If an other pane width was specified, honour it so long as it * doesn't shrink the main width to less than the main-pane-width */ if (otherwidth > 1 && w->sx - otherwidth > mainwidth) mainwidth = w->sx - otherwidth; if (mainwidth < PANE_MINIMUM + 1) mainwidth = PANE_MINIMUM + 1; /* Try and make everything fit. */ totalcolumns = columns * (PANE_MINIMUM + 1) - 1; if (mainwidth + totalcolumns > w->sx) { if (totalcolumns + PANE_MINIMUM + 1 > w->sx) mainwidth = PANE_MINIMUM + 2; else mainwidth = w->sx - totalcolumns; width = PANE_MINIMUM; } else width = (w->sx - mainwidth - (columns - 1)) / columns; /* Free old tree and create a new root. */ layout_free(w); lc = w->layout_root = layout_create_cell(NULL); layout_set_size(lc, mainwidth + columns * (width + 1) - 1, w->sy, 0, 0); layout_make_node(lc, LAYOUT_LEFTRIGHT); /* Create the main pane. */ lcmain = layout_create_cell(lc); layout_set_size(lcmain, mainwidth - 1, w->sy, 0, 0); layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes)); TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry); /* Create a grid of the remaining cells. */ wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry); for (j = 0; j < columns; j++) { /* If this is the last cell, all done. */ if (wp == NULL) break; /* Create the new column. */ lccolumn = layout_create_cell(lc); layout_set_size(lccolumn, width, w->sy, 0, 0); TAILQ_INSERT_TAIL(&lc->cells, lccolumn, entry); /* If only one row, just use the row directly. */ if (rows == 1) { layout_make_leaf(lccolumn, wp); wp = TAILQ_NEXT(wp, entry); continue; } /* Add in the rows. */ layout_make_node(lccolumn, LAYOUT_TOPBOTTOM); for (i = 0; i < rows; i++) { /* Create and add a pane cell. */ lcchild = layout_create_cell(lccolumn); layout_set_size(lcchild, width, height, 0, 0); layout_make_leaf(lcchild, wp); TAILQ_INSERT_TAIL(&lccolumn->cells, lcchild, entry); /* Move to the next cell. */ if ((wp = TAILQ_NEXT(wp, entry)) == NULL) break; } /* Adjust the column to fit the full height if necessary. */ if (i == rows) i--; used = ((i + 1) * (height + 1)) - 1; if (w->sy <= used) continue; lcchild = TAILQ_LAST(&lccolumn->cells, layout_cells); layout_resize_adjust(lcchild, LAYOUT_TOPBOTTOM, w->sy - used); } /* Adjust the last column width to fit if necessary. */ used = mainwidth + (columns * width) + columns - 1; if (w->sx > used) { lccolumn = TAILQ_LAST(&lc->cells, layout_cells); layout_resize_adjust(lccolumn, LAYOUT_LEFTRIGHT, w->sx - used); } /* Fix cell offsets. */ layout_fix_offsets(lc); layout_fix_panes(w, w->sx, w->sy); layout_print_cell(w->layout_root, __func__, 1); server_redraw_window(w); } void layout_set_tiled(struct window *w) { struct window_pane *wp; struct layout_cell *lc, *lcrow, *lcchild; u_int n, width, height, used; u_int i, j, columns, rows; layout_print_cell(w->layout_root, __func__, 1); /* Get number of panes. */ n = window_count_panes(w); if (n <= 1) return; /* How many rows and columns are wanted? */ rows = columns = 1; while (rows * columns < n) { rows++; if (rows * columns < n) columns++; } /* What width and height should they be? */ width = (w->sx - (columns - 1)) / columns; if (width < PANE_MINIMUM) width = PANE_MINIMUM; height = (w->sy - (rows - 1)) / rows; if (height < PANE_MINIMUM) height = PANE_MINIMUM; /* Free old tree and create a new root. */ layout_free(w); lc = w->layout_root = layout_create_cell(NULL); layout_set_size(lc, (width + 1) * columns - 1, (height + 1) * rows - 1, 0, 0); layout_make_node(lc, LAYOUT_TOPBOTTOM); /* Create a grid of the cells. */ wp = TAILQ_FIRST(&w->panes); for (j = 0; j < rows; j++) { /* If this is the last cell, all done. */ if (wp == NULL) break; /* Create the new row. */ lcrow = layout_create_cell(lc); layout_set_size(lcrow, w->sx, height, 0, 0); TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry); /* If only one column, just use the row directly. */ if (n - (j * columns) == 1 || columns == 1) { layout_make_leaf(lcrow, wp); wp = TAILQ_NEXT(wp, entry); continue; } /* Add in the columns. */ layout_make_node(lcrow, LAYOUT_LEFTRIGHT); for (i = 0; i < columns; i++) { /* Create and add a pane cell. */ lcchild = layout_create_cell(lcrow); layout_set_size(lcchild, width, height, 0, 0); layout_make_leaf(lcchild, wp); TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry); /* Move to the next cell. */ if ((wp = TAILQ_NEXT(wp, entry)) == NULL) break; } /* * Adjust the row and columns to fit the full width if * necessary. */ if (i == columns) i--; used = ((i + 1) * (width + 1)) - 1; if (w->sx <= used) continue; lcchild = TAILQ_LAST(&lcrow->cells, layout_cells); layout_resize_adjust(lcchild, LAYOUT_LEFTRIGHT, w->sx - used); } /* Adjust the last row height to fit if necessary. */ used = (rows * height) + rows - 1; if (w->sy > used) { lcrow = TAILQ_LAST(&lc->cells, layout_cells); layout_resize_adjust(lcrow, LAYOUT_TOPBOTTOM, w->sy - used); } /* Fix cell offsets. */ layout_fix_offsets(lc); layout_fix_panes(w, w->sx, w->sy); layout_print_cell(w->layout_root, __func__, 1); server_redraw_window(w); } tmate-1.8.10/layout.c000066400000000000000000000456501242461015400144050ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * The window layout is a tree of cells each of which can be one of: a * left-right container for a list of cells, a top-bottom container for a list * of cells, or a container for a window pane. * * Each window has a pointer to the root of its layout tree (containing its * panes), every pane has a pointer back to the cell containing it, and each * cell a pointer to its parent cell. */ int layout_resize_pane_grow(struct layout_cell *, enum layout_type, int); int layout_resize_pane_shrink(struct layout_cell *, enum layout_type, int); struct layout_cell * layout_create_cell(struct layout_cell *lcparent) { struct layout_cell *lc; lc = xmalloc(sizeof *lc); lc->type = LAYOUT_WINDOWPANE; lc->parent = lcparent; TAILQ_INIT(&lc->cells); lc->sx = UINT_MAX; lc->sy = UINT_MAX; lc->xoff = UINT_MAX; lc->yoff = UINT_MAX; lc->wp = NULL; return (lc); } void layout_free_cell(struct layout_cell *lc) { struct layout_cell *lcchild; switch (lc->type) { case LAYOUT_LEFTRIGHT: case LAYOUT_TOPBOTTOM: while (!TAILQ_EMPTY(&lc->cells)) { lcchild = TAILQ_FIRST(&lc->cells); TAILQ_REMOVE(&lc->cells, lcchild, entry); layout_free_cell(lcchild); } break; case LAYOUT_WINDOWPANE: if (lc->wp != NULL) lc->wp->layout_cell = NULL; break; } free(lc); } void layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n) { struct layout_cell *lcchild; log_debug( "%s:%*s%p type %u [parent %p] wp=%p [%u,%u %ux%u]", hdr, n, " ", lc, lc->type, lc->parent, lc->wp, lc->xoff, lc->yoff, lc->sx, lc->sy); switch (lc->type) { case LAYOUT_LEFTRIGHT: case LAYOUT_TOPBOTTOM: TAILQ_FOREACH(lcchild, &lc->cells, entry) layout_print_cell(lcchild, hdr, n + 1); break; case LAYOUT_WINDOWPANE: break; } } void layout_set_size( struct layout_cell *lc, u_int sx, u_int sy, u_int xoff, u_int yoff) { lc->sx = sx; lc->sy = sy; lc->xoff = xoff; lc->yoff = yoff; } void layout_make_leaf(struct layout_cell *lc, struct window_pane *wp) { lc->type = LAYOUT_WINDOWPANE; TAILQ_INIT(&lc->cells); wp->layout_cell = lc; lc->wp = wp; } void layout_make_node(struct layout_cell *lc, enum layout_type type) { if (type == LAYOUT_WINDOWPANE) fatalx("bad layout type"); lc->type = type; TAILQ_INIT(&lc->cells); if (lc->wp != NULL) lc->wp->layout_cell = NULL; lc->wp = NULL; } /* Fix cell offsets based on their sizes. */ void layout_fix_offsets(struct layout_cell *lc) { struct layout_cell *lcchild; u_int xoff, yoff; if (lc->type == LAYOUT_LEFTRIGHT) { xoff = lc->xoff; TAILQ_FOREACH(lcchild, &lc->cells, entry) { lcchild->xoff = xoff; lcchild->yoff = lc->yoff; if (lcchild->type != LAYOUT_WINDOWPANE) layout_fix_offsets(lcchild); xoff += lcchild->sx + 1; } } else { yoff = lc->yoff; TAILQ_FOREACH(lcchild, &lc->cells, entry) { lcchild->xoff = lc->xoff; lcchild->yoff = yoff; if (lcchild->type != LAYOUT_WINDOWPANE) layout_fix_offsets(lcchild); yoff += lcchild->sy + 1; } } } /* Update pane offsets and sizes based on their cells. */ void layout_fix_panes(struct window *w, u_int wsx, u_int wsy) { struct window_pane *wp; struct layout_cell *lc; u_int sx, sy; TAILQ_FOREACH(wp, &w->panes, entry) { if ((lc = wp->layout_cell) == NULL) continue; wp->xoff = lc->xoff; wp->yoff = lc->yoff; /* * Layout cells are limited by the smallest size of other cells * within the same row or column; if this isn't the case * resizing becomes difficult. * * However, panes do not have to take up their entire cell, so * they can be cropped to the window edge if the layout * overflows and they are partly visible. * * This stops cells being hidden unnecessarily. */ /* * Work out the horizontal size. If the pane is actually * outside the window or the entire pane is already visible, * don't crop. */ if (lc->xoff >= wsx || lc->xoff + lc->sx < wsx) sx = lc->sx; else { sx = wsx - lc->xoff; if (sx < 1) sx = lc->sx; } /* * Similarly for the vertical size; the minimum vertical size * is two because scroll regions cannot be one line. */ if (lc->yoff >= wsy || lc->yoff + lc->sy < wsy) sy = lc->sy; else { sy = wsy - lc->yoff; if (sy < 2) sy = lc->sy; } window_pane_resize(wp, sx, sy); } } /* Count the number of available cells in a layout. */ u_int layout_count_cells(struct layout_cell *lc) { struct layout_cell *lcchild; u_int n; switch (lc->type) { case LAYOUT_WINDOWPANE: return (1); case LAYOUT_LEFTRIGHT: case LAYOUT_TOPBOTTOM: n = 0; TAILQ_FOREACH(lcchild, &lc->cells, entry) n += layout_count_cells(lcchild); return (n); default: fatalx("bad layout type"); } } /* Calculate how much size is available to be removed from a cell. */ u_int layout_resize_check(struct layout_cell *lc, enum layout_type type) { struct layout_cell *lcchild; u_int available, minimum; if (lc->type == LAYOUT_WINDOWPANE) { /* Space available in this cell only. */ if (type == LAYOUT_LEFTRIGHT) available = lc->sx; else available = lc->sy; if (available > PANE_MINIMUM) available -= PANE_MINIMUM; else available = 0; } else if (lc->type == type) { /* Same type: total of available space in all child cells. */ available = 0; TAILQ_FOREACH(lcchild, &lc->cells, entry) available += layout_resize_check(lcchild, type); } else { /* Different type: minimum of available space in child cells. */ minimum = UINT_MAX; TAILQ_FOREACH(lcchild, &lc->cells, entry) { available = layout_resize_check(lcchild, type); if (available < minimum) minimum = available; } available = minimum; } return (available); } /* * Adjust cell size evenly, including altering its children. This function * expects the change to have already been bounded to the space available. */ void layout_resize_adjust(struct layout_cell *lc, enum layout_type type, int change) { struct layout_cell *lcchild; /* Adjust the cell size. */ if (type == LAYOUT_LEFTRIGHT) lc->sx += change; else lc->sy += change; /* If this is a leaf cell, that is all that is necessary. */ if (type == LAYOUT_WINDOWPANE) return; /* Child cell runs in a different direction. */ if (lc->type != type) { TAILQ_FOREACH(lcchild, &lc->cells, entry) layout_resize_adjust(lcchild, type, change); return; } /* * Child cell runs in the same direction. Adjust each child equally * until no further change is possible. */ while (change != 0) { TAILQ_FOREACH(lcchild, &lc->cells, entry) { if (change == 0) break; if (change > 0) { layout_resize_adjust(lcchild, type, 1); change--; continue; } if (layout_resize_check(lcchild, type) > 0) { layout_resize_adjust(lcchild, type, -1); change++; } } } } /* Destroy a cell and redistribute the space. */ void layout_destroy_cell(struct layout_cell *lc, struct layout_cell **lcroot) { struct layout_cell *lcother, *lcparent; /* * If no parent, this is the last pane so window close is imminent and * there is no need to resize anything. */ lcparent = lc->parent; if (lcparent == NULL) { layout_free_cell(lc); *lcroot = NULL; return; } /* Merge the space into the previous or next cell. */ if (lc == TAILQ_FIRST(&lcparent->cells)) lcother = TAILQ_NEXT(lc, entry); else lcother = TAILQ_PREV(lc, layout_cells, entry); if (lcparent->type == LAYOUT_LEFTRIGHT) layout_resize_adjust(lcother, lcparent->type, lc->sx + 1); else layout_resize_adjust(lcother, lcparent->type, lc->sy + 1); /* Remove this from the parent's list. */ TAILQ_REMOVE(&lcparent->cells, lc, entry); layout_free_cell(lc); /* * If the parent now has one cell, remove the parent from the tree and * replace it by that cell. */ lc = TAILQ_FIRST(&lcparent->cells); if (TAILQ_NEXT(lc, entry) == NULL) { TAILQ_REMOVE(&lcparent->cells, lc, entry); lc->parent = lcparent->parent; if (lc->parent == NULL) { lc->xoff = 0; lc->yoff = 0; *lcroot = lc; } else TAILQ_REPLACE(&lc->parent->cells, lcparent, lc, entry); layout_free_cell(lcparent); } } void layout_init(struct window *w, struct 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, w->sx, w->sy); } void layout_free(struct window *w) { layout_free_cell(w->layout_root); } /* Resize the entire layout after window resize. */ void layout_resize(struct window *w, u_int sx, u_int sy) { struct layout_cell *lc = w->layout_root; int xlimit, ylimit, xchange, ychange; /* * Adjust horizontally. Do not attempt to reduce the layout lower than * the minimum (more than the amount returned by layout_resize_check). * * This can mean that the window size is smaller than the total layout * size: redrawing this is handled at a higher level, but it does leave * a problem with growing the window size here: if the current size is * < the minimum, growing proportionately by adding to each pane is * wrong as it would keep the layout size larger than the window size. * Instead, spread the difference between the minimum and the new size * out proportionately - this should leave the layout fitting the new * window size. */ xchange = sx - w->sx; xlimit = layout_resize_check(lc, LAYOUT_LEFTRIGHT); if (xchange < 0 && xchange < -xlimit) xchange = -xlimit; if (xlimit == 0) { if (sx <= lc->sx) /* lc->sx is minimum possible */ xchange = 0; else xchange = sx - lc->sx; } if (xchange != 0) layout_resize_adjust(lc, LAYOUT_LEFTRIGHT, xchange); /* Adjust vertically in a similar fashion. */ ychange = sy - w->sy; ylimit = layout_resize_check(lc, LAYOUT_TOPBOTTOM); if (ychange < 0 && ychange < -ylimit) ychange = -ylimit; if (ylimit == 0) { if (sy <= lc->sy) /* lc->sy is minimum possible */ ychange = 0; else ychange = sy - lc->sy; } if (ychange != 0) layout_resize_adjust(lc, LAYOUT_TOPBOTTOM, ychange); /* Fix cell offsets. */ layout_fix_offsets(lc); layout_fix_panes(w, sx, sy); } /* Resize a 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); } /* Resize a single pane within the layout. */ void layout_resize_pane(struct window_pane *wp, enum layout_type type, int change) { struct layout_cell *lc, *lcparent; int needed, size; lc = wp->layout_cell; /* Find next parent of the same type. */ lcparent = lc->parent; while (lcparent != NULL && lcparent->type != type) { lc = lcparent; lcparent = lc->parent; } if (lcparent == NULL) return; /* If this is the last cell, move back one. */ if (lc == TAILQ_LAST(&lcparent->cells, layout_cells)) lc = TAILQ_PREV(lc, layout_cells, entry); /* Grow or shrink the cell. */ needed = change; while (needed != 0) { if (change > 0) { size = layout_resize_pane_grow(lc, type, needed); needed -= size; } else { size = layout_resize_pane_shrink(lc, type, needed); needed += size; } if (size == 0) /* no more change possible */ break; } /* Fix cell offsets. */ layout_fix_offsets(wp->window->layout_root); layout_fix_panes(wp->window, wp->window->sx, wp->window->sy); notify_window_layout_changed(wp->window); } /* Resize pane based on mouse events. */ void layout_resize_pane_mouse(struct client *c) { struct window *w; struct window_pane *wp; struct mouse_event *m = &c->tty.mouse; int pane_border; w = c->session->curw->window; pane_border = 0; if (m->event & MOUSE_EVENT_DRAG && m->flags & MOUSE_RESIZE_PANE) { TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->xoff + wp->sx == m->lx && wp->yoff <= 1 + m->ly && wp->yoff + wp->sy >= m->ly) { layout_resize_pane(wp, LAYOUT_LEFTRIGHT, m->x - m->lx); pane_border = 1; } if (wp->yoff + wp->sy == m->ly && wp->xoff <= 1 + m->lx && wp->xoff + wp->sx >= m->lx) { layout_resize_pane(wp, LAYOUT_TOPBOTTOM, m->y - m->ly); pane_border = 1; } } if (pane_border) server_redraw_window(w); } else if (~m->event & MOUSE_EVENT_UP) { TAILQ_FOREACH(wp, &w->panes, entry) { if ((wp->xoff + wp->sx == m->x && wp->yoff <= 1 + m->y && wp->yoff + wp->sy >= m->y) || (wp->yoff + wp->sy == m->y && wp->xoff <= 1 + m->x && wp->xoff + wp->sx >= m->x)) { pane_border = 1; } } } if (pane_border) m->flags |= MOUSE_RESIZE_PANE; else m->flags &= ~MOUSE_RESIZE_PANE; } /* Helper function to grow pane. */ int layout_resize_pane_grow( struct layout_cell *lc, enum layout_type type, int needed) { struct layout_cell *lcadd, *lcremove; u_int size; /* Growing. Always add to the current cell. */ lcadd = lc; /* Look towards the tail for a suitable cell for reduction. */ lcremove = TAILQ_NEXT(lc, entry); while (lcremove != NULL) { size = layout_resize_check(lcremove, type); if (size > 0) break; lcremove = TAILQ_NEXT(lcremove, entry); } /* If none found, look towards the head. */ if (lcremove == NULL) { lcremove = TAILQ_PREV(lc, layout_cells, entry); while (lcremove != NULL) { size = layout_resize_check(lcremove, type); if (size > 0) break; lcremove = TAILQ_PREV(lcremove, layout_cells, entry); } if (lcremove == NULL) return (0); } /* Change the cells. */ if (size > (u_int) needed) size = needed; layout_resize_adjust(lcadd, type, size); layout_resize_adjust(lcremove, type, -size); return (size); } /* Helper function to shrink pane. */ int layout_resize_pane_shrink( struct layout_cell *lc, enum layout_type type, int needed) { struct layout_cell *lcadd, *lcremove; u_int size; /* Shrinking. Find cell to remove from by walking towards head. */ lcremove = lc; do { size = layout_resize_check(lcremove, type); if (size != 0) break; lcremove = TAILQ_PREV(lcremove, layout_cells, entry); } while (lcremove != NULL); if (lcremove == NULL) return (0); /* And add onto the next cell (from the original cell). */ lcadd = TAILQ_NEXT(lc, entry); if (lcadd == NULL) return (0); /* Change the cells. */ if (size > (u_int) -needed) size = -needed; layout_resize_adjust(lcadd, type, size); layout_resize_adjust(lcremove, type, -size); return (size); } /* Assign window pane to newly split cell. */ void layout_assign_pane(struct layout_cell *lc, struct window_pane *wp) { layout_make_leaf(lc, wp); layout_fix_panes(wp->window, wp->window->sx, wp->window->sy); } /* * Split a pane into two. size is a hint, or -1 for default half/half * split. This must be followed by layout_assign_pane before much else happens! **/ struct layout_cell * layout_split_pane( struct window_pane *wp, enum layout_type type, int size, int insert_before) { struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2; u_int sx, sy, xoff, yoff, size1, size2; lc = wp->layout_cell; /* Copy the old cell size. */ sx = lc->sx; sy = lc->sy; xoff = lc->xoff; yoff = lc->yoff; /* Check there is enough space for the two new panes. */ switch (type) { case LAYOUT_LEFTRIGHT: if (sx < PANE_MINIMUM * 2 + 1) return (NULL); break; case LAYOUT_TOPBOTTOM: if (sy < PANE_MINIMUM * 2 + 1) return (NULL); break; default: fatalx("bad layout type"); } if (lc->parent != NULL && lc->parent->type == type) { /* * If the parent exists and is of the same type as the split, * create a new cell and insert it after this one. */ /* Create the new child cell. */ lcparent = lc->parent; lcnew = layout_create_cell(lcparent); if (insert_before) TAILQ_INSERT_BEFORE(lc, lcnew, entry); else TAILQ_INSERT_AFTER(&lcparent->cells, lc, lcnew, entry); } else { /* * Otherwise create a new parent and insert it. */ /* Create and insert the replacement parent. */ lcparent = layout_create_cell(lc->parent); layout_make_node(lcparent, type); layout_set_size(lcparent, sx, sy, xoff, yoff); if (lc->parent == NULL) wp->window->layout_root = lcparent; else TAILQ_REPLACE(&lc->parent->cells, lc, lcparent, entry); /* Insert the old cell. */ lc->parent = lcparent; TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry); /* Create the new child cell. */ lcnew = layout_create_cell(lcparent); if (insert_before) TAILQ_INSERT_HEAD(&lcparent->cells, lcnew, entry); else TAILQ_INSERT_TAIL(&lcparent->cells, lcnew, entry); } if (insert_before) { lc1 = lcnew; lc2 = lc; } else { lc1 = lc; lc2 = lcnew; } /* Set new cell sizes. size is the target size or -1 for middle split, * size1 is the size of the top/left and size2 the bottom/right. */ switch (type) { case LAYOUT_LEFTRIGHT: if (size < 0) size2 = ((sx + 1) / 2) - 1; else size2 = size; if (size2 < PANE_MINIMUM) size2 = PANE_MINIMUM; else if (size2 > sx - 2) size2 = sx - 2; size1 = sx - 1 - size2; layout_set_size(lc1, size1, sy, xoff, yoff); layout_set_size(lc2, size2, sy, xoff + lc1->sx + 1, yoff); break; case LAYOUT_TOPBOTTOM: if (size < 0) size2 = ((sy + 1) / 2) - 1; else size2 = size; if (size2 < PANE_MINIMUM) size2 = PANE_MINIMUM; else if (size2 > sy - 2) size2 = sy - 2; size1 = sy - 1 - size2; layout_set_size(lc1, sx, size1, xoff, yoff); layout_set_size(lc2, sx, size2, xoff, yoff + lc1->sy + 1); break; default: fatalx("bad layout type"); } /* Assign the panes. */ layout_make_leaf(lc, wp); return (lcnew); } /* Destroy the cell associated with a pane. */ void layout_close_pane(struct window_pane *wp) { /* Remove the cell. */ layout_destroy_cell(wp->layout_cell, &wp->window->layout_root); /* Fix pane offsets and sizes. */ if (wp->window->layout_root != NULL) { layout_fix_offsets(wp->window->layout_root); layout_fix_panes(wp->window, wp->window->sx, wp->window->sy); } notify_window_layout_changed(wp->window); } tmate-1.8.10/log.c000066400000000000000000000070201242461015400136360ustar00rootroot00000000000000/* $Id$ */ /* * 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" /* Log file, if needed. */ FILE *log_file; /* Debug level. */ int log_level = 0; void log_event_cb(int, const char *); void log_vwrite(const char *, va_list); __dead void log_vfatal(const char *, va_list); /* Log callback for libevent. */ void log_event_cb(unused int severity, const char *msg) { log_warnx("%s", msg); } /* Open logging to file. */ void log_open(int level, const char *path) { log_file = fopen(path, "w"); if (log_file == NULL) return; log_level = level; setlinebuf(log_file); event_set_log_callback(log_event_cb); tzset(); } /* Close logging. */ void log_close(void) { if (log_file != NULL) fclose(log_file); event_set_log_callback(NULL); } /* Write a log message. */ void log_vwrite(const char *msg, va_list ap) { char *fmt; if (log_file == NULL) return; if (asprintf(&fmt, "%s\n", msg) == -1) exit(1); if (vfprintf(log_file, fmt, ap) == -1) exit(1); fflush(log_file); free(fmt); } /* Log a warning with error string. */ void printflike1 log_warn(const char *msg, ...) { va_list ap; char *fmt; va_start(ap, msg); if (asprintf(&fmt, "%s: %s", msg, strerror(errno)) == -1) exit(1); log_vwrite(fmt, ap); free(fmt); va_end(ap); } /* Log a warning. */ void printflike1 log_warnx(const char *msg, ...) { va_list ap; va_start(ap, msg); log_vwrite(msg, ap); va_end(ap); } /* Log an informational message. */ void printflike1 log_info(const char *msg, ...) { va_list ap; if (log_level > -1) { va_start(ap, msg); log_vwrite(msg, ap); va_end(ap); } } /* Log a debug message. */ void printflike1 log_debug(const char *msg, ...) { va_list ap; if (log_level > 0) { va_start(ap, msg); log_vwrite(msg, ap); va_end(ap); } } /* Log a debug message at level 2. */ void printflike1 log_debug2(const char *msg, ...) { va_list ap; if (log_level > 1) { va_start(ap, msg); log_vwrite(msg, ap); va_end(ap); } } /* Log a critical error, with error string if necessary, and die. */ __dead void log_vfatal(const char *msg, va_list ap) { char *fmt; if (errno != 0) { if (asprintf(&fmt, "fatal: %s: %s", msg, strerror(errno)) == -1) exit(1); log_vwrite(fmt, ap); } else { if (asprintf(&fmt, "fatal: %s", msg) == -1) exit(1); log_vwrite(fmt, ap); } free(fmt); exit(1); } /* Log a critical error, with error string, and die. */ __dead void printflike1 log_fatal(const char *msg, ...) { va_list ap; va_start(ap, msg); log_vfatal(msg, ap); } /* Log a critical error and die. */ __dead void printflike1 log_fatalx(const char *msg, ...) { va_list ap; errno = 0; va_start(ap, msg); log_vfatal(msg, ap); } tmate-1.8.10/mode-key.c000066400000000000000000000536541242461015400146050ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Mode keys. These are the key bindings used when editing (status prompt), and * in the modes. They are split into two sets of three tables, one set of three * for vi and the other for emacs key bindings. The three tables are for * editing, for menu-like modes (choice, more), and for copy modes (copy, * scroll). * * The fixed tables of struct mode_key_entry below are the defaults: they are * built into a tree of struct mode_key_binding by mode_key_init_trees, which * can then be modified. * * vi command mode is handled by having a mode flag in the struct which allows * two sets of bindings to be swapped between. A couple of editing commands * (MODEKEYEDIT_SWITCHMODE, MODEKEYEDIT_SWITCHMODEAPPEND, * MODEKEYEDIT_SWITCHMODEAPPENDLINE, and MODEKEYEDIT_SWITCHMODEBEGINLINE) * are special-cased to do this. */ /* Edit keys command strings. */ const struct mode_key_cmdstr mode_key_cmdstr_edit[] = { { MODEKEYEDIT_BACKSPACE, "backspace" }, { MODEKEYEDIT_CANCEL, "cancel" }, { MODEKEYEDIT_COMPLETE, "complete" }, { MODEKEYEDIT_CURSORLEFT, "cursor-left" }, { MODEKEYEDIT_CURSORRIGHT, "cursor-right" }, { MODEKEYEDIT_DELETE, "delete" }, { MODEKEYEDIT_DELETELINE, "delete-line" }, { MODEKEYEDIT_DELETETOENDOFLINE, "delete-end-of-line" }, { MODEKEYEDIT_DELETEWORD, "delete-word" }, { MODEKEYEDIT_ENDOFLINE, "end-of-line" }, { MODEKEYEDIT_ENTER, "enter" }, { MODEKEYEDIT_HISTORYDOWN, "history-down" }, { MODEKEYEDIT_HISTORYUP, "history-up" }, { MODEKEYEDIT_NEXTSPACE, "next-space" }, { MODEKEYEDIT_NEXTSPACEEND, "next-space-end" }, { MODEKEYEDIT_NEXTWORD, "next-word" }, { MODEKEYEDIT_NEXTWORDEND, "next-word-end" }, { MODEKEYEDIT_PASTE, "paste" }, { MODEKEYEDIT_PREVIOUSSPACE, "previous-space" }, { MODEKEYEDIT_PREVIOUSWORD, "previous-word" }, { MODEKEYEDIT_STARTOFLINE, "start-of-line" }, { MODEKEYEDIT_SWITCHMODE, "switch-mode" }, { MODEKEYEDIT_SWITCHMODEAPPEND, "switch-mode-append" }, { MODEKEYEDIT_SWITCHMODEAPPENDLINE, "switch-mode-append-line" }, { MODEKEYEDIT_SWITCHMODEBEGINLINE, "switch-mode-begin-line" }, { MODEKEYEDIT_TRANSPOSECHARS, "transpose-chars" }, { 0, NULL } }; /* Choice keys command strings. */ const struct mode_key_cmdstr mode_key_cmdstr_choice[] = { { MODEKEYCHOICE_BACKSPACE, "backspace" }, { MODEKEYCHOICE_CANCEL, "cancel" }, { MODEKEYCHOICE_CHOOSE, "choose" }, { MODEKEYCHOICE_DOWN, "down" }, { MODEKEYCHOICE_PAGEDOWN, "page-down" }, { MODEKEYCHOICE_PAGEUP, "page-up" }, { MODEKEYCHOICE_SCROLLDOWN, "scroll-down" }, { MODEKEYCHOICE_SCROLLUP, "scroll-up" }, { MODEKEYCHOICE_STARTNUMBERPREFIX, "start-number-prefix" }, { MODEKEYCHOICE_TREE_COLLAPSE, "tree-collapse" }, { MODEKEYCHOICE_TREE_COLLAPSE_ALL, "tree-collapse-all" }, { MODEKEYCHOICE_TREE_EXPAND, "tree-expand" }, { MODEKEYCHOICE_TREE_EXPAND_ALL, "tree-expand-all" }, { MODEKEYCHOICE_TREE_TOGGLE, "tree-toggle" }, { MODEKEYCHOICE_UP, "up" }, { 0, NULL } }; /* Copy keys command strings. */ const struct mode_key_cmdstr mode_key_cmdstr_copy[] = { { MODEKEYCOPY_BACKTOINDENTATION, "back-to-indentation" }, { MODEKEYCOPY_BOTTOMLINE, "bottom-line" }, { MODEKEYCOPY_CANCEL, "cancel" }, { MODEKEYCOPY_CLEARSELECTION, "clear-selection" }, { MODEKEYCOPY_COPYPIPE, "copy-pipe" }, { MODEKEYCOPY_COPYLINE, "copy-line" }, { MODEKEYCOPY_COPYENDOFLINE, "copy-end-of-line" }, { MODEKEYCOPY_COPYSELECTION, "copy-selection" }, { MODEKEYCOPY_DOWN, "cursor-down" }, { MODEKEYCOPY_ENDOFLINE, "end-of-line" }, { MODEKEYCOPY_GOTOLINE, "goto-line" }, { MODEKEYCOPY_HALFPAGEDOWN, "halfpage-down" }, { MODEKEYCOPY_HALFPAGEUP, "halfpage-up" }, { MODEKEYCOPY_HISTORYBOTTOM, "history-bottom" }, { MODEKEYCOPY_HISTORYTOP, "history-top" }, { MODEKEYCOPY_JUMP, "jump-forward" }, { MODEKEYCOPY_JUMPAGAIN, "jump-again" }, { MODEKEYCOPY_JUMPREVERSE, "jump-reverse" }, { MODEKEYCOPY_JUMPBACK, "jump-backward" }, { MODEKEYCOPY_JUMPTO, "jump-to-forward" }, { MODEKEYCOPY_JUMPTOBACK, "jump-to-backward" }, { MODEKEYCOPY_LEFT, "cursor-left" }, { MODEKEYCOPY_RECTANGLETOGGLE, "rectangle-toggle" }, { MODEKEYCOPY_MIDDLELINE, "middle-line" }, { MODEKEYCOPY_NEXTPAGE, "page-down" }, { MODEKEYCOPY_NEXTSPACE, "next-space" }, { MODEKEYCOPY_NEXTSPACEEND, "next-space-end" }, { MODEKEYCOPY_NEXTWORD, "next-word" }, { MODEKEYCOPY_NEXTWORDEND, "next-word-end" }, { MODEKEYCOPY_PREVIOUSPAGE, "page-up" }, { MODEKEYCOPY_PREVIOUSSPACE, "previous-space" }, { MODEKEYCOPY_PREVIOUSWORD, "previous-word" }, { MODEKEYCOPY_RIGHT, "cursor-right" }, { MODEKEYCOPY_SCROLLDOWN, "scroll-down" }, { MODEKEYCOPY_SCROLLUP, "scroll-up" }, { MODEKEYCOPY_SEARCHAGAIN, "search-again" }, { MODEKEYCOPY_SEARCHDOWN, "search-forward" }, { MODEKEYCOPY_SEARCHREVERSE, "search-reverse" }, { MODEKEYCOPY_SEARCHUP, "search-backward" }, { MODEKEYCOPY_SELECTLINE, "select-line" }, { MODEKEYCOPY_STARTNUMBERPREFIX, "start-number-prefix" }, { MODEKEYCOPY_STARTOFLINE, "start-of-line" }, { MODEKEYCOPY_STARTSELECTION, "begin-selection" }, { MODEKEYCOPY_TOPLINE, "top-line" }, { MODEKEYCOPY_UP, "cursor-up" }, { 0, NULL } }; /* vi editing keys. */ const struct mode_key_entry mode_key_vi_edit[] = { { '\003' /* C-c */, 0, MODEKEYEDIT_CANCEL }, { '\010' /* C-h */, 0, MODEKEYEDIT_BACKSPACE }, { '\011' /* Tab */, 0, MODEKEYEDIT_COMPLETE }, { '\025' /* C-u */, 0, MODEKEYEDIT_DELETELINE }, { '\027' /* C-w */, 0, MODEKEYEDIT_DELETEWORD }, { '\033' /* Escape */, 0, MODEKEYEDIT_SWITCHMODE }, { '\r', 0, MODEKEYEDIT_ENTER }, { KEYC_BSPACE, 0, MODEKEYEDIT_BACKSPACE }, { KEYC_DC, 0, MODEKEYEDIT_DELETE }, { KEYC_DOWN, 0, MODEKEYEDIT_HISTORYDOWN }, { KEYC_LEFT, 0, MODEKEYEDIT_CURSORLEFT }, { KEYC_RIGHT, 0, MODEKEYEDIT_CURSORRIGHT }, { KEYC_UP, 0, MODEKEYEDIT_HISTORYUP }, { KEYC_HOME, 0, MODEKEYEDIT_STARTOFLINE }, { KEYC_END, 0, MODEKEYEDIT_ENDOFLINE }, { '$', 1, MODEKEYEDIT_ENDOFLINE }, { '0', 1, MODEKEYEDIT_STARTOFLINE }, { 'A', 1, MODEKEYEDIT_SWITCHMODEAPPENDLINE }, { 'B', 1, MODEKEYEDIT_PREVIOUSSPACE }, { 'D', 1, MODEKEYEDIT_DELETETOENDOFLINE }, { 'E', 1, MODEKEYEDIT_NEXTSPACEEND }, { 'I', 1, MODEKEYEDIT_SWITCHMODEBEGINLINE }, { 'W', 1, MODEKEYEDIT_NEXTSPACE }, { 'X', 1, MODEKEYEDIT_BACKSPACE }, { '\003' /* C-c */, 1, MODEKEYEDIT_CANCEL }, { '\010' /* C-h */, 1, MODEKEYEDIT_BACKSPACE }, { '\r', 1, MODEKEYEDIT_ENTER }, { '^', 1, MODEKEYEDIT_STARTOFLINE }, { 'a', 1, MODEKEYEDIT_SWITCHMODEAPPEND }, { 'b', 1, MODEKEYEDIT_PREVIOUSWORD }, { 'd', 1, MODEKEYEDIT_DELETELINE }, { 'e', 1, MODEKEYEDIT_NEXTWORDEND }, { 'h', 1, MODEKEYEDIT_CURSORLEFT }, { 'i', 1, MODEKEYEDIT_SWITCHMODE }, { 'j', 1, MODEKEYEDIT_HISTORYDOWN }, { 'k', 1, MODEKEYEDIT_HISTORYUP }, { 'l', 1, MODEKEYEDIT_CURSORRIGHT }, { 'p', 1, MODEKEYEDIT_PASTE }, { 'w', 1, MODEKEYEDIT_NEXTWORD }, { 'x', 1, MODEKEYEDIT_DELETE }, { KEYC_BSPACE, 1, MODEKEYEDIT_BACKSPACE }, { KEYC_DC, 1, MODEKEYEDIT_DELETE }, { KEYC_DOWN, 1, MODEKEYEDIT_HISTORYDOWN }, { KEYC_LEFT, 1, MODEKEYEDIT_CURSORLEFT }, { KEYC_RIGHT, 1, MODEKEYEDIT_CURSORRIGHT }, { KEYC_UP, 1, MODEKEYEDIT_HISTORYUP }, { 0, -1, 0 } }; struct mode_key_tree mode_key_tree_vi_edit; /* vi choice selection keys. */ const struct mode_key_entry mode_key_vi_choice[] = { { '0' | KEYC_ESCAPE, 0, MODEKEYCHOICE_STARTNUMBERPREFIX }, { '1' | KEYC_ESCAPE, 0, MODEKEYCHOICE_STARTNUMBERPREFIX }, { '2' | KEYC_ESCAPE, 0, MODEKEYCHOICE_STARTNUMBERPREFIX }, { '3' | KEYC_ESCAPE, 0, MODEKEYCHOICE_STARTNUMBERPREFIX }, { '4' | KEYC_ESCAPE, 0, MODEKEYCHOICE_STARTNUMBERPREFIX }, { '5' | KEYC_ESCAPE, 0, MODEKEYCHOICE_STARTNUMBERPREFIX }, { '6' | KEYC_ESCAPE, 0, MODEKEYCHOICE_STARTNUMBERPREFIX }, { '7' | KEYC_ESCAPE, 0, MODEKEYCHOICE_STARTNUMBERPREFIX }, { '8' | KEYC_ESCAPE, 0, MODEKEYCHOICE_STARTNUMBERPREFIX }, { '9' | KEYC_ESCAPE, 0, MODEKEYCHOICE_STARTNUMBERPREFIX }, { '\002' /* C-b */, 0, MODEKEYCHOICE_PAGEUP }, { '\003' /* C-c */, 0, MODEKEYCHOICE_CANCEL }, { '\005' /* C-e */, 0, MODEKEYCHOICE_SCROLLDOWN }, { '\006' /* C-f */, 0, MODEKEYCHOICE_PAGEDOWN }, { '\031' /* C-y */, 0, MODEKEYCHOICE_SCROLLUP }, { '\r', 0, MODEKEYCHOICE_CHOOSE }, { 'j', 0, MODEKEYCHOICE_DOWN }, { 'k', 0, MODEKEYCHOICE_UP }, { 'q', 0, MODEKEYCHOICE_CANCEL }, { KEYC_BSPACE, 0, MODEKEYCHOICE_BACKSPACE }, { KEYC_DOWN | KEYC_CTRL, 0, MODEKEYCHOICE_SCROLLDOWN }, { KEYC_DOWN, 0, MODEKEYCHOICE_DOWN }, { KEYC_NPAGE, 0, MODEKEYCHOICE_PAGEDOWN }, { KEYC_PPAGE, 0, MODEKEYCHOICE_PAGEUP }, { KEYC_UP | KEYC_CTRL, 0, MODEKEYCHOICE_SCROLLUP }, { KEYC_UP, 0, MODEKEYCHOICE_UP }, { ' ', 0, MODEKEYCHOICE_TREE_TOGGLE }, { KEYC_LEFT, 0, MODEKEYCHOICE_TREE_COLLAPSE }, { KEYC_RIGHT, 0, MODEKEYCHOICE_TREE_EXPAND }, { KEYC_LEFT | KEYC_CTRL, 0, MODEKEYCHOICE_TREE_COLLAPSE_ALL }, { KEYC_RIGHT | KEYC_CTRL, 0, MODEKEYCHOICE_TREE_EXPAND_ALL }, { 0, -1, 0 } }; struct mode_key_tree mode_key_tree_vi_choice; /* vi copy mode keys. */ const struct mode_key_entry mode_key_vi_copy[] = { { ' ', 0, MODEKEYCOPY_STARTSELECTION }, { '$', 0, MODEKEYCOPY_ENDOFLINE }, { ',', 0, MODEKEYCOPY_JUMPREVERSE }, { ';', 0, MODEKEYCOPY_JUMPAGAIN }, { '/', 0, MODEKEYCOPY_SEARCHDOWN }, { '0', 0, MODEKEYCOPY_STARTOFLINE }, { '1', 0, MODEKEYCOPY_STARTNUMBERPREFIX }, { '2', 0, MODEKEYCOPY_STARTNUMBERPREFIX }, { '3', 0, MODEKEYCOPY_STARTNUMBERPREFIX }, { '4', 0, MODEKEYCOPY_STARTNUMBERPREFIX }, { '5', 0, MODEKEYCOPY_STARTNUMBERPREFIX }, { '6', 0, MODEKEYCOPY_STARTNUMBERPREFIX }, { '7', 0, MODEKEYCOPY_STARTNUMBERPREFIX }, { '8', 0, MODEKEYCOPY_STARTNUMBERPREFIX }, { '9', 0, MODEKEYCOPY_STARTNUMBERPREFIX }, { ':', 0, MODEKEYCOPY_GOTOLINE }, { '?', 0, MODEKEYCOPY_SEARCHUP }, { 'B', 0, MODEKEYCOPY_PREVIOUSSPACE }, { 'D', 0, MODEKEYCOPY_COPYENDOFLINE }, { 'E', 0, MODEKEYCOPY_NEXTSPACEEND }, { 'F', 0, MODEKEYCOPY_JUMPBACK }, { 'G', 0, MODEKEYCOPY_HISTORYBOTTOM }, { 'H', 0, MODEKEYCOPY_TOPLINE }, { 'J', 0, MODEKEYCOPY_SCROLLDOWN }, { 'K', 0, MODEKEYCOPY_SCROLLUP }, { 'L', 0, MODEKEYCOPY_BOTTOMLINE }, { 'M', 0, MODEKEYCOPY_MIDDLELINE }, { 'N', 0, MODEKEYCOPY_SEARCHREVERSE }, { 'T', 0, MODEKEYCOPY_JUMPTOBACK }, { 'W', 0, MODEKEYCOPY_NEXTSPACE }, { '\002' /* C-b */, 0, MODEKEYCOPY_PREVIOUSPAGE }, { '\003' /* C-c */, 0, MODEKEYCOPY_CANCEL }, { '\004' /* C-d */, 0, MODEKEYCOPY_HALFPAGEDOWN }, { '\005' /* C-e */, 0, MODEKEYCOPY_SCROLLDOWN }, { '\006' /* C-f */, 0, MODEKEYCOPY_NEXTPAGE }, { '\010' /* C-h */, 0, MODEKEYCOPY_LEFT }, { '\025' /* C-u */, 0, MODEKEYCOPY_HALFPAGEUP }, { '\031' /* C-y */, 0, MODEKEYCOPY_SCROLLUP }, { '\033' /* Escape */, 0, MODEKEYCOPY_CLEARSELECTION }, { '\r', 0, MODEKEYCOPY_COPYSELECTION }, { '^', 0, MODEKEYCOPY_BACKTOINDENTATION }, { 'b', 0, MODEKEYCOPY_PREVIOUSWORD }, { 'e', 0, MODEKEYCOPY_NEXTWORDEND }, { 'f', 0, MODEKEYCOPY_JUMP }, { 'g', 0, MODEKEYCOPY_HISTORYTOP }, { 'h', 0, MODEKEYCOPY_LEFT }, { 'j', 0, MODEKEYCOPY_DOWN }, { 'k', 0, MODEKEYCOPY_UP }, { 'l', 0, MODEKEYCOPY_RIGHT }, { 'n', 0, MODEKEYCOPY_SEARCHAGAIN }, { 't', 0, MODEKEYCOPY_JUMPTO }, { 'q', 0, MODEKEYCOPY_CANCEL }, { 'v', 0, MODEKEYCOPY_RECTANGLETOGGLE }, { 'w', 0, MODEKEYCOPY_NEXTWORD }, { KEYC_BSPACE, 0, MODEKEYCOPY_LEFT }, { KEYC_DOWN | KEYC_CTRL, 0, MODEKEYCOPY_SCROLLDOWN }, { KEYC_DOWN, 0, MODEKEYCOPY_DOWN }, { KEYC_LEFT, 0, MODEKEYCOPY_LEFT }, { KEYC_NPAGE, 0, MODEKEYCOPY_NEXTPAGE }, { KEYC_PPAGE, 0, MODEKEYCOPY_PREVIOUSPAGE }, { KEYC_RIGHT, 0, MODEKEYCOPY_RIGHT }, { KEYC_UP | KEYC_CTRL, 0, MODEKEYCOPY_SCROLLUP }, { KEYC_UP, 0, MODEKEYCOPY_UP }, { 0, -1, 0 } }; struct mode_key_tree mode_key_tree_vi_copy; /* emacs editing keys. */ const struct mode_key_entry mode_key_emacs_edit[] = { { '\001' /* C-a */, 0, MODEKEYEDIT_STARTOFLINE }, { '\002' /* C-b */, 0, MODEKEYEDIT_CURSORLEFT }, { '\003' /* C-c */, 0, MODEKEYEDIT_CANCEL }, { '\004' /* C-d */, 0, MODEKEYEDIT_DELETE }, { '\005' /* C-e */, 0, MODEKEYEDIT_ENDOFLINE }, { '\006' /* C-f */, 0, MODEKEYEDIT_CURSORRIGHT }, { '\010' /* C-H */, 0, MODEKEYEDIT_BACKSPACE }, { '\011' /* Tab */, 0, MODEKEYEDIT_COMPLETE }, { '\013' /* C-k */, 0, MODEKEYEDIT_DELETETOENDOFLINE }, { '\016' /* C-n */, 0, MODEKEYEDIT_HISTORYDOWN }, { '\020' /* C-p */, 0, MODEKEYEDIT_HISTORYUP }, { '\024' /* C-t */, 0, MODEKEYEDIT_TRANSPOSECHARS }, { '\025' /* C-u */, 0, MODEKEYEDIT_DELETELINE }, { '\027' /* C-w */, 0, MODEKEYEDIT_DELETEWORD }, { '\031' /* C-y */, 0, MODEKEYEDIT_PASTE }, { '\033' /* Escape */, 0, MODEKEYEDIT_CANCEL }, { '\r', 0, MODEKEYEDIT_ENTER }, { 'b' | KEYC_ESCAPE, 0, MODEKEYEDIT_PREVIOUSWORD }, { 'f' | KEYC_ESCAPE, 0, MODEKEYEDIT_NEXTWORDEND }, { 'm' | KEYC_ESCAPE, 0, MODEKEYEDIT_STARTOFLINE }, { KEYC_BSPACE, 0, MODEKEYEDIT_BACKSPACE }, { KEYC_DC, 0, MODEKEYEDIT_DELETE }, { KEYC_DOWN, 0, MODEKEYEDIT_HISTORYDOWN }, { KEYC_LEFT, 0, MODEKEYEDIT_CURSORLEFT }, { KEYC_RIGHT, 0, MODEKEYEDIT_CURSORRIGHT }, { KEYC_UP, 0, MODEKEYEDIT_HISTORYUP }, { KEYC_HOME, 0, MODEKEYEDIT_STARTOFLINE }, { KEYC_END, 0, MODEKEYEDIT_ENDOFLINE }, { 0, -1, 0 } }; struct mode_key_tree mode_key_tree_emacs_edit; /* emacs choice selection keys. */ const struct mode_key_entry mode_key_emacs_choice[] = { { '0' | KEYC_ESCAPE, 0, MODEKEYCHOICE_STARTNUMBERPREFIX }, { '1' | KEYC_ESCAPE, 0, MODEKEYCHOICE_STARTNUMBERPREFIX }, { '2' | KEYC_ESCAPE, 0, MODEKEYCHOICE_STARTNUMBERPREFIX }, { '3' | KEYC_ESCAPE, 0, MODEKEYCHOICE_STARTNUMBERPREFIX }, { '4' | KEYC_ESCAPE, 0, MODEKEYCHOICE_STARTNUMBERPREFIX }, { '5' | KEYC_ESCAPE, 0, MODEKEYCHOICE_STARTNUMBERPREFIX }, { '6' | KEYC_ESCAPE, 0, MODEKEYCHOICE_STARTNUMBERPREFIX }, { '7' | KEYC_ESCAPE, 0, MODEKEYCHOICE_STARTNUMBERPREFIX }, { '8' | KEYC_ESCAPE, 0, MODEKEYCHOICE_STARTNUMBERPREFIX }, { '9' | KEYC_ESCAPE, 0, MODEKEYCHOICE_STARTNUMBERPREFIX }, { '\003' /* C-c */, 0, MODEKEYCHOICE_CANCEL }, { '\016' /* C-n */, 0, MODEKEYCHOICE_DOWN }, { '\020' /* C-p */, 0, MODEKEYCHOICE_UP }, { '\026' /* C-v */, 0, MODEKEYCHOICE_PAGEDOWN }, { '\033' /* Escape */, 0, MODEKEYCHOICE_CANCEL }, { '\r', 0, MODEKEYCHOICE_CHOOSE }, { 'q', 0, MODEKEYCHOICE_CANCEL }, { 'v' | KEYC_ESCAPE, 0, MODEKEYCHOICE_PAGEUP }, { KEYC_BSPACE, 0, MODEKEYCHOICE_BACKSPACE }, { KEYC_DOWN | KEYC_CTRL, 0, MODEKEYCHOICE_SCROLLDOWN }, { KEYC_DOWN, 0, MODEKEYCHOICE_DOWN }, { KEYC_NPAGE, 0, MODEKEYCHOICE_PAGEDOWN }, { KEYC_PPAGE, 0, MODEKEYCHOICE_PAGEUP }, { KEYC_UP | KEYC_CTRL, 0, MODEKEYCHOICE_SCROLLUP }, { KEYC_UP, 0, MODEKEYCHOICE_UP }, { ' ', 0, MODEKEYCHOICE_TREE_TOGGLE }, { KEYC_LEFT, 0, MODEKEYCHOICE_TREE_COLLAPSE }, { KEYC_RIGHT, 0, MODEKEYCHOICE_TREE_EXPAND }, { KEYC_LEFT | KEYC_CTRL, 0, MODEKEYCHOICE_TREE_COLLAPSE_ALL }, { KEYC_RIGHT | KEYC_CTRL, 0, MODEKEYCHOICE_TREE_EXPAND_ALL }, { 0, -1, 0 } }; struct mode_key_tree mode_key_tree_emacs_choice; /* emacs copy mode keys. */ const struct mode_key_entry mode_key_emacs_copy[] = { { ' ', 0, MODEKEYCOPY_NEXTPAGE }, { ',', 0, MODEKEYCOPY_JUMPREVERSE }, { ';', 0, MODEKEYCOPY_JUMPAGAIN }, { '1' | KEYC_ESCAPE, 0, MODEKEYCOPY_STARTNUMBERPREFIX }, { '2' | KEYC_ESCAPE, 0, MODEKEYCOPY_STARTNUMBERPREFIX }, { '3' | KEYC_ESCAPE, 0, MODEKEYCOPY_STARTNUMBERPREFIX }, { '4' | KEYC_ESCAPE, 0, MODEKEYCOPY_STARTNUMBERPREFIX }, { '5' | KEYC_ESCAPE, 0, MODEKEYCOPY_STARTNUMBERPREFIX }, { '6' | KEYC_ESCAPE, 0, MODEKEYCOPY_STARTNUMBERPREFIX }, { '7' | KEYC_ESCAPE, 0, MODEKEYCOPY_STARTNUMBERPREFIX }, { '8' | KEYC_ESCAPE, 0, MODEKEYCOPY_STARTNUMBERPREFIX }, { '9' | KEYC_ESCAPE, 0, MODEKEYCOPY_STARTNUMBERPREFIX }, { '<' | KEYC_ESCAPE, 0, MODEKEYCOPY_HISTORYTOP }, { '>' | KEYC_ESCAPE, 0, MODEKEYCOPY_HISTORYBOTTOM }, { 'F', 0, MODEKEYCOPY_JUMPBACK }, { 'N', 0, MODEKEYCOPY_SEARCHREVERSE }, { 'R' | KEYC_ESCAPE, 0, MODEKEYCOPY_TOPLINE }, { 'R', 0, MODEKEYCOPY_RECTANGLETOGGLE }, { 'T', 0, MODEKEYCOPY_JUMPTOBACK }, { '\000' /* C-Space */, 0, MODEKEYCOPY_STARTSELECTION }, { '\001' /* C-a */, 0, MODEKEYCOPY_STARTOFLINE }, { '\002' /* C-b */, 0, MODEKEYCOPY_LEFT }, { '\003' /* C-c */, 0, MODEKEYCOPY_CANCEL }, { '\005' /* C-e */, 0, MODEKEYCOPY_ENDOFLINE }, { '\006' /* C-f */, 0, MODEKEYCOPY_RIGHT }, { '\007' /* C-g */, 0, MODEKEYCOPY_CLEARSELECTION }, { '\013' /* C-k */, 0, MODEKEYCOPY_COPYENDOFLINE }, { '\016' /* C-n */, 0, MODEKEYCOPY_DOWN }, { '\020' /* C-p */, 0, MODEKEYCOPY_UP }, { '\022' /* C-r */, 0, MODEKEYCOPY_SEARCHUP }, { '\023' /* C-s */, 0, MODEKEYCOPY_SEARCHDOWN }, { '\026' /* C-v */, 0, MODEKEYCOPY_NEXTPAGE }, { '\027' /* C-w */, 0, MODEKEYCOPY_COPYSELECTION }, { '\033' /* Escape */, 0, MODEKEYCOPY_CANCEL }, { 'b' | KEYC_ESCAPE, 0, MODEKEYCOPY_PREVIOUSWORD }, { 'f', 0, MODEKEYCOPY_JUMP }, { 'f' | KEYC_ESCAPE, 0, MODEKEYCOPY_NEXTWORDEND }, { 'g', 0, MODEKEYCOPY_GOTOLINE }, { 'm' | KEYC_ESCAPE, 0, MODEKEYCOPY_BACKTOINDENTATION }, { 'n', 0, MODEKEYCOPY_SEARCHAGAIN }, { 'q', 0, MODEKEYCOPY_CANCEL }, { 'r' | KEYC_ESCAPE, 0, MODEKEYCOPY_MIDDLELINE }, { 't', 0, MODEKEYCOPY_JUMPTO }, { 'v' | KEYC_ESCAPE, 0, MODEKEYCOPY_PREVIOUSPAGE }, { 'w' | KEYC_ESCAPE, 0, MODEKEYCOPY_COPYSELECTION }, { KEYC_DOWN | KEYC_CTRL, 0, MODEKEYCOPY_SCROLLDOWN }, { KEYC_DOWN | KEYC_ESCAPE, 0, MODEKEYCOPY_HALFPAGEDOWN }, { KEYC_DOWN, 0, MODEKEYCOPY_DOWN }, { KEYC_LEFT, 0, MODEKEYCOPY_LEFT }, { KEYC_NPAGE, 0, MODEKEYCOPY_NEXTPAGE }, { KEYC_PPAGE, 0, MODEKEYCOPY_PREVIOUSPAGE }, { KEYC_RIGHT, 0, MODEKEYCOPY_RIGHT }, { KEYC_UP | KEYC_CTRL, 0, MODEKEYCOPY_SCROLLUP }, { KEYC_UP | KEYC_ESCAPE, 0, MODEKEYCOPY_HALFPAGEUP }, { KEYC_UP, 0, MODEKEYCOPY_UP }, { 0, -1, 0 } }; struct mode_key_tree mode_key_tree_emacs_copy; /* Table mapping key table names to default settings and trees. */ const struct mode_key_table mode_key_tables[] = { { "vi-edit", mode_key_cmdstr_edit, &mode_key_tree_vi_edit, mode_key_vi_edit }, { "vi-choice", mode_key_cmdstr_choice, &mode_key_tree_vi_choice, mode_key_vi_choice }, { "vi-copy", mode_key_cmdstr_copy, &mode_key_tree_vi_copy, mode_key_vi_copy }, { "emacs-edit", mode_key_cmdstr_edit, &mode_key_tree_emacs_edit, mode_key_emacs_edit }, { "emacs-choice", mode_key_cmdstr_choice, &mode_key_tree_emacs_choice, mode_key_emacs_choice }, { "emacs-copy", mode_key_cmdstr_copy, &mode_key_tree_emacs_copy, mode_key_emacs_copy }, { NULL, NULL, NULL, NULL } }; RB_GENERATE(mode_key_tree, mode_key_binding, entry, mode_key_cmp); int mode_key_cmp(struct mode_key_binding *mbind1, struct mode_key_binding *mbind2) { if (mbind1->mode != mbind2->mode) return (mbind1->mode - mbind2->mode); return (mbind1->key - mbind2->key); } const char * mode_key_tostring(const struct mode_key_cmdstr *cmdstr, enum mode_key_cmd cmd) { for (; cmdstr->name != NULL; cmdstr++) { if (cmdstr->cmd == cmd) return (cmdstr->name); } return (NULL); } enum mode_key_cmd mode_key_fromstring(const struct mode_key_cmdstr *cmdstr, const char *name) { for (; cmdstr->name != NULL; cmdstr++) { if (strcasecmp(cmdstr->name, name) == 0) return (cmdstr->cmd); } return (MODEKEY_NONE); } const struct mode_key_table * mode_key_findtable(const char *name) { const struct mode_key_table *mtab; for (mtab = mode_key_tables; mtab->name != NULL; mtab++) { if (strcasecmp(name, mtab->name) == 0) return (mtab); } return (NULL); } void mode_key_init_trees(void) { const struct mode_key_table *mtab; const struct mode_key_entry *ment; struct mode_key_binding *mbind; for (mtab = mode_key_tables; mtab->name != NULL; mtab++) { RB_INIT(mtab->tree); for (ment = mtab->table; ment->mode != -1; ment++) { mbind = xmalloc(sizeof *mbind); mbind->key = ment->key; mbind->mode = ment->mode; mbind->cmd = ment->cmd; mbind->arg = NULL; RB_INSERT(mode_key_tree, mtab->tree, mbind); } } } void mode_key_init(struct mode_key_data *mdata, struct mode_key_tree *mtree) { mdata->tree = mtree; mdata->mode = 0; } enum mode_key_cmd mode_key_lookup(struct mode_key_data *mdata, int key, const char **arg) { struct mode_key_binding *mbind, mtmp; mtmp.key = key; mtmp.mode = mdata->mode; if ((mbind = RB_FIND(mode_key_tree, mdata->tree, &mtmp)) == NULL) { if (mdata->mode != 0) return (MODEKEY_NONE); return (MODEKEY_OTHER); } switch (mbind->cmd) { case MODEKEYEDIT_SWITCHMODE: case MODEKEYEDIT_SWITCHMODEAPPEND: case MODEKEYEDIT_SWITCHMODEAPPENDLINE: case MODEKEYEDIT_SWITCHMODEBEGINLINE: mdata->mode = 1 - mdata->mode; /* FALLTHROUGH */ default: if (arg != NULL) *arg = mbind->arg; return (mbind->cmd); } } tmate-1.8.10/names.c000066400000000000000000000062251242461015400141660ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "tmux.h" void window_name_callback(unused int, unused short, void *); char *parse_window_name(const char *); void queue_window_name(struct window *w) { struct timeval tv; tv.tv_sec = 0; tv.tv_usec = NAME_INTERVAL * 1000L; if (event_initialized(&w->name_timer)) evtimer_del(&w->name_timer); evtimer_set(&w->name_timer, window_name_callback, w); evtimer_add(&w->name_timer, &tv); } void window_name_callback(unused int fd, unused short events, void *data) { struct window *w = data; char *name, *wname; if (w->active == NULL) return; if (!options_get_number(&w->options, "automatic-rename")) { if (event_initialized(&w->name_timer)) event_del(&w->name_timer); return; } queue_window_name(w); if (w->active->screen != &w->active->base) name = NULL; else name = osdep_get_name(w->active->fd, w->active->tty); if (name == NULL) wname = default_window_name(w); else { /* * If tmux is using the default command, it will be a login * shell and argv[0] may have a - prefix. Remove this if it is * present. Ick. */ if (w->active->cmd != NULL && *w->active->cmd == '\0' && name != NULL && name[0] == '-' && name[1] != '\0') wname = parse_window_name(name + 1); else wname = parse_window_name(name); free(name); } if (w->active->fd == -1) { xasprintf(&name, "%s[dead]", wname); free(wname); wname = name; } if (strcmp(wname, w->name)) { window_set_name(w, wname); server_status_window(w); } free(wname); } char * default_window_name(struct window *w) { if (w->active->screen != &w->active->base) return (xstrdup("[tmux]")); if (w->active->cmd != NULL && *w->active->cmd != '\0') return (parse_window_name(w->active->cmd)); return (parse_window_name(w->active->shell)); } char * parse_window_name(const char *in) { char *copy, *name, *ptr; name = copy = xstrdup(in); if (strncmp(name, "exec ", (sizeof "exec ") - 1) == 0) name = name + (sizeof "exec ") - 1; while (*name == ' ') name++; if ((ptr = strchr(name, ' ')) != NULL) *ptr = '\0'; if (*name != '\0') { ptr = name + strlen(name) - 1; while (ptr > name && !isalnum((u_char)*ptr)) *ptr-- = '\0'; } if (*name == '/') name = basename(name); name = xstrdup(name); free(copy); return (name); } tmate-1.8.10/notify.c000066400000000000000000000107441242461015400143740ustar00rootroot00000000000000/* $Id$ */ /* * 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" enum notify_type { NOTIFY_WINDOW_LAYOUT_CHANGED, NOTIFY_WINDOW_UNLINKED, NOTIFY_WINDOW_LINKED, NOTIFY_WINDOW_RENAMED, NOTIFY_ATTACHED_SESSION_CHANGED, NOTIFY_SESSION_RENAMED, NOTIFY_SESSION_CREATED, NOTIFY_SESSION_CLOSED }; struct notify_entry { enum notify_type type; struct client *client; struct session *session; struct window *window; TAILQ_ENTRY(notify_entry) entry; }; TAILQ_HEAD(, notify_entry) notify_queue = TAILQ_HEAD_INITIALIZER(notify_queue); int notify_enabled = 1; void notify_drain(void); void notify_add(enum notify_type, struct client *, struct session *, struct window *); void notify_enable(void) { notify_enabled = 1; notify_drain(); } void notify_disable(void) { notify_enabled = 0; } void notify_add(enum notify_type type, struct client *c, struct session *s, struct window *w) { struct notify_entry *ne; ne = xcalloc(1, sizeof *ne); ne->type = type; ne->client = c; ne->session = s; ne->window = w; TAILQ_INSERT_TAIL(¬ify_queue, ne, entry); if (c != NULL) c->references++; if (s != NULL) s->references++; if (w != NULL) w->references++; } void notify_drain(void) { struct notify_entry *ne, *ne1; if (!notify_enabled) return; TAILQ_FOREACH_SAFE(ne, ¬ify_queue, entry, ne1) { switch (ne->type) { case NOTIFY_WINDOW_LAYOUT_CHANGED: control_notify_window_layout_changed(ne->window); break; case NOTIFY_WINDOW_UNLINKED: control_notify_window_unlinked(ne->session, ne->window); break; case NOTIFY_WINDOW_LINKED: control_notify_window_linked(ne->session, ne->window); break; case NOTIFY_WINDOW_RENAMED: control_notify_window_renamed(ne->window); break; case NOTIFY_ATTACHED_SESSION_CHANGED: control_notify_attached_session_changed(ne->client); break; case NOTIFY_SESSION_RENAMED: control_notify_session_renamed(ne->session); break; case NOTIFY_SESSION_CREATED: control_notify_session_created(ne->session); break; case NOTIFY_SESSION_CLOSED: control_notify_session_close(ne->session); break; } if (ne->client != NULL) ne->client->references--; if (ne->session != NULL) ne->session->references--; if (ne->window != NULL) window_remove_ref(ne->window); TAILQ_REMOVE(¬ify_queue, ne, entry); free(ne); } } void notify_input(struct window_pane *wp, struct evbuffer *input) { struct client *c; u_int i; /* * notify_input() is not queued and only does anything when * notifications are enabled. */ if (!notify_enabled) return; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c != NULL && (c->flags & CLIENT_CONTROL)) control_notify_input(c, wp, input); } } void notify_window_layout_changed(struct window *w) { notify_add(NOTIFY_WINDOW_LAYOUT_CHANGED, NULL, NULL, w); notify_drain(); } void notify_window_unlinked(struct session *s, struct window *w) { notify_add(NOTIFY_WINDOW_UNLINKED, NULL, s, w); notify_drain(); } void notify_window_linked(struct session *s, struct window *w) { notify_add(NOTIFY_WINDOW_LINKED, NULL, s, w); notify_drain(); } void notify_window_renamed(struct window *w) { notify_add(NOTIFY_WINDOW_RENAMED, NULL, NULL, w); notify_drain(); } void notify_attached_session_changed(struct client *c) { notify_add(NOTIFY_ATTACHED_SESSION_CHANGED, c, NULL, NULL); notify_drain(); } void notify_session_renamed(struct session *s) { notify_add(NOTIFY_SESSION_RENAMED, NULL, s, NULL); notify_drain(); } void notify_session_created(struct session *s) { notify_add(NOTIFY_SESSION_CREATED, NULL, s, NULL); notify_drain(); } void notify_session_closed(struct session *s) { notify_add(NOTIFY_SESSION_CLOSED, NULL, s, NULL); notify_drain(); } tmate-1.8.10/options-table.c000066400000000000000000000426271242461015400156510ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2011 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * This file has a tables with all the server, session and window * options. These tables are the master copy of the options with their real * (user-visible) types, range limits and default values. At start these are * copied into the runtime global options trees (which only has number and * string types). These tables are then used to loop up the real type when * the user sets an option or its value needs to be shown. */ /* Choice option type lists. */ const char *options_table_mode_keys_list[] = { "emacs", "vi", NULL }; const char *options_table_mode_mouse_list[] = { "off", "on", "copy-mode", NULL }; const char *options_table_clock_mode_style_list[] = { "12", "24", NULL }; const char *options_table_status_keys_list[] = { "emacs", "vi", NULL }; const char *options_table_status_justify_list[] = { "left", "centre", "right", NULL }; const char *options_table_status_position_list[] = { "top", "bottom", NULL }; const char *options_table_bell_action_list[] = { "none", "any", "current", NULL }; /* Server options. */ const struct options_table_entry server_options_table[] = { { .name = "buffer-limit", .type = OPTIONS_TABLE_NUMBER, .minimum = 1, .maximum = INT_MAX, .default_num = 20 }, { .name = "escape-time", .type = OPTIONS_TABLE_NUMBER, .minimum = 0, .maximum = INT_MAX, .default_num = 500 }, { .name = "exit-unattached", .type = OPTIONS_TABLE_FLAG, .default_num = 0 }, { .name = "quiet", .type = OPTIONS_TABLE_FLAG, .default_num = 0 /* overridden in main() */ }, { .name = "set-clipboard", .type = OPTIONS_TABLE_FLAG, .default_num = 1 }, { .name = NULL } }; /* Session options. */ const struct options_table_entry session_options_table[] = { { .name = "assume-paste-time", .type = OPTIONS_TABLE_NUMBER, .minimum = 0, .maximum = INT_MAX, .default_num = 1, }, { .name = "base-index", .type = OPTIONS_TABLE_NUMBER, .minimum = 0, .maximum = INT_MAX, .default_num = 0 }, { .name = "bell-action", .type = OPTIONS_TABLE_CHOICE, .choices = options_table_bell_action_list, .default_num = BELL_ANY }, { .name = "bell-on-alert", .type = OPTIONS_TABLE_FLAG, .default_num = 0 }, { .name = "default-command", .type = OPTIONS_TABLE_STRING, .default_str = "" }, { .name = "default-path", .type = OPTIONS_TABLE_STRING, .default_str = "" }, { .name = "default-shell", .type = OPTIONS_TABLE_STRING, .default_str = _PATH_BSHELL }, { .name = "default-terminal", .type = OPTIONS_TABLE_STRING, .default_str = "screen-256color" }, { .name = "destroy-unattached", .type = OPTIONS_TABLE_FLAG, .default_num = 0 }, { .name = "detach-on-destroy", .type = OPTIONS_TABLE_FLAG, .default_num = 1 }, { .name = "display-panes-active-colour", .type = OPTIONS_TABLE_COLOUR, .default_num = 1 }, { .name = "display-panes-colour", .type = OPTIONS_TABLE_COLOUR, .default_num = 4 }, { .name = "display-panes-time", .type = OPTIONS_TABLE_NUMBER, .minimum = 1, .maximum = INT_MAX, .default_num = 1000 }, { .name = "display-time", .type = OPTIONS_TABLE_NUMBER, .minimum = 1, .maximum = INT_MAX, .default_num = 750 }, { .name = "history-limit", .type = OPTIONS_TABLE_NUMBER, .minimum = 0, .maximum = INT_MAX, .default_num = 2000 }, { .name = "lock-after-time", .type = OPTIONS_TABLE_NUMBER, .minimum = 0, .maximum = INT_MAX, .default_num = 0 }, { .name = "lock-command", .type = OPTIONS_TABLE_STRING, .default_str = "lock -np" }, { .name = "lock-server", .type = OPTIONS_TABLE_FLAG, .default_num = 1 }, { .name = "message-attr", .type = OPTIONS_TABLE_ATTRIBUTES, .default_num = 0 }, { .name = "message-bg", .type = OPTIONS_TABLE_COLOUR, .default_num = 3 }, { .name = "message-command-attr", .type = OPTIONS_TABLE_ATTRIBUTES, .default_num = 0 }, { .name = "message-command-bg", .type = OPTIONS_TABLE_COLOUR, .default_num = 0 }, { .name = "message-command-fg", .type = OPTIONS_TABLE_COLOUR, .default_num = 3 }, { .name = "message-fg", .type = OPTIONS_TABLE_COLOUR, .default_num = 0 }, { .name = "message-limit", .type = OPTIONS_TABLE_NUMBER, .minimum = 0, .maximum = INT_MAX, .default_num = 20 }, { .name = "mouse-resize-pane", .type = OPTIONS_TABLE_FLAG, .default_num = 0 }, { .name = "mouse-select-pane", .type = OPTIONS_TABLE_FLAG, .default_num = 0 }, { .name = "mouse-select-window", .type = OPTIONS_TABLE_FLAG, .default_num = 0 }, { .name = "mouse-utf8", .type = OPTIONS_TABLE_FLAG, .default_num = 0 }, { .name = "pane-active-border-bg", .type = OPTIONS_TABLE_COLOUR, .default_num = 8 }, { .name = "pane-active-border-fg", .type = OPTIONS_TABLE_COLOUR, .default_num = 2 }, { .name = "pane-border-bg", .type = OPTIONS_TABLE_COLOUR, .default_num = 8 }, { .name = "pane-border-fg", .type = OPTIONS_TABLE_COLOUR, .default_num = 8 }, { .name = "prefix", .type = OPTIONS_TABLE_KEY, .default_num = '\002', }, { .name = "prefix2", .type = OPTIONS_TABLE_KEY, .default_num = KEYC_NONE, }, { .name = "renumber-windows", .type = OPTIONS_TABLE_FLAG, .default_num = 0 }, { .name = "repeat-time", .type = OPTIONS_TABLE_NUMBER, .minimum = 0, .maximum = SHRT_MAX, .default_num = 500 }, { .name = "set-remain-on-exit", .type = OPTIONS_TABLE_FLAG, .default_num = 0 }, { .name = "set-titles", .type = OPTIONS_TABLE_FLAG, .default_num = 0 }, { .name = "set-titles-string", .type = OPTIONS_TABLE_STRING, .default_str = "#S:#I:#W - \"#T\"" }, { .name = "status", .type = OPTIONS_TABLE_FLAG, .default_num = 1 }, { .name = "status-attr", .type = OPTIONS_TABLE_ATTRIBUTES, .default_num = 0 }, { .name = "status-bg", .type = OPTIONS_TABLE_COLOUR, .default_num = 2 }, { .name = "status-fg", .type = OPTIONS_TABLE_COLOUR, .default_num = 0 }, { .name = "status-interval", .type = OPTIONS_TABLE_NUMBER, .minimum = 0, .maximum = INT_MAX, .default_num = 15 }, { .name = "status-justify", .type = OPTIONS_TABLE_CHOICE, .choices = options_table_status_justify_list, .default_num = 0 }, { .name = "status-keys", .type = OPTIONS_TABLE_CHOICE, .choices = options_table_status_keys_list, .default_num = MODEKEY_EMACS }, { .name = "status-left", .type = OPTIONS_TABLE_STRING, .default_str = "[#S]" }, { .name = "status-left-attr", .type = OPTIONS_TABLE_ATTRIBUTES, .default_num = 0 }, { .name = "status-left-bg", .type = OPTIONS_TABLE_COLOUR, .default_num = 8 }, { .name = "status-left-fg", .type = OPTIONS_TABLE_COLOUR, .default_num = 8 }, { .name = "status-left-length", .type = OPTIONS_TABLE_NUMBER, .minimum = 0, .maximum = SHRT_MAX, .default_num = 10 }, { .name = "status-position", .type = OPTIONS_TABLE_CHOICE, .choices = options_table_status_position_list, .default_num = 1 }, { .name = "status-right", .type = OPTIONS_TABLE_STRING, .default_str = "\"#22T\" %H:%M %d-%b-%y" }, { .name = "status-right-attr", .type = OPTIONS_TABLE_ATTRIBUTES, .default_num = 0 }, { .name = "status-right-bg", .type = OPTIONS_TABLE_COLOUR, .default_num = 8 }, { .name = "status-right-fg", .type = OPTIONS_TABLE_COLOUR, .default_num = 8 }, { .name = "status-right-length", .type = OPTIONS_TABLE_NUMBER, .minimum = 0, .maximum = SHRT_MAX, .default_num = 40 }, { .name = "status-utf8", .type = OPTIONS_TABLE_FLAG, .default_num = 0 /* overridden in main() */ }, { .name = "terminal-overrides", .type = OPTIONS_TABLE_STRING, .default_str = "*88col*:colors=88,*256col*:colors=256" ",xterm*:XT:Ms=\\E]52;%p1%s;%p2%s\\007" ":Cc=\\E]12;%p1%s\\007:Cr=\\E]112\\007" ":Cs=\\E[%p1%d q:Csr=\\E[2 q,screen*:XT" }, { .name = "update-environment", .type = OPTIONS_TABLE_STRING, .default_str = "DISPLAY SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID " "SSH_CONNECTION WINDOWID XAUTHORITY" }, { .name = "visual-activity", .type = OPTIONS_TABLE_FLAG, .default_num = 0 }, { .name = "visual-bell", .type = OPTIONS_TABLE_FLAG, .default_num = 0 }, { .name = "visual-content", .type = OPTIONS_TABLE_FLAG, .default_num = 0 }, { .name = "visual-silence", .type = OPTIONS_TABLE_FLAG, .default_num = 0 }, { .name = "word-separators", .type = OPTIONS_TABLE_STRING, .default_str = " -_@" }, { .name = "tmate-display-time", .type = OPTIONS_TABLE_NUMBER, .minimum = 1, .maximum = INT_MAX, .default_num = 30000 }, { .name = "tmate-identity", .type = OPTIONS_TABLE_STRING, .default_str = "" }, { .name = "tmate-server-host", .type = OPTIONS_TABLE_STRING, .default_str = "master.tmate.io" }, { .name = "tmate-server-port", .type = OPTIONS_TABLE_NUMBER, .minimum = 1, .maximum = 65535, .default_num = 22 }, { .name = "tmate-server-dsa-fingerprint", .type = OPTIONS_TABLE_STRING, .default_str = "f5:26:31:c3:8a:78:6e:5c:77:74:0f:41:5b:5f:21:88" }, { .name = "tmate-server-rsa-fingerprint", .type = OPTIONS_TABLE_STRING, .default_str = "af:2d:81:c1:fe:49:70:2d:7f:09:a9:d7:4b:32:e3:be" }, { .name = "tmate-server-ecdsa-fingerprint", .type = OPTIONS_TABLE_STRING, .default_str = "c7:a1:51:36:d2:bb:35:4b:0a:1a:c0:43:97:74:ea:42" }, { .name = NULL } }; /* Window options. */ const struct options_table_entry window_options_table[] = { { .name = "aggressive-resize", .type = OPTIONS_TABLE_FLAG, .default_num = 0 }, { .name = "allow-rename", .type = OPTIONS_TABLE_FLAG, .default_num = 1 }, { .name = "alternate-screen", .type = OPTIONS_TABLE_FLAG, .default_num = 1 }, { .name = "automatic-rename", .type = OPTIONS_TABLE_FLAG, .default_num = 1 }, { .name = "c0-change-trigger", .type = OPTIONS_TABLE_NUMBER, .default_num = 250, .minimum = 0, .maximum = USHRT_MAX }, { .name = "c0-change-interval", .type = OPTIONS_TABLE_NUMBER, .default_num = 100, .minimum = 1, .maximum = USHRT_MAX }, { .name = "clock-mode-colour", .type = OPTIONS_TABLE_COLOUR, .default_num = 4 }, { .name = "clock-mode-style", .type = OPTIONS_TABLE_CHOICE, .choices = options_table_clock_mode_style_list, .default_num = 1 }, { .name = "force-height", .type = OPTIONS_TABLE_NUMBER, .minimum = 0, .maximum = INT_MAX, .default_num = 0 }, { .name = "force-width", .type = OPTIONS_TABLE_NUMBER, .minimum = 0, .maximum = INT_MAX, .default_num = 0 }, { .name = "main-pane-height", .type = OPTIONS_TABLE_NUMBER, .minimum = 1, .maximum = INT_MAX, .default_num = 24 }, { .name = "main-pane-width", .type = OPTIONS_TABLE_NUMBER, .minimum = 1, .maximum = INT_MAX, .default_num = 80 }, { .name = "mode-attr", .type = OPTIONS_TABLE_ATTRIBUTES, .default_num = 0 }, { .name = "mode-bg", .type = OPTIONS_TABLE_COLOUR, .default_num = 3 }, { .name = "mode-fg", .type = OPTIONS_TABLE_COLOUR, .default_num = 0 }, { .name = "mode-keys", .type = OPTIONS_TABLE_CHOICE, .choices = options_table_mode_keys_list, .default_num = MODEKEY_EMACS }, { .name = "mode-mouse", .type = OPTIONS_TABLE_CHOICE, .choices = options_table_mode_mouse_list, .default_num = 0 }, { .name = "monitor-activity", .type = OPTIONS_TABLE_FLAG, .default_num = 0 }, { .name = "monitor-content", .type = OPTIONS_TABLE_STRING, .default_str = "" }, { .name = "monitor-silence", .type = OPTIONS_TABLE_NUMBER, .minimum = 0, .maximum = INT_MAX, .default_num = 0 }, { .name = "other-pane-height", .type = OPTIONS_TABLE_NUMBER, .minimum = 0, .maximum = INT_MAX, .default_num = 0 }, { .name = "other-pane-width", .type = OPTIONS_TABLE_NUMBER, .minimum = 0, .maximum = INT_MAX, .default_num = 0 }, { .name = "pane-base-index", .type = OPTIONS_TABLE_NUMBER, .minimum = 0, .maximum = USHRT_MAX, .default_num = 0 }, { .name = "remain-on-exit", .type = OPTIONS_TABLE_FLAG, .default_num = 0 }, { .name = "synchronize-panes", .type = OPTIONS_TABLE_FLAG, .default_num = 0 }, { .name = "utf8", .type = OPTIONS_TABLE_FLAG, .default_num = 0 /* overridden in main() */ }, { .name = "window-status-activity-attr", .type = OPTIONS_TABLE_ATTRIBUTES, .default_num = GRID_ATTR_REVERSE }, { .name = "window-status-activity-bg", .type = OPTIONS_TABLE_COLOUR, .default_num = 8 }, { .name = "window-status-activity-fg", .type = OPTIONS_TABLE_COLOUR, .default_num = 8 }, { .name = "window-status-bell-attr", .type = OPTIONS_TABLE_ATTRIBUTES, .default_num = GRID_ATTR_REVERSE }, { .name = "window-status-bell-bg", .type = OPTIONS_TABLE_COLOUR, .default_num = 8 }, { .name = "window-status-bell-fg", .type = OPTIONS_TABLE_COLOUR, .default_num = 8 }, { .name = "window-status-content-attr", .type = OPTIONS_TABLE_ATTRIBUTES, .default_num = GRID_ATTR_REVERSE }, { .name = "window-status-content-bg", .type = OPTIONS_TABLE_COLOUR, .default_num = 8 }, { .name = "window-status-content-fg", .type = OPTIONS_TABLE_COLOUR, .default_num = 8 }, { .name = "window-status-attr", .type = OPTIONS_TABLE_ATTRIBUTES, .default_num = 0 }, { .name = "window-status-bg", .type = OPTIONS_TABLE_COLOUR, .default_num = 8 }, { .name = "window-status-current-attr", .type = OPTIONS_TABLE_ATTRIBUTES, .default_num = 0 }, { .name = "window-status-current-bg", .type = OPTIONS_TABLE_COLOUR, .default_num = 8 }, { .name = "window-status-current-fg", .type = OPTIONS_TABLE_COLOUR, .default_num = 8 }, { .name = "window-status-current-format", .type = OPTIONS_TABLE_STRING, .default_str = "#I:#W#F" }, { .name = "window-status-last-attr", .type = OPTIONS_TABLE_ATTRIBUTES, .default_num = 0 }, { .name = "window-status-last-bg", .type = OPTIONS_TABLE_COLOUR, .default_num = 8 }, { .name = "window-status-last-fg", .type = OPTIONS_TABLE_COLOUR, .default_num = 8 }, { .name = "window-status-fg", .type = OPTIONS_TABLE_COLOUR, .default_num = 8 }, { .name = "window-status-format", .type = OPTIONS_TABLE_STRING, .default_str = "#I:#W#F" }, { .name = "window-status-separator", .type = OPTIONS_TABLE_STRING, .default_str = " " }, { .name = "wrap-search", .type = OPTIONS_TABLE_FLAG, .default_num = 1 }, { .name = "xterm-keys", .type = OPTIONS_TABLE_FLAG, .default_num = 0 }, { .name = NULL } }; /* Populate an options tree from a table. */ void options_table_populate_tree( const struct options_table_entry *table, struct options *oo) { const struct options_table_entry *oe; for (oe = table; oe->name != NULL; oe++) { if (oe->default_str != NULL) options_set_string(oo, oe->name, "%s", oe->default_str); else options_set_number(oo, oe->name, oe->default_num); } } /* Print an option using its type from the table. */ const char * options_table_print_entry(const struct options_table_entry *oe, struct options_entry *o, int no_quotes) { static char out[BUFSIZ]; const char *s; *out = '\0'; switch (oe->type) { case OPTIONS_TABLE_STRING: if (no_quotes) xsnprintf(out, sizeof out, "%s", o->str); else xsnprintf(out, sizeof out, "\"%s\"", o->str); break; case OPTIONS_TABLE_NUMBER: xsnprintf(out, sizeof out, "%lld", o->num); break; case OPTIONS_TABLE_KEY: xsnprintf(out, sizeof out, "%s", key_string_lookup_key(o->num)); break; case OPTIONS_TABLE_COLOUR: s = colour_tostring(o->num); xsnprintf(out, sizeof out, "%s", s); break; case OPTIONS_TABLE_ATTRIBUTES: s = attributes_tostring(o->num); xsnprintf(out, sizeof out, "%s", s); break; case OPTIONS_TABLE_FLAG: if (o->num) strlcpy(out, "on", sizeof out); else strlcpy(out, "off", sizeof out); break; case OPTIONS_TABLE_CHOICE: s = oe->choices[o->num]; xsnprintf(out, sizeof out, "%s", s); break; } return (out); } /* Find an option. */ int options_table_find( const char *optstr, const struct options_table_entry **table, const struct options_table_entry **oe) { static const struct options_table_entry *tables[] = { server_options_table, window_options_table, session_options_table }; const struct options_table_entry *oe_loop; u_int i; for (i = 0; i < nitems(tables); i++) { for (oe_loop = tables[i]; oe_loop->name != NULL; oe_loop++) { if (strncmp(oe_loop->name, optstr, strlen(optstr)) != 0) continue; /* If already found, ambiguous. */ if (*oe != NULL) return (-1); *oe = oe_loop; *table = tables[i]; /* Bail now if an exact match. */ if (strcmp((*oe)->name, optstr) == 0) break; } } return (0); } tmate-1.8.10/options.c000066400000000000000000000071031242461015400145520ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" /* * Option handling; each option has a name, type and value and is stored in * a splay tree. */ RB_GENERATE(options_tree, options_entry, entry, options_cmp); int options_cmp(struct options_entry *o1, struct options_entry *o2) { return (strcmp(o1->name, o2->name)); } void options_init(struct options *oo, struct options *parent) { RB_INIT(&oo->tree); oo->parent = parent; } void options_free(struct options *oo) { struct options_entry *o; while (!RB_EMPTY(&oo->tree)) { o = RB_ROOT(&oo->tree); RB_REMOVE(options_tree, &oo->tree, o); free(o->name); if (o->type == OPTIONS_STRING) free(o->str); free(o); } } struct options_entry * options_find1(struct options *oo, const char *name) { struct options_entry p; p.name = (char *) name; return (RB_FIND(options_tree, &oo->tree, &p)); } struct options_entry * options_find(struct options *oo, const char *name) { struct options_entry *o, p; p.name = (char *) name; o = RB_FIND(options_tree, &oo->tree, &p); while (o == NULL) { oo = oo->parent; if (oo == NULL) break; o = RB_FIND(options_tree, &oo->tree, &p); } return (o); } void options_remove(struct options *oo, const char *name) { struct options_entry *o; if ((o = options_find1(oo, name)) == NULL) return; RB_REMOVE(options_tree, &oo->tree, o); free(o->name); if (o->type == OPTIONS_STRING) free(o->str); free(o); } struct options_entry *printflike3 options_set_string(struct options *oo, const char *name, const char *fmt, ...) { struct options_entry *o; va_list ap; if ((o = options_find1(oo, name)) == NULL) { o = xmalloc(sizeof *o); o->name = xstrdup(name); RB_INSERT(options_tree, &oo->tree, o); } else if (o->type == OPTIONS_STRING) free(o->str); va_start(ap, fmt); o->type = OPTIONS_STRING; xvasprintf(&o->str, fmt, ap); va_end(ap); return (o); } char * options_get_string(struct options *oo, const char *name) { struct options_entry *o; if ((o = options_find(oo, name)) == NULL) fatalx("missing option"); if (o->type != OPTIONS_STRING) fatalx("option not a string"); return (o->str); } struct options_entry * options_set_number(struct options *oo, const char *name, long long value) { struct options_entry *o; if ((o = options_find1(oo, name)) == NULL) { o = xmalloc(sizeof *o); o->name = xstrdup(name); RB_INSERT(options_tree, &oo->tree, o); } else if (o->type == OPTIONS_STRING) free(o->str); o->type = OPTIONS_NUMBER; o->num = value; return (o); } long long options_get_number(struct options *oo, const char *name) { struct options_entry *o; if ((o = options_find(oo, name)) == NULL) fatalx("missing option"); if (o->type != OPTIONS_NUMBER) fatalx("option not a number"); return (o->num); } tmate-1.8.10/osdep-aix.c000066400000000000000000000021031242461015400147430ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2011 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" char * osdep_get_name(unused int fd, unused char *tty) { return (NULL); } char * osdep_get_cwd(unused int fd) { return (NULL); } struct event_base * osdep_event_init(void) { return (event_init()); } tmate-1.8.10/osdep-darwin.c000066400000000000000000000041151242461015400154530ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2009 Joshua Elsasser * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include char *osdep_get_name(int, char *); char *osdep_get_cwd(int); struct event_base *osdep_event_init(void); #define unused __attribute__ ((unused)) char * osdep_get_name(int fd, unused char *tty) { 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); } 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) { /* * On OS X, kqueue and poll are both completely broken and don't * work on anything except socket file descriptors (yes, really). */ setenv("EVENT_NOKQUEUE", "1", 1); setenv("EVENT_NOPOLL", "1", 1); return (event_init()); } tmate-1.8.10/osdep-dragonfly.c000066400000000000000000000057151242461015400161630ustar00rootroot00000000000000/* $Id$ */ /* * 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()); } tmate-1.8.10/osdep-freebsd.c000066400000000000000000000074041242461015400156050ustar00rootroot00000000000000/* $Id$ */ /* * 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 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); } char * osdep_get_cwd(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); } struct event_base * osdep_event_init(void) { /* * On some versions of FreeBSD, kqueue doesn't work properly on tty * file descriptors. This is fixed in recent FreeBSD versions. */ setenv("EVENT_NOKQUEUE", "1", 1); return (event_init()); } tmate-1.8.10/osdep-hpux.c000066400000000000000000000021031242461015400151460ustar00rootroot00000000000000/* $Id$ */ /* * 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()); } tmate-1.8.10/osdep-linux.c000066400000000000000000000042471242461015400153340ustar00rootroot00000000000000/* $Id$ */ /* * 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, 1, 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) { /* * On Linux, epoll doesn't work on /dev/null (yes, really). * * This has been commented because libevent versions up until the very * latest (1.4 git or 2.0.10) do not handle signals properly when using * poll or select, causing hangs. * */ /* setenv("EVENT_NOEPOLL", "1", 1); */ return (event_init()); } tmate-1.8.10/osdep-netbsd.c000066400000000000000000000061051242461015400154470ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #define is_runnable(p) \ ((p)->p_stat == LSRUN || (p)->p_stat == SIDL) #define is_stopped(p) \ ((p)->p_stat == SSTOP || (p)->p_stat == SZOMB) struct kinfo_proc2 *cmp_procs(struct kinfo_proc2 *, struct kinfo_proc2 *); char *osdep_get_name(int, char *); 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); mib[5] = 0; retry: if (sysctl(mib, __arraycount(mib), NULL, &len, NULL, 0) == -1) return (NULL); if ((newbuf = realloc(buf, len * sizeof (*buf))) == NULL) goto error; buf = newbuf; mib[5] = len / sizeof(*buf); if (sysctl(mib, __arraycount(mib), buf, &len, NULL, 0) == -1) { if (errno == ENOMEM) goto retry; /* possible infinite loop? */ goto error; } bestp = NULL; for (i = 0; i < len / sizeof (*buf); i++) { if (buf[i].p_tdev != sb.st_rdev) continue; if (bestp == NULL) bestp = &buf[i]; else bestp = cmp_procs(&buf[i], bestp); } name = NULL; if (bestp != NULL) name = strdup(bestp->p_comm); free(buf); return (name); error: free(buf); return (NULL); } char * osdep_get_cwd(int fd) { return (NULL); } struct event_base * osdep_event_init(void) { return (event_init()); } tmate-1.8.10/osdep-openbsd.c000066400000000000000000000071551242461015400156300ustar00rootroot00000000000000/* $Id$ */ /* * 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 #ifndef nitems #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) #endif #define is_runnable(p) \ ((p)->p_stat == SRUN || (p)->p_stat == SIDL || (p)->p_stat == SONPROC) #define is_stopped(p) \ ((p)->p_stat == SSTOP || (p)->p_stat == SZOMB || (p)->p_stat == SDEAD) struct kinfo_proc *cmp_procs(struct kinfo_proc *, struct kinfo_proc *); char *osdep_get_name(int, char *); char *osdep_get_cwd(int); struct event_base *osdep_event_init(void); struct kinfo_proc * cmp_procs(struct kinfo_proc *p1, struct kinfo_proc *p2) { if (is_runnable(p1) && !is_runnable(p2)) return (p1); if (!is_runnable(p1) && is_runnable(p2)) return (p2); if (is_stopped(p1) && !is_stopped(p2)) return (p1); if (!is_stopped(p1) && is_stopped(p2)) return (p2); if (p1->p_estcpu > p2->p_estcpu) return (p1); if (p1->p_estcpu < p2->p_estcpu) return (p2); if (p1->p_slptime < p2->p_slptime) return (p1); if (p1->p_slptime > p2->p_slptime) return (p2); if ((p1->p_flag & P_SINTR) && !(p2->p_flag & P_SINTR)) return (p1); if (!(p1->p_flag & P_SINTR) && (p2->p_flag & P_SINTR)) return (p2); if (strcmp(p1->p_comm, p2->p_comm) < 0) return (p1); if (strcmp(p1->p_comm, p2->p_comm) > 0) return (p2); if (p1->p_pid > p2->p_pid) return (p1); return (p2); } char * osdep_get_name(int fd, char *tty) { int mib[6] = { CTL_KERN, KERN_PROC, KERN_PROC_PGRP, 0, sizeof(struct kinfo_proc), 0 }; struct stat sb; size_t len; struct kinfo_proc *buf, *newbuf, *bestp; u_int i; char *name; buf = NULL; if (stat(tty, &sb) == -1) return (NULL); if ((mib[3] = tcgetpgrp(fd)) == -1) return (NULL); retry: if (sysctl(mib, nitems(mib), NULL, &len, NULL, 0) == -1) return (NULL); len = (len * 5) / 4; if ((newbuf = realloc(buf, len)) == NULL) goto error; buf = newbuf; mib[5] = (int)(len / sizeof(struct kinfo_proc)); if (sysctl(mib, nitems(mib), buf, &len, NULL, 0) == -1) { if (errno == ENOMEM) goto retry; goto error; } bestp = NULL; for (i = 0; i < len / sizeof (struct kinfo_proc); i++) { if ((dev_t)buf[i].p_tdev != sb.st_rdev) continue; if (bestp == NULL) bestp = &buf[i]; else bestp = cmp_procs(&buf[i], bestp); } name = NULL; if (bestp != NULL) name = strdup(bestp->p_comm); free(buf); return (name); error: free(buf); return (NULL); } 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()); } tmate-1.8.10/osdep-sunos.c000066400000000000000000000037231242461015400153420ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2009 Todd Carson * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include "tmux.h" char * osdep_get_name(int fd, char *tty) { struct psinfo p; struct stat st; char *path; ssize_t bytes; int f; pid_t pgrp; if ((f = open(tty, O_RDONLY)) < 0) return (NULL); if (fstat(f, &st) != 0 || ioctl(f, TIOCGPGRP, &pgrp) != 0) { close(f); return (NULL); } close(f); xasprintf(&path, "/proc/%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; ssize_t n; pid_t pgrp; if ((pgrp = tcgetpgrp(fd)) == -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()); } tmate-1.8.10/osdep-unknown.c000066400000000000000000000020741242461015400156700ustar00rootroot00000000000000/* $Id$ */ /* * 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()); } tmate-1.8.10/paste.c000066400000000000000000000077131242461015400142020ustar00rootroot00000000000000/* $Id$ */ /* * 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" /* * Stack of paste buffers. Note that paste buffer data is not necessarily a C * string! */ /* Return each item of the stack in turn. */ struct paste_buffer * paste_walk_stack(struct paste_stack *ps, u_int *idx) { struct paste_buffer *pb; pb = paste_get_index(ps, *idx); (*idx)++; return (pb); } /* Get the top item on the stack. */ struct paste_buffer * paste_get_top(struct paste_stack *ps) { if (ARRAY_LENGTH(ps) == 0) return (NULL); return (ARRAY_FIRST(ps)); } /* Get an item by its index. */ struct paste_buffer * paste_get_index(struct paste_stack *ps, u_int idx) { if (idx >= ARRAY_LENGTH(ps)) return (NULL); return (ARRAY_ITEM(ps, idx)); } /* Free the top item on the stack. */ int paste_free_top(struct paste_stack *ps) { struct paste_buffer *pb; if (ARRAY_LENGTH(ps) == 0) return (-1); pb = ARRAY_FIRST(ps); ARRAY_REMOVE(ps, 0); free(pb->data); free(pb); return (0); } /* Free an item by index. */ int paste_free_index(struct paste_stack *ps, u_int idx) { struct paste_buffer *pb; if (idx >= ARRAY_LENGTH(ps)) return (-1); pb = ARRAY_ITEM(ps, idx); ARRAY_REMOVE(ps, idx); free(pb->data); free(pb); return (0); } /* * Add an item onto the top of the stack, freeing the bottom if at limit. Note * that the caller is responsible for allocating data. */ void paste_add(struct paste_stack *ps, char *data, size_t size, u_int limit) { struct paste_buffer *pb; if (size == 0) return; while (ARRAY_LENGTH(ps) >= limit) { pb = ARRAY_LAST(ps); free(pb->data); free(pb); ARRAY_TRUNC(ps, 1); } pb = xmalloc(sizeof *pb); ARRAY_INSERT(ps, 0, pb); pb->data = data; pb->size = size; } /* * Replace an item on the stack. Note that the caller is responsible for * allocating data. */ int paste_replace(struct paste_stack *ps, u_int idx, char *data, size_t size) { struct paste_buffer *pb; if (size == 0) return (0); if (idx >= ARRAY_LENGTH(ps)) return (-1); pb = ARRAY_ITEM(ps, idx); free(pb->data); pb->data = data; pb->size = size; return (0); } /* Convert a buffer into a visible string. */ char * paste_print(struct paste_buffer *pb, size_t width) { char *buf; size_t len, used; if (width < 3) width = 3; buf = xmalloc(width * 4 + 1); len = pb->size; if (len > width) len = width; used = strvisx(buf, pb->data, len, VIS_OCTAL|VIS_TAB|VIS_NL); if (pb->size > width || used > width) strlcpy(buf + width - 3, "...", 4); return (buf); } /* Paste into a window pane, filtering '\n' according to separator. */ void paste_send_pane (struct paste_buffer *pb, struct window_pane *wp, const char *sep, int bracket) { const char *data = pb->data, *end = data + pb->size, *lf; size_t seplen; if (bracket) bufferevent_write(wp->event, "\033[200~", 6); seplen = strlen(sep); while ((lf = memchr(data, '\n', end - data)) != NULL) { if (lf != data) bufferevent_write(wp->event, data, lf - data); bufferevent_write(wp->event, sep, seplen); data = lf + 1; } if (end != data) bufferevent_write(wp->event, data, end - data); if (bracket) bufferevent_write(wp->event, "\033[201~", 6); } tmate-1.8.10/resize.c000066400000000000000000000110631242461015400143600ustar00rootroot00000000000000/* $Id$ */ /* * 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" #include "tmate.h" /* * Recalculate window and session sizes. * * Every session has the size of the smallest client it is attached to and * every window the size of the smallest session it is attached to. * * So, when a client is resized or a session attached to or detached from a * client, the window sizes must be recalculated. For each session, find the * smallest client it is attached to, and resize it to that size. Then for * every window, find the smallest session it is attached to, resize it to that * size and clear and redraw every client with it as the current window. * * This is quite inefficient - better/additional data structures are needed * to make it better. * * As a side effect, this function updates the SESSION_UNATTACHED flag. This * flag is necessary to make sure unattached sessions do not limit the size of * windows that are attached both to them and to other (attached) sessions. */ void recalculate_sizes(void) { struct session *s; struct client *c; struct window *w; struct window_pane *wp; u_int i, j, ssx, ssy, has, limit; int flag, has_status, is_zoomed; RB_FOREACH(s, sessions, &sessions) { has_status = options_get_number(&s->options, "status"); ssx = ssy = UINT_MAX; for (j = 0; j < ARRAY_LENGTH(&clients); j++) { c = ARRAY_ITEM(&clients, j); if (c == NULL || c->flags & CLIENT_SUSPENDED) continue; if (c->session == s) { #ifdef TMATE if (c->flags & CLIENT_FORCE_STATUS) has_status = 1; #endif if (c->tty.sx < ssx) ssx = c->tty.sx; if (has_status && !(c->flags & CLIENT_CONTROL) && c->tty.sy > 1 && c->tty.sy - 1 < ssy) ssy = c->tty.sy - 1; else if (c->tty.sy < ssy) ssy = c->tty.sy; } } #ifdef TMATE /* We assume a single session */ if (tmate_sx > 0 && tmate_sy > 0) { if ((u_int)tmate_sx < ssx) ssx = tmate_sx; if ((u_int)tmate_sy < ssy) ssy = tmate_sy; } #endif if (ssx == UINT_MAX || ssy == UINT_MAX) { s->flags |= SESSION_UNATTACHED; continue; } s->flags &= ~SESSION_UNATTACHED; if (has_status && ssy == 0) ssy = 1; if (s->sx == ssx && s->sy == ssy) continue; log_debug("session size %u,%u (was %u,%u)", ssx, ssy, s->sx, s->sy); s->sx = ssx; s->sy = ssy; } for (i = 0; i < ARRAY_LENGTH(&windows); i++) { w = ARRAY_ITEM(&windows, i); if (w == NULL) continue; flag = options_get_number(&w->options, "aggressive-resize"); ssx = ssy = UINT_MAX; RB_FOREACH(s, sessions, &sessions) { if (s->flags & SESSION_UNATTACHED) continue; if (flag) has = s->curw->window == w; else has = session_has(s, w) != NULL; if (has) { if (s->sx < ssx) ssx = s->sx; if (s->sy < ssy) ssy = s->sy; } } if (ssx == UINT_MAX || ssy == UINT_MAX) continue; limit = options_get_number(&w->options, "force-width"); if (limit != 0 && ssx > limit) ssx = limit; limit = options_get_number(&w->options, "force-height"); if (limit != 0 && ssy > limit) ssy = limit; if (w->sx == ssx && w->sy == ssy) continue; log_debug("window size %u,%u (was %u,%u)", ssx, ssy, w->sx, w->sy); is_zoomed = w->flags & WINDOW_ZOOMED; if (is_zoomed) window_unzoom(w); layout_resize(w, ssx, ssy); window_resize(w, ssx, ssy); if (is_zoomed && window_pane_visible(w->active)) window_zoom(w->active); /* * If the current pane is now not visible, move to the next * that is. */ wp = w->active; while (!window_pane_visible(w->active)) { w->active = TAILQ_PREV(w->active, window_panes, entry); if (w->active == NULL) w->active = TAILQ_LAST(&w->panes, window_panes); if (w->active == wp) break; } server_redraw_window(w); notify_window_layout_changed(w); } } tmate-1.8.10/screen-redraw.c000066400000000000000000000250101242461015400156150ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" int screen_redraw_cell_border1(struct window_pane *, u_int, u_int); int screen_redraw_cell_border(struct client *, u_int, u_int); int screen_redraw_check_cell(struct client *, u_int, u_int, struct window_pane **); int screen_redraw_check_active(u_int, u_int, int, struct window *, struct window_pane *); void screen_redraw_draw_number(struct client *, struct window_pane *); #define CELL_INSIDE 0 #define CELL_LEFTRIGHT 1 #define CELL_TOPBOTTOM 2 #define CELL_TOPLEFT 3 #define CELL_TOPRIGHT 4 #define CELL_BOTTOMLEFT 5 #define CELL_BOTTOMRIGHT 6 #define CELL_TOPJOIN 7 #define CELL_BOTTOMJOIN 8 #define CELL_LEFTJOIN 9 #define CELL_RIGHTJOIN 10 #define CELL_JOIN 11 #define CELL_OUTSIDE 12 #define CELL_BORDERS " xqlkmjwvtun~" /* Check if cell is on the border of a particular pane. */ int screen_redraw_cell_border1(struct window_pane *wp, u_int px, u_int py) { /* Inside pane. */ if (px >= wp->xoff && px < wp->xoff + wp->sx && py >= wp->yoff && py < wp->yoff + wp->sy) return (0); /* Left/right borders. */ if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= wp->yoff + wp->sy) { if (wp->xoff != 0 && px == wp->xoff - 1) return (1); if (px == wp->xoff + wp->sx) return (1); } /* Top/bottom borders. */ if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= wp->xoff + wp->sx) { if (wp->yoff != 0 && py == wp->yoff - 1) return (1); if (py == wp->yoff + wp->sy) return (1); } /* Outside pane. */ return (-1); } /* Check if a cell is on the pane border. */ int screen_redraw_cell_border(struct client *c, u_int px, u_int py) { struct window *w = c->session->curw->window; struct window_pane *wp; int retval; /* Check all the panes. */ TAILQ_FOREACH(wp, &w->panes, entry) { if (!window_pane_visible(wp)) continue; if ((retval = screen_redraw_cell_border1(wp, px, py)) != -1) return (retval); } return (0); } /* Check if cell inside a pane. */ int screen_redraw_check_cell(struct client *c, u_int px, u_int py, struct window_pane **wpp) { struct window *w = c->session->curw->window; struct window_pane *wp; int borders; if (px > w->sx || py > w->sy) return (CELL_OUTSIDE); TAILQ_FOREACH(wp, &w->panes, entry) { if (!window_pane_visible(wp)) continue; *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 (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); } } *wpp = NULL; return (CELL_OUTSIDE); } /* Check active pane indicator. */ int screen_redraw_check_active(u_int px, u_int py, int type, struct window *w, struct window_pane *wp) { /* Is this off the active pane border? */ if (screen_redraw_cell_border1(w->active, px, py) != 1) 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); /* 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 == w->active) 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 == w->active) return (py <= wp->sy / 2); return (py > wp->sy / 2); } return (0); } return (type); } /* Redraw entire screen. */ void screen_redraw_screen(struct client *c, int status_only, int borders_only) { struct window *w = c->session->curw->window; struct options *oo = &c->session->options; struct tty *tty = &c->tty; struct window_pane *wp; struct grid_cell active_gc, other_gc; u_int i, j, type, top; int status, spos, fg, bg; /* Suspended clients should not be updated. */ if (c->flags & CLIENT_SUSPENDED) return; /* Get status line, er, status. */ spos = options_get_number(oo, "status-position"); if (c->message_string != NULL || c->prompt_string != NULL) status = 1; else status = options_get_number(oo, "status"); top = 0; if (status && spos == 0) top = 1; /* If only drawing status and it is present, don't need the rest. */ if (status_only && status) { if (top) tty_draw_line(tty, &c->status, 0, 0, 0); else tty_draw_line(tty, &c->status, 0, 0, tty->sy - 1); tty_reset(tty); return; } /* Set up pane border attributes. */ memcpy(&other_gc, &grid_marker_cell, sizeof other_gc); memcpy(&active_gc, &grid_marker_cell, sizeof active_gc); active_gc.attr = other_gc.attr = GRID_ATTR_CHARSET; fg = options_get_number(oo, "pane-border-fg"); colour_set_fg(&other_gc, fg); bg = options_get_number(oo, "pane-border-bg"); colour_set_bg(&other_gc, bg); fg = options_get_number(oo, "pane-active-border-fg"); colour_set_fg(&active_gc, fg); bg = options_get_number(oo, "pane-active-border-bg"); colour_set_bg(&active_gc, bg); /* Draw background and borders. */ for (j = 0; j < tty->sy - status; j++) { if (status_only) { if (spos == 1 && j != tty->sy - 1) continue; else if (spos == 0 && j != 0) break; } for (i = 0; i < tty->sx; i++) { type = screen_redraw_check_cell(c, i, j, &wp); if (type == CELL_INSIDE) continue; if (screen_redraw_check_active(i, j, type, w, wp)) tty_attributes(tty, &active_gc); else tty_attributes(tty, &other_gc); tty_cursor(tty, i, top + j); tty_putc(tty, CELL_BORDERS[type]); } } /* If only drawing borders, that's it. */ if (borders_only) return; /* Draw the panes, if necessary. */ TAILQ_FOREACH(wp, &w->panes, entry) { if (!window_pane_visible(wp)) continue; for (i = 0; i < wp->sy; i++) { if (status_only) { if (spos == 1 && wp->yoff + i != tty->sy - 1) continue; else if (spos == 0 && wp->yoff + i != 0) break; } tty_draw_line( tty, wp->screen, i, wp->xoff, top + wp->yoff); } if (c->flags & CLIENT_IDENTIFY) screen_redraw_draw_number(c, wp); } /* Draw the status line. */ if (status) { if (top) tty_draw_line(tty, &c->status, 0, 0, 0); else tty_draw_line(tty, &c->status, 0, 0, tty->sy - 1); } tty_reset(tty); } /* Draw a single pane. */ void screen_redraw_pane(struct client *c, struct window_pane *wp) { u_int i, yoff; if (!window_pane_visible(wp)) return; yoff = wp->yoff; if (status_at_line(c) == 0) yoff++; for (i = 0; i < wp->sy; i++) tty_draw_line(&c->tty, wp->screen, i, wp->xoff, yoff); tty_reset(&c->tty); } /* Draw number on a pane. */ void screen_redraw_draw_number(struct client *c, struct window_pane *wp) { struct tty *tty = &c->tty; struct session *s = c->session; struct options *oo = &s->options; struct window *w = wp->window; struct grid_cell gc; u_int idx, px, py, i, j, xoff, yoff; int colour, active_colour; char buf[16], *ptr; size_t len; if (window_pane_index(wp, &idx) != 0) fatalx("index not found"); len = xsnprintf(buf, sizeof buf, "%u", idx); if (wp->sx < len) return; colour = options_get_number(oo, "display-panes-colour"); active_colour = options_get_number(oo, "display-panes-active-colour"); px = wp->sx / 2; py = wp->sy / 2; xoff = wp->xoff; yoff = wp->yoff; if (wp->sx < len * 6 || wp->sy < 5) { tty_cursor(tty, xoff + px - len / 2, yoff + py); goto draw_text; } px -= len * 3; py -= 2; memcpy(&gc, &grid_marker_cell, sizeof gc); if (w->active == wp) colour_set_bg(&gc, active_colour); else colour_set_bg(&gc, colour); tty_attributes(tty, &gc); for (ptr = buf; *ptr != '\0'; ptr++) { if (*ptr < '0' || *ptr > '9') continue; idx = *ptr - '0'; for (j = 0; j < 5; j++) { for (i = px; i < px + 5; i++) { tty_cursor(tty, xoff + i, yoff + py + j); if (clock_table[idx][j][i - px]) tty_putc(tty, ' '); } } px += 6; } len = xsnprintf(buf, sizeof buf, "%ux%u", wp->sx, wp->sy); if (wp->sx < len || wp->sy < 6) return; tty_cursor(tty, xoff + wp->sx - len, yoff); draw_text: memcpy(&gc, &grid_marker_cell, sizeof gc); if (w->active == wp) colour_set_fg(&gc, active_colour); else colour_set_fg(&gc, colour); tty_attributes(tty, &gc); tty_puts(tty, buf); tty_cursor(tty, 0, 0); } tmate-1.8.10/screen-write.c000066400000000000000000000625131242461015400154740ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" void screen_write_initctx(struct screen_write_ctx *, struct tty_ctx *, int); void screen_write_overwrite(struct screen_write_ctx *, u_int); int screen_write_combine( struct screen_write_ctx *, const struct utf8_data *); /* Initialise writing with a window. */ void screen_write_start( struct screen_write_ctx *ctx, struct window_pane *wp, struct screen *s) { ctx->wp = wp; if (wp != NULL && s == NULL) ctx->s = wp->screen; else ctx->s = s; } /* Finish writing. */ void screen_write_stop(unused struct screen_write_ctx *ctx) { } /* 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_INSERT|MODE_KCURSOR|MODE_KKEYPAD); s->mode &= ~(ALL_MOUSE_MODES|MODE_MOUSE_UTF8|MODE_MOUSE_SGR); screen_write_clearscreen(ctx); screen_write_cursormove(ctx, 0, 0); } /* Write character. */ void screen_write_putc(struct screen_write_ctx *ctx, struct grid_cell *gc, u_char ch) { grid_cell_one(gc, ch); screen_write_cell(ctx, gc); } /* Calculate string length, with embedded formatting. */ size_t printflike2 screen_write_cstrlen(int utf8flag, const char *fmt, ...) { va_list ap; char *msg, *msg2, *ptr, *ptr2; size_t size; va_start(ap, fmt); xvasprintf(&msg, fmt, ap); va_end(ap); msg2 = xmalloc(strlen(msg) + 1); ptr = msg; ptr2 = msg2; while (*ptr != '\0') { if (ptr[0] == '#' && ptr[1] == '[') { while (*ptr != ']' && *ptr != '\0') ptr++; if (*ptr == ']') ptr++; continue; } *ptr2++ = *ptr++; } *ptr2 = '\0'; size = screen_write_strlen(utf8flag, "%s", msg2); free(msg); free(msg2); return (size); } /* Calculate string length. */ size_t printflike2 screen_write_strlen(int utf8flag, const char *fmt, ...) { va_list ap; char *msg; struct utf8_data utf8data; u_char *ptr; size_t left, size = 0; va_start(ap, fmt); xvasprintf(&msg, fmt, ap); va_end(ap); ptr = msg; while (*ptr != '\0') { if (utf8flag && *ptr > 0x7f && utf8_open(&utf8data, *ptr)) { ptr++; left = strlen(ptr); if (left < utf8data.size - 1) break; while (utf8_append(&utf8data, *ptr)) ptr++; ptr++; size += utf8data.width; } else { size++; ptr++; } } free(msg); return (size); } /* Write simple string (no UTF-8 or maximum length). */ void printflike3 screen_write_puts( struct screen_write_ctx *ctx, struct grid_cell *gc, const char *fmt, ...) { va_list ap; va_start(ap, fmt); screen_write_vnputs(ctx, -1, gc, 0, fmt, ap); va_end(ap); } /* Write string with length limit (-1 for unlimited). */ void printflike5 screen_write_nputs(struct screen_write_ctx *ctx, ssize_t maxlen, struct grid_cell *gc, int utf8flag, const char *fmt, ...) { va_list ap; va_start(ap, fmt); screen_write_vnputs(ctx, maxlen, gc, utf8flag, fmt, ap); va_end(ap); } void screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, struct grid_cell *gc, int utf8flag, const char *fmt, va_list ap) { char *msg; struct utf8_data utf8data; u_char *ptr; size_t left, size = 0; xvasprintf(&msg, fmt, ap); ptr = msg; while (*ptr != '\0') { if (utf8flag && *ptr > 0x7f && utf8_open(&utf8data, *ptr)) { ptr++; left = strlen(ptr); if (left < utf8data.size - 1) break; while (utf8_append(&utf8data, *ptr)) ptr++; ptr++; if (maxlen > 0 && size + utf8data.width > (size_t) maxlen) { while (size < (size_t) maxlen) { screen_write_putc(ctx, gc, ' '); size++; } break; } size += utf8data.width; grid_cell_set(gc, &utf8data); screen_write_cell(ctx, gc); } else { if (maxlen > 0 && size + 1 > (size_t) maxlen) break; if (*ptr == '\001') gc->attr ^= GRID_ATTR_CHARSET; else { size++; screen_write_putc(ctx, gc, *ptr); } ptr++; } } free(msg); } /* Write string, similar to nputs, but with embedded formatting (#[]). */ void printflike5 screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen, struct grid_cell *gc, int utf8flag, const char *fmt, ...) { struct grid_cell lgc; struct utf8_data utf8data; va_list ap; char *msg; u_char *ptr, *last; size_t left, size = 0; va_start(ap, fmt); xvasprintf(&msg, fmt, ap); va_end(ap); memcpy(&lgc, gc, sizeof lgc); ptr = msg; while (*ptr != '\0') { if (ptr[0] == '#' && ptr[1] == '[') { ptr += 2; last = ptr + strcspn(ptr, "]"); if (*last == '\0') { /* No ]. Not much point in doing anything. */ break; } *last = '\0'; screen_write_parsestyle(gc, &lgc, ptr); ptr = last + 1; continue; } if (utf8flag && *ptr > 0x7f && utf8_open(&utf8data, *ptr)) { ptr++; left = strlen(ptr); if (left < utf8data.size - 1) break; while (utf8_append(&utf8data, *ptr)) ptr++; ptr++; if (maxlen > 0 && size + utf8data.width > (size_t) maxlen) { while (size < (size_t) maxlen) { screen_write_putc(ctx, gc, ' '); size++; } break; } size += utf8data.width; grid_cell_set(&lgc, &utf8data); screen_write_cell(ctx, &lgc); } else { if (maxlen > 0 && size + 1 > (size_t) maxlen) break; size++; screen_write_putc(ctx, &lgc, *ptr); ptr++; } } free(msg); } /* Parse an embedded style of the form "fg=colour,bg=colour,bright,...". */ void screen_write_parsestyle( struct grid_cell *defgc, struct grid_cell *gc, const char *in) { const char delimiters[] = " ,"; char tmp[32]; int val; size_t end; u_char fg, bg, attr, flags; if (*in == '\0') return; if (strchr(delimiters, in[strlen(in) - 1]) != NULL) return; fg = gc->fg; bg = gc->bg; attr = gc->attr; flags = gc->flags; do { end = strcspn(in, delimiters); if (end > (sizeof tmp) - 1) return; memcpy(tmp, in, end); tmp[end] = '\0'; if (strcasecmp(tmp, "default") == 0) { fg = defgc->fg; bg = defgc->bg; attr = defgc->attr; flags &= ~(GRID_FLAG_FG256|GRID_FLAG_BG256); flags |= defgc->flags & (GRID_FLAG_FG256|GRID_FLAG_BG256); } else if (end > 3 && strncasecmp(tmp + 1, "g=", 2) == 0) { if ((val = colour_fromstring(tmp + 3)) == -1) return; if (*in == 'f' || *in == 'F') { if (val != 8) { if (val & 0x100) { flags |= GRID_FLAG_FG256; val &= ~0x100; } else flags &= ~GRID_FLAG_FG256; fg = val; } else { fg = defgc->fg; flags &= ~GRID_FLAG_FG256; flags |= defgc->flags & GRID_FLAG_FG256; } } else if (*in == 'b' || *in == 'B') { if (val != 8) { if (val & 0x100) { flags |= GRID_FLAG_BG256; val &= ~0x100; } else flags &= ~GRID_FLAG_BG256; bg = val; } else { bg = defgc->bg; flags &= ~GRID_FLAG_BG256; flags |= defgc->flags & GRID_FLAG_BG256; } } else return; } else if (end > 2 && strncasecmp(tmp, "no", 2) == 0) { if ((val = attributes_fromstring(tmp + 2)) == -1) return; attr &= ~val; } else { if ((val = attributes_fromstring(tmp)) == -1) return; attr |= val; } in += end + strspn(in + end, delimiters); } while (*in != '\0'); gc->fg = fg; gc->bg = bg; gc->attr = attr; gc->flags = flags; } /* Copy from another screen. */ void screen_write_copy(struct screen_write_ctx *ctx, struct screen *src, u_int px, u_int py, u_int nx, u_int ny) { struct screen *s = ctx->s; struct grid *gd = src->grid; struct grid_line *gl; const struct grid_cell *gc; struct utf8_data ud; u_int xx, yy, cx, cy, ax, bx; cx = s->cx; cy = s->cy; for (yy = py; yy < py + ny; yy++) { gl = &gd->linedata[yy]; if (yy < gd->hsize + gd->sy) { /* * Find start and end position and copy between * them. Limit to the real end of the line then use a * clear EOL only if copying to the end, otherwise * could overwrite whatever is there already. */ if (px > gl->cellsize) ax = gl->cellsize; else ax = px; if (px + nx == gd->sx && px + nx > gl->cellsize) bx = gl->cellsize; else bx = px + nx; for (xx = ax; xx < bx; xx++) { if (xx >= gl->cellsize) gc = &grid_default_cell; else gc = &gl->celldata[xx]; grid_cell_get(gc, &ud); screen_write_cell(ctx, gc); } if (px + nx == gd->sx && px + nx > gl->cellsize) screen_write_clearendofline(ctx); } else screen_write_clearline(ctx); cy++; screen_write_cursormove(ctx, cx, cy); } } /* Set up context for TTY command. */ void screen_write_initctx( struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, int save_last) { struct screen *s = ctx->s; struct grid *gd = s->grid; const struct grid_cell *gc; u_int xx; ttyctx->wp = ctx->wp; ttyctx->ocx = s->cx; ttyctx->ocy = s->cy; ttyctx->orlower = s->rlower; ttyctx->orupper = s->rupper; if (!save_last) return; /* Save the last cell on the screen. */ gc = &grid_default_cell; for (xx = 1; xx <= screen_size_x(s); xx++) { gc = grid_view_peek_cell(gd, screen_size_x(s) - xx, s->cy); if (!(gc->flags & GRID_FLAG_PADDING)) break; } ttyctx->last_width = xx; memcpy(&ttyctx->last_cell, gc, sizeof ttyctx->last_cell); } /* 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; if (ny == 0) ny = 1; if (s->cy < s->rupper) { /* Above region. */ if (ny > s->cy) ny = s->cy; } else { /* Below region. */ if (ny > s->cy - s->rupper) ny = s->cy - s->rupper; } if (ny == 0) return; s->cy -= ny; } /* Cursor down by ny. */ void screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny) { struct screen *s = ctx->s; if (ny == 0) ny = 1; if (s->cy > s->rlower) { /* Below region. */ if (ny > screen_size_y(s) - 1 - s->cy) ny = screen_size_y(s) - 1 - s->cy; } else { /* Above region. */ if (ny > s->rlower - s->cy) ny = s->rlower - s->cy; } if (ny == 0) return; s->cy += ny; } /* Cursor right by nx. */ void screen_write_cursorright(struct screen_write_ctx *ctx, u_int nx) { struct screen *s = ctx->s; if (nx == 0) nx = 1; if (nx > screen_size_x(s) - 1 - s->cx) nx = screen_size_x(s) - 1 - s->cx; if (nx == 0) return; s->cx += nx; } /* Cursor left by nx. */ void screen_write_cursorleft(struct screen_write_ctx *ctx, u_int nx) { struct screen *s = ctx->s; if (nx == 0) nx = 1; if (nx > s->cx) nx = s->cx; if (nx == 0) return; s->cx -= nx; } /* Backspace; cursor left unless at start of wrapped line when can move up. */ void screen_write_backspace(struct screen_write_ctx *ctx) { struct screen *s = ctx->s; struct grid_line *gl; if (s->cx == 0) { if (s->cy == 0) return; gl = &s->grid->linedata[s->grid->hsize + s->cy - 1]; if (gl->flags & GRID_LINE_WRAPPED) { s->cy--; s->cx = screen_size_x(s) - 1; } } else s->cx--; } /* VT100 alignment test. */ void screen_write_alignmenttest(struct screen_write_ctx *ctx) { struct screen *s = ctx->s; struct tty_ctx ttyctx; struct grid_cell gc; u_int xx, yy; screen_write_initctx(ctx, &ttyctx, 0); memcpy(&gc, &grid_default_cell, sizeof gc); grid_cell_one(&gc, 'E'); for (yy = 0; yy < screen_size_y(s); yy++) { for (xx = 0; xx < screen_size_x(s); xx++) grid_view_set_cell(s->grid, xx, yy, &gc); } s->cx = 0; s->cy = 0; s->rupper = 0; s->rlower = screen_size_y(s) - 1; tty_write(tty_cmd_alignmenttest, &ttyctx); } /* Insert nx characters. */ void screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx) { struct screen *s = ctx->s; struct tty_ctx ttyctx; if (nx == 0) nx = 1; if (nx > screen_size_x(s) - s->cx) nx = screen_size_x(s) - s->cx; if (nx == 0) return; screen_write_initctx(ctx, &ttyctx, 0); if (s->cx <= screen_size_x(s) - 1) grid_view_insert_cells(s->grid, s->cx, s->cy, nx); ttyctx.num = nx; tty_write(tty_cmd_insertcharacter, &ttyctx); } /* Delete nx characters. */ void screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx) { struct screen *s = ctx->s; struct tty_ctx ttyctx; if (nx == 0) nx = 1; if (nx > screen_size_x(s) - s->cx) nx = screen_size_x(s) - s->cx; if (nx == 0) return; screen_write_initctx(ctx, &ttyctx, 0); if (s->cx <= screen_size_x(s) - 1) grid_view_delete_cells(s->grid, s->cx, s->cy, nx); ttyctx.num = nx; tty_write(tty_cmd_deletecharacter, &ttyctx); } /* Clear nx characters. */ void screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx) { struct screen *s = ctx->s; struct tty_ctx ttyctx; if (nx == 0) nx = 1; if (nx > screen_size_x(s) - s->cx) nx = screen_size_x(s) - s->cx; if (nx == 0) return; screen_write_initctx(ctx, &ttyctx, 0); if (s->cx <= screen_size_x(s) - 1) grid_view_clear(s->grid, s->cx, s->cy, nx, 1); ttyctx.num = nx; tty_write(tty_cmd_clearcharacter, &ttyctx); } /* Insert ny lines. */ void screen_write_insertline(struct screen_write_ctx *ctx, u_int ny) { struct screen *s = ctx->s; struct tty_ctx ttyctx; if (ny == 0) ny = 1; if (s->cy < s->rupper || s->cy > s->rlower) { if (ny > screen_size_y(s) - s->cy) ny = screen_size_y(s) - s->cy; if (ny == 0) return; screen_write_initctx(ctx, &ttyctx, 0); grid_view_insert_lines(s->grid, s->cy, ny); ttyctx.num = ny; tty_write(tty_cmd_insertline, &ttyctx); return; } if (ny > s->rlower + 1 - s->cy) ny = s->rlower + 1 - s->cy; if (ny == 0) return; screen_write_initctx(ctx, &ttyctx, 0); if (s->cy < s->rupper || s->cy > s->rlower) grid_view_insert_lines(s->grid, s->cy, ny); else grid_view_insert_lines_region(s->grid, s->rlower, s->cy, ny); ttyctx.num = ny; tty_write(tty_cmd_insertline, &ttyctx); } /* Delete ny lines. */ void screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny) { struct screen *s = ctx->s; struct tty_ctx ttyctx; if (ny == 0) ny = 1; if (s->cy < s->rupper || s->cy > s->rlower) { if (ny > screen_size_y(s) - s->cy) ny = screen_size_y(s) - s->cy; if (ny == 0) return; screen_write_initctx(ctx, &ttyctx, 0); grid_view_delete_lines(s->grid, s->cy, ny); ttyctx.num = ny; tty_write(tty_cmd_deleteline, &ttyctx); return; } if (ny > s->rlower + 1 - s->cy) ny = s->rlower + 1 - s->cy; if (ny == 0) return; screen_write_initctx(ctx, &ttyctx, 0); if (s->cy < s->rupper || s->cy > s->rlower) grid_view_delete_lines(s->grid, s->cy, ny); else grid_view_delete_lines_region(s->grid, s->rlower, s->cy, ny); ttyctx.num = ny; tty_write(tty_cmd_deleteline, &ttyctx); } /* Clear line at cursor. */ void screen_write_clearline(struct screen_write_ctx *ctx) { struct screen *s = ctx->s; struct tty_ctx ttyctx; screen_write_initctx(ctx, &ttyctx, 0); grid_view_clear(s->grid, 0, s->cy, screen_size_x(s), 1); tty_write(tty_cmd_clearline, &ttyctx); } /* Clear to end of line from cursor. */ void screen_write_clearendofline(struct screen_write_ctx *ctx) { struct screen *s = ctx->s; struct tty_ctx ttyctx; u_int sx; screen_write_initctx(ctx, &ttyctx, 0); sx = screen_size_x(s); if (s->cx <= sx - 1) grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1); tty_write(tty_cmd_clearendofline, &ttyctx); } /* Clear to start of line from cursor. */ void screen_write_clearstartofline(struct screen_write_ctx *ctx) { struct screen *s = ctx->s; struct tty_ctx ttyctx; u_int sx; screen_write_initctx(ctx, &ttyctx, 0); sx = screen_size_x(s); if (s->cx > sx - 1) grid_view_clear(s->grid, 0, s->cy, sx, 1); else grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1); tty_write(tty_cmd_clearstartofline, &ttyctx); } /* Move cursor to px,py. */ void screen_write_cursormove(struct screen_write_ctx *ctx, u_int px, u_int py) { struct screen *s = ctx->s; if (px > screen_size_x(s) - 1) px = screen_size_x(s) - 1; if (py > screen_size_y(s) - 1) py = screen_size_y(s) - 1; s->cx = px; s->cy = py; } /* Reverse index (up with scroll). */ void screen_write_reverseindex(struct screen_write_ctx *ctx) { struct screen *s = ctx->s; struct tty_ctx ttyctx; screen_write_initctx(ctx, &ttyctx, 0); if (s->cy == s->rupper) grid_view_scroll_region_down(s->grid, s->rupper, s->rlower); else if (s->cy > 0) s->cy--; tty_write(tty_cmd_reverseindex, &ttyctx); } /* Set scroll region. */ void screen_write_scrollregion( struct screen_write_ctx *ctx, u_int rupper, u_int rlower) { struct screen *s = ctx->s; if (rupper > screen_size_y(s) - 1) rupper = screen_size_y(s) - 1; if (rlower > screen_size_y(s) - 1) rlower = screen_size_y(s) - 1; if (rupper >= rlower) /* cannot be one line */ return; /* Cursor moves to top-left. */ s->cx = 0; s->cy = 0; s->rupper = rupper; s->rlower = rlower; } /* Line feed. */ void screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped) { struct screen *s = ctx->s; struct grid_line *gl; struct tty_ctx ttyctx; screen_write_initctx(ctx, &ttyctx, 0); gl = &s->grid->linedata[s->grid->hsize + s->cy]; if (wrapped) gl->flags |= GRID_LINE_WRAPPED; else gl->flags &= ~GRID_LINE_WRAPPED; if (s->cy == s->rlower) grid_view_scroll_region_up(s->grid, s->rupper, s->rlower); else if (s->cy < screen_size_y(s) - 1) s->cy++; ttyctx.num = wrapped; tty_write(tty_cmd_linefeed, &ttyctx); } /* Carriage return (cursor to start of line). */ void screen_write_carriagereturn(struct screen_write_ctx *ctx) { struct screen *s = ctx->s; s->cx = 0; } /* Clear to end of screen from cursor. */ void screen_write_clearendofscreen(struct screen_write_ctx *ctx) { struct screen *s = ctx->s; struct tty_ctx ttyctx; u_int sx, sy; screen_write_initctx(ctx, &ttyctx, 0); sx = screen_size_x(s); sy = screen_size_y(s); /* Scroll into history if it is enabled and clearing entire screen. */ if (s->cy == 0 && s->grid->flags & GRID_HISTORY) grid_view_clear_history(s->grid); else { if (s->cx <= sx - 1) grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1); grid_view_clear(s->grid, 0, s->cy + 1, sx, sy - (s->cy + 1)); } tty_write(tty_cmd_clearendofscreen, &ttyctx); } /* Clear to start of screen. */ void screen_write_clearstartofscreen(struct screen_write_ctx *ctx) { struct screen *s = ctx->s; struct tty_ctx ttyctx; u_int sx; screen_write_initctx(ctx, &ttyctx, 0); sx = screen_size_x(s); if (s->cy > 0) grid_view_clear(s->grid, 0, 0, sx, s->cy); if (s->cx > sx - 1) grid_view_clear(s->grid, 0, s->cy, sx, 1); else grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1); tty_write(tty_cmd_clearstartofscreen, &ttyctx); } /* Clear entire screen. */ void screen_write_clearscreen(struct screen_write_ctx *ctx) { struct screen *s = ctx->s; struct tty_ctx ttyctx; screen_write_initctx(ctx, &ttyctx, 0); /* Scroll into history if it is enabled. */ if (s->grid->flags & GRID_HISTORY) grid_view_clear_history(s->grid); else { grid_view_clear( s->grid, 0, 0, screen_size_x(s), screen_size_y(s)); } tty_write(tty_cmd_clearscreen, &ttyctx); } /* Clear entire history. */ void screen_write_clearhistory(struct screen_write_ctx *ctx) { struct screen *s = ctx->s; struct grid *gd = s->grid; grid_move_lines(gd, 0, gd->hsize, gd->sy); gd->hsize = 0; } /* 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 tty_ctx ttyctx; u_int width, xx, last; struct grid_cell tmp_gc, *tmp_gcp; struct utf8_data ud; int insert; /* Ignore padding. */ if (gc->flags & GRID_FLAG_PADDING) return; width = grid_cell_width(gc); /* * If this is a wide character and there is no room on the screen, for * the entire character, don't print it. */ if (!(s->mode & MODE_WRAP) && (width > 1 && (width > screen_size_x(s) || (s->cx != screen_size_x(s) && s->cx > screen_size_x(s) - width)))) return; /* * If the width is zero, combine onto the previous character, if * there is space. */ if (width == 0) { grid_cell_get(gc, &ud); if (screen_write_combine(ctx, &ud) == 0) { screen_write_initctx(ctx, &ttyctx, 0); tty_write(tty_cmd_utf8character, &ttyctx); } return; } /* Initialise the redraw context, saving the last cell. */ screen_write_initctx(ctx, &ttyctx, 1); /* If in insert mode, make space for the cells. */ if ((s->mode & MODE_INSERT) && s->cx <= screen_size_x(s) - width) { xx = screen_size_x(s) - s->cx - width; grid_move_cells(s->grid, s->cx + width, s->cx, s->cy, xx); insert = 1; } else insert = 0; /* Check this will fit on the current line and wrap if not. */ if ((s->mode & MODE_WRAP) && s->cx > screen_size_x(s) - width) { screen_write_linefeed(ctx, 1); s->cx = 0; /* carriage return */ } /* Sanity check cursor position. */ if (s->cx > screen_size_x(s) - width || s->cy > screen_size_y(s) - 1) return; /* Handle overwriting of UTF-8 characters. */ screen_write_overwrite(ctx, width); /* * If the new character is UTF-8 wide, fill in padding cells. Have * already ensured there is enough room. */ for (xx = s->cx + 1; xx < s->cx + width; xx++) { tmp_gcp = grid_view_get_cell(gd, xx, s->cy); if (tmp_gcp != NULL) tmp_gcp->flags |= GRID_FLAG_PADDING; } /* Set the cell. */ grid_view_set_cell(gd, s->cx, s->cy, gc); /* * Move the cursor. If not wrapping, stick at the last character and * replace it. */ last = !(s->mode & MODE_WRAP); if (s->cx <= screen_size_x(s) - last - width) s->cx += width; else s->cx = screen_size_x(s) - last; /* Draw to the screen if necessary. */ if (insert) { ttyctx.num = width; tty_write(tty_cmd_insertcharacter, &ttyctx); } if (screen_check_selection(s, s->cx - width, s->cy)) { memcpy(&tmp_gc, &s->sel.cell, sizeof tmp_gc); grid_cell_get(gc, &ud); grid_cell_set(&tmp_gc, &ud); tmp_gc.flags = gc->flags & ~(GRID_FLAG_FG256|GRID_FLAG_BG256); tmp_gc.flags |= s->sel.cell.flags & (GRID_FLAG_FG256|GRID_FLAG_BG256); ttyctx.cell = &tmp_gc; tty_write(tty_cmd_cell, &ttyctx); } else { ttyctx.cell = gc; tty_write(tty_cmd_cell, &ttyctx); } } /* Combine a UTF-8 zero-width character onto the previous. */ int screen_write_combine(struct screen_write_ctx *ctx, const struct utf8_data *ud) { struct screen *s = ctx->s; struct grid *gd = s->grid; struct grid_cell *gc; struct utf8_data ud1; /* Can't combine if at 0. */ if (s->cx == 0) return (-1); /* Empty data is out. */ if (ud->size == 0) fatalx("UTF-8 data empty"); /* Retrieve the previous cell. */ gc = grid_view_get_cell(gd, s->cx - 1, s->cy); grid_cell_get(gc, &ud1); /* Check there is enough space. */ if (ud1.size + ud->size > sizeof ud1.data) return (-1); /* Append the data and set the cell. */ memcpy(ud1.data + ud1.size, ud->data, ud->size); ud1.size += ud->size; grid_cell_set(gc, &ud1); return (0); } /* * UTF-8 wide characters are a bit of an annoyance. They take up more than one * cell on the screen, so following cells must not be drawn by marking them as * padding. * * So far, so good. The problem is, when overwriting a padding cell, or a UTF-8 * character, it is necessary to also overwrite any other cells which covered * by the same character. */ void screen_write_overwrite(struct screen_write_ctx *ctx, u_int width) { struct screen *s = ctx->s; struct grid *gd = s->grid; const struct grid_cell *gc; u_int xx; gc = grid_view_peek_cell(gd, s->cx, s->cy); if (gc->flags & GRID_FLAG_PADDING) { /* * A padding cell, so clear any following and leading padding * cells back to the character. Don't overwrite the current * cell as that happens later anyway. */ xx = s->cx + 1; while (--xx > 0) { gc = grid_view_peek_cell(gd, xx, s->cy); if (!(gc->flags & GRID_FLAG_PADDING)) break; grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); } /* Overwrite the character at the start of this padding. */ grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); } /* * Overwrite any padding cells that belong to a UTF-8 character * we'll be overwriting with the current character. */ xx = s->cx + width - 1; while (++xx < screen_size_x(s)) { gc = grid_view_peek_cell(gd, xx, s->cy); if (!(gc->flags & GRID_FLAG_PADDING)) break; grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); } } void screen_write_setselection(struct screen_write_ctx *ctx, u_char *str, u_int len) { struct tty_ctx ttyctx; screen_write_initctx(ctx, &ttyctx, 0); ttyctx.ptr = str; ttyctx.num = len; tty_write(tty_cmd_setselection, &ttyctx); } void screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len) { struct tty_ctx ttyctx; screen_write_initctx(ctx, &ttyctx, 0); ttyctx.ptr = str; ttyctx.num = len; tty_write(tty_cmd_rawstring, &ttyctx); } tmate-1.8.10/screen.c000066400000000000000000000201771242461015400143440ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include "tmux.h" void screen_resize_x(struct screen *, u_int); void screen_resize_y(struct screen *, u_int); /* Create a new screen. */ void screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit) { char hn[MAXHOSTNAMELEN]; s->grid = grid_create(sx, sy, hlimit); if (gethostname(hn, MAXHOSTNAMELEN) == 0) s->title = xstrdup(hn); else s->title = xstrdup(""); s->cstyle = 0; s->ccolour = xstrdup(""); s->tabs = NULL; screen_reinit(s); } /* Reinitialise screen. */ void screen_reinit(struct screen *s) { s->cx = 0; s->cy = 0; s->rupper = 0; s->rlower = screen_size_y(s) - 1; s->mode = MODE_CURSOR | MODE_WRAP; screen_reset_tabs(s); grid_clear_lines(s->grid, s->grid->hsize, s->grid->sy); screen_clear_selection(s); } /* Destroy a screen. */ void screen_free(struct screen *s) { free(s->tabs); free(s->title); free(s->ccolour); grid_destroy(s->grid); } /* 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_string) { free(s->ccolour); s->ccolour = xstrdup(colour_string); } /* Set screen title. */ void screen_set_title(struct screen *s, const char *title) { char tmp[BUFSIZ]; strlcpy(tmp, title, sizeof tmp); free(s->title); s->title = xstrdup(tmp); } /* 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)) { screen_resize_x(s, sx); /* * It is unclear what should happen to tabs on resize. xterm * seems to try and maintain them, rxvt resets them. Resetting * is simpler and more reliable so let's do that. */ screen_reset_tabs(s); } if (sy != screen_size_y(s)) screen_resize_y(s, sy); if (reflow) screen_reflow(s, sx); } void screen_resize_x(struct screen *s, u_int sx) { struct grid *gd = s->grid; if (sx == 0) fatalx("zero size"); /* * Treat resizing horizontally simply: just ensure the cursor is * on-screen and change the size. Don't bother to truncate any lines - * then the data should be accessible if the size is then incrased. * * The only potential wrinkle is if UTF-8 double-width characters are * left in the last column, but UTF-8 terminals should deal with this * sanely. */ if (s->cx >= sx) s->cx = sx - 1; gd->sx = sx; } void screen_resize_y(struct screen *s, u_int sy) { struct grid *gd = s->grid; u_int needed, available, oldy, i; if (sy == 0) fatalx("zero size"); oldy = screen_size_y(s); /* * When resizing: * * If the height is decreasing, delete lines from the bottom until * hitting the cursor, then push lines from the top into the history. * * When increasing, pull as many lines as possible from the history to * the top, then fill the remaining with blanks at the bottom. */ /* Size decreasing. */ if (sy < oldy) { needed = oldy - sy; /* Delete as many lines as possible from the bottom. */ available = oldy - 1 - s->cy; if (available > 0) { if (available > needed) available = needed; grid_view_delete_lines(gd, oldy - available, available); } needed -= available; /* * Now just increase the history size, if possible, to take * over the lines which are left. If history is off, delete * lines from the top. * * XXX Should apply history limit? */ available = s->cy; if (gd->flags & GRID_HISTORY) gd->hsize += needed; else if (needed > 0 && available > 0) { if (available > needed) available = needed; grid_view_delete_lines(gd, 0, available); } s->cy -= needed; } /* Resize line arrays. */ gd->linedata = xrealloc( gd->linedata, gd->hsize + sy, sizeof *gd->linedata); /* Size increasing. */ if (sy > oldy) { needed = sy - oldy; /* * Try to pull as much as possible out of the history, if is * is enabled. */ available = gd->hsize; if (gd->flags & GRID_HISTORY && available > 0) { if (available > needed) available = needed; gd->hsize -= available; s->cy += available; } else available = 0; needed -= available; /* Then fill the rest in with blanks. */ for (i = gd->hsize + sy - needed; i < gd->hsize + sy; i++) memset(&gd->linedata[i], 0, sizeof gd->linedata[i]); } /* Set the new size, and reset the scroll region. */ gd->sy = sy; s->rupper = 0; s->rlower = screen_size_y(s) - 1; } /* Set selection. */ void screen_set_selection(struct screen *s, u_int sx, u_int sy, u_int ex, u_int ey, u_int rectflag, struct grid_cell *gc) { struct screen_sel *sel = &s->sel; memcpy(&sel->cell, gc, sizeof sel->cell); sel->flag = 1; sel->rectflag = rectflag; sel->sx = sx; sel->sy = sy; sel->ex = ex; sel->ey = ey; } /* Clear selection. */ void screen_clear_selection(struct screen *s) { struct screen_sel *sel = &s->sel; sel->flag = 0; } /* Check if cell in selection. */ int screen_check_selection(struct screen *s, u_int px, u_int py) { struct screen_sel *sel = &s->sel; if (!sel->flag) return (0); if (sel->rectflag) { if (sel->sy < sel->ey) { /* start line < end line -- downward selection. */ if (py < sel->sy || py > sel->ey) return (0); } else if (sel->sy > sel->ey) { /* start line > end line -- upward selection. */ if (py > sel->sy || py < sel->ey) return (0); } else { /* starting line == ending line. */ if (py != sel->sy) return (0); } /* * Need to include the selection start row, but not the cursor * row, which means the selection changes depending on which * one is on the left. */ if (sel->ex < sel->sx) { /* Cursor (ex) is on the left. */ if (px < sel->ex) return (0); if (px > sel->sx) return (0); } else { /* Selection start (sx) is on the left. */ if (px < sel->sx) return (0); if (px > sel->ex) return (0); } } else { /* * Like emacs, keep the top-left-most character, and drop the * bottom-right-most, regardless of copy direction. */ if (sel->sy < sel->ey) { /* starting line < ending line -- downward selection. */ if (py < sel->sy || py > sel->ey) return (0); if ((py == sel->sy && px < sel->sx) || (py == sel->ey && px > sel->ex)) return (0); } else if (sel->sy > sel->ey) { /* starting line > ending line -- upward selection. */ if (py > sel->sy || py < sel->ey) return (0); if ((py == sel->sy && px >= sel->sx) || (py == sel->ey && px < sel->ex)) return (0); } else { /* starting line == ending line. */ if (py != sel->sy) return (0); if (sel->ex < sel->sx) { /* cursor (ex) is on the left */ if (px > sel->sx || px < sel->ex) return (0); } else { /* selection start (sx) is on the left */ if (px < sel->sx || px > sel->ex) return (0); } } } return (1); } /* Reflow wrapped lines. */ void screen_reflow(struct screen *s, u_int new_x) { struct grid *old = s->grid; s->grid = grid_create(old->sx, old->sy, old->hlimit); s->cy -= grid_reflow(s->grid, old, new_x); } tmate-1.8.10/server-client.c000066400000000000000000000615501242461015400156470ustar00rootroot00000000000000/* $Id$ */ /* * 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 "tmux.h" #include "tmate.h" void server_client_check_focus(struct window_pane *); void server_client_check_resize(struct window_pane *); void server_client_check_mouse(struct client *, struct window_pane *); void server_client_repeat_timer(int, short, void *); void server_client_check_exit(struct client *); void server_client_check_redraw(struct client *); void server_client_set_title(struct client *); void server_client_reset_state(struct client *); int server_client_assume_paste(struct session *); int server_client_msg_dispatch(struct client *); void server_client_msg_command(struct client *, struct msg_command_data *); void server_client_msg_identify( struct client *, struct msg_identify_data *, int); void server_client_msg_shell(struct client *); /* Create a new client. */ void server_client_create(int fd) { struct client *c; u_int i; setblocking(fd, 0); c = xcalloc(1, sizeof *c); c->references = 0; imsg_init(&c->ibuf, fd); server_update_event(c); if (gettimeofday(&c->creation_time, NULL) != 0) fatal("gettimeofday failed"); memcpy(&c->activity_time, &c->creation_time, sizeof c->activity_time); c->cmdq = cmdq_new(c); c->cmdq->client_exit = 1; c->stdin_data = evbuffer_new (); c->stdout_data = evbuffer_new (); c->stderr_data = evbuffer_new (); c->tty.fd = -1; c->title = NULL; c->session = NULL; c->last_session = NULL; c->tty.sx = 80; c->tty.sy = 24; screen_init(&c->status, c->tty.sx, 1, 0); RB_INIT(&c->status_new); RB_INIT(&c->status_old); c->message_string = NULL; ARRAY_INIT(&c->message_log); c->prompt_string = NULL; c->prompt_buffer = NULL; c->prompt_index = 0; c->tty.mouse.xb = c->tty.mouse.button = 3; c->tty.mouse.x = c->tty.mouse.y = -1; c->tty.mouse.lx = c->tty.mouse.ly = -1; c->tty.mouse.sx = c->tty.mouse.sy = -1; c->tty.mouse.event = MOUSE_EVENT_UP; c->tty.mouse.flags = 0; c->flags |= CLIENT_FOCUSED; evtimer_set(&c->repeat_timer, server_client_repeat_timer, c); for (i = 0; i < ARRAY_LENGTH(&clients); i++) { if (ARRAY_ITEM(&clients, i) == NULL) { ARRAY_SET(&clients, i, c); return; } } ARRAY_ADD(&clients, c); log_debug("new client %d", fd); } /* Open client terminal if needed. */ int server_client_open(struct client *c, struct session *s, char **cause) { struct options *oo = s != NULL ? &s->options : &global_s_options; char *overrides; if (c->flags & CLIENT_CONTROL) return (0); if (!(c->flags & CLIENT_TERMINAL)) { *cause = xstrdup ("not a terminal"); return (-1); } overrides = options_get_string(oo, "terminal-overrides"); if (tty_open(&c->tty, overrides, cause) != 0) return (-1); return (0); } /* Lost a client. */ void server_client_lost(struct client *c) { struct message_entry *msg; u_int i; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { if (ARRAY_ITEM(&clients, i) == c) ARRAY_SET(&clients, i, NULL); } log_debug("lost client %d", c->ibuf.fd); /* * If CLIENT_TERMINAL hasn't been set, then tty_init hasn't been called * and tty_free might close an unrelated fd. */ if (c->flags & CLIENT_TERMINAL) tty_free(&c->tty); evbuffer_free (c->stdin_data); evbuffer_free (c->stdout_data); if (c->stderr_data != c->stdout_data) evbuffer_free (c->stderr_data); status_free_jobs(&c->status_new); status_free_jobs(&c->status_old); screen_free(&c->status); free(c->title); evtimer_del(&c->repeat_timer); if (event_initialized(&c->identify_timer)) evtimer_del(&c->identify_timer); free(c->message_string); if (event_initialized (&c->message_timer)) evtimer_del(&c->message_timer); for (i = 0; i < ARRAY_LENGTH(&c->message_log); i++) { msg = &ARRAY_ITEM(&c->message_log, i); free(msg->msg); } ARRAY_FREE(&c->message_log); free(c->prompt_string); free(c->prompt_buffer); free(c->cwd); c->cmdq->dead = 1; cmdq_free(c->cmdq); c->cmdq = NULL; environ_free(&c->environ); close(c->ibuf.fd); imsg_clear(&c->ibuf); if (event_initialized(&c->event)) event_del(&c->event); for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) { if (ARRAY_ITEM(&dead_clients, i) == NULL) { ARRAY_SET(&dead_clients, i, c); break; } } if (i == ARRAY_LENGTH(&dead_clients)) ARRAY_ADD(&dead_clients, c); c->flags |= CLIENT_DEAD; server_add_accept(0); /* may be more file descriptors now */ recalculate_sizes(); server_check_unattached(); server_update_socket(); } /* Process a single client event. */ void server_client_callback(int fd, short events, void *data) { struct client *c = data; if (c->flags & CLIENT_DEAD) return; if (fd == c->ibuf.fd) { if (events & EV_WRITE && msgbuf_write(&c->ibuf.w) < 0) goto client_lost; if (c->flags & CLIENT_BAD) { if (c->ibuf.w.queued == 0) goto client_lost; return; } if (events & EV_READ && server_client_msg_dispatch(c) != 0) goto client_lost; } server_push_stdout(c); server_push_stderr(c); server_update_event(c); return; client_lost: server_client_lost(c); } /* Handle client status timer. */ void server_client_status_timer(void) { struct client *c; struct session *s; struct timeval tv; u_int i; int interval; time_t difference; if (gettimeofday(&tv, NULL) != 0) fatal("gettimeofday failed"); for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c == NULL || c->session == NULL) continue; if (c->message_string != NULL || c->prompt_string != NULL) { /* * Don't need timed redraw for messages/prompts so bail * now. The status timer isn't reset when they are * redrawn anyway. */ continue; } s = c->session; if (!options_get_number(&s->options, "status")) #ifdef TMATE if (!(c->flags & CLIENT_FORCE_STATUS)) #endif continue; interval = options_get_number(&s->options, "status-interval"); difference = tv.tv_sec - c->status_timer.tv_sec; if (difference >= interval) { status_update_jobs(c); c->flags |= CLIENT_STATUS; } } } /* Check for mouse keys. */ void server_client_check_mouse(struct client *c, struct window_pane *wp) { struct session *s = c->session; struct options *oo = &s->options; struct mouse_event *m = &c->tty.mouse; int statusat; statusat = status_at_line(c); /* Is this a window selection click on the status line? */ if (statusat != -1 && m->y == (u_int)statusat && options_get_number(oo, "mouse-select-window")) { if (m->event & MOUSE_EVENT_CLICK) { status_set_window_at(c, m->x); } else if (m->event == MOUSE_EVENT_WHEEL) { if (m->wheel == MOUSE_WHEEL_UP) session_previous(c->session, 0); else if (m->wheel == MOUSE_WHEEL_DOWN) session_next(c->session, 0); server_redraw_session(s); } recalculate_sizes(); return; } /* * Not on status line - adjust mouse position if status line is at the * top and limit if at the bottom. From here on a struct mouse * represents the offset onto the window itself. */ if (statusat == 0 && m->y > 0) m->y--; else if (statusat > 0 && m->y >= (u_int)statusat) m->y = statusat - 1; /* Is this a pane selection? Allow down only in copy mode. */ if (options_get_number(oo, "mouse-select-pane") && (m->event == MOUSE_EVENT_DOWN || wp->mode != &window_copy_mode)) { window_set_active_at(wp->window, m->x, m->y); server_redraw_window_borders(wp->window); wp = wp->window->active; /* may have changed */ } /* Check if trying to resize pane. */ if (options_get_number(oo, "mouse-resize-pane")) layout_resize_pane_mouse(c); /* Update last and pass through to client. */ window_pane_mouse(wp, c->session, m); } /* Is this fast enough to probably be a paste? */ 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) return (1); return (0); } /* Handle data key input from client. */ void server_client_handle_key(struct client *c, int key) { struct session *s; struct window *w; struct window_pane *wp; struct timeval tv; struct key_binding *bd; int xtimeout, isprefix, ispaste; /* Check the client is good to accept input. */ if ((c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0) return; if (c->session == NULL) return; s = c->session; /* Update the activity timer. */ if (gettimeofday(&c->activity_time, NULL) != 0) fatal("gettimeofday failed"); memcpy(&s->last_activity_time, &s->activity_time, sizeof s->last_activity_time); memcpy(&s->activity_time, &c->activity_time, sizeof s->activity_time); w = c->session->curw->window; wp = w->active; /* Special case: number keys jump to pane in identify mode. */ if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') { if (c->flags & CLIENT_READONLY) return; window_unzoom(w); wp = window_pane_at_index(w, key - '0'); if (wp != NULL && window_pane_visible(wp)) window_set_active_pane(w, wp); server_clear_identify(c); return; } /* Handle status line. */ if (!(c->flags & CLIENT_READONLY)) { #ifdef TMATE if (!(c->flags & CLIENT_FORCE_STATUS)) #endif status_message_clear(c); server_clear_identify(c); } if (c->prompt_string != NULL) { if (!(c->flags & CLIENT_READONLY)) status_prompt_key(c, key); return; } /* Check for mouse keys. */ if (key == KEYC_MOUSE) { if (c->flags & CLIENT_READONLY) return; server_client_check_mouse(c, wp); return; } /* Is this a prefix key? */ if (key == options_get_number(&s->options, "prefix")) isprefix = 1; else if (key == options_get_number(&s->options, "prefix2")) isprefix = 1; else isprefix = 0; /* Treat prefix as a regular key when pasting is detected. */ ispaste = server_client_assume_paste(s); if (ispaste) isprefix = 0; /* No previous prefix key. */ if (!(c->flags & CLIENT_PREFIX)) { if (isprefix) { c->flags |= CLIENT_PREFIX; server_status_client(c); return; } /* Try as a non-prefix key binding. */ if (ispaste || (bd = key_bindings_lookup(key)) == NULL) { if (!(c->flags & CLIENT_READONLY)) window_pane_key(wp, s, key); } else key_bindings_dispatch(bd, c); return; } /* Prefix key already pressed. Reset prefix and lookup key. */ c->flags &= ~CLIENT_PREFIX; server_status_client(c); if ((bd = key_bindings_lookup(key | KEYC_PREFIX)) == NULL) { /* If repeating, treat this as a key, else ignore. */ if (c->flags & CLIENT_REPEAT) { c->flags &= ~CLIENT_REPEAT; if (isprefix) c->flags |= CLIENT_PREFIX; else if (!(c->flags & CLIENT_READONLY)) window_pane_key(wp, s, key); } return; } /* If already repeating, but this key can't repeat, skip it. */ if (c->flags & CLIENT_REPEAT && !bd->can_repeat) { c->flags &= ~CLIENT_REPEAT; if (isprefix) c->flags |= CLIENT_PREFIX; else if (!(c->flags & CLIENT_READONLY)) window_pane_key(wp, s, key); return; } /* If this key can repeat, reset the repeat flags and timer. */ xtimeout = options_get_number(&s->options, "repeat-time"); if (xtimeout != 0 && bd->can_repeat) { c->flags |= CLIENT_PREFIX|CLIENT_REPEAT; tv.tv_sec = xtimeout / 1000; tv.tv_usec = (xtimeout % 1000) * 1000L; evtimer_del(&c->repeat_timer); evtimer_add(&c->repeat_timer, &tv); } /* Dispatch the command. */ key_bindings_dispatch(bd, c); } /* Client functions that need to happen every loop. */ void server_client_loop(void) { struct client *c; struct window *w; struct window_pane *wp; u_int i; int tmate_should_sync_layout = 0; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c == NULL) continue; server_client_check_exit(c); if (c->session != NULL) { server_client_check_redraw(c); server_client_reset_state(c); } } /* * Any windows will have been redrawn as part of clients, so clear * their flags now. Also check pane focus and resize. */ for (i = 0; i < ARRAY_LENGTH(&windows); i++) { w = ARRAY_ITEM(&windows, i); if (w == NULL) continue; if (w->flags & WINDOW_REDRAW) tmate_should_sync_layout = 1; w->flags &= ~WINDOW_REDRAW; TAILQ_FOREACH(wp, &w->panes, entry) { server_client_check_focus(wp); server_client_check_resize(wp); wp->flags &= ~PANE_REDRAW; } } if (tmate_should_sync_layout) tmate_sync_layout(); } /* Check if pane should be resized. */ void server_client_check_resize(struct window_pane *wp) { struct winsize ws; if (wp->fd == -1 || !(wp->flags & PANE_RESIZE)) return; memset(&ws, 0, sizeof ws); ws.ws_col = wp->sx; ws.ws_row = wp->sy; if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1) { #ifdef __sun /* * Some versions of Solaris apparently can return an error when * resizing; don't know why this happens, can't reproduce on * other platforms and ignoring it doesn't seem to cause any * issues. */ if (errno != EINVAL) #endif fatal("ioctl failed"); } wp->flags &= ~PANE_RESIZE; } /* Check whether pane should be focused. */ void server_client_check_focus(struct window_pane *wp) { u_int i; struct client *c; /* If we don't care about focus, forget it. */ if (!(wp->base.mode & MODE_FOCUSON)) return; /* 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. */ for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c == NULL || c->session == NULL) continue; if (!(c->flags & CLIENT_FOCUSED)) continue; if (c->session->flags & SESSION_UNATTACHED) continue; if (c->session->curw->window == wp->window) goto focused; } not_focused: if (wp->flags & PANE_FOCUSED) bufferevent_write(wp->event, "\033[O", 3); wp->flags &= ~PANE_FOCUSED; return; focused: if (!(wp->flags & PANE_FOCUSED)) bufferevent_write(wp->event, "\033[I", 3); 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. */ void server_client_reset_state(struct client *c) { struct window *w = c->session->curw->window; struct window_pane *wp = w->active; struct screen *s = wp->screen; struct options *oo = &c->session->options; struct options *wo = &w->options; int status, mode, o; if (c->flags & CLIENT_SUSPENDED) return; if (c->flags & CLIENT_CONTROL) return; tty_region(&c->tty, 0, c->tty.sy - 1); status = options_get_number(oo, "status"); #ifdef TMATE if (c->flags & CLIENT_FORCE_STATUS) status = 1; #endif if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status) tty_cursor(&c->tty, 0, 0); else { o = status && options_get_number (oo, "status-position") == 0; tty_cursor(&c->tty, wp->xoff + s->cx, o + wp->yoff + s->cy); } /* * Resizing panes with the mouse requires at least button mode to give * a smooth appearance. */ mode = s->mode; if ((c->tty.mouse.flags & MOUSE_RESIZE_PANE) && !(mode & (MODE_MOUSE_BUTTON|MODE_MOUSE_ANY))) mode |= MODE_MOUSE_BUTTON; /* * Any mode will do for mouse-select-pane, but set standard mode if * none. */ if ((mode & ALL_MOUSE_MODES) == 0) { if (TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry) != NULL && options_get_number(oo, "mouse-select-pane")) mode |= MODE_MOUSE_STANDARD; else if (options_get_number(oo, "mouse-resize-pane")) mode |= MODE_MOUSE_STANDARD; else if (options_get_number(oo, "mouse-select-window")) mode |= MODE_MOUSE_STANDARD; else if (options_get_number(wo, "mode-mouse")) mode |= MODE_MOUSE_STANDARD; } /* * Set UTF-8 mouse input if required. If the terminal is UTF-8, the * user has set mouse-utf8 and any mouse mode is in effect, turn on * UTF-8 mouse input. If the receiving terminal hasn't requested it * (that is, it isn't in s->mode), then it'll be converted in * input_mouse. */ if ((c->tty.flags & TTY_UTF8) && (mode & ALL_MOUSE_MODES) && options_get_number(oo, "mouse-utf8")) mode |= MODE_MOUSE_UTF8; else mode &= ~MODE_MOUSE_UTF8; /* Set the terminal mode and reset attributes. */ tty_update_mode(&c->tty, mode, s); tty_reset(&c->tty); } /* Repeat time callback. */ void server_client_repeat_timer(unused int fd, unused short events, void *data) { struct client *c = data; if (c->flags & CLIENT_REPEAT) { if (c->flags & CLIENT_PREFIX) server_status_client(c); c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT); } } /* Check if client should be exited. */ void server_client_check_exit(struct client *c) { struct msg_exit_data exitdata; if (!(c->flags & CLIENT_EXIT)) return; if (EVBUFFER_LENGTH(c->stdin_data) != 0) return; if (EVBUFFER_LENGTH(c->stdout_data) != 0) return; if (EVBUFFER_LENGTH(c->stderr_data) != 0) return; exitdata.retcode = c->retcode; server_write_client(c, MSG_EXIT, &exitdata, sizeof exitdata); c->flags &= ~CLIENT_EXIT; } /* Check for client redraws. */ void server_client_check_redraw(struct client *c) { struct session *s = c->session; struct window_pane *wp; int flags, redraw; if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED)) return; flags = c->tty.flags & TTY_FREEZE; c->tty.flags &= ~TTY_FREEZE; if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) { if (options_get_number(&s->options, "set-titles")) server_client_set_title(c); if (c->message_string != NULL) redraw = status_message_redraw(c); else if (c->prompt_string != NULL) redraw = status_prompt_redraw(c); else redraw = status_redraw(c); if (!redraw) c->flags &= ~CLIENT_STATUS; } if (c->flags & CLIENT_REDRAW) { screen_redraw_screen(c, 0, 0); c->flags &= ~(CLIENT_STATUS|CLIENT_BORDERS); } else if (c->flags & CLIENT_REDRAWWINDOW) { TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) screen_redraw_pane(c, wp); c->flags &= ~CLIENT_REDRAWWINDOW; } else { TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) { if (wp->flags & PANE_REDRAW) screen_redraw_pane(c, wp); } } if (c->flags & CLIENT_BORDERS) screen_redraw_screen(c, 0, 1); if (c->flags & CLIENT_STATUS) screen_redraw_screen(c, 1, 0); c->tty.flags |= flags; c->flags &= ~(CLIENT_REDRAW|CLIENT_STATUS|CLIENT_BORDERS); } /* Set client title. */ void server_client_set_title(struct client *c) { struct session *s = c->session; const char *template; char *title; template = options_get_string(&s->options, "set-titles-string"); title = status_replace(c, NULL, NULL, NULL, template, time(NULL), 1); if (c->title == NULL || strcmp(title, c->title) != 0) { free(c->title); c->title = xstrdup(title); tty_set_title(&c->tty, c->title); } free(title); } /* Dispatch message from client. */ int server_client_msg_dispatch(struct client *c) { struct imsg imsg; struct msg_command_data commanddata; struct msg_identify_data identifydata; struct msg_environ_data environdata; struct msg_stdin_data stdindata; ssize_t n, datalen; if ((n = imsg_read(&c->ibuf)) == -1 || n == 0) return (-1); for (;;) { if ((n = imsg_get(&c->ibuf, &imsg)) == -1) return (-1); if (n == 0) return (0); datalen = imsg.hdr.len - IMSG_HEADER_SIZE; if (imsg.hdr.peerid != PROTOCOL_VERSION) { server_write_client(c, MSG_VERSION, NULL, 0); c->flags |= CLIENT_BAD; imsg_free(&imsg); continue; } log_debug("got %d from client %d", imsg.hdr.type, c->ibuf.fd); switch (imsg.hdr.type) { case MSG_COMMAND: if (datalen != sizeof commanddata) fatalx("bad MSG_COMMAND size"); memcpy(&commanddata, imsg.data, sizeof commanddata); server_client_msg_command(c, &commanddata); break; case MSG_IDENTIFY: if (datalen != sizeof identifydata) fatalx("bad MSG_IDENTIFY size"); if (imsg.fd == -1) fatalx("MSG_IDENTIFY missing fd"); memcpy(&identifydata, imsg.data, sizeof identifydata); server_client_msg_identify(c, &identifydata, imsg.fd); break; case MSG_STDIN: if (datalen != sizeof stdindata) fatalx("bad MSG_STDIN size"); memcpy(&stdindata, imsg.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; if (tty_resize(&c->tty)) { recalculate_sizes(); server_redraw_client(c); } break; case MSG_EXITING: if (datalen != 0) fatalx("bad MSG_EXITING size"); c->session = NULL; tty_close(&c->tty); server_write_client(c, MSG_EXITED, NULL, 0); break; case MSG_WAKEUP: case MSG_UNLOCK: if (datalen != 0) fatalx("bad MSG_WAKEUP size"); if (!(c->flags & CLIENT_SUSPENDED)) break; c->flags &= ~CLIENT_SUSPENDED; if (gettimeofday(&c->activity_time, NULL) != 0) fatal("gettimeofday"); if (c->session != NULL) session_update_activity(c->session); tty_start_tty(&c->tty); server_redraw_client(c); recalculate_sizes(); break; case MSG_ENVIRON: if (datalen != sizeof environdata) fatalx("bad MSG_ENVIRON size"); memcpy(&environdata, imsg.data, sizeof environdata); environdata.var[(sizeof environdata.var) - 1] = '\0'; if (strchr(environdata.var, '=') != NULL) environ_put(&c->environ, environdata.var); break; case MSG_SHELL: if (datalen != 0) fatalx("bad MSG_SHELL size"); server_client_msg_shell(c); break; default: fatalx("unexpected message"); } imsg_free(&imsg); } } /* Handle command message. */ void server_client_msg_command(struct client *c, struct msg_command_data *data) { struct cmd_list *cmdlist = NULL; int argc; char **argv, *cause; argc = data->argc; data->argv[(sizeof data->argv) - 1] = '\0'; if (cmd_unpack_argv(data->argv, sizeof data->argv, argc, &argv) != 0) { cmdq_error(c->cmdq, "command too long"); goto error; } if (argc == 0) { argc = 1; argv = xcalloc(1, sizeof *argv); *argv = xstrdup("new-session"); } if ((cmdlist = cmd_list_parse(argc, argv, NULL, 0, &cause)) == NULL) { cmdq_error(c->cmdq, "%s", cause); cmd_free_argv(argc, argv); goto error; } cmd_free_argv(argc, argv); cmdq_run(c->cmdq, cmdlist); cmd_list_free(cmdlist); return; error: if (cmdlist != NULL) cmd_list_free(cmdlist); c->flags |= CLIENT_EXIT; } /* Handle identify message. */ void server_client_msg_identify( struct client *c, struct msg_identify_data *data, int fd) { c->cwd = NULL; data->cwd[(sizeof data->cwd) - 1] = '\0'; if (*data->cwd != '\0') c->cwd = xstrdup(data->cwd); if (data->flags & IDENTIFY_CONTROL) { c->stdin_callback = control_callback; evbuffer_free(c->stderr_data); c->stderr_data = c->stdout_data; c->flags |= CLIENT_CONTROL; if (data->flags & IDENTIFY_TERMIOS) evbuffer_add_printf(c->stdout_data, "\033P1000p"); server_write_client(c, MSG_STDIN, NULL, 0); c->tty.fd = -1; c->tty.log_fd = -1; close(fd); return; } if (!isatty(fd)) { close(fd); return; } data->term[(sizeof data->term) - 1] = '\0'; tty_init(&c->tty, c, fd, data->term); if (data->flags & IDENTIFY_UTF8) c->tty.flags |= TTY_UTF8; if (data->flags & IDENTIFY_256COLOURS) c->tty.term_flags |= TERM_256COLOURS; else if (data->flags & IDENTIFY_88COLOURS) c->tty.term_flags |= TERM_88COLOURS; tty_resize(&c->tty); if (!(data->flags & IDENTIFY_CONTROL)) c->flags |= CLIENT_TERMINAL; } /* Handle shell message. */ void server_client_msg_shell(struct client *c) { struct msg_shell_data data; const char *shell; shell = options_get_string(&global_s_options, "default-shell"); if (*shell == '\0' || areshell(shell)) shell = _PATH_BSHELL; if (strlcpy(data.shell, shell, sizeof data.shell) >= sizeof data.shell) strlcpy(data.shell, _PATH_BSHELL, sizeof data.shell); server_write_client(c, MSG_SHELL, &data, sizeof data); c->flags |= CLIENT_BAD; /* it will die after exec */ } tmate-1.8.10/server-fn.c000066400000000000000000000314311242461015400147670ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include "tmux.h" struct session *server_next_session(struct session *); void server_callback_identify(int, short, void *); void server_fill_environ(struct session *s, struct environ *env) { char var[MAXPATHLEN], *term; u_int idx; long pid; if (s != NULL) { term = options_get_string(&s->options, "default-terminal"); environ_set(env, "TERM", term); idx = s->id; } else idx = -1; pid = getpid(); xsnprintf(var, sizeof var, "%s,%ld,%d", socket_path, pid, idx); environ_set(env, "TMUX", var); } void server_write_ready(struct client *c) { if (c->flags & CLIENT_CONTROL) return; server_write_client(c, MSG_READY, NULL, 0); } int server_write_client( struct client *c, enum msgtype type, const void *buf, size_t len) { struct imsgbuf *ibuf = &c->ibuf; int error; if (c->flags & CLIENT_BAD) return (-1); log_debug("writing %d to client %d", type, c->ibuf.fd); error = imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, -1, (void *) buf, len); if (error == 1) server_update_event(c); return (error == 1 ? 0 : -1); } void server_write_session( struct session *s, enum msgtype type, const void *buf, size_t len) { struct client *c; u_int i; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c == NULL || c->session == NULL) continue; if (c->session == s) server_write_client(c, type, buf, len); } } void server_redraw_client(struct client *c) { c->flags |= CLIENT_REDRAW; } void server_status_client(struct client *c) { c->flags |= CLIENT_STATUS; } void server_redraw_session(struct session *s) { struct client *c; u_int i; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c == NULL || c->session == NULL) continue; if (c->session == s) server_redraw_client(c); } } void server_redraw_session_group(struct session *s) { struct session_group *sg; if ((sg = session_group_find(s)) == NULL) server_redraw_session(s); else { TAILQ_FOREACH(s, &sg->sessions, gentry) server_redraw_session(s); } } void server_status_session(struct session *s) { struct client *c; u_int i; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c == NULL || c->session == NULL) continue; if (c->session == s) server_status_client(c); } } void server_status_session_group(struct session *s) { struct session_group *sg; if ((sg = session_group_find(s)) == NULL) server_status_session(s); else { TAILQ_FOREACH(s, &sg->sessions, gentry) server_status_session(s); } } void server_redraw_window(struct window *w) { struct client *c; u_int i; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c == NULL || c->session == NULL) continue; if (c->session->curw->window == w) server_redraw_client(c); } w->flags |= WINDOW_REDRAW; } void server_redraw_window_borders(struct window *w) { struct client *c; u_int i; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c == NULL || c->session == NULL) continue; if (c->session->curw->window == w) c->flags |= CLIENT_BORDERS; } } void server_status_window(struct window *w) { struct session *s; /* * This is slightly different. We want to redraw the status line of any * clients containing this window rather than anywhere it is the * current window. */ RB_FOREACH(s, sessions, &sessions) { if (session_has(s, w) != NULL) server_status_session(s); } } void server_lock(void) { struct client *c; u_int i; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c == NULL || c->session == NULL) continue; server_lock_client(c); } } void server_lock_session(struct session *s) { struct client *c; u_int i; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c == NULL || c->session == NULL || c->session != s) continue; server_lock_client(c); } } void server_lock_client(struct client *c) { const char *cmd; size_t cmdlen; struct msg_lock_data lockdata; if (c->flags & CLIENT_CONTROL) return; if (c->flags & CLIENT_SUSPENDED) return; cmd = options_get_string(&c->session->options, "lock-command"); cmdlen = strlcpy(lockdata.cmd, cmd, sizeof lockdata.cmd); if (cmdlen >= sizeof lockdata.cmd) return; tty_stop_tty(&c->tty); tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_SMCUP)); tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_CLEAR)); tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_E3)); c->flags |= CLIENT_SUSPENDED; server_write_client(c, MSG_LOCK, &lockdata, sizeof lockdata); } void server_kill_window(struct window *w) { struct session *s, *next_s; struct winlink *wl; next_s = RB_MIN(sessions, &sessions); while (next_s != NULL) { s = next_s; next_s = RB_NEXT(sessions, &sessions, s); if (session_has(s, w) == NULL) continue; while ((wl = winlink_find_by_window(&s->windows, w)) != NULL) { if (session_detach(s, wl)) { server_destroy_session_group(s); break; } else server_redraw_session_group(s); } if (options_get_number(&s->options, "renumber-windows")) session_renumber_windows(s); } } int server_link_window(struct session *src, struct winlink *srcwl, struct session *dst, int dstidx, int killflag, int selectflag, char **cause) { struct winlink *dstwl; struct session_group *srcsg, *dstsg; srcsg = session_group_find(src); dstsg = session_group_find(dst); if (src != dst && srcsg != NULL && dstsg != NULL && srcsg == dstsg) { xasprintf(cause, "sessions are grouped"); return (-1); } dstwl = NULL; if (dstidx != -1) dstwl = winlink_find_by_index(&dst->windows, dstidx); if (dstwl != NULL) { if (dstwl->window == srcwl->window) { xasprintf(cause, "same index: %d", dstidx); return (-1); } if (killflag) { /* * Can't use session_detach as it will destroy session * if this makes it empty. */ notify_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) { struct window *w = wp->window; int old_fd; struct screen_write_ctx ctx; struct grid_cell gc; old_fd = wp->fd; if (wp->fd != -1) { bufferevent_free(wp->event); close(wp->fd); wp->fd = -1; } if (options_get_number(&w->options, "remain-on-exit")) { if (old_fd == -1) return; 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); screen_write_linefeed(&ctx, 1); memcpy(&gc, &grid_default_cell, sizeof gc); gc.attr |= GRID_ATTR_BRIGHT; screen_write_puts(&ctx, &gc, "Pane is dead"); screen_write_stop(&ctx); wp->flags |= PANE_REDRAW; return; } 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); } void server_destroy_session_group(struct session *s) { struct session_group *sg; if ((sg = session_group_find(s)) == NULL) server_destroy_session(s); else { TAILQ_FOREACH(s, &sg->sessions, gentry) server_destroy_session(s); TAILQ_REMOVE(&session_groups, sg, entry); free(sg); } } struct session * server_next_session(struct session *s) { struct session *s_loop, *s_out; s_out = NULL; RB_FOREACH(s_loop, sessions, &sessions) { if (s_loop == s) continue; if (s_out == NULL || timercmp(&s_loop->activity_time, &s_out->activity_time, <)) s_out = s_loop; } return (s_out); } void server_destroy_session(struct session *s) { struct client *c; struct session *s_new; u_int i; if (!options_get_number(&s->options, "detach-on-destroy")) s_new = server_next_session(s); else s_new = NULL; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c == NULL || c->session != s) continue; if (s_new == NULL) { c->session = NULL; c->flags |= CLIENT_EXIT; } else { c->last_session = NULL; c->session = s_new; notify_attached_session_changed(c); session_update_activity(s_new); server_redraw_client(c); } } recalculate_sizes(); } void server_check_unattached (void) { struct session *s; /* * If any sessions are no longer attached and have destroy-unattached * set, collect them. */ RB_FOREACH(s, sessions, &sessions) { if (!(s->flags & SESSION_UNATTACHED)) continue; if (options_get_number (&s->options, "destroy-unattached")) session_destroy(s); } } void server_set_identify(struct client *c) { struct timeval tv; int delay; delay = options_get_number(&c->session->options, "display-panes-time"); tv.tv_sec = delay / 1000; tv.tv_usec = (delay % 1000) * 1000L; if (event_initialized (&c->identify_timer)) evtimer_del(&c->identify_timer); evtimer_set(&c->identify_timer, server_callback_identify, c); evtimer_add(&c->identify_timer, &tv); c->flags |= CLIENT_IDENTIFY; c->tty.flags |= (TTY_FREEZE|TTY_NOCURSOR); server_redraw_client(c); } void server_clear_identify(struct client *c) { if (c->flags & CLIENT_IDENTIFY) { c->flags &= ~CLIENT_IDENTIFY; c->tty.flags &= ~(TTY_FREEZE|TTY_NOCURSOR); server_redraw_client(c); } } void server_callback_identify(unused int fd, unused short events, void *data) { struct client *c = data; server_clear_identify(c); } void server_update_event(struct client *c) { short events; events = 0; if (!(c->flags & CLIENT_BAD)) events |= EV_READ; if (c->ibuf.w.queued > 0) events |= EV_WRITE; if (event_initialized(&c->event)) event_del(&c->event); event_set(&c->event, c->ibuf.fd, events, server_client_callback, c); event_add(&c->event, NULL); } /* Push stdout to client if possible. */ void server_push_stdout(struct client *c) { struct msg_stdout_data data; size_t size; size = EVBUFFER_LENGTH(c->stdout_data); if (size == 0) return; if (size > sizeof data.data) size = sizeof data.data; memcpy(data.data, EVBUFFER_DATA(c->stdout_data), size); data.size = size; if (server_write_client(c, MSG_STDOUT, &data, sizeof data) == 0) evbuffer_drain(c->stdout_data, size); } /* Push stderr to client if possible. */ void server_push_stderr(struct client *c) { struct msg_stderr_data data; size_t size; if (c->stderr_data == c->stdout_data) { server_push_stdout(c); return; } size = EVBUFFER_LENGTH(c->stderr_data); if (size == 0) return; if (size > sizeof data.data) size = sizeof data.data; memcpy(data.data, EVBUFFER_DATA(c->stderr_data), size); data.size = size; if (server_write_client(c, MSG_STDERR, &data, sizeof data) == 0) evbuffer_drain(c->stderr_data, size); } /* Set stdin callback. */ 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 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); server_write_client(c, MSG_STDIN, NULL, 0); return (0); } void server_unzoom_window(struct window *w) { window_unzoom(w); server_redraw_window(w); server_status_window(w); } tmate-1.8.10/server-window.c000066400000000000000000000147251242461015400157020ustar00rootroot00000000000000/* $Id$ */ /* * 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" int server_window_check_bell(struct session *, struct winlink *); int server_window_check_activity(struct session *, struct winlink *); int server_window_check_silence(struct session *, struct winlink *); int server_window_check_content( struct session *, struct winlink *, struct window_pane *); void ring_bell(struct session *); /* Window functions that need to happen every loop. */ void server_window_loop(void) { struct window *w; struct winlink *wl; struct window_pane *wp; struct session *s; u_int i; for (i = 0; i < ARRAY_LENGTH(&windows); i++) { w = ARRAY_ITEM(&windows, i); if (w == NULL) continue; RB_FOREACH(s, sessions, &sessions) { wl = session_has(s, w); if (wl == NULL) continue; if (server_window_check_bell(s, wl) || server_window_check_activity(s, wl) || server_window_check_silence(s, wl)) server_status_session(s); TAILQ_FOREACH(wp, &w->panes, entry) server_window_check_content(s, wl, wp); } } } /* Check for bell in window. */ int server_window_check_bell(struct session *s, struct winlink *wl) { struct client *c; struct window *w = wl->window; u_int i; int action, visual; if (!(w->flags & WINDOW_BELL) || wl->flags & WINLINK_BELL) return (0); if (s->curw != wl || s->flags & SESSION_UNATTACHED) wl->flags |= WINLINK_BELL; if (s->flags & SESSION_UNATTACHED) return (0); if (s->curw->window == wl->window) w->flags &= ~WINDOW_BELL; visual = options_get_number(&s->options, "visual-bell"); action = options_get_number(&s->options, "bell-action"); if (action == BELL_NONE) return (0); for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c == NULL || c->session != s || (c->flags & CLIENT_CONTROL)) continue; if (!visual) { tty_bell(&c->tty); continue; } if (c->session->curw->window == w) status_message_set(c, "Bell in current window"); else if (action == BELL_ANY) { status_message_set(c, "Bell in window %u", winlink_find_by_window(&s->windows, w)->idx); } } return (1); } /* Check for activity in window. */ int server_window_check_activity(struct session *s, struct winlink *wl) { struct client *c; struct window *w = wl->window; u_int i; if (s->curw->window == wl->window) w->flags &= ~WINDOW_ACTIVITY; if (!(w->flags & WINDOW_ACTIVITY) || wl->flags & WINLINK_ACTIVITY) return (0); if (s->curw == wl && !(s->flags & SESSION_UNATTACHED)) return (0); if (!options_get_number(&w->options, "monitor-activity")) return (0); if (options_get_number(&s->options, "bell-on-alert")) ring_bell(s); wl->flags |= WINLINK_ACTIVITY; if (options_get_number(&s->options, "visual-activity")) { for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c == NULL || c->session != s) continue; status_message_set(c, "Activity in window %u", winlink_find_by_window(&s->windows, w)->idx); } } return (1); } /* Check for silence in window. */ int server_window_check_silence(struct session *s, struct winlink *wl) { struct client *c; struct window *w = wl->window; struct timeval timer; u_int i; int silence_interval, timer_difference; if (!(w->flags & WINDOW_SILENCE) || wl->flags & WINLINK_SILENCE) return (0); if (s->curw == wl && !(s->flags & SESSION_UNATTACHED)) { /* * Reset the timer for this window if we've focused it. We * don't want the timer tripping as soon as we've switched away * from this window. */ if (gettimeofday(&w->silence_timer, NULL) != 0) fatal("gettimeofday failed."); return (0); } silence_interval = options_get_number(&w->options, "monitor-silence"); if (silence_interval == 0) return (0); if (gettimeofday(&timer, NULL) != 0) fatal("gettimeofday"); timer_difference = timer.tv_sec - w->silence_timer.tv_sec; if (timer_difference <= silence_interval) return (0); if (options_get_number(&s->options, "bell-on-alert")) ring_bell(s); wl->flags |= WINLINK_SILENCE; if (options_get_number(&s->options, "visual-silence")) { for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c == NULL || c->session != s) continue; status_message_set(c, "Silence in window %u", winlink_find_by_window(&s->windows, w)->idx); } } return (1); } /* Check for content change in window. */ int server_window_check_content( struct session *s, struct winlink *wl, struct window_pane *wp) { struct client *c; struct window *w = wl->window; u_int i; char *found, *ptr; /* Activity flag must be set for new content. */ if (s->curw->window == w) w->flags &= ~WINDOW_ACTIVITY; if (!(w->flags & WINDOW_ACTIVITY) || wl->flags & WINLINK_CONTENT) return (0); if (s->curw == wl && !(s->flags & SESSION_UNATTACHED)) return (0); ptr = options_get_string(&w->options, "monitor-content"); if (ptr == NULL || *ptr == '\0') return (0); if ((found = window_pane_search(wp, ptr, NULL)) == NULL) return (0); free(found); if (options_get_number(&s->options, "bell-on-alert")) ring_bell(s); wl->flags |= WINLINK_CONTENT; if (options_get_number(&s->options, "visual-content")) { for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c == NULL || c->session != s) continue; status_message_set(c, "Content in window %u", winlink_find_by_window(&s->windows, w)->idx); } } return (1); } /* Ring terminal bell. */ void ring_bell(struct session *s) { struct client *c; u_int i; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c != NULL && c->session == s && !(c->flags & CLIENT_CONTROL)) tty_bell(&c->tty); } } tmate-1.8.10/server.c000066400000000000000000000275101242461015400143710ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tmux.h" #include "tmate.h" /* * Main server functions. */ /* Client list. */ struct clients clients; struct clients dead_clients; int server_fd; int server_shutdown; struct event server_ev_accept; struct event server_ev_second; struct paste_stack global_buffers; int server_create_socket(void); void server_loop(void); int server_should_shutdown(void); void server_send_shutdown(void); void server_clean_dead(void); void server_accept_callback(int, short, void *); void server_signal_callback(int, short, void *); void server_child_signal(void); void server_child_exited(pid_t, int); void server_child_stopped(pid_t, int); void server_second_callback(int, short, void *); void server_lock_server(void); void server_lock_sessions(void); /* Create server socket. */ int server_create_socket(void) { struct sockaddr_un sa; size_t size; mode_t mask; int fd; memset(&sa, 0, sizeof sa); sa.sun_family = AF_UNIX; size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path); if (size >= sizeof sa.sun_path) { errno = ENAMETOOLONG; fatal("socket failed"); } unlink(sa.sun_path); if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) fatal("socket failed"); mask = umask(S_IXUSR|S_IXGRP|S_IRWXO); if (bind(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) fatal("bind failed"); umask(mask); if (listen(fd, 16) == -1) fatal("listen failed"); setblocking(fd, 0); server_update_socket(); return (fd); } /* Fork new server. */ int server_start(int lockfd, char *lockfile) { int pair[2]; struct timeval tv; char *cause; /* The first client is special and gets a socketpair; create it. */ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) fatal("socketpair failed"); switch (fork()) { case -1: fatal("fork failed"); case 0: break; default: close(pair[1]); return (pair[0]); } close(pair[0]); /* * Must daemonise before loading configuration as the PID changes so * $TMUX would be wrong for sessions created in the config file. */ if (daemon(1, 0) != 0) fatal("daemon failed"); /* event_init() was called in our parent, need to reinit. */ if (event_reinit(ev_base) != 0) fatal("event_reinit failed"); clear_signals(0); logfile("server"); log_debug("server started, pid %ld", (long) getpid()); ARRAY_INIT(&windows); RB_INIT(&all_window_panes); ARRAY_INIT(&clients); ARRAY_INIT(&dead_clients); RB_INIT(&sessions); RB_INIT(&dead_sessions); TAILQ_INIT(&session_groups); ARRAY_INIT(&global_buffers); mode_key_init_trees(); key_bindings_init(); utf8_build(); start_time = time(NULL); log_debug("socket path %s", socket_path); #ifdef HAVE_SETPROCTITLE setproctitle("server (%s)", socket_path); #endif server_fd = server_create_socket(); server_client_create(pair[1]); unlink(lockfile); free(lockfile); close(lockfd); cfg_cmd_q = cmdq_new(NULL); cfg_cmd_q->emptyfn = cfg_default_done; cfg_finished = 0; cfg_references = 1; ARRAY_INIT(&cfg_causes); if (access(SYSTEM_CFG, R_OK) == 0) { if (load_cfg(SYSTEM_CFG, cfg_cmd_q, &cause) == -1) { xasprintf(&cause, "%s: %s", SYSTEM_CFG, cause); ARRAY_ADD(&cfg_causes, cause); } } else if (errno != ENOENT) { xasprintf(&cause, "%s: %s", SYSTEM_CFG, strerror(errno)); ARRAY_ADD(&cfg_causes, cause); } if (cfg_file != NULL) { if (load_cfg(cfg_file, cfg_cmd_q, &cause) == -1) { xasprintf(&cause, "%s: %s", cfg_file, cause); ARRAY_ADD(&cfg_causes, cause); } } if (tmate_cfg_file != NULL) { if (load_cfg(tmate_cfg_file, cfg_cmd_q, &cause) == -1) { xasprintf(&cause, "%s: %s", tmate_cfg_file, cause); ARRAY_ADD(&cfg_causes, cause); } } tmate_session_init(); cmdq_continue(cfg_cmd_q); server_add_accept(0); memset(&tv, 0, sizeof tv); tv.tv_sec = 1; evtimer_set(&server_ev_second, server_second_callback, NULL); evtimer_add(&server_ev_second, &tv); set_signals(server_signal_callback); tmate_session_start(); server_loop(); exit(0); } /* Main server loop. */ void server_loop(void) { while (!server_should_shutdown()) { event_loop(EVLOOP_ONCE); server_window_loop(); server_client_loop(); key_bindings_clean(); server_clean_dead(); } } /* Check if the server should be shutting down (no more clients or sessions). */ int server_should_shutdown(void) { u_int i; if (!options_get_number(&global_options, "exit-unattached")) { if (!RB_EMPTY(&sessions)) return (0); } for (i = 0; i < ARRAY_LENGTH(&clients); i++) { if (ARRAY_ITEM(&clients, i) != NULL) return (0); } return (1); } /* Shutdown the server by killing all clients and windows. */ void server_send_shutdown(void) { struct client *c; struct session *s, *next_s; u_int i; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c != NULL) { if (c->flags & (CLIENT_BAD|CLIENT_SUSPENDED)) server_client_lost(c); else server_write_client(c, MSG_SHUTDOWN, NULL, 0); c->session = NULL; } } s = RB_MIN(sessions, &sessions); while (s != NULL) { next_s = RB_NEXT(sessions, &sessions, s); session_destroy(s); s = next_s; } } /* Free dead, unreferenced clients and sessions. */ void server_clean_dead(void) { struct session *s, *next_s; struct client *c; u_int i; s = RB_MIN(sessions, &dead_sessions); while (s != NULL) { next_s = RB_NEXT(sessions, &dead_sessions, s); if (s->references == 0) { RB_REMOVE(sessions, &dead_sessions, s); free(s->name); free(s); } s = next_s; } for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) { c = ARRAY_ITEM(&dead_clients, i); if (c == NULL || c->references != 0) continue; ARRAY_SET(&dead_clients, i, NULL); free(c); } } /* Update socket execute permissions based on whether sessions are attached. */ void server_update_socket(void) { struct session *s; static int last = -1; int n, mode; struct stat sb; n = 0; RB_FOREACH(s, sessions, &sessions) { if (!(s->flags & SESSION_UNATTACHED)) { n++; break; } } if (n != last) { last = n; if (stat(socket_path, &sb) != 0) return; mode = sb.st_mode; if (n != 0) { if (mode & S_IRUSR) mode |= S_IXUSR; if (mode & S_IRGRP) mode |= S_IXGRP; if (mode & S_IROTH) mode |= S_IXOTH; } else mode &= ~(S_IXUSR|S_IXGRP|S_IXOTH); chmod(socket_path, mode); } } /* Callback for server socket. */ void server_accept_callback(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_shutdown) { 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 (event_initialized(&server_ev_accept)) event_del(&server_ev_accept); if (timeout == 0) { event_set(&server_ev_accept, server_fd, EV_READ, server_accept_callback, NULL); event_add(&server_ev_accept, NULL); } else { event_set(&server_ev_accept, server_fd, EV_TIMEOUT, server_accept_callback, NULL); event_add(&server_ev_accept, &tv); } } /* Signal handler. */ void server_signal_callback(int sig, unused short events, unused void *data) { switch (sig) { case SIGTERM: server_shutdown = 1; server_send_shutdown(); break; case SIGCHLD: server_child_signal(); break; case SIGUSR1: event_del(&server_ev_accept); close(server_fd); server_fd = server_create_socket(); server_add_accept(0); break; } } /* Handle SIGCHLD. */ void server_child_signal(void) { int status; pid_t pid; for (;;) { switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) { case -1: if (errno == ECHILD) return; fatal("waitpid failed"); case 0: return; } if (WIFSTOPPED(status)) server_child_stopped(pid, status); else if (WIFEXITED(status) || WIFSIGNALED(status)) server_child_exited(pid, status); } } /* Handle exited children. */ void server_child_exited(pid_t pid, int status) { struct window *w; struct window_pane *wp; struct job *job; u_int i; for (i = 0; i < ARRAY_LENGTH(&windows); i++) { if ((w = ARRAY_ITEM(&windows, i)) == NULL) continue; TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->pid == pid) { server_destroy_pane(wp); break; } } } LIST_FOREACH(job, &all_jobs, lentry) { if (pid == job->pid) { job_died(job, status); /* might free job */ break; } } } /* Handle stopped children. */ void server_child_stopped(pid_t pid, int status) { struct window *w; struct window_pane *wp; u_int i; if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU) return; for (i = 0; i < ARRAY_LENGTH(&windows); i++) { if ((w = ARRAY_ITEM(&windows, i)) == NULL) continue; TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->pid == pid) { if (killpg(pid, SIGCONT) != 0) kill(pid, SIGCONT); } } } } /* Handle once-per-second timer events. */ void server_second_callback(unused int fd, unused short events, unused void *arg) { struct window *w; struct window_pane *wp; struct timeval tv; u_int i; if (options_get_number(&global_s_options, "lock-server")) server_lock_server(); else server_lock_sessions(); for (i = 0; i < ARRAY_LENGTH(&windows); i++) { w = ARRAY_ITEM(&windows, i); if (w == NULL) continue; TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->mode != NULL && wp->mode->timer != NULL) wp->mode->timer(wp); } } server_client_status_timer(); evtimer_del(&server_ev_second); memset(&tv, 0, sizeof tv); tv.tv_sec = 1; evtimer_add(&server_ev_second, &tv); } /* Lock the server if ALL sessions have hit the time limit. */ void server_lock_server(void) { struct session *s; int timeout; time_t t; t = time(NULL); RB_FOREACH(s, sessions, &sessions) { if (s->flags & SESSION_UNATTACHED) continue; timeout = options_get_number(&s->options, "lock-after-time"); if (timeout <= 0 || t <= s->activity_time.tv_sec + timeout) return; /* not timed out */ } server_lock(); recalculate_sizes(); } /* Lock any sessions which have timed out. */ void server_lock_sessions(void) { struct session *s; int timeout; time_t t; t = time(NULL); RB_FOREACH(s, sessions, &sessions) { if (s->flags & SESSION_UNATTACHED) continue; timeout = options_get_number(&s->options, "lock-after-time"); if (timeout > 0 && t > s->activity_time.tv_sec + timeout) { server_lock_session(s); recalculate_sizes(); } } } tmate-1.8.10/session.c000066400000000000000000000344041242461015400145460ustar00rootroot00000000000000/* $Id$ */ /* * 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" #include "tmate.h" /* Global session list. */ struct sessions sessions; struct sessions dead_sessions; u_int next_session_id; struct session_groups session_groups; struct winlink *session_next_alert(struct winlink *); struct winlink *session_previous_alert(struct winlink *); RB_GENERATE(sessions, session, entry, session_cmp); int session_cmp(struct session *s1, struct session *s2) { return (strcmp(s1->name, s2->name)); } /* * Find if session is still alive. This is true if it is still on the global * sessions list. */ int session_alive(struct session *s) { struct session *s_loop; RB_FOREACH(s_loop, sessions, &sessions) { if (s_loop == s) return (1); } return (0); } /* Find session by name. */ struct session * session_find(const char *name) { struct session s; s.name = (char *) name; return (RB_FIND(sessions, &sessions, &s)); } /* 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 *name, const char *cmd, const char *cwd, struct environ *env, struct termios *tio, int idx, u_int sx, u_int sy, char **cause) { struct session *s; #ifdef TMATE if (next_session_id != 0) { xasprintf(cause, "multi sessions is not supported with tmate"); return NULL; } #endif s = xmalloc(sizeof *s); s->references = 0; s->flags = 0; if (gettimeofday(&s->creation_time, NULL) != 0) fatal("gettimeofday failed"); session_update_activity(s); s->cwd = xstrdup(cwd); s->curw = NULL; TAILQ_INIT(&s->lastw); RB_INIT(&s->windows); options_init(&s->options, &global_s_options); environ_init(&s->environ); if (env != NULL) environ_copy(env, &s->environ); s->tio = NULL; if (tio != NULL) { s->tio = xmalloc(sizeof *s->tio); memcpy(s->tio, tio, sizeof *s->tio); } s->sx = sx; s->sy = sy; 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); xasprintf(&s->name, "%u", s->id); } while (RB_FIND(sessions, &sessions, s) != NULL); } RB_INSERT(sessions, &sessions, s); if (cmd != NULL) { if (session_new(s, NULL, cmd, cwd, idx, cause) == NULL) { session_destroy(s); return (NULL); } session_select(s, RB_ROOT(&s->windows)->idx); } log_debug("session %s created", s->name); notify_session_created(s); return (s); } /* Destroy a session. */ void session_destroy(struct session *s) { struct winlink *wl; log_debug("session %s destroyed", s->name); RB_REMOVE(sessions, &sessions, s); notify_session_closed(s); free(s->tio); session_group_remove(s); environ_free(&s->environ); options_free(&s->options); while (!TAILQ_EMPTY(&s->lastw)) winlink_stack_remove(&s->lastw, TAILQ_FIRST(&s->lastw)); while (!RB_EMPTY(&s->windows)) { wl = RB_ROOT(&s->windows); notify_window_unlinked(s, wl->window); winlink_remove(&s->windows, wl); } free(s->cwd); RB_INSERT(sessions, &dead_sessions, s); } /* Check a session name is valid: not empty and no colons. */ int session_check_name(const char *name) { return (*name != '\0' && strchr(name, ':') == NULL); } /* Update session active time. */ void session_update_activity(struct session *s) { if (gettimeofday(&s->activity_time, NULL) != 0) fatal("gettimeofday"); } /* Find the next usable session. */ struct session * session_next_session(struct session *s) { struct session *s2; if (RB_EMPTY(&sessions) || !session_alive(s)) return (NULL); s2 = RB_NEXT(sessions, &sessions, s); if (s2 == NULL) s2 = RB_MIN(sessions, &sessions); if (s2 == s) return (NULL); return (s2); } /* Find the previous usable session. */ struct session * session_previous_session(struct session *s) { struct session *s2; if (RB_EMPTY(&sessions) || !session_alive(s)) return (NULL); s2 = RB_PREV(sessions, &sessions, s); if (s2 == NULL) s2 = RB_MAX(sessions, &sessions); if (s2 == s) return (NULL); return (s2); } /* Create a new window on a session. */ struct winlink * session_new(struct session *s, const char *name, const char *cmd, const char *cwd, int idx, char **cause) { struct window *w; struct winlink *wl; struct environ env; const char *shell; u_int hlimit; if ((wl = winlink_add(&s->windows, idx)) == NULL) { xasprintf(cause, "index in use: %d", idx); return (NULL); } environ_init(&env); environ_copy(&global_environ, &env); environ_copy(&s->environ, &env); server_fill_environ(s, &env); shell = options_get_string(&s->options, "default-shell"); if (*shell == '\0' || areshell(shell)) shell = _PATH_BSHELL; hlimit = options_get_number(&s->options, "history-limit"); w = window_create( name, cmd, shell, cwd, &env, s->tio, s->sx, s->sy, hlimit, cause); if (w == NULL) { winlink_remove(&s->windows, wl); environ_free(&env); return (NULL); } winlink_set_window(wl, w); notify_window_linked(s, w); environ_free(&env); if (options_get_number(&s->options, "set-remain-on-exit")) options_set_number(&w->options, "remain-on-exit", 1); session_group_synchronize_from(s); return (wl); } /* Attach a window to a session. */ struct winlink * session_attach(struct session *s, struct window *w, int idx, char **cause) { struct winlink *wl; if ((wl = winlink_add(&s->windows, idx)) == NULL) { xasprintf(cause, "index in use: %d", idx); return (NULL); } winlink_set_window(wl, w); notify_window_linked(s, w); #ifdef TMATE tmate_sync_layout(); #endif 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_window_unlinked(s, wl->window); winlink_stack_remove(&s->lastw, wl); winlink_remove(&s->windows, wl); #ifdef TMATE tmate_sync_layout(); #endif session_group_synchronize_from(s); if (RB_EMPTY(&s->windows)) { session_destroy(s); return (1); } return (0); } /* Return if session has window. */ struct winlink * session_has(struct session *s, struct window *w) { struct winlink *wl; RB_FOREACH(wl, winlinks, &s->windows) { if (wl->window == w) return (wl); } return (NULL); } struct winlink * session_next_alert(struct winlink *wl) { while (wl != NULL) { if (wl->flags & WINLINK_ALERTFLAGS) break; wl = winlink_next(wl); } return (wl); } /* Move session to next window. */ int session_next(struct session *s, int alert) { struct winlink *wl; if (s->curw == NULL) return (-1); wl = winlink_next(s->curw); if (alert) wl = session_next_alert(wl); if (wl == NULL) { wl = RB_MIN(winlinks, &s->windows); if (alert && ((wl = session_next_alert(wl)) == NULL)) return (-1); } return (session_set_current(s, wl)); } 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); #ifdef TMATE tmate_sync_layout(); #endif return (0); } /* Find the session group containing a session. */ struct session_group * session_group_find(struct session *target) { struct session_group *sg; struct session *s; TAILQ_FOREACH(sg, &session_groups, entry) { TAILQ_FOREACH(s, &sg->sessions, gentry) { if (s == target) return (sg); } } return (NULL); } /* Find session group index. */ u_int session_group_index(struct session_group *sg) { struct session_group *sg2; u_int i; i = 0; TAILQ_FOREACH(sg2, &session_groups, entry) { if (sg == sg2) return (i); i++; } fatalx("session group not found"); } /* * Add a session to the session group containing target, creating it if * necessary. */ void session_group_add(struct session *target, struct session *s) { struct session_group *sg; if ((sg = session_group_find(target)) == NULL) { sg = xmalloc(sizeof *sg); TAILQ_INSERT_TAIL(&session_groups, sg, entry); TAILQ_INIT(&sg->sessions); TAILQ_INSERT_TAIL(&sg->sessions, target, gentry); } TAILQ_INSERT_TAIL(&sg->sessions, s, gentry); } /* Remove a session from its group and destroy the group if empty. */ void session_group_remove(struct session *s) { struct session_group *sg; if ((sg = session_group_find(s)) == NULL) return; TAILQ_REMOVE(&sg->sessions, s, gentry); if (TAILQ_NEXT(TAILQ_FIRST(&sg->sessions), gentry) == NULL) TAILQ_REMOVE(&sg->sessions, TAILQ_FIRST(&sg->sessions), gentry); if (TAILQ_EMPTY(&sg->sessions)) { TAILQ_REMOVE(&session_groups, sg, entry); free(sg); } } /* Synchronize a session to its session group. */ void session_group_synchronize_to(struct session *s) { struct session_group *sg; struct session *target; if ((sg = session_group_find(s)) == NULL) return; target = NULL; TAILQ_FOREACH(target, &sg->sessions, gentry) { if (target != s) break; } session_group_synchronize1(target, s); } /* Synchronize a session group to a session. */ void session_group_synchronize_from(struct session *target) { struct session_group *sg; struct session *s; if ((sg = session_group_find(target)) == NULL) return; TAILQ_FOREACH(s, &sg->sessions, gentry) { if (s != target) session_group_synchronize1(target, s); } } /* * Synchronize a session with a target session. This means destroying all * winlinks then recreating them, then updating the current window, last window * stack and alerts. */ void session_group_synchronize1(struct session *target, struct session *s) { struct winlinks old_windows, *ww; struct winlink_stack old_lastw; struct winlink *wl, *wl2; /* Don't do anything if the session is empty (it'll be destroyed). */ ww = &target->windows; if (RB_EMPTY(ww)) return; /* If the current window has vanished, move to the next now. */ if (s->curw != NULL && winlink_find_by_index(ww, s->curw->idx) == NULL && session_last(s) != 0 && session_previous(s, 0) != 0) session_next(s, 0); /* Save the old pointer and reset it. */ memcpy(&old_windows, &s->windows, sizeof old_windows); RB_INIT(&s->windows); /* Link all the windows from the target. */ RB_FOREACH(wl, winlinks, ww) { wl2 = winlink_add(&s->windows, wl->idx); winlink_set_window(wl2, wl->window); notify_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); if (winlink_find_by_window_id(&s->windows, wl->window->id) == NULL) notify_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); 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_index(&s->windows, wl->idx); 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); } tmate-1.8.10/signal.c000066400000000000000000000062501242461015400143360ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2007 Nicholas Marriott * Copyright (c) 2010 Romain Francoise * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" struct event ev_sighup; struct event ev_sigchld; struct event ev_sigcont; struct event ev_sigterm; struct event ev_sigusr1; struct event ev_sigwinch; void set_signals(void(*handler)(int, short, unused void *)) { struct sigaction sigact; memset(&sigact, 0, sizeof sigact); sigemptyset(&sigact.sa_mask); sigact.sa_flags = SA_RESTART; sigact.sa_handler = SIG_IGN; if (sigaction(SIGINT, &sigact, NULL) != 0) fatal("sigaction failed"); if (sigaction(SIGPIPE, &sigact, NULL) != 0) fatal("sigaction failed"); if (sigaction(SIGUSR2, &sigact, NULL) != 0) fatal("sigaction failed"); if (sigaction(SIGTSTP, &sigact, NULL) != 0) fatal("sigaction failed"); signal_set(&ev_sighup, SIGHUP, handler, NULL); signal_add(&ev_sighup, NULL); signal_set(&ev_sigchld, SIGCHLD, handler, NULL); signal_add(&ev_sigchld, NULL); signal_set(&ev_sigcont, SIGCONT, handler, NULL); signal_add(&ev_sigcont, NULL); signal_set(&ev_sigterm, SIGTERM, handler, NULL); signal_add(&ev_sigterm, NULL); signal_set(&ev_sigusr1, SIGUSR1, handler, NULL); signal_add(&ev_sigusr1, NULL); signal_set(&ev_sigwinch, SIGWINCH, handler, NULL); signal_add(&ev_sigwinch, NULL); } void clear_signals(int after_fork) { struct sigaction sigact; memset(&sigact, 0, sizeof sigact); sigemptyset(&sigact.sa_mask); sigact.sa_flags = SA_RESTART; sigact.sa_handler = SIG_DFL; if (sigaction(SIGINT, &sigact, NULL) != 0) fatal("sigaction failed"); if (sigaction(SIGPIPE, &sigact, NULL) != 0) fatal("sigaction failed"); if (sigaction(SIGUSR2, &sigact, NULL) != 0) fatal("sigaction failed"); if (sigaction(SIGTSTP, &sigact, NULL) != 0) fatal("sigaction failed"); if (after_fork) { if (sigaction(SIGHUP, &sigact, NULL) != 0) fatal("sigaction failed"); if (sigaction(SIGCHLD, &sigact, NULL) != 0) fatal("sigaction failed"); if (sigaction(SIGCONT, &sigact, NULL) != 0) fatal("sigaction failed"); if (sigaction(SIGTERM, &sigact, NULL) != 0) fatal("sigaction failed"); if (sigaction(SIGUSR1, &sigact, NULL) != 0) fatal("sigaction failed"); if (sigaction(SIGWINCH, &sigact, NULL) != 0) fatal("sigaction failed"); } else { event_del(&ev_sighup); event_del(&ev_sigchld); event_del(&ev_sigcont); event_del(&ev_sigterm); event_del(&ev_sigusr1); event_del(&ev_sigwinch); } } tmate-1.8.10/status.c000066400000000000000000001110121242461015400143750ustar00rootroot00000000000000/* $Id$ */ /* * 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" #include "tmate.h" char *status_redraw_get_left( struct client *, time_t, int, struct grid_cell *, size_t *); char *status_redraw_get_right( struct client *, time_t, int, struct grid_cell *, size_t *); char *status_find_job(struct client *, char **); void status_job_free(void *); void status_job_callback(struct job *); char *status_print( struct client *, struct winlink *, time_t, struct grid_cell *); void status_replace1(struct client *, struct session *, struct winlink *, struct window_pane *, char **, char **, char *, size_t, int); void status_message_callback(int, short, void *); const char *status_prompt_up_history(u_int *); const char *status_prompt_down_history(u_int *); void status_prompt_add_history(const char *); char *status_prompt_complete(const char *); /* Status prompt history. */ ARRAY_DECL(, char *) status_prompt_history = ARRAY_INITIALIZER; /* Status output tree. */ RB_GENERATE(status_out_tree, status_out, entry, status_out_cmp); /* Output tree comparison function. */ int status_out_cmp(struct status_out *so1, struct status_out *so2) { return (strcmp(so1->cmd, so2->cmd)); } /* Get screen line of status line. -1 means off. */ int status_at_line(struct client *c) { struct session *s = c->session; if (!options_get_number(&s->options, "status")) return (-1); if (options_get_number(&s->options, "status-position") == 0) return (0); return (c->tty.sy - 1); } /* Retrieve options for left string. */ char * status_redraw_get_left(struct client *c, time_t t, int utf8flag, struct grid_cell *gc, size_t *size) { struct session *s = c->session; char *left; int fg, bg, attr; size_t leftlen; fg = options_get_number(&s->options, "status-left-fg"); if (fg != 8) colour_set_fg(gc, fg); bg = options_get_number(&s->options, "status-left-bg"); if (bg != 8) colour_set_bg(gc, bg); attr = options_get_number(&s->options, "status-left-attr"); if (attr != 0) gc->attr = attr; left = status_replace(c, NULL, NULL, NULL, options_get_string(&s->options, "status-left"), t, 1); *size = options_get_number(&s->options, "status-left-length"); leftlen = screen_write_cstrlen(utf8flag, "%s", left); if (leftlen < *size) *size = leftlen; return (left); } /* Retrieve options for right string. */ char * status_redraw_get_right(struct client *c, time_t t, int utf8flag, struct grid_cell *gc, size_t *size) { struct session *s = c->session; char *right; int fg, bg, attr; size_t rightlen; fg = options_get_number(&s->options, "status-right-fg"); if (fg != 8) colour_set_fg(gc, fg); bg = options_get_number(&s->options, "status-right-bg"); if (bg != 8) colour_set_bg(gc, bg); attr = options_get_number(&s->options, "status-right-attr"); if (attr != 0) gc->attr = attr; right = status_replace(c, NULL, NULL, NULL, options_get_string(&s->options, "status-right"), t, 1); *size = options_get_number(&s->options, "status-right-length"); rightlen = screen_write_cstrlen(utf8flag, "%s", right); if (rightlen < *size) *size = rightlen; return (right); } /* Set window at window list position. */ void status_set_window_at(struct client *c, u_int x) { struct session *s = c->session; struct winlink *wl; x += c->wlmouse; RB_FOREACH(wl, winlinks, &s->windows) { if (x < wl->status_width && session_select(s, wl->idx) == 0) { server_redraw_session(s); } x -= wl->status_width + 1; } } /* Draw status for client on the last lines of given context. */ int status_redraw(struct client *c) { struct screen_write_ctx ctx; struct session *s = c->session; struct winlink *wl; struct screen old_status, window_list; struct grid_cell stdgc, lgc, rgc, gc; struct options *oo; time_t t; char *left, *right, *sep; u_int offset, needed; u_int wlstart, wlwidth, wlavailable, wloffset, wlsize; size_t llen, rlen, seplen; int larrow, rarrow, utf8flag; /* No status line? */ if (c->tty.sy == 0 || !options_get_number(&s->options, "status")) #ifdef TMATE if (c->tty.sy == 0 || !(c->flags & CLIENT_FORCE_STATUS)) #endif return (1); left = right = NULL; larrow = rarrow = 0; /* Update status timer. */ if (gettimeofday(&c->status_timer, NULL) != 0) fatal("gettimeofday failed"); t = c->status_timer.tv_sec; /* Set up default colour. */ memcpy(&stdgc, &grid_default_cell, sizeof gc); colour_set_fg(&stdgc, options_get_number(&s->options, "status-fg")); colour_set_bg(&stdgc, options_get_number(&s->options, "status-bg")); stdgc.attr |= options_get_number(&s->options, "status-attr"); /* Create the target screen. */ memcpy(&old_status, &c->status, sizeof old_status); screen_init(&c->status, c->tty.sx, 1, 0); screen_write_start(&ctx, NULL, &c->status); for (offset = 0; offset < c->tty.sx; offset++) screen_write_putc(&ctx, &stdgc, ' '); screen_write_stop(&ctx); /* If the height is one line, blank status line. */ if (c->tty.sy <= 1) goto out; /* Get UTF-8 flag. */ utf8flag = options_get_number(&s->options, "status-utf8"); /* Work out left and right strings. */ memcpy(&lgc, &stdgc, sizeof lgc); left = status_redraw_get_left(c, t, utf8flag, &lgc, &llen); memcpy(&rgc, &stdgc, sizeof rgc); right = status_redraw_get_right(c, t, utf8flag, &rgc, &rlen); tmate_status(left, right); /* * Figure out how much space we have for the window list. If there * isn't enough space, just show a blank status line. */ needed = 0; if (llen != 0) needed += llen + 1; if (rlen != 0) needed += rlen + 1; if (c->tty.sx == 0 || c->tty.sx <= needed) goto out; wlavailable = c->tty.sx - needed; /* Calculate the total size needed for the window list. */ wlstart = wloffset = wlwidth = 0; RB_FOREACH(wl, winlinks, &s->windows) { free(wl->status_text); memcpy(&wl->status_cell, &stdgc, sizeof wl->status_cell); wl->status_text = status_print(c, wl, t, &wl->status_cell); wl->status_width = screen_write_cstrlen(utf8flag, "%s", wl->status_text); if (wl == s->curw) wloffset = wlwidth; oo = &wl->window->options; sep = options_get_string(oo, "window-status-separator"); seplen = screen_write_strlen(utf8flag, "%s", sep); wlwidth += wl->status_width + seplen; } /* Create a new screen for the window list. */ screen_init(&window_list, wlwidth, 1, 0); /* And draw the window list into it. */ screen_write_start(&ctx, NULL, &window_list); RB_FOREACH(wl, winlinks, &s->windows) { screen_write_cnputs(&ctx, -1, &wl->status_cell, utf8flag, "%s", wl->status_text); oo = &wl->window->options; sep = options_get_string(oo, "window-status-separator"); screen_write_nputs(&ctx, -1, &stdgc, utf8flag, "%s", sep); } screen_write_stop(&ctx); /* If there is enough space for the total width, skip to draw now. */ if (wlwidth <= wlavailable) goto draw; /* Find size of current window text. */ wlsize = s->curw->status_width; /* * If the current window is already on screen, good to draw from the * start and just leave off the end. */ if (wloffset + wlsize < wlavailable) { if (wlavailable > 0) { rarrow = 1; wlavailable--; } wlwidth = wlavailable; } else { /* * Work out how many characters we need to omit from the * start. There are wlavailable characters to fill, and * wloffset + wlsize must be the last. So, the start character * is wloffset + wlsize - wlavailable. */ if (wlavailable > 0) { larrow = 1; wlavailable--; } wlstart = wloffset + wlsize - wlavailable; if (wlavailable > 0 && wlwidth > wlstart + wlavailable + 1) { rarrow = 1; wlstart++; wlavailable--; } wlwidth = wlavailable; } /* Bail if anything is now too small too. */ if (wlwidth == 0 || wlavailable == 0) { screen_free(&window_list); goto out; } /* * Now the start position is known, work out the state of the left and * right arrows. */ offset = 0; RB_FOREACH(wl, winlinks, &s->windows) { if (wl->flags & WINLINK_ALERTFLAGS && larrow == 1 && offset < wlstart) larrow = -1; offset += wl->status_width; if (wl->flags & WINLINK_ALERTFLAGS && rarrow == 1 && offset > wlstart + wlwidth) rarrow = -1; } draw: /* Begin drawing. */ screen_write_start(&ctx, NULL, &c->status); /* Draw the left string and arrow. */ screen_write_cursormove(&ctx, 0, 0); if (llen != 0) { screen_write_cnputs(&ctx, llen, &lgc, utf8flag, "%s", left); screen_write_putc(&ctx, &stdgc, ' '); } if (larrow != 0) { memcpy(&gc, &stdgc, sizeof gc); if (larrow == -1) gc.attr ^= GRID_ATTR_REVERSE; screen_write_putc(&ctx, &gc, '<'); } /* Draw the right string and arrow. */ if (rarrow != 0) { screen_write_cursormove(&ctx, c->tty.sx - rlen - 2, 0); memcpy(&gc, &stdgc, sizeof gc); if (rarrow == -1) gc.attr ^= GRID_ATTR_REVERSE; screen_write_putc(&ctx, &gc, '>'); } else screen_write_cursormove(&ctx, c->tty.sx - rlen - 1, 0); if (rlen != 0) { screen_write_putc(&ctx, &stdgc, ' '); screen_write_cnputs(&ctx, rlen, &rgc, utf8flag, "%s", right); } /* Figure out the offset for the window list. */ if (llen != 0) wloffset = llen + 1; else wloffset = 0; if (wlwidth < wlavailable) { switch (options_get_number(&s->options, "status-justify")) { case 1: /* centered */ wloffset += (wlavailable - wlwidth) / 2; break; case 2: /* right */ wloffset += (wlavailable - wlwidth); break; } } if (larrow != 0) wloffset++; /* Copy the window list. */ c->wlmouse = -wloffset + wlstart; screen_write_cursormove(&ctx, wloffset, 0); screen_write_copy(&ctx, &window_list, wlstart, 0, wlwidth, 1); screen_free(&window_list); screen_write_stop(&ctx); out: free(left); free(right); if (grid_compare(c->status.grid, old_status.grid) == 0) { screen_free(&old_status); return (0); } screen_free(&old_status); return (1); } /* Replace a single special sequence (prefixed by #). */ void status_replace1(struct client *c, struct session *s, struct winlink *wl, struct window_pane *wp, char **iptr, char **optr, char *out, size_t outsize, int jobsflag) { char ch, tmp[256], *ptr, *endptr, *freeptr; size_t ptrlen; long limit; u_int idx; errno = 0; limit = strtol(*iptr, &endptr, 10); if ((limit == 0 && errno != EINVAL) || (limit == LONG_MIN && errno != ERANGE) || (limit == LONG_MAX && errno != ERANGE) || limit != 0) *iptr = endptr; if (limit <= 0) limit = LONG_MAX; freeptr = NULL; switch (*(*iptr)++) { case '(': if (!jobsflag) { ch = ')'; goto skip_to; } if ((ptr = status_find_job(c, iptr)) == NULL) return; goto do_replace; case 'D': xsnprintf(tmp, sizeof tmp, "%%%u", wp->id); ptr = tmp; goto do_replace; case 'H': if (gethostname(tmp, sizeof tmp) != 0) fatal("gethostname failed"); ptr = tmp; goto do_replace; case 'h': if (gethostname(tmp, sizeof tmp) != 0) fatal("gethostname failed"); if ((ptr = strchr(tmp, '.')) != NULL) *ptr = '\0'; ptr = tmp; goto do_replace; case 'I': xsnprintf(tmp, sizeof tmp, "%d", wl->idx); ptr = tmp; goto do_replace; case 'P': if (window_pane_index(wp, &idx) != 0) fatalx("index not found"); xsnprintf(tmp, sizeof tmp, "%u", idx); ptr = tmp; goto do_replace; case 'S': ptr = s->name; goto do_replace; case 'T': ptr = wp->base.title; goto do_replace; case 'W': ptr = wl->window->name; goto do_replace; case 'F': ptr = window_printable_flags(s, wl); freeptr = ptr; goto do_replace; case '[': /* * Embedded style, handled at display time. Leave present and * skip input until ]. */ ch = ']'; goto skip_to; case '{': ptr = (char *) "#{"; goto do_replace; case '#': *(*optr)++ = '#'; break; } return; do_replace: ptrlen = strlen(ptr); if ((size_t) limit < ptrlen) ptrlen = limit; if (*optr + ptrlen >= out + outsize - 1) goto out; while (ptrlen > 0 && *ptr != '\0') { *(*optr)++ = *ptr++; ptrlen--; } out: free(freeptr); return; skip_to: *(*optr)++ = '#'; (*iptr)--; /* include ch */ while (**iptr != ch && **iptr != '\0') { if (*optr >= out + outsize - 1) break; *(*optr)++ = *(*iptr)++; } } /* Replace special sequences in fmt. */ char * status_replace(struct client *c, struct session *s, struct winlink *wl, struct window_pane *wp, const char *fmt, time_t t, int jobsflag) { static char out[BUFSIZ]; char in[BUFSIZ], ch, *iptr, *optr, *expanded; size_t len; struct format_tree *ft; if (fmt == NULL) return (xstrdup("")); if (s == NULL) s = c->session; if (wl == NULL) wl = s->curw; if (wp == NULL) wp = wl->window->active; len = strftime(in, sizeof in, fmt, localtime(&t)); in[len] = '\0'; iptr = in; optr = out; while (*iptr != '\0') { if (optr >= out + (sizeof out) - 1) break; ch = *iptr++; if (ch != '#' || *iptr == '\0') { *optr++ = ch; continue; } status_replace1( c, s, wl, wp, &iptr, &optr, out, sizeof out, jobsflag); } *optr = '\0'; ft = format_create(); format_client(ft, c); format_session(ft, s); format_winlink(ft, s, wl); format_window_pane(ft, wp); expanded = format_expand(ft, out); format_free(ft); return (expanded); } /* Figure out job name and get its result, starting it off if necessary. */ char * status_find_job(struct client *c, char **iptr) { struct status_out *so, so_find; char *cmd; int lastesc; size_t len; if (**iptr == '\0') return (NULL); if (**iptr == ')') { /* no command given */ (*iptr)++; return (NULL); } cmd = xmalloc(strlen(*iptr) + 1); len = 0; lastesc = 0; for (; **iptr != '\0'; (*iptr)++) { if (!lastesc && **iptr == ')') break; /* unescaped ) is the end */ if (!lastesc && **iptr == '\\') { lastesc = 1; continue; /* skip \ if not escaped */ } lastesc = 0; cmd[len++] = **iptr; } if (**iptr == '\0') /* no terminating ) */ { free(cmd); return (NULL); } (*iptr)++; /* skip final ) */ cmd[len] = '\0'; /* First try in the new tree. */ so_find.cmd = cmd; so = RB_FIND(status_out_tree, &c->status_new, &so_find); if (so != NULL && so->out != NULL) { free(cmd); return (so->out); } /* If not found at all, start the job and add to the tree. */ if (so == NULL) { job_run(cmd, NULL, status_job_callback, status_job_free, c); c->references++; so = xmalloc(sizeof *so); so->cmd = xstrdup(cmd); so->out = NULL; RB_INSERT(status_out_tree, &c->status_new, so); } /* Lookup in the old tree. */ so_find.cmd = cmd; so = RB_FIND(status_out_tree, &c->status_old, &so_find); free(cmd); if (so != NULL) return (so->out); return (NULL); } /* Free job tree. */ void status_free_jobs(struct status_out_tree *sotree) { struct status_out *so, *so_next; so_next = RB_MIN(status_out_tree, sotree); while (so_next != NULL) { so = so_next; so_next = RB_NEXT(status_out_tree, sotree, so); RB_REMOVE(status_out_tree, sotree, so); free(so->out); free(so->cmd); free(so); } } /* Update jobs on status interval. */ void status_update_jobs(struct client *c) { /* Free the old tree. */ status_free_jobs(&c->status_old); /* Move the new to old. */ memcpy(&c->status_old, &c->status_new, sizeof c->status_old); RB_INIT(&c->status_new); } /* Free status job. */ void status_job_free(void *data) { struct client *c = data; c->references--; } /* Job has finished: save its result. */ void status_job_callback(struct job *job) { struct client *c = job->data; struct status_out *so, so_find; char *line, *buf; size_t len; if (c->flags & CLIENT_DEAD) return; so_find.cmd = job->cmd; so = RB_FIND(status_out_tree, &c->status_new, &so_find); if (so == NULL || so->out != NULL) return; buf = NULL; if ((line = evbuffer_readline(job->event->input)) == NULL) { len = EVBUFFER_LENGTH(job->event->input); buf = xmalloc(len + 1); if (len != 0) memcpy(buf, EVBUFFER_DATA(job->event->input), len); buf[len] = '\0'; } else buf = line; so->out = buf; server_status_client(c); } /* Return winlink status line entry and adjust gc as necessary. */ char * status_print( struct client *c, struct winlink *wl, time_t t, struct grid_cell *gc) { struct options *oo = &wl->window->options; struct session *s = c->session; const char *fmt; char *text; int fg, bg, attr; fg = options_get_number(oo, "window-status-fg"); if (fg != 8) colour_set_fg(gc, fg); bg = options_get_number(oo, "window-status-bg"); if (bg != 8) colour_set_bg(gc, bg); attr = options_get_number(oo, "window-status-attr"); if (attr != 0) gc->attr = attr; fmt = options_get_string(oo, "window-status-format"); if (wl == s->curw) { fg = options_get_number(oo, "window-status-current-fg"); if (fg != 8) colour_set_fg(gc, fg); bg = options_get_number(oo, "window-status-current-bg"); if (bg != 8) colour_set_bg(gc, bg); attr = options_get_number(oo, "window-status-current-attr"); if (attr != 0) gc->attr = attr; fmt = options_get_string(oo, "window-status-current-format"); } if (wl == TAILQ_FIRST(&s->lastw)) { fg = options_get_number(oo, "window-status-last-fg"); if (fg != 8) colour_set_fg(gc, fg); bg = options_get_number(oo, "window-status-last-bg"); if (bg != 8) colour_set_bg(gc, bg); attr = options_get_number(oo, "window-status-last-attr"); if (attr != 0) gc->attr = attr; } if (wl->flags & WINLINK_BELL) { fg = options_get_number(oo, "window-status-bell-fg"); if (fg != 8) colour_set_fg(gc, fg); bg = options_get_number(oo, "window-status-bell-bg"); if (bg != 8) colour_set_bg(gc, bg); attr = options_get_number(oo, "window-status-bell-attr"); if (attr != 0) gc->attr = attr; } else if (wl->flags & WINLINK_CONTENT) { fg = options_get_number(oo, "window-status-content-fg"); if (fg != 8) colour_set_fg(gc, fg); bg = options_get_number(oo, "window-status-content-bg"); if (bg != 8) colour_set_bg(gc, bg); attr = options_get_number(oo, "window-status-content-attr"); if (attr != 0) gc->attr = attr; } else if (wl->flags & (WINLINK_ACTIVITY|WINLINK_SILENCE)) { fg = options_get_number(oo, "window-status-activity-fg"); if (fg != 8) colour_set_fg(gc, fg); bg = options_get_number(oo, "window-status-activity-bg"); if (bg != 8) colour_set_bg(gc, bg); attr = options_get_number(oo, "window-status-activity-attr"); if (attr != 0) gc->attr = attr; } text = status_replace(c, NULL, wl, NULL, fmt, t, 1); return (text); } /* Set a status line message. */ void printflike2 status_message_set(struct client *c, const char *fmt, ...) { struct timeval tv; struct session *s = c->session; struct message_entry *msg; va_list ap; int delay; u_int i, limit; status_prompt_clear(c); status_message_clear(c); va_start(ap, fmt); xvasprintf(&c->message_string, fmt, ap); va_end(ap); ARRAY_EXPAND(&c->message_log, 1); msg = &ARRAY_LAST(&c->message_log); msg->msg_time = time(NULL); msg->msg = xstrdup(c->message_string); if (s == NULL) limit = 0; else limit = options_get_number(&s->options, "message-limit"); if (ARRAY_LENGTH(&c->message_log) > limit) { limit = ARRAY_LENGTH(&c->message_log) - limit; for (i = 0; i < limit; i++) { msg = &ARRAY_FIRST(&c->message_log); free(msg->msg); ARRAY_REMOVE(&c->message_log, 0); } } /* FIXME tmux: session can be NULL */ delay = options_get_number(&c->session->options, "display-time"); 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_STATUS; } /* 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; c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE); #ifdef TMATE if (c->flags & CLIENT_FORCE_STATUS) { c->flags &= ~CLIENT_FORCE_STATUS; recalculate_sizes(); } #endif c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */ screen_reinit(&c->status); } /* Clear status line message after timer expires. */ void status_message_callback(unused int fd, unused short event, void *data) { struct client *c = data; status_message_clear(c); } /* Draw client message on status line of present else on last line. */ int status_message_redraw(struct client *c) { struct screen_write_ctx ctx; struct session *s = c->session; struct screen old_status; size_t len; struct grid_cell gc; int utf8flag; if (c->tty.sx == 0 || c->tty.sy == 0) return (0); memcpy(&old_status, &c->status, sizeof old_status); screen_init(&c->status, c->tty.sx, 1, 0); utf8flag = options_get_number(&s->options, "status-utf8"); len = screen_write_strlen(utf8flag, "%s", c->message_string); if (len > c->tty.sx) len = c->tty.sx; memcpy(&gc, &grid_default_cell, sizeof gc); colour_set_fg(&gc, options_get_number(&s->options, "message-fg")); colour_set_bg(&gc, options_get_number(&s->options, "message-bg")); gc.attr |= options_get_number(&s->options, "message-attr"); screen_write_start(&ctx, NULL, &c->status); screen_write_cursormove(&ctx, 0, 0); screen_write_nputs(&ctx, len, &gc, utf8flag, "%s", c->message_string); for (; len < c->tty.sx; len++) screen_write_putc(&ctx, &gc, ' '); screen_write_stop(&ctx); if (grid_compare(c->status.grid, old_status.grid) == 0) { screen_free(&old_status); return (0); } screen_free(&old_status); return (1); } /* Enable status line prompt. */ void status_prompt_set(struct client *c, const char *msg, const char *input, int (*callbackfn)(void *, const char *), void (*freefn)(void *), void *data, int flags) { int keys; status_message_clear(c); status_prompt_clear(c); c->prompt_string = status_replace(c, NULL, NULL, NULL, msg, time(NULL), 0); c->prompt_buffer = status_replace(c, NULL, NULL, NULL, input, time(NULL), 0); c->prompt_index = strlen(c->prompt_buffer); c->prompt_callbackfn = callbackfn; c->prompt_freefn = freefn; c->prompt_data = data; c->prompt_hindex = 0; c->prompt_flags = flags; keys = options_get_number(&c->session->options, "status-keys"); if (keys == MODEKEY_EMACS) mode_key_init(&c->prompt_mdata, &mode_key_tree_emacs_edit); else mode_key_init(&c->prompt_mdata, &mode_key_tree_vi_edit); c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE); c->flags |= CLIENT_STATUS; } /* Remove status line prompt. */ void status_prompt_clear(struct client *c) { if (c->prompt_string == NULL) return; if (c->prompt_freefn != NULL && c->prompt_data != NULL) c->prompt_freefn(c->prompt_data); free(c->prompt_string); c->prompt_string = NULL; free(c->prompt_buffer); c->prompt_buffer = NULL; c->tty.flags &= ~(TTY_NOCURSOR|TTY_FREEZE); c->flags |= CLIENT_REDRAW; /* screen was frozen and may have changed */ screen_reinit(&c->status); } /* Update status line prompt with a new prompt string. */ void status_prompt_update(struct client *c, const char *msg, const char *input) { free(c->prompt_string); c->prompt_string = status_replace(c, NULL, NULL, NULL, msg, time(NULL), 0); free(c->prompt_buffer); c->prompt_buffer = status_replace(c, NULL, NULL, NULL, input, time(NULL), 0); c->prompt_index = strlen(c->prompt_buffer); c->prompt_hindex = 0; c->flags |= CLIENT_STATUS; } /* Draw client prompt on status line of present else on last line. */ int status_prompt_redraw(struct client *c) { struct screen_write_ctx ctx; struct session *s = c->session; struct screen old_status; size_t i, size, left, len, off; struct grid_cell gc, *gcp; int utf8flag; if (c->tty.sx == 0 || c->tty.sy == 0) return (0); memcpy(&old_status, &c->status, sizeof old_status); screen_init(&c->status, c->tty.sx, 1, 0); utf8flag = options_get_number(&s->options, "status-utf8"); len = screen_write_strlen(utf8flag, "%s", c->prompt_string); if (len > c->tty.sx) len = c->tty.sx; off = 0; memcpy(&gc, &grid_default_cell, sizeof gc); /* Change colours for command mode. */ if (c->prompt_mdata.mode == 1) { colour_set_fg(&gc, options_get_number(&s->options, "message-command-fg")); colour_set_bg(&gc, options_get_number(&s->options, "message-command-bg")); gc.attr |= options_get_number(&s->options, "message-command-attr"); } else { colour_set_fg(&gc, options_get_number(&s->options, "message-fg")); colour_set_bg(&gc, options_get_number(&s->options, "message-bg")); gc.attr |= options_get_number(&s->options, "message-attr"); } screen_write_start(&ctx, NULL, &c->status); screen_write_cursormove(&ctx, 0, 0); screen_write_nputs(&ctx, len, &gc, utf8flag, "%s", c->prompt_string); left = c->tty.sx - len; if (left != 0) { size = screen_write_strlen(utf8flag, "%s", c->prompt_buffer); if (c->prompt_index >= left) { off = c->prompt_index - left + 1; if (c->prompt_index == size) left--; size = left; } screen_write_nputs( &ctx, left, &gc, utf8flag, "%s", c->prompt_buffer + off); for (i = len + size; i < c->tty.sx; i++) screen_write_putc(&ctx, &gc, ' '); } screen_write_stop(&ctx); /* Apply fake cursor. */ off = len + c->prompt_index - off; gcp = grid_view_get_cell(c->status.grid, off, 0); gcp->attr ^= GRID_ATTR_REVERSE; if (grid_compare(c->status.grid, old_status.grid) == 0) { screen_free(&old_status); return (0); } screen_free(&old_status); return (1); } /* Handle keys in prompt. */ void status_prompt_key(struct client *c, int key) { struct session *sess = c->session; struct options *oo = &sess->options; struct paste_buffer *pb; char *s, *first, *last, word[64], swapc; const char *histstr; const char *wsep = NULL; u_char ch; size_t size, n, off, idx; size = strlen(c->prompt_buffer); switch (mode_key_lookup(&c->prompt_mdata, key, NULL)) { case MODEKEYEDIT_CURSORLEFT: if (c->prompt_index > 0) { c->prompt_index--; c->flags |= CLIENT_STATUS; } break; case MODEKEYEDIT_SWITCHMODE: c->flags |= CLIENT_STATUS; break; case MODEKEYEDIT_SWITCHMODEAPPEND: c->flags |= CLIENT_STATUS; /* FALLTHROUGH */ case MODEKEYEDIT_CURSORRIGHT: if (c->prompt_index < size) { c->prompt_index++; c->flags |= CLIENT_STATUS; } break; case MODEKEYEDIT_SWITCHMODEBEGINLINE: c->flags |= CLIENT_STATUS; /* FALLTHROUGH */ case MODEKEYEDIT_STARTOFLINE: if (c->prompt_index != 0) { c->prompt_index = 0; c->flags |= CLIENT_STATUS; } break; case MODEKEYEDIT_SWITCHMODEAPPENDLINE: c->flags |= CLIENT_STATUS; /* FALLTHROUGH */ case MODEKEYEDIT_ENDOFLINE: if (c->prompt_index != size) { c->prompt_index = size; c->flags |= CLIENT_STATUS; } break; case MODEKEYEDIT_COMPLETE: if (*c->prompt_buffer == '\0') break; idx = c->prompt_index; if (idx != 0) idx--; /* Find the word we are in. */ first = c->prompt_buffer + idx; while (first > c->prompt_buffer && *first != ' ') first--; while (*first == ' ') first++; last = c->prompt_buffer + idx; while (*last != '\0' && *last != ' ') last++; while (*last == ' ') last--; if (*last != '\0') last++; if (last <= first || ((size_t) (last - first)) > (sizeof word) - 1) break; memcpy(word, first, last - first); word[last - first] = '\0'; /* And try to complete it. */ if ((s = status_prompt_complete(word)) == NULL) break; /* Trim out word. */ n = size - (last - c->prompt_buffer) + 1; /* with \0 */ memmove(first, last, n); size -= last - first; /* Insert the new word. */ size += strlen(s); off = first - c->prompt_buffer; c->prompt_buffer = xrealloc(c->prompt_buffer, 1, size + 1); first = c->prompt_buffer + off; memmove(first + strlen(s), first, n); memcpy(first, s, strlen(s)); c->prompt_index = (first - c->prompt_buffer) + strlen(s); free(s); c->flags |= CLIENT_STATUS; break; case MODEKEYEDIT_BACKSPACE: if (c->prompt_index != 0) { if (c->prompt_index == size) c->prompt_buffer[--c->prompt_index] = '\0'; else { memmove(c->prompt_buffer + c->prompt_index - 1, c->prompt_buffer + c->prompt_index, size + 1 - c->prompt_index); c->prompt_index--; } c->flags |= CLIENT_STATUS; } break; case MODEKEYEDIT_DELETE: if (c->prompt_index != size) { memmove(c->prompt_buffer + c->prompt_index, c->prompt_buffer + c->prompt_index + 1, size + 1 - c->prompt_index); c->flags |= CLIENT_STATUS; } break; case MODEKEYEDIT_DELETELINE: *c->prompt_buffer = '\0'; c->prompt_index = 0; c->flags |= CLIENT_STATUS; break; case MODEKEYEDIT_DELETETOENDOFLINE: if (c->prompt_index < size) { c->prompt_buffer[c->prompt_index] = '\0'; c->flags |= CLIENT_STATUS; } break; case MODEKEYEDIT_DELETEWORD: wsep = options_get_string(oo, "word-separators"); idx = c->prompt_index; /* Find a non-separator. */ while (idx != 0) { idx--; if (!strchr(wsep, c->prompt_buffer[idx])) break; } /* Find the separator at the beginning of the word. */ while (idx != 0) { idx--; if (strchr(wsep, c->prompt_buffer[idx])) { /* Go back to the word. */ idx++; break; } } memmove(c->prompt_buffer + idx, c->prompt_buffer + c->prompt_index, size + 1 - c->prompt_index); memset(c->prompt_buffer + size - (c->prompt_index - idx), '\0', c->prompt_index - idx); c->prompt_index = idx; c->flags |= CLIENT_STATUS; break; case MODEKEYEDIT_NEXTSPACE: wsep = " "; /* FALLTHROUGH */ case MODEKEYEDIT_NEXTWORD: if (wsep == NULL) wsep = options_get_string(oo, "word-separators"); /* Find a separator. */ while (c->prompt_index != size) { c->prompt_index++; if (strchr(wsep, c->prompt_buffer[c->prompt_index])) break; } /* Find the word right after the separation. */ while (c->prompt_index != size) { c->prompt_index++; if (!strchr(wsep, c->prompt_buffer[c->prompt_index])) break; } c->flags |= CLIENT_STATUS; break; case MODEKEYEDIT_NEXTSPACEEND: wsep = " "; /* FALLTHROUGH */ case MODEKEYEDIT_NEXTWORDEND: if (wsep == NULL) wsep = options_get_string(oo, "word-separators"); /* Find a word. */ while (c->prompt_index != size) { c->prompt_index++; if (!strchr(wsep, c->prompt_buffer[c->prompt_index])) break; } /* Find the separator at the end of the word. */ while (c->prompt_index != size) { c->prompt_index++; if (strchr(wsep, c->prompt_buffer[c->prompt_index])) break; } c->flags |= CLIENT_STATUS; break; case MODEKEYEDIT_PREVIOUSSPACE: wsep = " "; /* FALLTHROUGH */ case MODEKEYEDIT_PREVIOUSWORD: if (wsep == NULL) wsep = options_get_string(oo, "word-separators"); /* Find a non-separator. */ while (c->prompt_index != 0) { c->prompt_index--; if (!strchr(wsep, c->prompt_buffer[c->prompt_index])) break; } /* Find the separator at the beginning of the word. */ while (c->prompt_index != 0) { c->prompt_index--; if (strchr(wsep, c->prompt_buffer[c->prompt_index])) { /* Go back to the word. */ c->prompt_index++; break; } } c->flags |= CLIENT_STATUS; break; case MODEKEYEDIT_HISTORYUP: histstr = status_prompt_up_history(&c->prompt_hindex); if (histstr == NULL) break; free(c->prompt_buffer); c->prompt_buffer = xstrdup(histstr); c->prompt_index = strlen(c->prompt_buffer); c->flags |= CLIENT_STATUS; break; case MODEKEYEDIT_HISTORYDOWN: histstr = status_prompt_down_history(&c->prompt_hindex); if (histstr == NULL) break; free(c->prompt_buffer); c->prompt_buffer = xstrdup(histstr); c->prompt_index = strlen(c->prompt_buffer); c->flags |= CLIENT_STATUS; break; case MODEKEYEDIT_PASTE: if ((pb = paste_get_top(&global_buffers)) == NULL) break; for (n = 0; n < pb->size; n++) { ch = (u_char) pb->data[n]; if (ch < 32 || ch == 127) break; } c->prompt_buffer = xrealloc(c->prompt_buffer, 1, size + n + 1); if (c->prompt_index == size) { memcpy(c->prompt_buffer + c->prompt_index, pb->data, n); c->prompt_index += n; c->prompt_buffer[c->prompt_index] = '\0'; } else { memmove(c->prompt_buffer + c->prompt_index + n, c->prompt_buffer + c->prompt_index, size + 1 - c->prompt_index); memcpy(c->prompt_buffer + c->prompt_index, pb->data, n); c->prompt_index += n; } c->flags |= CLIENT_STATUS; break; case MODEKEYEDIT_TRANSPOSECHARS: idx = c->prompt_index; if (idx < size) idx++; if (idx >= 2) { swapc = c->prompt_buffer[idx - 2]; c->prompt_buffer[idx - 2] = c->prompt_buffer[idx - 1]; c->prompt_buffer[idx - 1] = swapc; c->prompt_index = idx; c->flags |= CLIENT_STATUS; } break; case MODEKEYEDIT_ENTER: if (*c->prompt_buffer != '\0') status_prompt_add_history(c->prompt_buffer); if (c->prompt_callbackfn(c->prompt_data, c->prompt_buffer) == 0) status_prompt_clear(c); break; case MODEKEYEDIT_CANCEL: if (c->prompt_callbackfn(c->prompt_data, NULL) == 0) status_prompt_clear(c); break; case MODEKEY_OTHER: if ((key & 0xff00) != 0 || key < 32 || key == 127) break; c->prompt_buffer = xrealloc(c->prompt_buffer, 1, size + 2); if (c->prompt_index == size) { c->prompt_buffer[c->prompt_index++] = key; c->prompt_buffer[c->prompt_index] = '\0'; } else { memmove(c->prompt_buffer + c->prompt_index + 1, c->prompt_buffer + c->prompt_index, size + 1 - c->prompt_index); c->prompt_buffer[c->prompt_index++] = key; } if (c->prompt_flags & PROMPT_SINGLE) { if (c->prompt_callbackfn( c->prompt_data, c->prompt_buffer) == 0) status_prompt_clear(c); } c->flags |= CLIENT_STATUS; break; default: break; } } /* Get previous line from the history. */ const char * status_prompt_up_history(u_int *idx) { u_int size; /* * History runs from 0 to size - 1. * * Index is from 0 to size. Zero is empty. */ size = ARRAY_LENGTH(&status_prompt_history); if (size == 0 || *idx == size) return (NULL); (*idx)++; return (ARRAY_ITEM(&status_prompt_history, size - *idx)); } /* Get next line from the history. */ const char * status_prompt_down_history(u_int *idx) { u_int size; size = ARRAY_LENGTH(&status_prompt_history); if (size == 0 || *idx == 0) return (""); (*idx)--; if (*idx == 0) return (""); return (ARRAY_ITEM(&status_prompt_history, size - *idx)); } /* Add line to the history. */ void status_prompt_add_history(const char *line) { u_int size; size = ARRAY_LENGTH(&status_prompt_history); if (size > 0 && strcmp(ARRAY_LAST(&status_prompt_history), line) == 0) return; if (size == PROMPT_HISTORY) { free(ARRAY_FIRST(&status_prompt_history)); ARRAY_REMOVE(&status_prompt_history, 0); } ARRAY_ADD(&status_prompt_history, xstrdup(line)); } /* Complete word. */ char * status_prompt_complete(const char *s) { const struct cmd_entry **cmdent; const struct options_table_entry *oe; ARRAY_DECL(, const char *) list; char *prefix, *s2; u_int i; size_t j; if (*s == '\0') return (NULL); /* First, build a list of all the possible matches. */ ARRAY_INIT(&list); for (cmdent = cmd_table; *cmdent != NULL; cmdent++) { if (strncmp((*cmdent)->name, s, strlen(s)) == 0) ARRAY_ADD(&list, (*cmdent)->name); } for (oe = server_options_table; oe->name != NULL; oe++) { if (strncmp(oe->name, s, strlen(s)) == 0) ARRAY_ADD(&list, oe->name); } for (oe = session_options_table; oe->name != NULL; oe++) { if (strncmp(oe->name, s, strlen(s)) == 0) ARRAY_ADD(&list, oe->name); } for (oe = window_options_table; oe->name != NULL; oe++) { if (strncmp(oe->name, s, strlen(s)) == 0) ARRAY_ADD(&list, oe->name); } /* If none, bail now. */ if (ARRAY_LENGTH(&list) == 0) { ARRAY_FREE(&list); return (NULL); } /* If an exact match, return it, with a trailing space. */ if (ARRAY_LENGTH(&list) == 1) { xasprintf(&s2, "%s ", ARRAY_FIRST(&list)); ARRAY_FREE(&list); return (s2); } /* Now loop through the list and find the longest common prefix. */ prefix = xstrdup(ARRAY_FIRST(&list)); for (i = 1; i < ARRAY_LENGTH(&list); i++) { s = ARRAY_ITEM(&list, i); j = strlen(s); if (j > strlen(prefix)) j = strlen(prefix); for (; j > 0; j--) { if (prefix[j - 1] != s[j - 1]) prefix[j - 1] = '\0'; } } ARRAY_FREE(&list); return (prefix); } tmate-1.8.10/tmate-debug.c000066400000000000000000000032561242461015400152620ustar00rootroot00000000000000#include #include #include #include #include "tmate.h" #if DEBUG static int print_resolved_stack_frame(const char *frame) { char file[100]; char cmd[200]; char output[300]; char address[20]; char *line; FILE *ps; static regex_t _regex; static regex_t *regex; regmatch_t matches[3]; if (!regex) { if (regcomp(&_regex, "(.+)\\(\\) \\[([^]]+)\\]", REG_EXTENDED)) return -1; regex = &_regex; } if (regexec(regex, frame, 3, matches, 0)) return -1; memcpy(file, &frame[matches[1].rm_so], matches[1].rm_eo - matches[1].rm_so); file[matches[1].rm_eo - matches[1].rm_so] = 0; memcpy(address, &frame[matches[2].rm_so], matches[2].rm_eo - matches[2].rm_so); address[matches[2].rm_eo - matches[2].rm_so] = 0; sprintf(cmd, "addr2line -e %s %s -f -p -s", file, address); ps = popen(cmd, "r"); if (!ps) return -1; line = fgets(output, sizeof(output), ps); pclose(ps); if (!line) return -1; line[strlen(line)-1] = 0; /* remove \n */ tmate_debug("%s(%s) [%s]", file, line, address); return 0; } #endif void tmate_print_trace(void) { void *array[20]; size_t size; char **strings; size_t i; size = backtrace (array, 20); strings = backtrace_symbols (array, size); tmate_info ("============ %zd stack frames ============", size); for (i = 1; i < size; i++) { #if DEBUG if (print_resolved_stack_frame(strings[i]) < 0) #endif tmate_info("%s", strings[i]); } free (strings); } static void handle_sigsegv(int sig) { /* TODO send stack trace to server */ tmate_info("CRASH, printing stack trace"); tmate_print_trace(); tmate_fatal("CRASHED"); } void tmate_catch_sigsegv(void) { signal(SIGSEGV, handle_sigsegv); } tmate-1.8.10/tmate-decoder.c000066400000000000000000000112041242461015400155710ustar00rootroot00000000000000#include "tmate.h" int tmate_sx = -1; int tmate_sy = -1; struct tmate_unpacker { msgpack_object *argv; int argc; }; static void decoder_error(void) { /* TODO Don't kill the session, disconnect */ tmate_fatal("Received a bad message"); } static void init_unpacker(struct tmate_unpacker *uk, msgpack_object obj) { if (obj.type != MSGPACK_OBJECT_ARRAY) decoder_error(); uk->argv = obj.via.array.ptr; uk->argc = obj.via.array.size; } static int64_t unpack_int(struct tmate_unpacker *uk) { int64_t val; if (uk->argc == 0) decoder_error(); if (uk->argv[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER && uk->argv[0].type != MSGPACK_OBJECT_NEGATIVE_INTEGER) decoder_error(); val = uk->argv[0].via.i64; uk->argv++; uk->argc--; return val; } static void unpack_raw(struct tmate_unpacker *uk, const char **buf, size_t *len) { if (uk->argc == 0) decoder_error(); if (uk->argv[0].type != MSGPACK_OBJECT_RAW) decoder_error(); *len = uk->argv[0].via.raw.size; *buf = uk->argv[0].via.raw.ptr; uk->argv++; uk->argc--; } static char *unpack_string(struct tmate_unpacker *uk) { const char *buf; char *alloc_buf; size_t len; unpack_raw(uk, &buf, &len); alloc_buf = xmalloc(len + 1); memcpy(alloc_buf, buf, len); alloc_buf[len] = '\0'; return alloc_buf; } static void tmate_notify(struct tmate_unpacker *uk) { char *msg = unpack_string(uk); tmate_status_message("%s", msg); free(msg); } static void tmate_client_pane_key(struct tmate_unpacker *uk) { struct session *s; struct window *w; struct window_pane *wp; int key = unpack_int(uk); s = RB_MIN(sessions, &sessions); if (!s) return; w = s->curw->window; if (!w) return; wp = w->active; if (!wp) return; window_pane_key(wp, s, key); } static void tmate_client_resize(struct tmate_unpacker *uk) { /* TODO This is sad, we might want our own client. */ tmate_sx = unpack_int(uk); tmate_sy = unpack_int(uk); recalculate_sizes(); /* TODO Handle reconnection cases */ } static void tmate_client_exec_cmd(struct tmate_unpacker *uk) { struct cmd_q *cmd_q; struct cmd_list *cmdlist; char *cause; int client_id = unpack_int(uk); char *cmd_str = unpack_string(uk); if (cmd_string_parse(cmd_str, &cmdlist, NULL, 0, &cause) != 0) { tmate_failed_cmd(client_id, cause); free(cause); goto out; } /* error messages land in cfg_causes */ ARRAY_FREE(&cfg_causes); cmd_q = cmdq_new(NULL); cmdq_run(cmd_q, cmdlist); cmd_list_free(cmdlist); cmdq_free(cmd_q); if (!ARRAY_EMPTY(&cfg_causes)) { cause = ARRAY_ITEM(&cfg_causes, 0); tmate_failed_cmd(client_id, cause); free(cause); ARRAY_FREE(&cfg_causes); } out: free(cmd_str); } static void tmate_client_env(struct tmate_unpacker *uk) { char *name = unpack_string(uk); char *value = unpack_string(uk); tmate_set_env(name, value); free(name); free(value); } extern void signal_waiting_clients(const char *name); static void tmate_client_ready(struct tmate_decoder *decoder, struct tmate_unpacker *uk) { decoder->ready = 1; signal_waiting_clients("tmate-ready"); } static void handle_message(struct tmate_decoder *decoder, msgpack_object obj) { struct tmate_unpacker _uk; struct tmate_unpacker *uk = &_uk; init_unpacker(uk, obj); switch (unpack_int(uk)) { case TMATE_NOTIFY: tmate_notify(uk); break; case TMATE_CLIENT_PANE_KEY: tmate_client_pane_key(uk); break; case TMATE_CLIENT_RESIZE: tmate_client_resize(uk); break; case TMATE_CLIENT_EXEC_CMD: tmate_client_exec_cmd(uk); break; case TMATE_CLIENT_ENV: tmate_client_env(uk); break; case TMATE_CLIENT_READY: tmate_client_ready(decoder, uk); break; default: decoder_error(); } } void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len) { msgpack_unpacked result; msgpack_unpacker_buffer_consumed(&decoder->unpacker, len); msgpack_unpacked_init(&result); while (msgpack_unpacker_next(&decoder->unpacker, &result)) { handle_message(decoder, result.data); } msgpack_unpacked_destroy(&result); if (msgpack_unpacker_message_size(&decoder->unpacker) > TMATE_MAX_MESSAGE_SIZE) { tmate_fatal("Message too big"); } } void tmate_decoder_get_buffer(struct tmate_decoder *decoder, char **buf, size_t *len) { /* rewind the buffer if possible */ if (msgpack_unpacker_buffer_capacity(&decoder->unpacker) < TMATE_MAX_MESSAGE_SIZE) { msgpack_unpacker_expand_buffer(&decoder->unpacker, 0); } *buf = msgpack_unpacker_buffer(&decoder->unpacker); *len = msgpack_unpacker_buffer_capacity(&decoder->unpacker); } void tmate_decoder_init(struct tmate_decoder *decoder) { if (!msgpack_unpacker_init(&decoder->unpacker, 2*TMATE_MAX_MESSAGE_SIZE)) tmate_fatal("cannot initialize the unpacker"); decoder->ready = 0; } tmate-1.8.10/tmate-encoder.c000066400000000000000000000111621242461015400156060ustar00rootroot00000000000000#include "tmate.h" #define DEFAULT_ENCODER (&tmate_session.encoder) static int msgpack_write(void *data, const char *buf, unsigned int len) { struct tmate_encoder *encoder = data; evbuffer_add(encoder->buffer, buf, len); if ((encoder->ev_readable.ev_flags & EVLIST_INSERTED) && !(encoder->ev_readable.ev_flags & EVLIST_ACTIVE)) { event_active(&encoder->ev_readable, EV_READ, 0); } return 0; } void tmate_encoder_init(struct tmate_encoder *encoder) { msgpack_packer_init(&encoder->pk, encoder, &msgpack_write); encoder->ev_readable.ev_flags = 0; encoder->buffer = evbuffer_new(); } #define msgpack_pack_string(pk, str) do { \ int __strlen = strlen(str); \ msgpack_pack_raw(pk, __strlen); \ msgpack_pack_raw_body(pk, str, __strlen); \ } while(0) #define pack(what, ...) msgpack_pack_##what(&DEFAULT_ENCODER->pk, __VA_ARGS__) void tmate_write_header(void) { pack(array, 3); pack(int, TMATE_HEADER); pack(int, TMATE_PROTOCOL_VERSION); pack(string, VERSION); } void tmate_sync_layout(void) { struct session *s; struct winlink *wl; struct window *w; struct window_pane *wp; int num_panes = 0; int num_windows = 0; int active_pane_id = -1; int active_window_idx = -1; /* * We only allow one session, it makes our lives easier. * Especially when the HTML5 client will come along. * We make no distinction between a winlink and its window except * that we send the winlink idx to draw the status bar properly. */ s = RB_MIN(sessions, &sessions); if (!s) return; num_windows = 0; RB_FOREACH(wl, winlinks, &s->windows) { if (wl->window) num_windows++; } if (!num_windows) return; pack(array, 5); pack(int, TMATE_SYNC_LAYOUT); pack(int, s->sx); pack(int, s->sy); pack(array, num_windows); RB_FOREACH(wl, winlinks, &s->windows) { w = wl->window; if (!w) continue; if (active_window_idx == -1) active_window_idx = wl->idx; pack(array, 4); pack(int, wl->idx); pack(string, w->name); num_panes = 0; TAILQ_FOREACH(wp, &w->panes, entry) num_panes++; pack(array, num_panes); TAILQ_FOREACH(wp, &w->panes, entry) { pack(array, 5); pack(int, wp->id); pack(int, wp->sx); pack(int, wp->sy); pack(int, wp->xoff); pack(int, wp->yoff); if (wp == w->active) active_pane_id = wp->id; } pack(int, active_pane_id); } if (s->curw) active_window_idx = s->curw->idx; pack(int, active_window_idx); } void tmate_pty_data(struct window_pane *wp, const char *buf, size_t len) { size_t max_write, to_write; max_write = TMATE_MAX_MESSAGE_SIZE - 4; while (len > 0) { to_write = len < max_write ? len : max_write; pack(array, 3); pack(int, TMATE_PTY_DATA); pack(int, wp->id); pack(raw, to_write); pack(raw_body, buf, to_write); buf += to_write; len -= to_write; } } static const struct cmd_entry *replicated_cmds[] = { &cmd_bind_key_entry, &cmd_unbind_key_entry, &cmd_set_option_entry, &cmd_set_window_option_entry, NULL }; int tmate_should_replicate_cmd(const struct cmd_entry *cmd) { const struct cmd_entry **ptr; for (ptr = replicated_cmds; *ptr; ptr++) if (*ptr == cmd) return 1; return 0; } void tmate_exec_cmd(const char *cmd) { pack(array, 2); pack(int, TMATE_EXEC_CMD); pack(string, cmd); } void tmate_failed_cmd(int client_id, const char *cause) { pack(array, 3); pack(int, TMATE_FAILED_CMD); pack(int, client_id); pack(string, cause); } void tmate_status(const char *left, const char *right) { static char *old_left, *old_right; if (old_left && !strcmp(old_left, left) && old_right && !strcmp(old_right, right)) return; pack(array, 3); pack(int, TMATE_STATUS); pack(string, left); pack(string, right); free(old_left); free(old_right); old_left = xstrdup(left); old_right = xstrdup(right); } void tmate_sync_copy_mode(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; pack(array, 3); pack(int, TMATE_SYNC_COPY_MODE); pack(int, wp->id); if (wp->mode != &window_copy_mode) { pack(array, 0); return; } pack(array, 6); pack(int, data->backing == &wp->base); pack(int, data->oy); pack(int, data->cx); pack(int, data->cy); if (data->screen.sel.flag) { pack(array, 3); pack(int, data->selx); pack(int, -data->sely + screen_hsize(data->backing) + screen_size_y(data->backing) - 1); pack(int, data->rectflag); } else pack(array, 0); if (data->inputprompt) { pack(array, 3); pack(int, data->inputtype); pack(string, data->inputprompt); pack(string, data->inputstr); } else pack(array, 0); } void tmate_write_copy_mode(struct window_pane *wp, const char *str) { pack(array, 3); pack(int, TMATE_WRITE_COPY_MODE); pack(int, wp->id); pack(string, str); } tmate-1.8.10/tmate-env.c000066400000000000000000000014001242461015400147510ustar00rootroot00000000000000#include "tmate.h" struct tmate_env { TAILQ_ENTRY(tmate_env) entry; char *name; char *value; }; TAILQ_HEAD(, tmate_env) tmate_env_list; void tmate_set_env(const char *name, const char *value) { struct tmate_env *tmate_env; TAILQ_FOREACH(tmate_env, &tmate_env_list, entry) { if (!strcmp(tmate_env->name, name)) { free(tmate_env->value); tmate_env->value = xstrdup(value); return; } } tmate_env = xmalloc(sizeof(*tmate_env)); tmate_env->name = xstrdup(name); tmate_env->value = xstrdup(value); TAILQ_INSERT_HEAD(&tmate_env_list, tmate_env, entry); } void tmate_format(struct format_tree *ft) { struct tmate_env *tmate_env; TAILQ_FOREACH(tmate_env, &tmate_env_list, entry) { format_add(ft, tmate_env->name, "%s", tmate_env->value); } } tmate-1.8.10/tmate-msg.c000066400000000000000000000037241242461015400147620ustar00rootroot00000000000000#include #include "tmate.h" void status_message_callback(int, short, void *); /* Very similar to status.c:status_message_set */ static void tmate_status_message_client(struct client *c, const char *message) { struct timeval tv; struct session *s = c->session; struct message_entry *msg; int delay; u_int i, limit; status_prompt_clear(c); status_message_clear(c); xasprintf(&c->message_string, "[tmate] %s", message); ARRAY_EXPAND(&c->message_log, 1); msg = &ARRAY_LAST(&c->message_log); msg->msg_time = time(NULL); msg->msg = xstrdup(c->message_string); if (s) { limit = options_get_number(&s->options, "message-limit"); delay = options_get_number(&s->options, "tmate-display-time"); } else { /* Very early in the connection process we won't have a session */ limit = options_get_number(&global_s_options, "message-limit"); delay = options_get_number(&global_s_options, "tmate-display-time"); } if (ARRAY_LENGTH(&c->message_log) > limit) { limit = ARRAY_LENGTH(&c->message_log) - limit; for (i = 0; i < limit; i++) { msg = &ARRAY_FIRST(&c->message_log); free(msg->msg); ARRAY_REMOVE(&c->message_log, 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->flags |= CLIENT_STATUS | CLIENT_FORCE_STATUS; recalculate_sizes(); } void __tmate_status_message(const char *fmt, va_list ap) { struct client *c; unsigned int i; char *message; xvasprintf(&message, fmt, ap); tmate_debug("%s", message); for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c && !(c->flags & CLIENT_READONLY)) tmate_status_message_client(c, message); } free(message); } void printflike1 tmate_status_message(const char *fmt, ...) { va_list ap; va_start(ap, fmt); __tmate_status_message(fmt, ap); va_end(ap); } tmate-1.8.10/tmate-session.c000066400000000000000000000065721242461015400156630ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "tmate.h" #define TMATE_DNS_RETRY_TIMEOUT 10 struct tmate_session tmate_session; static struct evdns_base *ev_dnsbase; static struct event ev_dns_retry; static void lookup_and_connect(void); static void on_dns_retry(evutil_socket_t fd, short what, void *arg) { lookup_and_connect(); } static void dns_cb(int errcode, struct evutil_addrinfo *addr, void *ptr) { struct tmate_ssh_client *client; struct evutil_addrinfo *ai; struct timeval tv; const char *host = ptr; if (errcode) { tmate_status_message("%s lookup failure. Retrying in %d seconds (%s)", host, TMATE_DNS_RETRY_TIMEOUT, evutil_gai_strerror(errcode)); tv.tv_sec = TMATE_DNS_RETRY_TIMEOUT; tv.tv_usec = 0; evtimer_assign(&ev_dns_retry, ev_base, on_dns_retry, NULL); evtimer_add(&ev_dns_retry, &tv); return; } tmate_status_message("Connecting to %s...", host); for (ai = addr; ai; ai = ai->ai_next) { char buf[128]; const char *ip = NULL; if (ai->ai_family == AF_INET) { struct sockaddr_in *sin = (struct sockaddr_in *)ai->ai_addr; ip = evutil_inet_ntop(AF_INET, &sin->sin_addr, buf, 128); } else if (ai->ai_family == AF_INET6) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ai->ai_addr; ip = evutil_inet_ntop(AF_INET6, &sin6->sin6_addr, buf, 128); } tmate_debug("Trying server %s", ip); /* * Note: We don't deal with the client list. Clients manage it * and free client structs when necessary. */ (void)tmate_ssh_client_alloc(&tmate_session, ip); } evutil_freeaddrinfo(addr); /* * XXX For some reason, freeing the DNS resolver makes MacOSX flip out... * not sure what's going on... * evdns_base_free(ev_dnsbase, 0); * ev_dnsbase = NULL; */ } static void lookup_and_connect(void) { struct evutil_addrinfo hints; const char *tmate_server_host; if (!ev_dnsbase) ev_dnsbase = evdns_base_new(ev_base, 1); if (!ev_dnsbase) tmate_fatal("Cannot initialize the DNS lookup service"); memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_flags = 0; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; tmate_server_host = options_get_string(&global_s_options, "tmate-server-host"); tmate_info("Looking up %s...", tmate_server_host); (void)evdns_getaddrinfo(ev_dnsbase, tmate_server_host, NULL, &hints, dns_cb, tmate_server_host); } static void ssh_log_function(int priority, const char *function, const char *buffer, void *userdata) { tmate_debug("[%d] [%s] %s", priority, function, buffer); } void tmate_session_init(void) { ssh_set_log_callback(ssh_log_function); tmate_catch_sigsegv(); tmate_encoder_init(&tmate_session.encoder); tmate_decoder_init(&tmate_session.decoder); TAILQ_INIT(&tmate_session.clients); tmate_session.need_passphrase = 0; tmate_session.passphrase = NULL; /* The header will be written as soon as the first client connects */ tmate_write_header(); } void tmate_session_start(void) { /* We split init and start because: * - We need to process the tmux config file during the connection as * we are setting up the tmate identity. * - While we are parsing the config file, we need to be able to * serialize it, and so we need a worker encoder. */ lookup_and_connect(); } tmate-1.8.10/tmate-ssh-client.c000066400000000000000000000301341242461015400162400ustar00rootroot00000000000000#include #include #include #include #include #include "tmate.h" static void consume_channel(struct tmate_ssh_client *client); static void flush_input_stream(struct tmate_ssh_client *client); static void __flush_input_stream(evutil_socket_t fd, short what, void *arg); static void __on_session_event(evutil_socket_t fd, short what, void *arg); static void printflike2 kill_session(struct tmate_ssh_client *client, const char *fmt, ...); static void printflike2 reconnect_session(struct tmate_ssh_client *client, const char *fmt, ...); static void on_session_event(struct tmate_ssh_client *client); static void register_session_fd_event(struct tmate_ssh_client *client) { if (!event_initialized(&client->ev_ssh)) { int flag = 1; setsockopt(ssh_get_fd(client->session), IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)); event_assign(&client->ev_ssh, ev_base, ssh_get_fd(client->session), EV_READ | EV_PERSIST, __on_session_event, client); event_add(&client->ev_ssh, NULL); } } static void register_input_stream_event(struct tmate_ssh_client *client) { struct tmate_encoder *encoder = &client->tmate_session->encoder; if (!event_initialized(&encoder->ev_readable)) { event_assign(&encoder->ev_readable, ev_base, -1, EV_READ | EV_PERSIST, __flush_input_stream, client); event_add(&encoder->ev_readable, NULL); client->has_encoder = 1; } } static void consume_channel(struct tmate_ssh_client *client) { struct tmate_decoder *decoder = &client->tmate_session->decoder; char *buf; ssize_t len; for (;;) { tmate_decoder_get_buffer(decoder, &buf, &len); len = ssh_channel_read_nonblocking(client->channel, buf, len, 0); if (len < 0) { reconnect_session(client, "Error reading from channel: %s", ssh_get_error(client->session)); break; } if (len == 0) break; tmate_decoder_commit(decoder, len); } } static void connection_complete(struct tmate_ssh_client *connected_client) { struct tmate_session *session = connected_client->tmate_session; struct tmate_ssh_client *client, *tmp_client; TAILQ_FOREACH_SAFE(client, &session->clients, node, tmp_client) { if (client == connected_client) continue; assert(!client->has_encoder); kill_session(client, NULL); } } static char *get_identity(void) { char *identity; identity = options_get_string(&global_s_options, "tmate-identity"); if (!strlen(identity)) return NULL; if (strchr(identity, '/')) identity = xstrdup(identity); else xasprintf(&identity, "%%d/%s", identity); return identity; } static int passphrase_callback(const char *prompt, char *buf, size_t len, int echo, int verify, void *userdata) { struct tmate_ssh_client *client = userdata; client->tmate_session->need_passphrase = 1; if (client->tmate_session->passphrase) strncpy(buf, client->tmate_session->passphrase, len); else strcpy(buf, ""); return 0; } static void on_passphrase_read(const char *passphrase, void *private) { struct tmate_ssh_client *client = private; client->tmate_session->passphrase = xstrdup(passphrase); on_session_event(client); } static void request_passphrase(struct tmate_ssh_client *client) { struct window_pane *wp; struct window_copy_mode_data *data; /* * We'll display the prompt on the first pane. * It doesn't make much sense, but it's simpler to reuse the copy mode * and its key parsing logic compared to rolling something on our own. */ wp = RB_MIN(window_pane_tree, &all_window_panes); if (wp->mode) { data = wp->modedata; if (data->inputtype == WINDOW_COPY_PASSWORD) { /* We are already requesting the passphrase */ return; } window_pane_reset_mode(wp); } window_pane_set_mode(wp, &window_copy_mode); window_copy_init_from_pane(wp); data = wp->modedata; data->inputtype = WINDOW_COPY_PASSWORD; data->inputprompt = "SSH key passphrase"; mode_key_init(&data->mdata, &mode_key_tree_vi_edit); window_copy_update_selection(wp); window_copy_redraw_screen(wp); data->password_cb = on_passphrase_read; data->password_cb_private = client; } static void on_session_event(struct tmate_ssh_client *client) { char *identity; ssh_key pubkey; int key_type; unsigned char *hash; ssize_t hash_len; char *hash_str; char *server_hash_str; int match; int verbosity = SSH_LOG_NOLOG + debug_level; int port = options_get_number(&global_s_options, "tmate-server-port"); ssh_session session = client->session; ssh_channel channel = client->channel; switch (client->state) { case SSH_INIT: client->session = session = ssh_new(); if (!session) { tmate_fatal("cannot initialize"); return; } ssh_set_callbacks(session, &client->ssh_callbacks); client->channel = channel = ssh_channel_new(session); if (!channel) { tmate_fatal("cannot initialize"); return; } ssh_set_blocking(session, 0); ssh_options_set(session, SSH_OPTIONS_HOST, client->server_ip); ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); ssh_options_set(session, SSH_OPTIONS_PORT, &port); ssh_options_set(session, SSH_OPTIONS_USER, "tmate"); ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes"); if ((identity = get_identity())) { /* * FIXME libssh will continue with the next set of * keys if the identity has a passphrase and the * regular one doesn't. */ ssh_options_set(session, SSH_OPTIONS_IDENTITY, identity); free(identity); } client->state = SSH_CONNECT; /* fall through */ case SSH_CONNECT: switch (ssh_connect(session)) { case SSH_AGAIN: register_session_fd_event(client); return; case SSH_ERROR: reconnect_session(client, "Error connecting: %s", ssh_get_error(session)); return; case SSH_OK: register_session_fd_event(client); tmate_debug("Establishing connection to %s", client->server_ip); client->state = SSH_AUTH_SERVER; /* fall through */ } case SSH_AUTH_SERVER: if ((hash_len = ssh_get_pubkey_hash(session, &hash)) < 0) { kill_session(client, "Cannot authenticate server"); return; } hash_str = ssh_get_hexa(hash, hash_len); if (!hash_str) tmate_fatal("malloc failed"); if (ssh_get_publickey(session, &pubkey) < 0) tmate_fatal("ssh_get_publickey"); key_type = ssh_key_type(pubkey); switch (key_type) { case SSH_KEYTYPE_DSS: server_hash_str = options_get_string(&global_s_options, "tmate-server-dsa-fingerprint"); break; case SSH_KEYTYPE_RSA: server_hash_str = options_get_string(&global_s_options, "tmate-server-rsa-fingerprint"); break; case SSH_KEYTYPE_ECDSA: server_hash_str = options_get_string(&global_s_options, "tmate-server-ecdsa-fingerprint"); break; default: server_hash_str = ""; } match = !strcmp(hash_str, server_hash_str); ssh_key_free(pubkey); ssh_clean_pubkey_hash(&hash); free(hash_str); if (!match) { kill_session(client, "Cannot authenticate server"); return; } /* * At this point, we abort other connection attempts to the * other tmate servers, since we have reached the fastest one. * We need to do it before we ask the user its passphrase, * otherwise the speed test would be biased. */ tmate_debug("Connected to %s", client->server_ip); connection_complete(client); client->state = SSH_AUTH_CLIENT; /* fall through */ case SSH_AUTH_CLIENT: client->tried_passphrase = client->tmate_session->passphrase; switch (ssh_userauth_autopubkey(session, client->tried_passphrase)) { case SSH_AUTH_AGAIN: return; case SSH_AUTH_PARTIAL: case SSH_AUTH_INFO: case SSH_AUTH_DENIED: if (client->tmate_session->need_passphrase && !client->tried_passphrase) request_passphrase(client); else kill_session(client, "Access denied. Check your SSH keys."); return; case SSH_AUTH_ERROR: reconnect_session(client, "Auth error: %s", ssh_get_error(session)); return; case SSH_AUTH_SUCCESS: tmate_debug("Auth successful"); client->state = SSH_OPEN_CHANNEL; /* fall through */ } case SSH_OPEN_CHANNEL: switch (ssh_channel_open_session(channel)) { case SSH_AGAIN: return; case SSH_ERROR: reconnect_session(client, "Error opening channel: %s", ssh_get_error(session)); return; case SSH_OK: tmate_debug("Session opened, initalizing tmate"); client->state = SSH_BOOTSTRAP; /* fall through */ } case SSH_BOOTSTRAP: switch (ssh_channel_request_subsystem(channel, "tmate")) { case SSH_AGAIN: return; case SSH_ERROR: reconnect_session(client, "Error initializing tmate: %s", ssh_get_error(session)); return; case SSH_OK: tmate_debug("Ready"); /* Writes are now performed in a blocking fashion */ ssh_set_blocking(session, 1); client->state = SSH_READY; register_input_stream_event(client); flush_input_stream(client); /* fall through */ } case SSH_READY: consume_channel(client); if (!ssh_is_connected(session)) { reconnect_session(client, "Disconnected"); return; } } } static void flush_input_stream(struct tmate_ssh_client *client) { struct tmate_encoder *encoder = &client->tmate_session->encoder; struct evbuffer *evb = encoder->buffer; ssize_t len, written; char *buf; if (client->state < SSH_READY) return; for (;;) { len = evbuffer_get_length(evb); if (!len) break; buf = evbuffer_pullup(evb, -1); written = ssh_channel_write(client->channel, buf, len); if (written < 0) { reconnect_session(client, "Error writing to channel: %s", ssh_get_error(client->session)); return; } evbuffer_drain(evb, written); } } static void __flush_input_stream(evutil_socket_t fd, short what, void *arg) { flush_input_stream(arg); } static void __on_session_event(evutil_socket_t fd, short what, void *arg) { on_session_event(arg); } static void __kill_session(struct tmate_ssh_client *client, const char *fmt, va_list va) { struct tmate_encoder *encoder; if (fmt && TAILQ_EMPTY(&client->tmate_session->clients)) __tmate_status_message(fmt, va); else tmate_debug("Disconnecting %s", client->server_ip); if (event_initialized(&client->ev_ssh)) { event_del(&client->ev_ssh); client->ev_ssh.ev_flags = 0; } if (client->has_encoder) { encoder = &client->tmate_session->encoder; event_del(&encoder->ev_readable); encoder->ev_readable.ev_flags = 0; client->has_encoder = 0; } if (client->session) { /* ssh_free() also frees the associated channels. */ ssh_free(client->session); client->session = NULL; client->channel = NULL; } client->state = SSH_NONE; } static void printflike2 kill_session(struct tmate_ssh_client *client, const char *fmt, ...) { va_list ap; TAILQ_REMOVE(&client->tmate_session->clients, client, node); va_start(ap, fmt); __kill_session(client, fmt, ap); va_end(ap); free(client->server_ip); free(client); } static void connect_session(struct tmate_ssh_client *client) { if (!client->session) { client->state = SSH_INIT; on_session_event(client); } } static void on_reconnect_timer(evutil_socket_t fd, short what, void *arg) { connect_session(arg); } static void printflike2 reconnect_session(struct tmate_ssh_client *client, const char *fmt, ...) { struct timeval tv; va_list ap; #if 1 TAILQ_REMOVE(&client->tmate_session->clients, client, node); #endif va_start(ap, fmt); __kill_session(client, fmt, ap); va_end(ap); /* Not yet implemented... */ #if 0 tv.tv_sec = 1; tv.tv_usec = 0; evtimer_add(&client->ev_ssh_reconnect, &tv); #endif } struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session, const char *server_ip) { struct tmate_ssh_client *client; client = xmalloc(sizeof(*client)); memset(&client->ssh_callbacks, 0, sizeof(client->ssh_callbacks)); ssh_callbacks_init(&client->ssh_callbacks); client->ssh_callbacks.userdata = client; client->ssh_callbacks.auth_function = passphrase_callback; client->tmate_session = session; TAILQ_INSERT_TAIL(&session->clients, client, node); client->server_ip = xstrdup(server_ip); client->state = SSH_NONE; client->session = NULL; client->channel = NULL; client->has_encoder = 0; client->ev_ssh.ev_flags = 0; evtimer_assign(&client->ev_ssh_reconnect, ev_base, on_reconnect_timer, client); connect_session(client); return client; } tmate-1.8.10/tmate.1000077700000000000000000000000001242461015400151602tmux.1ustar00rootroot00000000000000tmate-1.8.10/tmate.h000066400000000000000000000076731242461015400142120ustar00rootroot00000000000000#ifndef TMATE_H #define TMATE_H #include #include #include #include #include #include "tmux.h" #define tmate_debug(...) log_debug("[tmate] " __VA_ARGS__) #define tmate_warn(...) log_warn("[tmate] " __VA_ARGS__) #define tmate_info(...) log_info("[tmate] " __VA_ARGS__) #define tmate_fatal(...) log_fatal("[tmate] " __VA_ARGS__) /* tmate-encoder.c */ #define TMATE_MAX_MESSAGE_SIZE (16*1024) #define TMATE_PROTOCOL_VERSION 4 enum tmate_commands { TMATE_HEADER, TMATE_SYNC_LAYOUT, TMATE_PTY_DATA, TMATE_EXEC_CMD, TMATE_FAILED_CMD, TMATE_STATUS, TMATE_SYNC_COPY_MODE, TMATE_WRITE_COPY_MODE, }; struct tmate_encoder { msgpack_packer pk; struct evbuffer *buffer; struct event ev_readable; }; extern void tmate_encoder_init(struct tmate_encoder *encoder); extern void tmate_write_header(void); extern void tmate_sync_layout(void); extern void tmate_pty_data(struct window_pane *wp, const char *buf, size_t len); extern int tmate_should_replicate_cmd(const struct cmd_entry *cmd); extern void tmate_exec_cmd(const char *cmd); extern void tmate_failed_cmd(int client_id, const char *cause); extern void tmate_status(const char *left, const char *right); extern void tmate_sync_copy_mode(struct window_pane *wp); extern void tmate_write_copy_mode(struct window_pane *wp, const char *str); /* tmate-decoder.c */ enum tmate_client_commands { TMATE_NOTIFY, TMATE_CLIENT_PANE_KEY, TMATE_CLIENT_RESIZE, TMATE_CLIENT_EXEC_CMD, TMATE_CLIENT_ENV, TMATE_CLIENT_READY, }; struct tmate_decoder { struct msgpack_unpacker unpacker; int ready; }; extern int tmate_sx; extern int tmate_sy; extern void tmate_decoder_init(struct tmate_decoder *decoder); extern void tmate_decoder_get_buffer(struct tmate_decoder *decoder, char **buf, size_t *len); extern void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len); /* tmate-ssh-client.c */ enum tmate_ssh_client_state_types { SSH_NONE, SSH_INIT, SSH_CONNECT, SSH_AUTH_SERVER, SSH_AUTH_CLIENT, SSH_OPEN_CHANNEL, SSH_BOOTSTRAP, SSH_READY, }; struct tmate_ssh_client { /* XXX The "session" word is used for three things: * - the ssh session * - the tmate sesssion * - the tmux session * A tmux session is associated 1:1 with a tmate session. * An ssh session belongs to a tmate session, and a tmate session * has one ssh session, except during bootstrapping where * there is one ssh session per tmate server, and the first one wins. */ struct tmate_session *tmate_session; TAILQ_ENTRY(tmate_ssh_client) node; char *server_ip; int has_encoder; int state; /* * ssh_callbacks is allocated because the libssh API sucks (userdata * has to be in the struct itself). */ struct ssh_callbacks_struct ssh_callbacks; char *tried_passphrase; ssh_session session; ssh_channel channel; struct event ev_ssh; struct event ev_ssh_reconnect; }; TAILQ_HEAD(tmate_ssh_clients, tmate_ssh_client); extern struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session, const char *server_ip); /* tmate-session.c */ struct tmate_session { struct tmate_encoder encoder; struct tmate_decoder decoder; /* * This list contains one connection per IP. The first connected * client wins, and saved in *client. When we have a winner, the * losers are disconnected and killed. */ struct tmate_ssh_clients clients; int need_passphrase; char *passphrase; }; extern struct tmate_session tmate_session; extern void tmate_session_init(void); extern void tmate_session_start(void); /* tmate-debug.c */ extern void tmate_print_trace(void); extern void tmate_catch_sigsegv(void); /* tmate-msg.c */ extern void __tmate_status_message(const char *fmt, va_list ap); extern void printflike1 tmate_status_message(const char *fmt, ...); /* tmate-env.c */ extern int tmate_has_received_env(void); extern void tmate_set_env(const char *name, const char *value); extern void tmate_format(struct format_tree *ft); #endif tmate-1.8.10/tmux.1000066400000000000000000002701561242461015400140040ustar00rootroot00000000000000.\" $Id$ .\" .\" 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: March 25 2013 $ .Dt TMUX 1 .Os .Sh NAME .Nm tmux .Nd terminal multiplexer .Sh SYNOPSIS .Nm tmux .Bk -words .Op Fl 28lCquvV .Op Fl c Ar shell-command .Op Fl f Ar file .Op Fl L Ar socket-name .Op Fl S Ar socket-path .Op Ar command Op Ar flags .Ek .Sh DESCRIPTION .Nm is a terminal multiplexer: it enables a number of terminals to be created, accessed, and controlled from a single screen. .Nm may be detached from a screen and continue running in the background, then later reattached. .Pp When .Nm is started it creates a new .Em session with a single .Em window and displays it on screen. A status line at the bottom of the screen shows information on the current session and is used to enter interactive commands. .Pp A session is a single collection of .Em pseudo terminals under the management of .Nm . Each session has one or more windows linked to it. A window occupies the entire screen and may be split into rectangular panes, each of which is a separate pseudo terminal (the .Xr pty 4 manual page documents the technical details of pseudo terminals). Any number of .Nm instances may connect to the same session, and any number of windows may be present in the same session. Once all sessions are killed, .Nm exits. .Pp Each session is persistent and will survive accidental disconnection (such as .Xr ssh 1 connection timeout) or intentional detaching (with the .Ql C-b d key strokes). .Nm may be reattached using: .Pp .Dl $ tmux attach .Pp In .Nm , a session is displayed on screen by a .Em client and all sessions are managed by a single .Em server . The server and each client are separate processes which communicate through a socket in .Pa /tmp . .Pp The options are as follows: .Bl -tag -width "XXXXXXXXXXXX" .It Fl 2 Force .Nm to assume the terminal supports 256 colours. .It Fl 8 Like .Fl 2 , but indicates that the terminal supports 88 colours. .It Fl C Start in control mode. 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 /etc/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 .Pa /tmp (or .Ev TMPDIR if set); the default socket is named .Em default . This option allows a different socket name to be specified, allowing several independent .Nm servers to be run. Unlike .Fl S a full path is not necessary: the sockets are all created in the same directory. .Pp If the socket is accidentally removed, the .Dv SIGUSR1 signal may be sent to the .Nm server process to recreate it. .It Fl l Behave as a login shell. This flag currently has no effect and is for compatibility with other shells when using tmux as a login shell. .It Fl q Set the .Ic quiet server option to prevent the server sending various informational messages. .It Fl S Ar socket-path Specify a full alternative path to the server socket. If .Fl S is specified, the default socket directory is not used and any .Fl L flag is ignored. .It Fl u .Nm attempts to guess if the terminal is likely to support UTF-8 by checking the first of the .Ev LC_ALL , .Ev LC_CTYPE and .Ev LANG environment variables to be set for the string "UTF-8". This is not always correct: the .Fl u flag explicitly informs .Nm that UTF-8 is supported. .Pp If the server is started from a client passed .Fl u or where UTF-8 is detected, the .Ic utf8 and .Ic status-utf8 options are enabled in the global window and session options respectively. .It Fl v Request verbose logging. This option may be specified multiple times for increasing verbosity. Log messages will be saved into .Pa tmux-client-PID.log and .Pa tmux-server-PID.log files in the current directory, where .Em PID is the PID of the server or client process. .It Fl V Report the .Nm version. .It Ar command Op Ar flags This specifies one of a set of commands used to control .Nm , as described in the following sections. If no commands are specified, the .Ic new-session command is assumed. .El .Sh KEY BINDINGS .Nm may be controlled from an attached client by using a key combination of a prefix key, .Ql C-b (Ctrl-b) by default, followed by a command key. .Pp The default command key bindings are: .Pp .Bl -tag -width "XXXXXXXXXX" -offset indent -compact .It C-b Send the prefix key (C-b) through to the application. .It C-o Rotate the panes in the current window forwards. .It C-z Suspend the .Nm client. .It ! Break the current pane out of the window. .It \&" Split the current pane into two, top and bottom. .It # List all paste buffers. .It $ Rename the current session. .It % Split the current pane into two, left and right. .It & Kill the current window. .It ' Prompt for a window index to select. .It , Rename the current window. .It - Delete the most recently copied buffer of text. .It . Prompt for an index to move the current window. .It 0 to 9 Select windows 0 to 9. .It : Enter the .Nm command prompt. .It ; Move to the previously active pane. .It = Choose which buffer to paste interactively from a list. .It \&? List all key bindings. .It D Choose a client to detach. .It \&[ Enter copy mode to copy text or view the history. .It \&] Paste the most recently copied buffer of text. .It c Create a new window. .It d Detach the current client. .It f Prompt to search for text in open windows. .It i Display some information about the current window. .It l Move to the previously selected window. .It n Change to the next window. .It o Select the next pane in the current window. .It p Change to the previous window. .It q Briefly display pane indexes. .It r Force redraw of the attached client. .It s Select a new session for the attached client interactively. .It L Switch the attached client back to the last session. .It t Show the time. .It w Choose the current window interactively. .It x Kill the current pane. .It { Swap the current pane with the previous pane. .It } Swap the current pane with the next pane. .It ~ Show previous messages from .Nm , if any. .It Page Up Enter copy mode and scroll one page up. .It Up, Down .It Left, Right Change to the pane above, below, to the left, or to the right of the current pane. .It M-1 to M-5 Arrange panes in one of the five preset layouts: even-horizontal, even-vertical, main-horizontal, main-vertical, or tiled. .It M-n Move to the next window with a bell or activity marker. .It M-o Rotate the panes in the current window backwards. .It M-p Move to the previous window with a bell or activity marker. .It C-Up, C-Down .It C-Left, C-Right Resize the current pane in steps of one cell. .It M-Up, M-Down .It M-Left, M-Right Resize the current pane in steps of five cells. .El .Pp Key bindings may be changed with the .Ic bind-key and .Ic unbind-key commands. .Sh COMMANDS This section contains a list of the commands supported by .Nm . Most commands accept the optional .Fl t argument with one of .Ar target-client , .Ar target-session .Ar target-window , or .Ar target-pane . These specify the client, session, window or pane which a command should affect. .Ar target-client is the name of the .Xr pty 4 file to which the client is connected, for example either of .Pa /dev/ttyp1 or .Pa ttyp1 for the client attached to .Pa /dev/ttyp1 . If no client is specified, the current client is chosen, if possible, or an error is reported. Clients may be listed with the .Ic list-clients command. .Pp .Ar target-session is the session id prefixed with a $, the name of a session (as listed by the .Ic list-sessions command), or the name of a client with the same syntax as .Ar target-client , in which case the session attached to the client is used. When looking for the session name, .Nm initially searches for an exact match; if none is found, the session names are checked for any for which .Ar target-session is a prefix or for which it matches as an .Xr fnmatch 3 pattern. If a single match is found, it is used as the target session; multiple matches produce an error. If a session is omitted, the current session is used if available; if no current session is available, the most recently used is chosen. .Pp .Ar target-window specifies a window in the form .Em session Ns \&: Ns Em window . .Em session follows the same rules as for .Ar target-session , and .Em window is looked for in order: as a window index, for example mysession:1; as a window ID, such as @1; as an exact window name, such as mysession:mywindow; then as an .Xr fnmatch 3 pattern or the start of a window name, such as mysession:mywin* or mysession:mywin. An empty window name specifies the next unused index if appropriate (for example the .Ic new-window and .Ic link-window commands) otherwise the current window in .Em session is chosen. The special character .Ql \&! uses the last (previously current) window, .Ql ^ selects the highest numbered window, .Ql $ selects the lowest numbered window, and .Ql + and .Ql - select the next window or the previous window by number. When the argument does not contain a colon, .Nm first attempts to parse it as window; if that fails, an attempt is made to match a session. .Pp .Ar target-pane takes a similar form to .Ar target-window but with the optional addition of a period followed by a pane index, for example: mysession:mywindow.1. If the pane index is omitted, the currently active pane in the specified window is used. If neither a colon nor period appears, .Nm first attempts to use the argument as a pane index; if that fails, it is looked up as for .Ar target-window . A .Ql + or .Ql - indicate the next or previous pane index, respectively. One of the strings .Em top , .Em bottom , .Em left , .Em right , .Em top-left , .Em top-right , .Em bottom-left or .Em bottom-right may be used instead of a pane index. .Pp The special characters .Ql + and .Ql - may be followed by an offset, for example: .Bd -literal -offset indent select-window -t:+2 .Ed .Pp When dealing with a session that doesn't contain sequential window indexes, they will be correctly skipped. .Pp .Nm also gives each pane created in a server an identifier consisting of a .Ql % and a number, starting from zero. A pane's identifier is unique for the life of the .Nm server and is passed to the child process of the pane in the .Ev TMUX_PANE environment variable. It may be used alone to target a pane or the window containing it. .Pp .Ar shell-command arguments are .Xr sh 1 commands. These must be passed as a single item, which typically means quoting them, for example: .Bd -literal -offset indent new-window 'vi /etc/passwd' .Ed .Pp .Ar command .Op Ar arguments refers to a .Nm command, passed with the command and arguments separately, for example: .Bd -literal -offset indent bind-key F1 set-window-option force-width 81 .Ed .Pp Or if using .Xr sh 1 : .Bd -literal -offset indent $ tmux bind-key F1 set-window-option force-width 81 .Ed .Pp Multiple commands may be specified together as part of a .Em command sequence . Each command should be separated by spaces and a semicolon; commands are executed sequentially from left to right and lines ending with a backslash continue on to the next line, except when escaped by another backslash. A literal semicolon may be included by escaping it with a backslash (for example, when specifying a command sequence to .Ic bind-key ) . .Pp Example .Nm commands include: .Bd -literal -offset indent refresh-client -t/dev/ttyp2 rename-session -tfirst newname set-window-option -t:0 monitor-activity on new-window ; split-window -d 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 dr .Op Fl t Ar target-session .Xc .D1 (alias: Ic attach ) If run from outside .Nm , create a new client in the current terminal and attach it to .Ar target-session . If used from inside, switch the current client. If .Fl d is specified, any other clients attached to the session are detached. .Fl r signifies the client is read-only (only keys bound to the .Ic detach-client 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. .It Xo Ic detach-client .Op Fl P .Op Fl a .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. .It Ic has-session Op Fl t Ar target-session .D1 (alias: Ic has ) Report an error and exit with 1 if the specified session does not exist. If it does exist, exit with 0. .It Ic kill-server Kill the .Nm server and clients and destroy all sessions. .It Ic kill-session .Op Fl a .Op Fl t Ar target-session Destroy the given session, closing any windows linked to it and no other sessions, and detaching all clients attached to it. If .Fl a is given, all sessions but the specified one is killed. .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 Ic list-commands .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 AdDP .Op Fl F Ar format .Op Fl n Ar window-name .Op Fl s Ar session-name .Op Fl t Ar target-session .Op Fl x Ar width .Op Fl y Ar height .Op Ar shell-command .Xc .D1 (alias: Ic new ) Create a new session with name .Ar session-name . .Pp The new session is attached to the current terminal unless .Fl d is given. .Ar window-name and .Ar shell-command are the name of and shell command to execute in the initial window. If .Fl d is used, .Fl x and .Fl y specify the size of the initial window (80 by 24 if not given). .Pp If run from a terminal, any .Xr termios 4 special characters are saved and used for new windows in the new session. .Pp The .Fl A flag makes .Ic new-session behave like .Ic attach-session if .Ar session-name already exists; in the case, .Fl D behaves like .Fl d to .Ic attach-session . .Pp If .Fl t is given, the new session is .Em grouped with .Ar target-session . This means they share the same set of windows - all windows from .Ar target-session are linked to the new session and any subsequent new windows or windows being closed are applied to both sessions. The current and previous window and any session options remain independent and either session may be killed without affecting the other. Giving .Fl n or .Ar shell-command are invalid if .Fl t is used. .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 . .It Xo Ic refresh-client .Op Fl S .Op Fl t Ar target-client .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 bar. .It Xo Ic rename-session .Op Fl t Ar target-session .Ar new-name .Xc .D1 (alias: Ic rename ) Rename the session to .Ar new-name . .It Xo Ic show-messages .Op Fl t Ar target-client .Xc .D1 (alias: Ic showmsgs ) Any messages displayed on the status line are saved in a per-client message log, up to a maximum of the limit set by the .Ar message-limit session option for the session attached to that client. This command displays the log for .Ar target-client . .It Ic source-file Ar path .D1 (alias: Ic source ) Execute commands from .Ar path . .It Ic start-server .D1 (alias: Ic start ) Start the .Nm server, if not already running, without creating any sessions. .It Xo Ic suspend-client .Op Fl t Ar target-client .Xc .D1 (alias: Ic suspendc ) Suspend a client by sending .Dv SIGTSTP (tty stop). .It Xo Ic switch-client .Op Fl lnpr .Op Fl c Ar target-client .Op Fl t Ar target-session .Xc .D1 (alias: Ic switchc ) Switch the current session for client .Ar target-client to .Ar target-session . If .Fl l , .Fl n or .Fl p is used, the client is moved to the last, next or previous session respectively. .Fl r toggles whether a client is read-only (see the .Ic attach-session command). .El .Sh WINDOWS AND PANES A .Nm window may be in one of several modes. The default permits direct access to the terminal attached to the window. The other is copy mode, which permits a section of a window or its history to be copied to a .Em paste buffer for later insertion into another window. This mode is entered with the .Ic copy-mode command, bound to .Ql \&[ by default. It is also entered when a command that produces output, such as .Ic list-keys , is executed from a key binding. .Pp The keys available depend on whether emacs or vi mode is selected (see the .Ic mode-keys option). The following keys are supported as appropriate for the mode: .Bl -column "FunctionXXXXXXXXXXXXXXXXX" "viXXXXXXXXXX" "emacs" -offset indent .It Sy "Function" Ta Sy "vi" Ta Sy "emacs" .It Li "Back to indentation" Ta "^" Ta "M-m" .It Li "Bottom of history" Ta "G" Ta "M-<" .It Li "Clear selection" Ta "Escape" Ta "C-g" .It Li "Copy selection" Ta "Enter" Ta "M-w" .It Li "Cursor down" Ta "j" Ta "Down" .It Li "Cursor left" Ta "h" Ta "Left" .It Li "Cursor right" Ta "l" Ta "Right" .It Li "Cursor to bottom line" Ta "L" Ta "" .It Li "Cursor to middle line" Ta "M" Ta "M-r" .It Li "Cursor to top line" Ta "H" Ta "M-R" .It Li "Cursor up" Ta "k" Ta "Up" .It Li "Delete entire line" Ta "d" Ta "C-u" .It Li "Delete/Copy to end of line" Ta "D" Ta "C-k" .It Li "End of line" Ta "$" Ta "C-e" .It Li "Go to line" Ta ":" Ta "g" .It Li "Half page down" Ta "C-d" Ta "M-Down" .It Li "Half page up" Ta "C-u" Ta "M-Up" .It Li "Jump forward" Ta "f" Ta "f" .It Li "Jump to forward" Ta "t" Ta "" .It Li "Jump backward" Ta "F" Ta "F" .It Li "Jump to backward" Ta "T" Ta "" .It Li "Jump again" Ta ";" Ta ";" .It Li "Jump again in reverse" Ta "," Ta "," .It Li "Next page" Ta "C-f" Ta "Page down" .It Li "Next space" Ta "W" Ta "" .It Li "Next space, end of word" Ta "E" Ta "" .It Li "Next word" Ta "w" Ta "" .It Li "Next word end" Ta "e" Ta "M-f" .It Li "Paste buffer" Ta "p" Ta "C-y" .It Li "Previous page" Ta "C-b" Ta "Page up" .It Li "Previous word" Ta "b" Ta "M-b" .It Li "Previous space" Ta "B" Ta "" .It Li "Quit mode" Ta "q" Ta "Escape" .It Li "Rectangle toggle" Ta "v" Ta "R" .It Li "Scroll down" Ta "C-Down or C-e" Ta "C-Down" .It Li "Scroll up" Ta "C-Up or C-y" Ta "C-Up" .It Li "Search again" Ta "n" Ta "n" .It Li "Search again in reverse" Ta "N" Ta "N" .It Li "Search backward" Ta "?" Ta "C-r" .It Li "Search forward" Ta "/" Ta "C-s" .It Li "Start of line" Ta "0" Ta "C-a" .It Li "Start selection" Ta "Space" Ta "C-Space" .It Li "Top of history" Ta "g" Ta "M->" .It Li "Transpose characters" Ta "" Ta "C-t" .El .Pp The next and previous word keys use space and the .Ql - , .Ql _ and .Ql @ characters as word delimiters by default, but this can be adjusted by setting the .Em word-separators 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. For example, to move the cursor forward by ten words, use .Ql M-1 0 M-f in emacs mode, and .Ql 10w in vi. .Pp When copying the selection, the repeat count indicates the buffer index to replace, if used. .Pp Mode key bindings are defined in a set of named tables: .Em vi-edit and .Em emacs-edit for keys used when line editing at the command prompt; .Em vi-choice and .Em emacs-choice for keys used when choosing from lists (such as produced by the .Ic choose-window command); and .Em vi-copy and .Em emacs-copy used in copy mode. The tables may be viewed with the .Ic list-keys command and keys modified or removed with .Ic bind-key and .Ic unbind-key . One command accepts an argument, .Ic copy-pipe , which copies the selection and pipes it to a command. For example the following will bind .Ql C-q to copy the selection into .Pa /tmp as well as the paste buffer: .Bd -literal -offset indent bind-key -temacs-copy C-q copy-pipe "cat >/tmp/out" .Ed .Pp The paste buffer key pastes the first line from the top paste buffer on the stack. .Pp The synopsis for the .Ic copy-mode command is: .Bl -tag -width Ds .It Xo Ic copy-mode .Op Fl u .Op Fl t Ar target-pane .Xc Enter copy mode. The .Fl u option scrolls one page up. .El .Pp Each window displayed by .Nm may be split into one or more .Em panes ; each pane takes up a certain area of the display and is a separate terminal. A window may be split into panes using the .Ic split-window command. Windows may be split horizontally (with the .Fl h flag) or vertically. Panes may be resized with the .Ic resize-pane command (bound to .Ql C-up , .Ql C-down .Ql C-left and .Ql C-right by default), the current pane may be changed with the .Ic select-pane command and the .Ic rotate-window and .Ic swap-pane commands may be used to swap panes without changing their position. Panes are numbered beginning from zero in the order they are created. .Pp A number of preset .Em layouts are available. These may be selected with the .Ic select-layout command or cycled with .Ic next-layout (bound to .Ql Space by default); once a layout is chosen, panes within it may be moved and resized as normal. .Pp The following layouts are supported: .Bl -tag -width Ds .It Ic even-horizontal Panes are spread out evenly from left to right across the window. .It Ic even-vertical Panes are spread evenly from top to bottom. .It Ic main-horizontal A large (main) pane is shown at the top of the window and the remaining panes are spread from left to right in the leftover space at the bottom. Use the .Em main-pane-height window option to specify the height of the top pane. .It Ic main-vertical Similar to .Ic main-horizontal but the large pane is placed on the left and the others spread from top to bottom along the right. See the .Em main-pane-width window option. .It Ic tiled Panes are spread out as evenly as possible over the window in both rows and columns. .El .Pp In addition, .Ic select-layout may be used to apply a previously used layout - the .Ic list-windows command displays the layout of each window in a form suitable for use with .Ic select-layout . For example: .Bd -literal -offset indent $ tmux list-windows 0: ksh [159x48] layout: bb62,159x48,0,0{79x48,0,0,79x48,80,0} $ tmux select-layout bb62,159x48,0,0{79x48,0,0,79x48,80,0} .Ed .Pp .Nm automatically adjusts the size of the layout for the current window size. Note that a layout cannot be applied to a window with more panes than that from which the layout was originally defined. .Pp Commands related to windows and panes are as follows: .Bl -tag -width Ds .It Xo Ic break-pane .Op Fl dP .Op Fl F Ar format .Op Fl t Ar target-pane .Xc .D1 (alias: Ic breakp ) Break .Ar target-pane off from its containing window to make it the only pane in a new window. If .Fl d is given, the new window does not become the current window. 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 aepPq .Op Fl b Ar buffer-index .Op Fl E Ar end-line .Op Fl S Ar start-line .Op Fl t Ar target-pane .Xc .D1 (alias: Ic capturep ) Capture the contents of a pane. 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. The default is to capture only the visible contents of the pane. .It Xo .Ic choose-client .Op Fl F Ar format .Op Fl t Ar target-window .Op Ar template .Xc Put a window into client choice mode, allowing a client to be selected interactively from a list. After a client is chosen, .Ql %% is replaced by the client .Xr pty 4 path in .Ar template and the result executed as a command. If .Ar template is not given, "detach-client -t '%%'" is used. For the meaning of the .Fl F flag, see the .Sx FORMATS section. This command works only if at least one client is attached. .It Xo .Ic choose-list .Op Fl l Ar items .Op Fl t Ar target-window .Op Ar template .Xc Put a window into list choice mode, allowing .Ar items to be selected. .Ar items can be a comma-separated list to display more than one item. If an item has spaces, that entry must be quoted. After an item is chosen, .Ql %% is replaced by the chosen item in the .Ar template and the result is executed as a command. If .Ar template is not given, "run-shell '%%'" is used. .Ar items also accepts format specifiers. For the meaning of this see the .Sx FORMATS section. This command works only if at least one client is attached. .It Xo .Ic choose-session .Op Fl F Ar format .Op Fl t Ar target-window .Op Ar template .Xc Put a window into session choice mode, where a session may be selected interactively from a list. When one is chosen, .Ql %% is replaced by the session name in .Ar template and the result executed as a command. If .Ar template is not given, "switch-client -t '%%'" is used. For the meaning of the .Fl F flag, see the .Sx FORMATS section. This command works only if at least one client is attached. .It Xo .Ic choose-tree .Op Fl suw .Op Fl b Ar session-template .Op Fl c Ar window-template .Op Fl S Ar format .Op Fl W Ar format .Op Fl t Ar target-window .Xc Put a window into tree choice mode, where either sessions or windows may be selected interactively from a list. By default, windows belonging to a session are indented to show their relationship to a session. .Pp Note that the .Ic choose-window and .Ic choose-session commands are wrappers around .Ic choose-tree . .Pp If .Fl s is given, will show sessions. If .Fl w is given, will show windows. .Pp By default, the tree is collapsed and sessions must be expanded to windows with the right arrow key. The .Fl u option will start with all sessions expanded instead. .Pp If .Fl b is given, will override the default session command. Note that .Ql %% can be used and will be replaced with the session name. The default option if not specified is "switch-client -t '%%'". If .Fl c is given, will override the default window command. Like .Fl b , .Ql %% can be used and will be replaced with the session name and window index. When a window is chosen from the list, the session command is run before the window command. .Pp If .Fl S is given will display the specified format instead of the default session format. If .Fl W is given will display the specified format instead of the default window format. For the meaning of the .Fl s and .Fl w options, see the .Sx FORMATS section. .Pp This command works only if at least one client is attached. .It Xo .Ic choose-window .Op Fl F Ar format .Op Fl t Ar target-window .Op Ar template .Xc Put a window into window choice mode, where a window may be chosen interactively from a list. After a window is selected, .Ql %% is replaced by the session name and window index in .Ar template and the result executed as a command. If .Ar template is not given, "select-window -t '%%'" is used. For the meaning of the .Fl F flag, see the .Sx FORMATS section. This command works only if at least one client is attached. .It Ic display-panes Op Fl t Ar target-client .D1 (alias: Ic displayp) Display a visible indicator of each pane shown by .Ar target-client . See the .Ic display-panes-time , .Ic display-panes-colour , and .Ic display-panes-active-colour session options. While the indicator is on screen, a pane may be selected with the .Ql 0 to .Ql 9 keys. .It Xo Ic find-window .Op Fl CNT .Op Fl F Ar format .Op Fl t Ar target-window .Ar match-string .Xc .D1 (alias: Ic findw ) Search for the .Xr fnmatch 3 pattern .Ar match-string in window names, titles, and visible content (but not history). 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 . If only one window is matched, it'll be automatically selected, otherwise a choice list is shown. For the meaning of the .Fl F flag, see the .Sx FORMATS section. 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 . .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 Ic last-pane Op Fl t Ar target-window .D1 (alias: Ic lastp ) Select the last (previously selected) pane. .It Ic last-window Op Fl t Ar target-session .D1 (alias: Ic last ) Select the last (previously selected) window. If no .Ar target-session is specified, select the last window of the current session. .It Xo Ic link-window .Op Fl dk .Op Fl s Ar src-window .Op Fl t Ar dst-window .Xc .D1 (alias: Ic linkw ) Link the window at .Ar src-window to the specified .Ar dst-window . If .Ar dst-window is specified and no such window exists, the .Ar src-window is linked there. If .Fl k is given and .Ar dst-window exists, it is killed, otherwise an error is generated. If .Fl d is given, the newly linked window is not selected. .It Xo Ic list-panes .Op Fl as .Op Fl 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 rdk .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 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. It may have an absolute path or one of the following values (or a subdirectory): .Bl -column "XXXXXXXXXXXX" "XXXXXXXXXXXXXXXXXXXXXXXX" -offset indent .It Li "Empty string" Ta "Current pane's directory" .It Li "~" Ta "User's home directory" .It Li "-" Ta "Where session was started" .It Li "." Ta "Where server was started" .El .Pp When the shell command completes, the window closes. See the .Ic remain-on-exit option to change this behaviour. .Pp The .Ev TERM environment variable must be set to .Dq screen for all programs running .Em inside .Nm . New windows will automatically have .Dq TERM=screen added to their environment, but care must be taken not to reset this in shell start-up files. .Pp The .Fl P option prints 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 o .Op Fl t Ar target-pane .Op Ar shell-command .Xc .D1 (alias: Ic pipep ) Pipe any output sent by the program in .Ar target-pane to a shell command. A pane may only be piped to one command at a time, any existing pipe is closed before .Ar shell-command is executed. The .Ar shell-command string may contain the special character sequences supported by the .Ic status-left option. If no .Ar shell-command is given, the current pipe (if any) is closed. .Pp The .Fl o option only opens a new pipe if no previous pipe exists, allowing a pipe to be toggled with a single key, for example: .Bd -literal -offset indent bind-key C-p pipe-pane -o 'cat >>~/output.#I-#P' .Ed .It Xo Ic previous-layout .Op Fl t Ar target-window .Xc .D1 (alias: Ic prevl ) Move to the previous layout in the session. .It Xo Ic previous-window .Op Fl a .Op Fl t Ar target-session .Xc .D1 (alias: Ic prev ) Move to the previous window in the session. With .Fl a , move to the previous window with 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 DLRUZ .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). .It Xo Ic respawn-pane .Op Fl k .Op Fl t Ar target-pane .Op Ar shell-command .Xc .D1 (alias: Ic respawnp ) Reactivate a pane in which the command has exited (see the .Ic remain-on-exit window option). If .Ar shell-command is not given, the command used when the pane was created is executed. The pane must be already inactive, unless .Fl k is given, in which case any existing command is killed. .It Xo Ic respawn-window .Op Fl k .Op Fl t Ar target-window .Op Ar shell-command .Xc .D1 (alias: Ic respawnw ) Reactivate a window in which the command has exited (see the .Ic remain-on-exit window option). If .Ar shell-command is not given, the command used when the window was created is executed. The window must be already inactive, unless .Fl k is given, in which case any existing command is killed. .It Xo Ic rotate-window .Op Fl DU .Op Fl t Ar target-window .Xc .D1 (alias: Ic rotatew ) Rotate the positions of the panes within a window, either upward (numerically lower) with .Fl U or downward (numerically higher). .It Xo Ic select-layout .Op Fl np .Op Fl t Ar target-window .Op Ar layout-name .Xc .D1 (alias: Ic selectl ) Choose a specific layout for a window. If .Ar layout-name is not given, the last preset layout used (if any) is reapplied. .Fl n and .Fl p are equivalent to the .Ic next-layout and .Ic previous-layout commands. .It Xo Ic select-pane .Op Fl lDLRU .Op Fl t Ar target-pane .Xc .D1 (alias: Ic selectp ) Make pane .Ar target-pane the active pane in window .Ar target-window . If one of .Fl D , .Fl L , .Fl R , or .Fl U is used, respectively the pane below, to the left, to the right, or above the target pane is used. .Fl l is the same as using the .Ic last-pane command. .It Xo Ic select-window .Op Fl 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 dhvP .Op Fl c Ar start-directory .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. All other options have the same meaning as for the .Ic new-window command. .It Xo Ic swap-pane .Op Fl dDU .Op Fl s Ar src-pane .Op Fl t Ar dst-pane .Xc .D1 (alias: Ic swapp ) Swap two panes. If .Fl U is used and no source pane is specified with .Fl s , .Ar dst-pane is swapped with the previous pane (before it numerically); .Fl D swaps with the next pane (after it numerically). .Fl d instructs .Nm not to change the active pane. .It Xo Ic swap-window .Op Fl d .Op Fl s Ar src-window .Op Fl t Ar dst-window .Xc .D1 (alias: Ic swapw ) This is similar to .Ic link-window , except the source and destination windows are swapped. It is an error if no window exists at .Ar src-window . .It Xo Ic unlink-window .Op Fl k .Op Fl t Ar target-window .Xc .D1 (alias: Ic unlinkw ) Unlink .Ar target-window . Unless .Fl k is given, a window may be unlinked only if it is linked to multiple sessions - windows may not be linked to no sessions; if .Fl k is specified and the window is linked to only one session, it is unlinked and destroyed. .El .Sh KEY BINDINGS .Nm allows a command to be bound to most keys, with or without a prefix key. When specifying keys, most represent themselves (for example .Ql A to .Ql Z ) . Ctrl keys may be prefixed with .Ql C- or .Ql ^ , and Alt (meta) with .Ql M- . In addition, the following special key names are accepted: .Em Up , .Em Down , .Em Left , .Em Right , .Em BSpace , .Em BTab , .Em DC (Delete), .Em End , .Em Enter , .Em Escape , .Em F1 to .Em F20 , .Em Home , .Em IC (Insert), .Em NPage/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 cnr .Op Fl t Ar key-table .Ar key Ar command Op Ar arguments .Xc .D1 (alias: Ic bind ) Bind key .Ar key to .Ar command . By default (without .Fl t ) the primary key bindings are modified (those normally activated with the prefix key); in this case, if .Fl n is specified, it is not necessary to use the prefix key, .Ar command is bound to .Ar key alone. The .Fl r flag indicates this key may repeat, see the .Ic repeat-time option. .Pp If .Fl t is present, .Ar key is bound in .Ar key-table : the binding for command mode with .Fl c or for normal mode without. To view the default bindings and possible commands, see the .Ic list-keys command. .It Ic list-keys Op Fl t Ar key-table .D1 (alias: Ic lsk ) List all key bindings. Without .Fl t the primary key bindings - those executed when preceded by the prefix key - are printed. .Pp With .Fl t , the key bindings in .Ar key-table are listed; this may be one of: .Em vi-edit , .Em emacs-edit , .Em vi-choice , .Em emacs-choice , .Em vi-copy or .Em emacs-copy . .It Xo Ic send-keys .Op Fl lR .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. The .Fl l flag disables key name lookup and sends the keys literally. All arguments are sent sequentially from first to last. The .Fl R flag causes the terminal state to be reset. .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 acn .Op Fl t Ar key-table .Ar key .Xc .D1 (alias: Ic unbind ) Unbind the command bound to .Ar key . Without .Fl t the primary key bindings are modified; in this case, if .Fl n is specified, the command bound to .Ar key without a prefix (if any) is removed. If .Fl a is present, all key bindings are removed. .Pp If .Fl t is present, .Ar key in .Ar key-table is unbound: the binding for command mode with .Fl c or for normal mode without. .El .Sh OPTIONS The appearance and behaviour of .Nm may be modified by changing the value of various options. There are three types of option: .Em server options , .Em session options and .Em window options . .Pp The .Nm server has a set of global options which do not apply to any particular window or session. These are altered with the .Ic set-option .Fl s command, or displayed with the .Ic show-options .Fl s command. .Pp In addition, each individual session may have a set of session options, and there is a separate set of global session options. Sessions which do not have a particular option configured inherit the value from the global session options. Session options are set or unset with the .Ic set-option command and may be listed with the .Ic show-options command. The available server and session options are listed under the .Ic set-option command. .Pp Similarly, a set of window options is attached to each window, and there is a set of global window options from which any unset options are inherited. Window options are altered with the .Ic set-window-option command and can be listed with the .Ic show-window-options command. All window options are documented with the .Ic set-window-option command. .Pp .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 agoqsuw .Op Fl t Ar target-session | Ar target-window .Ar option Ar value .Xc .D1 (alias: Ic set ) Set a window option with .Fl w (equivalent to the .Ic set-window-option command), a server option with .Fl s , otherwise a session option. .Pp If .Fl g is specified, the global session or window option is set. With .Fl a , and if the option expects a string, .Ar value is appended to the existing setting. The .Fl u flag unsets an option, so a session inherits the option from the global options. It is not possible to unset a global option. .Pp The .Fl o flag prevents setting an option that is already set. .Pp The .Fl q flag suppresses the informational message (as if the .Ic quiet server option was set). .Pp Available window options are listed under .Ic set-window-option . .Pp .Ar value depends on the option and may be a number, a string, or a flag (on, off, or omitted to toggle). .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 Ic escape-time Ar time Set the time in milliseconds for which .Nm waits after an escape is input to determine if it is part of a function or meta key sequences. The default is 500 milliseconds. .It Xo Ic exit-unattached .Op Ic on | off .Xc If enabled, the server will exit when there are no attached clients. .It Xo Ic quiet .Op Ic on | off .Xc Enable or disable the display of various informational messages (see also the .Fl q command line flag). .It Xo Ic set-clipboard .Op Ic on | off .Xc Attempt to set the terminal clipboard content using the \ee]52;...\e007 .Xr xterm 1 escape sequences. This option is on by default if there is an .Em \&Ms entry in the .Xr terminfo 5 description for the client terminal. Note that this feature needs to be enabled in .Xr xterm 1 by setting the resource: .Bd -literal -offset indent disallowedWindowOps: 20,21,SetXprop .Ed .Pp Or changing this property from the .Xr xterm 1 interactive menu when required. .El .Pp Available session options are: .Bl -tag -width Ds .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 .Xc Set action on window bell. .Ic any means a bell in any window linked to a session causes a bell in the current window of that session, .Ic none means all bells are ignored and .Ic current means only bells in windows other than the current window are ignored. .It Xo Ic bell-on-alert .Op Ic on | off .Xc If on, ring the terminal bell when an alert occurs. .It Ic default-command Ar shell-command Set the command used for new windows (if not specified when the window is created) to .Ar shell-command , which may be any .Xr sh 1 command. The default is an empty string, which instructs .Nm to create a login shell using the value of the .Ic default-shell option. .It Ic default-path Ar path Set the default working directory for new panes. If empty (the default), the working directory is determined from the process running in the active pane, from the command line environment or from the working directory where the session was created. Otherwise the same options are available as for the .Fl c flag to .Ic new-window . .It Ic default-shell Ar path Specify the default shell. This is used as the login shell for new windows when the .Ic default-command option is set to empty, and must be the full path of the executable. When started .Nm tries to set a default value from the first suitable of the .Ev SHELL environment variable, the shell returned by .Xr getpwuid 3 , or .Pa /bin/sh . This option should be configured when .Nm is used as a login shell. .It Ic default-terminal Ar terminal Set the default terminal for new windows created in this session - the default value of the .Ev TERM environment variable. For .Nm to work correctly, this .Em must be set to .Ql screen or a derivative of it. .It Xo Ic destroy-unattached .Op Ic on | off .Xc If enabled and the session is no longer attached to any clients, it is destroyed. .It Xo Ic detach-on-destroy .Op Ic on | off .Xc If on (the default), the client is detached when the session it is attached to is destroyed. If off, the client is switched to the most recently active of the remaining sessions. .It Ic display-panes-active-colour Ar colour Set the colour used by the .Ic display-panes command to show the indicator for the active pane. .It Ic display-panes-colour Ar colour Set the colour used by the .Ic display-panes command to show the indicators for inactive panes. .It Ic display-panes-time Ar time Set the time in milliseconds for which the indicators shown by the .Ic display-panes command appear. .It Ic display-time Ar time Set the amount of time for which status line messages and other on-screen indicators are displayed. .Ar time is in milliseconds. .It Ic history-limit Ar lines Set the maximum number of lines held in window history. This setting applies only to new windows - existing window histories are not resized and retain the limit at the point they were created. .It Ic lock-after-time Ar number Lock the session (like the .Ic lock-session command) after .Ar number seconds of inactivity, or the entire server (all sessions) if the .Ic lock-server option is set. The default is not to lock (set to 0). .It Ic lock-command Ar shell-command Command to run when locking each client. The default is to run .Xr lock 1 with .Fl np . .It Xo Ic lock-server .Op Ic on | off .Xc If this option is .Ic on (the default), instead of each session locking individually as each has been idle for .Ic lock-after-time , the entire server will lock after .Em all sessions would have locked. This has no effect as a session option; it must be set as a global option. .It Ic message-attr Ar attributes Set status line message attributes, where .Ar attributes is either .Ic none or a comma-delimited list of one or more of: .Ic bright (or .Ic bold ) , .Ic dim , .Ic underscore , .Ic blink , .Ic reverse , .Ic hidden , or .Ic italics . .It Ic message-bg Ar colour Set status line message background colour, where .Ar colour is one of: .Ic black , .Ic red , .Ic green , .Ic yellow , .Ic blue , .Ic magenta , .Ic cyan , .Ic white , aixterm bright variants (if supported: .Ic brightred , .Ic brightgreen , and so on), .Ic colour0 to .Ic colour255 from the 256-colour set, .Ic default , or a hexadecimal RGB string such as .Ql #ffffff , which chooses the closest match from the default 256-colour set. .It Ic message-command-attr Ar attributes Set status line message attributes when in command mode. .It Ic message-command-bg Ar colour Set status line message background colour when in command mode. .It Ic message-command-fg Ar colour Set status line message foreground colour when in command mode. .It Ic message-fg Ar colour Set status line message foreground colour. .It Ic message-limit Ar number Set the number of error or information messages to save in the message log for each client. The default is 20. .It Xo Ic mouse-resize-pane .Op Ic on | off .Xc If on, .Nm captures the mouse and allows panes to be resized by dragging on their borders. .It Xo Ic mouse-select-pane .Op Ic on | off .Xc If on, .Nm captures the mouse and when a window is split into multiple panes the mouse may be used to select the current pane. The mouse click is also passed through to the application as normal. .It Xo Ic mouse-select-window .Op Ic on | off .Xc If on, clicking the mouse on a window name in the status line will select that window. .It Xo Ic mouse-utf8 .Op Ic on | off .Xc If enabled, request mouse input as UTF-8 on UTF-8 terminals. .It Ic pane-active-border-bg Ar colour .It Ic pane-active-border-fg Ar colour Set the pane border colour for the currently active pane. .It Ic pane-border-bg Ar colour .It Ic pane-border-fg Ar colour Set the pane border colour for panes aside from the active pane. .It Ic prefix Ar key Set the key accepted as a prefix key. .It Ic prefix2 Ar key Set a secondary key accepted as a prefix key. .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-remain-on-exit .Op Ic on | off .Xc Set the .Ic remain-on-exit window option for any windows first created in this session. When this option is true, windows in which the running program has exited do not close, instead remaining open but inactivate. Use the .Ic respawn-window command to reactivate such a window, or the .Ic kill-window command to destroy it. .It Xo Ic set-titles .Op Ic on | off .Xc Attempt to set the client terminal title using the .Em tsl and .Em fsl .Xr terminfo 5 entries if they exist. .Nm automatically sets these to the \ee]2;...\e007 sequence if the terminal appears to be an xterm. This option is off by default. Note that elinks will only attempt to set the window title if the STY environment variable is set. .It Ic set-titles-string Ar string String used to set the window title if .Ic set-titles is on. Character sequences are replaced as for the .Ic status-left option. .It Xo Ic status .Op Ic on | off .Xc Show or hide the status line. .It Ic status-attr Ar attributes Set status line attributes. .It Ic status-bg Ar colour Set status line background colour. .It Ic status-fg Ar colour Set status line foreground colour. .It Ic status-interval Ar interval Update the status bar every .Ar interval seconds. By default, updates will occur every 15 seconds. A setting of zero disables redrawing at interval. .It Xo Ic status-justify .Op Ic left | centre | right .Xc Set the position of the window list component of the status line: left, centre or right justified. .It Xo Ic status-keys .Op Ic vi | emacs .Xc Use vi or emacs-style key bindings in the status line, for example at the command prompt. The default is emacs, unless the .Ev VISUAL or .Ev EDITOR environment variables are set and contain the string .Ql vi . .It Ic status-left Ar string Display .Ar string to the left of the status bar. .Ar string will be passed through .Xr strftime 3 before being used. By default, the session name is shown. .Ar string may contain any of the following special character sequences: .Bl -column "Character pair" "Replaced with" -offset indent .It Sy "Character pair" Ta Sy "Replaced with" .It Li "#(shell-command)" Ta "First line of the command's output" .It Li "#[attributes]" Ta "Colour or attribute change" .It Li "#H" Ta "Hostname of local host" .It Li "#h" Ta "Hostname of local host without the domain name" .It Li "#F" Ta "Current window flag" .It Li "#I" Ta "Current window index" .It Li "#D" Ta "Current pane unique identifier" .It Li "#P" Ta "Current pane index" .It Li "#S" Ta "Session name" .It Li "#T" Ta "Current pane title" .It Li "#W" Ta "Current window name" .It Li "##" Ta "A literal" Ql # .El .Pp The #(shell-command) form executes .Ql shell-command and inserts the first line of its output. Note that shell commands are only executed once at the interval specified by the .Ic status-interval option: if the status line is redrawn in the meantime, the previous result is used. Shell commands are executed with the .Nm global environment set (see the .Sx ENVIRONMENT section). .Pp For details on how the names and titles can be set see the .Sx "NAMES AND TITLES" section. .Pp #[attributes] allows a comma-separated list of attributes to be specified, these may be .Ql fg=colour to set the foreground colour, .Ql bg=colour to set the background colour, the name of one of the attributes (listed under the .Ic message-attr option) to turn an attribute on, or an attribute prefixed with .Ql no to turn one off, for example .Ic nobright . Examples are: .Bd -literal -offset indent #(sysctl vm.loadavg) #[fg=yellow,bold]#(apm -l)%%#[default] [#S] .Ed .Pp Where appropriate, special character sequences may be prefixed with a number to specify the maximum length, for example .Ql #24T . .Pp By default, UTF-8 in .Ar string is not interpreted, to enable UTF-8, use the .Ic status-utf8 option. .It Ic status-left-attr Ar attributes Set the attribute of the left part of the status line. .It Ic status-left-bg Ar colour Set the background colour of the left part of the status line. .It Ic status-left-fg Ar colour Set the foreground colour of the left part of the status line. .It Ic status-left-length Ar length Set the maximum .Ar length of the left component of the status bar. The default is 10. .It 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 bar. By default, the current window title in double quotes, the date and the time are shown. As with .Ic status-left , .Ar string will be passed to .Xr strftime 3 , character pairs are replaced, and UTF-8 is dependent on the .Ic status-utf8 option. .It Ic status-right-attr Ar attributes Set the attribute of the right part of the status line. .It Ic status-right-bg Ar colour Set the background colour of the right part of the status line. .It Ic status-right-fg Ar colour Set the foreground colour of the right part of the status line. .It Ic status-right-length Ar length Set the maximum .Ar length of the right component of the status bar. The default is 40. .It Xo Ic status-utf8 .Op Ic on | off .Xc Instruct .Nm to treat top-bit-set characters in the .Ic status-left and .Ic status-right strings as UTF-8; notably, this is important for wide characters. This option defaults to off. .It Ic terminal-overrides Ar string Contains a list of entries which override terminal descriptions read using .Xr terminfo 5 . .Ar string is a comma-separated list of items each a colon-separated string made up of a terminal type pattern (matched using .Xr fnmatch 3 ) and a set of .Em name=value entries. .Pp For example, to set the .Ql clear .Xr terminfo 5 entry to .Ql \ee[H\ee[2J for all terminal types and the .Ql dch1 entry to .Ql \ee[P for the .Ql rxvt terminal type, the option could be set to the string: .Bd -literal -offset indent "*:clear=\ee[H\ee[2J,rxvt:dch1=\ee[P" .Ed .Pp The terminal entry value is passed through .Xr strunvis 3 before interpretation. The default value forcibly corrects the .Ql colors entry for terminals which support 88 or 256 colours: .Bd -literal -offset indent "*88col*:colors=88,*256col*:colors=256,xterm*:XT" .Ed .It Ic update-environment Ar variables Set a space-separated string containing a list of environment variables to be copied into the session environment when a new session is created or an existing session is attached. Any variables that do not exist in the source environment are set to be removed from the session environment (as if .Fl r was given to the .Ic set-environment command). The default is "DISPLAY SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID SSH_CONNECTION WINDOWID XAUTHORITY". .It Xo Ic visual-activity .Op Ic on | off .Xc If on, display a status line message when activity occurs in a window for which the .Ic monitor-activity window option is enabled. .It Xo Ic visual-bell .Op Ic on | off .Xc If this option is on, a message is shown on a bell instead of it being passed through to the terminal (which normally makes a sound). Also see the .Ic bell-action option. .It Xo Ic visual-content .Op Ic on | off .Xc Like .Ic visual-activity , display a message when content is present in a window for which the .Ic monitor-content window option is enabled. .It Xo Ic visual-silence .Op Ic on | off .Xc If .Ic monitor-silence is enabled, prints a message after the interval has expired on a given window. .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 .It Xo Ic set-window-option .Op Fl agqu .Op Fl t Ar target-window .Ar option Ar value .Xc .D1 (alias: Ic setw ) Set a window option. The .Fl a , .Fl g , .Fl q and .Fl u flags work similarly to the .Ic set-option command. .Pp Supported window options are: .Pp .Bl -tag -width Ds -compact .It Xo Ic aggressive-resize .Op Ic on | off .Xc Aggressively resize the chosen window. This means that .Nm will resize the window to the size of the smallest session for which it is the current window, rather than the smallest session to which it is attached. The window may resize when the current window is changed on another sessions; this option is good for full-screen programs which support .Dv SIGWINCH and poor for interactive programs such as shells. .Pp .It Xo Ic allow-rename .Op Ic on | off .Xc Allow programs to change the window name using a terminal escape sequence (\\033k...\\033\\\\). The default is on. .Pp .It Xo Ic alternate-screen .Op Ic on | off .Xc This option configures whether programs running inside .Nm may use the terminal alternate screen feature, which allows the .Em smcup and .Em rmcup .Xr terminfo 5 capabilities. The alternate screen feature preserves the contents of the window when an interactive application starts and restores it on exit, so that any output visible before the application starts reappears unchanged after it exits. The default is on. .Pp .It Xo Ic automatic-rename .Op Ic on | off .Xc Control automatic window renaming. When this setting is enabled, .Nm will attempt - on supported platforms - to rename the window to reflect the command currently running in it. This flag is automatically disabled for an individual window when a name is specified at creation with .Ic new-window or .Ic new-session , or later with .Ic rename-window , or with a terminal escape sequence. It may be switched off globally with: .Bd -literal -offset indent set-window-option -g automatic-rename off .Ed .Pp .It Ic c0-change-interval Ar interval .It Ic c0-change-trigger Ar trigger These two options configure a simple form of rate limiting for a pane. If .Nm sees more than .Ar trigger C0 sequences that modify the screen (for example, carriage returns, linefeeds or backspaces) in one millisecond, it will stop updating the pane immediately and instead redraw it entirely every .Ar interval milliseconds. This helps to prevent fast output (such as .Xr yes 1 overwhelming the terminal). The default is a trigger of 250 and an interval of 100. A trigger of zero disables the rate limiting. .Pp .It Ic clock-mode-colour Ar colour Set clock colour. .Pp .It Xo Ic clock-mode-style .Op Ic 12 | 24 .Xc Set clock hour format. .Pp .It Ic force-height Ar height .It Ic force-width Ar width Prevent .Nm from resizing a window to greater than .Ar width or .Ar height . A value of zero restores the default unlimited setting. .Pp .It Ic main-pane-height Ar height .It Ic main-pane-width Ar width Set the width or height of the main (left or top) pane in the .Ic main-horizontal or .Ic main-vertical layouts. .Pp .It Ic mode-attr Ar attributes Set window modes attributes. .Pp .It Ic mode-bg Ar colour Set window modes background colour. .Pp .It Ic mode-fg Ar colour Set window modes foreground colour. .Pp .It Xo Ic mode-keys .Op Ic vi | emacs .Xc Use vi or emacs-style key bindings in copy and choice modes. As with the .Ic status-keys option, the default is emacs, unless .Ev VISUAL or .Ev EDITOR contains .Ql vi . .Pp .It Xo Ic mode-mouse .Op Ic on | off | copy-mode .Xc Mouse state in modes. If on, the mouse may be used to enter copy mode and copy a selection by dragging, to enter copy mode and scroll with the mouse wheel, or to select an option in choice mode. If set to .Em copy-mode , the mouse behaves as set to on, but cannot be used to enter copy mode. .Pp .It Xo Ic monitor-activity .Op Ic on | off .Xc Monitor for activity in the window. Windows with activity are highlighted in the status line. .Pp .It Ic monitor-content Ar match-string Monitor content in the window. When .Xr fnmatch 3 pattern .Ar match-string appears in the window, it is highlighted in the status line. .Pp .It Xo Ic monitor-silence .Op Ic interval .Xc Monitor for silence (no activity) in the window within .Ic interval seconds. Windows that have been silent for the interval are highlighted in the status line. An interval of zero disables the monitoring. .Pp .It Ic other-pane-height Ar height Set the height of the other panes (not the main pane) in the .Ic main-horizontal layout. If this option is set to 0 (the default), it will have no effect. If both the .Ic main-pane-height and .Ic other-pane-height options are set, the main pane will grow taller to make the other panes the specified height, but will never shrink to do so. .Pp .It Ic other-pane-width Ar width Like .Ic other-pane-height , but set the width of other panes in the .Ic main-vertical layout. .Pp .It Ic pane-base-index Ar index Like .Ic base-index , but set the starting index for pane numbers. .Pp .It Xo Ic remain-on-exit .Op Ic on | off .Xc A window with this flag set is not destroyed when the program running in it exits. The window may be reactivated with the .Ic respawn-window command. .Pp .It Xo Ic synchronize-panes .Op Ic on | off .Xc Duplicate input to any pane to all other panes in the same window (only for panes that are not in any special mode). .Pp .It Xo Ic utf8 .Op Ic on | off .Xc Instructs .Nm to expect UTF-8 sequences to appear in this window. .Pp .It Ic window-status-bell-attr Ar attributes Set status line attributes for windows which have a bell alert. .Pp .It Ic window-status-bell-bg Ar colour Set status line background colour for windows with a bell alert. .Pp .It Ic window-status-bell-fg Ar colour Set status line foreground colour for windows with a bell alert. .Pp .It Ic window-status-content-attr Ar attributes Set status line attributes for windows which have a content alert. .Pp .It Ic window-status-content-bg Ar colour Set status line background colour for windows with a content alert. .Pp .It Ic window-status-content-fg Ar colour Set status line foreground colour for windows with a content alert. .Pp .It Ic window-status-activity-attr Ar attributes Set status line attributes for windows which have an activity (or silence) alert. .Pp .It Ic window-status-activity-bg Ar colour Set status line background colour for windows with an activity alert. .Pp .It Ic window-status-activity-fg Ar colour Set status line foreground colour for windows with an activity alert. .Pp .It Ic window-status-attr Ar attributes Set status line attributes for a single window. .Pp .It Ic window-status-bg Ar colour Set status line background colour for a single window. .Pp .It Ic window-status-current-attr Ar attributes Set status line attributes for the currently active window. .Pp .It Ic window-status-current-bg Ar colour Set status line background colour for the currently active window. .Pp .It Ic window-status-current-fg Ar colour Set status line foreground colour for the currently active window. .Pp .It Ic window-status-current-format Ar string Like .Ar window-status-format , but is the format used when the window is the current window. .Pp .It Ic window-status-last-attr Ar attributes Set status line attributes for the last active window. .Pp .It Ic window-status-last-bg Ar colour Set status line background colour for the last active window. .Pp .It Ic window-status-last-fg Ar colour Set status line foreground colour for the last active window. .Pp .It Ic window-status-fg Ar colour Set status line foreground colour for a single window. .Pp .It Ic window-status-format Ar string Set the format in which the window is displayed in the status line window list. See the .Ar status-left option for details of special character sequences available. The default is .Ql #I:#W#F . .Pp .It Ic window-status-separator Ar string Sets the separator drawn between windows in the status line. The default is a single space character. .Pp .It Xo Ic xterm-keys .Op Ic on | off .Xc If this option is set, .Nm will generate .Xr xterm 1 -style function key sequences; these have a number included to indicate modifiers such as Shift, Alt or Ctrl. The default is off. .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. .El .It Xo Ic show-options .Op Fl gqsvw .Op Fl t Ar target-session | Ar target-window .Op Ar option .Xc .D1 (alias: Ic show ) Show the window options (or a single window option if given) with .Fl w (equivalent to .Ic show-window-options ) , the server options with .Fl s , otherwise the session options for .Ar target session . Global session or window options are listed if .Fl g is used. .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. .It Xo Ic show-window-options .Op Fl gv .Op Fl t Ar target-window .Op Ar option .Xc .D1 (alias: Ic showw ) List the window options or a single option for .Ar target-window , or the global window options if .Fl g is used. .Fl v shows only the option value, not the name. .El .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. Special character sequences are replaced as documented under the .Ic status-left option and an additional long form is accepted. Replacement variables are enclosed in .Ql #{ and .Ql } , for example .Ql #{session_name} is equivalent to .Ql #S . Conditionals are also accepted 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. .Pp The following variables are available, where appropriate: .Bl -column "session_created_string" "Replaced with" -offset indent .It Sy "Variable name" Ta Sy "Replaced with" .It Li "alternate_on" Ta "If pane is in alternate screen" .It Li "alternate_saved_x" Ta "Saved cursor X in alternate screen" .It Li "alternate_saved_y" Ta "Saved cursor Y in alternate screen" .It Li "buffer_sample" Ta "First 50 characters from the specified buffer" .It Li "buffer_size" Ta "Size of the specified buffer in bytes" .It Li "client_activity" Ta "Integer time client last had activity" .It Li "client_activity_string" Ta "String time client last had activity" .It Li "client_created" Ta "Integer time client created" .It Li "client_created_string" Ta "String time client created" .It Li "client_cwd" Ta "Working directory of client" .It Li "client_height" Ta "Height of client" .It Li "client_last_session" Ta "Name of the client's last session" .It Li "client_prefix" Ta "1 if prefix key has been pressed" .It Li "client_readonly" Ta "1 if client is readonly" .It Li "client_session" Ta "Name of the client's session" .It Li "client_termname" Ta "Terminal name of client" .It Li "client_tty" Ta "Pseudo terminal of client" .It Li "client_utf8" Ta "1 if client supports utf8" .It Li "client_width" Ta "Width of client" .It Li "cursor_flag" Ta "Pane cursor flag" .It Li "cursor_x" Ta "Cursor X position in pane" .It Li "cursor_y" Ta "Cursor Y position in pane" .It Li "history_bytes" Ta "Number of bytes in window history" .It Li "history_limit" Ta "Maximum window history lines" .It Li "history_size" Ta "Size of history in bytes" .It Li "host" Ta "Hostname of local host" .It Li "insert_flag" Ta "Pane insert flag" .It Li "keypad_cursor_flag" Ta "Pane keypad cursor flag" .It Li "keypad_flag" Ta "Pane keypad flag" .It Li "line" Ta "Line number in the list" .It Li "mouse_any_flag" Ta "Pane mouse any flag" .It Li "mouse_button_flag" Ta "Pane mouse button flag" .It Li "mouse_standard_flag" Ta "Pane mouse standard flag" .It Li "mouse_utf8_flag" Ta "Pane mouse UTF-8 flag" .It Li "pane_active" Ta "1 if active pane" .It Li "pane_current_command" Ta "Current command if available" .It Li "pane_current_path" Ta "Current path if available" .It Li "pane_dead" Ta "1 if pane is dead" .It Li "pane_height" Ta "Height of pane" .It Li "pane_id" Ta "Unique pane ID" .It Li "pane_in_mode" Ta "If pane is in a mode" .It Li "pane_index" Ta "Index of pane" .It Li "pane_pid" Ta "PID of first process in pane" .It Li "pane_start_command" Ta "Command pane started with" .It Li "pane_start_path" Ta "Path pane started with" .It Li "pane_tabs" Ta "Pane tab positions" .It Li "pane_title" Ta "Title of pane" .It Li "pane_tty" Ta "Pseudo terminal of pane" .It Li "pane_width" Ta "Width of pane" .It Li "saved_cursor_x" Ta "Saved cursor X in pane" .It Li "saved_cursor_y" Ta "Saved cursor Y in pane" .It Li "scroll_region_lower" Ta "Bottom of scroll region in pane" .It Li "scroll_region_upper" Ta "Top of scroll region in pane" .It Li "session_attached" Ta "1 if session attached" .It Li "session_created" Ta "Integer time session created" .It Li "session_created_string" Ta "String time session created" .It Li "session_group" Ta "Number of session group" .It Li "session_grouped" Ta "1 if session in a group" .It Li "session_height" Ta "Height of session" .It Li "session_id" Ta "Unique session ID" .It Li "session_name" Ta "Name of session" .It Li "session_width" Ta "Width of session" .It Li "session_windows" Ta "Number of windows in session" .It Li "window_active" Ta "1 if window active" .It Li "window_find_matches" Ta "Matched data from the find-window command if available" .It Li "window_flags" Ta "Window flags" .It Li "window_height" Ta "Height of window" .It Li "window_id" Ta "Unique window ID" .It Li "window_index" Ta "Index of window" .It Li "window_layout" Ta "Window layout description" .It Li "window_name" Ta "Name of window" .It Li "window_panes" Ta "Number of panes in window" .It Li "window_width" Ta "Width of window" .It Li "wrap_flag" Ta "Pane wrap flag" .El .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 and is not modified by .Nm . It is the same mechanism used to set for example the .Xr xterm 1 window title in an .Xr X 7 window manager. 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: .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 OSC title setting sequence, for example: .Bd -literal -offset indent $ printf '\e033]2;My Title\e033\e\e' .Ed .Sh ENVIRONMENT When the server is started, .Nm copies the environment into the .Em global environment ; in addition, each session has a .Em session environment . When a window is created, the session and global environments are merged. If a variable exists in both, the value from the session environment is used. The result is the initial environment passed to the new process. .Pp The .Ic update-environment session option may be used to update the session environment from the client when a new session is created or an old reattached. .Nm also initialises the .Ev TMUX variable with some internal information to allow commands to be executed from inside, and the .Ev TERM variable with the correct terminal setting of .Ql screen . .Pp Commands to alter and view the environment are: .Bl -tag -width Ds .It Xo Ic set-environment .Op Fl gru .Op Fl t Ar target-session .Ar name Op Ar value .Xc .D1 (alias: Ic setenv ) Set or unset an environment variable. If .Fl g is used, the change is made in the global environment; otherwise, it is applied to the session environment for .Ar target-session . The .Fl u flag unsets a variable. .Fl r indicates the variable is to be removed from the environment before starting a new process. .It Xo Ic show-environment .Op Fl g .Op Fl t Ar target-session .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 - . .El .Sh STATUS LINE .Nm includes an optional status line which is displayed in the bottom line of each terminal. By default, the status line is enabled (it may be disabled with the .Ic status session option) and contains, from left-to-right: the name of the current session in square brackets; the window list; the title of the active pane in double quotes; and the time and date. .Pp The status line is made of three parts: configurable left and right sections (which may contain dynamic content such as the time or output from a shell command, see the .Ic status-left , .Ic status-left-length , .Ic status-right , and .Ic status-right-length options below), and a central window list. By default, the window list shows the index, name and (if any) flag of the windows present in the current session in ascending numerical order. It may be customised with the .Ar window-status-format and .Ar window-status-current-format options. The flag is one of the following symbols appended to the window name: .Bl -column "Symbol" "Meaning" -offset indent .It Sy "Symbol" Ta Sy "Meaning" .It Li "*" Ta "Denotes the current window." .It Li "-" Ta "Marks the last window (previously selected)." .It Li "#" Ta "Window is monitored and activity has been detected." .It Li "!" Ta "A bell has occurred in the window." .It Li "+" Ta "Window is monitored for content and it has appeared." .It Li "~" Ta "The window has been silent for the monitor-silence interval." .It Li "Z" Ta "The window's active pane is zoomed." .El .Pp The # symbol relates to the .Ic monitor-activity and + to the .Ic monitor-content window options. The window name is printed in inverted colours if an alert (bell, activity or content) is present. .Pp The colour and attributes of the status line may be configured, the entire status line using the .Ic status-attr , .Ic status-fg and .Ic status-bg session options and individual windows using the .Ic window-status-attr , .Ic window-status-fg and .Ic window-status-bg window options. .Pp The status line is automatically refreshed at interval if it has changed, the interval may be controlled with the .Ic status-interval session option. .Pp Commands related to the status line are as follows: .Bl -tag -width Ds .It Xo Ic command-prompt .Op Fl I Ar inputs .Op Fl p Ar prompts .Op Fl t Ar target-client .Op Ar template .Xc Open the command prompt in a client. This may be used from inside .Nm to execute commands interactively. .Pp If .Ar template is specified, it is used as the command. If present, .Fl I is a comma-separated list of the initial text for each prompt. If .Fl p is given, .Ar prompts is a comma-separated list of prompts which are displayed in order; otherwise a single prompt is displayed, constructed from .Ar template if it is present, or .Ql \&: if not. .Pp Both .Ar inputs and .Ar prompts may contain the special character sequences supported by the .Ic status-left option. .Pp Before the command is executed, the first occurrence of the string .Ql %% and all occurrences of .Ql %1 are replaced by the response to the first prompt, the second .Ql %% and all .Ql %2 are replaced with the response to the second prompt, and so on for further prompts. Up to nine prompt responses may be replaced .Po .Ql %1 to .Ql %9 .Pc . .It Xo Ic confirm-before .Op Fl p Ar prompt .Op Fl t Ar target-client .Ar command .Xc .D1 (alias: Ic confirm ) Ask for confirmation before executing .Ar command . If .Fl p is given, .Ar prompt is the prompt to display; otherwise a prompt is constructed from .Ar command . It may contain the special character sequences supported by the .Ic status-left option. .Pp This command works only from inside .Nm . .It Xo Ic display-message .Op Fl p .Op Fl c Ar target-client .Op Fl t Ar target-pane .Op Ar message .Xc .D1 (alias: Ic display ) Display a message. If .Fl p is given, the output is printed to stdout, otherwise it is displayed in the .Ar target-client status line. The format of .Ar message is described in the .Sx FORMATS section; information is taken from .Ar target-pane if .Fl t is given, otherwise the active pane for the session attached to .Ar target-client . .El .Sh BUFFERS .Nm maintains a stack of .Em paste buffers . Up to the value of the .Ic buffer-limit option are kept; when a new buffer is added, the buffer at the bottom of the stack is removed. Buffers may be added using .Ic copy-mode or the .Ic set-buffer command, and pasted into a window using the .Ic paste-buffer command. .Pp A configurable history buffer is also maintained for each window. By default, up to 2000 lines are kept; this can be altered with the .Ic history-limit option (see the .Ic set-option command above). .Pp The buffer commands are as follows: .Bl -tag -width Ds .It Xo .Ic choose-buffer .Op Fl F Ar format .Op Fl t Ar target-window .Op Ar template .Xc Put a window into buffer choice mode, where a buffer may be chosen interactively from a list. After a buffer is selected, .Ql %% is replaced by the buffer index in .Ar template and the result executed as a command. If .Ar template is not given, "paste-buffer -b '%%'" is used. For the meaning of the .Fl F flag, see the .Sx FORMATS section. 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-index .D1 (alias: Ic deleteb ) Delete the buffer at .Ar buffer-index , or the top 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-index .Ar path .Xc .D1 (alias: Ic loadb ) Load the contents of the specified paste buffer from .Ar path . .It Xo Ic paste-buffer .Op Fl dpr .Op Fl b Ar buffer-index .Op Fl s Ar separator .Op Fl t Ar target-pane .Xc .D1 (alias: Ic pasteb ) Insert the contents of a paste buffer into the specified pane. If not specified, paste into the current one. With .Fl d , also delete the paste buffer from the stack. When output, any linefeed (LF) characters in the paste buffer are replaced with a separator, by default carriage return (CR). A custom separator may be specified using the .Fl s flag. The .Fl r flag means to do no replacement (equivalent to a separator of LF). 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-index .Ar path .Xc .D1 (alias: Ic saveb ) Save the contents of the specified paste buffer to .Ar path . The .Fl a option appends to rather than overwriting the file. .It Xo Ic set-buffer .Op Fl b Ar buffer-index .Ar data .Xc .D1 (alias: Ic setb ) Set the contents of the specified buffer to .Ar data . .It Xo Ic show-buffer .Op Fl b Ar buffer-index .Xc .D1 (alias: Ic showb ) Display the contents of the specified buffer. .El .Sh MISCELLANEOUS Miscellaneous commands are as follows: .Bl -tag -width Ds .It Ic clock-mode Op Fl t Ar target-pane Display a large clock. .It Xo Ic if-shell .Op Fl b .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, 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. .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 .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 Ic server-info .D1 (alias: Ic info ) Show server information and terminal details. .It Xo Ic wait-for .Fl LSU .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 . This command only works from outside .Nm . .El .Sh TERMINFO EXTENSIONS .Nm understands some extensions to .Xr terminfo 5 : .Bl -tag -width Ds .It Em Cc , Cr Set the cursor colour. The first takes a single string argument and is used to set the colour; the second takes no arguments and restores the default cursor colour. If set, a sequence such as this may be used to change the cursor colour from inside .Nm : .Bd -literal -offset indent $ printf '\e033]12;red\e033\e\e' .Ed .It Em Cs , Csr Change the cursor style. If set, a sequence such as this may be used to change the cursor to an underline: .Bd -literal -offset indent $ printf '\e033[4 q' .Ed .Pp If .Em Csr is set, it will be used to reset the cursor style instead of .Em Cs . .It Em \&Ms This sequence can be used by .Nm to store the current buffer in the host terminal's selection (clipboard). See the .Em set-clipboard option above and the .Xr xterm 1 man page. .El .Sh 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 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 %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 The layout of a window with ID .Ar window-id changed. The new layout is .Ar window-layout . .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 %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 %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-renamed Ar window-id Ar name The window with ID .Ar window-id was renamed to .Ar name . .El .Sh FILES .Bl -tag -width "/etc/tmux.confXXX" -compact .It Pa ~/.tmux.conf Default .Nm configuration file. .It Pa /etc/tmux.conf System-wide configuration file. .El .Sh EXAMPLES To create a new .Nm session running .Xr vi 1 : .Pp .Dl $ tmux new-session vi .Pp Most commands have a shorter form, known as an alias. For new-session, this is .Ic new : .Pp .Dl $ tmux new vi .Pp Alternatively, the shortest unambiguous form of a command is accepted. If there are several options, they are listed: .Bd -literal -offset indent $ tmux n ambiguous command: n, could be: new-session, new-window, next-window .Ed .Pp Within an active session, a new window may be created by typing .Ql C-b c (Ctrl followed by the .Ql b key followed by the .Ql c key). .Pp Windows may be navigated with: .Ql C-b 0 (to select window 0), .Ql C-b 1 (to select window 1), and so on; .Ql C-b n to select the next window; and .Ql C-b p to select the previous window. .Pp A session may be detached using .Ql C-b d (or by an external event such as .Xr ssh 1 disconnection) and reattached with: .Pp .Dl $ tmux attach-session .Pp Typing .Ql C-b \&? lists the current key bindings in the current window; up and down may be used to navigate the list or .Ql q to exit from it. .Pp Commands to be run when the .Nm server is started may be placed in the .Pa ~/.tmux.conf configuration file. Common examples include: .Pp Changing the default prefix key: .Bd -literal -offset indent set-option -g prefix C-a unbind-key C-b bind-key C-a send-prefix .Ed .Pp Turning the status line off, or changing its colour: .Bd -literal -offset indent set-option -g status off set-option -g status-bg blue .Ed .Pp Setting other options, such as the default command, or locking after 30 minutes of inactivity: .Bd -literal -offset indent set-option -g default-command "exec /bin/ksh" set-option -g lock-after-time 1800 .Ed .Pp Creating new key bindings: .Bd -literal -offset indent bind-key b set-option status bind-key / command-prompt "split-window 'exec man %%'" bind-key S command-prompt "new-window -n %1 'ssh %1'" .Ed .Sh SEE ALSO .Xr pty 4 .Sh AUTHORS .An Nicholas Marriott Aq nicm@users.sourceforge.net tmate-1.8.10/tmux.c000066400000000000000000000224621242461015400140610ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include "tmux.h" #if defined(DEBUG) && defined(__OpenBSD__) extern char *malloc_options; #endif struct options global_options; /* server options */ struct options global_s_options; /* session options */ struct options global_w_options; /* window options */ struct environ global_environ; struct event_base *ev_base; char *cfg_file, *tmate_cfg_file; char *shell_cmd; int debug_level; time_t start_time; char socket_path[MAXPATHLEN]; int login_shell; char *environ_path; pid_t environ_pid = -1; int environ_session_id = -1; __dead void usage(void); void parseenvironment(void); char *makesocketpath(const char *); #ifndef HAVE___PROGNAME char *__progname = (char *) "tmux"; #endif __dead void usage(void) { fprintf(stderr, "usage: %s [-28lquvV] [-c shell-command] [-f file] [-L socket-name]\n" " [-S socket-path] [command [flags]]\n", __progname); exit(1); } void logfile(const char *name) { char *path; if (debug_level > 0) { xasprintf(&path, "tmux-%s-%ld.log", name, (long) getpid()); log_open(debug_level, path); free(path); } } const char * getshell(void) { struct passwd *pw; const char *shell; shell = getenv("SHELL"); if (checkshell(shell)) return (shell); pw = getpwuid(getuid()); if (pw != NULL && checkshell(pw->pw_shell)) return (pw->pw_shell); return (_PATH_BSHELL); } int checkshell(const char *shell) { if (shell == NULL || *shell == '\0' || *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 = __progname; if (*progname == '-') progname++; if (strcmp(ptr, progname) == 0) return (1); return (0); } const char* get_full_path(const char *wd, const char *path) { static char newpath[MAXPATHLEN]; char oldpath[MAXPATHLEN]; if (getcwd(oldpath, sizeof oldpath) == NULL) return (NULL); if (chdir(wd) != 0) return (NULL); if (realpath(path, newpath) != 0) return (NULL); chdir(oldpath); return (newpath); } void parseenvironment(void) { char *env, path[256]; long pid; int id; if ((env = getenv("TMUX")) == NULL) return; if (sscanf(env, "%255[^,],%ld,%d", path, &pid, &id) != 3) return; environ_path = xstrdup(path); environ_pid = pid; environ_session_id = id; } char * makesocketpath(const char *label) { char base[MAXPATHLEN], realbase[MAXPATHLEN], *path, *s; struct stat sb; u_int uid; int do_random_label = 1; uid = getuid(); if ((s = getenv("TMPDIR")) == NULL || *s == '\0') xsnprintf(base, sizeof base, "%s/tmate-%u", _PATH_TMP, uid); else xsnprintf(base, sizeof base, "%s/tmate-%u", s, uid); if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) return (NULL); if (lstat(base, &sb) != 0) return (NULL); if (!S_ISDIR(sb.st_mode)) { errno = ENOTDIR; return (NULL); } if (sb.st_uid != uid || (sb.st_mode & (S_IRWXG|S_IRWXO)) != 0) { errno = EACCES; return (NULL); } if (realpath(base, realbase) == NULL) strlcpy(realbase, base, sizeof realbase); if (!label) { do_random_label = 1; label = "XXXXXX"; } xasprintf(&path, "%s/%s", realbase, label); if (do_random_label) mktemp(path); return (path); } void setblocking(int fd, int state) { int mode; if ((mode = fcntl(fd, F_GETFL)) != -1) { if (!state) mode |= O_NONBLOCK; else mode &= ~O_NONBLOCK; fcntl(fd, F_SETFL, mode); } } __dead void shell_exec(const char *shell, const char *shellcmd) { const char *shellname, *ptr; char *argv0; ptr = strrchr(shell, '/'); if (ptr != NULL && *(ptr + 1) != '\0') shellname = ptr + 1; else shellname = shell; if (login_shell) xasprintf(&argv0, "-%s", shellname); else xasprintf(&argv0, "%s", shellname); setenv("SHELL", shell, 1); setblocking(STDIN_FILENO, 1); setblocking(STDOUT_FILENO, 1); setblocking(STDERR_FILENO, 1); closefrom(STDERR_FILENO + 1); execl(shell, argv0, "-c", shellcmd, (char *) NULL); fatal("execl failed"); } int main(int argc, char **argv) { struct passwd *pw; char *s, *path, *label, *home, **var; int opt, flags, quiet, keys; #if defined(DEBUG) && defined(__OpenBSD__) malloc_options = (char *) "AFGJPX"; #endif flags = IDENTIFY_256COLOURS | IDENTIFY_UTF8; quiet = 0; label = path = NULL; login_shell = (**argv == '-'); while ((opt = getopt(argc, argv, "28c:Cdf:lL:qS:uUvV")) != -1) { switch (opt) { case '2': flags |= IDENTIFY_256COLOURS; flags &= ~IDENTIFY_88COLOURS; break; case '8': flags |= IDENTIFY_88COLOURS; flags &= ~IDENTIFY_256COLOURS; break; case 'c': free(shell_cmd); shell_cmd = xstrdup(optarg); break; case 'C': if (flags & IDENTIFY_CONTROL) flags |= IDENTIFY_TERMIOS; else flags |= IDENTIFY_CONTROL; break; case 'V': printf("%s %s\n", __progname, VERSION); exit(0); case 'f': free(cfg_file); cfg_file = xstrdup(optarg); break; case 'l': login_shell = 1; break; case 'L': free(label); label = xstrdup(optarg); break; case 'q': quiet = 1; break; case 'S': free(path); path = xstrdup(optarg); break; case 'u': flags |= IDENTIFY_UTF8; break; case 'v': debug_level++; break; default: usage(); } } argc -= optind; argv += optind; if (shell_cmd != NULL && argc != 0) usage(); if (!(flags & IDENTIFY_UTF8)) { /* * If the user has set whichever of LC_ALL, LC_CTYPE or LANG * exist (in that order) to contain UTF-8, it is a safe * assumption that either they are using a UTF-8 terminal, or * if not they know that output from UTF-8-capable programs may * be wrong. */ if ((s = getenv("LC_ALL")) == NULL || *s == '\0') { if ((s = getenv("LC_CTYPE")) == NULL || *s == '\0') s = getenv("LANG"); } if (s != NULL && (strcasestr(s, "UTF-8") != NULL || strcasestr(s, "UTF8") != NULL)) flags |= IDENTIFY_UTF8; } environ_init(&global_environ); for (var = environ; *var != NULL; var++) environ_put(&global_environ, *var); options_init(&global_options, NULL); options_table_populate_tree(server_options_table, &global_options); options_set_number(&global_options, "quiet", quiet); options_init(&global_s_options, NULL); options_table_populate_tree(session_options_table, &global_s_options); options_set_string(&global_s_options, "default-shell", "%s", getshell()); options_init(&global_w_options, NULL); options_table_populate_tree(window_options_table, &global_w_options); /* Enable UTF-8 if the first client is on UTF-8 terminal. */ if (flags & IDENTIFY_UTF8) { options_set_number(&global_s_options, "status-utf8", 1); options_set_number(&global_s_options, "mouse-utf8", 1); options_set_number(&global_w_options, "utf8", 1); } /* Override keys to vi if VISUAL or EDITOR are set. */ if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) { if (strrchr(s, '/') != NULL) s = strrchr(s, '/') + 1; if (strstr(s, "vi") != NULL) keys = MODEKEY_VI; else keys = MODEKEY_EMACS; options_set_number(&global_s_options, "status-keys", keys); options_set_number(&global_w_options, "mode-keys", keys); } /* Locate the configuration file. */ home = getenv("HOME"); if (home == NULL || *home == '\0') { pw = getpwuid(getuid()); if (pw != NULL) home = pw->pw_dir; } if (cfg_file == NULL) { xasprintf(&cfg_file, "%s/%s", home, DEFAULT_CFG); if (access(cfg_file, R_OK) != 0 && errno == ENOENT) { free(cfg_file); cfg_file = NULL; } } xasprintf(&tmate_cfg_file, "%s/%s", home, DEFAULT_TMATE_CFG); if (access(tmate_cfg_file, R_OK) != 0 && errno == ENOENT) { free(tmate_cfg_file); tmate_cfg_file = NULL; } /* * Figure out the socket path. If specified on the command-line with -S * or -L, use it, otherwise try $TMUX or assume -L default. */ parseenvironment(); if (path == NULL) { /* If no -L, use the environment. */ if (label == NULL) { if (environ_path != NULL) path = xstrdup(environ_path); } /* -L or default set. */ if (!path) { if ((path = makesocketpath(label)) == NULL) { fprintf(stderr, "can't create socket\n"); exit(1); } } } free(label); strlcpy(socket_path, path, sizeof socket_path); free(path); #ifdef HAVE_SETPROCTITLE /* Set process title. */ setproctitle("%s (%s)", __progname, socket_path); #endif /* Pass control to the client. */ ev_base = osdep_event_init(); exit(client_main(argc, argv, flags)); } tmate-1.8.10/tmux.h000066400000000000000000002167671242461015400141030ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef TMUX_H #define TMUX_H #define TMATE #define PROTOCOL_VERSION 7 #include #include #include #include #include #include #include #include #include #include "array.h" #include "compat.h" extern char *__progname; extern char **environ; /* Default configuration files. */ #define DEFAULT_CFG ".tmux.conf" #define DEFAULT_TMATE_CFG ".tmate.conf" #define SYSTEM_CFG "/etc/tmux.conf" /* Default prompt history length. */ #define PROMPT_HISTORY 100 /* * Minimum layout cell size, NOT including separator line. The scroll region * cannot be one line in height so this must be at least two. */ #define PANE_MINIMUM 2 /* Automatic name refresh interval, in milliseconds. */ #define NAME_INTERVAL 500 /* * Maximum sizes of strings in message data. Don't forget to bump * PROTOCOL_VERSION if any of these change! */ #define COMMAND_LENGTH 2048 /* packed argv size */ #define TERMINAL_LENGTH 128 /* length of TERM environment variable */ #define ENVIRON_LENGTH 1024 /* environment variable length */ /* * UTF-8 data size. This must be big enough to hold combined characters as well * as single. */ #define UTF8_SIZE 9 /* Fatal errors. */ #define fatal(msg) log_fatal("%s: %s", __func__, msg); #define fatalx(msg) log_fatalx("%s: %s", __func__, msg); /* Definition to shut gcc up about unused arguments. */ #define unused __attribute__ ((unused)) /* Attribute to make gcc check printf-like arguments. */ #define printflike1 __attribute__ ((format (printf, 1, 2))) #define printflike2 __attribute__ ((format (printf, 2, 3))) #define printflike3 __attribute__ ((format (printf, 3, 4))) #define printflike4 __attribute__ ((format (printf, 4, 5))) #define printflike5 __attribute__ ((format (printf, 5, 6))) /* Number of items in array. */ #ifndef nitems #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) #endif /* Default template for choose-buffer. */ #define CHOOSE_BUFFER_TEMPLATE \ "#{line}: #{buffer_size} bytes: \"#{buffer_sample}\"" /* Default template for choose-client. */ #define CHOOSE_CLIENT_TEMPLATE \ "#{client_tty}: #{session_name} " \ "[#{client_width}x#{client_height} #{client_termname}]" \ "#{?client_utf8, (utf8),}#{?client_readonly, (ro),} " \ "(last used #{client_activity_string})" /* Default templates for choose-tree. */ #define CHOOSE_TREE_SESSION_TEMPLATE \ "#{session_name}: #{session_windows} windows" \ "#{?session_grouped, (group ,}" \ "#{session_group}#{?session_grouped,),}" \ "#{?session_attached, (attached),}" #define CHOOSE_TREE_WINDOW_TEMPLATE \ "#{window_index}: #{window_name}#{window_flags} " \ "\"#{pane_title}\"" /* Default template for display-message. */ #define DISPLAY_MESSAGE_TEMPLATE \ "[#{session_name}] #{window_index}:" \ "#{window_name}, current pane #{pane_index} " \ "- (%H:%M %d-%b-%y)" /* Default template for find-window. */ #define FIND_WINDOW_TEMPLATE \ "#{window_index}: #{window_name} " \ "[#{window_width}x#{window_height}] " \ "(#{window_panes} panes) #{window_find_matches}" /* Default template for list-buffers. */ #define LIST_BUFFERS_TEMPLATE \ "#{line}: #{buffer_size} bytes: \"#{buffer_sample}\"" /* Default template for list-clients. */ #define LIST_CLIENTS_TEMPLATE \ "#{client_tty}: #{session_name} " \ "[#{client_width}x#{client_height} #{client_termname}]" \ "#{?client_utf8, (utf8),} #{?client_readonly, (ro),}" /* Default template for list-sessions. */ #define LIST_SESSIONS_TEMPLATE \ "#{session_name}: #{session_windows} windows " \ "(created #{session_created_string}) " \ "[#{session_width}x#{session_height}]" \ "#{?session_grouped, (group ,}" \ "#{session_group}#{?session_grouped,),}" \ "#{?session_attached, (attached),}" /* Default templates for list-windows. */ #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}] " /* Default templates for break-pane, new-window and split-window. */ #define BREAK_PANE_TEMPLATE "#{session_name}:#{window_index}.#{pane_index}" #define NEW_SESSION_TEMPLATE "#{session_name}:" #define NEW_WINDOW_TEMPLATE BREAK_PANE_TEMPLATE #define SPLIT_WINDOW_TEMPLATE BREAK_PANE_TEMPLATE /* Bell option values. */ #define BELL_NONE 0 #define BELL_ANY 1 #define BELL_CURRENT 2 /* Special key codes. */ #define KEYC_NONE 0xfff #define KEYC_BASE 0x1000 /* Key modifier bits. */ #define KEYC_ESCAPE 0x2000 #define KEYC_CTRL 0x4000 #define KEYC_SHIFT 0x8000 #define KEYC_PREFIX 0x10000 /* Mask to obtain key w/o modifiers. */ #define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT|KEYC_PREFIX) #define KEYC_MASK_KEY (~KEYC_MASK_MOD) /* Other key codes. */ enum key_code { /* Mouse key. */ KEYC_MOUSE = KEYC_BASE, /* Backspace key. */ KEYC_BSPACE, /* Function keys. */ KEYC_F1, KEYC_F2, KEYC_F3, KEYC_F4, KEYC_F5, KEYC_F6, KEYC_F7, KEYC_F8, KEYC_F9, KEYC_F10, KEYC_F11, KEYC_F12, KEYC_F13, KEYC_F14, KEYC_F15, KEYC_F16, KEYC_F17, KEYC_F18, KEYC_F19, KEYC_F20, KEYC_IC, KEYC_DC, KEYC_HOME, KEYC_END, KEYC_NPAGE, KEYC_PPAGE, KEYC_BTAB, /* Arrow keys. */ KEYC_UP, KEYC_DOWN, KEYC_LEFT, KEYC_RIGHT, /* Numeric keypad. */ KEYC_KP_SLASH, KEYC_KP_STAR, KEYC_KP_MINUS, KEYC_KP_SEVEN, KEYC_KP_EIGHT, KEYC_KP_NINE, KEYC_KP_PLUS, KEYC_KP_FOUR, KEYC_KP_FIVE, KEYC_KP_SIX, KEYC_KP_ONE, KEYC_KP_TWO, KEYC_KP_THREE, KEYC_KP_ENTER, KEYC_KP_ZERO, KEYC_KP_PERIOD, KEYC_FOCUS_IN, KEYC_FOCUS_OUT, }; /* Termcap codes. */ enum tty_code_code { TTYC_AX = 0, TTYC_ACSC, /* acs_chars, ac */ TTYC_BEL, /* bell, bl */ TTYC_BLINK, /* enter_blink_mode, mb */ TTYC_BOLD, /* enter_bold_mode, md */ TTYC_CC, /* set colour cursor, Cc */ TTYC_CIVIS, /* cursor_invisible, vi */ TTYC_CLEAR, /* clear_screen, cl */ TTYC_CNORM, /* cursor_normal, ve */ TTYC_COLORS, /* max_colors, Co */ TTYC_CR, /* restore cursor colour, Cr */ TTYC_CS1, /* set cursor style, Cs */ TTYC_CSR, /* change_scroll_region, cs */ TTYC_CSR1, /* reset cursor style, Csr */ TTYC_CUB, /* parm_left_cursor, LE */ TTYC_CUB1, /* cursor_left, le */ TTYC_CUD, /* parm_down_cursor, DO */ TTYC_CUD1, /* cursor_down, do */ TTYC_CUF, /* parm_right_cursor, RI */ TTYC_CUF1, /* cursor_right, nd */ TTYC_CUP, /* cursor_address, cm */ TTYC_CUU, /* parm_up_cursor, UP */ TTYC_CUU1, /* cursor_up, up */ TTYC_DCH, /* parm_dch, DC */ TTYC_DCH1, /* delete_character, dc */ TTYC_DIM, /* enter_dim_mode, mh */ TTYC_DL, /* parm_delete_line, DL */ TTYC_DL1, /* delete_line, dl */ TTYC_E3, TTYC_ECH, /* erase_chars, ec */ TTYC_EL, /* clr_eol, ce */ TTYC_EL1, /* clr_bol, cb */ TTYC_ENACS, /* ena_acs, eA */ TTYC_FSL, /* from_status_line, fsl */ TTYC_HOME, /* cursor_home, ho */ TTYC_HPA, /* column_address, ch */ TTYC_ICH, /* parm_ich, IC */ TTYC_ICH1, /* insert_character, ic */ TTYC_IL, /* parm_insert_line, IL */ TTYC_IL1, /* insert_line, il */ TTYC_INVIS, /* enter_secure_mode, mk */ TTYC_IS1, /* init_1string, i1 */ TTYC_IS2, /* init_2string, i2 */ TTYC_IS3, /* init_3string, i3 */ TTYC_KCBT, /* key_btab, kB */ TTYC_KCUB1, /* key_left, kl */ TTYC_KCUD1, /* key_down, kd */ TTYC_KCUF1, /* key_right, kr */ TTYC_KCUU1, /* key_up, ku */ TTYC_KDC2, TTYC_KDC3, TTYC_KDC4, TTYC_KDC5, TTYC_KDC6, TTYC_KDC7, TTYC_KDCH1, /* key_dc, kD */ TTYC_KDN2, TTYC_KDN3, TTYC_KDN4, TTYC_KDN5, TTYC_KDN6, TTYC_KDN7, TTYC_KEND, /* key_end, ke */ TTYC_KEND2, TTYC_KEND3, TTYC_KEND4, TTYC_KEND5, TTYC_KEND6, TTYC_KEND7, TTYC_KF1, /* key_f1, k1 */ TTYC_KF10, /* key_f10, k; */ TTYC_KF11, /* key_f11, F1 */ TTYC_KF12, /* key_f12, F2 */ TTYC_KF13, /* key_f13, F3 */ TTYC_KF14, /* key_f14, F4 */ TTYC_KF15, /* key_f15, F5 */ TTYC_KF16, /* key_f16, F6 */ TTYC_KF17, /* key_f17, F7 */ TTYC_KF18, /* key_f18, F8 */ TTYC_KF19, /* key_f19, F9 */ TTYC_KF2, /* key_f2, k2 */ TTYC_KF20, /* key_f20, F10 */ TTYC_KF3, /* key_f3, k3 */ TTYC_KF4, /* key_f4, k4 */ TTYC_KF5, /* key_f5, k5 */ TTYC_KF6, /* key_f6, k6 */ TTYC_KF7, /* key_f7, k7 */ TTYC_KF8, /* key_f8, k8 */ TTYC_KF9, /* key_f9, k9 */ TTYC_KHOM2, TTYC_KHOM3, TTYC_KHOM4, TTYC_KHOM5, TTYC_KHOM6, TTYC_KHOM7, TTYC_KHOME, /* key_home, kh */ TTYC_KIC2, TTYC_KIC3, TTYC_KIC4, TTYC_KIC5, TTYC_KIC6, TTYC_KIC7, TTYC_KICH1, /* key_ic, kI */ TTYC_KLFT2, TTYC_KLFT3, TTYC_KLFT4, TTYC_KLFT5, TTYC_KLFT6, TTYC_KLFT7, TTYC_KMOUS, /* key_mouse, Km */ TTYC_KNP, /* key_npage, kN */ TTYC_KNXT2, TTYC_KNXT3, TTYC_KNXT4, TTYC_KNXT5, TTYC_KNXT6, TTYC_KNXT7, TTYC_KPP, /* key_ppage, kP */ TTYC_KPRV2, TTYC_KPRV3, TTYC_KPRV4, TTYC_KPRV5, TTYC_KPRV6, TTYC_KPRV7, TTYC_KRIT2, TTYC_KRIT3, TTYC_KRIT4, TTYC_KRIT5, TTYC_KRIT6, TTYC_KRIT7, TTYC_KUP2, TTYC_KUP3, TTYC_KUP4, TTYC_KUP5, TTYC_KUP6, TTYC_KUP7, TTYC_MS, /* modify xterm(1) selection */ TTYC_OP, /* orig_pair, op */ TTYC_REV, /* enter_reverse_mode, mr */ TTYC_RI, /* scroll_reverse, sr */ TTYC_RMACS, /* exit_alt_charset_mode */ TTYC_RMCUP, /* exit_ca_mode, te */ TTYC_RMKX, /* keypad_local, ke */ TTYC_SETAB, /* set_a_background, AB */ TTYC_SETAF, /* set_a_foreground, AF */ TTYC_SGR0, /* exit_attribute_mode, me */ TTYC_SITM, /* enter_italics_mode, it */ TTYC_SMACS, /* enter_alt_charset_mode, as */ TTYC_SMCUP, /* enter_ca_mode, ti */ TTYC_SMKX, /* keypad_xmit, ks */ TTYC_SMSO, /* enter_standout_mode, so */ TTYC_SMUL, /* enter_underline_mode, us */ TTYC_TSL, /* to_status_line, tsl */ TTYC_VPA, /* row_address, cv */ TTYC_XENL, /* eat_newline_glitch, xn */ TTYC_XT, /* xterm(1)-compatible title, XT */ }; #define NTTYCODE (TTYC_XT + 1) /* Termcap types. */ enum tty_code_type { TTYCODE_NONE = 0, TTYCODE_STRING, TTYCODE_NUMBER, TTYCODE_FLAG, }; /* Termcap code. */ struct tty_code { enum tty_code_type type; union { char *string; int number; int flag; } value; }; /* Entry in terminal code table. */ struct tty_term_code_entry { enum tty_code_code code; enum tty_code_type type; const char *name; }; /* List of error causes. */ ARRAY_DECL(causelist, char *); /* Message codes. */ enum msgtype { MSG_COMMAND, MSG_DETACH, MSG_ERROR, MSG_EXIT, MSG_EXITED, MSG_EXITING, MSG_IDENTIFY, MSG_STDIN, MSG_READY, MSG_RESIZE, MSG_SHUTDOWN, MSG_SUSPEND, MSG_VERSION, MSG_WAKEUP, MSG_ENVIRON, MSG_UNLOCK, MSG_LOCK, MSG_SHELL, MSG_STDERR, MSG_STDOUT, MSG_DETACHKILL }; /* * Message data. * * Don't forget to bump PROTOCOL_VERSION if any of these change! */ struct msg_command_data { pid_t pid; /* from $TMUX or -1 */ int session_id; /* from $TMUX or -1 */ int argc; char argv[COMMAND_LENGTH]; }; struct msg_identify_data { char cwd[MAXPATHLEN]; char term[TERMINAL_LENGTH]; #define IDENTIFY_UTF8 0x1 #define IDENTIFY_256COLOURS 0x2 #define IDENTIFY_88COLOURS 0x4 #define IDENTIFY_CONTROL 0x8 #define IDENTIFY_TERMIOS 0x10 int flags; }; struct msg_lock_data { char cmd[COMMAND_LENGTH]; }; struct msg_environ_data { char var[ENVIRON_LENGTH]; }; struct msg_shell_data { char shell[MAXPATHLEN]; }; struct msg_exit_data { int retcode; }; 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 key commands. */ enum mode_key_cmd { MODEKEY_NONE, MODEKEY_OTHER, /* Editing keys. */ MODEKEYEDIT_BACKSPACE, MODEKEYEDIT_CANCEL, MODEKEYEDIT_COMPLETE, MODEKEYEDIT_CURSORLEFT, MODEKEYEDIT_CURSORRIGHT, MODEKEYEDIT_DELETE, MODEKEYEDIT_DELETELINE, MODEKEYEDIT_DELETETOENDOFLINE, MODEKEYEDIT_DELETEWORD, MODEKEYEDIT_ENDOFLINE, MODEKEYEDIT_ENTER, MODEKEYEDIT_HISTORYDOWN, MODEKEYEDIT_HISTORYUP, MODEKEYEDIT_NEXTSPACE, MODEKEYEDIT_NEXTSPACEEND, MODEKEYEDIT_NEXTWORD, MODEKEYEDIT_NEXTWORDEND, MODEKEYEDIT_PASTE, MODEKEYEDIT_PREVIOUSSPACE, MODEKEYEDIT_PREVIOUSWORD, MODEKEYEDIT_STARTOFLINE, MODEKEYEDIT_SWITCHMODE, MODEKEYEDIT_SWITCHMODEAPPEND, MODEKEYEDIT_SWITCHMODEAPPENDLINE, MODEKEYEDIT_SWITCHMODEBEGINLINE, MODEKEYEDIT_TRANSPOSECHARS, /* Menu (choice) keys. */ MODEKEYCHOICE_BACKSPACE, MODEKEYCHOICE_CANCEL, MODEKEYCHOICE_CHOOSE, MODEKEYCHOICE_DOWN, MODEKEYCHOICE_PAGEDOWN, MODEKEYCHOICE_PAGEUP, MODEKEYCHOICE_SCROLLDOWN, MODEKEYCHOICE_SCROLLUP, MODEKEYCHOICE_STARTNUMBERPREFIX, MODEKEYCHOICE_TREE_COLLAPSE, MODEKEYCHOICE_TREE_COLLAPSE_ALL, MODEKEYCHOICE_TREE_EXPAND, MODEKEYCHOICE_TREE_EXPAND_ALL, MODEKEYCHOICE_TREE_TOGGLE, MODEKEYCHOICE_UP, /* Copy keys. */ MODEKEYCOPY_BACKTOINDENTATION, MODEKEYCOPY_BOTTOMLINE, MODEKEYCOPY_CANCEL, MODEKEYCOPY_CLEARSELECTION, MODEKEYCOPY_COPYPIPE, MODEKEYCOPY_COPYLINE, MODEKEYCOPY_COPYENDOFLINE, MODEKEYCOPY_COPYSELECTION, MODEKEYCOPY_DOWN, MODEKEYCOPY_ENDOFLINE, MODEKEYCOPY_GOTOLINE, MODEKEYCOPY_HALFPAGEDOWN, MODEKEYCOPY_HALFPAGEUP, MODEKEYCOPY_HISTORYBOTTOM, MODEKEYCOPY_HISTORYTOP, MODEKEYCOPY_JUMP, MODEKEYCOPY_JUMPAGAIN, MODEKEYCOPY_JUMPREVERSE, MODEKEYCOPY_JUMPBACK, MODEKEYCOPY_JUMPTO, MODEKEYCOPY_JUMPTOBACK, MODEKEYCOPY_LEFT, MODEKEYCOPY_MIDDLELINE, MODEKEYCOPY_NEXTPAGE, MODEKEYCOPY_NEXTSPACE, MODEKEYCOPY_NEXTSPACEEND, MODEKEYCOPY_NEXTWORD, MODEKEYCOPY_NEXTWORDEND, MODEKEYCOPY_PREVIOUSPAGE, MODEKEYCOPY_PREVIOUSSPACE, MODEKEYCOPY_PREVIOUSWORD, MODEKEYCOPY_RECTANGLETOGGLE, MODEKEYCOPY_RIGHT, MODEKEYCOPY_SCROLLDOWN, MODEKEYCOPY_SCROLLUP, MODEKEYCOPY_SEARCHAGAIN, MODEKEYCOPY_SEARCHDOWN, MODEKEYCOPY_SEARCHREVERSE, MODEKEYCOPY_SEARCHUP, MODEKEYCOPY_SELECTLINE, MODEKEYCOPY_STARTNUMBERPREFIX, MODEKEYCOPY_STARTOFLINE, MODEKEYCOPY_STARTSELECTION, MODEKEYCOPY_TOPLINE, MODEKEYCOPY_UP, }; /* Entry in the default mode key tables. */ struct mode_key_entry { int key; /* * Editing mode for vi: 0 is edit mode, keys not in the table are * returned as MODEKEY_OTHER; 1 is command mode, keys not in the table * are returned as MODEKEY_NONE. This is also matched on, allowing some * keys to be bound in edit mode. */ int mode; enum mode_key_cmd cmd; }; /* Data required while mode keys are in use. */ struct mode_key_data { struct mode_key_tree *tree; int mode; }; #define MODEKEY_EMACS 0 #define MODEKEY_VI 1 /* Binding between a key and a command. */ struct mode_key_binding { int key; int mode; enum mode_key_cmd cmd; const char *arg; RB_ENTRY(mode_key_binding) entry; }; RB_HEAD(mode_key_tree, mode_key_binding); /* Command to string mapping. */ struct mode_key_cmdstr { enum mode_key_cmd cmd; const char *name; }; /* Named mode key table description. */ struct mode_key_table { const char *name; const struct mode_key_cmdstr *cmdstr; struct mode_key_tree *tree; const struct mode_key_entry *table; /* default entries */ }; /* Modes. */ #define MODE_CURSOR 0x1 #define MODE_INSERT 0x2 #define MODE_KCURSOR 0x4 #define MODE_KKEYPAD 0x8 /* set = application, clear = number */ #define MODE_WRAP 0x10 /* whether lines wrap */ #define MODE_MOUSE_STANDARD 0x20 #define MODE_MOUSE_BUTTON 0x40 #define MODE_MOUSE_ANY 0x80 #define MODE_MOUSE_UTF8 0x100 #define MODE_MOUSE_SGR 0x200 #define MODE_BRACKETPASTE 0x400 #define MODE_FOCUSON 0x800 #define ALL_MOUSE_MODES (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON|MODE_MOUSE_ANY) /* A single UTF-8 character. */ struct utf8_data { u_char data[UTF8_SIZE]; size_t have; size_t size; u_int width; }; /* Grid output. */ #if defined(DEBUG) && \ ((defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || \ (defined(__GNUC__) && __GNUC__ >= 3)) #define GRID_DEBUG(gd, fmt, ...) log_debug2("%s: (sx=%u, sy=%u, hsize=%u) " \ fmt, __func__, (gd)->sx, (gd)->sy, (gd)->hsize, ## __VA_ARGS__) #else #define GRID_DEBUG(...) #endif /* Grid attributes. */ #define GRID_ATTR_BRIGHT 0x1 #define GRID_ATTR_DIM 0x2 #define GRID_ATTR_UNDERSCORE 0x4 #define GRID_ATTR_BLINK 0x8 #define GRID_ATTR_REVERSE 0x10 #define GRID_ATTR_HIDDEN 0x20 #define GRID_ATTR_ITALICS 0x40 #define GRID_ATTR_CHARSET 0x80 /* alternative character set */ /* Grid flags. */ #define GRID_FLAG_FG256 0x1 #define GRID_FLAG_BG256 0x2 #define GRID_FLAG_PADDING 0x4 /* Grid line flags. */ #define GRID_LINE_WRAPPED 0x1 /* Grid cell data. */ struct grid_cell { u_char attr; u_char flags; u_char fg; u_char bg; u_char xstate; /* top 4 bits width, bottom 4 bits size */ u_char xdata[UTF8_SIZE]; } __packed; /* Grid line. */ struct grid_line { u_int cellsize; struct grid_cell *celldata; int flags; } __packed; /* Entire grid of cells. */ struct grid { int flags; #define GRID_HISTORY 0x1 /* scroll lines into history */ u_int sx; u_int sy; u_int hsize; u_int hlimit; struct grid_line *linedata; }; /* Option data structures. */ struct options_entry { char *name; enum { OPTIONS_STRING, OPTIONS_NUMBER, OPTIONS_DATA, } type; char *str; long long num; RB_ENTRY(options_entry) entry; }; struct options { RB_HEAD(options_tree, options_entry) tree; struct options *parent; }; /* Scheduled job. */ struct job { char *cmd; pid_t pid; int status; int fd; struct bufferevent *event; void (*callbackfn)(struct job *); void (*freefn)(void *); void *data; LIST_ENTRY(job) lentry; }; LIST_HEAD(joblist, job); /* Screen selection. */ struct screen_sel { int flag; int rectflag; u_int sx; u_int sy; u_int ex; u_int ey; struct grid_cell cell; }; /* Virtual screen. */ struct screen { char *title; struct grid *grid; /* grid data */ u_int cx; /* cursor x */ u_int cy; /* cursor y */ u_int cstyle; /* cursor style */ char *ccolour; /* cursor colour string */ u_int rupper; /* scroll region top */ u_int rlower; /* scroll region bottom */ int mode; bitstr_t *tabs; struct screen_sel sel; }; /* Screen write context. */ struct screen_write_ctx { struct window_pane *wp; struct screen *s; }; /* Screen size. */ #define screen_size_x(s) ((s)->grid->sx) #define screen_size_y(s) ((s)->grid->sy) #define screen_hsize(s) ((s)->grid->hsize) #define screen_hlimit(s) ((s)->grid->hlimit) /* Input parser context. */ struct input_ctx { struct window_pane *wp; struct screen_write_ctx ctx; struct grid_cell cell; struct grid_cell old_cell; u_int old_cx; u_int old_cy; u_char interm_buf[4]; size_t interm_len; u_char param_buf[64]; size_t param_len; u_char input_buf[256]; size_t input_len; int param_list[24]; /* -1 not present */ u_int param_list_len; struct utf8_data utf8data; int ch; int flags; #define INPUT_DISCARD 0x1 const struct input_state *state; /* * All input received since we were last in the ground state. Sent to * control clients on connection. */ struct evbuffer *since_ground; }; /* * Window mode. Windows can be in several modes and this is used to call the * right function to handle input and output. */ struct session; struct window; struct mouse_event; struct window_mode { struct screen *(*init)(struct window_pane *); void (*free)(struct window_pane *); void (*resize)(struct window_pane *, u_int, u_int); void (*key)(struct window_pane *, struct session *, int); void (*mouse)(struct window_pane *, struct session *, struct mouse_event *); void (*timer)(struct window_pane *); }; /* Structures for choose mode. */ struct window_choose_data { struct client *start_client; struct session *start_session; u_int idx; int type; #define TREE_OTHER 0x0 #define TREE_WINDOW 0x1 #define TREE_SESSION 0x2 struct session *tree_session; /* session of items in tree */ struct winlink *wl; int pane_id; char *ft_template; struct format_tree *ft; char *command; }; struct window_choose_mode_item { struct window_choose_data *wcd; char *name; int pos; int state; #define TREE_EXPANDED 0x1 }; /* Child window structure. */ struct window_pane { u_int id; struct window *window; struct layout_cell *layout_cell; struct layout_cell *saved_layout_cell; u_int sx; u_int sy; u_int xoff; u_int yoff; int flags; #define PANE_REDRAW 0x1 #define PANE_DROP 0x2 #define PANE_FOCUSED 0x4 #define PANE_RESIZE 0x8 char *cmd; char *shell; char *cwd; pid_t pid; char tty[TTY_NAME_MAX]; u_int changes; struct event changes_timer; u_int changes_redraw; int fd; struct bufferevent *event; struct input_ctx ictx; int pipe_fd; struct bufferevent *pipe_event; size_t pipe_off; size_t tmate_off; struct screen *screen; struct screen base; /* Saved in alternative screen mode. */ u_int saved_cx; u_int saved_cy; struct grid *saved_grid; struct grid_cell saved_cell; const struct window_mode *mode; void *modedata; 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_timer; struct timeval silence_timer; 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; u_int sx; u_int sy; int flags; #define WINDOW_BELL 0x1 #define WINDOW_ACTIVITY 0x2 #define WINDOW_REDRAW 0x4 #define WINDOW_SILENCE 0x8 #define WINDOW_ZOOMED 0x10 struct options options; u_int references; }; ARRAY_DECL(windows, struct window *); /* Entry on local window list. */ struct winlink { int idx; struct window *window; size_t status_width; struct grid_cell status_cell; char *status_text; int flags; #define WINLINK_BELL 0x1 #define WINLINK_ACTIVITY 0x2 #define WINLINK_CONTENT 0x4 #define WINLINK_SILENCE 0x8 #define WINLINK_ALERTFLAGS \ (WINLINK_BELL|WINLINK_ACTIVITY|WINLINK_CONTENT|WINLINK_SILENCE) RB_ENTRY(winlink) entry; TAILQ_ENTRY(winlink) sentry; }; RB_HEAD(winlinks, winlink); TAILQ_HEAD(winlink_stack, winlink); /* Layout direction. */ enum layout_type { LAYOUT_LEFTRIGHT, LAYOUT_TOPBOTTOM, LAYOUT_WINDOWPANE }; /* Layout cells queue. */ TAILQ_HEAD(layout_cells, layout_cell); /* Layout cell. */ struct layout_cell { enum layout_type type; struct layout_cell *parent; u_int sx; u_int sy; u_int xoff; u_int yoff; struct window_pane *wp; struct layout_cells cells; TAILQ_ENTRY(layout_cell) entry; }; /* Paste buffer. */ struct paste_buffer { char *data; size_t size; }; ARRAY_DECL(paste_stack, struct paste_buffer *); /* Environment variable. */ struct environ_entry { char *name; char *value; RB_ENTRY(environ_entry) entry; }; RB_HEAD(environ, environ_entry); /* Client session. */ struct session_group { TAILQ_HEAD(, session) sessions; TAILQ_ENTRY(session_group) entry; }; TAILQ_HEAD(session_groups, session_group); struct session { u_int id; char *name; char *cwd; struct timeval creation_time; struct timeval activity_time; struct timeval last_activity_time; u_int sx; u_int sy; struct winlink *curw; struct winlink_stack lastw; struct winlinks windows; struct options options; #define SESSION_UNATTACHED 0x1 /* not attached to any clients */ int flags; struct termios *tio; struct environ environ; int references; TAILQ_ENTRY(session) gentry; RB_ENTRY(session) entry; }; RB_HEAD(sessions, session); ARRAY_DECL(sessionslist, struct session *); /* TTY information. */ struct tty_key { char ch; int key; struct tty_key *left; struct tty_key *right; struct tty_key *next; }; struct tty_term { char *name; u_int references; char acs[UCHAR_MAX + 1][2]; struct tty_code codes[NTTYCODE]; #define TERM_256COLOURS 0x1 #define TERM_88COLOURS 0x2 #define TERM_EARLYWRAP 0x4 int flags; LIST_ENTRY(tty_term) entry; }; LIST_HEAD(tty_terms, tty_term); /* Mouse wheel states. */ #define MOUSE_WHEEL_UP 0 #define MOUSE_WHEEL_DOWN 1 /* Mouse events. */ #define MOUSE_EVENT_DOWN (1 << 0) #define MOUSE_EVENT_DRAG (1 << 1) #define MOUSE_EVENT_UP (1 << 2) #define MOUSE_EVENT_CLICK (1 << 3) #define MOUSE_EVENT_WHEEL (1 << 4) /* Mouse flags. */ #define MOUSE_RESIZE_PANE (1 << 0) /* * Mouse input. When sent by xterm: * * - buttons are in the bottom two bits: 0 = b1; 1 = b2; 2 = b3; 3 = released * - bits 3, 4 and 5 are for keys * - bit 6 is set for dragging * - bit 7 for buttons 4 and 5 * * With the SGR 1006 extension the released button becomes known. Store these * in separate fields and store the value converted to the old format in xb. */ struct mouse_event { u_int xb; u_int x; u_int lx; u_int sx; u_int y; u_int ly; u_int sy; u_int sgr; /* whether the input arrived in SGR format */ u_int sgr_xb; /* only for SGR: the unmangled button */ u_int sgr_rel; /* only for SGR: if it is a release event */ u_int button; u_int clicks; int wheel; int event; int flags; }; struct tty { struct client *client; char *path; u_int class; u_int sx; u_int sy; u_int cx; u_int cy; u_int cstyle; char *ccolour; int mode; u_int rlower; u_int rupper; char *termname; struct tty_term *term; int fd; struct bufferevent *event; int log_fd; struct termios tio; struct grid_cell cell; #define TTY_NOCURSOR 0x1 #define TTY_FREEZE 0x2 #define TTY_TIMER 0x4 #define TTY_UTF8 0x8 #define TTY_STARTED 0x10 #define TTY_OPENED 0x20 int flags; int term_flags; struct mouse_event mouse; struct event key_timer; struct tty_key *key_tree; }; /* TTY command context and function pointer. */ struct tty_ctx { struct window_pane *wp; const struct grid_cell *cell; 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; u_int xoff; u_int yoff; /* Saved last cell on line. */ struct grid_cell last_cell; u_int last_width; }; /* Saved message entry. */ struct message_entry { char *msg; time_t msg_time; }; /* Status output data from a job. */ struct status_out { char *cmd; char *out; RB_ENTRY(status_out) entry; }; RB_HEAD(status_out_tree, status_out); /* Client connection. */ struct client { struct imsgbuf ibuf; struct event event; int retcode; struct timeval creation_time; struct timeval activity_time; struct environ environ; char *title; char *cwd; struct tty tty; 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 status_out_tree status_old; struct status_out_tree status_new; struct timeval status_timer; struct screen status; #define CLIENT_TERMINAL 0x1 #define CLIENT_PREFIX 0x2 #define CLIENT_EXIT 0x4 #define CLIENT_REDRAW 0x8 #define CLIENT_STATUS 0x10 #define CLIENT_REPEAT 0x20 /* allow command to repeat within repeat time */ #define CLIENT_SUSPENDED 0x40 #define CLIENT_BAD 0x80 #define CLIENT_IDENTIFY 0x100 #define CLIENT_DEAD 0x200 #define CLIENT_BORDERS 0x400 #define CLIENT_READONLY 0x800 #define CLIENT_REDRAWWINDOW 0x1000 #define CLIENT_CONTROL 0x2000 #define CLIENT_FOCUSED 0x4000 #ifdef TMATE #define CLIENT_FORCE_STATUS 0x800000 #endif int flags; struct event identify_timer; char *message_string; struct event message_timer; ARRAY_DECL(, struct message_entry) message_log; char *prompt_string; char *prompt_buffer; size_t prompt_index; int (*prompt_callbackfn)(void *, const char *); void (*prompt_freefn)(void *); void *prompt_data; u_int prompt_hindex; #define PROMPT_SINGLE 0x1 int prompt_flags; struct mode_key_data prompt_mdata; struct session *session; struct session *last_session; int wlmouse; struct cmd_q *cmdq; int references; }; ARRAY_DECL(clients, struct client *); /* Parsed arguments. */ struct args { bitstr_t *flags; char *values[SCHAR_MAX]; /* XXX This is awfully big. */ int argc; char **argv; }; /* Command and list of commands. */ struct cmd { const struct cmd_entry *entry; struct args *args; char *file; u_int line; TAILQ_ENTRY(cmd) qentry; }; struct cmd_list { int references; TAILQ_HEAD(, cmd) list; }; /* Command return values. */ enum cmd_retval { CMD_RETURN_ERROR = -1, CMD_RETURN_NORMAL = 0, CMD_RETURN_WAIT, CMD_RETURN_STOP }; /* Command queue entry. */ struct cmd_q_item { struct cmd_list *cmdlist; TAILQ_ENTRY(cmd_q_item) qentry; }; TAILQ_HEAD(cmd_q_items, cmd_q_item); /* Command queue. */ struct cmd_q { int references; int dead; struct client *client; int client_exit; struct cmd_q_items queue; struct cmd_q_item *item; struct cmd *cmd; time_t time; u_int number; void (*emptyfn)(struct cmd_q *); void *data; struct msg_command_data *msgdata; TAILQ_ENTRY(cmd_q) waitentry; }; /* Command definition. */ struct cmd_entry { const char *name; const char *alias; const char *args_template; int args_lower; int args_upper; const char *usage; #define CMD_STARTSERVER 0x1 #define CMD_CANTNEST 0x2 #define CMD_SENDENVIRON 0x4 #define CMD_READONLY 0x8 int flags; void (*key_binding)(struct cmd *, int); int (*check)(struct args *); enum cmd_retval (*exec)(struct cmd *, struct cmd_q *); }; /* Key binding. */ struct key_binding { int key; struct cmd_list *cmdlist; int can_repeat; RB_ENTRY(key_binding) entry; }; RB_HEAD(key_bindings, key_binding); /* * Option table entries. The option table is the user-visible part of the * option, as opposed to the internal options (struct option) which are just * number or string. */ enum options_table_type { OPTIONS_TABLE_STRING, OPTIONS_TABLE_NUMBER, OPTIONS_TABLE_KEY, OPTIONS_TABLE_COLOUR, OPTIONS_TABLE_ATTRIBUTES, OPTIONS_TABLE_FLAG, OPTIONS_TABLE_CHOICE }; struct options_table_entry { const char *name; enum options_table_type type; u_int minimum; u_int maximum; const char **choices; const char *default_str; long long default_num; }; /* Tree of format entries. */ struct format_entry { char *key; char *value; RB_ENTRY(format_entry) entry; }; RB_HEAD(format_tree, format_entry); /* Common command usages. */ #define CMD_TARGET_PANE_USAGE "[-t target-pane]" #define CMD_TARGET_WINDOW_USAGE "[-t target-window]" #define CMD_TARGET_SESSION_USAGE "[-t target-session]" #define CMD_TARGET_CLIENT_USAGE "[-t target-client]" #define CMD_SRCDST_PANE_USAGE "[-s src-pane] [-t dst-pane]" #define CMD_SRCDST_WINDOW_USAGE "[-s src-window] [-t dst-window]" #define CMD_SRCDST_SESSION_USAGE "[-s src-session] [-t dst-session]" #define CMD_SRCDST_CLIENT_USAGE "[-s src-client] [-t dst-client]" #define CMD_BUFFER_USAGE "[-b buffer-index]" /* tmux.c */ extern struct options global_options; extern struct options global_s_options; extern struct options global_w_options; extern struct environ global_environ; extern struct event_base *ev_base; extern char *cfg_file, *tmate_cfg_file; extern char *shell_cmd; extern int debug_level; extern time_t start_time; extern char socket_path[MAXPATHLEN]; extern int login_shell; extern char *environ_path; extern pid_t environ_pid; extern int environ_session_id; void logfile(const char *); const char *getshell(void); int checkshell(const char *); int areshell(const char *); const char* get_full_path(const char *, const char *); void setblocking(int, int); __dead void shell_exec(const char *, const char *); /* cfg.c */ extern struct cmd_q *cfg_cmd_q; extern int cfg_finished; extern int cfg_references; extern struct causelist cfg_causes; int load_cfg(const char *, struct cmd_q *, char **); void cfg_default_done(struct cmd_q *); void cfg_show_causes(struct session *); /* format.c */ int format_cmp(struct format_entry *, struct format_entry *); RB_PROTOTYPE(format_tree, format_entry, entry, format_cmp); struct format_tree *format_create(void); void format_free(struct format_tree *); void printflike3 format_add( struct format_tree *, const char *, const char *, ...); const char *format_find(struct format_tree *, const char *); char *format_expand(struct format_tree *, const char *); void format_session(struct format_tree *, struct session *); void format_client(struct format_tree *, struct client *); void format_winlink( struct format_tree *, struct session *, struct winlink *); void format_window_pane(struct format_tree *, struct window_pane *); void format_paste_buffer(struct format_tree *, struct paste_buffer *); /* mode-key.c */ extern const struct mode_key_table mode_key_tables[]; extern struct mode_key_tree mode_key_tree_vi_edit; extern struct mode_key_tree mode_key_tree_vi_choice; extern struct mode_key_tree mode_key_tree_vi_copy; extern struct mode_key_tree mode_key_tree_emacs_edit; extern struct mode_key_tree mode_key_tree_emacs_choice; extern struct mode_key_tree mode_key_tree_emacs_copy; int mode_key_cmp(struct mode_key_binding *, struct mode_key_binding *); RB_PROTOTYPE(mode_key_tree, mode_key_binding, entry, mode_key_cmp); const char *mode_key_tostring(const struct mode_key_cmdstr *, enum mode_key_cmd); enum mode_key_cmd mode_key_fromstring(const struct mode_key_cmdstr *, const char *); const struct mode_key_table *mode_key_findtable(const char *); void mode_key_init_trees(void); void mode_key_init(struct mode_key_data *, struct mode_key_tree *); enum mode_key_cmd mode_key_lookup(struct mode_key_data *, int, const char **); /* notify.c */ void notify_enable(void); void notify_disable(void); void notify_input(struct window_pane *, struct evbuffer *); void notify_window_layout_changed(struct window *); void notify_window_unlinked(struct session *, struct window *); void notify_window_linked(struct session *, struct window *); void notify_window_renamed(struct window *); void notify_attached_session_changed(struct client *); void notify_session_renamed(struct session *); void notify_session_created(struct session *); void notify_session_closed(struct session *); /* options.c */ int options_cmp(struct options_entry *, struct options_entry *); RB_PROTOTYPE(options_tree, options_entry, entry, options_cmp); void options_init(struct options *, struct options *); void options_free(struct options *); struct options_entry *options_find1(struct options *, const char *); struct options_entry *options_find(struct options *, const char *); void options_remove(struct options *, const char *); struct options_entry *printflike3 options_set_string( struct options *, const char *, const char *, ...); char *options_get_string(struct options *, const char *); struct options_entry *options_set_number( struct options *, const char *, long long); long long options_get_number(struct options *, const char *); /* options-table.c */ extern const struct options_table_entry server_options_table[]; extern const struct options_table_entry session_options_table[]; extern const struct options_table_entry window_options_table[]; void options_table_populate_tree(const struct options_table_entry *, struct options *); const char *options_table_print_entry(const struct options_table_entry *, struct options_entry *, int); int options_table_find(const char *, const struct options_table_entry **, const struct options_table_entry **); /* job.c */ extern struct joblist all_jobs; struct job *job_run(const char *, struct session *, void (*)(struct job *), void (*)(void *), void *); void job_free(struct job *); void job_died(struct job *, int); /* environ.c */ int environ_cmp(struct environ_entry *, struct environ_entry *); RB_PROTOTYPE(environ, environ_entry, entry, environ_cmp); void environ_init(struct environ *); void environ_free(struct environ *); void environ_copy(struct environ *, struct environ *); struct environ_entry *environ_find(struct environ *, const char *); void environ_set(struct environ *, const char *, const char *); void environ_put(struct environ *, const char *); void environ_unset(struct environ *, const char *); void environ_update(const char *, struct environ *, struct environ *); void environ_push(struct environ *); /* tty.c */ void tty_init_termios(int, struct termios *, struct bufferevent *); void tty_raw(struct tty *, const char *); void tty_attributes(struct tty *, const struct grid_cell *); void tty_reset(struct tty *); void tty_region_pane(struct tty *, const struct tty_ctx *, u_int, u_int); void tty_region(struct tty *, u_int, u_int); void tty_cursor_pane(struct tty *, const struct tty_ctx *, u_int, u_int); void tty_cursor(struct tty *, u_int, u_int); void tty_putcode(struct tty *, enum tty_code_code); void tty_putcode1(struct tty *, enum tty_code_code, int); void tty_putcode2(struct tty *, enum tty_code_code, int, int); void tty_putcode_ptr1(struct tty *, enum tty_code_code, const void *); void tty_putcode_ptr2(struct tty *, enum tty_code_code, const void *, const void *); void tty_puts(struct tty *, const char *); void tty_putc(struct tty *, u_char); void tty_putn(struct tty *, const void *, size_t, u_int); void tty_init(struct tty *, struct client *, int, char *); int tty_resize(struct tty *); int tty_set_size(struct tty *, u_int, u_int); void tty_set_class(struct tty *, 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_force_cursor_colour(struct tty *, const char *); void tty_draw_line(struct tty *, struct screen *, u_int, u_int, u_int); int tty_open(struct tty *, const char *, char **); void tty_close(struct tty *); void tty_free(struct tty *); void tty_write( void (*)(struct tty *, const struct tty_ctx *), struct tty_ctx *); void tty_cmd_alignmenttest(struct tty *, const struct tty_ctx *); void tty_cmd_cell(struct tty *, const struct tty_ctx *); void tty_cmd_clearendofline(struct tty *, const struct tty_ctx *); void tty_cmd_clearendofscreen(struct tty *, const struct tty_ctx *); void tty_cmd_clearline(struct tty *, const struct tty_ctx *); void tty_cmd_clearscreen(struct tty *, const struct tty_ctx *); void tty_cmd_clearstartofline(struct tty *, const struct tty_ctx *); void tty_cmd_clearstartofscreen(struct tty *, const struct tty_ctx *); void tty_cmd_deletecharacter(struct tty *, const struct tty_ctx *); void tty_cmd_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_utf8character(struct tty *, const struct tty_ctx *); void tty_cmd_reverseindex(struct tty *, const struct tty_ctx *); void tty_cmd_setselection(struct tty *, const struct tty_ctx *); void tty_cmd_rawstring(struct tty *, const struct tty_ctx *); void tty_bell(struct tty *); /* tty-term.c */ extern struct tty_terms tty_terms; extern const struct tty_term_code_entry tty_term_codes[NTTYCODE]; struct tty_term *tty_term_find(char *, int, const char *, char **); void tty_term_free(struct tty_term *); int tty_term_has(struct tty_term *, enum tty_code_code); const char *tty_term_string(struct tty_term *, enum tty_code_code); const char *tty_term_string1(struct tty_term *, enum tty_code_code, int); const char *tty_term_string2( struct tty_term *, enum tty_code_code, int, int); const char *tty_term_ptr1( struct tty_term *, enum tty_code_code, const void *); const char *tty_term_ptr2(struct tty_term *, enum tty_code_code, const void *, const void *); int tty_term_number(struct tty_term *, enum tty_code_code); int tty_term_flag(struct tty_term *, enum tty_code_code); /* tty-acs.c */ const char *tty_acs_get(struct tty *, u_char); /* tty-keys.c */ void tty_keys_build(struct tty *); void tty_keys_free(struct tty *); int tty_keys_next(struct tty *); /* paste.c */ struct paste_buffer *paste_walk_stack(struct paste_stack *, u_int *); struct paste_buffer *paste_get_top(struct paste_stack *); struct paste_buffer *paste_get_index(struct paste_stack *, u_int); int paste_free_top(struct paste_stack *); int paste_free_index(struct paste_stack *, u_int); void paste_add(struct paste_stack *, char *, size_t, u_int); int paste_replace(struct paste_stack *, u_int, char *, size_t); char *paste_print(struct paste_buffer *, size_t); void paste_send_pane(struct paste_buffer *, struct window_pane *, const char *, int); /* clock.c */ extern const char clock_table[14][5][5]; void clock_draw(struct screen_write_ctx *, int, int); /* arguments.c */ struct args *args_create(int, ...); struct args *args_parse(const char *, int, char **); void args_free(struct args *); size_t args_print(struct args *, char *, size_t); int args_has(struct args *, u_char); void args_set(struct args *, u_char, const char *); const char *args_get(struct args *, u_char); long long args_strtonum( struct args *, u_char, long long, long long, char **); /* cmd.c */ int cmd_pack_argv(int, char **, char *, size_t); int cmd_unpack_argv(char *, size_t, int, char ***); char **cmd_copy_argv(int, char *const *); void cmd_free_argv(int, char **); struct cmd *cmd_parse(int, char **, const char *, u_int, char **); size_t cmd_print(struct cmd *, char *, size_t); struct session *cmd_current_session(struct cmd_q *, int); struct client *cmd_current_client(struct cmd_q *); struct client *cmd_find_client(struct cmd_q *, const char *, int); struct session *cmd_find_session(struct cmd_q *, const char *, int); struct winlink *cmd_find_window(struct cmd_q *, const char *, struct session **); int cmd_find_index(struct cmd_q *, const char *, struct session **); struct winlink *cmd_find_pane(struct cmd_q *, const char *, struct session **, struct window_pane **); char *cmd_template_replace(const char *, const char *, int); const char *cmd_get_default_path(struct cmd_q *, const char *); extern const struct cmd_entry *cmd_table[]; extern const struct cmd_entry cmd_attach_session_entry; extern const struct cmd_entry cmd_bind_key_entry; extern const struct cmd_entry cmd_break_pane_entry; extern const struct cmd_entry cmd_capture_pane_entry; extern const struct cmd_entry cmd_choose_buffer_entry; extern const struct cmd_entry cmd_choose_client_entry; extern const struct cmd_entry cmd_choose_list_entry; extern const struct cmd_entry cmd_choose_session_entry; extern const struct cmd_entry cmd_choose_tree_entry; extern const struct cmd_entry cmd_choose_window_entry; extern const struct cmd_entry cmd_clear_history_entry; extern const struct cmd_entry cmd_clock_mode_entry; extern const struct cmd_entry cmd_command_prompt_entry; extern const struct cmd_entry cmd_confirm_before_entry; extern const struct cmd_entry cmd_copy_mode_entry; extern const struct cmd_entry cmd_delete_buffer_entry; extern const struct cmd_entry cmd_detach_client_entry; extern const struct cmd_entry cmd_display_message_entry; extern const struct cmd_entry cmd_display_panes_entry; extern const struct cmd_entry cmd_down_pane_entry; extern const struct cmd_entry cmd_find_window_entry; extern const struct cmd_entry cmd_has_session_entry; extern const struct cmd_entry cmd_if_shell_entry; extern const struct cmd_entry cmd_join_pane_entry; extern const struct cmd_entry cmd_kill_pane_entry; extern const struct cmd_entry cmd_kill_server_entry; extern const struct cmd_entry cmd_kill_session_entry; extern const struct cmd_entry cmd_kill_window_entry; extern const struct cmd_entry cmd_last_pane_entry; extern const struct cmd_entry cmd_last_window_entry; extern const struct cmd_entry cmd_link_window_entry; extern const struct cmd_entry cmd_list_buffers_entry; extern const struct cmd_entry cmd_list_clients_entry; extern const struct cmd_entry cmd_list_commands_entry; extern const struct cmd_entry cmd_list_keys_entry; extern const struct cmd_entry cmd_list_panes_entry; extern const struct cmd_entry cmd_list_sessions_entry; extern const struct cmd_entry cmd_list_windows_entry; extern const struct cmd_entry cmd_load_buffer_entry; extern const struct cmd_entry cmd_lock_client_entry; extern const struct cmd_entry cmd_lock_server_entry; extern const struct cmd_entry cmd_lock_session_entry; extern const struct cmd_entry cmd_move_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_respawn_pane_entry; extern const struct cmd_entry cmd_respawn_window_entry; extern const struct cmd_entry cmd_rotate_window_entry; extern const struct cmd_entry cmd_run_shell_entry; extern const struct cmd_entry cmd_save_buffer_entry; extern const struct cmd_entry cmd_select_layout_entry; extern const struct cmd_entry cmd_select_pane_entry; extern const struct cmd_entry cmd_select_window_entry; extern const struct cmd_entry cmd_send_keys_entry; extern const struct cmd_entry cmd_send_prefix_entry; extern const struct cmd_entry cmd_server_info_entry; extern const struct cmd_entry cmd_set_buffer_entry; extern const struct cmd_entry cmd_set_environment_entry; extern const struct cmd_entry cmd_set_option_entry; extern const struct cmd_entry cmd_set_window_option_entry; extern const struct cmd_entry cmd_show_buffer_entry; extern const struct cmd_entry cmd_show_environment_entry; extern const struct cmd_entry cmd_show_messages_entry; extern const struct cmd_entry cmd_show_options_entry; extern const struct cmd_entry cmd_show_window_options_entry; extern const struct cmd_entry cmd_source_file_entry; extern const struct cmd_entry cmd_split_window_entry; extern const struct cmd_entry cmd_start_server_entry; extern const struct cmd_entry cmd_suspend_client_entry; extern const struct cmd_entry cmd_swap_pane_entry; extern const struct cmd_entry cmd_swap_window_entry; extern const struct cmd_entry cmd_switch_client_entry; extern const struct cmd_entry cmd_unbind_key_entry; extern const struct cmd_entry cmd_unlink_window_entry; extern const struct cmd_entry cmd_up_pane_entry; extern const struct cmd_entry cmd_wait_for_entry; /* cmd-attach-session.c */ enum cmd_retval cmd_attach_session(struct cmd_q *, const char*, int, int); /* cmd-list.c */ struct cmd_list *cmd_list_parse(int, char **, const char *, u_int, char **); void cmd_list_free(struct cmd_list *); size_t cmd_list_print(struct cmd_list *, char *, size_t); /* cmd-queue.c */ struct cmd_q *cmdq_new(struct client *); int cmdq_free(struct cmd_q *); void printflike2 cmdq_print(struct cmd_q *, const char *, ...); void printflike2 cmdq_info(struct cmd_q *, const char *, ...); void printflike2 cmdq_error(struct cmd_q *, const char *, ...); int cmdq_guard(struct cmd_q *, const char *); void cmdq_run(struct cmd_q *, struct cmd_list *); void cmdq_append(struct cmd_q *, struct cmd_list *); int cmdq_continue(struct cmd_q *); void cmdq_flush(struct cmd_q *); /* cmd-string.c */ int cmd_string_parse(const char *, struct cmd_list **, const char *, u_int, char **); /* client.c */ int client_main(int, char **, int); /* key-bindings.c */ extern struct key_bindings key_bindings; int key_bindings_cmp(struct key_binding *, struct key_binding *); RB_PROTOTYPE(key_bindings, key_binding, entry, key_bindings_cmp); struct key_binding *key_bindings_lookup(int); void key_bindings_add(int, int, struct cmd_list *); void key_bindings_remove(int); void key_bindings_clean(void); void key_bindings_init(void); void key_bindings_dispatch(struct key_binding *, struct client *); /* key-string.c */ int key_string_lookup_string(const char *); const char *key_string_lookup_key(int); /* server.c */ extern struct clients clients; extern struct clients dead_clients; extern struct paste_stack global_buffers; int server_start(int, char *); void server_update_socket(void); void server_add_accept(int); /* server-client.c */ void server_client_handle_key(struct client *, int); void server_client_create(int); int server_client_open(struct client *, struct session *, char **); void server_client_lost(struct client *); void server_client_callback(int, short, void *); void server_client_status_timer(void); void server_client_loop(void); /* server-window.c */ void server_window_loop(void); /* server-fn.c */ void server_fill_environ(struct session *, struct environ *); void server_write_ready(struct client *); int server_write_client( struct client *, enum msgtype, const void *, size_t); void server_write_session( struct session *, enum msgtype, const void *, size_t); void server_redraw_client(struct client *); void server_status_client(struct client *); void server_redraw_session(struct session *); void server_redraw_session_group(struct session *); void server_status_session(struct session *); void server_status_session_group(struct session *); void server_redraw_window(struct window *); void server_redraw_window_borders(struct window *); void server_status_window(struct window *); void server_lock(void); void server_lock_session(struct session *); void server_lock_client(struct client *); int server_unlock(const char *); void server_kill_window(struct window *); int server_link_window(struct session *, struct winlink *, struct session *, int, int, int, char **); void server_unlink_window(struct session *, struct winlink *); void server_destroy_pane(struct window_pane *); void server_destroy_session_group(struct session *); void server_destroy_session(struct session *); void server_check_unattached(void); void server_set_identify(struct client *); void server_clear_identify(struct client *); void server_update_event(struct client *); void server_push_stdout(struct client *); void server_push_stderr(struct client *); int server_set_stdin_callback(struct client *, void (*)(struct client *, int, void *), void *, char **); void server_unzoom_window(struct window *); /* status.c */ int status_out_cmp(struct status_out *, struct status_out *); RB_PROTOTYPE(status_out_tree, status_out, entry, status_out_cmp); int status_at_line(struct client *); void status_free_jobs(struct status_out_tree *); void status_update_jobs(struct client *); void status_set_window_at(struct client *, u_int); int status_redraw(struct client *); char *status_replace(struct client *, struct session *, struct winlink *, struct window_pane *, const char *, time_t, int); void printflike2 status_message_set(struct client *, const char *, ...); void status_message_clear(struct client *); int status_message_redraw(struct client *); void status_prompt_set(struct client *, const char *, const char *, int (*)(void *, const char *), void (*)(void *), void *, int); void status_prompt_clear(struct client *); int status_prompt_redraw(struct client *); void status_prompt_key(struct client *, int); void status_prompt_update(struct client *, const char *, const char *); /* resize.c */ void recalculate_sizes(void); /* input.c */ void input_init(struct window_pane *); void input_free(struct window_pane *); void input_parse(struct window_pane *); /* input-key.c */ void input_key(struct window_pane *, int); void input_mouse(struct window_pane *, struct session *, struct mouse_event *); /* xterm-keys.c */ char *xterm_keys_lookup(int); int xterm_keys_find(const char *, size_t, size_t *, int *); /* colour.c */ void colour_set_fg(struct grid_cell *, int); void colour_set_bg(struct grid_cell *, int); const char *colour_tostring(int); int colour_fromstring(const char *); u_char colour_256to16(u_char); u_char colour_256to88(u_char); /* attributes.c */ const char *attributes_tostring(u_char); int attributes_fromstring(const char *); /* grid.c */ extern const struct grid_cell grid_default_cell; extern const struct grid_cell grid_marker_cell; struct grid *grid_create(u_int, u_int, u_int); void grid_destroy(struct grid *); int grid_compare(struct grid *, struct grid *); void grid_collect_history(struct grid *); void grid_scroll_history(struct grid *); void grid_scroll_history_region(struct grid *, u_int, u_int); void grid_expand_line(struct grid *, u_int, u_int); const struct grid_cell *grid_peek_cell(struct grid *, u_int, u_int); const struct grid_line *grid_peek_line(struct grid *, u_int); struct grid_cell *grid_get_cell(struct grid *, u_int, u_int); void grid_set_cell(struct grid *, u_int, u_int, const struct grid_cell *); void grid_clear(struct grid *, u_int, u_int, u_int, u_int); void grid_clear_lines(struct grid *, u_int, u_int); void grid_move_lines(struct grid *, u_int, u_int, u_int); void grid_move_cells(struct grid *, u_int, u_int, u_int, u_int); char *grid_string_cells(struct grid *, u_int, u_int, u_int, struct grid_cell **, int, int, int); void grid_duplicate_lines( struct grid *, u_int, struct grid *, u_int, u_int); u_int grid_reflow(struct grid *, struct grid *, u_int); /* grid-cell.c */ u_int grid_cell_width(const struct grid_cell *); void grid_cell_get(const struct grid_cell *, struct utf8_data *); void grid_cell_set(struct grid_cell *, const struct utf8_data *); void grid_cell_one(struct grid_cell *, u_char); /* grid-view.c */ const struct grid_cell *grid_view_peek_cell(struct grid *, u_int, u_int); struct grid_cell *grid_view_get_cell(struct grid *, u_int, u_int); void grid_view_set_cell( struct grid *, u_int, u_int, const struct grid_cell *); void grid_view_clear_history(struct grid *); void grid_view_clear(struct grid *, u_int, u_int, u_int, u_int); void grid_view_scroll_region_up(struct grid *, u_int, u_int); void grid_view_scroll_region_down(struct grid *, u_int, u_int); void grid_view_insert_lines(struct grid *, u_int, u_int); void grid_view_insert_lines_region(struct grid *, u_int, u_int, u_int); void grid_view_delete_lines(struct grid *, u_int, u_int); void grid_view_delete_lines_region(struct grid *, u_int, u_int, u_int); void grid_view_insert_cells(struct grid *, u_int, u_int, u_int); void grid_view_delete_cells(struct grid *, u_int, u_int, u_int); char *grid_view_string_cells(struct grid *, u_int, u_int, u_int); /* screen-write.c */ void screen_write_start( struct screen_write_ctx *, struct window_pane *, struct screen *); void screen_write_stop(struct screen_write_ctx *); void screen_write_reset(struct screen_write_ctx *); size_t printflike2 screen_write_cstrlen(int, const char *, ...); void printflike5 screen_write_cnputs(struct screen_write_ctx *, ssize_t, struct grid_cell *, int, const char *, ...); size_t printflike2 screen_write_strlen(int, const char *, ...); void printflike3 screen_write_puts(struct screen_write_ctx *, struct grid_cell *, const char *, ...); void printflike5 screen_write_nputs(struct screen_write_ctx *, ssize_t, struct grid_cell *, int, const char *, ...); void screen_write_vnputs(struct screen_write_ctx *, ssize_t, struct grid_cell *, int, const char *, va_list); void screen_write_parsestyle( struct grid_cell *, struct grid_cell *, const char *); void screen_write_putc( struct screen_write_ctx *, struct grid_cell *, u_char); void screen_write_copy(struct screen_write_ctx *, struct screen *, u_int, u_int, u_int, u_int); void screen_write_backspace(struct screen_write_ctx *); void screen_write_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); void screen_write_deletecharacter(struct screen_write_ctx *, u_int); void screen_write_clearcharacter(struct screen_write_ctx *, u_int); void screen_write_insertline(struct screen_write_ctx *, u_int); void screen_write_deleteline(struct screen_write_ctx *, u_int); void screen_write_clearline(struct screen_write_ctx *); void screen_write_clearendofline(struct screen_write_ctx *); void screen_write_clearstartofline(struct screen_write_ctx *); void screen_write_cursormove(struct screen_write_ctx *, u_int, u_int); void screen_write_reverseindex(struct screen_write_ctx *); void screen_write_scrollregion(struct screen_write_ctx *, u_int, u_int); void screen_write_linefeed(struct screen_write_ctx *, int); void screen_write_linefeedscreen(struct screen_write_ctx *, int); void screen_write_carriagereturn(struct screen_write_ctx *); void screen_write_clearendofscreen(struct screen_write_ctx *); void screen_write_clearstartofscreen(struct screen_write_ctx *); void screen_write_clearscreen(struct screen_write_ctx *); void screen_write_clearhistory(struct screen_write_ctx *); 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 *, int, int); void screen_redraw_pane(struct client *, struct window_pane *); /* screen.c */ void screen_init(struct screen *, u_int, u_int, u_int); void screen_reinit(struct screen *); void screen_free(struct screen *); void screen_reset_tabs(struct screen *); void screen_set_cursor_style(struct screen *, u_int); void screen_set_cursor_colour(struct screen *, const char *); void screen_set_title(struct screen *, const char *); void screen_resize(struct screen *, u_int, u_int, int); void screen_set_selection(struct screen *, u_int, u_int, u_int, u_int, u_int, struct grid_cell *); void screen_clear_selection(struct screen *); int screen_check_selection(struct screen *, u_int, u_int); void screen_reflow(struct screen *, u_int); /* window.c */ extern struct windows windows; extern struct window_pane_tree all_window_panes; int winlink_cmp(struct winlink *, struct winlink *); RB_PROTOTYPE(winlinks, winlink, entry, winlink_cmp); int window_pane_cmp(struct window_pane *, struct window_pane *); RB_PROTOTYPE(window_pane_tree, window_pane, tree_entry, window_pane_cmp); struct winlink *winlink_find_by_index(struct winlinks *, int); struct winlink *winlink_find_by_window(struct winlinks *, struct window *); struct winlink *winlink_find_by_window_id(struct winlinks *, u_int); int winlink_next_index(struct winlinks *, int); u_int winlink_count(struct winlinks *); struct winlink *winlink_add(struct winlinks *, int); void winlink_set_window(struct winlink *, struct window *); void winlink_remove(struct winlinks *, struct winlink *); struct winlink *winlink_next(struct winlink *); struct winlink *winlink_previous(struct winlink *); struct winlink *winlink_next_by_number(struct winlink *, struct session *, int); struct winlink *winlink_previous_by_number(struct winlink *, struct session *, int); void winlink_stack_push(struct winlink_stack *, struct winlink *); void winlink_stack_remove(struct winlink_stack *, struct winlink *); int window_index(struct window *, u_int *); struct window *window_find_by_id(u_int); struct window *window_create1(u_int, u_int); struct window *window_create(const char *, const char *, const char *, const char *, struct environ *, struct termios *, u_int, u_int, u_int, char **); void window_destroy(struct window *); struct window_pane *window_get_active_at(struct window *, u_int, u_int); void window_set_active_at(struct window *, u_int, u_int); struct window_pane *window_find_string(struct window *, const char *); void window_set_active_pane(struct window *, struct window_pane *); struct window_pane *window_add_pane(struct window *, u_int); void window_resize(struct window *, u_int, u_int); int window_zoom(struct window_pane *); int window_unzoom(struct window *); 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(u_int); struct window_pane *window_pane_create(struct window *, u_int, u_int, u_int); void window_pane_destroy(struct window_pane *); void window_pane_timer_start(struct window_pane *); int window_pane_spawn(struct window_pane *, const char *, const char *, const char *, struct environ *, struct termios *, char **); void window_pane_resize(struct window_pane *, u_int, u_int); void window_pane_alternate_on(struct window_pane *, struct grid_cell *, int); void window_pane_alternate_off(struct window_pane *, struct grid_cell *, int); int window_pane_set_mode( struct window_pane *, const struct window_mode *); void window_pane_reset_mode(struct window_pane *); void window_pane_key(struct window_pane *, struct session *, int); void window_pane_mouse(struct window_pane *, struct session *, struct mouse_event *); int window_pane_visible(struct window_pane *); char *window_pane_search( struct window_pane *, const char *, u_int *); char *window_printable_flags(struct session *, struct winlink *); struct window_pane *window_pane_find_up(struct window_pane *); struct window_pane *window_pane_find_down(struct window_pane *); struct window_pane *window_pane_find_left(struct window_pane *); struct window_pane *window_pane_find_right(struct window_pane *); void window_set_name(struct window *, const char *); void window_remove_ref(struct window *); void winlink_clear_flags(struct winlink *); void window_mode_attrs(struct grid_cell *, struct options *); /* layout.c */ u_int layout_count_cells(struct layout_cell *); struct layout_cell *layout_create_cell(struct layout_cell *); void layout_free_cell(struct layout_cell *); void layout_print_cell(struct layout_cell *, const char *, u_int); void layout_destroy_cell(struct layout_cell *, struct layout_cell **); void layout_set_size( struct layout_cell *, u_int, u_int, u_int, u_int); void layout_make_leaf( struct layout_cell *, struct window_pane *); void layout_make_node(struct layout_cell *, enum layout_type); void layout_fix_offsets(struct layout_cell *); void layout_fix_panes(struct window *, u_int, u_int); u_int layout_resize_check(struct layout_cell *, enum layout_type); void layout_resize_adjust( struct layout_cell *, enum layout_type, int); void layout_init(struct window *, 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); void layout_resize_pane_to(struct window_pane *, enum layout_type, u_int); void layout_resize_pane_mouse(struct client *); 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 *); /* layout-custom.c */ char *layout_dump(struct window *); int layout_parse(struct window *, const char *); /* layout-set.c */ const char *layout_set_name(u_int); int layout_set_lookup(const char *); u_int layout_set_select(struct window *, u_int); u_int layout_set_next(struct window *); u_int layout_set_previous(struct window *); void layout_set_active_changed(struct window *); /* window-clock.c */ extern const struct window_mode window_clock_mode; /* window-copy.c */ enum window_copy_input_type { WINDOW_COPY_OFF, WINDOW_COPY_NUMERICPREFIX, WINDOW_COPY_SEARCHUP, WINDOW_COPY_SEARCHDOWN, WINDOW_COPY_JUMPFORWARD, WINDOW_COPY_JUMPBACK, WINDOW_COPY_JUMPTOFORWARD, WINDOW_COPY_JUMPTOBACK, WINDOW_COPY_GOTOLINE, #ifdef TMATE WINDOW_COPY_PASSWORD, #endif }; /* * 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). */ #ifdef TMATE typedef void (*copy_password_callback)(const char *password, void *private); #endif struct window_copy_mode_data { struct screen screen; struct screen *backing; int backing_written; /* backing display has started */ struct mode_key_data mdata; u_int oy; u_int selx; u_int sely; u_int rectflag; /* are we in rectangle copy mode? */ u_int cx; u_int cy; u_int lastcx; /* position in last line with content */ u_int lastsx; /* size of last line with content */ enum window_copy_input_type inputtype; const char *inputprompt; char *inputstr; int numprefix; enum window_copy_input_type searchtype; char *searchstr; enum window_copy_input_type jumptype; char jumpchar; #ifdef TMATE copy_password_callback password_cb; void *password_cb_private; #endif }; extern const struct window_mode window_copy_mode; void window_copy_init_from_pane(struct window_pane *); void window_copy_init_for_output(struct window_pane *); void printflike2 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 window_copy_update_selection(struct window_pane *); void window_copy_redraw_screen(struct window_pane *); /* window-choose.c */ extern const struct window_mode window_choose_mode; void window_choose_add(struct window_pane *, struct window_choose_data *); void window_choose_ready(struct window_pane *, u_int, void (*)(struct window_choose_data *)); struct window_choose_data *window_choose_data_create (int, struct client *, struct session *); void window_choose_data_free(struct window_choose_data *); void window_choose_data_run(struct window_choose_data *); struct window_choose_data *window_choose_add_window(struct window_pane *, struct client *, struct session *, struct winlink *, const char *, const char *, u_int); struct window_choose_data *window_choose_add_session(struct window_pane *, struct client *, struct session *, const char *, const char *, u_int); struct window_choose_data *window_choose_add_item(struct window_pane *, struct client *, struct winlink *, const char *, const char *, u_int); void window_choose_expand_all(struct window_pane *); /* names.c */ void queue_window_name(struct window *); char *default_window_name(struct window *); /* signal.c */ void set_signals(void(*)(int, short, void *)); void clear_signals(int); /* control.c */ void control_callback(struct client *, int, void*); void printflike2 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 *, struct evbuffer *); void control_notify_window_layout_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_attached_session_changed(struct client *); void control_notify_session_renamed(struct session *); void control_notify_session_created(struct session *); void control_notify_session_close(struct session *); /* session.c */ extern struct sessions sessions; extern struct sessions dead_sessions; extern struct session_groups session_groups; int session_cmp(struct session *, struct session *); RB_PROTOTYPE(sessions, session, entry, session_cmp); int session_alive(struct session *); struct session *session_find(const char *); struct session *session_find_by_id(u_int); struct session *session_create(const char *, const char *, const char *, struct environ *, struct termios *, int, u_int, u_int, char **); void session_destroy(struct session *); int session_check_name(const char *); void session_update_activity(struct session *); struct session *session_next_session(struct session *); struct session *session_previous_session(struct session *); struct winlink *session_new(struct session *, const char *, const char *, const char *, int, char **); struct winlink *session_attach( struct session *, struct window *, int, char **); int session_detach(struct session *, struct winlink *); struct winlink* session_has(struct session *, struct window *); int session_next(struct session *, int); int session_previous(struct session *, int); int session_select(struct session *, int); int session_last(struct session *); int session_set_current(struct session *, struct winlink *); struct session_group *session_group_find(struct session *); u_int session_group_index(struct session_group *); void session_group_add(struct session *, struct session *); void session_group_remove(struct session *); void session_group_synchronize_to(struct session *); void session_group_synchronize_from(struct session *); void session_group_synchronize1(struct session *, struct session *); void session_renumber_windows(struct session *); /* utf8.c */ void utf8_build(void); int utf8_open(struct utf8_data *, u_char); int utf8_append(struct utf8_data *, u_char); u_int utf8_combine(const struct utf8_data *); u_int utf8_split2(u_int, u_char *); /* osdep-*.c */ char *osdep_get_name(int, char *); char *osdep_get_cwd(int); struct event_base *osdep_event_init(void); /* log.c */ void log_open(int, const char *); void log_close(void); void printflike1 log_warn(const char *, ...); void printflike1 log_warnx(const char *, ...); void printflike1 log_info(const char *, ...); void printflike1 log_debug(const char *, ...); void printflike1 log_debug2(const char *, ...); __dead void printflike1 log_fatal(const char *, ...); __dead void printflike1 log_fatalx(const char *, ...); /* xmalloc.c */ char *xstrdup(const char *); void *xcalloc(size_t, size_t); void *xmalloc(size_t); void *xrealloc(void *, size_t, size_t); int printflike2 xasprintf(char **, const char *, ...); int xvasprintf(char **, const char *, va_list); int printflike3 xsnprintf(char *, size_t, const char *, ...); int xvsnprintf(char *, size_t, const char *, va_list); #endif /* TMUX_H */ tmate-1.8.10/tools/000077500000000000000000000000001242461015400140525ustar00rootroot00000000000000tmate-1.8.10/tools/256colors.pl000066400000000000000000000031741242461015400161520ustar00rootroot00000000000000#!/usr/bin/perl # Author: Todd Larason # $XFree86: xc/programs/xterm/vttests/256colors2.pl,v 1.2 2002/03/26 01:46:43 dickey Exp $ # use the resources for colors 0-15 - usually more-or-less a # reproduction of the standard ANSI colors, but possibly more # pleasing shades # colors 16-231 are a 6x6x6 color cube for ($red = 0; $red < 6; $red++) { for ($green = 0; $green < 6; $green++) { for ($blue = 0; $blue < 6; $blue++) { printf("\x1b]4;%d;rgb:%2.2x/%2.2x/%2.2x\x1b\\", 16 + ($red * 36) + ($green * 6) + $blue, ($red ? ($red * 40 + 55) : 0), ($green ? ($green * 40 + 55) : 0), ($blue ? ($blue * 40 + 55) : 0)); } } } # colors 232-255 are a grayscale ramp, intentionally leaving out # black and white for ($gray = 0; $gray < 24; $gray++) { $level = ($gray * 10) + 8; printf("\x1b]4;%d;rgb:%2.2x/%2.2x/%2.2x\x1b\\", 232 + $gray, $level, $level, $level); } # display the colors # first the system ones: print "System colors:\n"; for ($color = 0; $color < 8; $color++) { print "\x1b[48;5;${color}m "; } print "\x1b[0m\n"; for ($color = 8; $color < 16; $color++) { print "\x1b[48;5;${color}m "; } print "\x1b[0m\n\n"; # now the color cube print "Color cube, 6x6x6:\n"; for ($green = 0; $green < 6; $green++) { for ($red = 0; $red < 6; $red++) { for ($blue = 0; $blue < 6; $blue++) { $color = 16 + ($red * 36) + ($green * 6) + $blue; print "\x1b[48;5;${color}m "; } print "\x1b[0m "; } print "\n"; } # now the grayscale ramp print "Grayscale ramp:\n"; for ($color = 232; $color < 256; $color++) { print "\x1b[48;5;${color}m "; } print "\x1b[0m\n"; tmate-1.8.10/tools/UTF-8-demo.txt000066400000000000000000000333441242461015400163470ustar00rootroot00000000000000 UTF-8 encoded sample plain-text file ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ Markus Kuhn [ˈmaʳkʊs kuːn] — 2002-07-25 The ASCII compatible UTF-8 encoding used in this plain-text file is defined in Unicode, ISO 10646-1, and RFC 2279. Using Unicode/UTF-8, you can write in emails and source code things such as Mathematics and sciences: ∮ E⋅da = Q, n → ∞, ∑ f(i) = ∏ g(i), ⎧⎡⎛┌─────┐⎞⎤⎫ ⎪⎢⎜│a²+b³ ⎟⎥⎪ ∀x∈ℝ: ⌈x⌉ = −⌊−x⌋, α ∧ ¬β = ¬(¬α ∨ β), ⎪⎢⎜│───── ⎟⎥⎪ ⎪⎢⎜⎷ c₈ ⎟⎥⎪ ℕ ⊆ ℕ₀ ⊂ ℤ ⊂ ℚ ⊂ ℝ ⊂ ℂ, ⎨⎢⎜ ⎟⎥⎬ ⎪⎢⎜ ∞ ⎟⎥⎪ ⊥ < a ≠ b ≡ c ≤ d ≪ ⊤ ⇒ (⟦A⟧ ⇔ ⟪B⟫), ⎪⎢⎜ ⎲ ⎟⎥⎪ ⎪⎢⎜ ⎳aⁱ-bⁱ⎟⎥⎪ 2H₂ + O₂ ⇌ 2H₂O, R = 4.7 kΩ, ⌀ 200 mm ⎩⎣⎝i=1 ⎠⎦⎭ Linguistics and dictionaries: ði ıntəˈnæʃənəl fəˈnɛtık əsoʊsiˈeıʃn Y [ˈʏpsilɔn], Yen [jɛn], Yoga [ˈjoːgɑ] APL: ((V⍳V)=⍳⍴V)/V←,V ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈ Nicer typography in plain text files: ╔══════════════════════════════════════════╗ ║ ║ ║ • ‘single’ and “double” quotes ║ ║ ║ ║ • Curly apostrophes: “We’ve been here” ║ ║ ║ ║ • Latin-1 apostrophe and accents: '´` ║ ║ ║ ║ • ‚deutsche‘ „Anführungszeichen“ ║ ║ ║ ║ • †, ‡, ‰, •, 3–4, —, −5/+5, ™, … ║ ║ ║ ║ • ASCII safety test: 1lI|, 0OD, 8B ║ ║ ╭─────────╮ ║ ║ • the euro symbol: │ 14.95 € │ ║ ║ ╰─────────╯ ║ ╚══════════════════════════════════════════╝ Combining characters: STARGΛ̊TE SG-1, a = v̇ = r̈, a⃑ ⊥ b⃑ Greek (in Polytonic): The Greek anthem: Σὲ γνωρίζω ἀπὸ τὴν κόψη τοῦ σπαθιοῦ τὴν τρομερή, σὲ γνωρίζω ἀπὸ τὴν ὄψη ποὺ μὲ βία μετράει τὴ γῆ. ᾿Απ᾿ τὰ κόκκαλα βγαλμένη τῶν ῾Ελλήνων τὰ ἱερά καὶ σὰν πρῶτα ἀνδρειωμένη χαῖρε, ὦ χαῖρε, ᾿Ελευθεριά! From a speech of Demosthenes in the 4th century BC: Οὐχὶ ταὐτὰ παρίσταταί μοι γιγνώσκειν, ὦ ἄνδρες ᾿Αθηναῖοι, ὅταν τ᾿ εἰς τὰ πράγματα ἀποβλέψω καὶ ὅταν πρὸς τοὺς λόγους οὓς ἀκούω· τοὺς μὲν γὰρ λόγους περὶ τοῦ τιμωρήσασθαι Φίλιππον ὁρῶ γιγνομένους, τὰ δὲ πράγματ᾿ εἰς τοῦτο προήκοντα, ὥσθ᾿ ὅπως μὴ πεισόμεθ᾿ αὐτοὶ πρότερον κακῶς σκέψασθαι δέον. οὐδέν οὖν ἄλλο μοι δοκοῦσιν οἱ τὰ τοιαῦτα λέγοντες ἢ τὴν ὑπόθεσιν, περὶ ἧς βουλεύεσθαι, οὐχὶ τὴν οὖσαν παριστάντες ὑμῖν ἁμαρτάνειν. ἐγὼ δέ, ὅτι μέν ποτ᾿ ἐξῆν τῇ πόλει καὶ τὰ αὑτῆς ἔχειν ἀσφαλῶς καὶ Φίλιππον τιμωρήσασθαι, καὶ μάλ᾿ ἀκριβῶς οἶδα· ἐπ᾿ ἐμοῦ γάρ, οὐ πάλαι γέγονεν ταῦτ᾿ ἀμφότερα· νῦν μέντοι πέπεισμαι τοῦθ᾿ ἱκανὸν προλαβεῖν ἡμῖν εἶναι τὴν πρώτην, ὅπως τοὺς συμμάχους σώσομεν. ἐὰν γὰρ τοῦτο βεβαίως ὑπάρξῃ, τότε καὶ περὶ τοῦ τίνα τιμωρήσεταί τις καὶ ὃν τρόπον ἐξέσται σκοπεῖν· πρὶν δὲ τὴν ἀρχὴν ὀρθῶς ὑποθέσθαι, μάταιον ἡγοῦμαι περὶ τῆς τελευτῆς ὁντινοῦν ποιεῖσθαι λόγον. Δημοσθένους, Γ´ ᾿Ολυνθιακὸς Georgian: From a Unicode conference invitation: გთხოვთ ახლავე გაიაროთ რეგისტრაცია Unicode-ის მეათე საერთაშორისო კონფერენციაზე დასასწრებად, რომელიც გაიმართება 10-12 მარტს, ქ. მაინცში, გერმანიაში. კონფერენცია შეჰკრებს ერთად მსოფლიოს ექსპერტებს ისეთ დარგებში როგორიცაა ინტერნეტი და Unicode-ი, ინტერნაციონალიზაცია და ლოკალიზაცია, Unicode-ის გამოყენება ოპერაციულ სისტემებსა, და გამოყენებით პროგრამებში, შრიფტებში, ტექსტების დამუშავებასა და მრავალენოვან კომპიუტერულ სისტემებში. Russian: From a Unicode conference invitation: Зарегистрируйтесь сейчас на Десятую Международную Конференцию по Unicode, которая состоится 10-12 марта 1997 года в Майнце в Германии. Конференция соберет широкий круг экспертов по вопросам глобального Интернета и Unicode, локализации и интернационализации, воплощению и применению Unicode в различных операционных системах и программных приложениях, шрифтах, верстке и многоязычных компьютерных системах. Thai (UCS Level 2): Excerpt from a poetry on The Romance of The Three Kingdoms (a Chinese classic 'San Gua'): [----------------------------|------------------------] ๏ แผ่นดินฮั่นเสื่อมโทรมแสนสังเวช พระปกเกศกองบู๊กู้ขึ้นใหม่ สิบสองกษัตริย์ก่อนหน้าแลถัดไป สององค์ไซร้โง่เขลาเบาปัญญา ทรงนับถือขันทีเป็นที่พึ่ง บ้านเมืองจึงวิปริตเป็นนักหนา โฮจิ๋นเรียกทัพทั่วหัวเมืองมา หมายจะฆ่ามดชั่วตัวสำคัญ เหมือนขับไสไล่เสือจากเคหา รับหมาป่าเข้ามาเลยอาสัญ ฝ่ายอ้องอุ้นยุแยกให้แตกกัน ใช้สาวนั้นเป็นชนวนชื่นชวนใจ พลันลิฉุยกุยกีกลับก่อเหตุ ช่างอาเพศจริงหนาฟ้าร้องไห้ ต้องรบราฆ่าฟันจนบรรลัย ฤๅหาใครค้ำชูกู้บรรลังก์ ฯ (The above is a two-column text. If combining characters are handled correctly, the lines of the second column should be aligned with the | character above.) Ethiopian: Proverbs in the Amharic language: ሰማይ አይታረስ ንጉሥ አይከሰስ። ብላ ካለኝ እንደአባቴ በቆመጠኝ። ጌጥ ያለቤቱ ቁምጥና ነው። ደሀ በሕልሙ ቅቤ ባይጠጣ ንጣት በገደለው። የአፍ ወለምታ በቅቤ አይታሽም። አይጥ በበላ ዳዋ ተመታ። ሲተረጉሙ ይደረግሙ። ቀስ በቀስ፥ ዕንቁላል በእግሩ ይሄዳል። ድር ቢያብር አንበሳ ያስር። ሰው እንደቤቱ እንጅ እንደ ጉረቤቱ አይተዳደርም። እግዜር የከፈተውን ጉሮሮ ሳይዘጋው አይድርም። የጎረቤት ሌባ፥ ቢያዩት ይስቅ ባያዩት ያጠልቅ። ሥራ ከመፍታት ልጄን ላፋታት። ዓባይ ማደሪያ የለው፥ ግንድ ይዞ ይዞራል። የእስላም አገሩ መካ የአሞራ አገሩ ዋርካ። ተንጋሎ ቢተፉ ተመልሶ ባፉ። ወዳጅህ ማር ቢሆን ጨርስህ አትላሰው። እግርህን በፍራሽህ ልክ ዘርጋ። Runes: ᚻᛖ ᚳᚹᚫᚦ ᚦᚫᛏ ᚻᛖ ᛒᚢᛞᛖ ᚩᚾ ᚦᚫᛗ ᛚᚪᚾᛞᛖ ᚾᚩᚱᚦᚹᛖᚪᚱᛞᚢᛗ ᚹᛁᚦ ᚦᚪ ᚹᛖᛥᚫ (Old English, which transcribed into Latin reads 'He cwaeth that he bude thaem lande northweardum with tha Westsae.' and means 'He said that he lived in the northern land near the Western Sea.') Braille: ⡌⠁⠧⠑ ⠼⠁⠒ ⡍⠜⠇⠑⠹⠰⠎ ⡣⠕⠌ ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠙⠑⠁⠙⠒ ⠞⠕ ⠃⠑⠛⠔ ⠺⠊⠹⠲ ⡹⠻⠑ ⠊⠎ ⠝⠕ ⠙⠳⠃⠞ ⠱⠁⠞⠑⠧⠻ ⠁⠃⠳⠞ ⠹⠁⠞⠲ ⡹⠑ ⠗⠑⠛⠊⠌⠻ ⠕⠋ ⠙⠊⠎ ⠃⠥⠗⠊⠁⠇ ⠺⠁⠎ ⠎⠊⠛⠝⠫ ⠃⠹ ⠹⠑ ⠊⠇⠻⠛⠹⠍⠁⠝⠂ ⠹⠑ ⠊⠇⠻⠅⠂ ⠹⠑ ⠥⠝⠙⠻⠞⠁⠅⠻⠂ ⠁⠝⠙ ⠹⠑ ⠡⠊⠑⠋ ⠍⠳⠗⠝⠻⠲ ⡎⠊⠗⠕⠕⠛⠑ ⠎⠊⠛⠝⠫ ⠊⠞⠲ ⡁⠝⠙ ⡎⠊⠗⠕⠕⠛⠑⠰⠎ ⠝⠁⠍⠑ ⠺⠁⠎ ⠛⠕⠕⠙ ⠥⠏⠕⠝ ⠰⡡⠁⠝⠛⠑⠂ ⠋⠕⠗ ⠁⠝⠹⠹⠔⠛ ⠙⠑ ⠡⠕⠎⠑ ⠞⠕ ⠏⠥⠞ ⠙⠊⠎ ⠙⠁⠝⠙ ⠞⠕⠲ ⡕⠇⠙ ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ ⡍⠔⠙⠖ ⡊ ⠙⠕⠝⠰⠞ ⠍⠑⠁⠝ ⠞⠕ ⠎⠁⠹ ⠹⠁⠞ ⡊ ⠅⠝⠪⠂ ⠕⠋ ⠍⠹ ⠪⠝ ⠅⠝⠪⠇⠫⠛⠑⠂ ⠱⠁⠞ ⠹⠻⠑ ⠊⠎ ⠏⠜⠞⠊⠊⠥⠇⠜⠇⠹ ⠙⠑⠁⠙ ⠁⠃⠳⠞ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ ⡊ ⠍⠊⠣⠞ ⠙⠁⠧⠑ ⠃⠑⠲ ⠔⠊⠇⠔⠫⠂ ⠍⠹⠎⠑⠇⠋⠂ ⠞⠕ ⠗⠑⠛⠜⠙ ⠁ ⠊⠕⠋⠋⠔⠤⠝⠁⠊⠇ ⠁⠎ ⠹⠑ ⠙⠑⠁⠙⠑⠌ ⠏⠊⠑⠊⠑ ⠕⠋ ⠊⠗⠕⠝⠍⠕⠝⠛⠻⠹ ⠔ ⠹⠑ ⠞⠗⠁⠙⠑⠲ ⡃⠥⠞ ⠹⠑ ⠺⠊⠎⠙⠕⠍ ⠕⠋ ⠳⠗ ⠁⠝⠊⠑⠌⠕⠗⠎ ⠊⠎ ⠔ ⠹⠑ ⠎⠊⠍⠊⠇⠑⠆ ⠁⠝⠙ ⠍⠹ ⠥⠝⠙⠁⠇⠇⠪⠫ ⠙⠁⠝⠙⠎ ⠩⠁⠇⠇ ⠝⠕⠞ ⠙⠊⠌⠥⠗⠃ ⠊⠞⠂ ⠕⠗ ⠹⠑ ⡊⠳⠝⠞⠗⠹⠰⠎ ⠙⠕⠝⠑ ⠋⠕⠗⠲ ⡹⠳ ⠺⠊⠇⠇ ⠹⠻⠑⠋⠕⠗⠑ ⠏⠻⠍⠊⠞ ⠍⠑ ⠞⠕ ⠗⠑⠏⠑⠁⠞⠂ ⠑⠍⠏⠙⠁⠞⠊⠊⠁⠇⠇⠹⠂ ⠹⠁⠞ ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ (The first couple of paragraphs of "A Christmas Carol" by Dickens) Compact font selection example text: ABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789 abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ –—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвгд ∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა Greetings in various languages: Hello world, Καλημέρα κόσμε, コンニチハ Box drawing alignment tests: █ ▉ ╔══╦══╗ ┌──┬──┐ ╭──┬──╮ ╭──┬──╮ ┏━━┳━━┓ ┎┒┏┑ ╷ ╻ ┏┯┓ ┌┰┐ ▊ ╱╲╱╲╳╳╳ ║┌─╨─┐║ │╔═╧═╗│ │╒═╪═╕│ │╓─╁─╖│ ┃┌─╂─┐┃ ┗╃╄┙ ╶┼╴╺╋╸┠┼┨ ┝╋┥ ▋ ╲╱╲╱╳╳╳ ║│╲ ╱│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╿ │┃ ┍╅╆┓ ╵ ╹ ┗┷┛ └┸┘ ▌ ╱╲╱╲╳╳╳ ╠╡ ╳ ╞╣ ├╢ ╟┤ ├┼─┼─┼┤ ├╫─╂─╫┤ ┣┿╾┼╼┿┫ ┕┛┖┚ ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳ ║│╱ ╲│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╽ │┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▎ ║└─╥─┘║ │╚═╤═╝│ │╘═╪═╛│ │╙─╀─╜│ ┃└─╂─┘┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▏ ╚══╩══╝ └──┴──┘ ╰──┴──╯ ╰──┴──╯ ┗━━┻━━┛ ▗▄▖▛▀▜ └╌╌┘ ╎ ┗╍╍┛ ┋ ▁▂▃▄▅▆▇█ ▝▀▘▙▄▟ tmate-1.8.10/tools/ansicode.txt000066400000000000000000001210751242461015400164060ustar00rootroot00000000000000Summary of ANSI standards for ASCII terminals Joe Smith, 18-May-84 Contents: 1. Overview and Definitions 2. General rules for interpreting an ESCape Sequence 3. General rules for interpreting a Control Sequence 4. C0 and C1 control codes in numeric order 5. Two and three-character ESCape Sequences in numeric order 6. Control Sequences in numeric order 7. VT100 emulation requirements The VT100 USER GUIDE and ANSI standard X3.64-1979 both list the ANSI ESCape sequences in alphabetic order by mnemonic, but do not have a have a cross reference in order by ASCII code. This paper lists the combination of all definitions from the three ANSI standards in numeric order. For a description of the advantages of using these standards, see the article "Toward Standardized Video Terminals" in the April-1984 issue of BYTE magazine. ANSI X3.4-1977 defines the 7-bit ASCII character set (C0 and G0). It was written in 1968, revised in 1977, and explains the decisions made in laying out the ASCII code. In particular, it explains why ANSI chose to make ASCII incompatible with EBCDIC in order to make it self-consistant. ANSI X3.41-1974 introduces the idea of an 8-bit ASCII character set (C1 and G1 in addition to the existing C0 and G0). It describes how to use the 8-bit features in a 7-bit environment. X3.41 defines the format of all ESCape sequences, but defines only the 3-character ones with a parameter character in the middle. These instruct the terminal how to interpret the C0, G0, C1, and G1 characters (such as by selecting different character-set ROMs). Note: NAPLPS does videotex graphics by redefining the C1 set and selecting alternate G0, G1, G2, and G3 sets. See the February 1983 issue of BYTE magazine for details. ANSI X3.64-1979 defines the remaining ESCape sequences. It defines all the C1 control characters, and specifies that certain two-character ESCape sequences in the 7-bit environment are to act exactly like the 8-bit C1 control set. X3.64 introduces the idea of a Control-Sequence, which starts with CSI character, has an indefinite length, and is terminated by an alphabetic character. The VT100 was one of the first terminals to implement this standard. Definitions: Control Character - A single character with an ASCII code with the range of 000 to 037 and 200 to 237 octal, 00 to 1F and 80 to 9F hex. Escape Sequence - A two or three character string staring with ESCape. (Four or more character strings are allowed but not defined.) Control Sequence - A string starting with CSI (233 octal, 9B hex) or with ESCape Left-Bracket, and terminated by an alphabetic character. Any number of parameter characters (digits 0 to 9, semicolon, and question mark) may appear within the Control Sequence. The terminating character may be preceded by an intermediate character (such as space). Character classifications: C0 Control 000-037 octal, 00-1F hex (G0 is 041-176 octal, 21-7E hex) SPACE 040+240 octal, 20+A0 hex Always and everywhere a blank space Intermediate 040-057 octal, 20-2F hex !"#$%&'()*+,-./ Parameters 060-077 octal, 30-3F hex 0123456789:;<=>? Uppercase 100-137 octal, 40-5F hex @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ Lowercase 140-176 octal, 60-7E hex `abcdefghijlkmnopqrstuvwxyz{|}~ Alphabetic 100-176 octal, 40-7E hex (all of upper and lower case) Delete 177 octal, 7F hex Always and everywhere ignored C1 Control 200-237 octal, 80-9F hex 32 additional control characters G1 Displayable 241-376 octal, A1-FE hex 94 additional displayable characters Special 240+377 octal, A0+FF hex Same as SPACE and DELETE Note that in this paper, the terms uppercase, lowercase, and alphabetics include more characters than just A to Z. ------------------------------------------------------------------------------ General rules for interpreting an ESCape Sequence: An ESCape Sequence starts with the ESC character (033 octal, 1B hex). The length of the ESCape Sequence depends on the character that immediately follows the ESCape. If the next character is C0 control: Interpret it first, then resume processing ESCape sequence. Example: CR, LF, XON, and XOFF work as normal within an ESCape sequence. Intermediate: Expect zero or more intermediates, a parameter terminates a private function, an alphabetic terminates a standard sequence. Example: ESC ( A defines standard character set, ESC ( 0 a DEC set. Parameter: End of a private 2-character escape sequence. Example: ESC = sets special keypad mode, ESC > clears it. Uppercase: Translate it into a C1 control character and act on it. Example: ESC D does indexes down, ESC M indexes up. (CSI is special) Lowercase: End of a standard 2-character escape sequence. Example: ESC c resets the terminal. Delete: Ignore it, and continue interpreting the ESCape sequence C1 and G1: Treat the same as their 7-bit counterparts Note that CSI is the two-character sequence ESCape left-bracket or the 8-bit C1 code of 233 octal, 9B hex. CSI introduces a Control Sequence, which continues until an alphabetic character is received. General rules for interpreting a Control Sequence: 1) It starts with CSI, the Control Sequence Introducer. 2) It contains any number of parameter characters (0123456789:;<=>?). 3) It terminates with an alphabetic character. 4) Intermediate characters (if any) immediately precede the terminator. If the first character after CSI is one of "<=>?" (074-077 octal, 3C-3F hex), then Control Sequence is to be interpreted according to private standards (such as setting and resetting modes not defined by ANSI). The terminal should expect any number of numeric parameters, separated by semicolons (073 octal, 3B hex). Only after the terminating alphabetic character is received should the terminal act on the Control Sequence. ============================================================================= C0 set of 7-bit control characters (from ANSI X3.4-1977). Oct Hex Name * (* marks function used in DEC VT series or LA series terminals) --- -- - --- - -------------------------------------------------------------- 000 00 @ NUL * Null filler, terminal should ignore this character 001 01 A SOH Start of Header 002 02 B STX Start of Text, implied end of header 003 03 C ETX End of Text, causes some terminal to respond with ACK or NAK 004 04 D EOT End of Transmission 005 05 E ENQ * Enquiry, causes terminal to send ANSWER-BACK ID 006 06 F ACK Acknowledge, usually sent by terminal in response to ETX 007 07 G BEL * Bell, triggers the bell, buzzer, or beeper on the terminal 010 08 H BS * Backspace, can be used to define overstruck characters 011 09 I HT * Horizontal Tabulation, move to next predetermined position 012 0A J LF * Linefeed, move to same position on next line (see also NL) 013 0B K VT * Vertical Tabulation, move to next predetermined line 014 0C L FF * Form Feed, move to next form or page 015 0D M CR * Carriage Return, move to first character of current line 016 0E N SO * Shift Out, switch to G1 (other half of character set) 017 0F O SI * Shift In, switch to G0 (normal half of character set) 020 10 P DLE Data Link Escape, interpret next control character specially 021 11 Q XON * (DC1) Terminal is allowed to resume transmitting 022 12 R DC2 Device Control 2, causes ASR-33 to activate paper-tape reader 023 13 S XOFF* (DC2) Terminal must pause and refrain from transmitting 024 14 T DC4 Device Control 4, causes ASR-33 to deactivate paper-tape reader 025 15 U NAK Negative Acknowledge, used sometimes with ETX and ACK 026 16 V SYN Synchronous Idle, used to maintain timing in Sync communication 027 17 W ETB End of Transmission block 030 18 X CAN * Cancel (makes VT100 abort current escape sequence if any) 031 19 Y EM End of Medium 032 1A Z SUB * Substitute (VT100 uses this to display parity errors) 033 1B [ ESC * Prefix to an ESCape sequence 034 1C \ FS File Separator 035 1D ] GS Group Separator 036 1E ^ RS * Record Separator (sent by VT132 in block-transfer mode) 037 1F _ US Unit Separator 040 20 SP * Space (should never be defined to be otherwise) 177 7F DEL * Delete, should be ignored by terminal ============================================================================== C1 set of 8-bit control characters (from ANSI X3.64-1979) Oct Hex Name * (* marks function used in DEC VT series or LA series terminals) --- -- - --- - -------------------------------------------------------------- 200 80 @ Reserved for future standardization 201 81 A Reserved 202 82 B Reserved 203 83 C Reserved 204 84 D IND * Index, moves down one line same column regardless of NL 205 85 E NEL * NEw Line, moves done one line and to first column (CR+LF) 206 86 F SSA Start of Selected Area to be sent to auxiliary output device 207 87 G ESA End of Selected Area to be sent to auxiliary output device 210 88 H HTS * Horizontal Tabulation Set at current position 211 89 I HTJ Hor Tab Justify, moves string to next tab position 212 8A J VTS Vertical Tabulation Set at current line 213 8B K PLD Partial Line Down (subscript) 214 8C L PLU Partial Line Up (superscript) 215 8D M RI * Reverse Index, go up one line, reverse scroll if necessary 216 8E N SS2 * Single Shift to G2 217 8F O SS3 * Single Shift to G3 (VT100 uses this for sending PF keys) 220 90 P DCS * Device Control String, terminated by ST (VT125 enters graphics) 221 91 Q PU1 Private Use 1 222 92 R PU2 Private Use 2 223 93 S STS Set Transmit State 224 94 T CCH Cancel CHaracter, ignore previous character 225 95 U MW Message Waiting, turns on an indicator on the terminal 226 96 V SPA Start of Protected Area 227 97 W EPA End of Protected Area 230 98 X Reserved for for future standard 231 99 Y Reserved 232 9A Z * Reserved, but causes DEC terminals to respond with DA codes 233 9B [ CSI * Control Sequence Introducer (described in a seperate table) 234 9C \ ST * String Terminator (VT125 exits graphics) 235 9D ] OSC Operating System Command (reprograms intelligent terminal) 236 9E ^ PM Privacy Message (password verification), terminated by ST 237 9F _ APC Application Program Command (to word processor), term by ST ============================================================================== Character set selection sequences (from ANSI X3.41-1974) All are 3 characters long (including the ESCape). Alphabetic characters as 3rd character are defined by ANSI, parameter characters as 3rd character may be interpreted differently by each terminal manufacturer. Oct Hex * (* marks function used in DEC VT series or LA series terminals) --- -- -- - ------------------------------------------------------------------ 040 20 ANNOUNCER - Determines whether to use 7-bit or 8-bit ASCII A G0 only will be used. Ignore SI, SO, and G1. B G0 and G1 used internally. SI and SO affect G0, G1 is ignored. C G0 and G1 in an 8-bit only environment. SI and SO are ignored. D G0 and G1 are used, SI and SO affect G0. E F * 7-bit transmission, VT240/PRO350 sends CSI as two characters ESC [ G * 8-bit transmission, VT240/PRO350 sends CSI as single 8-bit character 041 21 ! Select C0 control set (choice of 63 standard, 16 private) 042 22 " Select C1 control set (choice of 63 standard, 16 private) 043 23 # Translate next character to a special single character #3 * DECDHL1 - Double height line, top half #4 * DECDHL2 - Double height line, bottom half #5 * DECSWL - Single width line #6 * DECDWL - Double width line #7 * DECHCP - Make a hardcopy of the graphics screen (GIGI,VT125,VT241) #8 * DECALN - Alignment display, fill screen with "E" to adjust focus 044 24 $ MULTIBYTE CHARACTERS - Displayable characters require 2-bytes each 045 25 % SPECIAL INTERPRETATION - Such as 9-bit data 046 26 & Reserved for future standardization 047 27 ' Reserved for future standardization 050 28 ( * SCS - Select G0 character set (choice of 63 standard, 16 private) (0 * DEC VT100 line drawing set (affects lowercase characters) (1 * DEC Alternate character ROM set (RAM set on GIGI and VT220) (2 * DEC Alternate character ROM set with line drawing (5 * DEC Finnish on LA100 (6 * DEC Norwegian/Danish on LA100 (7 * DEC Swedish on LA100 (9 * DEC French Canadian (< * DEC supplemental graphics (everything not in USASCII) (A * UKASCII (British pound sign) (B * USASCII (American pound sign) (C * ISO Finnish on LA120 (E * ISO Norwegian/Danish on LA120 (H * ISO Swedish on LA120 (K * ISO German on LA100,LA120 (R * ISO French on LA100,LA120 (Y * ISO Italian on LA100 (Z * ISO Spanish on LA100 051 29 ) * SCS - Select G1 character set (choice of 63 standard, 16 private) * (same character sets as listed under G0) 052 2A * * SCS - Select G2 character set * (same character sets as listed under G0) 053 2B + * SCS - Select G3 character set * (same character sets as listed under G0) 054 2C , SCS - Select G0 character set (additional 63+16 sets) 055 2D - SCS - Select G1 character set (additional 63+16 sets) 056 2E . SCS - Select G2 character set 057 2F / SCS - Select G3 character set ============================================================================== Private two-character escape sequences (allowed by ANSI X3.41-1974) These can be defined differently by each terminal manufacturer. Oct Hex * (* marks function used in DEC VT series or LA series terminals) --- -- - - ------------------------------------------------------------------ 060 30 0 061 31 1 DECGON graphics on for VT105, DECHTS horiz tab set for LA34/LA120 062 32 2 DECGOFF graphics off VT105, DECCAHT clear all horz tabs LA34/LA120 063 33 3 DECVTS - set vertical tab for LA34/LA120 064 34 4 DECCAVT - clear all vertical tabs for LA34/LA120 065 35 5 * DECXMT - Host requests that VT132 transmit as if ENTER were pressed 066 36 6 067 37 7 * DECSC - Save cursor position and character attributes 070 38 8 * DECRC - Restore cursor and attributes to previously saved position 071 39 9 072 3A : 073 3B ; 074 3C < * DECANSI - Switch from VT52 mode to VT100 mode 075 3D = * DECKPAM - Set keypad to applications mode (ESCape instead of digits) 076 3E > * DECKPNM - Set keypad to numeric mode (digits intead of ESCape seq) 077 3F ? DCS Device Control Strings used by DEC terminals (ends with ST) Pp = Start ReGIS graphics (VT125, GIGI, VT240, PRO350) Pq = Start SIXEL graphics (screen dump to LA34, LA100, screen load to VT125) Pr = SET-UP data for GIGI, $PrVC0$\ disables both visible cursors. Ps = Reprogram keys on the GIGI, $P0sDIR$\ makes keypad 0 send "DIR" 0-9=digits on keypad, 10=ENTER, 11=minus, 12=comma, 13=period, 14-17=PF1-PF4, 18-21=cursor keys. Enabled by $[?23h (PK1). Pt = Start VT105 graphics on a VT125 ============================================================================== Standard two-character escape sequences (defined by ANSI X3.64-1979) 100 40 @ See description of C1 control characters An ESCape followed by one of these uppercase characters is translated to an 8-bit C1 control character before being interpreted. 220 90 P DCS - Device Control String, terminated by ST - see table above. 133 5B [ CSI - Control Sequence Introducer - see table below. 137 5F _ See description of C1 control characters ============================================================================== Indepenent control functions (from Appendix E of X3.64-1977). These four controls have the same meaning regardless of the current definition of the C0 and C1 control sets. Each control is a two-character ESCape sequence, the 2nd character is lowercase. Oct Hex * (* marks function used in DEC VT series or LA series terminals) --- -- - - -------------------------------------------------------------------- 140 60 ` DMI - Disable Manual Input 141 61 a INT - INTerrupt the terminal and do special action 142 62 b EMI - Enable Manual Input 143 63 c * RIS - Reset to Initial State (VT100 does a power-on reset) ... The remaining lowercase characters are reserved by ANSI. 153 6B k NAPLPS lock-shift G1 to GR 154 6C l NAPLPS lock-shift G2 to GR 155 6D m NAPLPS lock-shift G3 to GR 156 6E n * LS2 - Shift G2 to GL (extension of SI) VT240,NAPLPS 157 6F o * LS3 - Shift G3 to GL (extension of SO) VT240,NAPLPS ... The remaining lowercase characters are reserved by ANSI. 174 7C | * LS3R - VT240 lock-shift G3 to GR 175 7D } * LS2R - VT240 lock-shift G2 to GR 176 7E ~ * LS1R - VT240 lock-shift G1 to GR ============================================================================== Control Sequences (defined by ANSI X3.64-1979) Control Sequences are started by either ESC [ or CSI and are terminated by an "alphabetic" character (100 to 176 octal, 40 to 7E hex). Intermediate characters are space through slash (40 to 57 octal, 20 to 2F hex) and parameter characters are zero through question mark (60 to 77 octal, 30 to 3F hex, including digits and semicolon). Parameters consist of zero or more decimal numbers separated by semicolons. Leading zeros are optional, leading blanks are not allowed. If no digits precede the final character, the default parameter is used. Many functions treat a parameter of 0 as if it were 1. Oct Hex * (* marks function used in DEC VT series or LA series terminals) --- -- - - -------------------------------------------------------------------- 100 40 @ ICH - Insert CHaracter [10@ = Make room for 10 characters at current position 101 41 A * CUU - CUrsor Up * [A = Move up one line, stop at top of screen, [9A = move up 9 102 42 B * CUD - CUrsor Down * [B = Move down one line, stop at bottom of screen 103 43 C * CUF - CUrsor Forward * [C = Move forward one position, stop at right edge of screen 104 44 D * CUB - CUrsor Backward * [D = Same as BackSpace, stop at left edge of screen 105 45 E CNL - Cursor to Next Line [5E = Move to first position of 5th line down 106 46 F CPL - Cursor to Previous Line [5F = Move to first position of 5th line previous 107 47 G CHA - Cursor Horizontal position Absolute [40G = Move to column 40 of current line 110 48 H * CUP - CUrsor Position * [H = Home, [24;80H = Row 24, Column 80 111 49 I CHT - Cursor Horizontal Tabulation [I = Same as HT (Control-I), [3I = Go forward 3 tabs 112 4A J * ED - Erase in Display (cursor does not move) * [J = [0J = Erase from current position to end (inclusive) * [1J = Erase from beginning to current position (inclusive) * [2J = Erase entire display * [?0J = Selective erase in display ([?1J, [?2J similar) 113 4B K * EL - Erase in Line (cursor does not move) * [K = [0K = Erase from current position to end (inclusive) * [1K = Erase from beginning to current position * [2K = Erase entire current line * [?0K = Selective erase to end of line ([?1K, [?2K similar) 114 4C L * IL - Insert Line, current line moves down (VT102 series) [3L = Insert 3 lines if currently in scrolling region 115 4D M * DL - Delete Line, lines below current move up (VT102 series) [2M = Delete 2 lines if currently in scrolling region 116 4E N EF - Erase in Field (as bounded by protected fields) [0N, [1N, [2N act like [L but within currend field 117 4F O EA - Erase in qualified Area (defined by DAQ) [0O, [1O, [2O act like [J but within current area 120 50 P * DCH - Delete Character, from current position to end of field [4P = Delete 4 characters, VT102 series 121 51 Q SEM - Set Editing extent Mode (limits ICH and DCH) [0Q = [Q = Insert/delete character affects rest of display [1Q = ICH/DCH affect the current line only [2Q = ICH/DCH affect current field (between tab stops) only [3Q = ICH/DCH affect qualified area (between protected fields) 122 52 R * CPR - Cursor Position Report (from terminal to host) * [24;80R = Cursor is positioned at line 24 column 80 123 53 S SU - Scroll up, entire display is moved up, new lines at bottom [3S = Move everything up 3 lines, bring in 3 new lines 124 54 T SD - Scroll down, new lines inserted at top of screen [4T = Scroll down 4, bring previous lines back into view 125 55 U NP - Next Page (if terminal has more than 1 page of memory) [2U = Scroll forward 2 pages 126 56 V PP - Previous Page (if terminal remembers lines scrolled off top) [1V = Scroll backward 1 page 127 57 W CTC - Cursor Tabulation Control [0W = Set horizontal tab for current line at current position [1W = Set vertical tab stop for current line of current page [2W = Clear horiz tab stop at current position of current line [3W = Clear vert tab stop at current line of current page [4W = Clear all horiz tab stops on current line only [5W = Clear all horiz tab stops for the entire terminal [6W = Clear all vert tabs stops for the entire terminal 130 58 X ECH - Erase CHaracter [4X = Change next 4 characters to "erased" state 131 59 Y CVT - Cursor Vertical Tab [2Y = Move forward to 2nd following vertical tab stop 132 5A Z CBT - Cursor Back Tab [3Z = Move backwards to 3rd previous horizontal tab stop 133 5B [ Reserved for future standardization left bracket 134 5C \ Reserved reverse slant 135 5D ] Reserved right bracket 136 5E ^ Reserved circumflex 137 5F _ Reserved underscore 140 60 ` * HPA - Horizontal Position Absolute (depends on PUM) [720` = Move to 720 decipoints (1 inch) from left margin * [80` = Move to column 80 on LA120 141 61 a * HPR - Horizontal Position Relative (depends on PUM) [360a = Move 360 decipoints (1/2 inch) from current position * [40a = Move 40 columns to right of current position on LA120 142 62 b REP - REPeat previous displayable character [80b = Repeat character 80 times 143 63 c * DA - Device Attributes * [c = Terminal will identify itself * [?1;2c = Terminal is saying it is a VT100 with AVO * [>0c = Secondary DA request (distinguishes VT240 from VT220) 144 64 d * VPA - Vertical Position Absolute (depends on PUM) [90d = Move to 90 decipoints (1/8 inch) from top margin * [10d = Move to line 10 if before that else line 10 next page 145 65 e * VPR - Vertical Position Relative (depends on PUM) [720e = Move 720 decipoints (1 inch) down from current position * [6e = Advance 6 lines forward on LA120 146 66 f * HVP - Horizontal and Vertical Position (depends on PUM) [720,1440f = Move to 1 inch down and 2 inches over (decipoints) * [24;80f = Move to row 24 column 80 if PUM is set to character 147 67 g * TBC - Tabulation Clear * [0g = Clear horizontal tab stop at current position * [1g = Clear vertical tab stop at current line (LA120) * [2g = Clear all horizontal tab stops on current line only LA120 * [3g = Clear all horizontal tab stops in the terminal 150 68 h * SM - Set Mode (. means permanently set on VT100) [0h = Error, this command is ignored * [1h = GATM - Guarded Area Transmit Mode, send all (VT132) [2h = KAM - Keyboard Action Mode, disable keyboard input [3h = CRM - Control Representation Mode, show all control chars * [4h = IRM - Insertion/Replacement Mode, set insert mode (VT102) [5h = SRTM - Status Report Transfer Mode, report after DCS * [6h = ERM - ERasure Mode, erase protected and unprotected [7h = VEM - Vertical Editing Mode, IL/DL affect previous lines [8h, [9h are reserved [10h = HEM - Horizontal Editing mode, ICH/DCH/IRM go backwards [11h = PUM - Positioning Unit Mode, use decipoints for HVP/etc . [12h = SRM - Send Receive Mode, transmit without local echo [13h = FEAM - Format Effector Action Mode, FE's are stored [14h = FETM - Format Effector Transfer Mode, send only if stored [15h = MATM - Multiple Area Transfer Mode, send all areas * [16h = TTM - Transmit Termination Mode, send scrolling region [17h = SATM - Send Area Transmit Mode, send entire buffer [18h = TSM - Tabulation Stop Mode, lines are independent [19h = EBM - Editing Boundry Mode, all of memory affected * [20h = LNM - Linefeed Newline Mode, LF interpreted as CR LF * [?1h = DECCKM - Cursor Keys Mode, send ESC O A for cursor up * [?2h = DECANM - ANSI Mode, use ESC < to switch VT52 to ANSI * [?3h = DECCOLM - COLumn mode, 132 characters per line * [?4h = DECSCLM - SCrolL Mode, smooth scrolling * [?5h = DECSCNM - SCreeN Mode, black on white background * [?6h = DECOM - Origin Mode, line 1 is relative to scroll region * [?7h = DECAWM - AutoWrap Mode, start newline after column 80 * [?8h = DECARM - Auto Repeat Mode, key will autorepeat * [?9h = DECINLM - INterLace Mode, interlaced for taking photos * [?10h = DECEDM - EDit Mode, VT132 is in EDIT mode * [?11h = DECLTM - Line Transmit Mode, ignore TTM, send line [?12h = ? * [?13h = DECSCFDM - Space Compression/Field Delimiting on, * [?14h = DECTEM - Transmit Execution Mode, transmit on ENTER [?15h = ? * [?16h = DECEKEM - Edit Key Execution Mode, EDIT key is local [?17h = ? * [?18h = DECPFF - Print FormFeed mode, send FF after printscreen * [?19h = DECPEXT - Print Extent mode, print entire screen * [?20h = OV1 - Overstrike, overlay characters on GIGI * [?21h = BA1 - Local BASIC, GIGI to keyboard and screen * [?22h = BA2 - Host BASIC, GIGI to host computer * [?23h = PK1 - GIGI numeric keypad sends reprogrammable sequences * [?24h = AH1 - Autohardcopy before erasing or rolling GIGI screen * [?29h = - Use only the proper pitch for the LA100 font * [?38h = DECTEK - TEKtronix mode graphics 151 69 i * MC - Media Copy (printer port on VT102) * [0i = Send contents of text screen to printer [1i = Fill screen from auxiliary input (printer's keyboard) [2i = Send screen to secondary output device [3i = Fill screen from secondary input device * [4i = Turn on copying received data to primary output (VT125) * [4i = Received data goes to VT102 screen, not to its printer * [5i = Turn off copying received data to primary output (VT125) * [5i = Received data goes to VT102's printer, not its screen * [6i = Turn off copying received data to secondary output (VT125) * [7i = Turn on copying received data to secondary output (VT125) * [?0i = Graphics screen dump goes to graphics printer VT125,VT240 * [?1i = Print cursor line, terminated by CR LF * [?2i = Graphics screen dump goes to host computer VT125,VT240 * [?4i = Disable auto print * [?5i = Auto print, send a line at a time when linefeed received 152 6A j Reserved for future standardization 153 6B k Reserved for future standardization 154 6C l * RM - Reset Mode (. means permanently reset on VT100) * [1l = GATM - Transmit only unprotected characters (VT132) . [2l = KAM - Enable input from keyboard . [3l = CRM - Control characters are not displayable characters * [4l = IRM - Reset to replacement mode (VT102) . [5l = SRTM - Report only on command (DSR) * [6l = ERM - Erase only unprotected fields . [7l = VEM - IL/DL affect lines after current line [8l, [9l are reserved . [10l = HEM - ICH and IRM shove characters forward, DCH pulls . [11l = PUM - Use character positions for HPA/HPR/VPA/VPR/HVP [12l = SRM - Local echo - input from keyboard sent to screen . [13l = FEAM - HPA/VPA/SGR/etc are acted upon when received . [14l = FETM - Format Effectors are sent to the printer [15l = MATM - Send only current area if SATM is reset * [16l = TTM - Transmit partial page, up to cursor position [17l = SATM - Transmit areas bounded by SSA/ESA/DAQ . [18l = TSM - Setting a tab stop on one line affects all lines . [19l = EBM - Insert does not overflow to next page * [20l = LNM - Linefeed does not change horizontal position * [?1l = DECCKM - Cursor keys send ANSI cursor position commands * [?2l = DECANM - Use VT52 emulation instead of ANSI mode * [?3l = DECCOLM - 80 characters per line (erases screen) * [?4l = DECSCLM - Jump scrolling * [?5l = DECSCNM - Normal screen (white on black background) * [?6l = DECOM - Line numbers are independent of scrolling region * [?7l = DECAWM - Cursor remains at end of line after column 80 * [?8l = DECARM - Keys do not repeat when held down * [?9l = DECINLM - Display is not interlaced to avoid flicker * [?10l = DECEDM - VT132 transmits all key presses * [?11l = DECLTM - Send page or partial page depending on TTM [?12l = ? * [?13l = DECSCFDM - Don't suppress trailing spaces on transmit * [?14l = DECTEM - ENTER sends ESC S (STS) a request to send [?15l = ? * [?16l = DECEKEM - EDIT key transmits either $[10h or $[10l [?17l = ? * [?18l = DECPFF - Don't send a formfeed after printing screen * [?19l = DECPEXT - Print only the lines within the scroll region * [?20l = OV0 - Space is destructive, replace not overstrike, GIGI * [?21l = BA0 - No BASIC, GIGI is On-Line or Local * [?22l = BA0 - No BASIC, GIGI is On-Line or Local * [?23l = PK0 - Ignore reprogramming on GIGI keypad and cursors * [?24l = AH0 - No auto-hardcopy when GIGI screen erased * [?29l = Allow all character pitches on the LA100 * [?38l = DECTEK - Ignore TEKtronix graphics commands 155 6D m * SGR - Set Graphics Rendition (affects character attributes) * [0m = Clear all special attributes * [1m = Bold or increased intensity * [2m = Dim or secondary color on GIGI (superscript on XXXXXX) [3m = Italic (subscript on XXXXXX) * [4m = Underscore, [0;4m = Clear, then set underline only * [5m = Slow blink [6m = Fast blink (overscore on XXXXXX) * [7m = Negative image, [0;1;7m = Bold + Inverse [8m = Concealed (do not display character echoed locally) [9m = Reserved for future standardization * [10m = Select primary font (LA100) * [11m - [19m = Selete alternate font (LA100 has 11 thru 14) [20m = FRAKTUR (whatever that means) * [22m = Cancel bold or dim attribute only (VT220) * [24m = Cancel underline attribute only (VT220) * [25m = Cancel fast or slow blink attribute only (VT220) * [27m = Cancel negative image attribute only (VT220) * [30m = Write with black, [40m = Set background to black (GIGI) * [31m = Write with red, [41m = Set background to red * [32m = Write with green, [42m = Set background to green * [33m = Write with yellow, [43m = Set background to yellow * [34m = Write with blue, [44m = Set background to blue * [35m = Write with magenta, [45m = Set background to magenta * [36m = Write with cyan, [46m = Set background to cyan * [37m = Write with white, [47m = Set background to white [38m, [39m, [48m, [49m are reserved 156 6E n * DSR - Device Status Report * [0n = Terminal is ready, no malfunctions detected [1n = Terminal is busy, retry later [2n = Terminal is busy, it will send DSR when ready * [3n = Malfunction, please try again [4n = Malfunction, terminal will send DSR when ready * [5n = Command to terminal to report its status * [6n = Command to terminal requesting cursor position (CPR) * [?15n = Command to terminal requesting printer status, returns [?10n = OK, [?11n = not OK, [?13n = no printer. * [?25n = "Are User Defined Keys Locked?" (VT220) 157 6F o DAQ - Define Area Qualification starting at current position [0o = Accept all input, transmit on request [1o = Protected and guarded, accept no input, do not transmit [2o = Accept any printing character in this field [3o = Numeric only field [4o = Alphabetic (A-Z and a-z) only [5o = Right justify in area [3;6o = Zero fill in area [7o = Set horizontal tab stop, this is the start of the field [8o = Protected and unguarded, accept no input, do transmit [9o = Space fill in area ============================================================================== Private Control Sequences (allowed by ANSI X3.41-1974). These take parameter strings and terminate with the last half of lowercase. Oct Hex * (* marks function used in DEC VT series or LA series terminals) --- -- - - -------------------------------------------------------------------- 160 70 p * DECSTR - Soft Terminal Reset [!p = Soft Terminal Reset 161 71 q * DECLL - Load LEDs [0q = Turn off all, [?1;4q turns on L1 and L4, etc [154;155;157q = VT100 goes bonkers [2;23!q = Partial screen dump from GIGI to graphics printer [0"q = DECSCA Select Character Attributes off [1"q = DECSCA - designate set as non-erasable [2"q = DECSCA - designate set as erasable 162 72 r * DECSTBM - Set top and bottom margins (scroll region on VT100) [4;20r = Set top margin at line 4 and bottom at line 20 163 73 s * DECSTRM - Set left and right margins on LA100,LA120 [5;130s = Set left margin at column 5 and right at column 130 164 74 t * DECSLPP - Set physical lines per page [66t = Paper has 66 lines (11 inches at 6 per inch) 165 75 u * DECSHTS - Set many horizontal tab stops at once on LA100 [9;17;25;33;41;49;57;65;73;81u = Set standard tab stops 166 76 v * DECSVTS - Set many vertical tab stops at once on LA100 [1;16;31;45v = Set vert tabs every 15 lines 167 77 w * DECSHORP - Set horizontal pitch on LAxxx printers [1w = 10 characters per inch, [2w = 12 characters per inch [0w=10, [3w=13.2, [4w=16.5, [5w=5, [6w=6, [7w=6.6, [8w=8.25 170 78 x * DECREQTPARM - Request terminal parameters [3;5;2;64;64;1;0x = Report, 7 bit Even, 1200 baud, 1200 baud 171 79 y * DECTST - Invoke confidence test [2;1y = Power-up test on VT100 series (and VT100 part of VT125) [3;1y = Power-up test on GIGI (VK100) [4;1y = Power-up test on graphics portion of VT125 172 7A z * DECVERP - Set vertical pitch on LA100 [1z = 6 lines per inch, [2z = 8 lines per inch [0z=6, [3z=12, [4z=3, [5z=3, [6z=4 173 7B { Private 174 7C | * DECTTC - Transmit Termination Character [0| = No extra characters, [1| = terminate with FF 175 7D } * DECPRO - Define protected field on VT132 [0} = No protection, [1;4;5;7} = Any attribute is protected [254} = Characters with no attributes are protected 176 7E ~ * DECKEYS - Sent by special function keys [1~=FIND, [2~=INSERT, [3~=REMOVE, [4~=SELECT, [5~=PREV, [6~=NEXT [17~=F6...[34~=F20 ([23~=ESC,[24~=BS,[25~=LF,[28~=HELP,[29~=DO) 177 7F DELETE is always ignored ============================================================================== Control Sequences with intermediate characters (from ANSI X3.64-1979). Note that there is a SPACE character before the terminating alphabetic. Oct Hex * (* marks function used in DEC VT series or LA series terminals) --- -- - - -------------------------------------------------------------------- 100 40 @ SL - Scroll Left [4 @ = Move everything over 4 columns, 4 new columns at right 101 41 A SR - Scroll Right [2 A = Move everything over 2 columns, 2 new columns at left 102 42 B GSM - Graphic Size Modification [110;50 B = Make 110% high, 50% wide 103 43 C GSS - Graphic Size Selection [120 C = Make characters 120 decipoints (1/6 inch) high 104 44 D FNT - FoNT selection (used by SGR, [10m thru [19m) [0;23 D = Make primary font be registered font #23 105 45 E TSS - Thin Space Specification [36 E = Define a thin space to be 36 decipoints (1/20 inch) 106 46 F JFY - JustiFY, done by the terminal/printer [0 E = No justification [1 E = Fill, bringing words up from next line if necessary [2 E = Interword spacing, adjust spaces between words [3 E = Letter spacing, adjust width of each letter [4 E = Use hyphenation [5 E = Flush left margin [6 E = Center following text between margins (until [0 E) [7 E = Flush right margin [8 E = Italian form (underscore instead of hyphen) 107 47 G SPI - SPacing Increment (in decipoints) [120;72 G = 6 per inch vertical, 10 per inch horizontal 110 48 H QUAD- Do quadding on current line of text (typography) [0 H = Flush left, [1 H = Flush left and fill with leader [2 H = Center, [3 H = Center and fill with leader [4 H = Flush right, [5 H = Flush right and fill with leader 111 49 I Reserved for future standardization 157 67 o Reserved for future standardization 160 70 p Private use ... May be defined by the printer manufacturer 176 7E ~ Private use 177 7F DELETE is always ignored ============================================================================== Minimum requirements for VT100 emulation: 1) To act as a passive display, implement the 4 cursor commands, the 2 erase commands, direct cursor addressing, and at least inverse characters. The software should be capable of handling strings with 16 numeric parameters with values in the range of 0 to 255. [A Move cursor up one row, stop if a top of screen [B Move cursor down one row, stop if at bottom of screen [C Move cursor forward one column, stop if at right edge of screen [D Move cursor backward one column, stop if at left edge of screen [H Home to row 1 column 1 (also [1;1H) [J Clear from current position to bottom of screen [K Clear from current position to end of line [24;80H Position to line 24 column 80 (any line 1 to 24, any column 1 to 132) [0m Clear attributes to normal characters [7m Add the inverse video attribute to succeeding characters [0;7m Set character attributes to inverse video only 2) To enter data in VT100 mode, implement the 4 cursor keys and the 4 PF keys. It must be possible to enter ESC, TAB, BS, DEL, and LF from the keyboard. [A Sent by the up-cursor key (alternately ESC O A) [B Sent by the down-cursor key (alternately ESC O B) [C Sent by the right-cursor key (alternately ESC O C) [D Sent by the left-cursor key (alternately ESC O D) OP PF1 key sends ESC O P OQ PF2 key sends ESC O Q OR PF3 key sends ESC O R OS PF3 key sends ESC O S [c Request for the terminal to identify itself [?1;0c VT100 with memory for 24 by 80, inverse video character attribute [?1;2c VT100 capable of 132 column mode, with bold+blink+underline+inverse 3) When doing full-screen editing on a VT100, implement directed erase, the numeric keypad in applications mode, and the limited scrolling region. The latter is needed to do insert/delete line functions without rewriting the screen. [0J Erase from current position to bottom of screen inclusive [1J Erase from top of screen to current position inclusive [2J Erase entire screen (without moving the cursor) [0K Erase from current position to end of line inclusive [1K Erase from beginning of line to current position inclusive [2K Erase entire line (without moving cursor) [12;24r Set scrolling region to lines 12 thru 24. If a linefeed or an INDex is received while on line 24, the former line 12 is deleted and rows 13-24 move up. If a RI (reverse Index) is received while on line 12, a blank line is inserted there as rows 12-13 move down. All VT100 compatible terminals (except GIGI) have this feature. ESC = Set numeric keypad to applications mode ESC > Set numeric keypad to numbers mode OA Up-cursor key sends ESC O A after ESC = ESC [ ? 1 h OB Down-cursor key sends ESC O B " " " OC Right-cursor key sends ESC O B " " " OB Left-cursor key sends ESC O B " " " OM ENTER key sends ESC O M after ESC = Ol COMMA on keypad sends ESC O l " " (that's lowercase L) Om MINUS on keypad sends ESC O m " " Op ZERO on keypad sends ESC O p " " Oq ONE on keypad sends ESC O q " " Or TWO on keypad sends ESC O r " " Os THREE on keypad sends ESC O s " " Ot FOUR on keypad sends ESC O t " " Ou FIVE on keypad sends ESC O u " " Ov SIX on keypad sends ESC O v " " Ow SEVEN on keypad sends ESC O w " " Ox EIGHT on keypad sends ESC O x " " Oy NINE on keypad sends ESC O y " " 4) If the hardware is capable of double width/double height: #3 Top half of a double-width double-height line #4 Bottom half of a double-width double-height line #5 Make line single-width (lines are set this way when cleared by ESC [ J) #6 Make line double-width normal height (40 or 66 characters) 5) If the terminal emulator is capable of insert/delete characters, insert/delete lines, insert/replace mode, and can do a full-screen dump to the printer (in text mode), then it should identify itself as a VT102 [c Request for the terminal to identify itself [?6c VT102 (printer port, 132 column mode, and ins/del standard) [1@ Insert a blank character position (shift line to the right) [1P Delete a character position (shift line to the left) [1L Insert blank line at current row (shift screen down) [1M Delete the current line (shift screen up) [4h Set insert mode, new characters shove existing ones to the right [4l Reset insert mode, new characters replace existing ones [0i Print screen (all 24 lines) to the printer [4i All received data goes to the printer (nothing to the screen) [5i All received data goes to the screen (nothing to the printer) [End of ANSICODE.TXT] tmate-1.8.10/tools/check-compat.sh000066400000000000000000000001411242461015400167400ustar00rootroot00000000000000# $Id$ grep "#include" compat.h|while read line; do grep "$line" *.[ch] compat/*.[ch] done tmate-1.8.10/tools/cmp-cvs.sh000066400000000000000000000003201242461015400157510ustar00rootroot00000000000000# $Id$ rm diff.out touch diff.out for i in *.[ch]; do diff -u $i /usr/src/usr.bin/tmux/$i >diff.tmp set -- `wc -l diff.tmp` [ $1 -eq 8 ] && continue echo $i cat diff.tmp >>diff.out done tmate-1.8.10/tools/fix-ids.sh000066400000000000000000000002641242461015400157530ustar00rootroot00000000000000# $Id$ for i in *.[ch] tmux.1; do (head -1 $i|grep '$OpenBSD' >/dev/null) || continue mv $i $i~ || exit sed 's/\$OpenBSD.* \$/$\Id$/' $i~ >$i || exit echo $i done tmate-1.8.10/tools/fuzz.c000066400000000000000000000006031242461015400152130ustar00rootroot00000000000000#include #include #include #include #include int main(void) { time_t t; int i; setvbuf(stdout, NULL, _IONBF, 0); t = time(NULL); srandom((u_int) t); for (;;) { putchar('\033'); for (i = 0; i < random() % 25; i++) { if (i > 22) putchar(';'); else putchar(random() % 256); } /* usleep(100); */ } } tmate-1.8.10/tools/putty-utf8.sh000066400000000000000000000000541242461015400164560ustar00rootroot00000000000000echo -ne \\033%G\\033[?47h\\033%G\\033[?47l tmate-1.8.10/tty-acs.c000066400000000000000000000050421242461015400144430ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2010 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" int tty_acs_cmp(const void *, const void *); /* Table mapping ACS entries to UTF-8. */ struct tty_acs_entry { u_char key; const char *string; }; const struct tty_acs_entry tty_acs_table[] = { { '+', "\342\206\222" }, { ',', "\342\206\220" }, { '-', "\342\206\221" }, { '.', "\342\206\223" }, { '0', "\342\226\256" }, { '`', "\342\227\206" }, { 'a', "\342\226\222" }, { 'f', "\302\260" }, { 'g', "\302\261" }, { 'h', "\342\226\222" }, { 'i', "\342\230\203" }, { 'j', "\342\224\230" }, { 'k', "\342\224\220" }, { 'l', "\342\224\214" }, { 'm', "\342\224\224" }, { 'n', "\342\224\274" }, { 'o', "\342\216\272" }, { 'p', "\342\216\273" }, { 'q', "\342\224\200" }, { 'r', "\342\216\274" }, { 's', "\342\216\275" }, { 't', "\342\224\234" }, { 'u', "\342\224\244" }, { 'v', "\342\224\264" }, { 'w', "\342\224\254" }, { 'x', "\342\224\202" }, { 'y', "\342\211\244" }, { 'z', "\342\211\245" }, { '{', "\317\200" }, { '|', "\342\211\240" }, { '}', "\302\243" }, { '~', "\302\267" } }; int tty_acs_cmp(const void *key, const void *value) { const struct tty_acs_entry *entry = value; u_char ch; ch = *(u_char *) key; return (ch - entry->key); } /* Retrieve ACS to output as a string. */ const char * tty_acs_get(struct tty *tty, u_char ch) { struct tty_acs_entry *entry; /* If not a UTF-8 terminal, use the ACS set. */ if (!(tty->flags & TTY_UTF8)) { if (tty->term->acs[ch][0] == '\0') return (NULL); return (&tty->term->acs[ch][0]); } /* Otherwise look up the UTF-8 translation. */ entry = bsearch(&ch, tty_acs_table, nitems(tty_acs_table), sizeof tty_acs_table[0], tty_acs_cmp); if (entry == NULL) return (NULL); return (entry->string); } tmate-1.8.10/tty-keys.c000066400000000000000000000526071242461015400146610ustar00rootroot00000000000000/* $Id$ */ /* * 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" /* * 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. */ void tty_keys_add1(struct tty_key **, const char *, int); void tty_keys_add(struct tty *, const char *, int); void tty_keys_free1(struct tty_key *); struct tty_key *tty_keys_find1( struct tty_key *, const char *, size_t, size_t *); struct tty_key *tty_keys_find(struct tty *, const char *, size_t, size_t *); void tty_keys_callback(int, short, void *); int tty_keys_mouse(struct tty *, const char *, size_t, size_t *); int tty_keys_device(struct tty *, const char *, size_t, size_t *); /* Default raw keys. */ struct tty_default_key_raw { const char *string; int key; }; 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[25^", KEYC_F13|KEYC_CTRL }, { "\033[26^", KEYC_F14|KEYC_CTRL }, { "\033[28^", KEYC_F15|KEYC_CTRL }, { "\033[29^", KEYC_F16|KEYC_CTRL }, { "\033[31^", KEYC_F17|KEYC_CTRL }, { "\033[32^", KEYC_F18|KEYC_CTRL }, { "\033[33^", KEYC_F19|KEYC_CTRL }, { "\033[34^", KEYC_F20|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[25$", KEYC_F13|KEYC_SHIFT }, { "\033[26$", KEYC_F14|KEYC_SHIFT }, { "\033[28$", KEYC_F15|KEYC_SHIFT }, { "\033[29$", KEYC_F16|KEYC_SHIFT }, { "\033[31$", KEYC_F17|KEYC_SHIFT }, { "\033[32$", KEYC_F18|KEYC_SHIFT }, { "\033[33$", KEYC_F19|KEYC_SHIFT }, { "\033[34$", KEYC_F20|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[25@", KEYC_F13|KEYC_CTRL|KEYC_SHIFT }, { "\033[26@", KEYC_F14|KEYC_CTRL|KEYC_SHIFT }, { "\033[28@", KEYC_F15|KEYC_CTRL|KEYC_SHIFT }, { "\033[29@", KEYC_F16|KEYC_CTRL|KEYC_SHIFT }, { "\033[31@", KEYC_F17|KEYC_CTRL|KEYC_SHIFT }, { "\033[32@", KEYC_F18|KEYC_CTRL|KEYC_SHIFT }, { "\033[33@", KEYC_F19|KEYC_CTRL|KEYC_SHIFT }, { "\033[34@", KEYC_F20|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 }, }; /* Default terminfo(5) keys. */ struct tty_default_key_code { enum tty_code_code code; int key; }; 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_F13 }, { TTYC_KF14, KEYC_F14 }, { TTYC_KF15, KEYC_F15 }, { TTYC_KF16, KEYC_F16 }, { TTYC_KF17, KEYC_F17 }, { TTYC_KF18, KEYC_F18 }, { TTYC_KF19, KEYC_F19 }, { TTYC_KF20, KEYC_F20 }, { 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 }, { TTYC_KDC3, KEYC_DC|KEYC_ESCAPE }, { TTYC_KDC4, KEYC_DC|KEYC_SHIFT|KEYC_ESCAPE }, { TTYC_KDC5, KEYC_DC|KEYC_CTRL }, { TTYC_KDC6, KEYC_DC|KEYC_SHIFT|KEYC_CTRL }, { TTYC_KDC7, KEYC_DC|KEYC_ESCAPE|KEYC_CTRL }, { TTYC_KDN2, KEYC_DOWN|KEYC_SHIFT }, { TTYC_KDN3, KEYC_DOWN|KEYC_ESCAPE }, { TTYC_KDN4, KEYC_DOWN|KEYC_SHIFT|KEYC_ESCAPE }, { TTYC_KDN5, KEYC_DOWN|KEYC_CTRL }, { TTYC_KDN6, KEYC_DOWN|KEYC_SHIFT|KEYC_CTRL }, { TTYC_KDN7, KEYC_DOWN|KEYC_ESCAPE|KEYC_CTRL }, { TTYC_KEND2, KEYC_END|KEYC_SHIFT }, { TTYC_KEND3, KEYC_END|KEYC_ESCAPE }, { TTYC_KEND4, KEYC_END|KEYC_SHIFT|KEYC_ESCAPE }, { TTYC_KEND5, KEYC_END|KEYC_CTRL }, { TTYC_KEND6, KEYC_END|KEYC_SHIFT|KEYC_CTRL }, { TTYC_KEND7, KEYC_END|KEYC_ESCAPE|KEYC_CTRL }, { TTYC_KHOM2, KEYC_HOME|KEYC_SHIFT }, { TTYC_KHOM3, KEYC_HOME|KEYC_ESCAPE }, { TTYC_KHOM4, KEYC_HOME|KEYC_SHIFT|KEYC_ESCAPE }, { TTYC_KHOM5, KEYC_HOME|KEYC_CTRL }, { TTYC_KHOM6, KEYC_HOME|KEYC_SHIFT|KEYC_CTRL }, { TTYC_KHOM7, KEYC_HOME|KEYC_ESCAPE|KEYC_CTRL }, { TTYC_KIC2, KEYC_IC|KEYC_SHIFT }, { TTYC_KIC3, KEYC_IC|KEYC_ESCAPE }, { TTYC_KIC4, KEYC_IC|KEYC_SHIFT|KEYC_ESCAPE }, { TTYC_KIC5, KEYC_IC|KEYC_CTRL }, { TTYC_KIC6, KEYC_IC|KEYC_SHIFT|KEYC_CTRL }, { TTYC_KIC7, KEYC_IC|KEYC_ESCAPE|KEYC_CTRL }, { TTYC_KLFT2, KEYC_LEFT|KEYC_SHIFT }, { TTYC_KLFT3, KEYC_LEFT|KEYC_ESCAPE }, { TTYC_KLFT4, KEYC_LEFT|KEYC_SHIFT|KEYC_ESCAPE }, { TTYC_KLFT5, KEYC_LEFT|KEYC_CTRL }, { TTYC_KLFT6, KEYC_LEFT|KEYC_SHIFT|KEYC_CTRL }, { TTYC_KLFT7, KEYC_LEFT|KEYC_ESCAPE|KEYC_CTRL }, { TTYC_KNXT2, KEYC_NPAGE|KEYC_SHIFT }, { TTYC_KNXT3, KEYC_NPAGE|KEYC_ESCAPE }, { TTYC_KNXT4, KEYC_NPAGE|KEYC_SHIFT|KEYC_ESCAPE }, { TTYC_KNXT5, KEYC_NPAGE|KEYC_CTRL }, { TTYC_KNXT6, KEYC_NPAGE|KEYC_SHIFT|KEYC_CTRL }, { TTYC_KNXT7, KEYC_NPAGE|KEYC_ESCAPE|KEYC_CTRL }, { TTYC_KPRV2, KEYC_PPAGE|KEYC_SHIFT }, { TTYC_KPRV3, KEYC_PPAGE|KEYC_ESCAPE }, { TTYC_KPRV4, KEYC_PPAGE|KEYC_SHIFT|KEYC_ESCAPE }, { TTYC_KPRV5, KEYC_PPAGE|KEYC_CTRL }, { TTYC_KPRV6, KEYC_PPAGE|KEYC_SHIFT|KEYC_CTRL }, { TTYC_KPRV7, KEYC_PPAGE|KEYC_ESCAPE|KEYC_CTRL }, { TTYC_KRIT2, KEYC_RIGHT|KEYC_SHIFT }, { TTYC_KRIT3, KEYC_RIGHT|KEYC_ESCAPE }, { TTYC_KRIT4, KEYC_RIGHT|KEYC_SHIFT|KEYC_ESCAPE }, { TTYC_KRIT5, KEYC_RIGHT|KEYC_CTRL }, { TTYC_KRIT6, KEYC_RIGHT|KEYC_SHIFT|KEYC_CTRL }, { TTYC_KRIT7, KEYC_RIGHT|KEYC_ESCAPE|KEYC_CTRL }, { TTYC_KUP2, KEYC_UP|KEYC_SHIFT }, { TTYC_KUP3, KEYC_UP|KEYC_ESCAPE }, { TTYC_KUP4, KEYC_UP|KEYC_SHIFT|KEYC_ESCAPE }, { TTYC_KUP5, KEYC_UP|KEYC_CTRL }, { TTYC_KUP6, KEYC_UP|KEYC_SHIFT|KEYC_CTRL }, { TTYC_KUP7, KEYC_UP|KEYC_ESCAPE|KEYC_CTRL }, }; /* Add key to tree. */ void tty_keys_add(struct tty *tty, const char *s, int key) { struct tty_key *tk; size_t size; const char *keystr; keystr = key_string_lookup_key(key); if ((tk = tty_keys_find(tty, s, strlen(s), &size)) == NULL) { log_debug("new key %s: 0x%x (%s)", s, key, keystr); tty_keys_add1(&tty->key_tree, s, key); } else { log_debug("replacing key %s: 0x%x (%s)", s, key, keystr); tk->key = key; } } /* Add next node to the tree. */ void tty_keys_add1(struct tty_key **tkp, const char *s, int key) { struct tty_key *tk; /* Allocate a tree entry if there isn't one already. */ tk = *tkp; if (tk == NULL) { tk = *tkp = xcalloc(1, sizeof *tk); tk->ch = *s; tk->key = KEYC_NONE; } /* Find the next entry. */ if (*s == tk->ch) { /* Move forward in string. */ s++; /* If this is the end of the string, no more is necessary. */ if (*s == '\0') { tk->key = key; return; } /* Use the child tree for the next character. */ tkp = &tk->next; } else { if (*s < tk->ch) tkp = &tk->left; else if (*s > tk->ch) tkp = &tk->right; } /* And recurse to add it. */ tty_keys_add1(tkp, s, key); } /* Initialise a key tree from the table. */ void tty_keys_build(struct tty *tty) { const struct tty_default_key_raw *tdkr; const struct tty_default_key_code *tdkc; u_int i; const char *s; 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); } } /* Free the entire key tree. */ void tty_keys_free(struct tty *tty) { tty_keys_free1(tty->key_tree); } /* Free a single key. */ void tty_keys_free1(struct tty_key *tk) { if (tk->next != NULL) tty_keys_free1(tk->next); if (tk->left != NULL) tty_keys_free1(tk->left); if (tk->right != NULL) tty_keys_free1(tk->right); free(tk); } /* Lookup a key in the tree. */ struct tty_key * tty_keys_find(struct tty *tty, const char *buf, size_t len, size_t *size) { *size = 0; return (tty_keys_find1(tty->key_tree, buf, len, size)); } /* Find the next node. */ struct tty_key * tty_keys_find1(struct tty_key *tk, const char *buf, size_t len, size_t *size) { /* If the node is NULL, this is the end of the tree. No match. */ if (tk == NULL) return (NULL); /* Pick the next in the sequence. */ if (tk->ch == *buf) { /* Move forward in the string. */ buf++; len--; (*size)++; /* At the end of the string, return the current node. */ if (len == 0 || (tk->next == NULL && tk->key != KEYC_NONE)) return (tk); /* Move into the next tree for the following character. */ tk = tk->next; } else { if (*buf < tk->ch) tk = tk->left; else if (*buf > tk->ch) tk = tk->right; } /* Move to the next in the tree. */ return (tty_keys_find1(tk, buf, len, size)); } /* * Process at least one key in the buffer and invoke tty->key_callback. Return * 0 if there are no further keys, or 1 if there could be more in the buffer. */ int tty_keys_next(struct tty *tty) { struct tty_key *tk; struct timeval tv; const char *buf; size_t len, size; cc_t bspace; int key, delay, expired = 0; /* Get key buffer. */ buf = EVBUFFER_DATA(tty->event->input); len = EVBUFFER_LENGTH(tty->event->input); if (len == 0) return (0); log_debug("keys are %zu (%.*s)", len, (int) len, buf); /* Is this device attributes response? */ switch (tty_keys_device(tty, buf, len, &size)) { case 0: /* yes */ key = KEYC_NONE; 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)) { case 0: /* yes */ key = KEYC_MOUSE; goto complete_key; case -1: /* no, or not valid */ break; case 1: /* partial */ goto partial_key; } /* Try to parse a key with an xterm-style modifier. */ switch (xterm_keys_find(buf, len, &size, &key)) { case 0: /* found */ goto complete_key; case -1: /* not found */ break; case 1: goto partial_key; } /* Look for matching key string and return if found. */ tk = tty_keys_find(tty, buf, len, &size); if (tk != NULL) { if (tk->next != NULL) goto partial_key; key = tk->key; goto complete_key; } first_key: /* Is this a meta key? */ if (len >= 2 && buf[0] == '\033') { if (buf[1] != '\033') { key = buf[1] | KEYC_ESCAPE; size = 2; goto complete_key; } tk = tty_keys_find(tty, buf + 1, len - 1, &size); if (tk != NULL && (!expired || tk->next == NULL)) { size++; /* include escape */ if (tk->next != NULL) goto partial_key; key = tk->key; if (key != KEYC_NONE) key |= KEYC_ESCAPE; goto complete_key; } } /* No key found, take first. */ key = (u_char) *buf; size = 1; /* * Check for backspace key using termios VERASE - the terminfo * kbs entry is extremely unreliable, so cannot be safely * used. termios should have a better idea. */ bspace = tty->tio.c_cc[VERASE]; if (bspace != _POSIX_VDISABLE && key == bspace) key = KEYC_BSPACE; goto complete_key; partial_key: log_debug("partial key %.*s", (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("complete key %.*s %#x", (int) size, buf, key); /* Remove data from buffer. */ evbuffer_drain(tty->event->input, 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_NONE) server_client_handle_key(tty->client, key); return (1); } /* Key timer callback. */ 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). */ int tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) { struct mouse_event *m = &tty->mouse; struct utf8_data utf8data; u_int i, value, x, y, b, sgr, sgr_b, sgr_rel; unsigned char c; /* * 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 = sgr_b = sgr_rel = 0; /* 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, < in SGR * extension. */ if (buf[2] == 'M') { /* Read the three inputs. */ *size = 3; for (i = 0; i < 3; i++) { if (len <= *size) return (1); if (tty->mode & MODE_MOUSE_UTF8) { if (utf8_open(&utf8data, buf[*size])) { if (utf8data.size != 2) return (-1); (*size)++; if (len <= *size) return (1); utf8_append(&utf8data, buf[*size]); value = utf8_combine(&utf8data); } else value = (u_char) buf[*size]; (*size)++; } else { value = (u_char) buf[*size]; (*size)++; } if (i == 0) b = value; else if (i == 1) x = value; else y = value; } log_debug("mouse input: %.*s", (int) *size, buf); /* Check and return the mouse input. */ if (b < 32 || x < 33 || y < 33) return (-1); b -= 32; x -= 33; y -= 33; } else if (buf[2] == '<') { /* Read the three inputs. */ *size = 3; while (1) { if (len <= *size) return (1); c = (u_char)buf[(*size)++]; if (c == ';') break; if (c < '0' || c > '9') return (-1); sgr_b = 10 * sgr_b + (c - '0'); } while (1) { if (len <= *size) return (1); c = (u_char)buf[(*size)++]; if (c == ';') break; if (c < '0' || c > '9') return (-1); x = 10 * x + (c - '0'); } while (1) { if (len <= *size) return (1); c = (u_char) buf[(*size)++]; if (c == 'M' || c == 'm') break; if (c < '0' || c > '9') return (-1); y = 10 * y + (c - '0'); } log_debug("mouse input (sgr): %.*s", (int) *size, buf); /* Check and return the mouse input. */ if (x < 1 || y < 1) return (-1); x--; y--; sgr = 1; sgr_rel = (c == 'm'); /* Figure out what b would be in old format. */ b = sgr_b; if (sgr_rel) b |= 3; } else return (-1); /* Fill in mouse structure. */ if (~m->event & MOUSE_EVENT_WHEEL) { m->lx = m->x; m->ly = m->y; } m->xb = b; m->sgr = sgr; m->sgr_xb = sgr_b; m->sgr_rel = sgr_rel; if (b & 64) { /* wheel button */ b &= 3; if (b == 0) m->wheel = MOUSE_WHEEL_UP; else if (b == 1) m->wheel = MOUSE_WHEEL_DOWN; m->event = MOUSE_EVENT_WHEEL; } else if ((b & 3) == 3) { if (~m->event & MOUSE_EVENT_DRAG && x == m->x && y == m->y) { m->event = MOUSE_EVENT_CLICK; } else m->event = MOUSE_EVENT_DRAG; m->event |= MOUSE_EVENT_UP; } else { if (b & 32) /* drag motion */ m->event = MOUSE_EVENT_DRAG; else { if (m->event & MOUSE_EVENT_UP && x == m->x && y == m->y) m->clicks = (m->clicks + 1) % 3; else m->clicks = 0; m->sx = x; m->sy = y; m->event = MOUSE_EVENT_DOWN; } m->button = (b & 3); } m->x = x; m->y = y; return (0); } /* * Handle device attributes input. Returns 0 for success, -1 for failure, 1 for * partial. */ int tty_keys_device(struct tty *tty, const char *buf, size_t len, size_t *size) { u_int i, class; char tmp[64], *endptr; /* * Primary device attributes are \033[?a;b and secondary are * \033[>a;b;c. */ *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] != '>' && 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; /* Only primary is of interest. */ if (buf[2] != '?') return (0); /* Convert service class. */ class = strtoul(tmp, &endptr, 10); if (*endptr != ';') class = 0; log_debug("received service class %u", class); tty_set_class(tty, class); return (0); } tmate-1.8.10/tty-term.c000066400000000000000000000364351242461015400146560ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #ifdef HAVE_CURSES_H #include #else #include #endif #include #include #include #include #include "tmux.h" void tty_term_override(struct tty_term *, const char *); char *tty_term_strip(const char *); struct tty_terms tty_terms = LIST_HEAD_INITIALIZER(tty_terms); const struct tty_term_code_entry tty_term_codes[NTTYCODE] = { { TTYC_ACSC, TTYCODE_STRING, "acsc" }, { TTYC_AX, TTYCODE_FLAG, "AX" }, { TTYC_BEL, TTYCODE_STRING, "bel" }, { TTYC_BLINK, TTYCODE_STRING, "blink" }, { TTYC_BOLD, TTYCODE_STRING, "bold" }, { TTYC_CC, TTYCODE_STRING, "Cc" }, { TTYC_CIVIS, TTYCODE_STRING, "civis" }, { TTYC_CLEAR, TTYCODE_STRING, "clear" }, { TTYC_CNORM, TTYCODE_STRING, "cnorm" }, { TTYC_COLORS, TTYCODE_NUMBER, "colors" }, { TTYC_CR, TTYCODE_STRING, "Cr" }, { TTYC_CS1, TTYCODE_STRING, "Cs" }, { TTYC_CSR, TTYCODE_STRING, "csr" }, { TTYC_CSR1, TTYCODE_STRING, "Csr" }, { TTYC_CUB, TTYCODE_STRING, "cub" }, { TTYC_CUB1, TTYCODE_STRING, "cub1" }, { TTYC_CUD, TTYCODE_STRING, "cud" }, { TTYC_CUD1, TTYCODE_STRING, "cud1" }, { TTYC_CUF, TTYCODE_STRING, "cuf" }, { TTYC_CUF1, TTYCODE_STRING, "cuf1" }, { TTYC_CUP, TTYCODE_STRING, "cup" }, { TTYC_CUU, TTYCODE_STRING, "cuu" }, { TTYC_CUU1, TTYCODE_STRING, "cuu1" }, { TTYC_DCH, TTYCODE_STRING, "dch" }, { TTYC_DCH1, TTYCODE_STRING, "dch1" }, { TTYC_DIM, TTYCODE_STRING, "dim" }, { TTYC_DL, TTYCODE_STRING, "dl" }, { TTYC_DL1, TTYCODE_STRING, "dl1" }, { TTYC_E3, TTYCODE_STRING, "E3" }, { TTYC_ECH, TTYCODE_STRING, "ech" }, { TTYC_EL, TTYCODE_STRING, "el" }, { TTYC_EL1, TTYCODE_STRING, "el1" }, { TTYC_ENACS, TTYCODE_STRING, "enacs" }, { TTYC_FSL, TTYCODE_STRING, "fsl" }, { TTYC_HOME, TTYCODE_STRING, "home" }, { TTYC_HPA, TTYCODE_STRING, "hpa" }, { TTYC_ICH, TTYCODE_STRING, "ich" }, { TTYC_ICH1, TTYCODE_STRING, "ich1" }, { TTYC_IL, TTYCODE_STRING, "il" }, { TTYC_IL1, TTYCODE_STRING, "il1" }, { TTYC_INVIS, TTYCODE_STRING, "invis" }, { TTYC_IS1, TTYCODE_STRING, "is1" }, { TTYC_IS2, TTYCODE_STRING, "is2" }, { TTYC_IS3, TTYCODE_STRING, "is3" }, { TTYC_KCBT, TTYCODE_STRING, "kcbt" }, { TTYC_KCUB1, TTYCODE_STRING, "kcub1" }, { TTYC_KCUD1, TTYCODE_STRING, "kcud1" }, { TTYC_KCUF1, TTYCODE_STRING, "kcuf1" }, { TTYC_KCUU1, TTYCODE_STRING, "kcuu1" }, { TTYC_KDC2, TTYCODE_STRING, "kDC" }, { TTYC_KDC3, TTYCODE_STRING, "kDC3" }, { TTYC_KDC4, TTYCODE_STRING, "kDC4" }, { TTYC_KDC5, TTYCODE_STRING, "kDC5" }, { TTYC_KDC6, TTYCODE_STRING, "kDC6" }, { TTYC_KDC7, TTYCODE_STRING, "kDC7" }, { TTYC_KDCH1, TTYCODE_STRING, "kdch1" }, { TTYC_KDN2, TTYCODE_STRING, "kDN" }, { TTYC_KDN3, TTYCODE_STRING, "kDN3" }, { TTYC_KDN4, TTYCODE_STRING, "kDN4" }, { TTYC_KDN5, TTYCODE_STRING, "kDN5" }, { TTYC_KDN6, TTYCODE_STRING, "kDN6" }, { TTYC_KDN7, TTYCODE_STRING, "kDN7" }, { TTYC_KEND, TTYCODE_STRING, "kend" }, { TTYC_KEND2, TTYCODE_STRING, "kEND" }, { TTYC_KEND3, TTYCODE_STRING, "kEND3" }, { TTYC_KEND4, TTYCODE_STRING, "kEND4" }, { TTYC_KEND5, TTYCODE_STRING, "kEND5" }, { TTYC_KEND6, TTYCODE_STRING, "kEND6" }, { TTYC_KEND7, TTYCODE_STRING, "kEND7" }, { TTYC_KF1, TTYCODE_STRING, "kf1" }, { TTYC_KF10, TTYCODE_STRING, "kf10" }, { TTYC_KF11, TTYCODE_STRING, "kf11" }, { TTYC_KF12, TTYCODE_STRING, "kf12" }, { TTYC_KF13, TTYCODE_STRING, "kf13" }, { TTYC_KF14, TTYCODE_STRING, "kf14" }, { TTYC_KF15, TTYCODE_STRING, "kf15" }, { TTYC_KF16, TTYCODE_STRING, "kf16" }, { TTYC_KF17, TTYCODE_STRING, "kf17" }, { TTYC_KF18, TTYCODE_STRING, "kf18" }, { TTYC_KF19, TTYCODE_STRING, "kf19" }, { TTYC_KF2, TTYCODE_STRING, "kf2" }, { TTYC_KF20, TTYCODE_STRING, "kf20" }, { TTYC_KF3, TTYCODE_STRING, "kf3" }, { TTYC_KF4, TTYCODE_STRING, "kf4" }, { TTYC_KF5, TTYCODE_STRING, "kf5" }, { TTYC_KF6, TTYCODE_STRING, "kf6" }, { TTYC_KF7, TTYCODE_STRING, "kf7" }, { TTYC_KF8, TTYCODE_STRING, "kf8" }, { TTYC_KF9, TTYCODE_STRING, "kf9" }, { TTYC_KHOM2, TTYCODE_STRING, "kHOM" }, { TTYC_KHOM3, TTYCODE_STRING, "kHOM3" }, { TTYC_KHOM4, TTYCODE_STRING, "kHOM4" }, { TTYC_KHOM5, TTYCODE_STRING, "kHOM5" }, { TTYC_KHOM6, TTYCODE_STRING, "kHOM6" }, { TTYC_KHOM7, TTYCODE_STRING, "kHOM7" }, { TTYC_KHOME, TTYCODE_STRING, "khome" }, { TTYC_KIC2, TTYCODE_STRING, "kIC" }, { TTYC_KIC3, TTYCODE_STRING, "kIC3" }, { TTYC_KIC4, TTYCODE_STRING, "kIC4" }, { TTYC_KIC5, TTYCODE_STRING, "kIC5" }, { TTYC_KIC6, TTYCODE_STRING, "kIC6" }, { TTYC_KIC7, TTYCODE_STRING, "kIC7" }, { TTYC_KICH1, TTYCODE_STRING, "kich1" }, { TTYC_KLFT2, TTYCODE_STRING, "kLFT" }, { TTYC_KLFT3, TTYCODE_STRING, "kLFT3" }, { TTYC_KLFT4, TTYCODE_STRING, "kLFT4" }, { TTYC_KLFT5, TTYCODE_STRING, "kLFT5" }, { TTYC_KLFT6, TTYCODE_STRING, "kLFT6" }, { TTYC_KLFT7, TTYCODE_STRING, "kLFT7" }, { TTYC_KMOUS, TTYCODE_STRING, "kmous" }, { TTYC_KNP, TTYCODE_STRING, "knp" }, { TTYC_KNXT2, TTYCODE_STRING, "kNXT" }, { TTYC_KNXT3, TTYCODE_STRING, "kNXT3" }, { TTYC_KNXT4, TTYCODE_STRING, "kNXT4" }, { TTYC_KNXT5, TTYCODE_STRING, "kNXT5" }, { TTYC_KNXT6, TTYCODE_STRING, "kNXT6" }, { TTYC_KNXT7, TTYCODE_STRING, "kNXT7" }, { TTYC_KPP, TTYCODE_STRING, "kpp" }, { TTYC_KPRV2, TTYCODE_STRING, "kPRV" }, { TTYC_KPRV3, TTYCODE_STRING, "kPRV3" }, { TTYC_KPRV4, TTYCODE_STRING, "kPRV4" }, { TTYC_KPRV5, TTYCODE_STRING, "kPRV5" }, { TTYC_KPRV6, TTYCODE_STRING, "kPRV6" }, { TTYC_KPRV7, TTYCODE_STRING, "kPRV7" }, { TTYC_KRIT2, TTYCODE_STRING, "kRIT" }, { TTYC_KRIT3, TTYCODE_STRING, "kRIT3" }, { TTYC_KRIT4, TTYCODE_STRING, "kRIT4" }, { TTYC_KRIT5, TTYCODE_STRING, "kRIT5" }, { TTYC_KRIT6, TTYCODE_STRING, "kRIT6" }, { TTYC_KRIT7, TTYCODE_STRING, "kRIT7" }, { TTYC_KUP2, TTYCODE_STRING, "kUP" }, { TTYC_KUP3, TTYCODE_STRING, "kUP3" }, { TTYC_KUP4, TTYCODE_STRING, "kUP4" }, { TTYC_KUP5, TTYCODE_STRING, "kUP5" }, { TTYC_KUP6, TTYCODE_STRING, "kUP6" }, { TTYC_KUP7, TTYCODE_STRING, "kUP7" }, { TTYC_MS, TTYCODE_STRING, "Ms" }, { TTYC_OP, TTYCODE_STRING, "op" }, { TTYC_REV, TTYCODE_STRING, "rev" }, { TTYC_RI, TTYCODE_STRING, "ri" }, { TTYC_RMACS, TTYCODE_STRING, "rmacs" }, { TTYC_RMCUP, TTYCODE_STRING, "rmcup" }, { TTYC_RMKX, TTYCODE_STRING, "rmkx" }, { TTYC_SETAB, TTYCODE_STRING, "setab" }, { TTYC_SETAF, TTYCODE_STRING, "setaf" }, { TTYC_SGR0, TTYCODE_STRING, "sgr0" }, { TTYC_SITM, TTYCODE_STRING, "sitm" }, { TTYC_SMACS, TTYCODE_STRING, "smacs" }, { TTYC_SMCUP, TTYCODE_STRING, "smcup" }, { TTYC_SMKX, TTYCODE_STRING, "smkx" }, { TTYC_SMSO, TTYCODE_STRING, "smso" }, { TTYC_SMUL, TTYCODE_STRING, "smul" }, { TTYC_TSL, TTYCODE_STRING, "tsl" }, { TTYC_VPA, TTYCODE_STRING, "vpa" }, { TTYC_XENL, TTYCODE_FLAG, "xenl" }, { TTYC_XT, TTYCODE_FLAG, "XT" }, }; char * tty_term_strip(const char *s) { const char *ptr; static char buf[BUFSIZ]; size_t len; /* Ignore strings with no padding. */ if (strchr(s, '$') == NULL) return (xstrdup(s)); len = 0; for (ptr = s; *ptr != '\0'; ptr++) { if (*ptr == '$' && *(ptr + 1) == '<') { while (*ptr != '\0' && *ptr != '>') ptr++; if (*ptr == '>') ptr++; } buf[len++] = *ptr; if (len == (sizeof buf) - 1) break; } buf[len] = '\0'; return (xstrdup(buf)); } void tty_term_override(struct tty_term *term, const char *overrides) { const struct tty_term_code_entry *ent; struct tty_code *code; char *termnext, *termstr; char *entnext, *entstr; char *s, *ptr, *val; const char *errstr; u_int i; int n, removeflag; s = xstrdup(overrides); termnext = s; while ((termstr = strsep(&termnext, ",")) != NULL) { entnext = termstr; entstr = strsep(&entnext, ":"); if (entstr == NULL || entnext == NULL) continue; if (fnmatch(entstr, term->name, 0) != 0) continue; while ((entstr = strsep(&entnext, ":")) != NULL) { if (*entstr == '\0') continue; val = NULL; removeflag = 0; if ((ptr = strchr(entstr, '=')) != NULL) { *ptr++ = '\0'; val = xstrdup(ptr); if (strunvis(val, ptr) == -1) { free(val); val = xstrdup(ptr); } } else if (entstr[strlen(entstr) - 1] == '@') { entstr[strlen(entstr) - 1] = '\0'; removeflag = 1; } else val = xstrdup(""); log_debug("%s override: %s %s", term->name, entstr, removeflag ? "@" : val); for (i = 0; i < NTTYCODE; i++) { ent = &tty_term_codes[i]; if (strcmp(entstr, ent->name) != 0) continue; code = &term->codes[ent->code]; if (removeflag) { code->type = TTYCODE_NONE; continue; } switch (ent->type) { case TTYCODE_NONE: break; case TTYCODE_STRING: if (code->type == TTYCODE_STRING) free(code->value.string); code->value.string = xstrdup(val); code->type = ent->type; break; case TTYCODE_NUMBER: n = strtonum(val, 0, INT_MAX, &errstr); if (errstr != NULL) break; code->value.number = n; code->type = ent->type; break; case TTYCODE_FLAG: code->value.flag = 1; code->type = ent->type; break; } } free(val); } } free(s); } struct tty_term * tty_term_find(char *name, int fd, const char *overrides, char **cause) { struct tty_term *term; const struct tty_term_code_entry *ent; struct tty_code *code; u_int i; int n, error; char *s; const char *acs; LIST_FOREACH(term, &tty_terms, entry) { if (strcmp(term->name, name) == 0) { term->references++; return (term); } } log_debug("new term: %s", name); term = xmalloc(sizeof *term); term->name = xstrdup(name); term->references = 1; term->flags = 0; memset(term->codes, 0, sizeof term->codes); LIST_INSERT_HEAD(&tty_terms, term, entry); /* Set up curses terminal. */ if (setupterm(name, fd, &error) != OK) { switch (error) { case 1: xasprintf( cause, "can't use hardcopy terminal: %s", name); break; case 0: xasprintf( cause, "missing or unsuitable terminal: %s", name); break; case -1: xasprintf(cause, "can't find terminfo database"); break; default: xasprintf(cause, "unknown error"); break; } goto error; } /* Fill in codes. */ for (i = 0; i < NTTYCODE; i++) { ent = &tty_term_codes[i]; code = &term->codes[ent->code]; code->type = TTYCODE_NONE; switch (ent->type) { case TTYCODE_NONE: break; case TTYCODE_STRING: s = tigetstr((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.number = n; break; } } tty_term_override(term, overrides); /* 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 or 88 colours. */ if (tty_term_number(term, TTYC_COLORS) == 256) term->flags |= TERM_256COLOURS; if (tty_term_number(term, TTYC_COLORS) == 88) term->flags |= TERM_88COLOURS; /* * Terminals without xenl (eat newline glitch) wrap at at $COLUMNS - 1 * rather than $COLUMNS (the cursor can never be beyond $COLUMNS - 1). * * This is irritating, most notably because it is impossible to write * to the very bottom-right of the screen without scrolling. * * Flag the terminal here and apply some workarounds in other places to * do the best possible. */ if (!tty_term_flag(term, TTYC_XENL)) term->flags |= TERM_EARLYWRAP; /* Generate ACS table. If none is present, use nearest ASCII. */ memset(term->acs, 0, sizeof term->acs); if (tty_term_has(term, TTYC_ACSC)) acs = tty_term_string(term, TTYC_ACSC); else acs = "a#j+k+l+m+n+o-p-q-r-s-t+u+v+w+x|y~."; for (; acs[0] != '\0' && acs[1] != '\0'; acs += 2) term->acs[(u_char) acs[0]][0] = acs[1]; /* On terminals with xterm titles (XT), fill in tsl and fsl. */ if (tty_term_flag(term, TTYC_XT) && !tty_term_has(term, TTYC_TSL) && !tty_term_has(term, TTYC_FSL)) { code = &term->codes[TTYC_TSL]; code->value.string = xstrdup("\033]0;"); code->type = TTYCODE_STRING; code = &term->codes[TTYC_FSL]; code->value.string = xstrdup("\007"); code->type = TTYCODE_STRING; } return (term); error: tty_term_free(term); return (NULL); } void tty_term_free(struct tty_term *term) { u_int i; if (--term->references != 0) return; LIST_REMOVE(term, entry); for (i = 0; i < NTTYCODE; i++) { if (term->codes[i].type == TTYCODE_STRING) free(term->codes[i].value.string); } 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) log_fatalx("not a string: %d", code); return (term->codes[code].value.string); } /* No vtparm. Fucking curses. */ const char * tty_term_string1(struct tty_term *term, enum tty_code_code code, int a) { return (tparm((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_ptr1(struct tty_term *term, enum tty_code_code code, const void *a) { return (tparm((char *) tty_term_string(term, code), 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), a, b, 0, 0, 0, 0, 0, 0, 0)); } int tty_term_number(struct tty_term *term, enum tty_code_code code) { if (!tty_term_has(term, code)) return (0); if (term->codes[code].type != TTYCODE_NUMBER) log_fatalx("not a number: %d", code); return (term->codes[code].value.number); } int tty_term_flag(struct tty_term *term, enum tty_code_code code) { if (!tty_term_has(term, code)) return (0); if (term->codes[code].type != TTYCODE_FLAG) log_fatalx("not a flag: %d", code); return (term->codes[code].value.flag); } tmate-1.8.10/tty.c000066400000000000000000001125651242461015400137100ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include "tmux.h" void tty_read_callback(struct bufferevent *, void *); void tty_error_callback(struct bufferevent *, short, void *); int tty_try_256(struct tty *, u_char, const char *); int tty_try_88(struct tty *, u_char, const char *); void tty_colours(struct tty *, const struct grid_cell *); void tty_check_fg(struct tty *, struct grid_cell *); void tty_check_bg(struct tty *, struct grid_cell *); void tty_colours_fg(struct tty *, const struct grid_cell *); void tty_colours_bg(struct tty *, const struct grid_cell *); int tty_large_region(struct tty *, const struct tty_ctx *); void tty_redraw_region(struct tty *, const struct tty_ctx *); void tty_emulate_repeat( struct tty *, enum tty_code_code, enum tty_code_code, u_int); void tty_repeat_space(struct tty *, u_int); void tty_cell(struct tty *, const struct grid_cell *); #define tty_use_acs(tty) \ (tty_term_has((tty)->term, TTYC_ACSC) && !((tty)->flags & TTY_UTF8)) #define tty_pane_full_width(tty, ctx) \ ((ctx)->xoff == 0 && screen_size_x((ctx)->wp->screen) >= (tty)->sx) void tty_init(struct tty *tty, struct client *c, int fd, char *term) { char *path; memset(tty, 0, sizeof *tty); tty->log_fd = -1; if (term == NULL || *term == '\0') tty->termname = xstrdup("unknown"); else tty->termname = xstrdup(term); tty->fd = fd; tty->client = c; if ((path = ttyname(fd)) == NULL) fatalx("ttyname failed"); tty->path = xstrdup(path); tty->cstyle = 0; tty->ccolour = xstrdup(""); tty->flags = 0; tty->term_flags = 0; } int tty_resize(struct tty *tty) { struct winsize ws; u_int sx, sy; if (ioctl(tty->fd, TIOCGWINSZ, &ws) != -1) { sx = ws.ws_col; if (sx == 0) sx = 80; sy = ws.ws_row; if (sy == 0) sy = 24; } else { sx = 80; sy = 24; } if (!tty_set_size(tty, sx, sy)) return (0); tty->cx = UINT_MAX; tty->cy = UINT_MAX; tty->rupper = UINT_MAX; tty->rlower = UINT_MAX; /* * If the terminal has been started, reset the actual scroll region and * cursor position, as this may not have happened. */ if (tty->flags & TTY_STARTED) { tty_cursor(tty, 0, 0); tty_region(tty, 0, tty->sy - 1); } return (1); } int tty_set_size(struct tty *tty, u_int sx, u_int sy) { if (sx == tty->sx && sy == tty->sy) return (0); tty->sx = sx; tty->sy = sy; return (1); } int tty_open(struct tty *tty, const char *overrides, char **cause) { char out[64]; int fd; if (debug_level > 3) { xsnprintf(out, sizeof out, "tmux-out-%ld.log", (long) getpid()); fd = open(out, O_WRONLY|O_CREAT|O_TRUNC, 0644); if (fd != -1 && fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) fatal("fcntl failed"); tty->log_fd = fd; } tty->term = tty_term_find(tty->termname, tty->fd, overrides, cause); if (tty->term == NULL) { tty_close(tty); return (-1); } tty->flags |= TTY_OPENED; tty->flags &= ~(TTY_NOCURSOR|TTY_FREEZE|TTY_TIMER); tty->event = bufferevent_new( tty->fd, tty_read_callback, NULL, tty_error_callback, tty); tty_start_tty(tty); tty_keys_build(tty); return (0); } void tty_read_callback(unused struct bufferevent *bufev, void *data) { struct tty *tty = data; while (tty_keys_next(tty)) ; } void tty_error_callback( unused struct bufferevent *bufev, unused short what, unused void *data) { } void tty_init_termios(int fd, struct termios *orig_tio, struct bufferevent *bufev) { struct termios tio; if (fd == -1 || tcgetattr(fd, orig_tio) != 0) return; setblocking(fd, 0); if (bufev != NULL) bufferevent_enable(bufev, EV_READ|EV_WRITE); memcpy(&tio, orig_tio, sizeof tio); tio.c_iflag &= ~(IXON|IXOFF|ICRNL|INLCR|IGNCR|IMAXBEL|ISTRIP); tio.c_iflag |= IGNBRK; tio.c_oflag &= ~(OPOST|ONLCR|OCRNL|ONLRET); tio.c_lflag &= ~(IEXTEN|ICANON|ECHO|ECHOE|ECHONL|ECHOCTL| ECHOPRT|ECHOKE|ECHOCTL|ISIG); tio.c_cc[VMIN] = 1; tio.c_cc[VTIME] = 0; if (tcsetattr(fd, TCSANOW, &tio) == 0) tcflush(fd, TCIOFLUSH); } void tty_start_tty(struct tty *tty) { tty_init_termios(tty->fd, &tty->tio, tty->event); tty_putcode(tty, TTYC_SMCUP); tty_putcode(tty, TTYC_SGR0); memcpy(&tty->cell, &grid_default_cell, sizeof tty->cell); tty_putcode(tty, TTYC_RMKX); if (tty_use_acs(tty)) tty_putcode(tty, TTYC_ENACS); tty_putcode(tty, TTYC_CLEAR); tty_putcode(tty, TTYC_CNORM); if (tty_term_has(tty->term, TTYC_KMOUS)) tty_puts(tty, "\033[?1000l\033[?1006l\033[?1005l"); if (tty_term_has(tty->term, TTYC_XT)) tty_puts(tty, "\033[c\033[>4;1m\033[?1004h"); tty->cx = UINT_MAX; tty->cy = UINT_MAX; tty->rlower = UINT_MAX; tty->rupper = UINT_MAX; tty->mode = MODE_CURSOR; tty->flags |= TTY_STARTED; tty_force_cursor_colour(tty, ""); } void tty_set_class(struct tty *tty, u_int class) { if (tty->class != 0) return; tty->class = class; } void tty_stop_tty(struct tty *tty) { struct winsize ws; if (!(tty->flags & TTY_STARTED)) return; tty->flags &= ~TTY_STARTED; bufferevent_disable(tty->event, EV_READ|EV_WRITE); /* * Be flexible about error handling and try not kill the server just * because the fd is invalid. Things like ssh -t can easily leave us * with a dead tty. */ if (ioctl(tty->fd, TIOCGWINSZ, &ws) == -1) return; if (tcsetattr(tty->fd, TCSANOW, &tty->tio) == -1) return; tty_raw(tty, tty_term_string2(tty->term, TTYC_CSR, 0, ws.ws_row - 1)); if (tty_use_acs(tty)) tty_raw(tty, tty_term_string(tty->term, TTYC_RMACS)); tty_raw(tty, tty_term_string(tty->term, TTYC_SGR0)); tty_raw(tty, tty_term_string(tty->term, TTYC_RMKX)); tty_raw(tty, tty_term_string(tty->term, TTYC_CLEAR)); if (tty_term_has(tty->term, TTYC_CS1) && tty->cstyle != 0) { if (tty_term_has(tty->term, TTYC_CSR1)) tty_raw(tty, tty_term_string(tty->term, TTYC_CSR1)); else tty_raw(tty, tty_term_string1(tty->term, TTYC_CS1, 0)); } tty_raw(tty, tty_term_string(tty->term, TTYC_CR)); tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM)); if (tty_term_has(tty->term, TTYC_KMOUS)) tty_raw(tty, "\033[?1000l\033[?1006l\033[?1005l"); if (tty_term_has(tty->term, TTYC_XT)) tty_raw(tty, "\033[>4m\033[?1004l"); tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP)); setblocking(tty->fd, 1); } void tty_close(struct tty *tty) { if (tty->log_fd != -1) { close(tty->log_fd); tty->log_fd = -1; } if (event_initialized(&tty->key_timer)) evtimer_del(&tty->key_timer); tty_stop_tty(tty); if (tty->flags & TTY_OPENED) { bufferevent_free(tty->event); tty_term_free(tty->term); tty_keys_free(tty); tty->flags &= ~TTY_OPENED; } if (tty->fd != -1) { close(tty->fd); tty->fd = -1; } } void tty_free(struct tty *tty) { tty_close(tty); free(tty->ccolour); if (tty->path != NULL) free(tty->path); if (tty->termname != NULL) free(tty->termname); } 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_putcode_ptr1(struct tty *tty, enum tty_code_code code, const void *a) { if (a != NULL) tty_puts(tty, tty_term_ptr1(tty->term, code, a)); } void tty_putcode_ptr2(struct tty *tty, enum tty_code_code code, const void *a, const void *b) { if (a != NULL && b != NULL) tty_puts(tty, tty_term_ptr2(tty->term, code, a, b)); } void tty_puts(struct tty *tty, const char *s) { if (*s == '\0') return; bufferevent_write(tty->event, s, strlen(s)); if (tty->log_fd != -1) write(tty->log_fd, s, strlen(s)); } void tty_putc(struct tty *tty, u_char ch) { const char *acs; u_int sx; if (tty->cell.attr & GRID_ATTR_CHARSET) { acs = tty_acs_get(tty, ch); if (acs != NULL) bufferevent_write(tty->event, acs, strlen(acs)); else bufferevent_write(tty->event, &ch, 1); } else bufferevent_write(tty->event, &ch, 1); if (ch >= 0x20 && ch != 0x7f) { sx = tty->sx; if (tty->term->flags & TERM_EARLYWRAP) sx--; if (tty->cx >= sx) { tty->cx = 1; if (tty->cy != tty->rlower) tty->cy++; } else tty->cx++; } if (tty->log_fd != -1) write(tty->log_fd, &ch, 1); } void tty_putn(struct tty *tty, const void *buf, size_t len, u_int width) { bufferevent_write(tty->event, buf, len); if (tty->log_fd != -1) write(tty->log_fd, buf, len); tty->cx += width; } void tty_set_title(struct tty *tty, const char *title) { if (!tty_term_has(tty->term, TTYC_TSL) || !tty_term_has(tty->term, TTYC_FSL)) return; tty_putcode(tty, TTYC_TSL); tty_puts(tty, title); tty_putcode(tty, TTYC_FSL); } void tty_force_cursor_colour(struct tty *tty, const char *ccolour) { if (*ccolour == '\0') tty_putcode(tty, TTYC_CR); else tty_putcode_ptr1(tty, TTYC_CC, ccolour); free(tty->ccolour); tty->ccolour = xstrdup(ccolour); } void tty_update_mode(struct tty *tty, int mode, struct screen *s) { int changed; if (strcmp(s->ccolour, tty->ccolour)) tty_force_cursor_colour(tty, s->ccolour); if (tty->flags & TTY_NOCURSOR) mode &= ~MODE_CURSOR; changed = mode ^ tty->mode; if (changed & MODE_CURSOR) { if (mode & MODE_CURSOR) tty_putcode(tty, TTYC_CNORM); else tty_putcode(tty, TTYC_CIVIS); } if (tty->cstyle != s->cstyle) { if (tty_term_has(tty->term, TTYC_CS1)) { if (s->cstyle == 0 && tty_term_has(tty->term, TTYC_CSR1)) tty_putcode(tty, TTYC_CSR1); else tty_putcode1(tty, TTYC_CS1, s->cstyle); } tty->cstyle = s->cstyle; } if (changed & (ALL_MOUSE_MODES|MODE_MOUSE_UTF8)) { if (mode & ALL_MOUSE_MODES) { /* * Enable the UTF-8 (1005) extension if configured to. * Enable the SGR (1006) extension unconditionally, as * this is safe from misinterpretation. Do it in this * order, because in some terminals it's the last one * that takes effect and SGR is the preferred one. */ if (mode & MODE_MOUSE_UTF8) tty_puts(tty, "\033[?1005h"); else tty_puts(tty, "\033[?1005l"); tty_puts(tty, "\033[?1006h"); if (mode & MODE_MOUSE_ANY) tty_puts(tty, "\033[?1003h"); else if (mode & MODE_MOUSE_BUTTON) tty_puts(tty, "\033[?1002h"); else if (mode & MODE_MOUSE_STANDARD) tty_puts(tty, "\033[?1000h"); } else { if (tty->mode & MODE_MOUSE_ANY) tty_puts(tty, "\033[?1003l"); else if (tty->mode & MODE_MOUSE_BUTTON) tty_puts(tty, "\033[?1002l"); else if (tty->mode & MODE_MOUSE_STANDARD) tty_puts(tty, "\033[?1000l"); tty_puts(tty, "\033[?1006l"); if (tty->mode & MODE_MOUSE_UTF8) tty_puts(tty, "\033[?1005l"); } } if (changed & MODE_KKEYPAD) { if (mode & MODE_KKEYPAD) tty_putcode(tty, TTYC_SMKX); else tty_putcode(tty, TTYC_RMKX); } if (changed & MODE_BRACKETPASTE) { if (mode & MODE_BRACKETPASTE) tty_puts(tty, "\033[?2004h"); else tty_puts(tty, "\033[?2004l"); } tty->mode = mode; } void tty_emulate_repeat( struct tty *tty, enum tty_code_code code, enum tty_code_code code1, u_int n) { if (tty_term_has(tty->term, code)) tty_putcode1(tty, code, n); else { while (n-- > 0) tty_putcode(tty, code1); } } void tty_repeat_space(struct tty *tty, u_int n) { while (n-- > 0) tty_putc(tty, ' '); } /* * 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. */ 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); } /* * Redraw scroll region using data from screen (already updated). Used when * CSR not supported, or window is a pane that doesn't take up the full * width of the terminal. */ void tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; struct screen *s = wp->screen; u_int i; /* * If region is 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_line(tty, s, i, ctx->xoff, ctx->yoff); } else { for (i = ctx->orupper; i <= ctx->orlower; i++) tty_draw_line(tty, s, i, ctx->xoff, ctx->yoff); } } void tty_draw_line(struct tty *tty, struct screen *s, u_int py, u_int ox, u_int oy) { const struct grid_cell *gc; struct grid_line *gl; struct grid_cell tmpgc; struct utf8_data ud; u_int i, sx; tty_update_mode(tty, tty->mode & ~MODE_CURSOR, s); sx = screen_size_x(s); if (sx > s->grid->linedata[s->grid->hsize + py].cellsize) sx = s->grid->linedata[s->grid->hsize + py].cellsize; if (sx > tty->sx) sx = tty->sx; /* * Don't move the cursor to the start permission if it will wrap there * itself. */ gl = NULL; if (py != 0) gl = &s->grid->linedata[s->grid->hsize + py - 1]; if (oy + py == 0 || gl == NULL || !(gl->flags & GRID_LINE_WRAPPED) || tty->cx < tty->sx || ox != 0 || (oy + py != tty->cy + 1 && tty->cy != s->rlower + oy)) tty_cursor(tty, ox, oy + py); for (i = 0; i < sx; i++) { gc = grid_view_peek_cell(s->grid, i, py); if (screen_check_selection(s, i, py)) { memcpy(&tmpgc, &s->sel.cell, sizeof tmpgc); grid_cell_get(gc, &ud); grid_cell_set(&tmpgc, &ud); tmpgc.flags = gc->flags & ~(GRID_FLAG_FG256|GRID_FLAG_BG256); tmpgc.flags |= s->sel.cell.flags & (GRID_FLAG_FG256|GRID_FLAG_BG256); tty_cell(tty, &tmpgc); } else tty_cell(tty, gc); } if (sx >= tty->sx) { tty_update_mode(tty, tty->mode, s); return; } tty_reset(tty); tty_cursor(tty, ox + sx, oy + py); if (sx != screen_size_x(s) && ox + screen_size_x(s) >= tty->sx && tty_term_has(tty->term, TTYC_EL)) tty_putcode(tty, TTYC_EL); else tty_repeat_space(tty, screen_size_x(s) - sx); tty_update_mode(tty, tty->mode, s); } void tty_write( void (*cmdfn)(struct tty *, const struct tty_ctx *), struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; struct client *c; u_int i; /* wp can be NULL if updating the screen but not the terminal. */ if (wp == NULL) return; if (wp->window->flags & WINDOW_REDRAW || wp->flags & PANE_REDRAW) return; if (!window_pane_visible(wp) || wp->flags & PANE_DROP) return; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); if (c == NULL || c->session == NULL || c->tty.term == NULL) continue; if (c->flags & CLIENT_SUSPENDED) continue; if (c->tty.flags & TTY_FREEZE) continue; if (c->session->curw->window != wp->window) continue; ctx->xoff = wp->xoff; ctx->yoff = wp->yoff; if (status_at_line(c) == 0) ctx->yoff++; cmdfn(&c->tty, ctx); } } void tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; if (!tty_pane_full_width(tty, ctx)) { tty_draw_line(tty, wp->screen, ctx->ocy, ctx->xoff, ctx->yoff); return; } tty_reset(tty); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); if (tty_term_has(tty->term, TTYC_ICH) || tty_term_has(tty->term, TTYC_ICH1)) tty_emulate_repeat(tty, TTYC_ICH, TTYC_ICH1, ctx->num); else tty_draw_line(tty, wp->screen, ctx->ocy, ctx->xoff, ctx->yoff); } void tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; if (!tty_pane_full_width(tty, ctx) || (!tty_term_has(tty->term, TTYC_DCH) && !tty_term_has(tty->term, TTYC_DCH1))) { tty_draw_line(tty, wp->screen, ctx->ocy, ctx->xoff, ctx->yoff); return; } tty_reset(tty); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); if (tty_term_has(tty->term, TTYC_DCH) || tty_term_has(tty->term, TTYC_DCH1)) tty_emulate_repeat(tty, TTYC_DCH, TTYC_DCH1, ctx->num); } void tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx) { u_int i; tty_reset(tty); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); if (tty_term_has(tty->term, TTYC_ECH)) tty_putcode1(tty, TTYC_ECH, ctx->num); else { for (i = 0; i < ctx->num; i++) tty_putc(tty, ' '); } } void tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx) { if (!tty_pane_full_width(tty, ctx) || !tty_term_has(tty->term, TTYC_CSR) || !tty_term_has(tty->term, TTYC_IL1)) { tty_redraw_region(tty, ctx); return; } tty_reset(tty); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); tty_emulate_repeat(tty, TTYC_IL, TTYC_IL1, ctx->num); } void tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx) { if (!tty_pane_full_width(tty, ctx) || !tty_term_has(tty->term, TTYC_CSR) || !tty_term_has(tty->term, TTYC_DL1)) { tty_redraw_region(tty, ctx); return; } tty_reset(tty); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); tty_emulate_repeat(tty, TTYC_DL, TTYC_DL1, ctx->num); } void tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; struct screen *s = wp->screen; tty_reset(tty); tty_cursor_pane(tty, ctx, 0, ctx->ocy); if (tty_pane_full_width(tty, ctx) && tty_term_has(tty->term, TTYC_EL)) tty_putcode(tty, TTYC_EL); else tty_repeat_space(tty, screen_size_x(s)); } void tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; struct screen *s = wp->screen; tty_reset(tty); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); if (tty_pane_full_width(tty, ctx) && tty_term_has(tty->term, TTYC_EL)) tty_putcode(tty, TTYC_EL); else tty_repeat_space(tty, screen_size_x(s) - ctx->ocx); } void tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx) { tty_reset(tty); if (ctx->xoff == 0 && tty_term_has(tty->term, TTYC_EL1)) { tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); tty_putcode(tty, TTYC_EL1); } else { tty_cursor_pane(tty, ctx, 0, ctx->ocy); tty_repeat_space(tty, ctx->ocx + 1); } } void tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx) { if (ctx->ocy != ctx->orupper) return; if (!tty_pane_full_width(tty, ctx) || !tty_term_has(tty->term, TTYC_CSR) || !tty_term_has(tty->term, TTYC_RI)) { tty_redraw_region(tty, ctx); return; } tty_reset(tty); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper); tty_putcode(tty, TTYC_RI); } void tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; if (ctx->ocy != ctx->orlower) return; if (!tty_pane_full_width(tty, ctx) || !tty_term_has(tty->term, TTYC_CSR)) { if (tty_large_region(tty, ctx)) wp->flags |= PANE_REDRAW; else tty_redraw_region(tty, ctx); return; } /* * If this line wrapped naturally (ctx->num is nonzero), don't do * anything - the cursor can just be moved to the last cell and wrap * naturally. */ if (ctx->num && !(tty->term->flags & TERM_EARLYWRAP)) return; tty_reset(tty); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); tty_putc(tty, '\n'); } void tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; struct screen *s = wp->screen; u_int i, j; tty_reset(tty); tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); if (tty_pane_full_width(tty, ctx) && tty_term_has(tty->term, TTYC_EL)) { tty_putcode(tty, TTYC_EL); if (ctx->ocy != screen_size_y(s) - 1) { tty_cursor_pane(tty, ctx, 0, ctx->ocy + 1); for (i = ctx->ocy + 1; i < screen_size_y(s); i++) { tty_putcode(tty, TTYC_EL); if (i == screen_size_y(s) - 1) continue; tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1); tty->cy++; } } } else { tty_repeat_space(tty, screen_size_x(s) - ctx->ocx); for (j = ctx->ocy + 1; j < screen_size_y(s); j++) { tty_cursor_pane(tty, ctx, 0, j); tty_repeat_space(tty, screen_size_x(s)); } } } void tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; struct screen *s = wp->screen; u_int i, j; tty_reset(tty); tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); tty_cursor_pane(tty, ctx, 0, 0); if (tty_pane_full_width(tty, ctx) && tty_term_has(tty->term, TTYC_EL)) { for (i = 0; i < ctx->ocy; i++) { tty_putcode(tty, TTYC_EL); tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1); tty->cy++; } } else { for (j = 0; j < ctx->ocy; j++) { tty_cursor_pane(tty, ctx, 0, j); tty_repeat_space(tty, screen_size_x(s)); } } tty_repeat_space(tty, ctx->ocx + 1); } void tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; struct screen *s = wp->screen; u_int i, j; tty_reset(tty); tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); tty_cursor_pane(tty, ctx, 0, 0); if (tty_pane_full_width(tty, ctx) && tty_term_has(tty->term, TTYC_EL)) { for (i = 0; i < screen_size_y(s); i++) { tty_putcode(tty, TTYC_EL); if (i != screen_size_y(s) - 1) { tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1); tty->cy++; } } } else { for (j = 0; j < screen_size_y(s); j++) { tty_cursor_pane(tty, ctx, 0, j); tty_repeat_space(tty, screen_size_x(s)); } } } void tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; struct screen *s = wp->screen; u_int i, j; tty_reset(tty); tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); for (j = 0; j < screen_size_y(s); j++) { tty_cursor_pane(tty, ctx, 0, j); for (i = 0; i < screen_size_x(s); i++) tty_putc(tty, 'E'); } } void tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; struct screen *s = wp->screen; u_int cx; u_int width; tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); /* Is the cursor in the very last position? */ width = grid_cell_width(ctx->cell); if (ctx->ocx > wp->sx - width) { if (ctx->xoff != 0 || wp->sx != tty->sx) { /* * The pane doesn't fill the entire line, the linefeed * will already have happened, so just move the cursor. */ if (ctx->ocy != wp->yoff + wp->screen->rlower) tty_cursor_pane(tty, ctx, 0, ctx->ocy + 1); else tty_cursor_pane(tty, ctx, 0, ctx->ocy); } else if (tty->cx < tty->sx) { /* * The cursor isn't in the last position already, so * move as far left as possible and redraw the last * cell to move into the last position. */ cx = screen_size_x(s) - grid_cell_width(&ctx->last_cell); tty_cursor_pane(tty, ctx, cx, ctx->ocy); tty_cell(tty, &ctx->last_cell); } } else tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); tty_cell(tty, ctx->cell); } void tty_cmd_utf8character(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; /* * Cannot rely on not being a partial character, so just redraw the * whole line. */ tty_draw_line(tty, wp->screen, ctx->ocy, ctx->xoff, ctx->yoff); } void tty_cmd_setselection(struct tty *tty, const struct tty_ctx *ctx) { char *buf; size_t off; if (!tty_term_has(tty->term, TTYC_MS)) return; off = 4 * ((ctx->num + 2) / 3) + 1; /* storage for base64 */ buf = xmalloc(off); b64_ntop(ctx->ptr, ctx->num, buf, off); tty_putcode_ptr2(tty, TTYC_MS, "", buf); free(buf); } void tty_cmd_rawstring(struct tty *tty, const struct tty_ctx *ctx) { u_int i; u_char *str = ctx->ptr; for (i = 0; i < ctx->num; i++) tty_putc(tty, str[i]); tty->cx = tty->cy = UINT_MAX; tty->rupper = tty->rlower = UINT_MAX; tty_reset(tty); tty_cursor(tty, 0, 0); } void tty_cell(struct tty *tty, const struct grid_cell *gc) { struct utf8_data ud; u_int i; /* Skip last character if terminal is stupid. */ if (tty->term->flags & TERM_EARLYWRAP && tty->cy == tty->sy - 1 && tty->cx == tty->sx - 1) return; /* If this is a padding character, do nothing. */ if (gc->flags & GRID_FLAG_PADDING) return; /* Set the attributes. */ tty_attributes(tty, gc); /* Get the cell and if ASCII write with putc to do ACS translation. */ grid_cell_get(gc, &ud); if (ud.size == 1) { if (*ud.data < 0x20 || *ud.data == 0x7f) return; tty_putc(tty, *ud.data); return; } /* If not UTF-8, write _. */ if (!(tty->flags & TTY_UTF8)) { for (i = 0; i < ud.width; i++) tty_putc(tty, '_'); return; } /* Write the data. */ tty_putn(tty, ud.data, ud.size, ud.width); } void tty_reset(struct tty *tty) { struct grid_cell *gc = &tty->cell; if (memcmp(gc, &grid_default_cell, sizeof *gc) == 0) return; if ((gc->attr & GRID_ATTR_CHARSET) && tty_use_acs(tty)) tty_putcode(tty, TTYC_RMACS); tty_putcode(tty, TTYC_SGR0); memcpy(gc, &grid_default_cell, sizeof *gc); } /* Set region inside pane. */ void tty_region_pane( struct tty *tty, const struct tty_ctx *ctx, u_int rupper, u_int rlower) { tty_region(tty, ctx->yoff + rupper, ctx->yoff + rlower); } /* Set region at absolute position. */ void tty_region(struct tty *tty, u_int rupper, u_int rlower) { if (tty->rlower == rlower && tty->rupper == rupper) return; if (!tty_term_has(tty->term, TTYC_CSR)) return; tty->rupper = rupper; tty->rlower = rlower; /* * Some terminals (such as PuTTY) do not correctly reset the cursor to * 0,0 if it is beyond the last column (they do not reset their wrap * flag so further output causes a line feed). As a workaround, do an * explicit move to 0 first. */ if (tty->cx >= tty->sx) tty_cursor(tty, 0, tty->cy); tty_putcode2(tty, TTYC_CSR, tty->rupper, tty->rlower); tty_cursor(tty, 0, 0); } /* Move cursor inside pane. */ void tty_cursor_pane(struct tty *tty, const struct tty_ctx *ctx, u_int cx, u_int cy) { tty_cursor(tty, ctx->xoff + cx, ctx->yoff + cy); } /* Move cursor to absolute position. */ void tty_cursor(struct tty *tty, u_int cx, u_int cy) { struct tty_term *term = tty->term; u_int thisx, thisy; int change; if (cx > tty->sx - 1) cx = tty->sx - 1; thisx = tty->cx; thisy = tty->cy; /* No change. */ if (cx == thisx && cy == thisy) return; /* Very end of the line, just use absolute movement. */ if (thisx > tty->sx - 1) goto absolute; /* Move to home position (0, 0). */ if (cx == 0 && cy == 0 && tty_term_has(term, TTYC_HOME)) { tty_putcode(tty, TTYC_HOME); goto out; } /* Zero on the next line. */ if (cx == 0 && cy == thisy + 1 && thisy != tty->rlower) { tty_putc(tty, '\r'); tty_putc(tty, '\n'); goto out; } /* Moving column or row. */ if (cy == thisy) { /* * Moving column only, row staying the same. */ /* To left edge. */ if (cx == 0) { tty_putc(tty, '\r'); goto out; } /* One to the left. */ if (cx == thisx - 1 && tty_term_has(term, TTYC_CUB1)) { tty_putcode(tty, TTYC_CUB1); goto out; } /* One to the right. */ if (cx == thisx + 1 && tty_term_has(term, TTYC_CUF1)) { tty_putcode(tty, TTYC_CUF1); goto out; } /* Calculate difference. */ change = thisx - cx; /* +ve left, -ve right */ /* * Use HPA if change is larger than absolute, otherwise move * the cursor with CUB/CUF. */ if ((u_int) abs(change) > cx && tty_term_has(term, TTYC_HPA)) { tty_putcode1(tty, TTYC_HPA, cx); goto out; } else if (change > 0 && tty_term_has(term, TTYC_CUB)) { tty_putcode1(tty, TTYC_CUB, change); goto out; } else if (change < 0 && tty_term_has(term, TTYC_CUF)) { tty_putcode1(tty, TTYC_CUF, -change); goto out; } } else if (cx == thisx) { /* * Moving row only, column staying the same. */ /* One above. */ if (thisy != tty->rupper && cy == thisy - 1 && tty_term_has(term, TTYC_CUU1)) { tty_putcode(tty, TTYC_CUU1); goto out; } /* One below. */ if (thisy != tty->rlower && cy == thisy + 1 && tty_term_has(term, TTYC_CUD1)) { tty_putcode(tty, TTYC_CUD1); goto out; } /* Calculate difference. */ change = thisy - cy; /* +ve up, -ve down */ /* * Try to use VPA if change is larger than absolute or if this * change would cross the scroll region, otherwise use CUU/CUD. */ if ((u_int) abs(change) > cy || (change < 0 && cy - change > tty->rlower) || (change > 0 && cy - change < tty->rupper)) { if (tty_term_has(term, TTYC_VPA)) { tty_putcode1(tty, TTYC_VPA, cy); goto out; } } else if (change > 0 && tty_term_has(term, TTYC_CUU)) { tty_putcode1(tty, TTYC_CUU, change); goto out; } else if (change < 0 && tty_term_has(term, TTYC_CUD)) { tty_putcode1(tty, TTYC_CUD, -change); goto out; } } absolute: /* Absolute movement. */ tty_putcode2(tty, TTYC_CUP, cy, cx); out: tty->cx = cx; tty->cy = cy; } void tty_attributes(struct tty *tty, const struct grid_cell *gc) { struct grid_cell *tc = &tty->cell, gc2; u_char changed; memcpy(&gc2, gc, sizeof gc2); /* * If no setab, try to use the reverse attribute as a best-effort for a * non-default background. This is a bit of a hack but it doesn't do * any serious harm and makes a couple of applications happier. */ if (!tty_term_has(tty->term, TTYC_SETAB)) { if (gc2.attr & GRID_ATTR_REVERSE) { if (gc2.fg != 7 && gc2.fg != 8) gc2.attr &= ~GRID_ATTR_REVERSE; } else { if (gc2.bg != 0 && gc2.bg != 8) gc2.attr |= GRID_ATTR_REVERSE; } } /* Fix up the colours if necessary. */ tty_check_fg(tty, &gc2); tty_check_bg(tty, &gc2); /* If any bits are being cleared, reset everything. */ if (tc->attr & ~gc2.attr) tty_reset(tty); /* * Set the colours. This may call tty_reset() (so it comes next) and * may add to (NOT remove) the desired attributes by changing new_attr. */ tty_colours(tty, &gc2); /* Filter out attribute bits already set. */ changed = gc2.attr & ~tc->attr; tc->attr = gc2.attr; /* Set the attributes. */ if (changed & GRID_ATTR_BRIGHT) tty_putcode(tty, TTYC_BOLD); if (changed & GRID_ATTR_DIM) tty_putcode(tty, TTYC_DIM); if (changed & GRID_ATTR_ITALICS) { if (tty_term_has(tty->term, TTYC_SITM)) tty_putcode(tty, TTYC_SITM); else tty_putcode(tty, TTYC_SMSO); } if (changed & GRID_ATTR_UNDERSCORE) tty_putcode(tty, TTYC_SMUL); if (changed & GRID_ATTR_BLINK) tty_putcode(tty, TTYC_BLINK); if (changed & GRID_ATTR_REVERSE) { if (tty_term_has(tty->term, TTYC_REV)) tty_putcode(tty, TTYC_REV); else if (tty_term_has(tty->term, TTYC_SMSO)) tty_putcode(tty, TTYC_SMSO); } if (changed & GRID_ATTR_HIDDEN) tty_putcode(tty, TTYC_INVIS); if ((changed & GRID_ATTR_CHARSET) && tty_use_acs(tty)) tty_putcode(tty, TTYC_SMACS); } void tty_colours(struct tty *tty, const struct grid_cell *gc) { struct grid_cell *tc = &tty->cell; u_char fg = gc->fg, bg = gc->bg, flags = gc->flags; int have_ax, fg_default, bg_default; /* No changes? Nothing is necessary. */ if (fg == tc->fg && bg == tc->bg && ((flags ^ tc->flags) & (GRID_FLAG_FG256|GRID_FLAG_BG256)) == 0) return; /* * Is either the default colour? This is handled specially because the * best solution might be to reset both colours to default, in which * case if only one is default need to fall onward to set the other * colour. */ fg_default = (fg == 8 && !(flags & GRID_FLAG_FG256)); bg_default = (bg == 8 && !(flags & GRID_FLAG_BG256)); if (fg_default || bg_default) { /* * If don't have AX but do have op, send sgr0 (op can't * actually be used because it is sometimes the same as sgr0 * and sometimes isn't). This resets both colours to default. * * Otherwise, try to set the default colour only as needed. */ have_ax = tty_term_has(tty->term, TTYC_AX); if (!have_ax && tty_term_has(tty->term, TTYC_OP)) tty_reset(tty); else { if (fg_default && (tc->fg != 8 || tc->flags & GRID_FLAG_FG256)) { if (have_ax) tty_puts(tty, "\033[39m"); else if (tc->fg != 7 || tc->flags & GRID_FLAG_FG256) tty_putcode1(tty, TTYC_SETAF, 7); tc->fg = 8; tc->flags &= ~GRID_FLAG_FG256; } if (bg_default && (tc->bg != 8 || tc->flags & GRID_FLAG_BG256)) { if (have_ax) tty_puts(tty, "\033[49m"); else if (tc->bg != 0 || tc->flags & GRID_FLAG_BG256) tty_putcode1(tty, TTYC_SETAB, 0); tc->bg = 8; tc->flags &= ~GRID_FLAG_BG256; } } } /* Set the foreground colour. */ if (!fg_default && (fg != tc->fg || ((flags & GRID_FLAG_FG256) != (tc->flags & GRID_FLAG_FG256)))) tty_colours_fg(tty, gc); /* * Set the background colour. This must come after the foreground as * tty_colour_fg() can call tty_reset(). */ if (!bg_default && (bg != tc->bg || ((flags & GRID_FLAG_BG256) != (tc->flags & GRID_FLAG_BG256)))) tty_colours_bg(tty, gc); } void tty_check_fg(struct tty *tty, struct grid_cell *gc) { u_int colours; /* Is this a 256-colour colour? */ if (gc->flags & GRID_FLAG_FG256) { /* And not a 256 colour mode? */ if (!(tty->term->flags & TERM_88COLOURS) && !(tty->term_flags & TERM_88COLOURS) && !(tty->term->flags & TERM_256COLOURS) && !(tty->term_flags & TERM_256COLOURS)) { gc->fg = colour_256to16(gc->fg); if (gc->fg & 8) { gc->fg &= 7; gc->attr |= GRID_ATTR_BRIGHT; } else gc->attr &= ~GRID_ATTR_BRIGHT; gc->flags &= ~GRID_FLAG_FG256; } return; } /* Is this an aixterm colour? */ colours = tty_term_number(tty->term, TTYC_COLORS); if (gc->fg >= 90 && gc->fg <= 97 && colours < 16) { gc->fg -= 90; gc->attr |= GRID_ATTR_BRIGHT; } } void tty_check_bg(struct tty *tty, struct grid_cell *gc) { u_int colours; /* Is this a 256-colour colour? */ if (gc->flags & GRID_FLAG_BG256) { /* * And not a 256 colour mode? Translate to 16-colour * palette. Bold background doesn't exist portably, so just * discard the bold bit if set. */ if (!(tty->term->flags & TERM_88COLOURS) && !(tty->term_flags & TERM_88COLOURS) && !(tty->term->flags & TERM_256COLOURS) && !(tty->term_flags & TERM_256COLOURS)) { gc->bg = colour_256to16(gc->bg); if (gc->bg & 8) gc->bg &= 7; gc->attr &= ~GRID_ATTR_BRIGHT; gc->flags &= ~GRID_FLAG_BG256; } return; } /* Is this an aixterm colour? */ colours = tty_term_number(tty->term, TTYC_COLORS); if (gc->bg >= 90 && gc->bg <= 97 && colours < 16) { gc->bg -= 90; gc->attr |= GRID_ATTR_BRIGHT; } } void tty_colours_fg(struct tty *tty, const struct grid_cell *gc) { struct grid_cell *tc = &tty->cell; u_char fg = gc->fg; char s[32]; /* Is this a 256-colour colour? */ if (gc->flags & GRID_FLAG_FG256) { /* Try as 256 colours or translating to 88. */ if (tty_try_256(tty, fg, "38") == 0) goto save_fg; if (tty_try_88(tty, fg, "38") == 0) goto save_fg; /* Else already handled by tty_check_fg. */ return; } /* Is this an aixterm bright colour? */ if (fg >= 90 && fg <= 97) { xsnprintf(s, sizeof s, "\033[%dm", fg); tty_puts(tty, s); goto save_fg; } /* Otherwise set the foreground colour. */ tty_putcode1(tty, TTYC_SETAF, fg); save_fg: /* Save the new values in the terminal current cell. */ tc->fg = fg; tc->flags &= ~GRID_FLAG_FG256; tc->flags |= gc->flags & GRID_FLAG_FG256; } void tty_colours_bg(struct tty *tty, const struct grid_cell *gc) { struct grid_cell *tc = &tty->cell; u_char bg = gc->bg; char s[32]; /* Is this a 256-colour colour? */ if (gc->flags & GRID_FLAG_BG256) { /* Try as 256 colours or translating to 88. */ if (tty_try_256(tty, bg, "48") == 0) goto save_bg; if (tty_try_88(tty, bg, "48") == 0) goto save_bg; /* Else already handled by tty_check_bg. */ return; } /* Is this an aixterm bright colour? */ if (bg >= 90 && bg <= 97) { /* 16 colour terminals or above only. */ if (tty_term_number(tty->term, TTYC_COLORS) >= 16) { xsnprintf(s, sizeof s, "\033[%dm", bg + 10); tty_puts(tty, s); goto save_bg; } bg -= 90; /* no such thing as a bold background */ } /* Otherwise set the background colour. */ tty_putcode1(tty, TTYC_SETAB, bg); save_bg: /* Save the new values in the terminal current cell. */ tc->bg = bg; tc->flags &= ~GRID_FLAG_BG256; tc->flags |= gc->flags & GRID_FLAG_BG256; } int tty_try_256(struct tty *tty, u_char colour, const char *type) { char s[32]; if (!(tty->term->flags & TERM_256COLOURS) && !(tty->term_flags & TERM_256COLOURS)) return (-1); xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour); tty_puts(tty, s); return (0); } int tty_try_88(struct tty *tty, u_char colour, const char *type) { char s[32]; if (!(tty->term->flags & TERM_88COLOURS) && !(tty->term_flags & TERM_88COLOURS)) return (-1); colour = colour_256to88(colour); xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour); tty_puts(tty, s); return (0); } void tty_bell(struct tty *tty) { tty_putcode(tty, TTYC_BEL); } tmate-1.8.10/utf8.c000066400000000000000000000246721242461015400137570ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" struct utf8_width_entry { u_int first; u_int last; int width; struct utf8_width_entry *left; struct utf8_width_entry *right; }; /* Random order. Not optimal but it'll do for now... */ struct utf8_width_entry utf8_width_table[] = { { 0x00951, 0x00954, 0, NULL, NULL }, { 0x00ccc, 0x00ccd, 0, NULL, NULL }, { 0x0fff9, 0x0fffb, 0, NULL, NULL }, { 0x20000, 0x2fffd, 2, NULL, NULL }, { 0x00ebb, 0x00ebc, 0, NULL, NULL }, { 0x01932, 0x01932, 0, NULL, NULL }, { 0x0070f, 0x0070f, 0, NULL, NULL }, { 0x00a70, 0x00a71, 0, NULL, NULL }, { 0x02329, 0x02329, 2, NULL, NULL }, { 0x00acd, 0x00acd, 0, NULL, NULL }, { 0x00ac7, 0x00ac8, 0, NULL, NULL }, { 0x00a3c, 0x00a3c, 0, NULL, NULL }, { 0x009cd, 0x009cd, 0, NULL, NULL }, { 0x00591, 0x005bd, 0, NULL, NULL }, { 0x01058, 0x01059, 0, NULL, NULL }, { 0x0ffe0, 0x0ffe6, 2, NULL, NULL }, { 0x01100, 0x0115f, 2, NULL, NULL }, { 0x0fe20, 0x0fe23, 0, NULL, NULL }, { 0x0302a, 0x0302f, 0, NULL, NULL }, { 0x01772, 0x01773, 0, NULL, NULL }, { 0x005bf, 0x005bf, 0, NULL, NULL }, { 0x006ea, 0x006ed, 0, NULL, NULL }, { 0x00bc0, 0x00bc0, 0, NULL, NULL }, { 0x00962, 0x00963, 0, NULL, NULL }, { 0x01732, 0x01734, 0, NULL, NULL }, { 0x00d41, 0x00d43, 0, NULL, NULL }, { 0x01b42, 0x01b42, 0, NULL, NULL }, { 0x00a41, 0x00a42, 0, NULL, NULL }, { 0x00eb4, 0x00eb9, 0, NULL, NULL }, { 0x00b01, 0x00b01, 0, NULL, NULL }, { 0x00e34, 0x00e3a, 0, NULL, NULL }, { 0x03040, 0x03098, 2, NULL, NULL }, { 0x0093c, 0x0093c, 0, NULL, NULL }, { 0x00c4a, 0x00c4d, 0, NULL, NULL }, { 0x01032, 0x01032, 0, NULL, NULL }, { 0x00f37, 0x00f37, 0, NULL, NULL }, { 0x00901, 0x00902, 0, NULL, NULL }, { 0x00cbf, 0x00cbf, 0, NULL, NULL }, { 0x0a806, 0x0a806, 0, NULL, NULL }, { 0x00dd2, 0x00dd4, 0, NULL, NULL }, { 0x00f71, 0x00f7e, 0, NULL, NULL }, { 0x01752, 0x01753, 0, NULL, NULL }, { 0x1d242, 0x1d244, 0, NULL, NULL }, { 0x005c1, 0x005c2, 0, NULL, NULL }, { 0x0309b, 0x0a4cf, 2, NULL, NULL }, { 0xe0100, 0xe01ef, 0, NULL, NULL }, { 0x017dd, 0x017dd, 0, NULL, NULL }, { 0x00600, 0x00603, 0, NULL, NULL }, { 0x009e2, 0x009e3, 0, NULL, NULL }, { 0x00cc6, 0x00cc6, 0, NULL, NULL }, { 0x0a80b, 0x0a80b, 0, NULL, NULL }, { 0x01712, 0x01714, 0, NULL, NULL }, { 0x00b3c, 0x00b3c, 0, NULL, NULL }, { 0x01b00, 0x01b03, 0, NULL, NULL }, { 0x007eb, 0x007f3, 0, NULL, NULL }, { 0xe0001, 0xe0001, 0, NULL, NULL }, { 0x1d185, 0x1d18b, 0, NULL, NULL }, { 0x0feff, 0x0feff, 0, NULL, NULL }, { 0x01b36, 0x01b3a, 0, NULL, NULL }, { 0x01920, 0x01922, 0, NULL, NULL }, { 0x00670, 0x00670, 0, NULL, NULL }, { 0x00f90, 0x00f97, 0, NULL, NULL }, { 0x01927, 0x01928, 0, NULL, NULL }, { 0x0200b, 0x0200f, 0, NULL, NULL }, { 0x0ff00, 0x0ff60, 2, NULL, NULL }, { 0x0f900, 0x0faff, 2, NULL, NULL }, { 0x0fb1e, 0x0fb1e, 0, NULL, NULL }, { 0x00cbc, 0x00cbc, 0, NULL, NULL }, { 0x00eb1, 0x00eb1, 0, NULL, NULL }, { 0x10a38, 0x10a3a, 0, NULL, NULL }, { 0x007a6, 0x007b0, 0, NULL, NULL }, { 0x00f80, 0x00f84, 0, NULL, NULL }, { 0x005c4, 0x005c5, 0, NULL, NULL }, { 0x0ac00, 0x0d7a3, 2, NULL, NULL }, { 0x017c9, 0x017d3, 0, NULL, NULL }, { 0x00d4d, 0x00d4d, 0, NULL, NULL }, { 0x1d167, 0x1d169, 0, NULL, NULL }, { 0x01036, 0x01037, 0, NULL, NULL }, { 0xe0020, 0xe007f, 0, NULL, NULL }, { 0x00f35, 0x00f35, 0, NULL, NULL }, { 0x017b4, 0x017b5, 0, NULL, NULL }, { 0x0206a, 0x0206f, 0, NULL, NULL }, { 0x00c46, 0x00c48, 0, NULL, NULL }, { 0x01939, 0x0193b, 0, NULL, NULL }, { 0x01dc0, 0x01dca, 0, NULL, NULL }, { 0x10a0c, 0x10a0f, 0, NULL, NULL }, { 0x0102d, 0x01030, 0, NULL, NULL }, { 0x017c6, 0x017c6, 0, NULL, NULL }, { 0x00ec8, 0x00ecd, 0, NULL, NULL }, { 0x00b41, 0x00b43, 0, NULL, NULL }, { 0x017b7, 0x017bd, 0, NULL, NULL }, { 0x1d173, 0x1d182, 0, NULL, NULL }, { 0x00a47, 0x00a48, 0, NULL, NULL }, { 0x0232a, 0x0232a, 2, NULL, NULL }, { 0x01b3c, 0x01b3c, 0, NULL, NULL }, { 0x10a01, 0x10a03, 0, NULL, NULL }, { 0x00ae2, 0x00ae3, 0, NULL, NULL }, { 0x00483, 0x00486, 0, NULL, NULL }, { 0x0135f, 0x0135f, 0, NULL, NULL }, { 0x01a17, 0x01a18, 0, NULL, NULL }, { 0x006e7, 0x006e8, 0, NULL, NULL }, #ifndef __APPLE__ { 0x03099, 0x0309a, 0, NULL, NULL }, #endif { 0x00b4d, 0x00b4d, 0, NULL, NULL }, { 0x00ce2, 0x00ce3, 0, NULL, NULL }, { 0x00bcd, 0x00bcd, 0, NULL, NULL }, { 0x00610, 0x00615, 0, NULL, NULL }, { 0x00f99, 0x00fbc, 0, NULL, NULL }, { 0x009c1, 0x009c4, 0, NULL, NULL }, { 0x00730, 0x0074a, 0, NULL, NULL }, { 0x00300, 0x0036f, 0, NULL, NULL }, { 0x03030, 0x0303e, 2, NULL, NULL }, { 0x01b34, 0x01b34, 0, NULL, NULL }, { 0x1d1aa, 0x1d1ad, 0, NULL, NULL }, { 0x00dca, 0x00dca, 0, NULL, NULL }, { 0x006d6, 0x006e4, 0, NULL, NULL }, { 0x00f86, 0x00f87, 0, NULL, NULL }, { 0x00b3f, 0x00b3f, 0, NULL, NULL }, { 0x0fe30, 0x0fe6f, 2, NULL, NULL }, { 0x01039, 0x01039, 0, NULL, NULL }, { 0x0094d, 0x0094d, 0, NULL, NULL }, { 0x00c55, 0x00c56, 0, NULL, NULL }, { 0x00488, 0x00489, 0, NULL, NULL }, { 0x00e47, 0x00e4e, 0, NULL, NULL }, { 0x00a81, 0x00a82, 0, NULL, NULL }, { 0x00ac1, 0x00ac5, 0, NULL, NULL }, { 0x0202a, 0x0202e, 0, NULL, NULL }, { 0x00dd6, 0x00dd6, 0, NULL, NULL }, { 0x018a9, 0x018a9, 0, NULL, NULL }, { 0x0064b, 0x0065e, 0, NULL, NULL }, { 0x00abc, 0x00abc, 0, NULL, NULL }, { 0x00b82, 0x00b82, 0, NULL, NULL }, { 0x00f39, 0x00f39, 0, NULL, NULL }, { 0x020d0, 0x020ef, 0, NULL, NULL }, { 0x01dfe, 0x01dff, 0, NULL, NULL }, { 0x30000, 0x3fffd, 2, NULL, NULL }, { 0x00711, 0x00711, 0, NULL, NULL }, { 0x0fe00, 0x0fe0f, 0, NULL, NULL }, { 0x01160, 0x011ff, 0, NULL, NULL }, { 0x0180b, 0x0180d, 0, NULL, NULL }, { 0x10a3f, 0x10a3f, 0, NULL, NULL }, { 0x00981, 0x00981, 0, NULL, NULL }, { 0x0a825, 0x0a826, 0, NULL, NULL }, { 0x00941, 0x00948, 0, NULL, NULL }, { 0x01b6b, 0x01b73, 0, NULL, NULL }, { 0x00e31, 0x00e31, 0, NULL, NULL }, { 0x0fe10, 0x0fe19, 2, NULL, NULL }, { 0x00a01, 0x00a02, 0, NULL, NULL }, { 0x00a4b, 0x00a4d, 0, NULL, NULL }, { 0x00f18, 0x00f19, 0, NULL, NULL }, { 0x00fc6, 0x00fc6, 0, NULL, NULL }, { 0x02e80, 0x03029, 2, NULL, NULL }, { 0x00b56, 0x00b56, 0, NULL, NULL }, { 0x009bc, 0x009bc, 0, NULL, NULL }, { 0x005c7, 0x005c7, 0, NULL, NULL }, { 0x02060, 0x02063, 0, NULL, NULL }, { 0x00c3e, 0x00c40, 0, NULL, NULL }, { 0x10a05, 0x10a06, 0, NULL, NULL }, }; struct utf8_width_entry *utf8_width_root = NULL; int utf8_overlap(struct utf8_width_entry *, struct utf8_width_entry *); u_int utf8_combine(const struct utf8_data *); u_int utf8_width(const struct utf8_data *); /* * Open UTF-8 sequence. * * 11000010-11011111 C2-DF start of 2-byte sequence * 11100000-11101111 E0-EF start of 3-byte sequence * 11110000-11110100 F0-F4 start of 4-byte sequence * * Returns 1 if more UTF-8 to come, 0 if not UTF-8. */ int utf8_open(struct utf8_data *utf8data, u_char ch) { memset(utf8data, 0, sizeof *utf8data); if (ch >= 0xc2 && ch <= 0xdf) utf8data->size = 2; else if (ch >= 0xe0 && ch <= 0xef) utf8data->size = 3; else if (ch >= 0xf0 && ch <= 0xf4) utf8data->size = 4; else return (0); utf8_append(utf8data, ch); return (1); } /* * Append character to UTF-8, closing if finished. * * Returns 1 if more UTF-8 data to come, 0 if finished. */ int utf8_append(struct utf8_data *utf8data, u_char ch) { if (utf8data->have >= utf8data->size) fatalx("UTF-8 character overflow"); if (utf8data->size > sizeof utf8data->data) fatalx("UTF-8 character size too large"); utf8data->data[utf8data->have++] = ch; if (utf8data->have != utf8data->size) return (1); utf8data->width = utf8_width(utf8data); return (0); } /* Check if two width tree entries overlap. */ int utf8_overlap( struct utf8_width_entry *item1, struct utf8_width_entry *item2) { if (item1->first >= item2->first && item1->first <= item2->last) return (1); if (item1->last >= item2->first && item1->last <= item2->last) return (1); if (item2->first >= item1->first && item2->first <= item1->last) return (1); if (item2->last >= item1->first && item2->last <= item1->last) return (1); return (0); } /* Build UTF-8 width tree. */ void utf8_build(void) { struct utf8_width_entry **ptr, *item, *node; u_int i, j; for (i = 0; i < nitems(utf8_width_table); i++) { item = &utf8_width_table[i]; for (j = 0; j < nitems(utf8_width_table); j++) { if (i != j && utf8_overlap(item, &utf8_width_table[j])) log_fatalx("utf8 overlap: %u %u", i, j); } ptr = &utf8_width_root; while (*ptr != NULL) { node = *ptr; if (item->last < node->first) ptr = &(node->left); else if (item->first > node->last) ptr = &(node->right); } *ptr = item; } } /* Combine UTF-8 into 32-bit Unicode. */ u_int utf8_combine(const struct utf8_data *utf8data) { u_int value; value = 0xff; switch (utf8data->size) { case 1: value = utf8data->data[0]; break; case 2: value = utf8data->data[1] & 0x3f; value |= (utf8data->data[0] & 0x1f) << 6; break; case 3: value = utf8data->data[2] & 0x3f; value |= (utf8data->data[1] & 0x3f) << 6; value |= (utf8data->data[0] & 0x0f) << 12; break; case 4: value = utf8data->data[3] & 0x3f; value |= (utf8data->data[2] & 0x3f) << 6; value |= (utf8data->data[1] & 0x3f) << 12; value |= (utf8data->data[0] & 0x3f) << 18; break; } return (value); } /* Split a two-byte UTF-8 character. */ u_int utf8_split2(u_int uc, u_char *ptr) { if (uc > 0x7f) { ptr[0] = (uc >> 6) | 0xc0; ptr[1] = (uc & 0x3f) | 0x80; return (2); } ptr[0] = uc; return (1); } /* Lookup width of UTF-8 data in tree. */ u_int utf8_width(const struct utf8_data *utf8data) { struct utf8_width_entry *item; u_int value; value = utf8_combine(utf8data); item = utf8_width_root; while (item != NULL) { if (value < item->first) item = item->left; else if (value > item->last) item = item->right; else return (item->width); } return (1); } tmate-1.8.10/window-choose.c000066400000000000000000000575251242461015400156610ustar00rootroot00000000000000/* $Id$ */ /* * 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" struct screen *window_choose_init(struct window_pane *); void window_choose_free(struct window_pane *); void window_choose_resize(struct window_pane *, u_int, u_int); void window_choose_key(struct window_pane *, struct session *, int); void window_choose_mouse( struct window_pane *, struct session *, struct mouse_event *); void window_choose_default_callback(struct window_choose_data *); void window_choose_fire_callback( struct window_pane *, struct window_choose_data *); void window_choose_redraw_screen(struct window_pane *); void window_choose_write_line( struct window_pane *, struct screen_write_ctx *, u_int); void window_choose_scroll_up(struct window_pane *); void window_choose_scroll_down(struct window_pane *); void window_choose_collapse(struct window_pane *, struct session *); void window_choose_expand(struct window_pane *, struct session *, u_int); void window_choose_collapse_all(struct window_pane *); enum window_choose_input_type { WINDOW_CHOOSE_NORMAL = -1, WINDOW_CHOOSE_GOTO_ITEM, }; const struct window_mode window_choose_mode = { window_choose_init, window_choose_free, window_choose_resize, window_choose_key, window_choose_mouse, NULL, }; struct window_choose_mode_data { struct screen screen; struct mode_key_data mdata; ARRAY_DECL(, struct window_choose_mode_item) list; ARRAY_DECL(, struct window_choose_mode_item) old_list; int width; u_int top; u_int selected; enum window_choose_input_type input_type; const char *input_prompt; char *input_str; void (*callbackfn)(struct window_choose_data *); }; void window_choose_free1(struct window_choose_mode_data *); int window_choose_key_index(struct window_choose_mode_data *, u_int); int window_choose_index_key(struct window_choose_mode_data *, int); void window_choose_prompt_input(enum window_choose_input_type, const char *, struct window_pane *, int); void window_choose_add(struct window_pane *wp, struct window_choose_data *wcd) { struct window_choose_mode_data *data = wp->modedata; struct window_choose_mode_item *item; char tmp[10]; ARRAY_EXPAND(&data->list, 1); item = &ARRAY_LAST(&data->list); item->name = format_expand(wcd->ft, wcd->ft_template); item->wcd = wcd; item->pos = ARRAY_LENGTH(&data->list) - 1; item->state = 0; data->width = xsnprintf (tmp, sizeof tmp , "%u", item->pos); } void window_choose_ready(struct window_pane *wp, u_int cur, void (*callbackfn)(struct window_choose_data *)) { struct window_choose_mode_data *data = wp->modedata; struct screen *s = &data->screen; data->selected = cur; if (data->selected > screen_size_y(s) - 1) data->top = ARRAY_LENGTH(&data->list) - screen_size_y(s); data->callbackfn = callbackfn; if (data->callbackfn == NULL) data->callbackfn = window_choose_default_callback; ARRAY_CONCAT(&data->old_list, &data->list); window_choose_collapse_all(wp); } struct screen * window_choose_init(struct window_pane *wp) { struct window_choose_mode_data *data; struct screen *s; int keys; wp->modedata = data = xmalloc(sizeof *data); data->callbackfn = NULL; data->input_type = WINDOW_CHOOSE_NORMAL; data->input_str = xstrdup(""); data->input_prompt = NULL; ARRAY_INIT(&data->list); ARRAY_INIT(&data->old_list); data->top = 0; s = &data->screen; screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); s->mode &= ~MODE_CURSOR; if (options_get_number(&wp->window->options, "mode-mouse")) s->mode |= MODE_MOUSE_STANDARD; keys = options_get_number(&wp->window->options, "mode-keys"); if (keys == MODEKEY_EMACS) mode_key_init(&data->mdata, &mode_key_tree_emacs_choice); else mode_key_init(&data->mdata, &mode_key_tree_vi_choice); return (s); } struct window_choose_data * window_choose_data_create(int type, struct client *c, struct session *s) { struct window_choose_data *wcd; wcd = xmalloc(sizeof *wcd); wcd->type = type; wcd->ft = format_create(); wcd->ft_template = NULL; wcd->command = NULL; wcd->wl = NULL; wcd->pane_id = -1; wcd->idx = -1; wcd->tree_session = NULL; wcd->start_client = c; wcd->start_client->references++; wcd->start_session = s; wcd->start_session->references++; return (wcd); } void window_choose_data_free(struct window_choose_data *wcd) { wcd->start_client->references--; wcd->start_session->references--; if (wcd->tree_session != NULL) wcd->tree_session->references--; free(wcd->ft_template); format_free(wcd->ft); free(wcd->command); free(wcd); } void window_choose_data_run(struct window_choose_data *cdata) { struct cmd_list *cmdlist; char *cause; /* * The command template will have already been replaced. But if it's * NULL, bail here. */ if (cdata->command == NULL) return; if (cmd_string_parse(cdata->command, &cmdlist, NULL, 0, &cause) != 0) { if (cause != NULL) { *cause = toupper((u_char) *cause); status_message_set(cdata->start_client, "%s", cause); free(cause); } return; } cmdq_run(cdata->start_client->cmdq, cmdlist); cmd_list_free(cmdlist); } void window_choose_default_callback(struct window_choose_data *wcd) { if (wcd == NULL) return; if (wcd->start_client->flags & CLIENT_DEAD) return; window_choose_data_run(wcd); } void window_choose_free(struct window_pane *wp) { if (wp->modedata != NULL) window_choose_free1(wp->modedata); } void window_choose_free1(struct window_choose_mode_data *data) { struct window_choose_mode_item *item; u_int i; if (data == NULL) return; for (i = 0; i < ARRAY_LENGTH(&data->old_list); i++) { item = &ARRAY_ITEM(&data->old_list, i); window_choose_data_free(item->wcd); free(item->name); } ARRAY_FREE(&data->list); ARRAY_FREE(&data->old_list); free(data->input_str); screen_free(&data->screen); free(data); } void window_choose_resize(struct window_pane *wp, u_int sx, u_int sy) { struct window_choose_mode_data *data = wp->modedata; struct screen *s = &data->screen; data->top = 0; if (data->selected > sy - 1) data->top = data->selected - (sy - 1); screen_resize(s, sx, sy, 0); window_choose_redraw_screen(wp); } void window_choose_fire_callback( struct window_pane *wp, struct window_choose_data *wcd) { struct window_choose_mode_data *data = wp->modedata; wp->modedata = NULL; window_pane_reset_mode(wp); data->callbackfn(wcd); window_choose_free1(data); } void window_choose_prompt_input(enum window_choose_input_type input_type, const char *prompt, struct window_pane *wp, int key) { struct window_choose_mode_data *data = wp->modedata; size_t input_len; data->input_type = input_type; data->input_prompt = prompt; input_len = strlen(data->input_str) + 2; data->input_str = xrealloc(data->input_str, 1, input_len); data->input_str[input_len - 2] = key; data->input_str[input_len - 1] = '\0'; window_choose_redraw_screen(wp); } void window_choose_collapse(struct window_pane *wp, struct session *s) { struct window_choose_mode_data *data = wp->modedata; struct window_choose_mode_item *item, *chosen; struct window_choose_data *wcd; u_int i, pos; ARRAY_DECL(, struct window_choose_mode_item) list_copy; ARRAY_INIT(&list_copy); pos = data->selected; chosen = &ARRAY_ITEM(&data->list, pos); chosen->state &= ~TREE_EXPANDED; /* * Trying to mangle the &data->list in-place has lots of problems, so * assign the actual result we want to render and copy the new one over * the top of it. */ for (i = 0; i < ARRAY_LENGTH(&data->list); i++) { item = &ARRAY_ITEM(&data->list, i); wcd = item->wcd; if (s == wcd->tree_session) { /* We only show the session when collapsed. */ if (wcd->type & TREE_SESSION) { item->state &= ~TREE_EXPANDED; ARRAY_ADD(&list_copy, ARRAY_ITEM(&data->list, i)); /* * Update the selection to this session item so * we don't end up highlighting a non-existent * item. */ data->selected = i; } } else ARRAY_ADD(&list_copy, ARRAY_ITEM(&data->list, i)); } if (!ARRAY_EMPTY(&list_copy)) { ARRAY_FREE(&data->list); ARRAY_CONCAT(&data->list, &list_copy); ARRAY_FREE(&list_copy); } } void window_choose_collapse_all(struct window_pane *wp) { struct window_choose_mode_data *data = wp->modedata; struct window_choose_mode_item *item; struct session *s, *chosen; u_int i; chosen = ARRAY_ITEM(&data->list, data->selected).wcd->start_session; RB_FOREACH(s, sessions, &sessions) window_choose_collapse(wp, s); /* Reset the selection back to the starting session. */ for (i = 0; i < ARRAY_LENGTH(&data->list); i++) { item = &ARRAY_ITEM(&data->list, i); if (chosen != item->wcd->tree_session) continue; if (item->wcd->type & TREE_SESSION) data->selected = i; } window_choose_redraw_screen(wp); } void window_choose_expand_all(struct window_pane *wp) { struct window_choose_mode_data *data = wp->modedata; struct window_choose_mode_item *item; struct session *s; u_int i; RB_FOREACH(s, sessions, &sessions) { for (i = 0; i < ARRAY_LENGTH(&data->list); i++) { item = &ARRAY_ITEM(&data->list, i); if (s != item->wcd->tree_session) continue; if (item->wcd->type & TREE_SESSION) window_choose_expand(wp, s, i); } } window_choose_redraw_screen(wp); } void window_choose_expand(struct window_pane *wp, struct session *s, u_int pos) { struct window_choose_mode_data *data = wp->modedata; struct window_choose_mode_item *item, *chosen; struct window_choose_data *wcd; u_int i, items; chosen = &ARRAY_ITEM(&data->list, pos); items = ARRAY_LENGTH(&data->old_list) - 1; /* It's not possible to expand anything other than sessions. */ if (!(chosen->wcd->type & TREE_SESSION)) return; /* Don't re-expand a session which is already expanded. */ if (chosen->state & TREE_EXPANDED) return; /* Mark the session entry as expanded. */ chosen->state |= TREE_EXPANDED; /* * Go back through the original list of all sessions and windows, and * pull out the windows where the session matches the selection chosen * to expand. */ for (i = items; i > 0; i--) { item = &ARRAY_ITEM(&data->old_list, i); item->state |= TREE_EXPANDED; wcd = item->wcd; if (s == wcd->tree_session) { /* * Since the session is already displayed, we only care * to add back in window for it. */ if (wcd->type & TREE_WINDOW) { /* * If the insertion point for adding the * windows to the session falls inside the * range of the list, then we insert these * entries in order *AFTER* the selected * session. */ if (pos < i ) { ARRAY_INSERT(&data->list, pos + 1, ARRAY_ITEM(&data->old_list, i)); } else { /* Ran out of room, add to the end. */ ARRAY_ADD(&data->list, ARRAY_ITEM(&data->old_list, i)); } } } } } void window_choose_key(struct window_pane *wp, unused struct session *sess, int key) { struct window_choose_mode_data *data = wp->modedata; struct screen *s = &data->screen; struct screen_write_ctx ctx; struct window_choose_mode_item *item; size_t input_len; u_int items, n; int idx; items = ARRAY_LENGTH(&data->list); if (data->input_type == WINDOW_CHOOSE_GOTO_ITEM) { switch (mode_key_lookup(&data->mdata, key, NULL)) { case MODEKEYCHOICE_CANCEL: data->input_type = WINDOW_CHOOSE_NORMAL; window_choose_redraw_screen(wp); break; case MODEKEYCHOICE_CHOOSE: n = strtonum(data->input_str, 0, INT_MAX, NULL); if (n > items - 1) { data->input_type = WINDOW_CHOOSE_NORMAL; window_choose_redraw_screen(wp); break; } item = &ARRAY_ITEM(&data->list, n); window_choose_fire_callback(wp, item->wcd); break; case MODEKEYCHOICE_BACKSPACE: input_len = strlen(data->input_str); if (input_len > 0) data->input_str[input_len - 1] = '\0'; window_choose_redraw_screen(wp); break; default: if (key < '0' || key > '9') break; window_choose_prompt_input(WINDOW_CHOOSE_GOTO_ITEM, "Goto Item", wp, key); break; } return; } switch (mode_key_lookup(&data->mdata, key, NULL)) { case MODEKEYCHOICE_CANCEL: window_choose_fire_callback(wp, NULL); break; case MODEKEYCHOICE_CHOOSE: item = &ARRAY_ITEM(&data->list, data->selected); window_choose_fire_callback(wp, item->wcd); break; case MODEKEYCHOICE_TREE_TOGGLE: item = &ARRAY_ITEM(&data->list, data->selected); if (item->state & TREE_EXPANDED) window_choose_collapse(wp, item->wcd->tree_session); else { window_choose_expand(wp, item->wcd->tree_session, data->selected); } window_choose_redraw_screen(wp); break; case MODEKEYCHOICE_TREE_COLLAPSE: item = &ARRAY_ITEM(&data->list, data->selected); if (item->state & TREE_EXPANDED) { window_choose_collapse(wp, item->wcd->tree_session); window_choose_redraw_screen(wp); } break; case MODEKEYCHOICE_TREE_COLLAPSE_ALL: window_choose_collapse_all(wp); break; case MODEKEYCHOICE_TREE_EXPAND: item = &ARRAY_ITEM(&data->list, data->selected); if (!(item->state & TREE_EXPANDED)) { window_choose_expand(wp, item->wcd->tree_session, data->selected); window_choose_redraw_screen(wp); } break; case MODEKEYCHOICE_TREE_EXPAND_ALL: window_choose_expand_all(wp); break; case MODEKEYCHOICE_UP: if (items == 0) break; if (data->selected == 0) { data->selected = items - 1; if (data->selected > screen_size_y(s) - 1) data->top = items - screen_size_y(s); window_choose_redraw_screen(wp); break; } data->selected--; if (data->selected < data->top) window_choose_scroll_up(wp); else { screen_write_start(&ctx, wp, NULL); window_choose_write_line( wp, &ctx, data->selected - data->top); window_choose_write_line( wp, &ctx, data->selected + 1 - data->top); screen_write_stop(&ctx); } break; case MODEKEYCHOICE_DOWN: if (items == 0) break; if (data->selected == items - 1) { data->selected = 0; data->top = 0; window_choose_redraw_screen(wp); break; } data->selected++; if (data->selected < data->top + screen_size_y(s)) { screen_write_start(&ctx, wp, NULL); window_choose_write_line( wp, &ctx, data->selected - data->top); window_choose_write_line( wp, &ctx, data->selected - 1 - data->top); screen_write_stop(&ctx); } else window_choose_scroll_down(wp); break; case MODEKEYCHOICE_SCROLLUP: if (items == 0 || data->top == 0) break; if (data->selected == data->top + screen_size_y(s) - 1) { data->selected--; window_choose_scroll_up(wp); screen_write_start(&ctx, wp, NULL); window_choose_write_line( wp, &ctx, screen_size_y(s) - 1); screen_write_stop(&ctx); } else window_choose_scroll_up(wp); break; case MODEKEYCHOICE_SCROLLDOWN: if (items == 0 || data->top + screen_size_y(&data->screen) >= items) break; if (data->selected == data->top) { data->selected++; window_choose_scroll_down(wp); screen_write_start(&ctx, wp, NULL); window_choose_write_line(wp, &ctx, 0); screen_write_stop(&ctx); } else window_choose_scroll_down(wp); break; case MODEKEYCHOICE_PAGEUP: if (data->selected < screen_size_y(s)) { data->selected = 0; data->top = 0; } else { data->selected -= screen_size_y(s); if (data->top < screen_size_y(s)) data->top = 0; else data->top -= screen_size_y(s); } window_choose_redraw_screen(wp); break; case MODEKEYCHOICE_PAGEDOWN: data->selected += screen_size_y(s); if (data->selected > items - 1) data->selected = items - 1; data->top += screen_size_y(s); if (screen_size_y(s) < items) { if (data->top + screen_size_y(s) > items) data->top = items - screen_size_y(s); } else data->top = 0; if (data->selected < data->top) data->top = data->selected; window_choose_redraw_screen(wp); break; case MODEKEYCHOICE_BACKSPACE: input_len = strlen(data->input_str); if (input_len > 0) data->input_str[input_len - 1] = '\0'; window_choose_redraw_screen(wp); break; case MODEKEYCHOICE_STARTNUMBERPREFIX: key &= KEYC_MASK_KEY; if (key < '0' || key > '9') break; window_choose_prompt_input(WINDOW_CHOOSE_GOTO_ITEM, "Goto Item", wp, key); break; default: idx = window_choose_index_key(data, key); if (idx < 0 || (u_int) idx >= ARRAY_LENGTH(&data->list)) break; data->selected = idx; item = &ARRAY_ITEM(&data->list, data->selected); window_choose_fire_callback(wp, item->wcd); break; } } void window_choose_mouse( struct window_pane *wp, unused struct session *sess, struct mouse_event *m) { struct window_choose_mode_data *data = wp->modedata; struct screen *s = &data->screen; struct window_choose_mode_item *item; u_int idx; if (~m->event & MOUSE_EVENT_CLICK) return; if (m->x >= screen_size_x(s)) return; if (m->y >= screen_size_y(s)) return; idx = data->top + m->y; if (idx >= ARRAY_LENGTH(&data->list)) return; data->selected = idx; item = &ARRAY_ITEM(&data->list, data->selected); window_choose_fire_callback(wp, item->wcd); } void window_choose_write_line( struct window_pane *wp, struct screen_write_ctx *ctx, u_int py) { struct window_choose_mode_data *data = wp->modedata; struct window_choose_mode_item *item; struct options *oo = &wp->window->options; struct screen *s = &data->screen; struct grid_cell gc; size_t last, xoff = 0; char hdr[32], label[32]; int utf8flag, key; if (data->callbackfn == NULL) fatalx("called before callback assigned"); last = screen_size_y(s) - 1; utf8flag = options_get_number(&wp->window->options, "utf8"); memcpy(&gc, &grid_default_cell, sizeof gc); if (data->selected == data->top + py) window_mode_attrs(&gc, oo); screen_write_cursormove(ctx, 0, py); if (data->top + py < ARRAY_LENGTH(&data->list)) { item = &ARRAY_ITEM(&data->list, data->top + py); if (item->wcd->wl != NULL && item->wcd->wl->flags & WINLINK_ALERTFLAGS) gc.attr |= GRID_ATTR_BRIGHT; key = window_choose_key_index(data, data->top + py); if (key != -1) xsnprintf (label, sizeof label, "(%c)", key); else xsnprintf (label, sizeof label, "(%d)", item->pos); screen_write_nputs(ctx, screen_size_x(s) - 1, &gc, utf8flag, "%*s %s %s", data->width + 2, label, /* * Add indication to tree if necessary about whether it's * expanded or not. */ (item->wcd->type & TREE_SESSION) ? (item->state & TREE_EXPANDED ? "-" : "+") : "", item->name); } while (s->cx < screen_size_x(s) - 1) screen_write_putc(ctx, &gc, ' '); if (data->input_type != WINDOW_CHOOSE_NORMAL) { window_mode_attrs(&gc, oo); xoff = xsnprintf(hdr, sizeof hdr, "%s: %s", data->input_prompt, data->input_str); screen_write_cursormove(ctx, 0, last); screen_write_puts(ctx, &gc, "%s", hdr); screen_write_cursormove(ctx, xoff, py); memcpy(&gc, &grid_default_cell, sizeof gc); } } int window_choose_key_index(struct window_choose_mode_data *data, u_int idx) { static const char keys[] = "0123456789" "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; const char *ptr; int mkey; for (ptr = keys; *ptr != '\0'; ptr++) { mkey = mode_key_lookup(&data->mdata, *ptr, NULL); if (mkey != MODEKEY_NONE && mkey != MODEKEY_OTHER) continue; if (idx-- == 0) return (*ptr); } return (-1); } int window_choose_index_key(struct window_choose_mode_data *data, int key) { static const char keys[] = "0123456789" "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; const char *ptr; int mkey; u_int idx = 0; for (ptr = keys; *ptr != '\0'; ptr++) { mkey = mode_key_lookup(&data->mdata, *ptr, NULL); if (mkey != MODEKEY_NONE && mkey != MODEKEY_OTHER) continue; if (key == *ptr) return (idx); idx++; } return (-1); } void window_choose_redraw_screen(struct window_pane *wp) { struct window_choose_mode_data *data = wp->modedata; struct screen *s = &data->screen; struct screen_write_ctx ctx; u_int i; screen_write_start(&ctx, wp, NULL); for (i = 0; i < screen_size_y(s); i++) window_choose_write_line(wp, &ctx, i); screen_write_stop(&ctx); } void window_choose_scroll_up(struct window_pane *wp) { struct window_choose_mode_data *data = wp->modedata; struct screen_write_ctx ctx; if (data->top == 0) return; data->top--; screen_write_start(&ctx, wp, NULL); screen_write_cursormove(&ctx, 0, 0); screen_write_insertline(&ctx, 1); window_choose_write_line(wp, &ctx, 0); if (screen_size_y(&data->screen) > 1) window_choose_write_line(wp, &ctx, 1); screen_write_stop(&ctx); } void window_choose_scroll_down(struct window_pane *wp) { struct window_choose_mode_data *data = wp->modedata; struct screen *s = &data->screen; struct screen_write_ctx ctx; if (data->top >= ARRAY_LENGTH(&data->list)) return; data->top++; screen_write_start(&ctx, wp, NULL); screen_write_cursormove(&ctx, 0, 0); screen_write_deleteline(&ctx, 1); window_choose_write_line(wp, &ctx, screen_size_y(s) - 1); if (screen_size_y(&data->screen) > 1) window_choose_write_line(wp, &ctx, screen_size_y(s) - 2); screen_write_stop(&ctx); } struct window_choose_data * window_choose_add_session(struct window_pane *wp, struct client *c, struct session *s, const char *template, const char *action, u_int idx) { struct window_choose_data *wcd; wcd = window_choose_data_create(TREE_SESSION, c, c->session); wcd->idx = s->id; wcd->tree_session = s; wcd->tree_session->references++; wcd->ft_template = xstrdup(template); format_add(wcd->ft, "line", "%u", idx); format_session(wcd->ft, s); wcd->command = cmd_template_replace(action, s->name, 1); window_choose_add(wp, wcd); return (wcd); } struct window_choose_data * window_choose_add_item(struct window_pane *wp, struct client *c, struct winlink *wl, const char *template, const char *action, u_int idx) { struct window_choose_data *wcd; char *expanded; wcd = window_choose_data_create(TREE_OTHER, c, c->session); wcd->idx = wl->idx; wcd->ft_template = xstrdup(template); format_add(wcd->ft, "line", "%u", idx); format_session(wcd->ft, wcd->start_session); format_winlink(wcd->ft, wcd->start_session, wl); format_window_pane(wcd->ft, wl->window->active); /* * Interpolate action here, since the data we pass back is the expanded * template itself. */ xasprintf(&expanded, "%s", format_expand(wcd->ft, wcd->ft_template)); wcd->command = cmd_template_replace(action, expanded, 1); free(expanded); window_choose_add(wp, wcd); return (wcd); } struct window_choose_data * window_choose_add_window(struct window_pane *wp, struct client *c, struct session *s, struct winlink *wl, const char *template, const char *action, u_int idx) { struct window_choose_data *wcd; char *expanded; wcd = window_choose_data_create(TREE_WINDOW, c, c->session); wcd->idx = wl->idx; wcd->wl = wl; wcd->tree_session = s; wcd->tree_session->references++; wcd->ft_template = xstrdup(template); format_add(wcd->ft, "line", "%u", idx); format_session(wcd->ft, s); format_winlink(wcd->ft, s, wl); format_window_pane(wcd->ft, wl->window->active); xasprintf(&expanded, "%s:%d", s->name, wl->idx); wcd->command = cmd_template_replace(action, expanded, 1); free(expanded); window_choose_add(wp, wcd); return (wcd); } tmate-1.8.10/window-clock.c000066400000000000000000000060671242461015400154670ustar00rootroot00000000000000/* $Id$ */ /* * 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" struct screen *window_clock_init(struct window_pane *); void window_clock_free(struct window_pane *); void window_clock_resize(struct window_pane *, u_int, u_int); void window_clock_key(struct window_pane *, struct session *, int); void window_clock_timer(struct window_pane *); void window_clock_draw_screen(struct window_pane *); const struct window_mode window_clock_mode = { window_clock_init, window_clock_free, window_clock_resize, window_clock_key, NULL, window_clock_timer, }; struct window_clock_mode_data { struct screen screen; time_t tim; }; struct screen * window_clock_init(struct window_pane *wp) { struct window_clock_mode_data *data; struct screen *s; wp->modedata = data = xmalloc(sizeof *data); data->tim = time(NULL); s = &data->screen; screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); s->mode &= ~MODE_CURSOR; window_clock_draw_screen(wp); return (s); } void window_clock_free(struct window_pane *wp) { struct window_clock_mode_data *data = wp->modedata; screen_free(&data->screen); free(data); } void window_clock_resize(struct window_pane *wp, u_int sx, u_int sy) { struct window_clock_mode_data *data = wp->modedata; struct screen *s = &data->screen; screen_resize(s, sx, sy, 0); window_clock_draw_screen(wp); } void window_clock_key( struct window_pane *wp, unused struct session *sess, unused int key) { window_pane_reset_mode(wp); } void window_clock_timer(struct window_pane *wp) { struct window_clock_mode_data *data = wp->modedata; struct tm now, then; time_t t; t = time(NULL); gmtime_r(&t, &now); gmtime_r(&data->tim, &then); if (now.tm_min == then.tm_min) return; data->tim = t; window_clock_draw_screen(wp); server_redraw_window(wp->window); } void window_clock_draw_screen(struct window_pane *wp) { struct window_clock_mode_data *data = wp->modedata; struct screen_write_ctx ctx; int colour, style; colour = options_get_number(&wp->window->options, "clock-mode-colour"); style = options_get_number(&wp->window->options, "clock-mode-style"); screen_write_start(&ctx, NULL, &data->screen); clock_draw(&ctx, colour, style); screen_write_stop(&ctx); } tmate-1.8.10/window-copy.c000066400000000000000000001474111242461015400153450ustar00rootroot00000000000000/* $Id$ */ /* * 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" #include "tmate.h" struct screen *window_copy_init(struct window_pane *); void window_copy_free(struct window_pane *); void window_copy_resize(struct window_pane *, u_int, u_int); void window_copy_key(struct window_pane *, struct session *, int); int window_copy_key_input(struct window_pane *, int); int window_copy_key_numeric_prefix(struct window_pane *, int); void window_copy_mouse( struct window_pane *, struct session *, struct mouse_event *); void window_copy_redraw_lines(struct window_pane *, u_int, u_int); void window_copy_write_line( struct window_pane *, struct screen_write_ctx *, u_int); void window_copy_write_lines( struct window_pane *, struct screen_write_ctx *, u_int, u_int); void window_copy_scroll_to(struct window_pane *, u_int, u_int); int window_copy_search_compare( struct grid *, u_int, u_int, struct grid *, u_int); int window_copy_search_lr( struct grid *, struct grid *, u_int *, u_int, u_int, u_int); int window_copy_search_rl( struct grid *, struct grid *, u_int *, u_int, u_int, u_int); void window_copy_search_up(struct window_pane *, const char *); void window_copy_search_down(struct window_pane *, const char *); void window_copy_goto_line(struct window_pane *, const char *); void window_copy_update_cursor(struct window_pane *, u_int, u_int); void window_copy_start_selection(struct window_pane *); void *window_copy_get_selection(struct window_pane *, size_t *); void window_copy_copy_buffer(struct window_pane *, int, void *, size_t); void window_copy_copy_pipe( struct window_pane *, struct session *, int, const char *); void window_copy_copy_selection(struct window_pane *, int); void window_copy_clear_selection(struct window_pane *); void window_copy_copy_line( struct window_pane *, char **, size_t *, u_int, u_int, u_int); int window_copy_in_set(struct window_pane *, u_int, u_int, const char *); u_int window_copy_find_length(struct window_pane *, u_int); void window_copy_cursor_start_of_line(struct window_pane *); void window_copy_cursor_back_to_indentation(struct window_pane *); void window_copy_cursor_end_of_line(struct window_pane *); void window_copy_cursor_left(struct window_pane *); void window_copy_cursor_right(struct window_pane *); void window_copy_cursor_up(struct window_pane *, int); void window_copy_cursor_down(struct window_pane *, int); void window_copy_cursor_jump(struct window_pane *); void window_copy_cursor_jump_back(struct window_pane *); void window_copy_cursor_jump_to(struct window_pane *); void window_copy_cursor_jump_to_back(struct window_pane *); void window_copy_cursor_next_word(struct window_pane *, const char *); void window_copy_cursor_next_word_end(struct window_pane *, const char *); void window_copy_cursor_previous_word(struct window_pane *, const char *); void window_copy_scroll_up(struct window_pane *, u_int); void window_copy_scroll_down(struct window_pane *, u_int); void window_copy_rectangle_toggle(struct window_pane *); const struct window_mode window_copy_mode = { window_copy_init, window_copy_free, window_copy_resize, window_copy_key, window_copy_mouse, NULL, }; struct screen * window_copy_init(struct window_pane *wp) { struct window_copy_mode_data *data; struct screen *s; int keys; wp->modedata = data = xmalloc(sizeof *data); data->oy = 0; data->cx = 0; data->cy = 0; data->lastcx = 0; data->lastsx = 0; data->backing_written = 0; data->rectflag = 0; data->inputtype = WINDOW_COPY_OFF; data->inputprompt = NULL; data->inputstr = xstrdup(""); data->numprefix = -1; data->searchtype = WINDOW_COPY_OFF; data->searchstr = NULL; if (wp->fd != -1) bufferevent_disable(wp->event, EV_READ|EV_WRITE); data->jumptype = WINDOW_COPY_OFF; data->jumpchar = '\0'; s = &data->screen; screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); if (options_get_number(&wp->window->options, "mode-mouse")) s->mode |= MODE_MOUSE_STANDARD; keys = options_get_number(&wp->window->options, "mode-keys"); if (keys == MODEKEY_EMACS) mode_key_init(&data->mdata, &mode_key_tree_emacs_copy); else mode_key_init(&data->mdata, &mode_key_tree_vi_copy); data->backing = NULL; #ifdef TMATE data->password_cb = NULL; #endif return (s); } void window_copy_init_from_pane(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; struct screen_write_ctx ctx; u_int i; if (wp->mode != &window_copy_mode) fatalx("not in copy mode"); data->backing = &wp->base; data->cx = data->backing->cx; data->cy = data->backing->cy; s->cx = data->cx; s->cy = data->cy; screen_write_start(&ctx, NULL, s); for (i = 0; i < screen_size_y(s); i++) window_copy_write_line(wp, &ctx, i); screen_write_cursormove(&ctx, data->cx, data->cy); screen_write_stop(&ctx); tmate_sync_copy_mode(wp); } void window_copy_init_for_output(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; data->backing = xmalloc(sizeof *data->backing); screen_init(data->backing, screen_size_x(&wp->base), screen_size_y(&wp->base), UINT_MAX); data->backing->mode &= ~MODE_WRAP; } void window_copy_free(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; if (wp->fd != -1) bufferevent_enable(wp->event, EV_READ|EV_WRITE); free(data->searchstr); free(data->inputstr); 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_copy_mode_data *data = wp->modedata; struct screen *backing = data->backing; struct screen_write_ctx back_ctx, ctx; struct grid_cell gc; int utf8flag; u_int old_hsize; #ifdef TMATE char *msg; #endif if (backing == &wp->base) return; utf8flag = options_get_number(&wp->window->options, "utf8"); memcpy(&gc, &grid_default_cell, sizeof gc); old_hsize = screen_hsize(data->backing); screen_write_start(&back_ctx, NULL, backing); if (data->backing_written) { /* * On the second or later line, do a CRLF before writing * (so it's on a new line). */ screen_write_carriagereturn(&back_ctx); screen_write_linefeed(&back_ctx, 0); } else data->backing_written = 1; #ifdef TMATE xvasprintf(&msg, fmt, ap); screen_write_nputs(&back_ctx, 0, &gc, utf8flag, "%s", msg); tmate_write_copy_mode(wp, msg); free(msg); #else screen_write_vnputs(&back_ctx, 0, &gc, utf8flag, fmt, ap); #endif screen_write_stop(&back_ctx); data->oy += screen_hsize(data->backing) - old_hsize; screen_write_start(&ctx, wp, &data->screen); /* * If the history has changed, draw the top line. * (If there's any history at all, it has changed.) */ if (screen_hsize(data->backing)) window_copy_redraw_lines(wp, 0, 1); /* Write the line, if it's visible. */ if (backing->cy + data->oy < screen_size_y(backing)) window_copy_redraw_lines(wp, backing->cy, 1); screen_write_stop(&ctx); } void window_copy_pageup(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; u_int n; n = 1; if (screen_size_y(s) > 2) n = screen_size_y(s) - 2; if (data->oy + n > screen_hsize(data->backing)) data->oy = screen_hsize(data->backing); else data->oy += n; window_copy_update_selection(wp); window_copy_redraw_screen(wp); } void window_copy_resize(struct window_pane *wp, u_int sx, u_int sy) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; struct screen_write_ctx ctx; screen_resize(s, sx, sy, 0); if (data->backing != &wp->base) screen_resize(data->backing, sx, sy, 0); if (data->cy > sy - 1) data->cy = sy - 1; if (data->cx > sx) data->cx = sx; if (data->oy > screen_hsize(data->backing)) data->oy = screen_hsize(data->backing); window_copy_clear_selection(wp); screen_write_start(&ctx, NULL, s); window_copy_write_lines(wp, &ctx, 0, screen_size_y(s) - 1); screen_write_stop(&ctx); window_copy_redraw_screen(wp); } static void __window_copy_key(struct window_pane *wp, struct session *sess, int key) { const char *word_separators; struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; u_int n; int np, keys; enum mode_key_cmd cmd; const char *arg; np = data->numprefix; if (np <= 0) np = 1; if (data->inputtype == WINDOW_COPY_JUMPFORWARD || data->inputtype == WINDOW_COPY_JUMPBACK || data->inputtype == WINDOW_COPY_JUMPTOFORWARD || data->inputtype == WINDOW_COPY_JUMPTOBACK) { /* Ignore keys with modifiers. */ if ((key & KEYC_MASK_MOD) == 0) { data->jumpchar = key; if (data->inputtype == WINDOW_COPY_JUMPFORWARD) { for (; np != 0; np--) window_copy_cursor_jump(wp); } else if (data->inputtype == WINDOW_COPY_JUMPBACK) { for (; np != 0; np--) window_copy_cursor_jump_back(wp); } else if (data->inputtype == WINDOW_COPY_JUMPTOFORWARD) { for (; np != 0; np--) window_copy_cursor_jump_to(wp); } else if (data->inputtype == WINDOW_COPY_JUMPTOBACK) { for (; np != 0; np--) window_copy_cursor_jump_to_back(wp); } } data->jumptype = data->inputtype; data->inputtype = WINDOW_COPY_OFF; window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); return; } else if (data->inputtype == WINDOW_COPY_NUMERICPREFIX) { if (window_copy_key_numeric_prefix(wp, key) == 0) return; data->inputtype = WINDOW_COPY_OFF; window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); } else if (data->inputtype != WINDOW_COPY_OFF) { if (window_copy_key_input(wp, key) != 0) goto input_off; return; } cmd = mode_key_lookup(&data->mdata, key, &arg); switch (cmd) { case MODEKEYCOPY_CANCEL: window_pane_reset_mode(wp); return; case MODEKEYCOPY_LEFT: for (; np != 0; np--) window_copy_cursor_left(wp); break; case MODEKEYCOPY_RIGHT: for (; np != 0; np--) window_copy_cursor_right(wp); break; case MODEKEYCOPY_UP: for (; np != 0; np--) window_copy_cursor_up(wp, 0); break; case MODEKEYCOPY_DOWN: for (; np != 0; np--) window_copy_cursor_down(wp, 0); break; case MODEKEYCOPY_SCROLLUP: for (; np != 0; np--) window_copy_cursor_up(wp, 1); break; case MODEKEYCOPY_SCROLLDOWN: for (; np != 0; np--) window_copy_cursor_down(wp, 1); break; case MODEKEYCOPY_PREVIOUSPAGE: for (; np != 0; np--) window_copy_pageup(wp); break; case MODEKEYCOPY_NEXTPAGE: n = 1; if (screen_size_y(s) > 2) n = screen_size_y(s) - 2; for (; np != 0; np--) { if (data->oy < n) data->oy = 0; else data->oy -= n; } window_copy_update_selection(wp); window_copy_redraw_screen(wp); break; case MODEKEYCOPY_HALFPAGEUP: n = screen_size_y(s) / 2; for (; np != 0; np--) { if (data->oy + n > screen_hsize(data->backing)) data->oy = screen_hsize(data->backing); else data->oy += n; } window_copy_update_selection(wp); window_copy_redraw_screen(wp); break; case MODEKEYCOPY_HALFPAGEDOWN: n = screen_size_y(s) / 2; for (; np != 0; np--) { if (data->oy < n) data->oy = 0; else data->oy -= n; } window_copy_update_selection(wp); window_copy_redraw_screen(wp); break; case MODEKEYCOPY_TOPLINE: data->cx = 0; data->cy = 0; window_copy_update_selection(wp); window_copy_redraw_screen(wp); break; case MODEKEYCOPY_MIDDLELINE: data->cx = 0; data->cy = (screen_size_y(s) - 1) / 2; window_copy_update_selection(wp); window_copy_redraw_screen(wp); break; case MODEKEYCOPY_BOTTOMLINE: data->cx = 0; data->cy = screen_size_y(s) - 1; window_copy_update_selection(wp); window_copy_redraw_screen(wp); break; case MODEKEYCOPY_HISTORYTOP: data->cx = 0; data->cy = 0; data->oy = screen_hsize(data->backing); window_copy_update_selection(wp); window_copy_redraw_screen(wp); break; case MODEKEYCOPY_HISTORYBOTTOM: data->cx = 0; data->cy = screen_size_y(s) - 1; data->oy = 0; window_copy_update_selection(wp); window_copy_redraw_screen(wp); break; case MODEKEYCOPY_STARTSELECTION: window_copy_start_selection(wp); window_copy_redraw_screen(wp); break; case MODEKEYCOPY_COPYLINE: case MODEKEYCOPY_SELECTLINE: window_copy_cursor_start_of_line(wp); /* FALLTHROUGH */ case MODEKEYCOPY_COPYENDOFLINE: window_copy_start_selection(wp); for (; np > 1; np--) window_copy_cursor_down(wp, 0); window_copy_cursor_end_of_line(wp); window_copy_redraw_screen(wp); /* If a copy command then copy the selection and exit. */ if (sess != NULL && (cmd == MODEKEYCOPY_COPYLINE || cmd == MODEKEYCOPY_COPYENDOFLINE)) { window_copy_copy_selection(wp, -1); window_pane_reset_mode(wp); return; } break; case MODEKEYCOPY_CLEARSELECTION: window_copy_clear_selection(wp); window_copy_redraw_screen(wp); break; case MODEKEYCOPY_COPYPIPE: if (sess != NULL) { window_copy_copy_pipe(wp, sess, data->numprefix, arg); window_pane_reset_mode(wp); return; } break; case MODEKEYCOPY_COPYSELECTION: if (sess != NULL) { window_copy_copy_selection(wp, data->numprefix); window_pane_reset_mode(wp); return; } break; case MODEKEYCOPY_STARTOFLINE: window_copy_cursor_start_of_line(wp); break; case MODEKEYCOPY_BACKTOINDENTATION: window_copy_cursor_back_to_indentation(wp); break; case MODEKEYCOPY_ENDOFLINE: window_copy_cursor_end_of_line(wp); break; case MODEKEYCOPY_NEXTSPACE: for (; np != 0; np--) window_copy_cursor_next_word(wp, " "); break; case MODEKEYCOPY_NEXTSPACEEND: for (; np != 0; np--) window_copy_cursor_next_word_end(wp, " "); break; case MODEKEYCOPY_NEXTWORD: word_separators = options_get_string(&sess->options, "word-separators"); for (; np != 0; np--) window_copy_cursor_next_word(wp, word_separators); break; case MODEKEYCOPY_NEXTWORDEND: word_separators = options_get_string(&sess->options, "word-separators"); for (; np != 0; np--) window_copy_cursor_next_word_end(wp, word_separators); break; case MODEKEYCOPY_PREVIOUSSPACE: for (; np != 0; np--) window_copy_cursor_previous_word(wp, " "); break; case MODEKEYCOPY_PREVIOUSWORD: word_separators = options_get_string(&sess->options, "word-separators"); for (; np != 0; np--) window_copy_cursor_previous_word(wp, word_separators); break; case MODEKEYCOPY_JUMP: data->inputtype = WINDOW_COPY_JUMPFORWARD; data->inputprompt = "Jump Forward"; *data->inputstr = '\0'; window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); return; /* skip numprefix reset */ case MODEKEYCOPY_JUMPAGAIN: if (data->jumptype == WINDOW_COPY_JUMPFORWARD) { for (; np != 0; np--) window_copy_cursor_jump(wp); } else if (data->jumptype == WINDOW_COPY_JUMPBACK) { for (; np != 0; np--) window_copy_cursor_jump_back(wp); } else if (data->jumptype == WINDOW_COPY_JUMPTOFORWARD) { for (; np != 0; np--) window_copy_cursor_jump_to(wp); } else if (data->jumptype == WINDOW_COPY_JUMPTOBACK) { for (; np != 0; np--) window_copy_cursor_jump_to_back(wp); } break; case MODEKEYCOPY_JUMPREVERSE: if (data->jumptype == WINDOW_COPY_JUMPFORWARD) { for (; np != 0; np--) window_copy_cursor_jump_back(wp); } else if (data->jumptype == WINDOW_COPY_JUMPBACK) { for (; np != 0; np--) window_copy_cursor_jump(wp); } else if (data->jumptype == WINDOW_COPY_JUMPTOFORWARD) { for (; np != 0; np--) window_copy_cursor_jump_to_back(wp); } else if (data->jumptype == WINDOW_COPY_JUMPTOBACK) { for (; np != 0; np--) window_copy_cursor_jump_to(wp); } break; case MODEKEYCOPY_JUMPBACK: data->inputtype = WINDOW_COPY_JUMPBACK; data->inputprompt = "Jump Back"; *data->inputstr = '\0'; window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); return; /* skip numprefix reset */ case MODEKEYCOPY_JUMPTO: data->inputtype = WINDOW_COPY_JUMPTOFORWARD; data->inputprompt = "Jump To"; *data->inputstr = '\0'; window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); return; /* skip numprefix reset */ case MODEKEYCOPY_JUMPTOBACK: data->inputtype = WINDOW_COPY_JUMPTOBACK; data->inputprompt = "Jump To Back"; *data->inputstr = '\0'; window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); return; /* skip numprefix reset */ case MODEKEYCOPY_SEARCHUP: data->inputtype = WINDOW_COPY_SEARCHUP; data->inputprompt = "Search Up"; goto input_on; case MODEKEYCOPY_SEARCHDOWN: data->inputtype = WINDOW_COPY_SEARCHDOWN; data->inputprompt = "Search Down"; goto input_on; case MODEKEYCOPY_SEARCHAGAIN: case MODEKEYCOPY_SEARCHREVERSE: switch (data->searchtype) { case WINDOW_COPY_OFF: case WINDOW_COPY_GOTOLINE: case WINDOW_COPY_JUMPFORWARD: case WINDOW_COPY_JUMPBACK: case WINDOW_COPY_JUMPTOFORWARD: case WINDOW_COPY_JUMPTOBACK: case WINDOW_COPY_NUMERICPREFIX: #ifdef TMATE case WINDOW_COPY_PASSWORD: break; #endif case WINDOW_COPY_SEARCHUP: if (cmd == MODEKEYCOPY_SEARCHAGAIN) { for (; np != 0; np--) { window_copy_search_up( wp, data->searchstr); } } else { for (; np != 0; np--) { window_copy_search_down( wp, data->searchstr); } } break; case WINDOW_COPY_SEARCHDOWN: if (cmd == MODEKEYCOPY_SEARCHAGAIN) { for (; np != 0; np--) { window_copy_search_down( wp, data->searchstr); } } else { for (; np != 0; np--) { window_copy_search_up( wp, data->searchstr); } } break; } break; case MODEKEYCOPY_GOTOLINE: data->inputtype = WINDOW_COPY_GOTOLINE; data->inputprompt = "Goto Line"; *data->inputstr = '\0'; goto input_on; case MODEKEYCOPY_STARTNUMBERPREFIX: key &= KEYC_MASK_KEY; if (key >= '0' && key <= '9') { data->inputtype = WINDOW_COPY_NUMERICPREFIX; data->numprefix = 0; window_copy_key_numeric_prefix(wp, key); return; } break; case MODEKEYCOPY_RECTANGLETOGGLE: window_copy_rectangle_toggle(wp); break; default: break; } data->numprefix = -1; return; input_on: keys = options_get_number(&wp->window->options, "mode-keys"); if (keys == MODEKEY_EMACS) mode_key_init(&data->mdata, &mode_key_tree_emacs_edit); else mode_key_init(&data->mdata, &mode_key_tree_vi_edit); window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); return; input_off: keys = options_get_number(&wp->window->options, "mode-keys"); if (keys == MODEKEY_EMACS) mode_key_init(&data->mdata, &mode_key_tree_emacs_copy); else mode_key_init(&data->mdata, &mode_key_tree_vi_copy); data->inputtype = WINDOW_COPY_OFF; data->inputprompt = NULL; window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); } void window_copy_key(struct window_pane *wp, struct session *sess, int key) { __window_copy_key(wp, sess, key); tmate_sync_copy_mode(wp); } int window_copy_key_input(struct window_pane *wp, int key) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; size_t inputlen; int np; switch (mode_key_lookup(&data->mdata, key, NULL)) { case MODEKEYEDIT_CANCEL: data->numprefix = -1; return (-1); case MODEKEYEDIT_BACKSPACE: inputlen = strlen(data->inputstr); if (inputlen > 0) data->inputstr[inputlen - 1] = '\0'; break; case MODEKEYEDIT_DELETELINE: *data->inputstr = '\0'; break; case MODEKEYEDIT_ENTER: np = data->numprefix; if (np <= 0) np = 1; switch (data->inputtype) { case WINDOW_COPY_OFF: case WINDOW_COPY_JUMPFORWARD: case WINDOW_COPY_JUMPBACK: case WINDOW_COPY_JUMPTOFORWARD: case WINDOW_COPY_JUMPTOBACK: case WINDOW_COPY_NUMERICPREFIX: break; case WINDOW_COPY_SEARCHUP: for (; np != 0; np--) window_copy_search_up(wp, data->inputstr); data->searchtype = data->inputtype; data->searchstr = xstrdup(data->inputstr); break; case WINDOW_COPY_SEARCHDOWN: for (; np != 0; np--) window_copy_search_down(wp, data->inputstr); data->searchtype = data->inputtype; data->searchstr = xstrdup(data->inputstr); break; case WINDOW_COPY_GOTOLINE: window_copy_goto_line(wp, data->inputstr); *data->inputstr = '\0'; break; #ifdef TMATE case WINDOW_COPY_PASSWORD: if (data->password_cb) { data->password_cb(data->inputstr, data->password_cb_private); } *data->inputstr = '\0'; window_copy_copy_selection(wp, -1); window_pane_reset_mode(wp); #endif } data->numprefix = -1; return (1); case MODEKEY_OTHER: if (key < 32 || key > 126) break; inputlen = strlen(data->inputstr) + 2; data->inputstr = xrealloc(data->inputstr, 1, inputlen); data->inputstr[inputlen - 2] = key; data->inputstr[inputlen - 1] = '\0'; break; default: break; } window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); return (0); } int window_copy_key_numeric_prefix(struct window_pane *wp, int key) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; key &= KEYC_MASK_KEY; if (key < '0' || key > '9') return (1); if (data->numprefix >= 100) /* no more than three digits */ return (0); data->numprefix = data->numprefix * 10 + key - '0'; window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); return (0); } static void __window_copy_mouse( struct window_pane *wp, struct session *sess, struct mouse_event *m) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; u_int i; if (m->x >= screen_size_x(s)) return; if (m->y >= screen_size_y(s)) return; /* If mouse wheel (buttons 4 and 5), scroll. */ if (m->event == MOUSE_EVENT_WHEEL) { if (m->wheel == MOUSE_WHEEL_UP) { for (i = 0; i < 5; i++) window_copy_cursor_up(wp, 1); } else if (m->wheel == MOUSE_WHEEL_DOWN) { for (i = 0; i < 5; i++) window_copy_cursor_down(wp, 1); if (data->oy == 0) goto reset_mode; } return; } /* * If already reading motion, move the cursor while buttons are still * pressed, or stop the selection on their release. */ if (s->mode & MODE_MOUSE_BUTTON) { if (~m->event & MOUSE_EVENT_UP) { window_copy_update_cursor(wp, m->x, m->y); if (window_copy_update_selection(wp)) window_copy_redraw_screen(wp); return; } goto reset_mode; } /* Otherwise if other buttons pressed, start selection and motion. */ if (~m->event & MOUSE_EVENT_UP) { s->mode &= ~MODE_MOUSE_STANDARD; s->mode |= MODE_MOUSE_BUTTON; window_copy_update_cursor(wp, m->x, m->y); window_copy_start_selection(wp); window_copy_redraw_screen(wp); } return; reset_mode: s->mode &= ~MODE_MOUSE_BUTTON; s->mode |= MODE_MOUSE_STANDARD; if (sess != NULL) { window_copy_copy_selection(wp, -1); window_pane_reset_mode(wp); } } void window_copy_mouse( struct window_pane *wp, struct session *sess, struct mouse_event *m) { __window_copy_mouse(wp, sess, m); tmate_sync_copy_mode(wp); } void window_copy_scroll_to(struct window_pane *wp, u_int px, u_int py) { struct window_copy_mode_data *data = wp->modedata; struct grid *gd = data->backing->grid; u_int offset, gap; data->cx = px; gap = gd->sy / 4; if (py < gd->sy) { offset = 0; data->cy = py; } else if (py > gd->hsize + gd->sy - gap) { offset = gd->hsize; data->cy = py - gd->hsize; } else { offset = py + gap - gd->sy; data->cy = py - offset; } data->oy = gd->hsize - offset; window_copy_update_selection(wp); window_copy_redraw_screen(wp); } int window_copy_search_compare( struct grid *gd, u_int px, u_int py, struct grid *sgd, u_int spx) { const struct grid_cell *gc, *sgc; struct utf8_data ud, sud; gc = grid_peek_cell(gd, px, py); grid_cell_get(gc, &ud); sgc = grid_peek_cell(sgd, spx, 0); grid_cell_get(sgc, &sud); if (ud.size != sud.size || ud.width != sud.width) return (0); return (memcmp(ud.data, sud.data, ud.size) == 0); } int window_copy_search_lr(struct grid *gd, struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last) { u_int ax, bx, px; for (ax = first; ax < last; ax++) { if (ax + sgd->sx >= gd->sx) break; for (bx = 0; bx < sgd->sx; bx++) { px = ax + bx; if (!window_copy_search_compare(gd, px, py, sgd, bx)) break; } if (bx == sgd->sx) { *ppx = ax; return (1); } } return (0); } int window_copy_search_rl(struct grid *gd, struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last) { u_int ax, bx, px; for (ax = last + 1; ax > first; ax--) { if (gd->sx - (ax - 1) < sgd->sx) continue; for (bx = 0; bx < sgd->sx; bx++) { px = ax - 1 + bx; if (!window_copy_search_compare(gd, px, py, sgd, bx)) break; } if (bx == sgd->sx) { *ppx = ax - 1; return (1); } } return (0); } void window_copy_search_up(struct window_pane *wp, const char *searchstr) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = data->backing, ss; struct screen_write_ctx ctx; struct grid *gd = s->grid, *sgd; struct grid_cell gc; size_t searchlen; u_int i, last, fx, fy, px; int utf8flag, n, wrapped, wrapflag; if (*searchstr == '\0') return; utf8flag = options_get_number(&wp->window->options, "utf8"); wrapflag = options_get_number(&wp->window->options, "wrap-search"); searchlen = screen_write_strlen(utf8flag, "%s", searchstr); screen_init(&ss, searchlen, 1, 0); screen_write_start(&ctx, NULL, &ss); memcpy(&gc, &grid_default_cell, sizeof gc); screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr); screen_write_stop(&ctx); fx = data->cx; fy = gd->hsize - data->oy + data->cy; if (fx == 0) { if (fy == 0) return; fx = gd->sx - 1; fy--; } else fx--; n = wrapped = 0; retry: sgd = ss.grid; for (i = fy + 1; i > 0; i--) { last = screen_size_x(s); if (i == fy + 1) last = fx; n = window_copy_search_rl(gd, sgd, &px, i - 1, 0, last); if (n) { window_copy_scroll_to(wp, px, i - 1); break; } } if (wrapflag && !n && !wrapped) { fx = gd->sx - 1; fy = gd->hsize + gd->sy - 1; wrapped = 1; goto retry; } screen_free(&ss); } void window_copy_search_down(struct window_pane *wp, const char *searchstr) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = data->backing, ss; struct screen_write_ctx ctx; struct grid *gd = s->grid, *sgd; struct grid_cell gc; size_t searchlen; u_int i, first, fx, fy, px; int utf8flag, n, wrapped, wrapflag; if (*searchstr == '\0') return; utf8flag = options_get_number(&wp->window->options, "utf8"); wrapflag = options_get_number(&wp->window->options, "wrap-search"); searchlen = screen_write_strlen(utf8flag, "%s", searchstr); screen_init(&ss, searchlen, 1, 0); screen_write_start(&ctx, NULL, &ss); memcpy(&gc, &grid_default_cell, sizeof gc); screen_write_nputs(&ctx, -1, &gc, utf8flag, "%s", searchstr); screen_write_stop(&ctx); fx = data->cx; fy = gd->hsize - data->oy + data->cy; if (fx == gd->sx - 1) { if (fy == gd->hsize + gd->sy) return; fx = 0; fy++; } else fx++; n = wrapped = 0; retry: sgd = ss.grid; for (i = fy + 1; i < gd->hsize + gd->sy + 1; i++) { first = 0; if (i == fy + 1) first = fx; n = window_copy_search_lr(gd, sgd, &px, i - 1, first, gd->sx); if (n) { window_copy_scroll_to(wp, px, i - 1); break; } } if (wrapflag && !n && !wrapped) { fx = 0; fy = 0; wrapped = 1; goto retry; } screen_free(&ss); } void window_copy_goto_line(struct window_pane *wp, const char *linestr) { struct window_copy_mode_data *data = wp->modedata; const char *errstr; u_int lineno; lineno = strtonum(linestr, 0, screen_hsize(data->backing), &errstr); if (errstr != NULL) return; data->oy = lineno; window_copy_update_selection(wp); window_copy_redraw_screen(wp); } void window_copy_write_line( struct window_pane *wp, struct screen_write_ctx *ctx, u_int py) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; struct options *oo = &wp->window->options; struct grid_cell gc; #ifdef TMATE char hdr[256]; #else char hdr[32]; #endif size_t last, xoff = 0, size = 0; window_mode_attrs(&gc, oo); last = screen_size_y(s) - 1; if (py == 0) { #ifdef TMATE if (data->inputtype != WINDOW_COPY_PASSWORD) { #endif size = xsnprintf(hdr, sizeof hdr, "[%u/%u]", data->oy, screen_hsize(data->backing)); if (size > screen_size_x(s)) size = screen_size_x(s); screen_write_cursormove(ctx, screen_size_x(s) - size, 0); screen_write_puts(ctx, &gc, "%s", hdr); #ifdef TMATE } #endif } else if (py == last && data->inputtype != WINDOW_COPY_OFF) { if (data->inputtype == WINDOW_COPY_NUMERICPREFIX) { xoff = size = xsnprintf(hdr, sizeof hdr, "Repeat: %u", data->numprefix); } else { #ifdef TMATE if (data->inputtype == WINDOW_COPY_PASSWORD) { int password_len = strlen(data->inputstr); xoff = size = xsnprintf(hdr, sizeof hdr, "%s: ", data->inputprompt); memset(hdr+xoff, '*', password_len); xoff += password_len; size += password_len; hdr[xoff] = '\0'; } else #endif xoff = size = xsnprintf(hdr, sizeof hdr, "%s: %s", data->inputprompt, data->inputstr); } screen_write_cursormove(ctx, 0, last); screen_write_puts(ctx, &gc, "%s", hdr); } else size = 0; screen_write_cursormove(ctx, xoff, py); screen_write_copy(ctx, data->backing, xoff, (screen_hsize(data->backing) - data->oy) + py, screen_size_x(s) - size, 1); if (py == data->cy && data->cx == screen_size_x(s)) { memcpy(&gc, &grid_default_cell, sizeof gc); screen_write_cursormove(ctx, screen_size_x(s) - 1, py); screen_write_putc(ctx, &gc, '$'); } } void window_copy_write_lines( struct window_pane *wp, struct screen_write_ctx *ctx, u_int py, u_int ny) { u_int yy; for (yy = py; yy < py + ny; yy++) window_copy_write_line(wp, ctx, py); } void window_copy_redraw_lines(struct window_pane *wp, u_int py, u_int ny) { struct window_copy_mode_data *data = wp->modedata; struct screen_write_ctx ctx; u_int i; screen_write_start(&ctx, wp, NULL); for (i = py; i < py + ny; i++) window_copy_write_line(wp, &ctx, i); screen_write_cursormove(&ctx, data->cx, data->cy); screen_write_stop(&ctx); } void window_copy_redraw_screen(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; window_copy_redraw_lines(wp, 0, screen_size_y(&data->screen)); } void window_copy_update_cursor(struct window_pane *wp, u_int cx, u_int cy) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; struct screen_write_ctx ctx; u_int old_cx, old_cy; old_cx = data->cx; old_cy = data->cy; data->cx = cx; data->cy = cy; if (old_cx == screen_size_x(s)) window_copy_redraw_lines(wp, old_cy, 1); if (data->cx == screen_size_x(s)) window_copy_redraw_lines(wp, data->cy, 1); else { screen_write_start(&ctx, wp, NULL); screen_write_cursormove(&ctx, data->cx, data->cy); screen_write_stop(&ctx); } } void window_copy_start_selection(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; data->selx = data->cx; data->sely = screen_hsize(data->backing) + data->cy - data->oy; s->sel.flag = 1; window_copy_update_selection(wp); } int window_copy_update_selection(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; struct options *oo = &wp->window->options; struct grid_cell gc; u_int sx, sy, ty, cy; if (!s->sel.flag) return (0); /* Set colours. */ window_mode_attrs(&gc, oo); /* Find top of screen. */ ty = screen_hsize(data->backing) - data->oy; /* Adjust the selection. */ sx = data->selx; sy = data->sely; if (sy < ty) { /* above screen */ if (!data->rectflag) sx = 0; sy = 0; } else if (sy > ty + screen_size_y(s) - 1) { /* below screen */ if (!data->rectflag) sx = screen_size_x(s) - 1; sy = screen_size_y(s) - 1; } else sy -= ty; sy = screen_hsize(s) + sy; screen_set_selection(s, sx, sy, data->cx, screen_hsize(s) + data->cy, data->rectflag, &gc); if (data->rectflag) { /* * Can't rely on the caller to redraw the right lines for * rectangle selection - find the highest line and the number * of lines, and redraw just past that in both directions */ cy = data->cy; if (sy < cy) window_copy_redraw_lines(wp, sy, cy - sy + 1); else window_copy_redraw_lines(wp, cy, sy - cy + 1); } return (1); } void * window_copy_get_selection(struct window_pane *wp, size_t *len) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; char *buf; size_t off; u_int i, xx, yy, sx, sy, ex, ey; u_int firstsx, lastex, restex, restsx; int keys; if (!s->sel.flag) 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->cx; yy = screen_hsize(data->backing) + data->cy - data->oy; if (yy < data->sely || (yy == data->sely && xx < data->selx)) { sx = xx; sy = yy; ex = data->selx; ey = data->sely; } else { sx = data->selx; sy = data->sely; ex = xx; ey = yy; } /* Trim ex to end of line. */ xx = window_copy_find_length(wp, ey); if (ex > xx) ex = xx; /* * Deal with rectangle-copy if necessary; four situations: start of * first line (firstsx), end of last line (lastex), start (restsx) and * end (restex) of all other lines. */ xx = screen_size_x(s); /* * Behave according to mode-keys. If it is emacs, copy like emacs, * keeping the top-left-most character, and dropping the * bottom-right-most, regardless of copy direction. If it is vi, also * keep bottom-right-most character. */ keys = options_get_number(&wp->window->options, "mode-keys"); if (data->rectflag) { /* * Need to ignore the column with the cursor in it, which for * rectangular copy means knowing which side the cursor is on. */ if (data->selx < data->cx) { /* Selection start is on the left. */ if (keys == MODEKEY_EMACS) { lastex = data->cx; restex = data->cx; } else { lastex = data->cx + 1; restex = data->cx + 1; } firstsx = data->selx; restsx = data->selx; } else { /* Cursor is on the left. */ lastex = data->selx + 1; restex = data->selx + 1; firstsx = data->cx; restsx = data->cx; } } else { if (keys == MODEKEY_EMACS) lastex = ex; else lastex = ex + 1; restex = xx; firstsx = sx; restsx = 0; } /* Copy the lines. */ if (sy == ey) window_copy_copy_line(wp, &buf, &off, sy, firstsx, lastex); else { window_copy_copy_line(wp, &buf, &off, sy, firstsx, restex); if (ey - sy > 1) { for (i = sy + 1; i < ey; i++) { window_copy_copy_line( wp, &buf, &off, i, restsx, restex); } } window_copy_copy_line(wp, &buf, &off, ey, restsx, lastex); } /* Don't bother if no data. */ if (off == 0) { free(buf); return (NULL); } *len = off - 1; /* remove final \n */ return (buf); } void window_copy_copy_buffer(struct window_pane *wp, int idx, void *buf, size_t len) { u_int limit; struct screen_write_ctx ctx; if (options_get_number(&global_options, "set-clipboard")) { screen_write_start(&ctx, wp, NULL); screen_write_setselection(&ctx, buf, len); screen_write_stop(&ctx); } if (idx == -1) { limit = options_get_number(&global_options, "buffer-limit"); paste_add(&global_buffers, buf, len, limit); } else paste_replace(&global_buffers, idx, buf, len); } void window_copy_copy_pipe( struct window_pane *wp, struct session *sess, int idx, const char *arg) { void *buf; size_t len; struct job *job; buf = window_copy_get_selection(wp, &len); if (buf == NULL) return; job = job_run(arg, sess, NULL, NULL, NULL); bufferevent_write(job->event, buf, len); window_copy_copy_buffer(wp, idx, buf, len); } void window_copy_copy_selection(struct window_pane *wp, int idx) { void* buf; size_t len; buf = window_copy_get_selection(wp, &len); if (buf == NULL) return; window_copy_copy_buffer(wp, idx, buf, len); } void window_copy_copy_line(struct window_pane *wp, char **buf, size_t *off, u_int sy, u_int sx, u_int ex) { struct window_copy_mode_data *data = wp->modedata; struct grid *gd = data->backing->grid; const struct grid_cell *gc; struct grid_line *gl; struct utf8_data ud; u_int i, xx, wrapped = 0; if (sx > ex) return; /* * Work out if the line was wrapped at the screen edge and all of it is * on screen. */ gl = &gd->linedata[sy]; if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx) wrapped = 1; /* If the line was wrapped, don't strip spaces (use the full length). */ if (wrapped) xx = gl->cellsize; else xx = window_copy_find_length(wp, sy); if (ex > xx) ex = xx; if (sx > xx) sx = xx; if (sx < ex) { for (i = sx; i < ex; i++) { gc = grid_peek_cell(gd, i, sy); if (gc->flags & GRID_FLAG_PADDING) continue; grid_cell_get(gc, &ud); *buf = xrealloc(*buf, 1, (*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, 1, (*off) + 1); (*buf)[(*off)++] = '\n'; } } void window_copy_clear_selection(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; u_int px, py; screen_clear_selection(&data->screen); py = screen_hsize(data->backing) + data->cy - data->oy; px = window_copy_find_length(wp, py); if (data->cx > px) window_copy_update_cursor(wp, px, data->cy); } int window_copy_in_set(struct window_pane *wp, u_int px, u_int py, const char *set) { struct window_copy_mode_data *data = wp->modedata; const struct grid_cell *gc; struct utf8_data ud; gc = grid_peek_cell(data->backing->grid, px, py); grid_cell_get(gc, &ud); if (ud.size != 1 || gc->flags & GRID_FLAG_PADDING) return (0); if (*ud.data == 0x00 || *ud.data == 0x7f) return (0); return (strchr(set, *ud.data) != NULL); } u_int window_copy_find_length(struct window_pane *wp, u_int py) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = data->backing; const struct grid_cell *gc; struct utf8_data ud; u_int px; /* * If the pane has been resized, its grid can contain old overlong * lines. grid_peek_cell does not allow accessing cells beyond the * width of the grid, and screen_write_copy treats them as spaces, so * ignore them here too. */ px = s->grid->linedata[py].cellsize; if (px > screen_size_x(s)) px = screen_size_x(s); while (px > 0) { gc = grid_peek_cell(s->grid, px - 1, py); grid_cell_get(gc, &ud); if (ud.size != 1 || *ud.data != ' ') break; px--; } return (px); } void window_copy_cursor_start_of_line(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; struct screen *back_s = data->backing; struct grid *gd = back_s->grid; u_int py; if (data->cx == 0) { py = screen_hsize(back_s) + data->cy - data->oy; while (py > 0 && gd->linedata[py-1].flags & GRID_LINE_WRAPPED) { window_copy_cursor_up(wp, 0); py = screen_hsize(back_s) + data->cy - data->oy; } } window_copy_update_cursor(wp, 0, data->cy); if (window_copy_update_selection(wp)) window_copy_redraw_lines(wp, data->cy, 1); } void window_copy_cursor_back_to_indentation(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; u_int px, py, xx; const struct grid_cell *gc; struct utf8_data ud; px = 0; py = screen_hsize(data->backing) + data->cy - data->oy; xx = window_copy_find_length(wp, py); while (px < xx) { gc = grid_peek_cell(data->backing->grid, px, py); grid_cell_get(gc, &ud); if (ud.size != 1 || *ud.data != ' ') break; px++; } window_copy_update_cursor(wp, px, data->cy); if (window_copy_update_selection(wp)) window_copy_redraw_lines(wp, data->cy, 1); } void window_copy_cursor_end_of_line(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; struct screen *back_s = data->backing; struct grid *gd = back_s->grid; u_int px, py; py = screen_hsize(back_s) + data->cy - data->oy; px = window_copy_find_length(wp, py); if (data->cx == px) { if (data->screen.sel.flag && data->rectflag) px = screen_size_x(back_s); if (gd->linedata[py].flags & GRID_LINE_WRAPPED) { while (py < gd->sy + gd->hsize && gd->linedata[py].flags & GRID_LINE_WRAPPED) { window_copy_cursor_down(wp, 0); py = screen_hsize(back_s) + data->cy - data->oy; } px = window_copy_find_length(wp, py); } } window_copy_update_cursor(wp, px, data->cy); if (window_copy_update_selection(wp)) window_copy_redraw_lines(wp, data->cy, 1); } void window_copy_cursor_left(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; if (data->cx == 0) { window_copy_cursor_up(wp, 0); window_copy_cursor_end_of_line(wp); } else { window_copy_update_cursor(wp, data->cx - 1, data->cy); if (window_copy_update_selection(wp)) window_copy_redraw_lines(wp, data->cy, 1); } } void window_copy_cursor_right(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; u_int px, py; if (data->screen.sel.flag && data->rectflag) px = screen_size_x(&data->screen); else { py = screen_hsize(data->backing) + data->cy - data->oy; px = window_copy_find_length(wp, py); } if (data->cx >= px) { window_copy_cursor_start_of_line(wp); window_copy_cursor_down(wp, 0); } else { window_copy_update_cursor(wp, data->cx + 1, data->cy); if (window_copy_update_selection(wp)) window_copy_redraw_lines(wp, data->cy, 1); } } void window_copy_cursor_up(struct window_pane *wp, int scroll_only) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; u_int ox, oy, px, py; oy = screen_hsize(data->backing) + data->cy - data->oy; ox = window_copy_find_length(wp, oy); if (data->cx != ox) { data->lastcx = data->cx; data->lastsx = ox; } data->cx = data->lastcx; if (scroll_only || data->cy == 0) { window_copy_scroll_down(wp, 1); if (scroll_only) { if (data->cy == screen_size_y(s) - 1) window_copy_redraw_lines(wp, data->cy, 1); else window_copy_redraw_lines(wp, data->cy, 2); } } else { window_copy_update_cursor(wp, data->cx, data->cy - 1); if (window_copy_update_selection(wp)) { if (data->cy == screen_size_y(s) - 1) window_copy_redraw_lines(wp, data->cy, 1); else window_copy_redraw_lines(wp, data->cy, 2); } } if (!data->screen.sel.flag || !data->rectflag) { py = screen_hsize(data->backing) + data->cy - data->oy; px = window_copy_find_length(wp, py); if ((data->cx >= data->lastsx && data->cx != px) || data->cx > px) window_copy_cursor_end_of_line(wp); } } void window_copy_cursor_down(struct window_pane *wp, int scroll_only) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; u_int ox, oy, px, py; oy = screen_hsize(data->backing) + data->cy - data->oy; ox = window_copy_find_length(wp, oy); if (data->cx != ox) { data->lastcx = data->cx; data->lastsx = ox; } data->cx = data->lastcx; if (scroll_only || data->cy == screen_size_y(s) - 1) { window_copy_scroll_up(wp, 1); if (scroll_only && data->cy > 0) window_copy_redraw_lines(wp, data->cy - 1, 2); } else { window_copy_update_cursor(wp, data->cx, data->cy + 1); if (window_copy_update_selection(wp)) window_copy_redraw_lines(wp, data->cy - 1, 2); } if (!data->screen.sel.flag || !data->rectflag) { py = screen_hsize(data->backing) + data->cy - data->oy; px = window_copy_find_length(wp, py); if ((data->cx >= data->lastsx && data->cx != px) || data->cx > px) window_copy_cursor_end_of_line(wp); } } void window_copy_cursor_jump(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; struct screen *back_s = data->backing; const struct grid_cell *gc; struct utf8_data ud; u_int px, py, xx; px = data->cx + 1; py = screen_hsize(back_s) + data->cy - data->oy; xx = window_copy_find_length(wp, py); while (px < xx) { gc = grid_peek_cell(back_s->grid, px, py); grid_cell_get(gc, &ud); if (!(gc->flags & GRID_FLAG_PADDING) && ud.size == 1 && *ud.data == data->jumpchar) { window_copy_update_cursor(wp, px, data->cy); if (window_copy_update_selection(wp)) window_copy_redraw_lines(wp, data->cy, 1); return; } px++; } } void window_copy_cursor_jump_back(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; struct screen *back_s = data->backing; const struct grid_cell *gc; struct utf8_data ud; u_int px, py; px = data->cx; py = screen_hsize(back_s) + data->cy - data->oy; if (px > 0) px--; for (;;) { gc = grid_peek_cell(back_s->grid, px, py); grid_cell_get(gc, &ud); if (!(gc->flags & GRID_FLAG_PADDING) && ud.size == 1 && *ud.data == data->jumpchar) { window_copy_update_cursor(wp, px, data->cy); if (window_copy_update_selection(wp)) window_copy_redraw_lines(wp, data->cy, 1); return; } if (px == 0) break; px--; } } void window_copy_cursor_jump_to(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; struct screen *back_s = data->backing; const struct grid_cell *gc; struct utf8_data ud; u_int px, py, xx; px = data->cx + 1; py = screen_hsize(back_s) + data->cy - data->oy; xx = window_copy_find_length(wp, py); while (px < xx) { gc = grid_peek_cell(back_s->grid, px, py); grid_cell_get(gc, &ud); if (!(gc->flags & GRID_FLAG_PADDING) && ud.size == 1 && *ud.data == data->jumpchar) { window_copy_update_cursor(wp, px - 1, data->cy); if (window_copy_update_selection(wp)) window_copy_redraw_lines(wp, data->cy, 1); return; } px++; } } void window_copy_cursor_jump_to_back(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; struct screen *back_s = data->backing; const struct grid_cell *gc; struct utf8_data ud; u_int px, py; px = data->cx; py = screen_hsize(back_s) + data->cy - data->oy; if (px > 0) px--; for (;;) { gc = grid_peek_cell(back_s->grid, px, py); grid_cell_get(gc, &ud); if (!(gc->flags & GRID_FLAG_PADDING) && ud.size == 1 && *ud.data == data->jumpchar) { window_copy_update_cursor(wp, px + 1, data->cy); if (window_copy_update_selection(wp)) window_copy_redraw_lines(wp, data->cy, 1); return; } if (px == 0) break; px--; } } void window_copy_cursor_next_word(struct window_pane *wp, const char *separators) { struct window_copy_mode_data *data = wp->modedata; struct screen *back_s = data->backing; u_int px, py, xx, yy; int expected = 0; px = data->cx; py = screen_hsize(back_s) + data->cy - data->oy; xx = window_copy_find_length(wp, py); yy = screen_hsize(back_s) + screen_size_y(back_s) - 1; /* * First skip past any nonword characters and then any word characters. * * expected is initially set to 0 for the former and then 1 for the * latter. */ do { while (px > xx || window_copy_in_set(wp, px, py, separators) == expected) { /* Move down if we're past the end of the line. */ if (px > xx) { if (py == yy) return; window_copy_cursor_down(wp, 0); px = 0; py = screen_hsize(back_s) + data->cy - data->oy; xx = window_copy_find_length(wp, py); } else px++; } expected = !expected; } while (expected == 1); window_copy_update_cursor(wp, px, data->cy); if (window_copy_update_selection(wp)) window_copy_redraw_lines(wp, data->cy, 1); } void window_copy_cursor_next_word_end(struct window_pane *wp, const char *separators) { struct window_copy_mode_data *data = wp->modedata; struct screen *back_s = data->backing; u_int px, py, xx, yy; int expected = 1; px = data->cx; py = screen_hsize(back_s) + data->cy - data->oy; xx = window_copy_find_length(wp, py); yy = screen_hsize(back_s) + screen_size_y(back_s) - 1; /* * First skip past any word characters, then any nonword characters. * * expected is initially set to 1 for the former and then 0 for the * latter. */ do { while (px > xx || window_copy_in_set(wp, px, py, separators) == expected) { /* Move down if we're past the end of the line. */ if (px > xx) { if (py == yy) return; window_copy_cursor_down(wp, 0); px = 0; py = screen_hsize(back_s) + data->cy - data->oy; xx = window_copy_find_length(wp, py); } else px++; } expected = !expected; } while (expected == 0); window_copy_update_cursor(wp, px, data->cy); if (window_copy_update_selection(wp)) window_copy_redraw_lines(wp, data->cy, 1); } /* Move to the previous place where a word begins. */ void window_copy_cursor_previous_word(struct window_pane *wp, const char *separators) { struct window_copy_mode_data *data = wp->modedata; u_int px, py; px = data->cx; py = screen_hsize(data->backing) + data->cy - data->oy; /* Move back to the previous word character. */ for (;;) { if (px > 0) { px--; if (!window_copy_in_set(wp, px, py, separators)) break; } else { if (data->cy == 0 && (screen_hsize(data->backing) == 0 || data->oy >= screen_hsize(data->backing) - 1)) goto out; window_copy_cursor_up(wp, 0); py = screen_hsize(data->backing) + data->cy - data->oy; px = window_copy_find_length(wp, py); } } /* Move back to the beginning of this word. */ while (px > 0 && !window_copy_in_set(wp, px - 1, py, separators)) px--; out: window_copy_update_cursor(wp, px, data->cy); if (window_copy_update_selection(wp)) window_copy_redraw_lines(wp, data->cy, 1); } void window_copy_scroll_up(struct window_pane *wp, u_int ny) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; struct screen_write_ctx ctx; if (data->oy < ny) ny = data->oy; if (ny == 0) return; data->oy -= ny; screen_write_start(&ctx, wp, NULL); screen_write_cursormove(&ctx, 0, 0); screen_write_deleteline(&ctx, ny); window_copy_write_lines(wp, &ctx, screen_size_y(s) - ny, ny); window_copy_write_line(wp, &ctx, 0); if (screen_size_y(s) > 1) window_copy_write_line(wp, &ctx, 1); if (screen_size_y(s) > 3) window_copy_write_line(wp, &ctx, screen_size_y(s) - 2); if (s->sel.flag && screen_size_y(s) > ny) { window_copy_update_selection(wp); window_copy_write_line(wp, &ctx, screen_size_y(s) - ny - 1); } screen_write_cursormove(&ctx, data->cx, data->cy); window_copy_update_selection(wp); screen_write_stop(&ctx); } void window_copy_scroll_down(struct window_pane *wp, u_int ny) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; struct screen_write_ctx ctx; if (ny > screen_hsize(data->backing)) return; if (data->oy > screen_hsize(data->backing) - ny) ny = screen_hsize(data->backing) - data->oy; if (ny == 0) return; data->oy += ny; screen_write_start(&ctx, wp, NULL); screen_write_cursormove(&ctx, 0, 0); screen_write_insertline(&ctx, ny); window_copy_write_lines(wp, &ctx, 0, ny); if (s->sel.flag && screen_size_y(s) > ny) { window_copy_update_selection(wp); window_copy_write_line(wp, &ctx, ny); } else if (ny == 1) /* nuke position */ window_copy_write_line(wp, &ctx, 1); screen_write_cursormove(&ctx, data->cx, data->cy); window_copy_update_selection(wp); screen_write_stop(&ctx); } void window_copy_rectangle_toggle(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; u_int px, py; data->rectflag = !data->rectflag; py = screen_hsize(data->backing) + data->cy - data->oy; px = window_copy_find_length(wp, py); if (data->cx > px) window_copy_update_cursor(wp, px, data->cy); window_copy_update_selection(wp); window_copy_redraw_screen(wp); } tmate-1.8.10/window.c000066400000000000000000000643251242461015400143770ustar00rootroot00000000000000/* $Id$ */ /* * 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" #include "tmate.h" /* * Each window is attached to a number of panes, each of which is a pty. This * file contains code to handle them. * * A pane has two buffers attached, these are filled and emptied by the main * server poll loop. Output data is received from pty's in screen format, * translated and returned as a series of escape sequences and strings via * input_parse (in input.c). Input data is received as key codes and written * directly via input_key. * * Each pane also has a "virtual" screen (screen.c) which contains the current * state and is redisplayed when the window is reattached to a client. * * Windows are stored directly on a global array and wrapped in any number of * winlink structs to be linked onto local session RB trees. A reference count * is maintained and a window removed from the global list and destroyed when * it reaches zero. */ /* Global window list. */ struct windows windows; /* Global panes tree. */ struct window_pane_tree all_window_panes; u_int next_window_pane_id; u_int next_window_id; void window_pane_timer_callback(int, short, void *); void window_pane_read_callback(struct bufferevent *, void *); void window_pane_error_callback(struct bufferevent *, short, void *); RB_GENERATE(winlinks, winlink, entry, winlink_cmp); int winlink_cmp(struct winlink *wl1, struct winlink *wl2) { return (wl1->idx - wl2->idx); } RB_GENERATE(window_pane_tree, window_pane, tree_entry, window_pane_cmp); int window_pane_cmp(struct window_pane *wp1, struct window_pane *wp2) { return (wp1->id - wp2->id); } struct winlink * winlink_find_by_window(struct winlinks *wwl, struct window *w) { struct winlink *wl; RB_FOREACH(wl, winlinks, wwl) { if (wl->window == w) return (wl); } return (NULL); } struct winlink * winlink_find_by_index(struct winlinks *wwl, int idx) { struct winlink wl; if (idx < 0) fatalx("bad index"); wl.idx = idx; return (RB_FIND(winlinks, wwl, &wl)); } 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); } int winlink_next_index(struct winlinks *wwl, int idx) { int i; i = idx; do { if (winlink_find_by_index(wwl, i) == NULL) return (i); if (i == INT_MAX) i = 0; else i++; } while (i != idx); return (-1); } u_int winlink_count(struct winlinks *wwl) { struct winlink *wl; u_int n; n = 0; RB_FOREACH(wl, winlinks, wwl) n++; return (n); } struct winlink * winlink_add(struct winlinks *wwl, int idx) { struct winlink *wl; if (idx < 0) { if ((idx = winlink_next_index(wwl, -idx - 1)) == -1) return (NULL); } else if (winlink_find_by_index(wwl, idx) != NULL) return (NULL); wl = xcalloc(1, sizeof *wl); wl->idx = idx; RB_INSERT(winlinks, wwl, wl); return (wl); } void winlink_set_window(struct winlink *wl, struct window *w) { wl->window = w; w->references++; } void winlink_remove(struct winlinks *wwl, struct winlink *wl) { struct window *w = wl->window; RB_REMOVE(winlinks, wwl, wl); free(wl->status_text); free(wl); if (w != NULL) window_remove_ref(w); } struct winlink * winlink_next(struct winlink *wl) { return (RB_NEXT(winlinks, wwl, wl)); } struct winlink * winlink_previous(struct winlink *wl) { return (RB_PREV(winlinks, wwl, wl)); } struct winlink * winlink_next_by_number(struct winlink *wl, struct session *s, int n) { for (; n > 0; n--) { if ((wl = RB_NEXT(winlinks, wwl, wl)) == NULL) wl = RB_MIN(winlinks, &s->windows); } return (wl); } struct winlink * winlink_previous_by_number(struct winlink *wl, struct session *s, int n) { for (; n > 0; n--) { if ((wl = RB_PREV(winlinks, wwl, wl)) == NULL) wl = RB_MAX(winlinks, &s->windows); } return (wl); } void winlink_stack_push(struct winlink_stack *stack, struct winlink *wl) { if (wl == NULL) return; winlink_stack_remove(stack, wl); TAILQ_INSERT_HEAD(stack, wl, sentry); } void winlink_stack_remove(struct winlink_stack *stack, struct winlink *wl) { struct winlink *wl2; if (wl == NULL) return; TAILQ_FOREACH(wl2, stack, sentry) { if (wl2 == wl) { TAILQ_REMOVE(stack, wl, sentry); return; } } } int window_index(struct window *s, u_int *i) { for (*i = 0; *i < ARRAY_LENGTH(&windows); (*i)++) { if (s == ARRAY_ITEM(&windows, *i)) return (0); } return (-1); } struct window * window_find_by_id(u_int id) { struct window *w; u_int i; for (i = 0; i < ARRAY_LENGTH(&windows); i++) { w = ARRAY_ITEM(&windows, i); if (w->id == id) return (w); } return (NULL); } struct window * window_create1(u_int sx, u_int sy) { struct window *w; u_int i; w = xcalloc(1, sizeof *w); w->id = next_window_id++; 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; options_init(&w->options, &global_w_options); if (options_get_number(&w->options, "automatic-rename")) queue_window_name(w); for (i = 0; i < ARRAY_LENGTH(&windows); i++) { if (ARRAY_ITEM(&windows, i) == NULL) { ARRAY_SET(&windows, i, w); break; } } if (i == ARRAY_LENGTH(&windows)) ARRAY_ADD(&windows, w); w->references = 0; return (w); } struct window * window_create(const char *name, const char *cmd, const char *shell, const char *cwd, struct environ *env, struct termios *tio, u_int sx, u_int sy, u_int hlimit, char **cause) { struct window *w; struct window_pane *wp; w = window_create1(sx, sy); wp = window_add_pane(w, hlimit); layout_init(w, wp); if (window_pane_spawn(wp, cmd, shell, cwd, env, tio, cause) != 0) { window_destroy(w); return (NULL); } w->active = TAILQ_FIRST(&w->panes); if (name != NULL) { w->name = xstrdup(name); options_set_number(&w->options, "automatic-rename", 0); } else w->name = default_window_name(w); return (w); } void window_destroy(struct window *w) { u_int i; window_unzoom(w); if (window_index(w, &i) != 0) fatalx("index not found"); ARRAY_SET(&windows, i, NULL); while (!ARRAY_EMPTY(&windows) && ARRAY_LAST(&windows) == NULL) ARRAY_TRUNC(&windows, 1); if (w->layout_root != NULL) layout_free(w); if (event_initialized(&w->name_timer)) evtimer_del(&w->name_timer); options_free(&w->options); window_destroy_panes(w); free(w->name); free(w); } void window_remove_ref(struct window *w) { if (w->references == 0) fatal("bad reference count"); w->references--; if (w->references == 0) window_destroy(w); } void window_set_name(struct window *w, const char *new_name) { #ifdef TMATE /* * We don't want to sync the layout too much. * We might want to have some sort of timer for when to * sync the layout. */ if (!strcmp(w->name, new_name)) return; #endif free(w->name); w->name = xstrdup(new_name); notify_window_renamed(w); tmate_sync_layout(); } void window_resize(struct window *w, u_int sx, u_int sy) { w->sx = sx; w->sy = sy; } void window_set_active_pane(struct window *w, struct window_pane *wp) { if (wp == w->active) return; w->last = w->active; w->active = wp; while (!window_pane_visible(w->active)) { w->active = TAILQ_PREV(w->active, window_panes, entry); if (w->active == NULL) w->active = TAILQ_LAST(&w->panes, window_panes); if (w->active == wp) return; } } struct window_pane * window_get_active_at(struct window *w, u_int x, u_int y) { struct window_pane *wp; TAILQ_FOREACH(wp, &w->panes, entry) { if (!window_pane_visible(wp)) continue; if (x < wp->xoff || x > wp->xoff + wp->sx) continue; if (y < wp->yoff || y > wp->yoff + wp->sy) continue; return (wp); } return (NULL); } void window_set_active_at(struct window *w, u_int x, u_int y) { struct window_pane *wp; wp = window_get_active_at(w, x, y); if (wp != NULL && wp != w->active) window_set_active_pane(w, wp); } struct window_pane * window_find_string(struct window *w, const char *s) { u_int x, y; x = w->sx / 2; y = w->sy / 2; if (strcasecmp(s, "top") == 0) y = 0; else if (strcasecmp(s, "bottom") == 0) y = w->sy - 1; else if (strcasecmp(s, "left") == 0) x = 0; else if (strcasecmp(s, "right") == 0) x = w->sx - 1; else if (strcasecmp(s, "top-left") == 0) { x = 0; y = 0; } else if (strcasecmp(s, "top-right") == 0) { x = w->sx - 1; y = 0; } else if (strcasecmp(s, "bottom-left") == 0) { x = 0; y = w->sy - 1; } else if (strcasecmp(s, "bottom-right") == 0) { x = w->sx - 1; y = w->sy - 1; } else return (NULL); return (window_get_active_at(w, x, y)); } 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_pane_visible(wp)) return (-1); if (window_count_panes(w) == 1) return (-1); if (w->active != wp) window_set_active_pane(w, wp); 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; 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; TAILQ_FOREACH(wp, &w->panes, entry) { wp->layout_cell = wp->saved_layout_cell; wp->saved_layout_cell = NULL; } layout_fix_panes(w, w->sx, w->sy); return (0); } struct window_pane * window_add_pane(struct window *w, u_int hlimit) { struct window_pane *wp; wp = window_pane_create(w, w->sx, w->sy, hlimit); if (TAILQ_EMPTY(&w->panes)) TAILQ_INSERT_HEAD(&w->panes, wp, entry); else TAILQ_INSERT_AFTER(&w->panes, w->active, wp, entry); return (wp); } void window_remove_pane(struct window *w, struct window_pane *wp) { if (wp == w->active) { w->active = w->last; w->last = NULL; if (w->active == NULL) { w->active = TAILQ_PREV(wp, window_panes, entry); if (w->active == NULL) w->active = TAILQ_NEXT(wp, entry); } } else if (wp == w->last) w->last = NULL; TAILQ_REMOVE(&w->panes, wp, entry); window_pane_destroy(wp); } struct window_pane * window_pane_at_index(struct window *w, u_int idx) { struct window_pane *wp; u_int n; n = 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); } } /* Return list of printable window flag symbols. No flags is just a space. */ char * window_printable_flags(struct session *s, struct winlink *wl) { char flags[BUFSIZ]; int pos; pos = 0; if (wl->flags & WINLINK_ACTIVITY) flags[pos++] = '#'; if (wl->flags & WINLINK_BELL) flags[pos++] = '!'; if (wl->flags & WINLINK_CONTENT) flags[pos++] = '+'; if (wl->flags & WINLINK_SILENCE) flags[pos++] = '~'; if (wl == s->curw) flags[pos++] = '*'; if (wl == TAILQ_FIRST(&s->lastw)) flags[pos++] = '-'; if (wl->window->flags & WINDOW_ZOOMED) flags[pos++] = 'Z'; if (pos == 0) flags[pos++] = ' '; flags[pos] = '\0'; return (xstrdup(flags)); } /* Find pane in global tree by id. */ struct window_pane * window_pane_find_by_id(u_int id) { struct window_pane wp; wp.id = id; return (RB_FIND(window_pane_tree, &all_window_panes, &wp)); } struct window_pane * window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) { struct window_pane *wp; wp = xcalloc(1, sizeof *wp); wp->window = w; wp->id = next_window_pane_id++; RB_INSERT(window_pane_tree, &all_window_panes, wp); wp->cmd = NULL; wp->shell = NULL; wp->cwd = NULL; wp->fd = -1; wp->event = NULL; wp->mode = NULL; wp->layout_cell = NULL; wp->xoff = 0; wp->yoff = 0; wp->sx = sx; wp->sy = sy; wp->pipe_fd = -1; wp->pipe_off = 0; wp->pipe_event = NULL; wp->tmate_off = 0; wp->saved_grid = NULL; screen_init(&wp->base, sx, sy, hlimit); wp->screen = &wp->base; input_init(wp); return (wp); } void window_pane_destroy(struct window_pane *wp) { window_pane_reset_mode(wp); if (event_initialized(&wp->changes_timer)) evtimer_del(&wp->changes_timer); if (wp->fd != -1) { bufferevent_free(wp->event); close(wp->fd); } input_free(wp); 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); } RB_REMOVE(window_pane_tree, &all_window_panes, wp); free(wp->cwd); free(wp->shell); free(wp->cmd); free(wp); } int window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell, const char *cwd, struct environ *env, struct termios *tio, char **cause) { struct winsize ws; char *argv0, paneid[16]; const char *ptr; struct termios tio2; if (wp->fd != -1) { bufferevent_free(wp->event); close(wp->fd); } if (cmd != NULL) { free(wp->cmd); wp->cmd = xstrdup(cmd); } if (shell != NULL) { free(wp->shell); wp->shell = xstrdup(shell); } if (cwd != NULL) { free(wp->cwd); wp->cwd = xstrdup(cwd); } log_debug("spawn: %s -- %s", wp->shell, wp->cmd); memset(&ws, 0, sizeof ws); ws.ws_col = screen_size_x(&wp->base); ws.ws_row = screen_size_y(&wp->base); switch (wp->pid = forkpty(&wp->fd, wp->tty, NULL, &ws)) { case -1: wp->fd = -1; xasprintf(cause, "%s: %s", cmd, strerror(errno)); return (-1); case 0: if (chdir(wp->cwd) != 0) chdir("/"); if (tcgetattr(STDIN_FILENO, &tio2) != 0) fatal("tcgetattr failed"); if (tio != NULL) memcpy(tio2.c_cc, tio->c_cc, sizeof tio2.c_cc); tio2.c_cc[VERASE] = '\177'; #ifdef IUTF8 if (options_get_number(&wp->window->options, "utf8")) tio2.c_iflag |= IUTF8; #endif if (tcsetattr(STDIN_FILENO, TCSANOW, &tio2) != 0) fatal("tcgetattr failed"); closefrom(STDERR_FILENO + 1); xsnprintf(paneid, sizeof paneid, "%%%u", wp->id); environ_set(env, "TMUX_PANE", paneid); environ_push(env); clear_signals(1); log_close(); setenv("SHELL", wp->shell, 1); ptr = strrchr(wp->shell, '/'); if (*wp->cmd != '\0') { /* Use the command. */ if (ptr != NULL && *(ptr + 1) != '\0') xasprintf(&argv0, "%s", ptr + 1); else xasprintf(&argv0, "%s", wp->shell); execl(wp->shell, argv0, "-c", wp->cmd, (char *) NULL); fatal("execl failed"); } /* No command; fork a login shell. */ if (ptr != NULL && *(ptr + 1) != '\0') xasprintf(&argv0, "-%s", ptr + 1); else xasprintf(&argv0, "-%s", wp->shell); execl(wp->shell, argv0, (char *) NULL); fatal("execl failed"); } setblocking(wp->fd, 0); wp->event = bufferevent_new(wp->fd, window_pane_read_callback, NULL, window_pane_error_callback, wp); bufferevent_enable(wp->event, EV_READ|EV_WRITE); return (0); } void window_pane_timer_start(struct window_pane *wp) { struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 1000; evtimer_del(&wp->changes_timer); evtimer_set(&wp->changes_timer, window_pane_timer_callback, wp); evtimer_add(&wp->changes_timer, &tv); } void window_pane_timer_callback(unused int fd, unused short events, void *data) { struct window_pane *wp = data; struct window *w = wp->window; u_int interval, trigger; interval = options_get_number(&w->options, "c0-change-interval"); trigger = options_get_number(&w->options, "c0-change-trigger"); if (wp->changes_redraw++ == interval) { wp->flags |= PANE_REDRAW; wp->changes_redraw = 0; } if (trigger == 0 || wp->changes < trigger) { wp->flags |= PANE_REDRAW; wp->flags &= ~PANE_DROP; } else window_pane_timer_start(wp); wp->changes = 0; } void window_pane_read_callback(unused struct bufferevent *bufev, void *data) { struct window_pane *wp = data; char *new_data; size_t new_size; new_size = EVBUFFER_LENGTH(wp->event->input) - wp->pipe_off; if (wp->pipe_fd != -1 && new_size > 0) { /* FIXME tmux: * - new_data = EVBUFFER_DATA(wp->event->input); * + new_data = EVBUFFER_DATA(wp->event->input) + wp->pipe_off; * also, can the buffer be too small? */ new_data = EVBUFFER_DATA(wp->event->input); bufferevent_write(wp->pipe_event, new_data, new_size); } new_size = EVBUFFER_LENGTH(wp->event->input) - wp->tmate_off; new_data = EVBUFFER_DATA(wp->event->input) + wp->tmate_off; if (new_size > 0) tmate_pty_data(wp, new_data, new_size); input_parse(wp); wp->pipe_off = EVBUFFER_LENGTH(wp->event->input); wp->tmate_off = EVBUFFER_LENGTH(wp->event->input); /* * If we get here, we're not outputting anymore, so set the silence * flag on the window. */ wp->window->flags |= WINDOW_SILENCE; if (gettimeofday(&wp->window->silence_timer, NULL) != 0) fatal("gettimeofday failed."); } void window_pane_error_callback( unused struct bufferevent *bufev, unused short what, void *data) { struct window_pane *wp = data; server_destroy_pane(wp); } void window_pane_resize(struct window_pane *wp, u_int sx, u_int sy) { if (sx == wp->sx && sy == wp->sy) return; wp->sx = sx; wp->sy = sy; screen_resize(&wp->base, sx, sy, wp->saved_grid == NULL); if (wp->mode != NULL) wp->mode->resize(wp, 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->window->options, "alternate-screen")) return; sx = screen_size_x(s); sy = screen_size_y(s); wp->saved_grid = grid_create(sx, sy, 0); grid_duplicate_lines(wp->saved_grid, 0, s->grid, screen_hsize(s), sy); 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); 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 (wp->saved_grid == NULL) return; if (!options_get_number(&wp->window->options, "alternate-screen")) return; sx = screen_size_x(s); sy = screen_size_y(s); /* * If the current size is bigger, temporarily resize to the old size * before copying back. */ if (sy > wp->saved_grid->sy) screen_resize(s, sx, wp->saved_grid->sy, 1); /* Restore the grid, cursor position and cell. */ grid_duplicate_lines(s->grid, screen_hsize(s), wp->saved_grid, 0, sy); if (cursor) s->cx = wp->saved_cx; if (s->cx > screen_size_x(s) - 1) s->cx = screen_size_x(s) - 1; if (cursor) s->cy = wp->saved_cy; if (s->cy > screen_size_y(s) - 1) s->cy = screen_size_y(s) - 1; memcpy(gc, &wp->saved_cell, sizeof *gc); /* * Turn history back on (so resize can use it) and then resize back to * the current size. */ wp->base.grid->flags |= GRID_HISTORY; if (sy > wp->saved_grid->sy || sx != wp->saved_grid->sx) screen_resize(s, sx, sy, 1); grid_destroy(wp->saved_grid); wp->saved_grid = NULL; wp->flags |= PANE_REDRAW; } int window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode) { struct screen *s; if (wp->mode != NULL) return (1); wp->mode = mode; if ((s = wp->mode->init(wp)) != NULL) wp->screen = s; wp->flags |= PANE_REDRAW; return (0); } void window_pane_reset_mode(struct window_pane *wp) { if (wp->mode == NULL) return; wp->mode->free(wp); wp->mode = NULL; wp->screen = &wp->base; wp->flags |= PANE_REDRAW; tmate_sync_copy_mode(wp); } void window_pane_key(struct window_pane *wp, struct session *sess, int key) { struct window_pane *wp2; if (!window_pane_visible(wp)) return; if (wp->mode != NULL) { if (wp->mode->key != NULL) wp->mode->key(wp, sess, key); return; } if (wp->fd == -1) return; input_key(wp, key); if (options_get_number(&wp->window->options, "synchronize-panes")) { TAILQ_FOREACH(wp2, &wp->window->panes, entry) { if (wp2 == wp || wp2->mode != NULL) continue; if (wp2->fd != -1 && window_pane_visible(wp2)) input_key(wp2, key); } } } void window_pane_mouse( struct window_pane *wp, struct session *sess, struct mouse_event *m) { if (!window_pane_visible(wp)) return; if (m->x < wp->xoff || m->x >= wp->xoff + wp->sx) return; if (m->y < wp->yoff || m->y >= wp->yoff + wp->sy) return; m->x -= wp->xoff; m->y -= wp->yoff; if (wp->mode != NULL) { if (wp->mode->mouse != NULL && options_get_number(&wp->window->options, "mode-mouse")) wp->mode->mouse(wp, sess, m); } else if (wp->fd != -1) input_mouse(wp, sess, m); } int window_pane_visible(struct window_pane *wp) { struct window *w = wp->window; if (wp->layout_cell == NULL) return (0); if (wp->xoff >= w->sx || wp->yoff >= w->sy) return (0); if (wp->xoff + wp->sx > w->sx || wp->yoff + wp->sy > w->sy) return (0); return (1); } char * window_pane_search(struct window_pane *wp, const char *searchstr, u_int *lineno) { struct screen *s = &wp->base; char *newsearchstr, *line, *msg; u_int i; msg = NULL; xasprintf(&newsearchstr, "*%s*", searchstr); for (i = 0; i < screen_size_y(s); i++) { line = grid_view_string_cells(s->grid, 0, i, screen_size_x(s)); if (fnmatch(newsearchstr, line, 0) == 0) { msg = line; if (lineno != NULL) *lineno = i; break; } free(line); } free(newsearchstr); return (msg); } /* Find the pane directly above another. */ struct window_pane * window_pane_find_up(struct window_pane *wp) { struct window_pane *wp2; u_int left, top; if (wp == NULL || !window_pane_visible(wp)) return (NULL); top = wp->yoff; if (top == 0) top = wp->window->sy + 1; left = wp->xoff; TAILQ_FOREACH(wp2, &wp->window->panes, entry) { if (!window_pane_visible(wp2)) continue; if (wp2->yoff + wp2->sy + 1 != top) continue; if (left >= wp2->xoff && left <= wp2->xoff + wp2->sx) return (wp2); } return (NULL); } /* Find the pane directly below another. */ struct window_pane * window_pane_find_down(struct window_pane *wp) { struct window_pane *wp2; u_int left, bottom; if (wp == NULL || !window_pane_visible(wp)) return (NULL); bottom = wp->yoff + wp->sy + 1; if (bottom >= wp->window->sy) bottom = 0; left = wp->xoff; TAILQ_FOREACH(wp2, &wp->window->panes, entry) { if (!window_pane_visible(wp2)) continue; if (wp2->yoff != bottom) continue; if (left >= wp2->xoff && left <= wp2->xoff + wp2->sx) return (wp2); } return (NULL); } /* * Find the pane directly to the left of another, adjacent to the left side and * containing the top edge. */ struct window_pane * window_pane_find_left(struct window_pane *wp) { struct window_pane *wp2; u_int left, top; if (wp == NULL || !window_pane_visible(wp)) return (NULL); left = wp->xoff; if (left == 0) left = wp->window->sx + 1; top = wp->yoff; TAILQ_FOREACH(wp2, &wp->window->panes, entry) { if (!window_pane_visible(wp2)) continue; if (wp2->xoff + wp2->sx + 1 != left) continue; if (top >= wp2->yoff && top <= wp2->yoff + wp2->sy) return (wp2); } return (NULL); } /* * Find the pane directly to the right of another, that is adjacent to the * right edge and including the top edge. */ struct window_pane * window_pane_find_right(struct window_pane *wp) { struct window_pane *wp2; u_int right, top; if (wp == NULL || !window_pane_visible(wp)) return (NULL); right = wp->xoff + wp->sx + 1; if (right >= wp->window->sx) right = 0; top = wp->yoff; TAILQ_FOREACH(wp2, &wp->window->panes, entry) { if (!window_pane_visible(wp2)) continue; if (wp2->xoff != right) continue; if (top >= wp2->yoff && top <= wp2->yoff + wp2->sy) return (wp2); } return (NULL); } /* Clear alert flags for a winlink */ void winlink_clear_flags(struct winlink *wl) { struct winlink *wm; struct session *s; struct window *w; u_int i; for (i = 0; i < ARRAY_LENGTH(&windows); i++) { if ((w = ARRAY_ITEM(&windows, i)) == NULL) continue; RB_FOREACH(s, sessions, &sessions) { if ((wm = session_has(s, w)) == NULL) continue; if (wm->window != wl->window) continue; if ((wm->flags & WINLINK_ALERTFLAGS) == 0) continue; wm->flags &= ~WINLINK_ALERTFLAGS; server_status_session(s); } } } /* Set the grid_cell with fg/bg/attr information when window is in a mode. */ void window_mode_attrs(struct grid_cell *gc, struct options *oo) { memcpy(gc, &grid_default_cell, sizeof *gc); colour_set_fg(gc, options_get_number(oo, "mode-fg")); colour_set_bg(gc, options_get_number(oo, "mode-bg")); gc->attr |= options_get_number(oo, "mode-attr"); } tmate-1.8.10/www/000077500000000000000000000000001242461015400135365ustar00rootroot00000000000000tmate-1.8.10/www/images/000077500000000000000000000000001242461015400150035ustar00rootroot00000000000000tmate-1.8.10/www/images/tmux3.png000066400000000000000000001120711242461015400165730ustar00rootroot00000000000000PNG  IHDR2w pHYs  ~IDATx;㸶 ڣ=Sf]6#߄lFg1#s)B~8/!O3^_๾^8z^9 9dEWni+tx|v]-[qg;5@پ/6kĜz6F~Do:~K{˓wWv*~OQ["^ދgsb;VܔힿkO73~ 7>ĞWq:?U.ջ"o`/I}+M?v\3{\vmYc6 O!ks`WO`P_ٶ*ܸx=(g*mo>Iu<Åx@odq9)Iw}ҏs5/و6~k"s/?#z~i5Zٿ,ך>~糞;QG}M'JOK4cqeM)tΟ}ex&ͽ)ۜ]?e|DT真%糚6=8O˙.]9P{KΑi5%ߵJϯ룴3Y6>YO^`NW}#XOgg66WyG^?܏O=zK~bN5/[yKd8g<{mG]N2@o!cVc9ԋx9̩ٸf򹿜Wŵ{YtZo?KEo_Bp2)է-N<~ֳV>up?6"^մsY쥮`7S4ҿ#aNrV{mWw G`,vBkpx6[%xk1{[-D4z*=*`0]Nܾ/:z=קy“9Uݾefweѻy^Ϗ|_5zr^-=zڳ=rs2saC(x9}|%j\:sW9N\]]q.oλ gx'zii{#C|Ǝo | ];;uէ+ZOOo-m͝9y:&Xx72ߌ,t_ϭ/o1iވV;|.5':F[ۼfl 1iپoOtl[رhsbgx|J_`=YOx?!g}սui}J1[T.m:.J.]oI˟,&qy=}BM3rseo4q|66 j[@mSG[gOi?(bMt|}66`]_ɏ8m䔢=|__\sx/okkgd7[֌}Jakgu>OUu~agݺ>wk/+OR:30=;hn{oM}>[z&Xvfjm>C~{鯻 33~ƎWqC}FW闞Oߪ?5nM~_l|US2M4/=ľ57gx6{.3V}~1=ln붷Zh?}4bb_qS4g.[@Oo}_ΚM܎0>q9[7 c[bn_~}GƷϙO{Mt_WGIOs 7:>g3)_ZKyv>_Joώ?kկ{*}c\YeN^x/! 3Eyޠ#K(Gwh?='W'n1_Vun<[Z%q`6Xq;ֈ}vI綴,w՜0/snݙAGԷgluCOvLo~xj=jh 5`:.A|M P[Vl} ˳:=} X&n|m3\yJ:g(kW՝zцkA>m_O/W)\("bZԽVŊٿص_>P/ѣ Gxbnu{ų}?*=Ccx_zцX}j3 V| xbVo]|ۭ"/-]s~ԧeȽ L\s0nHgƫcN j{^^'>g__v=g?ݻ޻w>2RD3Qs6/LWϪ9?Vǫ]UzYK}i{#|4+}?af;ުW>ۧ0B}%/DpP#l )O@H[_\m*ؓ3q 0/ϸlD~jE1Ho4*\SX]--mgƴz1W5!3D;g;2͘əǷ>NK+OygW۸'z}\ǜ^sUFysН/%NU"hc6gNٶWp{Mnsm/UD˙|޴VV-0S\F3}z`Oq{ѶEEտ;Gq9oac8D?[IG7ff=ze϶:{ح~|g`6|C\ 乪j6ἷU`K#z7'W+v7x!3f,ҟ'}FxמWQ=jy6_9U8qcik5~Fԣ#/ݏ*=TsM7msPश/mok]^Z1֌۸!wfQb/}|fh[5vo&tQ[O&>WJUso{9I>5מg؊aU9~O<<9[g5."[忾}F>+[ݏjU["?DK۱flo;?5CK?}Ӷ3Oģ7*W_`*E~Ɩ(娖b̓޺)-给뷴GY qՕɽui_^ׄT۵=5ۨZmU}ud-ϗ&*mo*3sidViW{DM[gsm[Hгj`qmgX:q>σjʫS[7BW_E-Ag]7p3%~G=UmUc5Jw{oEжHhIF Wra0+m[xj-O{K_?}w65ҷ]>eߍ=Z}G`mTF?3mV3<6z+y _5u޶K?so߫x-OtyOD{:2!Rnl<[oշi{+=k[vGz8_y(L듈R$GZ^'JӏS乼/*68_A8õؓ}zch=*`0`am,E}xsa|Ƿ}~z"GOe-,c[s2v 8Z0{sw?+D*5-#b*[}d`6ۚ^?jϽo)o|Lt+'KQ<Ga9?ſ&?}MG[Y2f],Όk9ʜL@_93 n7e,M?:?5ϙ8s'1q >q6όh%z-Mק?vtP<ɗ@s+c+vh>`aW7kn"9pO钶 9i\}jxXw>WG:մg#`RVA=*`0[%يc]ddL 6nq~W?Ws]:cJޢS/s7돣3mK2K/qCMt)+c쏭Z]?g*;hu6Q>9?=,W#^Yn>9Q=o|醀i|=~_}\Y_+,KxR?ij6݋^Aڜd-'ofzyG%ӵOv=?;>o|J> ~\=;~ =\г~@0 x"P[}(8ft=<5@ '5q޴dA5}GzO{kU֪t#{K6?.G7jEٍp_f9Wӷ9GG~7O}_e\{卮 }OtCҧ_eD/șHO'߽g0Ӫ?G'}"C|g(5lvJn礓'z z8}s{n΍WM+_axU[nU_=CZ-@/qkϜVv ҵP41/mC`]&n]E%oίWA9C|ijg]k}e-\W/'l38ɭ~?q}7ޖN &gvDFh=š[6.ƳOޞz_&#ڭvsjz;yWWj}]hU S@+[6/< PísrZs=x{L͕s.^%!3Y[ [W;I,mQ3^ϥOX"/LϭvǶz/fkڧ?>>>RyvI=]5cݜq?W>w}UPמ7ޖOC3¬Q{[*nh=*`0]>3*:4m"LsJVio(olu|o:۸VqN^y۱g|ˏ䨘2Ig?}Ͽ->gBREX?^J繊o_'9g_7j,}oŪx;*>e~ڪ=[уV1pn?>g#@c3߁_Z hU%1ϨRlյg1DO ?9Es 3V'ܢ?7g+ҭKӏ8Umx;݈X+nc^̓ܶj])J4nY}\me<+kgǧg{\ۇO%Co6hw|iZ(om9t{KHo"w%'͹^ϼtDz lrEoxx/Ͷ$o}sN荵7>5RO9~s']U[=`mV?_̙ӷU2;砜_Ea|%*Pm,iZۈ_Ly~ t(gӟs|mG=|\=\Y+l/>igs9Ҭ[}jYWq3[c'\cJ3(νk+bVw9q=P[<϶nDYnGLO>^yom\;AȰnŧ~g >vu:OO|FBPb-޼]|2/9WvISw;kˊ۫W[DyoVMK3J~9j]NFo<^Onն#i52Og}Zp}yX]sY}o{~OZt9~YL Ї`~j%c~U2{`0tYSsgzr<]Wޤ?%?{Gˏ4ӑ9k[s:46U/Ͻx^w35q|~J?e&/=?_}(d,XV?M-|b*'G9z9UȾ^1=qkv;mWKoye݉ۙ:s>/b=˛3&>3e{U~Ɲ5q ߡ~Z>vƞ߲ulƳ￿^e?zoKCB+mvM=鳞EOyWrnXEG[sJ߿vcȷjdl߉>9?>u1DXktgbv?MƯ/y6/˭~ԗf#o{fȩyy6v\賕h\]͔kWw^rƓը3qmpo<ܓhޕ^~('}~1s>j:7Wݞ븲vԔm>+nxV E?vҭD^9U~ֵϦy G塦sg S"j}Ɯws)۸oiGD5\?Y\M@~=Kzو6~k"s/?L< &4WW#&na[%;sv;+>beb tɹ\evfs_9?KXg5mz{d+nNSXlј홝m^8P"ƓFt=V9O{gm9܋Li{{?wf;߭gxgoێlw+O{Oco>>#4zO3>:!ǿO'_ҼtM#q{J\Wtl[/37čmgi>j49ޞWޜ֞>&g{˛Q5||c)Fz1!WW?~Γ{FQޚ<č?"3j<,3nU z1ד'g#@+{UrW~WΟxXgD:J.}|+}Vl%WbiǷҽ lgcuN3'­6>Sr-m)V=k QY3::?3vk5ZL$ٽ K7AM![Vu1{-}a͆j߮gJU(>ϫoX:tNDψ?[}+[{;砚uoۜƽUʲW}(i譾o:?(w35k-#'evC3LMH鉥jДm-uo%]Of[i'1%.'noܻ}-fzznq\d\{+M}EOm'>ysZw>j49-o~~W}8jg|=ǷH*[~ ~z||im3V9gvhۧU~~Msm{Oggm1\">Wwlܕ0V=뮳jhbm?9Xp`䶛ݾikt:9[ɕnX~TcU6<yZ]MN=J4ϿψnWWGU|Q"'m" FOmm7dk1iDy_S٧[ 'ǿ{i\Eǧ&=Fi+?gD]_ѣY[?=z =}G$Z!}q;okKz~%=V^rjꕎ$cN+isӦb4W7=3.SS?79մuڊtyJni.o~Ͻ$*mfy{i{0Q˭}۸*9͏ƚ*xPX `,J`OV f`>Լ^>w.>m{Dv[tC7n|->?y9nhD>׫>˻kp }LRIMnQ_<1^Rޫ^ǟ9[< yRo#hD>׫s~5= =]nWħ&ɡ{#aiiim~V3_\lK_Z=/@%{H4C_{uԷj%퟇WaFE,ְ4W,EtL܂Z+M֍mĔmtN@[ݨ]}[>ŧt>FNAX#cneDok6q6)[z4gؼͶibg0ȰHuTZ|ٮw=3ۏ0fUL_z~͇iψeǾ 38^[/8\q{]|ҭ,3W5-n5sJg(!^O<O\Z 4n]O{eT</ڀc5{L?*=Yq 0[پz^_}JNWiB+5SyklONnTSgH:?Ki׍Uj{fN{Gc5LSI)(jk{=˛[}bS'"CMEm5n|s~ljੌ~i#~E鰮7Vy%@Oc_EGqIG>Nc|g.rQz6fK?g+Ѹ_}*3=eOx;j˱igU|-rb+`&l5yYƶ'+bґJg')#gw#pV>cj;Vw-5{oi~fODv|h2u"YeRYU(iOrrv[zl|{mwK]+qQ[ ׋1WGqTζ,c۳)۱q=x6vL wX݈\=ǟɸv/>5I>4>~~׊Ii_B5!30F0=ۿI)ǫ; lYs mFl> '+VjmVC{M|=z\qjU^|Btڮ? ͏ƚ3g03'[% fVɊ[L sgzǜ~ޕ>~u95E\KۧF*ҟު}6?}j*S|_=G.=Gm/c_>>]Ϯ/v?Fz7g)=U>+zD/ݳ'{k7Ef>bӝ)Q?run.byWT9g9XcѨVqySNl=w[3Dmkk){癲MVgVi/}"Ɵk!>=tpo|oc"= VyZs-7F}>#c+Uz͓a[ꭶdltIGĵǟ~=O{XZo^}^7:?=3B4ѣߞ0'{n\ΊZնr9Jb2F[.-[}S~"$&n[bCɔSKQzcNMkwmζ,c۳)۱è`\u5eʖ>Yo~oDۈnoVl;.8siWV'+nLJ{{;O=@O~0֜kc4V{2q `dd-`&nn&6zW)k./mZ][ſ,)DĪ>Qǧ>ϖֲFM{x^'9Og/ŧt?63o1Y9Zz|λ짤xye+cN_ZF<-՞iۗ4.;gΘyul-;?;a`O l\K|+`^9O[WU=GKsx9S}w>ElJ/Lm>:âRm9[i\|gO9G㺣ֽ켕3 W}Lj>ҧG(iLJѬgеf׎ni?5RO߈P^-ݰwn8|<ƊƓ?(F'`mzCͫG)x/ӹ#alO}t(gVf{`a[%夂yRmM}S^~4IP!>x6qw& ]"f<1}SqӷqGu->5z+ξj#-ac_s,p`m֔Ϧgy~.ou32>]]Zxw'>z^k["8׫[yf͙+2:D0'[% -i&`#J`OJ-`YϸlPskO~J\}UNIgH3OZ[*ޗ3ZGOyOO|oè16susճVimO{ոܶi>.oto*V-OyKPzʓz8gE|^Mw6[FKtUr/_JǮƺHꛇGƳj{Quq5v|0Z$ҿ_SN!''mf*ea-TtIV\}bxkKꢦ}F^@ @+.I<{|Δ˛|scKVңmu-!ڽm#YƶOScǓOlײ>|rVõ?>*O^%?]^Wks)9SNr|FOb&ϾbeϜ5Wkzm|vcy^m<{Ruyogd9ۺ&[?{EpiK_x%&UhKǓVO-mog\<'z|kҚnWZ*>o+0ϫ3T}$[}Ϋ_G c|6`O_B0[[%'+n3q 0wD m^9w]mgȏ<ߧ\ޚ^p?}{Ƨ>`]}슫AgVuԶV\7>W#<=< 4_-=yjd?m5"*Os/-dIw`6FKlU~csl93sVv9rʉFq;,[̵Ôm3e[:F=?δ:? =~eMMَ:-4ZuKy 'KS}oF}YSOȍjUGZzԋ{f3<gf+n7\~G_Xz꽦,|FI*0UޱKѮs,]:{72Ϧt$77D{s~->mWl_ؓ3q 0[L f`>z^wJ޻s/e9[ ޛ_C#|ԧ]q5ܪTMħx~ub|&Xz5g:{y^j%DħU<#Sg__Zl=&?>yΏ-Sf*ҟ->sVʭq8Vno9İs翨h"n3S=3ϔmzܺ8X)WOGx`;-.:UZšmzv{hrahso|[eދO~kֿ*/ۿ~/5VmϿGח%{9(gCW\_:Q{(*y3<5>-cl;F8Lavǥڮ+z^|fkoDܫWEOagg)^ĭC˺%Yj>ӷΤϨ/E/lOȍ[VEX!i@)c9^/Y/+n^yBVc+fʬO|5-nέiUyǮ.ODϱt4MJ<ґ܄S8_Z]?gOC%c-`&n3q 0Y|;}z^9:gY!"t$pVX,oN /OmZx[Nۃc\#\uF9ӽv4g[S#ֳViEHhgƫAc^Q IwkcJW~ҼPyX1>@ψtA/+J2æg9[ɕnX~TcN EggZx3e=Jgk_e5CwAtOV_]VP_Λ3S[A^Ws뿈>g^:>ov7+*9Lru>~];LgZq x˜5Wk@gkosǾ 38^gYH{Vsn{OV{t*穯]sWrJ3j|hsT{n{9۶=&KgT\|C|7[ͧp>\'[% fVɊ[L }զ4z9Gn{Syrj|/o:Q=456>]p,yݮdrƓ>v[7>W=˵+_oܟ_<3`/;OZWsFt]+>iֻ~who=_MX ح~vpE#3Kb8Vћw|)l=wa붴kZhWOzwJ=ZϿ޶Ǔ; mq=_:R2gy58khmurWS9#6k9O|g#vD\?ܫ|K3[|"ҏC|==}!1Wӿ//J#rV5n"z8|$6Eg_&nN(r3e=n|}zJtKn5|j| r6z jKWkH%>i_?ro~\!^)sBUT0vօW#g[x&J?.9gf#Dl:~IOs}i1ߣg֍O"<=]n<ͻn l񖞴7H.w.JneY۔knEVr9o*mדw[#@o{ѣ>麾w|Vm3lvWj'nj\m.ꟁnϫןn'W_V} VlV, jOw+}&\9鮢:sdVݞ8gyϹJgŎjq-Mzʳ]oZڽOdVі63*WcU sF&W{qvEǼ>cWwO;Vɫ/a{z'K= 3"t r(c7n+Zj~m_uջ-k`M}mx$_ދW=::?ӧǹN5x{iFoq݋OMo[u5vwR\KGc)-o~{allo]\~z'"z0{n/?nvSlitL1^5{~~՚Y%lOZZQz&Wt3eRK?޺)g[i'1%6 &n{Zvsz\nqg6gnk{W۸~Zl̢ۧ;O|Vr k[V9Zf 0s|V%r[}/'j~ەvOj[mG4??mZUK^4=J?^_j??^]qz;JK0) X}EW6 f[JcXIu|Fx6pVK2q 0X3n?ԤT{d:+N9#"'}Jwfj??9mWy[::OMV1pU9~tUTZshs/"˭W>s+z4[E4?F7sjpk~#nIӫiFVqU}Fv1_s 7>}@}~6gKoyd馝3G&[El) n[m-M-.>5)9>5RO[Eo^η4?'"g|(-{ ੌ~>vg9ǧWE{mUJW} %-K1[ߦ(cK5V޹VQ[%to \'gbr5)Q?譆n'>}Ɠb>ILI>;>+o!`{Emמ^E)mKt⹾%<瓀[}ֽqwT98 EUfRNfnom㣝}Afq:gF\q{A]Kei?=}=zx?ږ[?+\kҸ6~[^dV篶ت#5ⓓ{>š*xPX `,J`OV f`6y^+)\s/.:~Ŗk^m?6^==P;3zW5p9= *y[O94>sQ8(JnUjqizn[/FoV{}tYn׽%]{'z<鳕h~j(=ck_9Dlͥs>!/w@[)vΚjϭ"6{G+zöCiæ׏~ny K{>:aжw8ψ?[OZW'c#{9(ntJ-*9+>[PҚa/Gꢦ}Fo?:?Ώ@LܺK_ϼu<~V]^{x'ͻo|4U%3׈[izĕoƟ'զ8SsYROz6miNw繁W[Ƨ)[fS_5v+Ct\Ux|Nso<ﵟcőU{=[|yXqrovG c |6`O_B0[[%'+n3q 0[L f`0u~ z^ci2kU2`&n3q 0[L f`0-yίUlNfvy"ֶghQ1?»W'7'Gٽgw~ɟ5Ո|s17׫>|:N:K_MH]iKW Q;@_fο IQ P_ɿկpƎ'9]~h8e3'n*3|HS95y%"kʫ̜" )zHrq5ߧo:~K{ `>o唟 9GlQV4>mO뷴SjC:߶Qų>9zyƨxַψR?q9${9j}U꿟|ןV'n_W_V~um\3rW֎g-3͹bt}~rϿVXySZs[3[ߞƳm{5 =yW_\o3F?}F|{J{s~lޚ֤Ubj{Sq@;e϶-yvv/7fg]i;dX|VӦG2qD;|Zk:]Dx~R{n#..-K{Aϫ#J?mn}H?kv:N~lZ}n{o]/E-=>>?wyq\Et<+zIoKވu0y^UF&=UuwgIUeUN?]^RނS{>?3&om L/=>'翐\g &k&fd`uΏ nm'v#$9Jf:M`:J`-_B0[G}y˪59EdfW58C{h_ߨ">"b]hO5lŭAvwUj 4%OmϞ Zu{"ȋ3>?X>2q3=~V򓳒 -j٪VƧmIrmR_k5!t[]^:>>r~{~j|DZ̨xַψRWq9@97ZN?}>?9w,պs^kv8?gJagWc[qMV?ȸv* &nZ rUm?؛i~Oq;\ϴ&>JqZ}nKouQ-g?܋Ofv?//ѶW')5gmLݞ{~xUZc.( nŤoEk["?ǵWO&˹woKވu*yR]UF&=U<U2kL 2q>bL0OX3q 6`5_V-D|'-kU2`&n3q 0[L -3{z^%強>Wm[W|:W;.`-V>y"oܧ<5@Vi9?eP LN-b}V(қ~w_} Vɓ:O#f69ߜMwөtse XI'&|̶"j8 s}ʛCi='I6͔-_&nG{U{_}m #cX/XEN[m՞Ks(bed3]и~U3lsՔ-/! ճTjM9X-vRɰOWhלFϻzn<pJ-`&n3q 0[`f_׫F4jsTj5n>sz瘜w~וsjǷtNQj{dvO?-o~CK?UCic l<naߛ'utZ}sbK Qr=ClXZb,S f{V[ΖQzέNhVɓيjkzx޸5+׳gjFt{[+{9> ?>\skog*ϐ{&yavu yר>[NnqӁ?D(ȭV@&ng^΢Wj3zO䞭h}3fdg=&9Jkm\^Zl-m*X`~_?kݴeݵw}?VF':3ߚ|Zl +]Qw>ΫKkVU9ɛ6PVڲV[ƥmupdqʪglN;g|-=Jl]Z$n,؁[q "!3:BNyϙ.9'=zTۦ\=eg`XiWkCךY};֚&M? gu'_Mxw-T^gyZSOxWw.sEuσX[?}Ry^&.MG{ӗOMnO;Aʛ[R3yTǽM\=^ֲmʹt+>[>77Ϟӊ=&iO~L,Z[8+n+fcYq Z`,`9Џ^_`>BO}^R>s_1:hLtz?@˨׫>|:N>x:+n[v5/ h駏I&2_DoPk?*o1Os]i:^~ӏ]Ggym{mM Oe[ϸFP?0'[%Yq:M o!Yc*`0}Af+군Oywkr۫c_ZR^NjoQm4m__<Øi+NL嗱D>6egxZz^ }nV}x &ȹZO@o!V7礙mܜKs:1ǿ+u'g{#㙮Wӧthtqc7M~/bj`5{nfp&{JVOөߺm}UjƖ?OL/p?:}/5o_1ix2C}_OZ 87Bj se@ a`-_B0[L ?z^~UsȜߕRGs>SPWQWK-->W~//otq[3]z6zt~GoJ< 0)_ϸ_[yxVQzsmz\q[:6D]$⽔/f3?m6ϔ9usr鰣bggHt yƞ8@OοP[R</!-`&n3q rzww8Bo!RR5SjS9msDw+s zcz'm*'ο?@˨:jt%sS27G}biDrx2]-[xq|ﯦW?vտ/^M2cnbOL¸Sui?=2 p;q9P-[]u鷊Ri~Rʞ椾妟s4]+{F2g[%_߿Vf]{~ 駏I&2_΍暧դUb& 9#s/?9)/+ϸOhXg1W8>NzϏcxNW?O~3~3=&c8:}/H? cmQV_]s~:>~MKV5٪?|ޙgA__߼w_[% o~K]Kz` οϪ7`w_B=_O/"C=y3_8=`({駓y>^~ӏ]GgΥy^{^M Oe=؃>szIy Oɴ翐 S]r8B[%Y:M Xh[z/@OJ-`'na Xq 0Xr߭:nbMow|~N3hԼ ?@οP3n3q 0[L lGп^>Q0?3q 0[L f`aw3]Vlz ~^γ}SעpeML5[q{vT14^s|iJR$">WGjSsND}P,=SK?gKs~/qնmOt|O\~}\@οP#V*lh } 9@S :`EXc'nwr=3bu4Xq{o+95g\ ;d)m4u/q*gdM$ >v97 f^}8[W'TFl Zl 7H>Fzw-;H4n_~N)ֵ7{דQ9|s{Flgd_I8MUDTt&no+{U[oNDrxu8e3'n*矺h/s }.&"mӜ?»W-Fu?u@[km<8{q5ߧo:~K{Ki4 lu-d ȫ_p~5?"r姙_>oWUY>#JQ:m^Ϥ[}끶Q}mzk_sRߊr.WD\ǕlլV-=՚Ö́D\yu˫/iժ#b<{OiysgUr~~˙yjM_-iւxL_u{OϸPGm8nnb-)ն7(ێ?9?><M华zYz>i#[q SV,V~3q[o.2[H!?b9ܶwgM$#zhn|<OH?<_O nz폿O_^'sүi[\&[Ǒϸ=[[?_k Z&z|X}1~/|n{=.Wѥ~j} SYthۭ~z=Ɵq[Ǧ^jZi@9__Ry_i&^/l/oۈ/mKM~Js|~~9Y>GN3>lYy5u*z䬩=`D3_]y\||~#?]^RނS{>?T3&okqnjhj]VBkp=V<ȯwY] `RV>NS¸i-mXq ,^+̤,LM@+_B077Y}z"3 rr5O)f?>Š[~argOff-L3>?X`6i9o7|sU WѾz5ҕFj9g{Rk*}?|~~rvMloxU~描mv~e` [-^WTRSkEy 3Ϲ>GDϞmxUZg`?L^|wk^ϸh5T][OWڎW9?>3>`]EXJ4}?rzU[_gf mcz Gٽgj^=E'W /ϸu}]V#>*(e˿Ї[fNJgd%c,d} ]`[% f)lB<|K`k0OX3q 0['zN 0[bdL f`0[f~?zJ?yo}Js۶;ԯtvh]Z}D߸Oyj$Ӂs~ʠZ=P97Oܿ5@'uF:O]M5ͼm~)r51Ͽ9SkUmܳ D,"JOLmE4q4t7=}[){N<ӓm )[`]L܎Z!a6>VG$._&nMڪ=_M't!QVp)g4q {lg 4)[`u_B@g> Ͽ1rzZgΓaci9[ |3tq7Ywܺy55e v 9l 0[L f`0} ^WG9Uij>}}19=+Jo霤Ȝ98~~{k[ؓۇsu194xrs&J?>5=1eg}y)(U|ƶ1*yj3¾7=9O%ﵺ=z5eŭlE{uk{ZwٰtXl-ru/[VЖ'դ 7=qkVgԌ(uVW>Zm]s|\|k8OK{lT!?M8!NKPk['"{/5?LμE0kg5X9V=[Z5g3f~/p V, {Lr̹ZZU/n׺i˺kx$Gz5SkM{O_6<*ص"3qL?gk513bNFomжLIDAT׷UpsOYVokꫴxcljKx>+b7AOr7Y=; @[&ny8!K2q {߲`O)GO|I6Q 9lO~KJk˴.Tq >z9zzcIz*WKo[=k*9yKiP;Xq +29ylQZaŘ̙yu <fy笼lζⰴoeԊ@gUj'ݞӣP:W9=%}i.0?miutN!:BNyn5]1LtˏUQm[_Zm= P-4i)_M)5ŲaD׹vkkY!\0KP%zgxkMg'Kfg-TzogvoZ٦JwU?OJb|oj[,Z[9 @οPÊ[d"Ϧ0q X`_B0[L^ݿο3nz͔ZgTN|[GF뜣1݊\-^Ix58ʉ,52ο] ܔQXZ;5'ކLqe3oU>?ϫgwO,q CW̘ۿXӵ0F~q]mZ4~iϷsοs}bvgjqοcd6sKjCV6»zWz]F~TԻx8&ǯ9oE,M+"w{FʞQY}8G%r`kW7@?_%}-g#tkү̽sil5ɭBxOοw 3nx0eE|̕{7^'S{+铟{Gi;>?WӂS{z>o3__ߜ^RނSw`26c/8T?οV@kVsxB)dI8+_K=5k(s&㢚.]'Ԛ|^1c>3n֞:~8U+ڡRIMWp[;gMwx/m3A?GG3\sym?jzoU޸_`#9u'~}9@o!J?L v*GPX.+n_+Bh_nRxY~J?+'嫼%w+oxX:?=b-M9RUK39.5/W0Oh?_=˙z1穾ϫOLy8%[y?Z}&k寧SV5Q!o^Ph*!gkmsl_yNJ+ך"35뿥mc%g:s2^M]˙:jۖ=9孏R{rob&3<鵞떷&oi}|v6<ωC'UO^qحt=.mglais_>ksMZYAgQ+z:}_'4Y~JF﹕:O{79>37?礕NN5vާs|E|k~}xǏ>kmَ/zI;O~>rsIv^yiʰ{1֯.[%pWϾ]kr_ SZϰ,-c@q;~,y|=xO}B9 mLmIOwg> 26g>}-αޛ:xs~yzH{/ozJ^~v{nlU?~dx_>LD3H_{z^/>÷Jz2pQr6%%U̹1V<`4_~q,Г/[L f`0-`&n3q 0[L f`0-`&n{vqGz??_% @ ?7 y]zY]^OyͮU~ϭu+'͈q)5GcY9{9<.]k,?S6-gϨj9ǕHgJ[~鴽6oStU]zKէZMyiE}6]wcהږqOtֶej9[lN'qή7QԯTG7wYͿɕ@ ZW_lKgE{L|I͜UupNIs~/P1u9V\mꕳLR0uަqYjY xR:e|zU9VǷ~O?WKq,ܪ}xS,exmm7}_3Do&|>tznIߑYgS∥֥UuNwT,a= ݸC;; 0Gv`]<;xC4/&wnVm^Q.p7Զ^qrvP__t{Y~;}k /iKSpR.cWٴ_DS*W:߲͟?bFsg+ŏS]xg&>kgRSmI}[+}; WA>%U]/T{r9?>1}󷋏oJJo:8?eT(dyj΋Sip/4O)M&i>yl)=LY?p.^M)=)7jv:F<4L>>}Kpq~vywyP_gSrS't|<ϲ|,RJozǿͯen#sKYp/TDPcdg++keͳM*[g+*wISoy5?ыz@O)ۛic} l}RᲴןū\ z|vz&.9+}ϣ1ܹ3Qei%}Z҅j{Z ~Wyryjl5؞iT>tJ>xGIw΢9?wgOΝOǞ)=|׌MjM-j gc_g\}u}Ju\J}FۯVQM/ն\).ϽWb{w}l {RRd}^s`SݸեY3q~̖$ftb:@$΀zr9t{޲JJZ%ŢxwlRl;fO_}Sg9[NNVû=7ok仙O`MoGjoij[`:n3}AWu>d>XUfhrr%6͖bZz9tƕ_i5mh6:vu|[wY:o^ B,8 ˚G &Kq1ËTw_ޤv\_fab eh}/s2ƾ\:^|PuaiM7޾]>vfIM-4DMҥnX5U- 7M{{$ WM;Og,o]- .o~j[f3ſb_ݻMk<³]= oo{{%6niߴe6[./eaM[(.omX˫..벼vS}Wwoq)&r1󣣣ϝob4~;7|S;1=ǟ Q*b=sQᕆZ4k/r9$}4C>Ҽڷ^+WUjw{aQW8[s{F|3Тn [b>u2RM_umFxQ;Ԩ.0X߯K=ؓtuhϯ\oL^OוiC'G~Oү:Ϣ4eл}S[gMWK-kxd212$&&T/iվ};Gk'wv:ݰs絃u ]`-n~S%߽CM{Tkrq~vƭ#WB&cUHzeWKDn[of_ݎ}IU:Zq>\X_aC {\`812lxws׭lj> +3檃~]oL<e` ֩{l_4|bjDѨ7}.նA_W'K^;}/",Eխ/F40l#{ň!{h|AbD6as|8z[:?ٓO@8#Wp0E/Xk:#JlB #Čnؐ-}*\Ӟ+~_Il֩9uΝs sOu <9:TS@xsOu uf^~g_g=W;طpe < <9:TS@xsOu <9:T[:OglG>L?/ёssS@xsOu <9:TS@xsOu u{jX,te]ZnbBOCq_n@tTl\^]eKY^ۤc-BOb8y"wyq~6Ϗ ַt{:y`{Q1Z —gqkS-))>4J{i({5 ΋:s#UL{gSj,}믽#GNDCվ-Ȇj:Ҿ4_qin|W6=,?1tw:dHS=V{of*7[\QkIRMu4e:zzw)]SԷgW`rq~Ov~]g7usDn[of3۳7A8ֿPz$xIҾG7{U;n_l8nU;n6eϽ^6i8n[:9' .wP5ыTw_ޤv_:\_fºئ?uKu|i_2>9tptY:->T]X`qySov׵ϫ{WW]^"ˁK߭ /6.oiߤvk.o w?}>4j+&oL>ԙntY͚`ϝob4~q5|S;͜Lޝ::wl~MUڷϠ^Uv廪DG^46j:wlS8o4hF"m;}gh+kEF恝cdd`|:fŐ=Wqy~u.DlOWX))sTLvo믽^p} 8}XcjŹ`:)ޛAu`= {cѰ[]f,bX\+Ys_~˭k8s|) !9=Ʈϟ??wCgI5|׿7I׍οr-=0?Rř Joss bwd<|1/Ж@GCt͢ x^*"}:?ώp{>sʛöϯo?5f*oiZ:>gAMk߶ߜ]#g-w3:XsT6sCξc霷=`7:g薶Q\p.~FkqSatOFNlD8Ss?YGػw ]}>G5[''}c~4s`UM>sh`4W@ K_d.?d[ݴ)":r.!~j%f h~9N1 _{ns\a-ZG>F<ǵA邟3_7ֶ]R2g˜[)}:`K(cmƌ#g۹_k,5l? ?Қyϝ3gr߳n3fR=Wv`ހ5m+yFʙ40/u \ND/'8R#g/dKkN=M@Yu9|h!5kލjiH.͹}"<8{'w,o\ ,q>ϟ?~>_vt>ο0>z.Bۧ&׿?>>>>>UU?מؽ$޶#O&ΨȷTږh[Dy+/gks @fu1@ 1D3߯st4-=-5jl?W⢫ӳX%I_V[U#FȹGE>snDhۘoX߳>}\P⡴?ig49-|ٗ/_|B:6+ƗnSh?ss K{عz~FzHI4}箵-Z=shhX[z1FF8Fo->ghJ.m_??|>?9:pov{/х\WZ"꿭ٍ3S|j%w*k5\]F=T]隉OD,Z^巩z[;oe`ѼE,GYM9jE}Z`ߗ߫N7z&4\Gtt?W\}sT* ѭ VVzx-n-9U7h`F 멀"80Ch>}ۧgeܨ{~_wK%z.gxYJ.h|sW/'^ZjcJQ p&87 " #_,?:*y`+t/_S?tY*3[✻-~T)PZ?5r}+~:I֔v)i+rKPָHK/wO8q<۴"gFCp͢9X0>u\,gjZ5FZR $'gQP @7 \znzggmx&#xS`mVs݊R!OqKyn'sퟟ槉jooֳ'?~˟TJOʼG3?̨y g}c9W?> _3'vP㪜l|"m Dm57 y!Ȳ.T6qOg_(QʜoC|WfE>k~Z==j@eR霜TΠsl`Lgr>}U/Q pRP:ݜ-Կ{J~~*MKq9Sf>| q tOs.}l960G[Fw` o>[h)B<.f,y{.6fJy"٧V:7>Zki;K,zn_ /f?+n1ǔXjiՂE_1U-Yn0{"##xt!?n>3.꘮V-bs;pg'=9FQxCSۜk8KSk2??}[ߵޜWj9L/y4SS4ήy눸Smʚ{m`2M@r=x<G:-?DVaϟw˗/;cܦ?Jst#+֧Xꎆimg5Bdr}@ G*#vfm%jͣWkFWʕ}w'{qCt鹃<HN s)T~KED;IoY2S[NKM)rj *s=&}N>ͤZqӪN7zv} ׷HDٷ6""uJ,^]KS~*sy>W?j{/('M}LDy#[&sJƢة? {f[b֙J7Y8Dxkǁ,mۚPvp1."l9s-3,w PaR?i"${ Pml;dLs ]\1rxNk+27-n9=nfW`܍[\N/{s./󓟫sZ e+i/]}o3h`~/j.?qݾ }DzprSo?S{qQ4fsߟ_ϫ7b/1.|O,Yo~MԨ?_3gYjr& p;q3jhO:s )|η_[Jc>~ڦ}GQuCqwϽ6[x-UOؽʯُhO?nϷ;Z鷷JQD̠~ȵw[&=/}cеmK~ ]5[hv.{hc;s7io[0g]1itHGOI@MRwY҂}?;;Zm-?!:9٧Ϲɿ(O~i5u@Ct[Us7J*?G33s}K^kuaA,a[̹6c*foWg:^qy+QWZ-W)J9g)RgG ]/Jf`49ޣ]n~Eh'\z`h~C{ZK?[Rgʛ*>{4(s]R0(ˎҗpupU"2=Z~hզ}Zj{M\׶`k_['o?Wpד;6}JZx<{wRg5BO^-:ѭ_SbzJwƹ>;;Q7)ȗK-)sw\g8h;w'{qCtJ<>hi>/p%|2|ՕH'I~KD{,//_|1PY7?nooq93gy]{>sn}uSɞ ?CyF>WJQ=obҁ_0TYZ#YlR#\,w W>7 EqلQ@]opf%sE -zg'KW9/7^nH]C{~~KGFp{&ti>CtTZ?*s.Guտ#9~k\/q|{o^"pԲ8s0C}@Q )vi- Иe*fgK:1C-?ĵ檭&2[w3o龿R_*W\1r<w_>|C?.rS|JM?Qn!jhFS?j>vHWmA?N~7V2sFJǚ?-Q1yˌ:3N~z)hn"s^l!p]srʯGO~*o}|ҖOq+gqs华ψax,;fD?Wwxg#}.|OmӼ=Z$sBo)JZ]o?uz-'}\ys>Uj:Wqw>3zTfk5<īmKO7knLϥ\{IuDOQ_B(.?c}*}axvsF;zwh-kG[0D2=O=gz [&7K+?v'k{f۶ԭ|h is#^,rå?[z:oJ GpVhG0վ&G>a2~=ܞVaOkDQUzts|lniUuW?2_ޜՌZy[H}}"fg6a'pgcF'yWR%u @=[}u'P*z^Ϩ?No0>Qq%5@!i%?eM~suNs%-}lDѵFϹm?z:"a?UjS}]Sw.FxXw':gy۶f}~e6MҗG[>ڎ:#GǦ)M_+?ޏm@a툚YGj`4OD#,:'UpO? K_җpupϨc {}I1pw=`^?"_|:--}W3ayw~;avf,ΰ@[CGjmJk0P9 D%OdΙTHK~~񥟊ҒFO}gYv"[ۙyڦLo'?W59}#קk}X&?)?p>[-m~KtyGv߉kߜ W_ `}j-O9o[7 5^i4?9'Uz}N?~s.qOh[Eoi{>{TC܁:A >Z ?\sn̓W:g'mqj}0n q q':RY޻{}fҭm80UU?)>7g^-lUw+/0uBl{3gqQ;2Z+V `"LpgnH?51Q== h:sZ\s˻߾xo 8'GK5["&ozp K/o7rIr[umkg}n?'eۘ~M}LD.IZS'훿/sΝi8O-?&jX.IGґw#{ 0 mҗpupU"2=Z~osUڦ1PK/K{箉"׫Uq*Fks?wUZ_֔N0O飇-5Z~_X#D{}ΨVC)zu \E0uu#Vۗh[8E8η `vu}_)n.AsKG~js?۪>u1̨6b_?\]dRoH)orm0{O{O3bq}JpCu)#$=k[mkNt?cm}F_z饗^z>-}M}y5o\GOjmzVw])\W{vҁoC ]战]80ک|D_wDfttʾ_:׿{o6t4в^E/Y3هJFUG{dYz;PjWӿW>`}Faf.^z饗mzEؽۍc7ʁV@ni歾qXX^z饗>"=zgxpO{WuV=ppn\FqNI/q9v.w}K3DWZ}$Ԕh?G/Q9J\S?OJM GKqPY8{>s3`|T=6Q?/Yu9 <Ddz-Q>sJѶ<~]EȘ- ¹5kϧvfM읻?A)v~7x<G黥FkQŪxN|F0]\y?pFh]ȇq޶5-Vjgǹc?̮&=m}7g~[F7ym~F(oξS:>JgsSڛͲL*qO_}ku*;>OK[ap[xp~?SFP}h[]KgY9n 'oCtM/6*¹jO4?S_i\7>['\sV7=Rs=2|S"rXѮϥkG9W\??9R`)~,ȂϨc1ٙQ0P赠x<4ӟ:''?}i[W#8m~hV{m$VϚ|WߦID6W}o7^n_ f<`{0kԄE?}c9W̩'@[r@+Gr[@hyvPZ;XnC0\~3#fՉL}ʶumSjO'.>GnxX[d~P-AsKY/_jY?~9;f}+srN4oyxy[`k̢*;tSZ]^j5}b4k~˻ZM~Z-Zn)i>*?qymu Whj; Q6VRz>\z}7~^mkgo_ m`{9m[5J9[,ss8V ?k(nW-yZJf3fwsiӑCFWw.KGƉKܻ/Ds9̥cPyWPj/"7p8*޹>6ϫp+8P+-v^EBam.?`.@GVu8r8PIъ[-'?=0\wsuXjCt'wXJNyk<}*?kw?mٶ9@ 'K\>n&k8"_^} 8mVY8fQ3P2b3.`c`Yu9NoaH0'm˘X5xIxFow93Fbs?`%owx[*m_X#DZ-֨WyKik8m{{J'mͯ$"fw|eQJ:g/H8%` z*?4Bܻiaf[9s0:uh-'r>u9{Sۖ:?'UKZJ7'?Jx/_|6r}}*LfݶsW_Gp}ϕO՝۪~:].?Xnq&෭s޷J>Q~o??}t]?\|mJَsn97Dz*zs򟾪O@\L}ǿ]t`,3M®?P;7Um: Tڿ*WWFv'f3Ja1NkaM^i{?;sy. mW͏_up]|Ϝ[vhrEOWR.z.~x\!p.7'+-w7D3w .{>99v2u:ѷ䧬O~Ω9];oqۏOwQn+_C[ #Z6{$׏@zku֧x<[}c׿?>>>>>>zWhƴwJ[#PጺѺȑU%ږzNA gf4 Z5Ľҿ=ߖ.?)otە(}~tMKto~Rj߸Ϲ=-:[ m3ns깵^VYW}6}yOTZmgO~:w.ٞ pu8Nt]_-2NG~J6Z}h=Wt}޹?}i!zGkkؿF;!zU8ǣ֜0MlR:0GR}׿B9,I@8s_hǕA qX} }~._Z \Zmh`l*PmOs/mkJ[ι^>w"s Ea>O @Uz,w8PW`S}Y5\b^~?j}Y~/NLNs-)k򓟫sZ e+iz K!s]q?N]Eѧ ᷪ/OK7}OEOk5z,Ǒ}gz^ߵԟ#8S+Go?wϕ('9j{>"Υ9׾ew 0 83T=_%,} 0PYPy "-?3hiGSsks̿~t Ի[Fy|JM?Qn@o!2f^6߶+p=pOwu8n.xCªCtmK:p2[x@{7"?m ѥOyn}3 9g祥ئo[?`՝}Em[wbjA[C9n7zv} ׷8e.Q_/=?yH}_]ߟG[wVcx$Z^2Gb~#sfGƞV*/yk,m5}XG qT9}"f0D`w;yq;F:@=@OGXVY^;-`/v$??##uץWҚc΢3D]O^?ѧ9ѲȎm+[^; :;<.Ioo-& ,} 0;K_ !38Yu9'"23A~\}ԍS=s5Bw/y%d̸ۃFB塴7zzU窣#\>v(}T~󽿿FD(B:?N/Gi?^k2{oGh3FRGWۗh[V,3^1\K>wqF䧭!omOynqݷT+ח\^¶ƎZ}ګ4mC}֪isP_ms 1oc_}?cx5ǻ#2VZ]o?3^ηjgګ/oi}:> f]5F?#}gmp}*gԵ-o϶#lw^ѭ?F[!?smq=ҧk۫tk\F>hR#Ϣh^|ۯ>Ml;\:0{GRb.sD/ c*-qܯ#3~=Zx8=Jw֫%3Dxk3<5T9~}R{m/`ǩ5gW?eAoyvV3ʿyd8Vǎ g 3\1#Ƽ ǬO׵r\/ϮE<_oF)~3 ˹TFs[Y҂Gǵs~.Qۙh{sdJo p'ЎVN959Ε4gǹoʭiiU}ʉÜi\yKlZDW/=9Z~%>_-z.o}z՜Qmu}#3Qߵmr^9~-[SVc7;\Efe*Ϩ |RiP֨Ef?;?`/Q4Z|瓄c~^[DFx`-ƌFZ9˓nJGhvJ?=1˗/uR`|{]̘q.NƬ8`X#+=t3{%Ѐ`T\,+9鷧eUܖjD"=h;KgtK_Z?^ 9r/v~^SKڨvm]{}k>9/6~ȣI_}> Xͅ}~ !V˳?Gh=P3"g 3?_ [ѻy6җmZL9`K97vI_Z?YfuN< /y3pyW.\z:wct1kܒ>#;5wp08^}ga5D,Si4uL)vcs"@V3מ:|@7U` h#Z'`> ̨cK-XіjLgoN)җ ym,Eq)׾Ds/|k7^_Zf/#z7frY.`™e ^|tz c6F:9o[v9vsCݎcQvmf߿}2 @>ǗYosWZڋϟ?~>_1K]@OO+œ~TN~w}c9j\Y+Wݴ&Y+]F}53"b`9kU'm#n{U;׏uksԘJ8ge=Jk;{lBݜm:? oC|W캣LiFoUyK[-'*j֔fl\{g9^[E7lRG7"*x7zJ\/Wڿ۶W?'>M^q_sr|{oӿ^?ѧ)yۧ,jGy3PwRT'R!0/ݣWQW57"(z)msmns*~R^Usm^ݿ`%#jq5N<;x{+#oOAtSoLyVIycǹlfcᶖY 3cghuU5g W"gn6`do68^vZ mm/a:B< hK;0sm~?2h5fdnCtۿP0w5\c!m1[n^  ̢-ߜpg*8PW`񍶨c}{jq\r9΢'5Dǝq8PI x$r~~zan_WϽa}EѥpݹoK?^߳jڢgi޹!Jߚʩ.ۿst]?93~F;.ox7N{,gw]jOvۏCF{ jb-:'U -Ї)=gn<&@/B}d=u$y5G4re회 DEoK~}'Hq[F.j+ycivO_]i\#Zf?@]MC,zcSWl[[>;Թ57t`Y17^6!v!}%F+K/k/C{~J ѥk~sSވ։K-nik~կn'?~{nи곴f~I⿴j9Z?mD[Ͻq]夏>'~VRP{~ԟ/E={cwQ3nk?\&}{S}gmp}S荨ψ="'6X}O?:O峢>[_华O6oY*>=ok}{o_ŵ?[&+{ok/taHj[OMGX=^y^yWmg=qRg![o6ttc}*c@os-n5?^x8.?}ڼKCtGT9}gD20}ǿhօ4jBCwڗrNf?-"N󪋐}07pD}ՆnxNOMVCYJ^b1~g\o9~>unmK_?LD\-+qdx9ƝEϝ۷Oӷ:_m!)1韁@=:'UpO  Lҗpupu $g鞕)-K~FDs%=E׌0sW}giu[Zw=lwDpgTP51ϯ_[{=^ 4}8Wpf凡eOζ]դ9MT/>Mѧ"SöGUh%}|ӟj9w>VOiߚs~l~nN=Js2?W?$/WetU eO:}g}h[k8bsy]{\iʭ$ gYΕOrDv{J 97H=%.r5[ؿ>uEu~-|@Z}0 q 6FB>eyloW}\Vrn`Uzz- y}eH([0U @bv`= .=(>D=@yDs+`Uo m{"ܓ# q򺺹vHlt}Q=Q@~EUsvnʻr:KEoUҶnKBuׂ=,eo[s[{&a>?oKa@|໢0 `L?zJ$kJ"5^jߜVPR5viQA]XDq?QkMINRM맴G~f5sb}m=uq5aO3u+kߜ_j>+:Zȵp|ozY3JߍN?W?~ϫRz"cq4>,tynf[7[\{-[TzCCL\JD[zo6t40^|ux3rq#8?w}1;Cw*'G<>1./u}?{SqSJVdY ?][=?8PW`w}w{+﵎|,8o ;N'OY\S?4sD5ͯs9.… qdSWۥW?s-55ʹlo9es?a@]LtʣOӗn~4SS3oS/Xm@>=<.I@v,d0;K_ q#~q #k.gxy "ӣVmZR1V;D`m|gmrJ|JxPFkHrZ$uj swe:Q7)ȧK-\pȗ/_<jmfw[qO!oOyn}ʛ:rh蹭?gOE'۶~F.o~oPS}~sD<0QعR:?^m>syWޜY'sl忛fSG){}z h*8'z4܅Qoy} [kh۾5isKzbȉqg4dq\^^K8g<i&b~k9x9/-cz y̚X{+-9qWN:ɕ N̼Gg>%\l8?Vz`qhgr|q ؿ~KO7fOm"[<0sǯdXKۋ1cCm~_ JT9}\1D78gyOQ|֤ϟ.Ku>?&!}kN+ y"ifPݲ۔8" z\^WgB2T~h5ut>Ct5֥یxH\|˹!y۶SۏG>iwkZE/zI~M~j[*믔{o\xܾE۽s¶r>u4lwsOo͖X_ȂEy`ΈD <.IS҈`Q^`|fQRF_Vup3~ߟ_٧۷믿jnJG~N۾g~D[}eSܾP3ɵ{w\<Ǖheqzme?r[J3(}GTWm|>-/KttjsȢ__`6p>FO yqt+7o[6V9'Z̨O<-ykO[p.?zh<-k~;D{bLOҁsҗ7tEWS?ήa8mmWrk,:!4ՆJo.eVUY -_?k8E9Cuf~մ-~ηoܖڮgkQ'nGg}NZgENmӾOh1Ohi߯"9KܾR-rM-E¹KVPN_pe#7ݾL6͉s[M$?/Ț ԕ־f)3t1眵JYt3bfQHKF?qn#0Dǵ1ֿ ΧM[k_꡴ff=2/A٧ `mHf]z^JZm?:mhwLf/@%ʷ6ܔd7 W:}fsU|~Jm_w8մW֫fkYtGirQKA \}_|S5훳SKs e՗7"k+[^m?|;D[ue%i _?gF#c!?3PwoZOY:`LK!u0.w4Yp>M_w^+ѣo/q+tܽPϟ~rj)(}g4K_pf2wcT|0YhGV>eV+ٯGz[K}XSWrmE5/sԌA L~rnziʣ׏> m/.K/ߒrٵj5[";w5׏55]̚ma VNnۮ{ V,} @V7a罙̰^{Q!jRrz-}tt.t(9b1Eщ|`F`ؿ〥,Rz}H>l`%OZ%Y \^wk͈cU9uZc`J<{o2K>O[=$͹.˿*c}*K}FDxNoͽ헝O_|e:ѻ[3 qҗpORX`v̨W0Ϩ $Qfzѯ;>URyg"qrmqKtkξpJyY\~ P#=|S!+mp9F0b>1`>{ \{=X!T=q}bWJ%[s}U~jj2'?m.tgG(>[3`yWK~ϥ0P#h.ghntH)`IigS?3nFO+HINv.oXZ޶5\iy˞Z*h޳Qn'. V0h\Ct {qFy$̈"" YnmWzj8P78J,M.?Zm1t̗%J=]^abU=@uv|R~KE?nz( ~ aty!:F@9og#|3GO::Kx<F˅C￿?-˗/?6|6}B5ۯk@0t0P0+B6T?ns2P|2O@ ~@OuG`42AWʣG3f۾f>>z_2﹅1F`F X,;`m\|JfZ?&~v1E)sr~?׉:`Joa`J遺,} 0P0Pzcx׉@z&'.au_~@ǶϺảv?E}, *o@o[_'_P\+p-{80n:?`v? /ynaO{*~ퟫգgHHwӹ~d\/o56pl!!RS?}SQX5 .BzIܧs%MnM~ƜwgD 9%-=XC 4ጺ҅rYX0'}…[>rjfVGyy}Z`ɅGzo4?c^ŕ\;y\n9飷߳?gЫO{ k }7agrrm1ʫ;yu^xF]Eb]m<a03hZw6@rfmmwL}{ہغf钘gŞV讍>Ϩ{b8^q # ܲهg"Ϝg:w~JWsڋ`@]2N.֮~t2:1prW1kL ^(3jz^Jz==6$n~OՉ~W'!wkzwՒ@f3\KA}[?t~OPw949j6}ڷ}#(/ߟEwD3<.z| <-sҏbN~Zo~.Zt}I~<˛.ݞ p`ܯןX=|>s:h><S@+ꦗ>rD>sҋ^K[dV%@,pѻhpwkcgWn󍩜Պnyɵ|+xvl/۬4O^??`|\ߵɉQ-P[INӧrn=_u{z{Y׻g{ `ry~e}<S}(p}+(=q^y~WKw#/^am0k"~~dHQ4.fK[xSG+~ퟫգ_Vn/~ )n:7~K\/o56q_\݀FxMl?"}SQX_״/pZr}L~}@[K_. H9c8sW}-f^µ+O_1Wsx.q\̹L\0Q7Wˮ]]۹.3Q50hdA9y߿]0ۮ_k_SX$<Ɯ4ons[޽x3C:K_I_ZkzQoߚV=;wү0fԭ*)_?S󔗫RnNfe5b9O[Տez'+g[cc_S];skycmo!ݞ;⸾O9y/NO.?ۏS)>|f~l\~K.EsT3}9GQJ^~7a~~ε:ٯgn?ǿ\r}w?rŚ܋`ΰ_U:Y3X;J@K_}|R; YV\@\wOkXs=K_ b/z@O|> /vkS99~.U s,3܉ǣf}me;,f5,+'<nKh"6vg]׻g;Dnxfm_qx,ҳ׀v(.r<ѧ=n?_̹gHHwӹh~d\/o56pl!][{zn)Sn_~Ψ+]/gȚs.\xs/g`if5Owe ,znqRpQoXn̰yoPV멁Mi߸گO~˓Y;z4,z"] vqϨ۪Q=PN_?Ptn5rr>y@_ܞϿT>lwM?ݻ*nlg1Ex|,b#Ϩ<[~^O]=]r<c' @]2Ϸ7s]Uۭ]ix'+gؾf^lF~m?WRG=VOo39sW'K/}mx.vkS99x<6GW-9Bbg8:Wa9Uz߾}ѻ_QJק}Hs:#?>i5pt}{[n7$=zwm_Q "`<Wr.b{Jvw)QԹIߖt q+p-1 wf~ho療AKG[(SGJtjv.go_R~D)}W[  Q'gRMM-o_*1\Ӿ@KtOY&Y䟟r ro1kpF]B~9KG,Bu4@Q>%~3"ۂ-jx8g~/Ϙ{<.@職sɹ5PsK@}~/}\\drS+˖1هbs?Z~X6R^yN_su5tT=ѿH8P3yUÜ ԍ/gv&ԷԻ}W8kϜm~;Sg=1gV}Z:*0`!@؋Ϲf_Zc!/~ݟܦ,Mٿ9[31p?F[l3Ϩ^:t4?-_5Vk<`{)MI[ W9{m&?wK8%m|x3[ : slb`YS1x%\@ߣw=K_no/vkS99j!mZHo<qtFr/uuA9 FJ~;K{붯}L yԹIߖt q+c_~:t久>u4tJVu齠@vsfs~Kly7"ѻnSyK:z~>͹/od;Ϫ%{A*ѻ5y6,@+-} a82a.I۷o߾}S믿׿ͨ Ψ{>sRk)NJ^޻:OV\Lν{}jxGe^YybFYƯj9[\$?GOzxѳ\#jƣFt3W5ǵ} ԥo=1 zGDy{1\7 {:7D6[/鼽`[ɘ j?hk,'3e.'g皛oq?FƧүIϴIUS4?Y֯t鿸,U_־5)m?OU]XhsUm'S3o_jL?!9vF>W-y6F_t9gY:5~jڷ>ωhdz=x҅ae>0@[>[3fKc_'?fROӛ*H3/jF`F)Wm;͌ҥ䢗+3Gj&=kj,-\]޽t)oٶmWmG>ӥO,sĿgtZ*Ct/i^tUۼ=ƌOLן۷KJ3 =׿-}  >]cnĀ@|>K?h(\3`1ߣwׯs~KƙWxFvM-[>ߘL|sKz&Xm?Gdk}me;,gH ^/;s^m6QYƟ58f}W}ĿQk)'}i_ -;^,9q6k4!%lmo9i"s_?>-߭k,6%~~u*VhO+Igg*ſCYFoR5tYoo~"rxKٵQ'^k(ߗ+a3e.'g皛%xҗ9AGpKG[XS o\ ]n sS?Ěat"_-jl!1R?EsUm'Ӧ'Ŀ=o':?mk8oӟ[ u;-F@kڽܠX5[ؿ\ϖnAn?PwKOM6lkn?[9K_vҗXNۣ\KմEFĆk8>cq-ZNz?yi=z8^}z1oI9z88A0˂͖-{-^W̨`Flvyiy[;DBNˎ\KymYFhYQ"2{'g7@C7GnӤaO}F]Pl'v5DwmToO;z潐YI}tV^ϷM%Gўw6۷O6]9)uO{ݧy=n;8.Wѥ^u/W>X YW;-581Õw!;Po/od `+zll_Q3kD:hnh#'Pٌ~Xۥ >{nf:?s涃w9Û9j6}ڷ}#(/ߟEwD3<z}nj?Oߊ*gK'=3նRWm)Omӯz|W?]?i99Śα˟k-@ڏ@Z*]z.ّBp.M 1P7=Cq̲0r0u@ 2xF\:%}k^ӊ_Ge9czeMerҗ(oqr-?q{Er{F8`AK}mϗJh1kUlP?5R5tYoogă/?e.'g皛%aK~1R?EsUm'Ӧ'Ŀ=o':?mk8oӟ[ u,fdqE C#,}j=~Υo[M[-oDlF#n/?ׂۭ售gߣs!]ҵ="_[g̸?f~~#,ȵQdiLl2BG}([7~=uTF73.t崚k5nF/8r$DuY߾}ggO1P@%]>jK5<6N~pe-}:x ?TD"Z2,v[ty=]t2N{QKgm_ɩ:D7RG_>{yk&ݓ~Ktrxץޏ|_Jy7K_,} bi>(onŀ  X`A߾}[Nʿ믿~je-G9?e7/feZI_EBz#p=5BN)?WE]m q?>_\߮#r;c(Ke~mVK}gϮg2{mֶ/?e.'g皛%'W-} OZjTzQ\էl[?/o}i"/O$O!B]owX9ț!x*B L/kK ՗O>K0G^g,}=[)]'"?{q?\l=?ν{h{t֔Ό:RKo/yſC$%Kӏz}j}ʘ^YybFYƯj9[\$?GOzxѳ\#?'Fn P~|>׿UEx>-3pm/PFom}m~F(?W%̥\ss`V,} OZjTzQJ6jU?/o}i"/O$O!B]oN,=ҥ,.J4n%pҗqKBΒe5]?ҷ]ҭ-z.u.6Ŀ_#K_krJ׳hmwh>=[qd䒦õcQRJ:%wsFk,M?"۲s-6r"?nӧkʼ7sQq/]+մ_;q5zđm''bͲhӿ6XnX:\u`_^V3fi_/\c WVj_IOo Ma~ [jzfpRۮ~+.o}ӷKY҉?*{L+9TUF_J6og/KQ\S5`p_|>@X)o%\җp#C< f낖?ǿ>~{e? `l  xFӰ}rS=agNYe7/feZI_EBz#p=5BN\?5zax=|>fst~F9\,b[;e*[/y[ciJ7{?s?o9?kKfK` l#QaK~rRcҋ>e)O~y'M[O/{26'Ot~/Wq޶ߧ?zKw5dGWqqҗqKBΒe5]?ҷ]ҭ- #qIDATږ7"6Ŀ_#k[n1t=F{ǹpӳGF.i:O\{y_J#}>WLן>_(Yb,} %K5JlnĀy  X`AGi.hK̉1s&Ō_K9KrHHo=gF=N|U/Xׯ?}>sʹϿ\#r;c(Ke~mVK}gϮm^3/[-} ,s>?>8t<,㗥/UKJ/JV`QF4?孯6mU>Q$ſɐۜD?i\^#yR-yn.ݪ?|)"L/k%j=~Υo[M[-oDlF#n/?ׂۭ售g=z8^}z1oIJoͨ`)TvyiyW;DBNˎ\Ky˶,#pGisv?K^ Pe9K9ϸ.ҕjKָid6ړr1fYF,=ir%}%,} %܈aF 8,} 0P%;]іB*czeMerҗ(oqr-?q{Er{#c֭Xׯ?}>sʹt~F9\,b[;e*[/y[ciJ7{?;\eKfKi`m9}cN椴%?edOٶ~J__}Hߓ!9vF>P[QmKLe\e~"S9n5mѶ!?8΂oۭ售gߣsէgKVh{D_޶e7ju^/ 9-;rm./Y~E8e!>Jg[DmDcw:{!scE}tV^ϷM%Gўw6۷O6`|nirUݿwU[jqc++Sw/oB[90H@D/@_דug鶫J~[-sK?gK}%JhKi~ڦWm~}oklZU|_xv kGnK/Ff_VP3uA￟u06K_ <`AG>ўM23TƬʲϛ32~-/VQ"!8Z~sNj!'tU3NIs_lk>p[g|>^rnE߮#r;c(Ke~mVK}ggx/ݝ{-K_0\:Oϸ577K-} OfY'(Y~S䗷~rڴUyD'CmsDrU{m}stڨ9>FotVGK,//ZͷGϹmti午 /׈q}Zp"3҂{q?\l{m[Ѷ?fyͨ`)TvyiyW;DBNˎ\KymYFhYQ9n:lʼ7sQq/]+մ_;q>zđm''bͲhӿoĵW:\u1ꭄry9/}"mqc++=pUT0>`4J~C_䘷'0~>ͨ>K]V]o9?]̥TҙjWrjtэmz6W=G?C9>;Ǽp'_?ǿ>~{e? B Xndޥ)a=F 8,} 0P%;]іB*czeMerҗ(oqr-?q{Er"JwHs|ׯ?}>sʹt~F9\,b[;e*[/y[ciJ7{j23)/?e.'g皛%6wi_K_𓫖+^,?W)Oi~[_?9mڪ}Hߓ!9vF>Pg_P>qEoϩ[Դ I]eRcJgYo2tRN3.&崚k5nG/8rtDuY߾w[Dw)V7 Pe,߹>,r_D\c WܾYxS ]h#q鋀z2,v[tyeSt2N{QKgm_ɩ:D7RGV-oߣ/&ܿ/1/K_,} 72RenĀY.`-89>K_,(2u0S-43TƬʲϛ32~-/VQ"!8Z~sNj!'Doڲ{:bn"?tvoڂo%c/(5.-} ,s>?>8t<k[uV7D3ƛu䪥Jm4?孯(E_ ItU5B}|'Wo[Oܿ4}s W_>[S?'"6Ŀ_#K_krJ׳hmwh1ضvo:oFK)/ϵȵQditIx7^<ݿfV>\6P@y/srRq/]+մ_;q:zđm'Fn#/rx>yrU?{Zp}ſsRs 3\Ѿw_u}R ]h#̨>K]V)oO?]̥TҙjWrjtэmz6gty>[SsX#8V|xWv(a(gK/Ff_VbnĀҗpupK_ 3f`8!җp3uHK/K/K/8A||> -=3̨AlMX^z饗^z饗~, ,} b;xSП/Zh.``PfK::::'U_~=~O:JsUYɏRj+j)'~/[\$?GO>DQr{F8ӽ}iGi)jܶѽUM0ޏ|_ͤw(XQJ-ַ~^ӭEas:B\73wKk'}ts[ޣwҗLP*"fэ_W%0V]س%U9wٟgR69*M_bm˛s}nYL+.?mk8xs5sRGVgp*]Fc!W ^{4P@63qs#h͍ป6-VhszH?N\W]s=㰴DKC~5KsGI {K3o1z@zfX{6 j>%gg~s?µ卮8C?C(r.JwX\ܭQg5j`\UVK&o= \4qӿ6X>;췏Ehԣ@LmۑcҖkxlJ;דăI>3ѽO0Z4A_sVa^Mv_t՞g󞟛QO9ρ{"Vi~J6w%7>}>KnY?l_+9TUF_J6o,ǯ5{wm%?鳴񖎃זa[I^{?>K8V]`gK/jGr@h f뭑￟s`t\YM;p㲭OM# 0W/QHKik6Q%~Wkܙ3eBߡOGTg[l~>g}P-=?}HlЉ;K}tǘlj>{vrпjtfs}6VKA,>fzMiRLoUVs򷟮wK{w}瓣y|Zo5g..?\:rޥM{} V?wȹo{=;]kk뿦>DT{:7W7ʧpO~]@R9C,)\,:Zb,Kɪ?BKŭݶ?q">9Jק%nh瞣V:0*۟z|2k&n tsm-6GF/=zϥM58?zwU[g;y(m>:z=\wf8]nA;;Q}*=-kf+E c"J#R=@ı M3 39Q#;DJ%a/E/P1/iv|4ך%`ˣӌ3w/S?DӚbf]{G.6GCwOήt^ !#(t9߻R9і?ŧLyտ9][ܞ-_U^owuS&f*':~*˝й"n 1=5g½gp^5Vr_goFW%y?um4?{nq)p/ O9j*Jkؗ"bͣ?\:}K1Byl铟V7+M#z~[-q'Sڟz>cz0ovnqKw_>ρyt?\=GgP߾ѥ3DWxn}T~s%#wFXUꟚA stӪw&bu-  Гu*r@+?RpIENDB`tmate-1.8.10/www/index.html.in000066400000000000000000000052301242461015400161400ustar00rootroot00000000000000 tmux

tmux is a terminal multiplexer

What is a terminal multiplexer? It lets you switch easily between several programs in one terminal, detach them (they keep running in the background) and reattach them to a different terminal. And do a lot more. See the manual.

Download tmux %%VERSION%% or get the development version. tmux is hosted on SourceForge and needs libevent and ncurses .

For support contact the tmux-users@lists.sf.net mailing list or IRC channel #tmux on freenode.

There are some programs to use with tmux on GitHub and a book on tmux.

Screenshot Screenshot Screenshot
tmate-1.8.10/www/main.css000066400000000000000000000015721242461015400152010ustar00rootroot00000000000000body { font-family: Sans-Serif; font-size: 10pt; background-color: white; } #body-wrapper { overflow: auto; } #upper-left-title { font-size:xx-large; margin-top: 0; } #left-menu li { list-style: none; margin-top: 1em; } .menu-headings { border-top: 1px solid black; border-bottom: 1px solid black; font-weight: bold; padding: 0.5em; } #left-menu-container { padding-right: 0.5em; margin-top: 0.5em; margin-bottom: 0.5em; margin-right: 0.5em; border-right: 3px solid black; text-align: right; width: 12em; float: left; } #main-content-wrapper { margin-left: 2em; margin-right: 4em; } #main-content-wrapper li { list-style: disc; margin-left: 12em } #screenshots { text-align: center; margin: 1em; margin-left: 10em; } #screenshots img { text-align: center; margin: 0.5em; } tmate-1.8.10/xmalloc.c000066400000000000000000000050141242461015400145150ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2004 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include "tmux.h" char * xstrdup(const char *s) { char *ptr; size_t len; len = strlen(s) + 1; ptr = xmalloc(len); strlcpy(ptr, s, len); return (ptr); } void * xcalloc(size_t nmemb, size_t size) { void *ptr; if (size == 0 || nmemb == 0) fatalx("zero size"); if (SIZE_MAX / nmemb < size) fatalx("nmemb * size > SIZE_MAX"); if ((ptr = calloc(nmemb, size)) == NULL) fatal("xcalloc failed"); return (ptr); } void * xmalloc(size_t size) { void *ptr; if (size == 0) fatalx("zero size"); if ((ptr = malloc(size)) == NULL) fatal("xmalloc failed"); return (ptr); } void * xrealloc(void *oldptr, size_t nmemb, size_t size) { size_t newsize = nmemb * size; void *newptr; if (newsize == 0) fatalx("zero size"); if (SIZE_MAX / nmemb < size) fatalx("nmemb * size > SIZE_MAX"); if ((newptr = realloc(oldptr, newsize)) == NULL) fatal("xrealloc failed"); return (newptr); } int printflike2 xasprintf(char **ret, const char *fmt, ...) { va_list ap; int i; va_start(ap, fmt); i = xvasprintf(ret, fmt, ap); va_end(ap); return (i); } int xvasprintf(char **ret, const char *fmt, va_list ap) { int i; i = vasprintf(ret, fmt, ap); if (i < 0 || *ret == NULL) fatal("xvasprintf failed"); return (i); } int printflike3 xsnprintf(char *buf, size_t len, const char *fmt, ...) { va_list ap; int i; va_start(ap, fmt); i = xvsnprintf(buf, len, fmt, ap); va_end(ap); return (i); } int xvsnprintf(char *buf, size_t len, const char *fmt, va_list ap) { int i; if (len > INT_MAX) fatalx("len > INT_MAX"); i = vsnprintf(buf, len, fmt, ap); if (i < 0) fatal("vsnprintf failed"); return (i); } tmate-1.8.10/xterm-keys.c000066400000000000000000000132121242461015400151650ustar00rootroot00000000000000/* $Id$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * xterm-style function keys append one of the following values before the last * character: * * 2 Shift * 3 Alt * 4 Shift + Alt * 5 Ctrl * 6 Shift + Ctrl * 7 Alt + Ctrl * 8 Shift + Alt + Ctrl * * Rather than parsing them, just match against a table. * * There are three forms for F1-F4 (\\033O_P and \\033O1;_P and \\033[1;_P). * We accept any but always output the latter (it comes first in the table). */ int xterm_keys_match(const char *, const char *, size_t); int xterm_keys_modifiers(const char *, const char *, size_t); struct xterm_keys_entry { int key; const char *template; }; const struct xterm_keys_entry xterm_keys_table[] = { { KEYC_F1, "\033[1;_P" }, { KEYC_F1, "\033O1;_P" }, { KEYC_F1, "\033O_P" }, { KEYC_F2, "\033[1;_Q" }, { KEYC_F2, "\033O1;_Q" }, { KEYC_F2, "\033O_Q" }, { KEYC_F3, "\033[1;_R" }, { KEYC_F3, "\033O1;_R" }, { KEYC_F3, "\033O_R" }, { KEYC_F4, "\033[1;_S" }, { KEYC_F4, "\033O1;_S" }, { KEYC_F4, "\033O_S" }, { KEYC_F5, "\033[15;_~" }, { KEYC_F6, "\033[17;_~" }, { KEYC_F7, "\033[18;_~" }, { KEYC_F8, "\033[19;_~" }, { KEYC_F9, "\033[20;_~" }, { KEYC_F10, "\033[21;_~" }, { KEYC_F11, "\033[23;_~" }, { KEYC_F12, "\033[24;_~" }, { KEYC_F13, "\033[25;_~" }, { KEYC_F14, "\033[26;_~" }, { KEYC_F15, "\033[28;_~" }, { KEYC_F16, "\033[29;_~" }, { KEYC_F17, "\033[31;_~" }, { KEYC_F18, "\033[32;_~" }, { KEYC_F19, "\033[33;_~" }, { KEYC_F20, "\033[34;_~" }, { KEYC_UP, "\033[1;_A" }, { KEYC_DOWN, "\033[1;_B" }, { KEYC_RIGHT, "\033[1;_C" }, { KEYC_LEFT, "\033[1;_D" }, { KEYC_HOME, "\033[1;_H" }, { KEYC_END, "\033[1;_F" }, { KEYC_PPAGE, "\033[5;_~" }, { KEYC_NPAGE, "\033[6;_~" }, { KEYC_IC, "\033[2;_~" }, { KEYC_DC, "\033[3;_~" }, { '!', "\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). */ int xterm_keys_match(const char *template, const char *buf, size_t len) { size_t pos; if (len == 0) return (0); pos = 0; do { if (*template != '_' && buf[pos] != *template) return (-1); } while (pos++ != len && *++template != '\0'); if (*template != '\0') /* partial */ return (1); return (0); } /* Find modifiers based on template. */ int xterm_keys_modifiers(const char *template, const char *buf, size_t len) { size_t idx; int param, modifiers; idx = strcspn(template, "_"); if (idx >= len) return (0); param = buf[idx] - '1'; modifiers = 0; if (param & 1) modifiers |= KEYC_SHIFT; if (param & 2) modifiers |= KEYC_ESCAPE; if (param & 4) modifiers |= KEYC_CTRL; if (param & 8) modifiers |= KEYC_ESCAPE; return (modifiers); } /* * Lookup key from a buffer against the table. Returns 0 for found (and the * key), -1 for not found, 1 for partial match. */ int xterm_keys_find(const char *buf, size_t len, size_t *size, int *key) { const struct xterm_keys_entry *entry; u_int i; for (i = 0; i < nitems(xterm_keys_table); i++) { entry = &xterm_keys_table[i]; switch (xterm_keys_match(entry->template, buf, len)) { case 0: *size = strlen(entry->template); *key = entry->key; *key |= xterm_keys_modifiers(entry->template, buf, len); return (0); case 1: return (1); } } return (-1); } /* Lookup a key number from the table. */ char * xterm_keys_lookup(int key) { const struct xterm_keys_entry *entry; u_int i; int modifiers; char *out; modifiers = 1; if (key & KEYC_SHIFT) modifiers += 1; if (key & KEYC_ESCAPE) modifiers += 2; if (key & KEYC_CTRL) modifiers += 4; /* * If the key has no modifiers, return NULL and let it fall through to * the normal lookup. */ if (modifiers == 1) return (NULL); /* Otherwise, find the key in the table. */ key &= ~(KEYC_SHIFT|KEYC_ESCAPE|KEYC_CTRL); for (i = 0; i < nitems(xterm_keys_table); i++) { entry = &xterm_keys_table[i]; if (key == entry->key) break; } if (i == nitems(xterm_keys_table)) return (NULL); /* Copy the template and replace the modifier. */ out = xstrdup(entry->template); out[strcspn(out, "_")] = '0' + modifiers; return (out); }