pax_global_header00006660000000000000000000000064135640716420014522gustar00rootroot0000000000000052 comment=5e00bfa5e137e76c81888727712ced2b3fd99f5b tmate-2.4.0/000077500000000000000000000000001356407164200126375ustar00rootroot00000000000000tmate-2.4.0/.gitignore000066400000000000000000000003601356407164200146260ustar00rootroot00000000000000*.o *~ *.diff *.patch *.core core tags .deps/ compat/.dirstamp aclocal.m4 autom4te.cache/ config.log config.status etc/ tmux Makefile Makefile.in configure tmate cscope.* ctags *.log tmate.1.* downloads/ ext/ libssh-*/ msgpack-*/ releases/ tmate-2.4.0/.mailmap000066400000000000000000000036421356407164200142650ustar00rootroot00000000000000Bob Beck beck Claudio Jeker claudio Igor Sobrado sobrado Ingo Schwarze schwarze Jacek Masiulaniec jacekm Jason McIntyre jmc Joel Sing jsing Jonathan Gray jsg Kenneth R Westerback krw Marc Espie espie Matthew Dempsky matthew Matthias Kilian kili Matthieu Herrb matthieu Michael McConville mmcc Miod Vallat miod Nicholas Marriott Nicholas Marriott Nicholas Marriott nicm Nicholas Marriott no_author Okan Demirmen okan Philip Guenther guenther Pierre-Yves Ritschard pyr Ray Lai ray Ryan McBride mcbride Sebastian Benoit benno Stefan Sperling stsp Stuart Henderson sthen Ted Unangst tedu Theo de Raadt Theo Deraadt Theo de Raadt deraadt Thomas Adam Thomas Thomas Adam Thomas Adam Thomas Adam n6tadam Tim van der Molen tim Tobias Stoeckmann tobias Todd C Miller millert William Yodlowsky william tmate-2.4.0/.travis.yml000066400000000000000000000020011356407164200147410ustar00rootroot00000000000000language: c services: - docker matrix: include: - arch: amd64 env: PLATFORM=amd64 - arch: amd64 env: PLATFORM=i386 - arch: arm64 env: PLATFORM=arm32v6 - arch: arm64 env: PLATFORM=arm32v7 - arch: arm64 env: PLATFORM=arm64v8 script: - 'docker build . --tag local-$PLATFORM/tmate-build --build-arg PLATFORM=$PLATFORM' # On arch=arm64, some directories are not setup correctly, and 'ruby -S gem # install dpl' required by the release push scripts fails. - 'if [ "$TRAVIS_TAG" ]; then sudo chown -R $USER: /var/lib/gems /usr/local/bin; fi' - 'if [ "$TRAVIS_TAG" ]; then ./build_static_release.sh $TRAVIS_TAG $PLATFORM; fi' deploy: provider: releases api_key: secure: T2109tjjOsrVLEpJZK/uxmO0AuDGXYFdN4AAsNTmVwu/W5dcX57Kk2TCgqDuLfD21iGGXP0U/OYHM06IfBDODBWCA9P8ASHYsenS7wIiFnvCEMbfzoAFyBMrXN2kNdM2+ho3aqc0xE2lQKOKDLxpGm5FZrzujscXXzxQjWBU5Hk= skip_cleanup: true overwrite: true file_glob: true file: releases/*.tar.* on: repo: tmate-io/tmate branch: master tags: true tmate-2.4.0/CHANGES000066400000000000000000002462141356407164200136430ustar00rootroot00000000000000CHANGES FROM 2.0 to 2.1 18 October 2015 Incompatible Changes ==================== * Mouse-mode has been rewritten. There's now no longer options for: - mouse-resize-pane - mouse-select-pane - mouse-select-window - mode-mouse Instead there is just one option: 'mouse' which turns on mouse support entirely. * 'default-terminal' is now a session option. Furthermore, if this is set to 'screen-*' then emulate what screen does. If italics are wanted, this can be set to 'tmux' but this is still new and not necessarily supported on all platforms with older ncurses installs. * The c0-* options for rate-limiting have been removed. Instead, a backoff approach is used. Normal Changes ============== * New formats: - session_activity - window_linked - window_activity_format - session_alerts - session_last_attached - client_pid - pid * 'copy-selection', 'append-selection', 'start-named-buffer' now understand an '-x' flag to prevent it exiting copying mode. * 'select-pane' now understands '-P' to set window/pane background colours. * 'renumber-windows' now understands windows which are unlinked. * 'bind' now understands multiple key tables. Allows for key-chaining. * 'select-layout' understands '-o' to undo the last layout change. * The environment is updated when switching sessions as well as attaching. * 'select-pane' now understands '-M' for marking a pane. This marked pane can then be used with commands which understand src-pane specifiers automatically. * If a session/window target is prefixed with '=' then only an exact match is considered. * 'move-window' understands '-a'. * 'update-environment' understands '-E' when attach-session is used on an already attached client. * 'show-environment' understands '-s' to output Bourne-compatible commands. * New option: 'history-file' to save/restore command prompt history. * Copy mode is exited if the history is cleared whilst in copy-mode. * 'copy-mode' learned '-e' to exit copy-mode when scrolling to end. CHANGES FROM 1.9a to 2.0 6 March 2015 Incompatible Changes ==================== * The choose-list command has been removed. * 'terminal-overrides' is now a server option, not a session option. * 'message-limit' is now a server option, not a session option. * 'monitor-content' option has been removed. * 'pane_start_path' option has been removed. * The "info" mechanism which used to (for some commands) provide feedback has been removed, and like other commands, they now produce nothing on success. Normal Changes ============== * tmux can now write an entry to utmp if the library 'utempter' is present at compile time. * set-buffer learned append mode (-a), and a corresponding 'append-selection' command has been added to copy-mode. * choose-mode now has the following commands which can be bound: - start-of-list - end-of-list - top-line - bottom-line * choose-buffer now understands UTF-8. * Pane navigation has changed: - The old way of always using the top or left if the choice is ambiguous. - The new way of remembering the last used pane is annoying if the layout is balanced and the leftmost is obvious to the user (because clearly if we go right from the top-left in a tiled set of four we want to end up in top-right, even if we were last using the bottom-right). So instead, use a combination of both: if there is only one possible pane alongside the current pane, move to it, otherwise choose the most recently used of the choice. * 'set-buffer' can now be told to give names to buffers. * The 'new-session', 'new-window', 'split-window', and 'respawn-pane' commands now understand multiple arguments and handle quoting problems correctly. * 'capture-pane' understands '-S-' to mean the start of the pane, and '-E-' to mean the end of the pane. * Support for function keys beyond F12 has changed. The following explains: - F13-F24 are S-F1 to S-F12 - F25-F36 are C-F1 to C-F12 - F37-F48 are C-S-F1 to C-S-F12 - F49-F60 are M-F1 to M-F12 - F61-F63 are M-S-F1 to M-S-F3 Therefore, F13 becomes a binding of S-F1, etc. * Support using pane id as part of session or window specifier (so % means session-of-%1 or window-of-%1) and window id as part of session (so @1 means session-of-@1). * 'copy-pipe' command now understands formats via -F * 'if-shell' command now understands formats via -F * 'split-window' and 'join-window' understand -b to create the pane to the left or above the target pane. CHANGES FROM 1.9 to 1.9a 22 February 2014 NOTE: This is a bug-fix release to address some important bugs which just missed the 1.9 deadline, but were found afterwards. Normal Changes ============== * Fix crash due to uninitialized lastwp member of layout_cell * Fix -fg/-bg/-style with 256 colour terminals. CHANGES FROM 1.8 to 1.9, 20 February 2014 NOTE: This release has bumped the tmux protocol version. It is therefore advised that the prior tmux server is restarted when this version of tmux is installed, to avoid protocol mismatch errors for newer clients trying to talk to an older running tmux server. Incompatible Changes ==================== * 88 colour support has been removed. * 'default-path' has been removed. The new-window command accepts '-c' to cater for this. The previous value of "." can be replaced with: 'neww -c $PWD', the previous value of '' which meant current path of the pane can be specified as: 'neww -c "#{pane_current_path}"' Deprecated Changes ================== * The single format specifiers: #A -> #Z (where defined) have been deprecated and replaced with longer-named equivalents, as listed in the FORMATS section of the tmux manpage. * The various foo-{fg,bg,attr} commands have been deprecated and replaced with equivalent foo-style option instead. Currently this is still backwards-compatible, but will be removed over time. Normal Changes ============== * A new environment variable TMUX_TMPDIR is now honoured, allowing the socket directory to be set outside of TMPDIR (/tmp/ if not set). * If -s not given to swap-pane the current pane is assumed. * A #{pane_syncronized} format specifier has been added to be a conditional format if a pane is in a syncronised mode (c.f. syncronize-panes) * Tmux now runs under Cygwin natively. * Formats can now be nested within each other and expanded accordingly. * Added 'automatic-rename-format' option to allow the automatic rename mechanism to use something other than the default of #{pane_current_command}. * new-session learnt '-c' to specify the starting directory for that session and all subsequent windows therein. * The session name is now shown in the message printed to the terminal when a session is detached. * Lots more format specifiers have been added. * Server race conditions have been fixed; in particular commands are not run until after the configuration file is read completely. * Case insensitive searching in tmux's copy-mode is now possible. * attach-session and switch-client learnt the '-t' option to accept a window and/or a pane to use. * Copy-mode is only exited if no selection is in progress. * Paste key in copy-mode is now possible to enter text from the clipboard. * status-interval set to '0' now works as intended. * tmux now supports 256 colours running under fbterm. * Many bug fixes! CHANGES FROM 1.7 to 1.8, 26 March 2013 Incompatible Changes ==================== * layout redo/undo has been removed. Normal Changes ============== * Add halfpage up/down bindings to copy mode. * Session choosing fixed to work with unattached sessions. * New window options window-status-last-{attr,bg,fg} to denote the last window which was active. * Scrolling in copy-mode now scrolls the region without moving the mouse cursor. * run-shell learnt '-t' to specify the pane to use when displaying output. * Support for middle-click pasting. * choose-tree learns '-u' to start uncollapsed. * select-window learnt '-T' to toggle to the last window if it's already current. * New session option 'assume-paste-time' for pasting text versus key-binding actions. * choose-* commands now work outside of an attached client. * Aliases are now shown for list-commands command. * Status learns about formats. * Free-form options can be set with set-option if prepended with an '@' sign. * capture-pane learnt '-p' to send to stdout, and '-e' for capturing escape sequences, and '-a' to capture the alternate screen, and '-P' to dump pending output. * Many new formats added (client_session, client_last_session, etc.) * Control mode, which is a way for a client to send tmux commands. Currently more useful to users of iterm2. * resize-pane learnt '-x' and '-y' for absolute pane sizing. * Config file loading now reports errors from all files which are loaded via the 'source-file' command. * 'copy-pipe' mode command to copy selection and pipe the selection to a command. * Panes can now emit focus notifications for certain applications which use those. * run-shell and if-shell now accept formats. * resize-pane learnt '-Z' for zooming a pane temporarily. * new-session learnt '-A' to make it behave like attach-session. * set-option learnt '-o' to prevent setting an option which is already set. * capture-pane and show-options learns '-q' to silence errors. * New command 'wait-for' which blocks a client until woken up again. * Resizing panes will now reflow the text inside them. * Lots and lots of bug fixes, fixing memory-leaks, etc. * Various manpage improvements. CHANGES FROM 1.6 to 1.7, 13 October 2012 * tmux configuration files now support line-continuation with a "\" at the end of a line. * New option status-position to move the status line to the top or bottom of the screen. * Enforce history-limit option when clearing the screen. * Give each window a unique id, like panes but prefixed with @. * Add pane id to each pane in layout description (while still accepting the old form). * Provide defined ways to set the various default-path possibilities: ~ for home directory, . for server start directory, - for session start directory and empty for the pane's working directory (the default). All can also be used as part of a relative path (eg -/foo). Also provide -c flags to neww and splitw to override default-path setting. * Add -l flag to send-keys to send input literally (without translating key names). * Allow a single option to be specified to show-options to show just that option. * New command "move-pane" (like join-pane but allows the same window). * join-pane and move-pane commands learn "-b" option to place the pane to the left or above. * Support for bracketed-paste mode. * Allow send-keys command to accept hex values. * Add locking around "start-server" to avoid race-conditions. * break-pane learns -P/-F arguments for display formatting. * set-option learns "-q" to make it quiet, and not print out anything. * copy mode learns "wrap-search" option. * Add a simple form of output rate limiting by counting the number of certain C0 sequences (linefeeds, backspaces, carriage returns) and if it exceeds a threshold (current default 250/millisecond), start to redraw the pane every 100 milliseconds instead of making each change as it comes. Two configuration options - c0-change-trigger and c0-change-interval. * find-window learns new flags: "-C", "-N", "-T" to match against either or all of a window's content, name, or title. Defaults to all three options if none specified. * find-window automatically selects the appropriate pane for the found matches. * show-environment can now accept one option to show that environment value. * Exit mouse mode when end-of-screen reached when scrolling with the mouse wheel. * select-layout learns -u and -U for layout history stacks. * kill-window, detach-client, kill-session all learn "-a" option for killing all but the current thing specified. * move-window learns "-r" option to renumber window sequentially in a session. * New session option "renumber-windows" to automatically renumber windows in a session when a window is closed. (see "move-window -r"). * Only enter copy-mode on scroll up. * choose-* and list-* commands all use "-F" for format specifiers. * When spawning external commands, the value from the "default-shell" option is now used, rather than assuming /bin/sh. * New choose-tree command to render window/sessions as a tree for selection. * display-message learns new format options. * For linked-windows across sessions, all flags for that window are now cleared across sessions. * Lots and lots of bug fixes, fixing memory-leaks, etc. * Various manpage improvements. CHANGES FROM 1.5 TO 1.6, 23 January 2012 * Extend the mode-mouse option to add a third choice which means the mouse does not enter copy mode. * Add a -r flag to switch-client to toggle the client read-only flag. * Add pane-base-index option. * Support \ for line continuation in the configuration file. * Framework for more powerful formatting of command output and use it for list-{panes,windows,sessions}. This allows more descriptive replacements (such as #{session_name}) and conditionals. * Mark dead panes with some text saying they are dead. * Reject $SHELL if it is not a full path. * Add -S option to refresh-client to redraw status line. * Add an else clause for if-shell. * Try to resolve relative paths for loadb and saveb (first, using client working directory, if any, then default-path or session working directory). * Support for \e[3J to clear the history and send the corresponding terminfo code (E3) before locking. * When in copy mode, make repeat count indicate buffer to replace, if used. * Add screen*:XT to terminal-overrides for tmux-in-tmux. * Status-line message attributes added. * Move word-separators to be a session rather than window option. * Change the way the working directory for new processes is discovered. If default-path isn't empty, it is used. Otherwise, if a new window is created from the command-line, the working directory of the client is used. If not, platform specific code is used to retrieve the current working directory of the process in the active pane. If that fails, the directory where the session was created is used, instead. * Do not change the current pane if both mouse-select-{pane,window} are enabled. * Add \033[s and \033[u to save and restore cursor position. * Allow $HOME to be used as default-path. * Add CNL and CPL escape sequences. * Calculate last position correctly for UTF-8 wide characters. * Add an option allow-rename to disable the window rename escape sequence. * Attributes for each type of status-line alert (ie bell, content and activity) added. Therefore, remove the superfluous options window-status-alert-{attr,bg,fg}. * Add a -R flag to send-keys to reset the terminal. * Add strings to allow the aixterm bright colours to be used when configuring colours. * Drop the ability to have a list of keys in the prefix in favour of two separate options, prefix and prefix2. * Flag -2 added to send-prefix to send the secondary prefix key. * Show pane size in top right of display panes mode. * Some memory leaks plugged. * More command-prompt editing improvements. * Various manpage improvements. * More Vi mode improvements. CHANGES FROM 1.4 TO 1.5, 09 July 2011 * Support xterm mouse modes 1002 and 1003. * Change from a per-session stack of buffers to one global stack. This renders copy-buffer useless and makes buffer-limit now a server option. * Fix most-recently-used choice by avoiding reset the activity timer for unattached sessions every second. * Add a -P option to new-window and split-window to print the new window or pane index in target form (useful to pass it into other commands). * Handle a # at the end of a replacement string (such as status-left) correctly. * Support for UTF-8 mouse input (\033[1005h) which was added in xterm 262. If the new mouse-utf8 option is on, UTF-8 mouse input is enabled for all UTF-8 terminals. The option defaults to on if LANG etc are set in the same manner as the utf8 option. * Support for HP-UX. * Accept colours of the hex form #ffffff and translate to the nearest from the xterm(1) 256-colour set. * Clear the non-blocking IO flag (O_NONBLOCK) on the stdio file descriptors before closing them (fixes things like "tmux ls && cat"). * Use TMPDIR if set. * Fix next and previous session functions to actually work. * Support -x and -y for new-session to specify the initial size of the window if created detached with -d. * Make bind-key accept characters with the top-bit-set and print them as octal. * Set $TMUX without the session when background jobs are run. * Simplify the way jobs work and drop the persist type, so all jobs are fire-and-forget. * Accept tcgetattr/tcsetattr(3) failure, fixes problems with fatal() if the terminal disappears while locked. * Add a -P option to detach to HUP the client's parent process (usually causing it to exit as well). * Support passing through escape sequences to the underlying terminal by using DCS with a "tmux;" prefix. * Prevent tiled producing a corrupt layout when only one column is needed. * Give each pane created in a tmux server a unique id (starting from 0), put it in the TMUX_PANE environment variable and accept it as a target. * Allow a start and end line to be specified for capture-pane which may be negative to capture part of the history. * Add -a and -s options to lsp to list all panes in the server or session respectively. Likewise add -s to lsw. * Change -t on display-message to be target-pane for the #[A-Z] replacements and add -c as target-client. * The attach-session command now prefers the most recently used unattached session. * Add -s option to detach-client to detach all clients attached to a session. * Add -t to list-clients. * Change window with mouse wheel over status line if mouse-select-window is on. * When mode-mouse is on, automatically enter copy mode when the mouse is dragged or the mouse wheel is used. Also exit copy mode when the mouse wheel is scrolled off the bottom. * Provide #h character pair for short hostname (no domain). * Don't use strnvis(3) for the title as it breaks UTF-8. * Use the tsl and fsl terminfo(5) capabilities to update terminal title and automatically fill them in on terminals with the XT capability (which means their title setting is xterm-compatible). * Add a new option, mouse-resize-pane. When on, panes may be resized by dragging their borders. * Fix crash by resetting last pane on {break,swap}-pane across windows. * Add three new copy-mode commands - select-line, copy-line, copy-end-of-line. * Support setting the xterm clipboard when copying from copy mode using the xterm escape sequence for the purpose (if xterm is configured to allow it). * Support xterm(1) cursor colour change sequences through terminfo(5) Cc (set) and Cr (reset) extensions. * Support DECSCUSR sequence to set the cursor style with two new terminfo(5) extensions, Cs and Csr. * Make the command-prompt custom prompts recognize the status-left option character pairs. * Add a respawn-pane command. * Add a couple of extra xterm-style keys that gnome terminal provides. * Allow the initial context on prompts to be set with the new -I option to command-prompt. Include the current window and session name in the prompt when renaming and add a new key binding ($) for rename session. * Option bell-on-alert added to trigger the terminal bell when there is an alert. * Change the list-keys format so that it shows the keys using actual tmux commands which should be able to be directly copied into the config file. * Show full targets for lsp/lsw -a. * Make confirm-before prompt customizable with -p option like command-prompt and add the character pairs #W and #P to the default kill-{pane,window} prompts. * Avoid sending data to suspended/locked clients. * Small memory leaks in error paths plugged. * Vi mode improvements. CHANGES FROM 1.3 TO 1.4, 27 December 2010 * Window bell reporting fixed. * Show which pane is active in the list-panes output. * Backoff reworked. * Prevent the server from dying when switching into copy mode when already in a different mode. * Reset running jobs when the status line is enabled or disabled. * Simplify xterm modifier detection. * Avoid crashing in copy mode if the screen size is too small for the indicator. * Flags -n and -p added to switch-client. * Use UTF-8 line drawing characters on UTF-8 terminals, thus fixing some terminals (eg putty) which disable the vt100 ACS mode switching sequences in UTF-8 mode. On terminals without ACS, use ASCII equivalents. * New server option exit-unattached added. * New session option destroy-unattached added. * Fall back on normal session choice method if $TMUX exists but is invalid rather than rejecting. * Mark repeating keys with "(repeat)" in the key list. * When removing a pane, don't change the active pane unless the active pane is actually the one being removed. * New command last-pane added. * AIX fixes. * Flag -a added to unbind-key. * Add XAUTHORITY to update-environment. * More info regarding window and pane flags is now shown in list-*. * If VISUAL or EDITOR contains "vi" configure mode-keys and status-key to vi. * New window option monitor-silence and session option visual-silence added. * In the built-in layouts distribute the panes more evenly. * Set the default value of main-pane-width to 80 instead of 81. * Command-line flag -V added. * Instead of keeping a per-client prompt history make it global. * Fix rectangle copy to behave like emacs (the cursor is not part of the selection on the right edge but on the left it is). * Flag -l added to switch-client. * Retrieve environment variables from the global environment rather than getenv(3), thus allowing them to be updated during the configuration file. * New window options other-pane-{height,width} added. * More minor bugs fixed and manpage improvements. CHANGES FROM 1.2 TO 1.3, 18 July 2010 * New input parser. * Flags to move through panes -UDLR added to select-pane. * Commands up-pane, and down-pane removed, since equivalent behaviour is now available through the target flag (-t:+ and -t:-). * Jump-forward/backward in copy move (based on vi's F, and f commands). * Make paste-buffer accept a pane as a target. * Flag -a added to new-window to insert a window after an existing one, moving windows up if necessary. * Merge more mode into copy mode. * Run job commands explicitly in the global environment (which can be modified with setenv -g), rather than with the environment tmux started with. * Use the machine's hostname as the default title, instead of an empty string. * Prevent double free if the window option remain-on-exit is set. * Key string conversions rewritten. * Mark zombie windows as dead in the choose-window list. * Tiled layout added. * Signal handling reworked. * Reset SIGCHLD after fork to fix problems with some shells. * Select-prompt command removed. Therefore, bound ' to command-prompt -p index "select-window -t:%%" by default. * Catch SIGHUP and terminate if running as a client, thus avoiding clients from being left hanging around when, for instance, a SSH session is disconnected. * Solaris 9 fixes (such as adding compat {get,set}env(3) code). * Accept none instead of default for attributes. * Window options window-status-alert-{alert,bg,fg} added. * Flag -s added to the paste-buffer command to specify a custom separator. * Allow dragging to make a selection in copy mode if the mode-mouse option is set. * Support the mouse scroll wheel. * Make pipe-pane accept special character sequences (eg #I). * Fix problems with window sizing when starting tmux from .xinitrc. * Give tmux sockets (but not the containing folder) group permissions. * Extend the target flags (ie -t) to accept an offset (for example -t:+2), and make it wrap windows, and panes. * New command choose-buffer added. * New server option detach-on-destroy to set what happens to a client when the session it is attached to is destroyed. If on (default), the client is detached. Otherwise, the client is switched to the most recently active of the remaining sessions. * The commands load-buffer, and save-buffer now accept a dash (-) as the file to read from stdin, or write to stdout. * Custom layouts added. * Additional code reduction, bug fixes, and manpage enhancements. CHANGES FROM 1.1 TO 1.2, 10 March 2010 * Switch to libevent. * Emulate the ri (reverse index) capability, ergo allowing tmux to at least start on Sun consoles (TERM=sun, or sun-color). * Assign each entry a number, or lowercase letter in choose mode, and accept that as a shortcut key. * Permit top-bit-set characters to be entered in the status line. * Mark no-prefix keys with (no prefix), rather than [] in list-keys. * New command show-messages (alias showmsgs), and new session option message-limit, to show a per-client log of status lines messages up to the number defined by message-limit. * Do not interpret #() for display-message to avoid leaking commands. * New window options window-status-format, and window-status-current-format to control the format of each window in the status line. * Add a -p flag to display-message to print the output, instead of displaying it in the status line. * Emulate il1, dl1, ich1 to run with vt100 feature set. * New command capture-pane (alias capturep) to copy the entire pane contents to a paste buffer. * Avoid duplicating code by adding a -w flag to set-option, and show-options to set, and show window options. The commands set-window-option, and show-window-options are now aliases. * Panes can now be referred to as top, bottom, top-left, etc. * Add server-wide options, which can be set with set-option -s, and shown with show-options -s. * New server option quiet (like -q from the command line). * New server option escape-time to set the timeout used to detect if escapes are alone, part of a function key, or meta sequence. * New session options pane-active-border-bg, pane-active-border-fg, pane-border-bg, and pane-border-fg to set pane colours. * Make split-window accept a pane target, instead of a window. * New command join-pane (alias joinp) to split, and move an existing pane into the space (the opposite of break-pane), thus simplifying calls to split-window, followed by move-window. * Permit S- prefix on keys for shift when the terminal/terminfo supports them. * Window targets (-t flag) can now refer to the last window (!), next (+), and previous (-) window by number. * Mode keys to jump to the bottom/top of history, end of the next word, scroll up/down, and reverse search in copy mode. * New session option display-panes-active-colour to display the active pane in a different colour with the display-panes command. * Read the socket path from $TMUX if it's present, and -L, and -S are not given. * Vi-style mode keys B, W, and E to navigate between words in copy mode. * Start in more mode when configuration file errors are detected. * Rectangle copy support added. * If attach-session was specified with the -r flag, make the client read-only. * Per-window alternate-screen option. * Make load-buffer work with FIFOs. * New window option word-separators to set the characters considered as word separators in copy mode. * Permit keys in copy mode to be prefixed by a repeat count, entered with [1-9] in vi mode, or M-[1-9] in emacs mode. * utf8 improvements. * As usual, additional code reduction, bug fixes, and manpage enhancements. CHANGES FROM 1.0 TO 1.1, 05 November 2009 * New run-shell (alias run) command to run an external command without a window, capture it's stdout, and send it to output mode. * Ability to define multiple prefix keys. * Internal locking mechanism removed. Instead, detach each client and run the external command specified in the new session option lock-command (by default lock -np), thus allowing the system password to be used. * set-password command, and -U command line flag removed per the above change. * Add support for -c command line flag to execute a shell command. * New lock-client (alias lockc), and lock-session (alias locks) commands to lock a particular client, or all clients attached to a session. * Support C-n/C-p/C-v/M-v with emacs keys in choice mode. * Use : for goto line rather than g in vi mode. * Try to guess which client to use when no target client was specified. Finds the current session, and if only one client is present, use it. Otherwise, return the most recently used client. * Make C-Down/C-Up in copy mode scroll the screen down/up one line without moving the cursor. * Scroll mode superseded by copy mode. * New synchronize-panes window option to send all input to all other panes in the same window. * New lock-server session option to lock, when off (on by default), each session when it has been idle for the lock-after-time setting. When on, the entire server locks when all sessions have been idle for their individual lock-after-time setting. * Add support for grouped sessions which have independent name, options, current window, but where the linked windows are synchronized (ie creating, killing windows are mirrored between the sessions). A grouped session may be created by passing -t to new-session. * New mouse-select-pane session option to select the current pane with the mouse. * Queue, and run commands in the background for if-shell, status-left, status-right, and #() by starting each once every status-interval. Adds the capability to call some programs which would previously cause the server to hang (eg sleep/tmux). It also avoids running commands excessively (ie if used multiple times, it will be run only once). * When a window is zombified and automatic-rename is on, append [dead] to the name. * Split list-panes (alias lsp) off from list-windows. * New pipe-pane (alias pipep) to redirect a pane output to an external command. * Support for automatic-renames for Solaris. * Permit attributes to be turned off in #[] by prefixing with no (eg nobright). * Add H/M/L in vi mode, and M-R/M-r in emacs to move the cursor to the top, middle, and bottom of the screen. * -a option added to kill-pane to kill all except current pane. * The -d command line flag is now gone (can be replaced by terminal-overrides). Just use op/AX to detect default colours. * input/tty/utf8 improvements. * xterm-keys rewrite. * Additional code reduction, and bug fixes. CHANGES FROM 0.9 TO 1.0, 20 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. tmate-2.4.0/COPYING000066400000000000000000000021311356407164200136670ustar00rootroot00000000000000THIS IS FOR INFORMATION ONLY, CODE IS UNDER THE LICENCE AT THE TOP OF ITS FILE. The README, CHANGES, FAQ and TODO files are licensed under the ISC license. 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, typically: Copyright (c) Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. tmate-2.4.0/Dockerfile000066400000000000000000000021201356407164200146240ustar00rootroot00000000000000ARG PLATFORM=amd64 FROM ${PLATFORM}/alpine:3.10 WORKDIR /build RUN apk add --no-cache wget cmake make gcc g++ linux-headers zlib-dev openssl-dev \ automake autoconf libevent-dev ncurses-dev msgpack-c-dev libexecinfo-dev \ ncurses-static libexecinfo-static libevent-static msgpack-c ncurses-libs \ libevent libexecinfo openssl zlib RUN set -ex; \ mkdir -p /src/libssh/build; \ cd /src; \ wget -O libssh.tar.xz https://www.libssh.org/files/0.9/libssh-0.9.0.tar.xz; \ tar -xf libssh.tar.xz -C /src/libssh --strip-components=1; \ cd /src/libssh/build; \ cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr \ -DWITH_SFTP=OFF -DWITH_SERVER=OFF -DWITH_PCAP=OFF \ -DWITH_STATIC_LIB=ON -DWITH_GSSAPI=OFF ..; \ make -j $(nproc); \ make install COPY compat ./compat COPY *.c *.h autogen.sh Makefile.am configure.ac ./ RUN ./autogen.sh && ./configure --enable-static RUN make -j $(nproc) RUN objcopy --only-keep-debug tmate tmate.symbols && strip tmate RUN ./tmate -V tmate-2.4.0/FAQ000066400000000000000000000443201356407164200131740ustar00rootroot00000000000000tmux 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. - 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? Check the latest version of tmux from Git to see if the problem is still reproducible. Sometimes the length of time between releases means a lot of fixes can be sitting in Git and the problem might already be fixed. Please send bug reports by email to nicholas.marriott@gmail.com or tmux-users@googlegroups.com. 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 tmux-users@googlegroups.com. * 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. * How do I make ctrl and shift arrow keys work in emacs? The terminal-init-screen function in term/screen.el is called for new frames, but it doesn't configure any function keys. If the tmux xterm-keys option is on, it is enough to define the same keys as xterm. Add the following to init.el or .emacs to do this: (defadvice terminal-init-screen ;; The advice is named `tmux', and is run before `terminal-init-screen' runs. (before tmux activate) ;; Docstring. This describes the advice and is made available inside emacs; ;; for example when doing C-h f terminal-init-screen RET "Apply xterm keymap, allowing use of keys passed through tmux." ;; This is the elisp code that is run before `terminal-init-screen'. (if (getenv "TMUX") (let ((map (copy-keymap xterm-function-map))) (set-keymap-parent map (keymap-parent input-decode-map)) (set-keymap-parent input-decode-map map)))) And ensure .tmux.conf contains "set -g xterm-keys on". Alternatively, the screen.el file can be copied to the load path and customized. * 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' * I don't see italics! Or less and vim show italics and reverse the wrong way round! GNU screen does not support italics and the "screen" terminfo description uses the italics escape sequence incorrectly. As of tmux 2.1, if default-terminal is set to "screen" or matches "screen-*", tmux will behave like screen and italics will be disabled. To enable italics, create a new terminfo entry called "tmux" (some platforms may already have this, you can check with "infocmp tmux"): $ cat </dev/null" Or for inside and outside copy mode with the prefix key: bind C-y run -b "tmux save-buffer - | xclip -i" On OS X, reattach-to-usernamespace lets pbcopy/pbpaste work: https://github.com/ChrisJohnsen/tmux-MacOSX-pasteboard * Why do I see dots around a session when I attach to it? tmux limits the size of the window to the smallest attached session. If it didn't do this then it would be impossible to see the entire window. The dots mark the size of the window tmux can display. To avoid this, detach all other clients when attaching: $ tmux attach -d Or from inside tmux by detaching individual clients with C-b D or all using: C-b : attach -d tmate-2.4.0/Makefile.am000066400000000000000000000130601356407164200146730ustar00rootroot00000000000000# Makefile.am # Obvious program stuff. bin_PROGRAMS = tmate CLEANFILES = tmate.1.mdoc tmate.1.man # Distribution tarball options. EXTRA_DIST = \ CHANGES FAQ README TODO COPYING example_tmux.conf compat/*.[ch] \ array.h compat.h tmux.h osdep-*.c xmalloc.h mdoc2man.awk tmate.1 dist-hook: make clean grep "^#found_debug=" configure # Preprocessor flags. CPPFLAGS += @XOPEN_DEFINES@ -DTMUX_CONF="\"$(sysconfdir)/tmux.conf\"" # 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 if IS_LINUX CFLAGS += -rdynamic # for stack traces endif # Set flags for gcc. if IS_GCC CFLAGS += -std=gnu99 -O2 if IS_DEBUG CFLAGS += -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 += -Wundef -Wbad-function-cast -Winline CFLAGS += -Wno-pointer-sign -Wno-attributes CPPFLAGS += -DDEBUG endif if IS_COVERAGE CFLAGS += -g -O0 --coverage LDFLAGS += --coverage endif CPPFLAGS += -iquote. endif CFLAGS += -Wno-unused-parameter -Wno-unused-variable -Wno-null-pointer-arithmetic CFLAGS += -Wno-deprecated-declarations -Wno-format-nonliteral # Set flags for Solaris. if IS_SUNOS if IS_GCC CPPFLAGS += -D_XPG6 -D__EXTENSIONS__ -D_POSIX_PTHREAD_SEMANTICS else CPPFLAGS += -D_XPG4_2 -D__EXTENSIONS__ -D_POSIX_PTHREAD_SEMANTICS endif endif # Set flags for Sun CC. if IS_SUNCC CFLAGS += -erroff=E_EMPTY_DECLARATION endif # Set _LINUX_SOURCE_COMPAT for AIX for malloc(0). if IS_AIX DEFS += -D_LINUX_SOURCE_COMPAT=1 endif # List of sources. dist_tmate_SOURCES = \ alerts.c \ arguments.c \ attributes.c \ cfg.c \ client.c \ cmd-attach-session.c \ cmd-bind-key.c \ cmd-break-pane.c \ cmd-capture-pane.c \ cmd-choose-buffer.c \ cmd-choose-client.c \ cmd-choose-tree.c \ cmd-clear-history.c \ cmd-command-prompt.c \ cmd-confirm-before.c \ cmd-copy-mode.c \ cmd-detach-client.c \ cmd-display-message.c \ cmd-display-panes.c \ cmd-find.c \ cmd-find-window.c \ cmd-if-shell.c \ cmd-join-pane.c \ cmd-kill-pane.c \ cmd-kill-server.c \ cmd-kill-session.c \ cmd-kill-window.c \ cmd-list-buffers.c \ cmd-list-clients.c \ cmd-list-keys.c \ cmd-list-panes.c \ cmd-list-sessions.c \ cmd-list-windows.c \ cmd-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-set-buffer.c \ cmd-set-environment.c \ cmd-set-hook.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-string.c \ cmd-swap-pane.c \ cmd-swap-window.c \ cmd-switch-client.c \ cmd-unbind-key.c \ cmd-wait-for.c \ cmd.c \ colour.c \ control.c \ control-notify.c \ environ.c \ format.c \ grid-view.c \ grid.c \ hooks.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 \ proc.c \ resize.c \ screen-redraw.c \ screen-write.c \ screen.c \ server-client.c \ server-fn.c \ server.c \ session.c \ signal.c \ status.c \ style.c \ tmate-debug.c \ tmate-ssh-client.c \ tmate-encoder.c \ tmate-decoder.c \ tmate-env.c \ tmate-msg.c \ tmate-msgpack.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_FPARSELN nodist_tmate_SOURCES += compat/fparseln.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 if NO_CFMAKERAW nodist_tmate_SOURCES += compat/cfmakeraw.c endif if NO_OPENAT nodist_tmate_SOURCES += compat/openat.c endif if NO_REALLOCARRAY nodist_tmate_SOURCES += compat/reallocarray.c endif # Install tmate.1 in the right format. install-exec-hook: if test x@MANFORMAT@ = xmdoc; then \ sed -e "s|@SYSCONFDIR@|$(sysconfdir)|g" $(srcdir)/tmate.1 \ >$(srcdir)/tmate.1.mdoc; \ else \ sed -e "s|@SYSCONFDIR@|$(sysconfdir)|g" $(srcdir)/tmate.1| \ $(AWK) -f$(srcdir)/mdoc2man.awk >$(srcdir)/tmate.1.man; \ fi $(mkdir_p) $(DESTDIR)$(mandir)/man1 $(INSTALL_DATA) $(srcdir)/tmate.1.@MANFORMAT@ \ $(DESTDIR)$(mandir)/man1/tmate.1 tmate-2.4.0/README000066400000000000000000000040411356407164200135160ustar00rootroot00000000000000Welcome 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, OS X and Solaris. tmux depends on libevent 2.x. Download it from: http://libevent.org To build tmux from a release tarball, do: $ ./configure && make $ sudo make install To get and build the latest from version control: $ git clone https://github.com/tmux/tmux.git $ cd tmux $ sh autogen.sh $ ./configure && make For more information see http://git-scm.com. Patches should be sent by email to the mailing list at tmux-users@googlegroups.com. 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 an example configuration in example_tmux.conf. A vim(1) syntax file is available at: https://github.com/keith/tmux.vim https://raw.githubusercontent.com/keith/tmux.vim/master/syntax/tmux.vim And a bash(1) completion file at: https://github.com/przepompownia/tmux-bash-completion For debugging, running tmux with -v or -vv will generate server and client log files in the current directory. tmux mailing lists are available. For general discussion and bug reports: https://groups.google.com/forum/#!forum/tmux-users And for Git commit emails: https://groups.google.com/forum/#!forum/tmux-git Bug reports, feature suggestions and especially code contributions are most welcome. Please send by email to: tmux-users@googlegroups.com This file and the CHANGES, FAQ, SYNCING and TODO files are licensed under the ISC license. All other files have a license and copyright notice at their start. -- Nicholas Marriott tmate-2.4.0/README-tmux000066400000000000000000000037721356407164200145230ustar00rootroot00000000000000Welcome 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, OS X and Solaris. tmux depends on libevent 2.x. 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 https://github.com/tmux/tmux.git $ cd tmux $ sh autogen.sh $ ./configure && make For more information see http://git-scm.com. Patches should be sent by email to the mailing list at tmux-users@googlegroups.com. 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. For general discussion and bug reports: https://groups.google.com/forum/#!forum/tmux-users And for Git commit emails: https://groups.google.com/forum/#!forum/tmux-git Bug reports, feature suggestions and especially code contributions are most welcome. Please send by email to: tmux-users@googlegroups.com This file and the CHANGES, FAQ, SYNCING 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 tmate-2.4.0/README.md000066400000000000000000000002721356407164200141170ustar00rootroot00000000000000tmate ===== What is it? ----------- Tmate is a fork of tmux. It provides an instant pairing solution. License ------- tmate is built on top of tmux. tmux and tmate are BSD-licensed. tmate-2.4.0/SYNCING000066400000000000000000000133361356407164200137020ustar00rootroot00000000000000Preamble ======== Tmux portable relies on repositories "tmux" and "tmux-openbsd". Here's a description of them: * "tmux" 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). 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 and tmux-openbsd cloned, as in: % cd /some/where/useful % git clone https://github.com/tmux/tmux.git % git clone https://github.com/ThomasAdam/tmux-openbsd.git 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" 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" repository, as shown by the following command: % cd /path/to/tmux % 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" 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", we have to ensure the "master" branch from "tmux-openbsd" is up-to-date first, and then reference that update in "tmux", as in: % cd /path/to/tmux-openbsd % git checkout master % git pull Then back in "tmux": % cd /path/to/tmux % git fetch obsd-tmux Creating the necessary branches =============================== Now that "tmux" 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" repository: % git checkout -b obsd-master obsd-tmux/master Adding in the fake history points ================================= To tie both the "master" branch from "tmux" and the "obsd-master" branch from "tmux-openbsd" together, the fake history points added to the "tmux" repository need to be added. To do this, we must add an additional refspec line, as in: % cd /path/to/tmux % 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": % 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 Keeping an eye on libutil in OpenBSD ==================================== A lot of the compat/ code in tmux comes from libutil, especially imsg. Sometimes the API can change, etc., which might cause interesting problems trying to run the portable version of tmux. It's worth checking periodically for any changes to libutil in OpenBSD and syncing those files to compat/ as and when appropriate. 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 2.X Where "2.X" is the next version. Push the tag out with: % git push 2.X 4. Build the tarball with 'make dist'. 5. Check the tarball. If it's good, go here to select the tag just pushed: https://github.com/tmux/tmux/tags Click the "Add release notes", upload the tarball and add a link in the description field to the CHANGES file. 7. Clone the tmux.github.io repository, and change the RELEASE version in the Makefile. Commit it, and run 'make' to replace %%VERSION%%. Push the result out. 8. Bump version in tmu/tmux.git configure.ac and uncomment "found_debug=yes" to create a debug build by default. tmate-2.4.0/TODO000066400000000000000000000136341356407164200133360ustar00rootroot00000000000000- command bits and pieces: * allow multiple targets: fnmatch for -t/-c, for example detach all clients with -t* * add -c for new-session like new-window * ' and " should be parsed the same (eg "\e" vs '\e') in config and command prompt * last-pane across sessions * list-keys should quote output so that bindings can just be used in config file as-is - make command sequences more usable * don't require space after ; * options for error handling: && and ||? - options bits and pieces: * set-remain-on-exit is a complete hack * way to set socket path from config file - format improvements: * option to quote format (#{q:session_name}) * formats need conditions for >0 (for #P) * some way to pad # stuff with spaces * formats to show if a window is linked into multiple sessions, into multiple attached sessions, and is the active window in multiple attached sessions? - choose mode improvements: * choose-pane command (augment choose-tree to do this?) * choose-mode and copy-mode are very similar, make choose-mode a subset? * flag to choose-* for sort order * choose mode would be better per client than per window? * two choices (first one then second, for swap-pane and join-pane) * choose modes should ditch the key bindings and just have fixed keys, and be more customized to their purpose (d to delete a buffer for choose-buffer, a preview of buffer contents, etc) - improve monitor-*: * straighten out rules for multiple clients * think about what happens across sessions * monitor changes within a region * perhaps monitor /all/ panes in the window not just one - improve mouse support: * bind commands to mouse in different areas? * commands executed when clicking on a pattern (URL) - hooks! - warts on current naming: * display-time but message-fg/bg/attr * list-* vs show-* * split-window -> split-pane? - better UTF-8 support: * message display * prompt input * searching in copy mode - copy/paste improvements: * incremental searching * paste w/o trailing whitespace * command to toggle selection not to move it in copy-mode * regex searching * copy-pipe should have -x as well * copy mode key bindings should just be a standard key table, using something like "copy-mode start-selection"; it could use command-prompt for search, goto, etc: bind -Temacs command-prompt -p'Search Up: ' 'copy-mode search-up %%' it'd need a separate lookup, because modes are per-pane, perhaps a table() cb to give the table name ("vi" or "emacs"). anything in the table fires the command, anything not in the table is injected as a key * searching in copy mode should unwrap lines, so if you seach for "foobar" then it should be found even if it is now "foo\nbar" (if the WRAP flag is set on the line) * {} to go to next/previous blank line in copy mode - layout stuff * way to tag a layout as a number/name * maybe keep last layout + size around and if size reverts just put it back * 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 * way to set hints/limits about pane size for resizing * panning over window (window larger than visible) * a mode where one application can cross two panes (ie x|y, width = COLUMNS/2 but height = ROWS * 2) * general key to space cells out evenly (horiz or vert) within their parent cell (could replace even-vert/even-horiz layouts) * separate active panes for different clients - terminfo bits * use a better termcap internally instead of screen, perhaps xterm * use screen-256color when started on 256 colour terminal? - code cleanup * 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 * the way pane, window, session destroy is handled is too complicated and the distinction between session.c, window.c and server-fn.c functions is not clear. could we just have kill_pane(), kill_window(), unlink_window(), kill_session() that fix up all data structures (flagging sessions as dead) and return a value to say whether clients need to be checked for dead sessions? sort of like session_detach now but more so. or some other scheme to make it simpler and clearer? also would be nice to remove/rename server-fn.c * more readable way to work out the various things commands need to know about the client, notably: - is this the config file? (cmdq->c == NULL) - is this a command client? (cmdq->c != NULL && cmdq->c->session == NULL) - is this a control client? - can i do stdin or stdout to this client? or even guarantee that cmdq->c != NULL and provide a better way to tell when in the config file - then we use cmdq->c if we need a client w/o a session else cmd_current_client * optimize pane redraws, 20120318184853.GK10965@yelena.nicm.ath.cx - miscellaneous * way to keep a job running just read its last line of output for #() * link panes into multiple windows * live update: server started with -U connects to server, requests sessions and windows, receives file descriptors * there are inconsistencies in what we get from old shell and what comes from config for new sessions and windows. likewise, panes and jobs and run-shell and lock command all start with slightly different environments * multiline status line? separate command prompt and status line? * customizable command aliases * automatic pane logging * BCE? We are halfway there (output side is done for pane backgrounds), just need to change how screen/grid handles erase tmate-2.4.0/alerts.c000066400000000000000000000154651356407164200143100ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2015 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" int alerts_fired; void alerts_timer(int, short, void *); int alerts_enabled(struct window *, int); void alerts_callback(int, short, void *); void alerts_reset(struct window *); void alerts_run_hook(struct session *, struct winlink *, int); int alerts_check_all(struct session *, struct winlink *); int alerts_check_bell(struct session *, struct winlink *); int alerts_check_activity(struct session *, struct winlink *); int alerts_check_silence(struct session *, struct winlink *); void alerts_ring_bell(struct session *); void alerts_timer(__unused int fd, __unused short events, void *arg) { struct window *w = arg; log_debug("@%u alerts timer expired", w->id); alerts_reset(w); alerts_queue(w, WINDOW_SILENCE); } void alerts_callback(__unused int fd, __unused short events, __unused void *arg) { struct window *w; struct session *s; struct winlink *wl; int flags, alerts; RB_FOREACH(w, windows, &windows) { RB_FOREACH(s, sessions, &sessions) { RB_FOREACH(wl, winlinks, &s->windows) { if (wl->window != w) continue; flags = w->flags; alerts = alerts_check_all(s, wl); log_debug("%s:%d @%u alerts check, alerts %#x, " "flags %#x", s->name, wl->idx, w->id, alerts, flags); } } } alerts_fired = 0; } void alerts_run_hook(struct session *s, struct winlink *wl, int flags) { struct cmd_find_state fs; if (cmd_find_from_winlink(&fs, s, wl) != 0) return; if (flags & WINDOW_BELL) hooks_run(s->hooks, NULL, &fs, "alert-bell"); if (flags & WINDOW_SILENCE) hooks_run(s->hooks, NULL, &fs, "alert-silence"); if (flags & WINDOW_ACTIVITY) hooks_run(s->hooks, NULL, &fs, "alert-activity"); } int alerts_check_all(struct session *s, struct winlink *wl) { int alerts; alerts = alerts_check_bell(s, wl); alerts |= alerts_check_activity(s, wl); alerts |= alerts_check_silence(s, wl); if (alerts != 0) { alerts_run_hook(s, wl, alerts); server_status_session(s); } return (alerts); } void alerts_check_session(struct session *s) { struct winlink *wl; RB_FOREACH(wl, winlinks, &s->windows) alerts_check_all(s, wl); } int alerts_enabled(struct window *w, int flags) { if (flags & WINDOW_BELL) return (1); if (flags & WINDOW_ACTIVITY) { if (options_get_number(w->options, "monitor-activity")) return (1); } if (flags & WINDOW_SILENCE) { if (options_get_number(w->options, "monitor-silence") != 0) return (1); } return (0); } void alerts_reset_all(void) { struct window *w; RB_FOREACH(w, windows, &windows) alerts_reset(w); } void alerts_reset(struct window *w) { struct timeval tv; w->flags &= ~WINDOW_SILENCE; event_del(&w->alerts_timer); timerclear(&tv); tv.tv_sec = options_get_number(w->options, "monitor-silence"); log_debug("@%u alerts timer reset %u", w->id, (u_int)tv.tv_sec); if (tv.tv_sec != 0) event_add(&w->alerts_timer, &tv); } void alerts_queue(struct window *w, int flags) { if (w->flags & WINDOW_ACTIVITY) alerts_reset(w); if (!event_initialized(&w->alerts_timer)) evtimer_set(&w->alerts_timer, alerts_timer, w); if (!alerts_fired) { w->flags |= flags; log_debug("@%u alerts flags added %#x", w->id, flags); if (alerts_enabled(w, flags)) { log_debug("alerts check queued (by @%u)", w->id); event_once(-1, EV_TIMEOUT, alerts_callback, NULL, NULL); alerts_fired = 1; } } } int alerts_check_bell(struct session *s, struct winlink *wl) { struct client *c; struct window *w = wl->window; int action, visual; if (!(w->flags & WINDOW_BELL)) return (0); if (s->curw != wl) { wl->flags |= WINLINK_BELL; w->flags &= ~WINDOW_BELL; } if (s->curw->window == w) w->flags &= ~WINDOW_BELL; action = options_get_number(s->options, "bell-action"); if (action == BELL_NONE) return (0); visual = options_get_number(s->options, "visual-bell"); TAILQ_FOREACH(c, &clients, entry) { if (c->session != s || c->flags & CLIENT_CONTROL) continue; if (!visual) { if ((action == BELL_CURRENT && c->session->curw->window == w) || (action == BELL_OTHER && c->session->curw->window != w) || action == BELL_ANY) tty_putcode(&c->tty, TTYC_BEL); continue; } if (action == BELL_CURRENT && c->session->curw->window == w) status_message_set(c, "Bell in current window"); else if (action == BELL_ANY || (action == BELL_OTHER && c->session->curw->window != w)) status_message_set(c, "Bell in window %d", wl->idx); } return (WINDOW_BELL); } int alerts_check_activity(struct session *s, struct winlink *wl) { struct client *c; struct window *w = wl->window; if (s->curw->window == w) w->flags &= ~WINDOW_ACTIVITY; if (!(w->flags & WINDOW_ACTIVITY) || wl->flags & WINLINK_ACTIVITY) return (0); if (s->curw == wl) return (0); if (!options_get_number(w->options, "monitor-activity")) return (0); if (options_get_number(s->options, "bell-on-alert")) alerts_ring_bell(s); wl->flags |= WINLINK_ACTIVITY; if (options_get_number(s->options, "visual-activity")) { TAILQ_FOREACH(c, &clients, entry) { if (c->session != s) continue; status_message_set(c, "Activity in window %d", wl->idx); } } return (WINDOW_ACTIVITY); } int alerts_check_silence(struct session *s, struct winlink *wl) { struct client *c; struct window *w = wl->window; if (s->curw->window == w) w->flags &= ~WINDOW_SILENCE; if (!(w->flags & WINDOW_SILENCE) || wl->flags & WINLINK_SILENCE) return (0); if (s->curw == wl) return (0); if (options_get_number(w->options, "monitor-silence") == 0) return (0); if (options_get_number(s->options, "bell-on-alert")) alerts_ring_bell(s); wl->flags |= WINLINK_SILENCE; if (options_get_number(s->options, "visual-silence")) { TAILQ_FOREACH(c, &clients, entry) { if (c->session != s) continue; status_message_set(c, "Silence in window %d", wl->idx); } } return (WINDOW_SILENCE); } void alerts_ring_bell(struct session *s) { struct client *c; TAILQ_FOREACH(c, &clients, entry) { if (c->session == s && !(c->flags & CLIENT_CONTROL)) tty_putcode(&c->tty, TTYC_BEL); } } tmate-2.4.0/arguments.c000066400000000000000000000126001356407164200150070ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2010 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" /* * Manipulate command arguments. */ struct args_entry { u_char flag; char *value; RB_ENTRY(args_entry) entry; }; struct args_entry *args_find(struct args *, u_char); RB_GENERATE(args_tree, args_entry, entry, args_cmp); /* Arguments tree comparison function. */ int args_cmp(struct args_entry *a1, struct args_entry *a2) { return (a1->flag - a2->flag); } /* 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); 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); } /* Find a flag in the arguments tree. */ struct args_entry * args_find(struct args *args, u_char ch) { struct args_entry entry; entry.flag = ch; return (RB_FIND(args_tree, &args->tree, &entry)); } /* Parse an argv and argc into a new argument set. */ struct args * args_parse(const char *template, int argc, char **argv) { struct args *args; int opt; args = xcalloc(1, sizeof *args); optreset = 1; optind = 1; while ((opt = getopt(argc, argv, template)) != -1) { if (opt < 0) continue; if (opt == '?' || strchr(template, opt) == NULL) { args_free(args); return (NULL); } args_set(args, opt, optarg); } argc -= optind; argv += optind; args->argc = argc; args->argv = cmd_copy_argv(argc, argv); return (args); } /* Free an arguments set. */ void args_free(struct args *args) { struct args_entry *entry; struct args_entry *entry1; cmd_free_argv(args->argc, args->argv); RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) { RB_REMOVE(args_tree, &args->tree, entry); free(entry->value); free(entry); } free(args); } /* Add to string. */ static void printflike(3, 4) args_print_add(char **buf, size_t *len, const char *fmt, ...) { va_list ap; char *s; size_t slen; va_start(ap, fmt); slen = xvasprintf(&s, fmt, ap); va_end(ap); *len += slen; *buf = xrealloc(*buf, *len); strlcat(*buf, s, *len); free(s); } /* Print a set of arguments. */ char * args_print(struct args *args) { size_t len; char *buf; int i; struct args_entry *entry; len = 1; buf = xcalloc(1, len); /* Process the flags first. */ RB_FOREACH(entry, args_tree, &args->tree) { if (entry->value != NULL) continue; if (*buf == '\0') args_print_add(&buf, &len, "-"); args_print_add(&buf, &len, "%c", entry->flag); } /* Then the flags with arguments. */ RB_FOREACH(entry, args_tree, &args->tree) { if (entry->value == NULL) continue; if (*buf != '\0') args_print_add(&buf, &len, " -%c ", entry->flag); else args_print_add(&buf, &len, "-%c ", entry->flag); if (strchr(entry->value, ' ') != NULL) args_print_add(&buf, &len, "\"%s\"", entry->value); else args_print_add(&buf, &len, "%s", entry->value); } /* And finally the argument vector. */ for (i = 0; i < args->argc; i++) { if (*buf != '\0') args_print_add(&buf, &len, " "); if (strchr(args->argv[i], ' ') != NULL) args_print_add(&buf, &len, "\"%s\"", args->argv[i]); else args_print_add(&buf, &len, "%s", args->argv[i]); } return (buf); } /* Return if an argument is present. */ int args_has(struct args *args, u_char ch) { return (args_find(args, ch) == NULL ? 0 : 1); } /* Set argument value in the arguments tree. */ void args_set(struct args *args, u_char ch, const char *value) { struct args_entry *entry; /* Replace existing argument. */ if ((entry = args_find(args, ch)) != NULL) { free(entry->value); entry->value = NULL; } else { entry = xcalloc(1, sizeof *entry); entry->flag = ch; RB_INSERT(args_tree, &args->tree, entry); } if (value != NULL) entry->value = xstrdup(value); } /* Get argument value. Will be NULL if it isn't present. */ const char * args_get(struct args *args, u_char ch) { struct args_entry *entry; if ((entry = args_find(args, ch)) == NULL) return (NULL); return (entry->value); } /* Convert an argument value to a number. */ long long args_strtonum(struct args *args, u_char ch, long long minval, long long maxval, char **cause) { const char *errstr; long long ll; struct args_entry *entry; if ((entry = args_find(args, ch)) == NULL) { *cause = xstrdup("missing"); return (0); } ll = strtonum(entry->value, minval, maxval, &errstr); if (errstr != NULL) { *cause = xstrdup(errstr); return (0); } *cause = NULL; return (ll); } tmate-2.4.0/array.h000066400000000000000000000066301356407164200141330ustar00rootroot00000000000000/* $OpenBSD$ */ /* * 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, (a)->space); \ } \ while ((a)->space <= ((a)->num + (n)) * ARRAY_ITEMSIZE(a)) { \ (a)->list = xreallocarray((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-2.4.0/attributes.c000066400000000000000000000050631356407164200151750ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Joshua Elsasser * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" const char * attributes_tostring(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-2.4.0/autogen.sh000077500000000000000000000005711356407164200146430ustar00rootroot00000000000000#!/bin/sh 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-2.4.0/build_static_release.sh000077500000000000000000000016641356407164200173530ustar00rootroot00000000000000#!/bin/bash set -eux # This is invoked by .travis.yml VERSION=$1 PLATFORM=$2 SRC_VERSION=`cat configure.ac | grep AC_INIT | sed -E 's/^AC_INIT\(tmate, (.+)\)$/\1/'` if [ $SRC_VERSION != $VERSION ]; then echo "Version mismatch: $SRC_VERSION != $VERSION" exit 1 fi RELEASE_NAME=tmate-$VERSION-static-linux-$PLATFORM echo "Building $RELEASE_NAME" docker build . --tag local-$PLATFORM/tmate-build --build-arg PLATFORM=$PLATFORM mkdir -p releases cd releases rm -rf $RELEASE_NAME mkdir -p $RELEASE_NAME docker run --rm local-$PLATFORM/tmate-build cat tmate > $RELEASE_NAME/tmate chmod +x $RELEASE_NAME/tmate tar -cf - $RELEASE_NAME | xz > tmate-$VERSION-static-linux-$PLATFORM.tar.xz rm -rf $RELEASE_NAME-symbols mkdir -p $RELEASE_NAME-symbols docker run --rm local-$PLATFORM/tmate-build cat tmate.symbols > $RELEASE_NAME-symbols/tmate.symbols tar -cf - $RELEASE_NAME-symbols | xz > dbg-symbols-tmate-$VERSION-static-linux-$PLATFORM.tar.xz tmate-2.4.0/cfg.c000066400000000000000000000121351356407164200135440ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include "tmux.h" #include "tmate.h" char *cfg_file; #ifdef TMATE char *tmate_cfg_file; #endif struct cmd_q *cfg_cmd_q; int cfg_finished; int cfg_references; char **cfg_causes; u_int cfg_ncauses; struct client *cfg_client; void cfg_default_done(struct cmd_q *); void set_cfg_file(const char *path) { free(cfg_file); cfg_file = xstrdup(path); } void start_cfg(void) { char *cause = NULL; const char *home; cfg_cmd_q = cmdq_new(NULL); cfg_cmd_q->emptyfn = cfg_default_done; cfg_finished = 0; cfg_references = 1; cfg_client = TAILQ_FIRST(&clients); if (cfg_client != NULL) cfg_client->references++; if (access(TMUX_CONF, R_OK) == 0) { if (load_cfg(TMUX_CONF, cfg_cmd_q, &cause) == -1) cfg_add_cause("%s: %s", TMUX_CONF, cause); } else if (errno != ENOENT) cfg_add_cause("%s: %s", TMUX_CONF, strerror(errno)); if (cfg_file == NULL && (home = find_home()) != NULL) { xasprintf(&cfg_file, "%s/.tmate.conf", home); if (access(cfg_file, R_OK) != 0 && errno == ENOENT) { free(cfg_file); cfg_file = NULL; } } if (cfg_file != NULL && load_cfg(cfg_file, cfg_cmd_q, &cause) == -1) cfg_add_cause("%s: %s", cfg_file, cause); free(cause); cmdq_continue(cfg_cmd_q); } int load_cfg(const char *path, struct cmd_q *cmdq, char **cause) { FILE *f; char delim[3] = { '\\', '\\', '\0' }; u_int found; size_t line = 0; char *buf, *cause1, *p; struct cmd_list *cmdlist; log_debug("loading %s", path); if ((f = fopen(path, "rb")) == NULL) { xasprintf(cause, "%s: %s", path, strerror(errno)); return (-1); } found = 0; while ((buf = fparseln(f, NULL, &line, delim, 0)) != NULL) { log_debug("%s: %s", path, buf); /* Skip empty lines. */ p = buf; while (isspace((u_char) *p)) p++; if (*p == '\0') { free(buf); continue; } /* Parse and run the command. */ if (cmd_string_parse(p, &cmdlist, path, line, &cause1) != 0) { free(buf); if (cause1 == NULL) continue; cfg_add_cause("%s:%zu: %s", path, line, cause1); free(cause1); continue; } free(buf); if (cmdlist == NULL) continue; cmdq_append(cmdq, cmdlist, NULL); cmd_list_free(cmdlist); found++; } fclose(f); return (found); } static void print_cfg_errors(void) { u_int i; for (i = 0; i < cfg_ncauses; i++) { tmate_info("%s", cfg_causes[i]); free(cfg_causes[i]); } free(cfg_causes); cfg_causes = NULL; cfg_ncauses = 0; } void cfg_default_done(__unused struct cmd_q *cmdq) { if (--cfg_references != 0) return; cfg_finished = 1; #ifdef TMATE /* We do it this late, this way, CLI options take precedence over cfg file */ tmate_load_cli_options(); tmate_session_start(); if (tmate_foreground && cfg_ncauses) { print_cfg_errors(); exit(1); } #endif if (!RB_EMPTY(&sessions)) cfg_show_causes(RB_MIN(sessions, &sessions)); cmdq_free(cfg_cmd_q); cfg_cmd_q = NULL; if (cfg_client != NULL) { /* * The client command queue starts with client_exit set to 1 so * only continue if not empty (that is, we have been delayed * during configuration parsing for long enough that the * MSG_COMMAND has arrived), else the client will exit before * the MSG_COMMAND which might tell it not to. */ if (!TAILQ_EMPTY(&cfg_client->cmdq->queue)) cmdq_continue(cfg_client->cmdq); server_client_unref(cfg_client); cfg_client = NULL; } } void cfg_add_cause(const char *fmt, ...) { va_list ap; char *msg; va_start(ap, fmt); xvasprintf(&msg, fmt, ap); va_end(ap); cfg_ncauses++; cfg_causes = xreallocarray(cfg_causes, cfg_ncauses, sizeof *cfg_causes); cfg_causes[cfg_ncauses - 1] = msg; } void cfg_print_causes(struct cmd_q *cmdq) { u_int i; for (i = 0; i < cfg_ncauses; i++) { cmdq_print(cmdq, "%s", cfg_causes[i]); free(cfg_causes[i]); } free(cfg_causes); cfg_causes = NULL; cfg_ncauses = 0; } void cfg_show_causes(struct session *s) { struct window_pane *wp; u_int i; if (s == NULL || cfg_ncauses == 0) return; wp = s->curw->window->active; window_pane_set_mode(wp, &window_copy_mode); window_copy_init_for_output(wp); for (i = 0; i < cfg_ncauses; i++) { window_copy_add(wp, "%s", cfg_causes[i]); free(cfg_causes[i]); } free(cfg_causes); cfg_causes = NULL; cfg_ncauses = 0; } tmate-2.4.0/client.c000066400000000000000000000466251356407164200142760ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tmux.h" #include "tmate.h" struct tmuxproc *client_proc; struct tmuxpeer *client_peer; int client_flags; 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; const char *client_exitsession; int client_attached; __dead void client_exec(const char *,const char *); int client_get_lock(char *); int client_connect(struct event_base *, const char *, int); void client_send_identify(const char *, const char *); void client_stdin_callback(int, short, void *); void client_write(int, const char *, size_t); void client_signal(int); void client_dispatch(struct imsg *, void *); void client_dispatch_attached(struct imsg *); void client_dispatch_wait(struct imsg *, const char *); const char *client_exit_message(void); /* * Get server create lock. If already held then server start is happening in * another client, so block until the lock is released and return -2 to * retry. Return -1 on failure to continue and start the server anyway. */ int client_get_lock(char *lockfile) { int lockfd; log_debug("lock file is %s", lockfile); if ((lockfd = open(lockfile, O_WRONLY|O_CREAT, 0600)) == -1) { log_debug("open failed: %s", strerror(errno)); return (-1); } if (flock(lockfd, LOCK_EX|LOCK_NB) == -1) { log_debug("flock failed: %s", strerror(errno)); if (errno != EAGAIN) return (lockfd); while (flock(lockfd, LOCK_EX) == -1 && errno == EINTR) /* nothing */; close(lockfd); return (-2); } log_debug("flock succeeded"); return (lockfd); } /* Connect client to server. */ int client_connect(struct event_base *base, const char *path, int start_server) { struct sockaddr_un sa; size_t size; int fd, lockfd = -1, locked = 0; char *lockfile = NULL; memset(&sa, 0, sizeof sa); sa.sun_family = AF_UNIX; size = strlcpy(sa.sun_path, path, sizeof sa.sun_path); if (size >= sizeof sa.sun_path) { errno = ENAMETOOLONG; return (-1); } log_debug("socket is %s", path); retry: if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) return (-1); log_debug("trying connect"); if (connect(fd, (struct sockaddr *)&sa, sizeof sa) == -1) { log_debug("connect failed: %s", strerror(errno)); if (errno != ECONNREFUSED && errno != ENOENT) goto failed; if (!start_server) goto failed; close(fd); if (!locked) { xasprintf(&lockfile, "%s.lock", path); if ((lockfd = client_get_lock(lockfile)) < 0) { log_debug("didn't get lock (%d)", lockfd); free(lockfile); lockfile = NULL; if (lockfd == -2) goto retry; } log_debug("got lock (%d)", lockfd); /* * Always retry at least once, even if we got the lock, * because another client could have taken the lock, * started the server and released the lock between our * connect() and flock(). */ locked = 1; goto retry; } if (lockfd >= 0 && unlink(path) != 0 && errno != ENOENT) { free(lockfile); close(lockfd); return (-1); } fd = server_start(base, lockfd, lockfile); } if (locked && lockfd >= 0) { free(lockfile); close(lockfd); } setblocking(fd, 0); return (fd); failed: if (locked) { free(lockfile); close(lockfd); } close(fd); return (-1); } /* Get exit string from reason number. */ const char * client_exit_message(void) { static char msg[256]; switch (client_exitreason) { case CLIENT_EXIT_NONE: break; case CLIENT_EXIT_DETACHED: if (client_exitsession != NULL) { xsnprintf(msg, sizeof msg, "detached " "(from session %s)", client_exitsession); return (msg); } return ("detached"); case CLIENT_EXIT_DETACHED_HUP: if (client_exitsession != NULL) { xsnprintf(msg, sizeof msg, "detached and SIGHUP " "(from session %s)", client_exitsession); return (msg); } return ("detached and SIGHUP"); case CLIENT_EXIT_LOST_TTY: return ("lost tty"); case CLIENT_EXIT_TERMINATED: return ("terminated"); case CLIENT_EXIT_LOST_SERVER: return ("lost server"); case CLIENT_EXIT_EXITED: return ("exited"); case CLIENT_EXIT_SERVER_EXITED: return ("server exited"); } return ("unknown reason"); } #ifdef TMATE extern const struct cmd_entry cmd_attach_session_entry; extern const struct cmd_entry cmd_new_session_entry; /* For foreground mode */ static int __argc; static const char **__argv; #endif int run_headless_command(int argc, const char **argv, int flags, void (*err_callback)(const char *)) { struct cmd_q *cmd_q; struct cmd_list *cmdlist; char *cause; cmd_q = cmdq_new(NULL); /* No client */ if ((cmdlist = cmd_list_parse(argc, (char **)argv, NULL, 0, &cause)) == NULL) { if (err_callback) err_callback(cause); return -1; } cmdq_run(cmd_q, cmdlist, NULL); cmd_list_free(cmdlist); cmdq_free(cmd_q); if (flags & DEFER_ERRORS_CFG) return 0; /* error messages land in cfg_causes */ int ret = cfg_ncauses ? -1 : 0; for (u_int i = 0; i < cfg_ncauses; i++) { if (err_callback) err_callback(cfg_causes[i]); free(cfg_causes[i]); } free(cfg_causes); cfg_causes = NULL; cfg_ncauses = 0; return ret; } static void initial_client_cmd_err_callback(const char *cause) { tmate_info("%s", cause); } void run_initial_client_cmd(void) { int argc = __argc; const char **argv = __argv; const char *default_argv[] = {"new-session"}; if (argc == 0) { argc = 1; argv = default_argv; } if (run_headless_command(argc, argv, 0, initial_client_cmd_err_callback) < 0) exit(1); } /* Client main loop. */ int client_main(struct event_base *base, int argc, char **argv, int flags, const char *shellcmd) { struct cmd *cmd; struct cmd_list *cmdlist; struct msg_command_data *data; int cmdflags, fd, i; const char *ttynam, *cwd; pid_t ppid; enum msgtype msg; char *cause, path[PATH_MAX]; struct termios tio, saved_tio; size_t size; #ifdef TMATE int cant_nest = 0; __argc = argc; __argv = (const char **)argv; #endif /* Ignore SIGCHLD now or daemon() in the server will leave a zombie. */ signal(SIGCHLD, SIG_IGN); /* Save the flags. */ client_flags = flags; /* Set up the initial command. */ cmdflags = 0; if (shellcmd != NULL) { msg = MSG_SHELL; cmdflags = CMD_STARTSERVER; } else if (argc == 0) { msg = MSG_COMMAND; cmdflags = CMD_STARTSERVER; #ifdef TMATE cant_nest = 1; #endif } 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; #ifdef TMATE if (cmd->entry == &cmd_attach_session_entry || cmd->entry == &cmd_new_session_entry) cant_nest = 1; #endif } cmd_list_free(cmdlist); } #ifdef TMATE if (cant_nest && getenv("TMUX")) { fprintf(stderr, "sessions should be nested with care, " "unset $TMUX to force\n"); return (1); } #endif /* Create client process structure (starts logging). */ client_proc = proc_start("client", base, 0, client_signal); /* Initialize the client socket and start the server. */ fd = client_connect(base, socket_path, cmdflags & CMD_STARTSERVER); if (fd == -1) { if (errno == ECONNREFUSED) { fprintf(stderr, "no server running on %s\n", socket_path); } else { #ifdef TMATE if (errno == ENOENT) fprintf(stderr, "You must specify a socket name with -S. For example: \n" " tmate -S /tmp/tmate.sock new-session -d\n" " tmate -S /tmp/tmate.sock wait tmate-ready\n"); else #endif fprintf(stderr, "error connecting to %s (%s)\n", socket_path, strerror(errno)); } return (1); } client_peer = proc_add_peer(client_proc, fd, client_dispatch, (void *)shellcmd); /* Save these before pledge(). */ if ((cwd = getcwd(path, sizeof path)) == NULL) { if ((cwd = find_home()) == NULL) cwd = "/"; } if ((ttynam = ttyname(STDIN_FILENO)) == NULL) ttynam = ""; #ifdef __OpenBSD__ /* * Drop privileges for client. "proc exec" is needed for -c and for * locking (which uses system(3)). * * "tty" is needed to restore termios(4) and also for some reason -CC * does not work properly without it (input is not recognised). * * "sendfd" is dropped later in client_dispatch_wait(). */ if (pledge("stdio unix sendfd proc exec tty", NULL) != 0) fatal("pledge failed"); #endif /* Free stuff that is not used in the client. */ options_free(global_options); options_free(global_s_options); options_free(global_w_options); environ_free(global_environ); /* Create stdin handler. */ setblocking(STDIN_FILENO, 0); event_set(&client_stdin, STDIN_FILENO, EV_READ|EV_PERSIST, client_stdin_callback, NULL); if (client_flags & CLIENT_CONTROLCONTROL) { if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) fatal("tcgetattr failed"); cfmakeraw(&tio); tio.c_iflag = ICRNL|IXANY; tio.c_oflag = OPOST|ONLCR; #ifdef NOKERNINFO tio.c_lflag = NOKERNINFO; #endif tio.c_cflag = CREAD|CS8|HUPCL; tio.c_cc[VMIN] = 1; tio.c_cc[VTIME] = 0; cfsetispeed(&tio, cfgetispeed(&saved_tio)); cfsetospeed(&tio, cfgetospeed(&saved_tio)); tcsetattr(STDIN_FILENO, TCSANOW, &tio); } /* Send identify messages. */ client_send_identify(ttynam, cwd); /* Send first command. */ if (msg == MSG_COMMAND) { /* How big is the command? */ size = 0; for (i = 0; i < argc; i++) size += strlen(argv[i]) + 1; data = xmalloc((sizeof *data) + size); /* Prepare command for server. */ data->argc = argc; if (cmd_pack_argv(argc, argv, (char *)(data + 1), size) != 0) { fprintf(stderr, "command too long\n"); free(data); return (1); } size += sizeof *data; /* Send the command. */ if (proc_send(client_peer, msg, -1, data, size) != 0) { fprintf(stderr, "failed to send command\n"); free(data); return (1); } free(data); } else if (msg == MSG_SHELL) proc_send(client_peer, msg, -1, NULL, 0); /* Start main loop. */ proc_loop(client_proc, NULL); /* Print the exit message, if any, and exit. */ if (client_attached) { if (client_exitreason != CLIENT_EXIT_NONE) printf("[%s]\n", client_exit_message()); ppid = getppid(); if (client_exittype == MSG_DETACHKILL && ppid > 1) kill(ppid, SIGHUP); } else if (client_flags & CLIENT_CONTROLCONTROL) { if (client_exitreason != CLIENT_EXIT_NONE) printf("%%exit %s\n", client_exit_message()); else printf("%%exit\n"); printf("\033\\"); tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio); } else if (client_exitreason != CLIENT_EXIT_NONE) fprintf(stderr, "%s\n", client_exit_message()); setblocking(STDIN_FILENO, 1); return (client_exitval); } /* Send identify messages to server. */ void client_send_identify(const char *ttynam, const char *cwd) { const char *s; char **ss; size_t sslen; int fd, flags = client_flags; pid_t pid; proc_send(client_peer, MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags); if ((s = getenv("TERM")) == NULL) s = ""; proc_send(client_peer, MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1); proc_send(client_peer, MSG_IDENTIFY_TTYNAME, -1, ttynam, strlen(ttynam) + 1); proc_send(client_peer, MSG_IDENTIFY_CWD, -1, cwd, strlen(cwd) + 1); if ((fd = dup(STDIN_FILENO)) == -1) fatal("dup failed"); proc_send(client_peer, MSG_IDENTIFY_STDIN, fd, NULL, 0); pid = getpid(); proc_send(client_peer, MSG_IDENTIFY_CLIENTPID, -1, &pid, sizeof pid); for (ss = environ; *ss != NULL; ss++) { sslen = strlen(*ss) + 1; if (sslen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) continue; proc_send(client_peer, MSG_IDENTIFY_ENVIRON, -1, *ss, sslen); } proc_send(client_peer, MSG_IDENTIFY_DONE, -1, NULL, 0); } /* Callback for client stdin read events. */ void client_stdin_callback(__unused int fd, __unused short events, __unused void *arg) { struct msg_stdin_data data; data.size = read(STDIN_FILENO, data.data, sizeof data.data); if (data.size < 0 && (errno == EINTR || errno == EAGAIN)) return; proc_send(client_peer, MSG_STDIN, -1, &data, sizeof data); if (data.size <= 0) event_del(&client_stdin); } /* Force write to file descriptor. */ 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; } } /* Run command in shell; used for -c. */ __dead void client_exec(const char *shell, const char *shellcmd) { const char *name, *ptr; char *argv0; log_debug("shell %s, command %s", shell, shellcmd); ptr = strrchr(shell, '/'); if (ptr != NULL && *(ptr + 1) != '\0') name = ptr + 1; else name = shell; if (client_flags & CLIENT_LOGIN) xasprintf(&argv0, "-%s", name); else xasprintf(&argv0, "%s", name); setenv("SHELL", shell, 1); setblocking(STDIN_FILENO, 1); setblocking(STDOUT_FILENO, 1); setblocking(STDERR_FILENO, 1); closefrom(STDERR_FILENO + 1); execl(shell, argv0, "-c", shellcmd, (char *) NULL); fatal("execl failed"); } /* Callback to handle signals in the client. */ void client_signal(int sig) { struct sigaction sigact; int status; if (sig == SIGCHLD) waitpid(WAIT_ANY, &status, WNOHANG); else if (!client_attached) { if (sig == SIGTERM) proc_exit(client_proc); } else { switch (sig) { case SIGHUP: client_exitreason = CLIENT_EXIT_LOST_TTY; client_exitval = 1; proc_send(client_peer, MSG_EXITING, -1, NULL, 0); break; case SIGTERM: client_exitreason = CLIENT_EXIT_TERMINATED; client_exitval = 1; proc_send(client_peer, MSG_EXITING, -1, NULL, 0); break; case SIGWINCH: proc_send(client_peer, MSG_RESIZE, -1, NULL, 0); break; case SIGCONT: memset(&sigact, 0, sizeof sigact); sigemptyset(&sigact.sa_mask); sigact.sa_flags = SA_RESTART; sigact.sa_handler = SIG_IGN; if (sigaction(SIGTSTP, &sigact, NULL) != 0) fatal("sigaction failed"); proc_send(client_peer, MSG_WAKEUP, -1, NULL, 0); break; } } } /* Callback for client read events. */ void client_dispatch(struct imsg *imsg, void *arg) { if (imsg == NULL) { client_exitreason = CLIENT_EXIT_LOST_SERVER; client_exitval = 1; proc_exit(client_proc); return; } if (client_attached) client_dispatch_attached(imsg); else client_dispatch_wait(imsg, arg); } /* Dispatch imsgs when in wait state (before MSG_READY). */ void client_dispatch_wait(struct imsg *imsg, const char *shellcmd) { char *data; ssize_t datalen; struct msg_stdout_data stdoutdata; struct msg_stderr_data stderrdata; int retval; #ifdef __OpenBSD__ static int pledge_applied; /* * "sendfd" is no longer required once all of the identify messages * have been sent. We know the server won't send us anything until that * point (because we don't ask it to), so we can drop "sendfd" once we * get the first message from the server. */ if (!pledge_applied) { if (pledge("stdio unix proc exec tty", NULL) != 0) fatal("pledge failed"); pledge_applied = 1; }; #endif data = imsg->data; datalen = imsg->hdr.len - IMSG_HEADER_SIZE; switch (imsg->hdr.type) { case MSG_EXIT: case MSG_SHUTDOWN: if (datalen != sizeof retval && datalen != 0) fatalx("bad MSG_EXIT size"); if (datalen == sizeof retval) { memcpy(&retval, data, sizeof retval); client_exitval = retval; } proc_exit(client_proc); break; case MSG_READY: if (datalen != 0) fatalx("bad MSG_READY size"); event_del(&client_stdin); client_attached = 1; proc_send(client_peer, MSG_RESIZE, -1, NULL, 0); break; case MSG_STDIN: if (datalen != 0) fatalx("bad MSG_STDIN size"); event_add(&client_stdin, NULL); break; case MSG_STDOUT: if (datalen != sizeof stdoutdata) fatalx("bad MSG_STDOUT size"); memcpy(&stdoutdata, data, sizeof stdoutdata); client_write(STDOUT_FILENO, stdoutdata.data, stdoutdata.size); break; case MSG_STDERR: if (datalen != sizeof stderrdata) fatalx("bad MSG_STDERR size"); memcpy(&stderrdata, data, sizeof stderrdata); client_write(STDERR_FILENO, stderrdata.data, stderrdata.size); break; case MSG_VERSION: if (datalen != 0) fatalx("bad MSG_VERSION size"); fprintf(stderr, "protocol version mismatch " "(client %d, server %u)\n", PROTOCOL_VERSION, imsg->hdr.peerid & 0xff); client_exitval = 1; proc_exit(client_proc); break; case MSG_SHELL: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_SHELL string"); clear_signals(0); client_exec(data, shellcmd); /* NOTREACHED */ case MSG_DETACH: case MSG_DETACHKILL: proc_send(client_peer, MSG_EXITING, -1, NULL, 0); break; case MSG_EXITED: proc_exit(client_proc); break; } } /* Dispatch imsgs in attached state (after MSG_READY). */ void client_dispatch_attached(struct imsg *imsg) { struct sigaction sigact; char *data; ssize_t datalen; data = imsg->data; datalen = imsg->hdr.len - IMSG_HEADER_SIZE; switch (imsg->hdr.type) { case MSG_DETACH: case MSG_DETACHKILL: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_DETACH string"); client_exitsession = xstrdup(data); client_exittype = imsg->hdr.type; if (imsg->hdr.type == MSG_DETACHKILL) client_exitreason = CLIENT_EXIT_DETACHED_HUP; else client_exitreason = CLIENT_EXIT_DETACHED; proc_send(client_peer, MSG_EXITING, -1, NULL, 0); break; case MSG_EXIT: if (datalen != 0 && datalen != sizeof (int)) fatalx("bad MSG_EXIT size"); proc_send(client_peer, MSG_EXITING, -1, NULL, 0); client_exitreason = CLIENT_EXIT_EXITED; break; case MSG_EXITED: if (datalen != 0) fatalx("bad MSG_EXITED size"); proc_exit(client_proc); break; case MSG_SHUTDOWN: if (datalen != 0) fatalx("bad MSG_SHUTDOWN size"); proc_send(client_peer, MSG_EXITING, -1, NULL, 0); client_exitreason = CLIENT_EXIT_SERVER_EXITED; client_exitval = 1; break; case MSG_SUSPEND: if (datalen != 0) fatalx("bad MSG_SUSPEND size"); memset(&sigact, 0, sizeof sigact); sigemptyset(&sigact.sa_mask); sigact.sa_flags = SA_RESTART; sigact.sa_handler = SIG_DFL; if (sigaction(SIGTSTP, &sigact, NULL) != 0) fatal("sigaction failed"); kill(getpid(), SIGTSTP); break; case MSG_LOCK: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_LOCK string"); system(data); proc_send(client_peer, MSG_UNLOCK, -1, NULL, 0); break; } } tmate-2.4.0/cmd-attach-session.c000066400000000000000000000103161356407164200164720ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "tmux.h" /* * Attach existing session to the current terminal. */ enum cmd_retval cmd_attach_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_attach_session_entry = { .name = "attach-session", .alias = "attach", .args = { "c:dErt:", 0, 0 }, .usage = "[-dEr] [-c working-directory] " CMD_TARGET_SESSION_USAGE, .tflag = CMD_SESSION_WITHPANE, .flags = CMD_STARTSERVER, .exec = cmd_attach_session_exec }; enum cmd_retval cmd_attach_session(struct cmd_q *cmdq, int dflag, int rflag, const char *cflag, int Eflag) { struct session *s = cmdq->state.tflag.s; struct client *c = cmdq->client, *c_loop; struct winlink *wl = cmdq->state.tflag.wl; struct window_pane *wp = cmdq->state.tflag.wp; const char *update; char *cause, *cwd; struct format_tree *ft; if (RB_EMPTY(&sessions)) { cmdq_error(cmdq, "no sessions"); return (CMD_RETURN_ERROR); } if (c == NULL) return (CMD_RETURN_NORMAL); if (server_client_check_nested(c)) { cmdq_error(cmdq, "sessions should be nested with care, " "unset $TMUX to force"); return (CMD_RETURN_ERROR); } if (wl != NULL) { if (wp != NULL) window_set_active_pane(wp->window, wp); session_set_current(s, wl); } if (cflag != NULL) { ft = format_create(cmdq, 0); format_defaults(ft, c, s, wl, wp); cwd = format_expand(ft, cflag); format_free(ft); free((void *)s->cwd); s->cwd = cwd; } if (c->session != NULL) { if (dflag) { TAILQ_FOREACH(c_loop, &clients, entry) { if (c_loop->session != s || c == c_loop) continue; server_client_detach(c_loop, MSG_DETACH); } } if (!Eflag) { update = options_get_string(s->options, "update-environment"); environ_update(update, c->environ, s->environ); } c->session = s; server_client_set_key_table(c, NULL); status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s, NULL); gettimeofday(&s->last_attached_time, NULL); server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; } else { if (server_client_open(c, &cause) != 0) { cmdq_error(cmdq, "open terminal failed: %s", cause); free(cause); return (CMD_RETURN_ERROR); } if (rflag) c->flags |= CLIENT_READONLY; if (dflag) { TAILQ_FOREACH(c_loop, &clients, entry) { if (c_loop->session != s || c == c_loop) continue; server_client_detach(c_loop, MSG_DETACH); } } if (!Eflag) { update = options_get_string(s->options, "update-environment"); environ_update(update, c->environ, s->environ); } c->session = s; server_client_set_key_table(c, NULL); status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s, NULL); gettimeofday(&s->last_attached_time, NULL); server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; if (~c->flags & CLIENT_CONTROL) proc_send(c->peer, MSG_READY, -1, NULL, 0); hooks_run(c->session->hooks, c, NULL, "client-attached"); cmdq->client_exit = 0; } recalculate_sizes(); alerts_check_session(s); 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_has(args, 'd'), args_has(args, 'r'), args_get(args, 'c'), args_has(args, 'E'))); } tmate-2.4.0/cmd-bind-key.c000066400000000000000000000100441356407164200152450ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Bind a key to a command, this recurses through cmd_*. */ enum cmd_retval cmd_bind_key_exec(struct cmd *, struct cmd_q *); enum cmd_retval cmd_bind_key_mode_table(struct cmd *, struct cmd_q *, key_code); const struct cmd_entry cmd_bind_key_entry = { .name = "bind-key", .alias = "bind", .args = { "cnrt:T:", 1, -1 }, .usage = "[-cnr] [-t mode-table] [-T key-table] key command " "[arguments]", .flags = 0, .exec = cmd_bind_key_exec }; 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; key_code key; const char *tablename; if (args_has(args, 't')) { if (args->argc != 2 && args->argc != 3) { cmdq_error(cmdq, "not enough arguments"); return (CMD_RETURN_ERROR); } } else { if (args->argc < 2) { cmdq_error(cmdq, "not enough arguments"); return (CMD_RETURN_ERROR); } } key = key_string_lookup_string(args->argv[0]); if (key == KEYC_NONE || key == KEYC_UNKNOWN) { cmdq_error(cmdq, "unknown key: %s", args->argv[0]); return (CMD_RETURN_ERROR); } if (args_has(args, 't')) return (cmd_bind_key_mode_table(self, cmdq, key)); if (args_has(args, 'T')) tablename = args_get(args, 'T'); else if (args_has(args, 'n')) tablename = "root"; else tablename = "prefix"; 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); } key_bindings_add(tablename, key, args_has(args, 'r'), cmdlist); return (CMD_RETURN_NORMAL); } enum cmd_retval cmd_bind_key_mode_table(struct cmd *self, struct cmd_q *cmdq, key_code 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); } switch (cmd) { case MODEKEYCOPY_APPENDSELECTION: case MODEKEYCOPY_COPYSELECTION: case MODEKEYCOPY_STARTNAMEDBUFFER: if (args->argc == 2) arg = NULL; else { arg = args->argv[2]; if (strcmp(arg, "-x") != 0) { cmdq_error(cmdq, "unknown argument"); return (CMD_RETURN_ERROR); } } break; case MODEKEYCOPY_COPYPIPE: if (args->argc != 3) { cmdq_error(cmdq, "no argument given"); return (CMD_RETURN_ERROR); } arg = args->argv[2]; break; default: if (args->argc != 2) { cmdq_error(cmdq, "no argument allowed"); return (CMD_RETURN_ERROR); } arg = NULL; break; } 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-2.4.0/cmd-break-pane.c000066400000000000000000000063271356407164200155610ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Break pane off into a window. */ #define BREAK_PANE_TEMPLATE "#{session_name}:#{window_index}.#{pane_index}" enum cmd_retval cmd_break_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_break_pane_entry = { .name = "break-pane", .alias = "breakp", .args = { "dPF:s:t:", 0, 0 }, .usage = "[-dP] [-F format] [-s src-pane] [-t dst-window]", .sflag = CMD_PANE, .tflag = CMD_WINDOW_INDEX, .flags = 0, .exec = 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 = cmdq->state.sflag.wl; struct session *src_s = cmdq->state.sflag.s; struct session *dst_s = cmdq->state.tflag.s; struct window_pane *wp = cmdq->state.sflag.wp; struct window *w = wl->window; char *name; char *cause; int idx = cmdq->state.tflag.idx; struct format_tree *ft; const char *template; char *cp; if (idx != -1 && winlink_find_by_index(&dst_s->windows, idx) != NULL) { cmdq_error(cmdq, "index %d already in use", idx); return (CMD_RETURN_ERROR); } if (window_count_panes(w) == 1) { cmdq_error(cmdq, "can't break with only one pane"); return (CMD_RETURN_ERROR); } server_unzoom_window(w); TAILQ_REMOVE(&w->panes, wp, entry); window_lost_pane(w, wp); layout_close_pane(wp); w = wp->window = window_create1(dst_s->sx, dst_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); wp->flags |= PANE_CHANGED; if (idx == -1) idx = -1 - options_get_number(dst_s->options, "base-index"); wl = session_attach(dst_s, w, idx, &cause); /* can't fail */ if (!args_has(self->args, 'd')) session_select(dst_s, wl->idx); server_redraw_session(src_s); if (src_s != dst_s) server_redraw_session(dst_s); server_status_session_group(src_s); if (src_s != dst_s) server_status_session_group(dst_s); if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) template = BREAK_PANE_TEMPLATE; ft = format_create(cmdq, 0); format_defaults(ft, cmdq->state.c, dst_s, wl, wp); cp = format_expand(ft, template); cmdq_print(cmdq, "%s", cp); free(cp); format_free(ft); } return (CMD_RETURN_NORMAL); #endif } tmate-2.4.0/cmd-capture-pane.c000066400000000000000000000127231356407164200161350ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Jonathan Alvarado * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Write the entire contents of a pane to a buffer or stdout. */ 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 = { .name = "capture-pane", .alias = "capturep", .args = { "ab:CeE:JpPqS:t:", 0, 0 }, .usage = "[-aCeJpPq] " CMD_BUFFER_USAGE " [-E end-line] " "[-S start-line]" CMD_TARGET_PANE_USAGE, .tflag = CMD_PANE, .flags = 0, .exec = cmd_capture_pane_exec }; char * cmd_capture_pane_append(char *buf, size_t *len, char *line, size_t linelen) { buf = xrealloc(buf, *len + linelen + 1); memcpy(buf + *len, line, linelen); *len += linelen; return (buf); } char * cmd_capture_pane_pending(struct args *args, struct window_pane *wp, size_t *len) { struct evbuffer *pending; char *buf, *line, tmp[5]; size_t linelen; u_int i; pending = input_pending(wp); if (pending == NULL) return (xstrdup("")); line = EVBUFFER_DATA(pending); linelen = EVBUFFER_LENGTH(pending); buf = xstrdup(""); if (args_has(args, 'C')) { for (i = 0; i < linelen; i++) { if (line[i] >= ' ') { tmp[0] = line[i]; tmp[1] = '\0'; } else xsnprintf(tmp, sizeof tmp, "\\%03hho", line[i]); buf = cmd_capture_pane_append(buf, len, tmp, strlen(tmp)); } } else buf = cmd_capture_pane_append(buf, len, line, linelen); return (buf); } 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; const char *Sflag, *Eflag; size_t linelen; sx = screen_size_x(&wp->base); if (args_has(args, 'a')) { gd = wp->saved_grid; if (gd == NULL) { if (!args_has(args, 'q')) { cmdq_error(cmdq, "no alternate screen"); return (NULL); } return (xstrdup("")); } } else gd = wp->base.grid; Sflag = args_get(args, 'S'); if (Sflag != NULL && strcmp(Sflag, "-") == 0) top = 0; else { n = args_strtonum(args, 'S', INT_MIN, SHRT_MAX, &cause); if (cause != NULL) { top = gd->hsize; free(cause); } else if (n < 0 && (u_int) -n > gd->hsize) top = 0; else top = gd->hsize + n; if (top > gd->hsize + gd->sy - 1) top = gd->hsize + gd->sy - 1; } Eflag = args_get(args, 'E'); if (Eflag != NULL && strcmp(Eflag, "-") == 0) bottom = gd->hsize + gd->sy - 1; else { n = args_strtonum(args, 'E', INT_MIN, SHRT_MAX, &cause); if (cause != NULL) { bottom = gd->hsize + gd->sy - 1; free(cause); } else if (n < 0 && (u_int) -n > gd->hsize) bottom = 0; else bottom = gd->hsize + n; if (bottom > gd->hsize + gd->sy - 1) bottom = gd->hsize + gd->sy - 1; } if (bottom < top) { tmp = bottom; bottom = top; top = tmp; } with_codes = args_has(args, 'e'); escape_c0 = args_has(args, 'C'); join_lines = args_has(args, 'J'); buf = NULL; for (i = top; i <= bottom; i++) { line = grid_string_cells(gd, 0, i, sx, &gc, with_codes, escape_c0, !join_lines); linelen = strlen(line); buf = cmd_capture_pane_append(buf, len, line, linelen); gl = grid_peek_line(gd, i); if (!join_lines || !(gl->flags & GRID_LINE_WRAPPED)) buf[(*len)++] = '\n'; free(line); } return (buf); } 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 = cmdq->state.tflag.wp; char *buf, *cause; const char *bufname; size_t len; 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"); free(buf); return (CMD_RETURN_ERROR); } evbuffer_add(c->stdout_data, buf, len); free(buf); if (args_has(args, 'P') && len > 0) evbuffer_add(c->stdout_data, "\n", 1); server_client_push_stdout(c); } else { bufname = NULL; if (args_has(args, 'b')) bufname = args_get(args, 'b'); if (paste_set(buf, len, bufname, &cause) != 0) { cmdq_error(cmdq, "%s", cause); free(cause); free(buf); return (CMD_RETURN_ERROR); } } return (CMD_RETURN_NORMAL); } tmate-2.4.0/cmd-choose-buffer.c000066400000000000000000000052331356407164200162760ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2010 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Enter choice mode to choose a buffer. */ #define CHOOSE_BUFFER_TEMPLATE \ "#{buffer_name}: #{buffer_size} bytes: #{buffer_sample}" enum cmd_retval cmd_choose_buffer_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_choose_buffer_entry = { .name = "choose-buffer", .alias = NULL, .args = { "F:t:", 0, 1 }, .usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]", .tflag = CMD_WINDOW, .flags = 0, .exec = 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 = cmdq->state.c; struct winlink *wl = cmdq->state.tflag.wl; struct window_choose_data *cdata; struct paste_buffer *pb; char *action, *action_data; const char *template; u_int idx; if (c == NULL) { cmdq_error(cmdq, "no client available"); return (CMD_RETURN_ERROR); } if ((template = args_get(args, 'F')) == NULL) template = CHOOSE_BUFFER_TEMPLATE; if (paste_get_top(NULL) == 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; pb = NULL; while ((pb = paste_walk(pb)) != NULL) { cdata = window_choose_data_create(TREE_OTHER, c, c->session); cdata->idx = idx; cdata->ft_template = xstrdup(template); format_defaults_paste_buffer(cdata->ft, pb); xasprintf(&action_data, "%s", paste_buffer_name(pb)); cdata->command = cmd_template_replace(action, action_data, 1); free(action_data); window_choose_add(wl->window->active, cdata); idx++; } free(action); window_choose_ready(wl->window->active, 0, NULL); return (CMD_RETURN_NORMAL); } tmate-2.4.0/cmd-choose-client.c000066400000000000000000000064421356407164200163060ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Enter choice mode to choose a client. */ #define CHOOSE_CLIENT_TEMPLATE \ "#{client_tty}: #{session_name} " \ "[#{client_width}x#{client_height} #{client_termname}]" \ "#{?client_utf8, (utf8),}#{?client_readonly, (ro),} " \ "(last used #{t:client_activity})" 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 = { .name = "choose-client", .alias = NULL, .args = { "F:t:", 0, 1 }, .usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]", .tflag = CMD_WINDOW, .flags = 0, .exec = 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 = cmdq->state.c; struct client *c1; struct window_choose_data *cdata; struct winlink *wl = cmdq->state.tflag.wl; const char *template; char *action; u_int idx, cur; if (c == NULL) { cmdq_error(cmdq, "no client available"); 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; TAILQ_FOREACH(c1, &clients, entry) { if (c1->session == NULL || c1->tty.path == NULL) continue; if (c1 == cmdq->client) cur = idx; cdata = window_choose_data_create(TREE_OTHER, c, c->session); cdata->idx = idx; cdata->ft_template = xstrdup(template); format_add(cdata->ft, "line", "%u", idx); format_defaults(cdata->ft, c1, NULL, NULL, NULL); cdata->command = cmd_template_replace(action, c1->tty.path, 1); window_choose_add(wl->window->active, cdata); idx++; } 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; u_int idx; if (cdata == NULL) return; if (cdata->start_client->flags & CLIENT_DEAD) return; idx = 0; TAILQ_FOREACH(c, &clients, entry) { if (idx == cdata->idx) break; idx++; } if (c == NULL || c->session == NULL) return; window_choose_data_run(cdata); } tmate-2.4.0/cmd-choose-tree.c000066400000000000000000000154021356407164200157630ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2012 Thomas Adam * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #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. */ #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}\"" enum cmd_retval cmd_choose_tree_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_choose_tree_entry = { .name = "choose-tree", .alias = NULL, .args = { "S:W:swub:c:t:", 0, 1 }, .usage = "[-suw] [-b session-template] [-c window template] " "[-S format] [-W format] " CMD_TARGET_WINDOW_USAGE, .tflag = CMD_WINDOW, .flags = 0, .exec = cmd_choose_tree_exec }; const struct cmd_entry cmd_choose_session_entry = { .name = "choose-session", .alias = NULL, .args = { "F:t:", 0, 1 }, .usage = CMD_TARGET_WINDOW_USAGE " [-F format] [template]", .tflag = CMD_WINDOW, .flags = 0, .exec = cmd_choose_tree_exec }; const struct cmd_entry cmd_choose_window_entry = { .name = "choose-window", .alias = NULL, .args = { "F:t:", 0, 1 }, .usage = CMD_TARGET_WINDOW_USAGE "[-F format] [template]", .tflag = CMD_WINDOW, .flags = 0, .exec = cmd_choose_tree_exec }; enum cmd_retval cmd_choose_tree_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c = cmdq->state.c; struct winlink *wl = cmdq->state.tflag.wl, *wm; struct session *s = cmdq->state.tflag.s, *s2; 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 == NULL) { cmdq_error(cmdq, "no client available"); 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); window_choose_set_current(wl->window->active, cur_win); } return (CMD_RETURN_NORMAL); } tmate-2.4.0/cmd-clear-history.c000066400000000000000000000027651356407164200163430ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "tmux.h" /* * Clear pane history. */ enum cmd_retval cmd_clear_history_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_clear_history_entry = { .name = "clear-history", .alias = "clearhist", .args = { "t:", 0, 0 }, .usage = CMD_TARGET_PANE_USAGE, .tflag = CMD_PANE, .flags = 0, .exec = cmd_clear_history_exec }; enum cmd_retval cmd_clear_history_exec(__unused struct cmd *self, struct cmd_q *cmdq) { struct window_pane *wp = cmdq->state.tflag.wp; struct grid *gd; gd = cmdq->state.tflag.wp->base.grid; if (wp->mode == &window_copy_mode) window_pane_reset_mode(wp); grid_clear_history(gd); return (CMD_RETURN_NORMAL); } tmate-2.4.0/cmd-command-prompt.c000066400000000000000000000103551356407164200165050ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include "tmux.h" /* * Prompt for command in client. */ 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 = { .name = "command-prompt", .alias = NULL, .args = { "I:p:t:", 0, 1 }, .usage = "[-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " " "[template]", .tflag = CMD_CLIENT, .flags = 0, .exec = 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; }; 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 = cmdq->state.c; char *prompt, *ptr, *input = NULL; size_t n; 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, NULL); 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-2.4.0/cmd-confirm-before.c000066400000000000000000000057641356407164200164550ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Tiago Cunha * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" /* * Asks for confirmation before executing a command. */ 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 = { .name = "confirm-before", .alias = "confirm", .args = { "p:t:", 1, 1 }, .usage = "[-p prompt] " CMD_TARGET_CLIENT_USAGE " command", .tflag = CMD_CLIENT, .flags = 0, .exec = cmd_confirm_before_exec }; struct cmd_confirm_before_data { char *cmd; struct client *client; }; 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 = cmdq->state.c; char *cmd, *copy, *new_prompt, *ptr; const char *prompt; 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, NULL); 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; server_client_unref(c); free(cdata->cmd); free(cdata); } tmate-2.4.0/cmd-copy-mode.c000066400000000000000000000046001356407164200154400ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "tmux.h" /* * Enter copy or clock mode. */ enum cmd_retval cmd_copy_mode_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_copy_mode_entry = { .name = "copy-mode", .alias = NULL, .args = { "Met:u", 0, 0 }, .usage = "[-Mu] " CMD_TARGET_PANE_USAGE, .tflag = CMD_PANE, .flags = 0, .exec = cmd_copy_mode_exec }; const struct cmd_entry cmd_clock_mode_entry = { .name = "clock-mode", .alias = NULL, .args = { "t:", 0, 0 }, .usage = CMD_TARGET_PANE_USAGE, .tflag = CMD_PANE, .flags = 0, .exec = cmd_copy_mode_exec }; enum cmd_retval cmd_copy_mode_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c = cmdq->client; struct session *s; struct window_pane *wp = cmdq->state.tflag.wp; if (args_has(args, 'M')) { if ((wp = cmd_mouse_pane(&cmdq->item->mouse, &s, NULL)) == NULL) return (CMD_RETURN_NORMAL); if (c == NULL || c->session != s) return (CMD_RETURN_NORMAL); } if (self->entry == &cmd_clock_mode_entry) { window_pane_set_mode(wp, &window_clock_mode); return (CMD_RETURN_NORMAL); } if (wp->mode != &window_copy_mode) { if (window_pane_set_mode(wp, &window_copy_mode) != 0) return (CMD_RETURN_NORMAL); window_copy_init_from_pane(wp, args_has(self->args, 'e')); } if (args_has(args, 'M')) { if (wp->mode != NULL && wp->mode != &window_copy_mode) return (CMD_RETURN_NORMAL); window_copy_start_drag(c, &cmdq->item->mouse); } if (wp->mode == &window_copy_mode && args_has(self->args, 'u')) window_copy_pageup(wp); return (CMD_RETURN_NORMAL); } tmate-2.4.0/cmd-detach-client.c000066400000000000000000000046421356407164200162560ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Detach a client. */ enum cmd_retval cmd_detach_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_detach_client_entry = { .name = "detach-client", .alias = "detach", .args = { "as:t:P", 0, 0 }, .usage = "[-P] [-a] [-s target-session] " CMD_TARGET_CLIENT_USAGE, .sflag = CMD_SESSION, .tflag = CMD_CLIENT, .flags = CMD_READONLY, .exec = cmd_detach_client_exec }; const struct cmd_entry cmd_suspend_client_entry = { .name = "suspend-client", .alias = "suspendc", .args = { "t:", 0, 0 }, .usage = CMD_TARGET_CLIENT_USAGE, .tflag = CMD_CLIENT, .flags = 0, .exec = 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 = cmdq->state.c, *cloop; struct session *s; enum msgtype msgtype; if (self->entry == &cmd_suspend_client_entry) { tty_stop_tty(&c->tty); c->flags |= CLIENT_SUSPENDED; proc_send(c->peer, MSG_SUSPEND, -1, NULL, 0); return (CMD_RETURN_NORMAL); } if (args_has(args, 'P')) msgtype = MSG_DETACHKILL; else msgtype = MSG_DETACH; if (args_has(args, 's')) { s = cmdq->state.sflag.s; TAILQ_FOREACH(cloop, &clients, entry) { if (cloop->session == s) server_client_detach(cloop, msgtype); } return (CMD_RETURN_STOP); } if (args_has(args, 'a')) { TAILQ_FOREACH(cloop, &clients, entry) { if (cloop->session != NULL && cloop != c) server_client_detach(cloop, msgtype); } return (CMD_RETURN_NORMAL); } server_client_detach(c, msgtype); return (CMD_RETURN_STOP); } tmate-2.4.0/cmd-display-message.c000066400000000000000000000045661356407164200166460ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Tiago Cunha * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Displays a message in the status line. */ #define DISPLAY_MESSAGE_TEMPLATE \ "[#{session_name}] #{window_index}:" \ "#{window_name}, current pane #{pane_index} " \ "- (%H:%M %d-%b-%y)" enum cmd_retval cmd_display_message_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_display_message_entry = { .name = "display-message", .alias = "display", .args = { "c:pt:F:", 0, 1 }, .usage = "[-p] [-c target-client] [-F format] " CMD_TARGET_PANE_USAGE " [message]", .cflag = CMD_CLIENT_CANFAIL, .tflag = CMD_PANE, .flags = 0, .exec = 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 = cmdq->state.c; struct session *s = cmdq->state.tflag.s; struct winlink *wl = cmdq->state.tflag.wl; struct window_pane *wp = cmdq->state.tflag.wp; const char *template; char *msg; struct format_tree *ft; if (args_has(args, 'F') && args->argc != 0) { cmdq_error(cmdq, "only one of -F or argument must be given"); return (CMD_RETURN_ERROR); } template = args_get(args, 'F'); if (args->argc != 0) template = args->argv[0]; if (template == NULL) template = DISPLAY_MESSAGE_TEMPLATE; ft = format_create(cmdq, 0); format_defaults(ft, c, s, wl, wp); msg = format_expand_time(ft, template, time(NULL)); 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-2.4.0/cmd-display-panes.c000066400000000000000000000025351356407164200163220ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "tmux.h" /* * 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 = { .name = "display-panes", .alias = "displayp", .args = { "t:", 0, 0 }, .usage = CMD_TARGET_CLIENT_USAGE, .tflag = CMD_CLIENT, .flags = 0, .exec = cmd_display_panes_exec }; enum cmd_retval cmd_display_panes_exec(__unused struct cmd *self, struct cmd_q *cmdq) { server_set_identify(cmdq->state.c); return (CMD_RETURN_NORMAL); } tmate-2.4.0/cmd-find-window.c000066400000000000000000000143431356407164200157760ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" /* * Find window containing text. */ #define FIND_WINDOW_TEMPLATE \ "#{window_index}: #{window_name} " \ "[#{window_width}x#{window_height}] " \ "(#{window_panes} panes) #{window_find_matches}" 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 = { .name = "find-window", .alias = "findw", .args = { "F:CNt:T", 1, 4 }, .usage = "[-CNT] [-F format] " CMD_TARGET_WINDOW_USAGE " match-string", .tflag = CMD_WINDOW, .flags = 0, .exec = cmd_find_window_exec }; struct cmd_find_window_data { struct winlink *wl; char *list_ctx; u_int pane_id; TAILQ_ENTRY(cmd_find_window_data) entry; }; TAILQ_HEAD(cmd_find_window_list, cmd_find_window_data); u_int cmd_find_window_match_flags(struct args *); void cmd_find_window_match(struct cmd_find_window_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_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; find_data = xcalloc(1, 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; TAILQ_INSERT_TAIL(find_list, find_data, entry); } else free(find_data); } enum cmd_retval cmd_find_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c = cmdq->state.c; struct window_choose_data *cdata; struct session *s = cmdq->state.tflag.s; struct winlink *wl = cmdq->state.tflag.wl, *wm; struct cmd_find_window_list find_list; struct cmd_find_window_data *find_data; struct cmd_find_window_data *find_data1; char *str, *searchstr; const char *template; u_int i, match_flags; if (c == NULL) { cmdq_error(cmdq, "no client available"); 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]; TAILQ_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 (TAILQ_EMPTY(&find_list)) { cmdq_error(cmdq, "no windows matching: %s", str); return (CMD_RETURN_ERROR); } if (TAILQ_NEXT(TAILQ_FIRST(&find_list), entry) == NULL) { if (session_select(s, TAILQ_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; i = 0; TAILQ_FOREACH(find_data, &find_list, entry) { cdata = window_choose_data_create(TREE_OTHER, c, c->session); cdata->idx = find_data->wl->idx; cdata->wl = find_data->wl; cdata->ft_template = xstrdup(template); cdata->pane_id = find_data->pane_id; format_add(cdata->ft, "line", "%u", i); format_add(cdata->ft, "window_find_matches", "%s", find_data->list_ctx); format_defaults(cdata->ft, NULL, s, find_data->wl, NULL); window_choose_add(wl->window->active, cdata); i++; } window_choose_ready(wl->window->active, 0, cmd_find_window_callback); out: TAILQ_FOREACH_SAFE(find_data, &find_list, entry, find_data1) { free(find_data->list_ctx); TAILQ_REMOVE(&find_list, find_data, entry); free(find_data); } 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-2.4.0/cmd-find.c000066400000000000000000000732261356407164200144760ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2015 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "tmux.h" struct session *cmd_find_try_TMUX(struct client *, struct window *); int cmd_find_client_better(struct client *, struct client *); struct client *cmd_find_best_client(struct client **, u_int); int cmd_find_session_better(struct session *, struct session *, int); struct session *cmd_find_best_session(struct session **, u_int, int); int cmd_find_best_session_with_window(struct cmd_find_state *); int cmd_find_best_winlink_with_window(struct cmd_find_state *); int cmd_find_current_session_with_client(struct cmd_find_state *); int cmd_find_current_session(struct cmd_find_state *); struct client *cmd_find_current_client(struct cmd_q *); const char *cmd_find_map_table(const char *[][2], const char *); int cmd_find_get_session(struct cmd_find_state *, const char *); int cmd_find_get_window(struct cmd_find_state *, const char *); int cmd_find_get_window_with_session(struct cmd_find_state *, const char *); int cmd_find_get_window_with_pane(struct cmd_find_state *); int cmd_find_get_pane(struct cmd_find_state *, const char *); int cmd_find_get_pane_with_session(struct cmd_find_state *, const char *); int cmd_find_get_pane_with_window(struct cmd_find_state *, const char *); const char *cmd_find_session_table[][2] = { { NULL, NULL } }; const char *cmd_find_window_table[][2] = { { "{start}", "^" }, { "{last}", "!" }, { "{end}", "$" }, { "{next}", "+" }, { "{previous}", "-" }, { NULL, NULL } }; const char *cmd_find_pane_table[][2] = { { "{last}", "!" }, { "{next}", "+" }, { "{previous}", "-" }, { "{top}", "top" }, { "{bottom}", "bottom" }, { "{left}", "left" }, { "{right}", "right" }, { "{top-left}", "top-left" }, { "{top-right}", "top-right" }, { "{bottom-left}", "bottom-left" }, { "{bottom-right}", "bottom-right" }, { "{up-of}", "{up-of}" }, { "{down-of}", "{down-of}" }, { "{left-of}", "{left-of}" }, { "{right-of}", "{right-of}" }, { NULL, NULL } }; /* Get session from TMUX if present. */ struct session * cmd_find_try_TMUX(struct client *c, struct window *w) { struct environ_entry *envent; char tmp[256]; long long pid; u_int session; struct session *s; envent = environ_find(c->environ, "TMUX"); if (envent == NULL) return (NULL); if (sscanf(envent->value, "%255[^,],%lld,%d", tmp, &pid, &session) != 3) return (NULL); if (pid != getpid()) return (NULL); log_debug("client %p TMUX is %s (session @%u)", c, envent->value, session); s = session_find_by_id(session); if (s == NULL || (w != NULL && !session_has(s, w))) return (NULL); return (s); } /* Is this client better? */ int cmd_find_client_better(struct client *c, struct client *than) { if (than == NULL) return (1); return (timercmp(&c->activity_time, &than->activity_time, >)); } /* Find best client from a list, or all if list is NULL. */ struct client * cmd_find_best_client(struct client **clist, u_int csize) { struct client *c_loop, *c; u_int i; c = NULL; if (clist != NULL) { for (i = 0; i < csize; i++) { if (clist[i]->session == NULL) continue; if (cmd_find_client_better(clist[i], c)) c = clist[i]; } } else { TAILQ_FOREACH(c_loop, &clients, entry) { if (c_loop->session == NULL) continue; if (cmd_find_client_better(c_loop, c)) c = c_loop; } } return (c); } /* Is this session better? */ int cmd_find_session_better(struct session *s, struct session *than, int flags) { int attached; if (than == NULL) return (1); if (flags & CMD_FIND_PREFER_UNATTACHED) { attached = (~than->flags & SESSION_UNATTACHED); if (attached && (s->flags & SESSION_UNATTACHED)) return (1); else if (!attached && (~s->flags & SESSION_UNATTACHED)) return (0); } return (timercmp(&s->activity_time, &than->activity_time, >)); } /* Find best session from a list, or all if list is NULL. */ struct session * cmd_find_best_session(struct session **slist, u_int ssize, int flags) { struct session *s_loop, *s; u_int i; s = NULL; if (slist != NULL) { for (i = 0; i < ssize; i++) { if (cmd_find_session_better(slist[i], s, flags)) s = slist[i]; } } else { RB_FOREACH(s_loop, sessions, &sessions) { if (cmd_find_session_better(s_loop, s, flags)) s = s_loop; } } return (s); } /* Find best session and winlink for window. */ int cmd_find_best_session_with_window(struct cmd_find_state *fs) { struct session **slist = NULL; u_int ssize; struct session *s; if (fs->cmdq != NULL && fs->cmdq->client != NULL) { fs->s = cmd_find_try_TMUX(fs->cmdq->client, fs->w); if (fs->s != NULL) return (cmd_find_best_winlink_with_window(fs)); } ssize = 0; RB_FOREACH(s, sessions, &sessions) { if (!session_has(s, fs->w)) continue; slist = xreallocarray(slist, ssize + 1, sizeof *slist); slist[ssize++] = s; } if (ssize == 0) goto fail; fs->s = cmd_find_best_session(slist, ssize, fs->flags); if (fs->s == NULL) goto fail; free(slist); return (cmd_find_best_winlink_with_window(fs)); fail: free(slist); return (-1); } /* * Find the best winlink for a window (the current if it contains the pane, * otherwise the first). */ int cmd_find_best_winlink_with_window(struct cmd_find_state *fs) { struct winlink *wl, *wl_loop; wl = NULL; if (fs->s->curw->window == fs->w) wl = fs->s->curw; else { RB_FOREACH(wl_loop, winlinks, &fs->s->windows) { if (wl_loop->window == fs->w) { wl = wl_loop; break; } } } if (wl == NULL) return (-1); fs->wl = wl; fs->idx = fs->wl->idx; return (0); } /* Find current session when we have an unattached client. */ int cmd_find_current_session_with_client(struct cmd_find_state *fs) { struct window_pane *wp; /* * If this is running in a pane, we can use that to limit the list of * sessions to those containing that pane (we still use the current * window in the best session). */ if (fs->cmdq != NULL && fs->cmdq->client->tty.path != NULL) { RB_FOREACH(wp, window_pane_tree, &all_window_panes) { if (strcmp(wp->tty, fs->cmdq->client->tty.path) == 0) break; } } else wp = NULL; /* Not running in a pane. We know nothing. Find the best session. */ if (wp == NULL) goto unknown_pane; /* Find the best session and winlink containing this pane. */ fs->w = wp->window; if (cmd_find_best_session_with_window(fs) != 0) { if (wp != NULL) { /* * The window may have been destroyed but the pane * still on all_window_panes due to something else * holding a reference. */ goto unknown_pane; } return (-1); } /* Use the current window and pane from this session. */ fs->wl = fs->s->curw; fs->idx = fs->wl->idx; fs->w = fs->wl->window; fs->wp = fs->w->active; return (0); unknown_pane: fs->s = NULL; if (fs->cmdq != NULL) fs->s = cmd_find_try_TMUX(fs->cmdq->client, NULL); if (fs->s == NULL) fs->s = cmd_find_best_session(NULL, 0, fs->flags); if (fs->s == NULL) return (-1); fs->wl = fs->s->curw; fs->idx = fs->wl->idx; fs->w = fs->wl->window; fs->wp = fs->w->active; return (0); } /* * Work out the best current state. If this function succeeds, the state is * guaranteed to be completely filled in. */ int cmd_find_current_session(struct cmd_find_state *fs) { /* If we know the current client, use it. */ if (fs->cmdq != NULL && fs->cmdq->client != NULL) { log_debug("%s: have client %p%s", __func__, fs->cmdq->client, fs->cmdq->client->session == NULL ? "" : " (with session)"); if (fs->cmdq->client->session == NULL) return (cmd_find_current_session_with_client(fs)); fs->s = fs->cmdq->client->session; fs->wl = fs->s->curw; fs->idx = fs->wl->idx; fs->w = fs->wl->window; fs->wp = fs->w->active; return (0); } /* We know nothing, find the best session and client. */ fs->s = cmd_find_best_session(NULL, 0, fs->flags); if (fs->s == NULL) return (-1); fs->wl = fs->s->curw; fs->idx = fs->wl->idx; fs->w = fs->wl->window; fs->wp = fs->w->active; return (0); } /* Work out the best current client. */ struct client * cmd_find_current_client(struct cmd_q *cmdq) { struct cmd_find_state current; struct session *s; struct client *c, **clist = NULL; u_int csize; /* If the queue client has a session, use it. */ if (cmdq->client != NULL && cmdq->client->session != NULL) { log_debug("%s: using cmdq %p client %p", __func__, cmdq, cmdq->client); return (cmdq->client); } /* Otherwise find the current session. */ cmd_find_clear_state(¤t, cmdq, 0); if (cmd_find_current_session(¤t) != 0) return (NULL); /* If it is attached, find the best of it's clients. */ s = current.s; log_debug("%s: current session $%u %s", __func__, s->id, s->name); if (~s->flags & SESSION_UNATTACHED) { csize = 0; TAILQ_FOREACH(c, &clients, entry) { if (c->session != s) continue; clist = xreallocarray(clist, csize + 1, sizeof *clist); clist[csize++] = c; } if (csize != 0) { c = cmd_find_best_client(clist, csize); if (c != NULL) { free(clist); return (c); } } free(clist); } /* Otherwise pick best of all clients. */ return (cmd_find_best_client(NULL, 0)); } /* Maps string in table. */ const char * cmd_find_map_table(const char *table[][2], const char *s) { u_int i; for (i = 0; table[i][0] != NULL; i++) { if (strcmp(s, table[i][0]) == 0) return (table[i][1]); } return (s); } /* Find session from string. Fills in s. */ int cmd_find_get_session(struct cmd_find_state *fs, const char *session) { struct session *s, *s_loop; struct client *c; log_debug("%s: %s", __func__, session); /* Check for session ids starting with $. */ if (*session == '$') { fs->s = session_find_by_id_str(session); if (fs->s == NULL) return (-1); return (0); } /* Look for exactly this session. */ fs->s = session_find(session); if (fs->s != NULL) return (0); /* Look for as a client. */ c = cmd_find_client(NULL, session, 1); if (c != NULL && c->session != NULL) { fs->s = c->session; return (0); } /* Stop now if exact only. */ if (fs->flags & CMD_FIND_EXACT_SESSION) return (-1); /* Otherwise look for prefix. */ s = NULL; RB_FOREACH(s_loop, sessions, &sessions) { if (strncmp(session, s_loop->name, strlen(session)) == 0) { if (s != NULL) return (-1); s = s_loop; } } if (s != NULL) { fs->s = s; return (0); } /* Then as a pattern. */ s = NULL; RB_FOREACH(s_loop, sessions, &sessions) { if (fnmatch(session, s_loop->name, 0) == 0) { if (s != NULL) return (-1); s = s_loop; } } if (s != NULL) { fs->s = s; return (0); } return (-1); } /* Find window from string. Fills in s, wl, w. */ int cmd_find_get_window(struct cmd_find_state *fs, const char *window) { log_debug("%s: %s", __func__, window); /* Check for window ids starting with @. */ if (*window == '@') { fs->w = window_find_by_id_str(window); if (fs->w == NULL) return (-1); return (cmd_find_best_session_with_window(fs)); } /* Not a window id, so use the current session. */ fs->s = fs->current->s; /* We now only need to find the winlink in this session. */ if (cmd_find_get_window_with_session(fs, window) == 0) return (0); /* Otherwise try as a session itself. */ if (cmd_find_get_session(fs, window) == 0) { fs->wl = fs->s->curw; fs->w = fs->wl->window; if (~fs->flags & CMD_FIND_WINDOW_INDEX) fs->idx = fs->wl->idx; return (0); } return (-1); } /* * Find window from string, assuming it is in given session. Needs s, fills in * wl and w. */ int cmd_find_get_window_with_session(struct cmd_find_state *fs, const char *window) { struct winlink *wl; const char *errstr; int idx, n, exact; struct session *s; log_debug("%s: %s", __func__, window); exact = (fs->flags & CMD_FIND_EXACT_WINDOW); /* * Start with the current window as the default. So if only an index is * found, the window will be the current. */ fs->wl = fs->s->curw; fs->w = fs->wl->window; /* Check for window ids starting with @. */ if (*window == '@') { fs->w = window_find_by_id_str(window); if (fs->w == NULL || !session_has(fs->s, fs->w)) return (-1); return (cmd_find_best_winlink_with_window(fs)); } /* Try as an offset. */ if (!exact && (window[0] == '+' || window[0] == '-')) { if (window[1] != '\0') n = strtonum(window + 1, 1, INT_MAX, NULL); else n = 1; s = fs->s; if (fs->flags & CMD_FIND_WINDOW_INDEX) { if (window[0] == '+') { if (INT_MAX - s->curw->idx < n) return (-1); fs->idx = s->curw->idx + n; } else { if (n < s->curw->idx) return (-1); fs->idx = s->curw->idx - n; } return (0); } if (window[0] == '+') fs->wl = winlink_next_by_number(s->curw, s, n); else fs->wl = winlink_previous_by_number(s->curw, s, n); if (fs->wl != NULL) { fs->idx = fs->wl->idx; fs->w = fs->wl->window; return (0); } } /* Try special characters. */ if (!exact) { if (strcmp(window, "!") == 0) { fs->wl = TAILQ_FIRST(&fs->s->lastw); if (fs->wl == NULL) return (-1); fs->idx = fs->wl->idx; fs->w = fs->wl->window; return (0); } else if (strcmp(window, "^") == 0) { fs->wl = RB_MIN(winlinks, &fs->s->windows); if (fs->wl == NULL) return (-1); fs->idx = fs->wl->idx; fs->w = fs->wl->window; return (0); } else if (strcmp(window, "$") == 0) { fs->wl = RB_MAX(winlinks, &fs->s->windows); if (fs->wl == NULL) return (-1); fs->idx = fs->wl->idx; fs->w = fs->wl->window; return (0); } } /* First see if this is a valid window index in this session. */ if (window[0] != '+' && window[0] != '-') { idx = strtonum(window, 0, INT_MAX, &errstr); if (errstr == NULL) { if (fs->flags & CMD_FIND_WINDOW_INDEX) { fs->idx = idx; return (0); } fs->wl = winlink_find_by_index(&fs->s->windows, idx); if (fs->wl != NULL) { fs->w = fs->wl->window; return (0); } } } /* Look for exact matches, error if more than one. */ fs->wl = NULL; RB_FOREACH(wl, winlinks, &fs->s->windows) { if (strcmp(window, wl->window->name) == 0) { if (fs->wl != NULL) return (-1); fs->wl = wl; } } if (fs->wl != NULL) { fs->idx = fs->wl->idx; fs->w = fs->wl->window; return (0); } /* Stop now if exact only. */ if (exact) return (-1); /* Try as the start of a window name, error if multiple. */ fs->wl = NULL; RB_FOREACH(wl, winlinks, &fs->s->windows) { if (strncmp(window, wl->window->name, strlen(window)) == 0) { if (fs->wl != NULL) return (-1); fs->wl = wl; } } if (fs->wl != NULL) { fs->idx = fs->wl->idx; fs->w = fs->wl->window; return (0); } /* Now look for pattern matches, again error if multiple. */ fs->wl = NULL; RB_FOREACH(wl, winlinks, &fs->s->windows) { if (fnmatch(window, wl->window->name, 0) == 0) { if (fs->wl != NULL) return (-1); fs->wl = wl; } } if (fs->wl != NULL) { fs->idx = fs->wl->idx; fs->w = fs->wl->window; return (0); } return (-1); } /* Find window from given pane. Needs wp, fills in s and wl and w. */ int cmd_find_get_window_with_pane(struct cmd_find_state *fs) { log_debug("%s", __func__); fs->w = fs->wp->window; return (cmd_find_best_session_with_window(fs)); } /* Find pane from string. Fills in s, wl, w, wp. */ int cmd_find_get_pane(struct cmd_find_state *fs, const char *pane) { log_debug("%s: %s", __func__, pane); /* Check for pane ids starting with %. */ if (*pane == '%') { fs->wp = window_pane_find_by_id_str(pane); if (fs->wp == NULL) return (-1); fs->w = fs->wp->window; return (cmd_find_best_session_with_window(fs)); } /* Not a pane id, so try the current session and window. */ fs->s = fs->current->s; fs->wl = fs->current->wl; fs->idx = fs->current->idx; fs->w = fs->current->w; /* We now only need to find the pane in this window. */ if (cmd_find_get_pane_with_window(fs, pane) == 0) return (0); /* Otherwise try as a window itself (this will also try as session). */ if (cmd_find_get_window(fs, pane) == 0) { fs->wp = fs->w->active; return (0); } return (-1); } /* * Find pane from string, assuming it is in given session. Needs s, fills in wl * and w and wp. */ int cmd_find_get_pane_with_session(struct cmd_find_state *fs, const char *pane) { log_debug("%s: %s", __func__, pane); /* Check for pane ids starting with %. */ if (*pane == '%') { fs->wp = window_pane_find_by_id_str(pane); if (fs->wp == NULL) return (-1); fs->w = fs->wp->window; return (cmd_find_best_winlink_with_window(fs)); } /* Otherwise use the current window. */ fs->wl = fs->s->curw; fs->idx = fs->wl->idx; fs->w = fs->wl->window; /* Now we just need to look up the pane. */ return (cmd_find_get_pane_with_window(fs, pane)); } /* * Find pane from string, assuming it is in the given window. Needs w, fills in * wp. */ int cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane) { const char *errstr; int idx; struct window_pane *wp; u_int n; log_debug("%s: %s", __func__, pane); /* Check for pane ids starting with %. */ if (*pane == '%') { fs->wp = window_pane_find_by_id_str(pane); if (fs->wp == NULL || fs->wp->window != fs->w) return (-1); return (0); } /* Try special characters. */ if (strcmp(pane, "!") == 0) { if (fs->w->last == NULL) return (-1); fs->wp = fs->w->last; return (0); } else if (strcmp(pane, "{up-of}") == 0) { fs->wp = window_pane_find_up(fs->w->active); if (fs->wp == NULL) return (-1); return (0); } else if (strcmp(pane, "{down-of}") == 0) { fs->wp = window_pane_find_down(fs->w->active); if (fs->wp == NULL) return (-1); return (0); } else if (strcmp(pane, "{left-of}") == 0) { fs->wp = window_pane_find_left(fs->w->active); if (fs->wp == NULL) return (-1); return (0); } else if (strcmp(pane, "{right-of}") == 0) { fs->wp = window_pane_find_right(fs->w->active); if (fs->wp == NULL) return (-1); return (0); } /* Try as an offset. */ if (pane[0] == '+' || pane[0] == '-') { if (pane[1] != '\0') n = strtonum(pane + 1, 1, INT_MAX, NULL); else n = 1; wp = fs->w->active; if (pane[0] == '+') fs->wp = window_pane_next_by_number(fs->w, wp, n); else fs->wp = window_pane_previous_by_number(fs->w, wp, n); if (fs->wp != NULL) return (0); } /* Get pane by index. */ idx = strtonum(pane, 0, INT_MAX, &errstr); if (errstr == NULL) { fs->wp = window_pane_at_index(fs->w, idx); if (fs->wp != NULL) return (0); } /* Try as a description. */ fs->wp = window_find_string(fs->w, pane); if (fs->wp != NULL) return (0); return (-1); } /* Clear state. */ void cmd_find_clear_state(struct cmd_find_state *fs, struct cmd_q *cmdq, int flags) { memset(fs, 0, sizeof *fs); fs->cmdq = cmdq; fs->flags = flags; fs->idx = -1; } /* Check if a state if valid. */ int cmd_find_valid_state(struct cmd_find_state *fs) { struct winlink *wl; if (fs->s == NULL || fs->wl == NULL || fs->w == NULL || fs->wp == NULL) return (0); if (!session_alive(fs->s)) return (0); RB_FOREACH(wl, winlinks, &fs->s->windows) { if (wl->window == fs->w && wl == fs->wl) break; } if (wl == NULL) return (0); if (fs->w != fs->wl->window) return (0); if (!window_has_pane(fs->w, fs->wp)) return (0); return (window_pane_visible(fs->wp)); } /* Copy a state. */ void cmd_find_copy_state(struct cmd_find_state *dst, struct cmd_find_state *src) { dst->s = src->s; dst->wl = src->wl; dst->idx = src->idx; dst->w = src->w; dst->wp = src->wp; } /* Log the result. */ void cmd_find_log_state(const char *prefix, struct cmd_find_state *fs) { if (fs->s != NULL) log_debug("%s: s=$%u", prefix, fs->s->id); else log_debug("%s: s=none", prefix); if (fs->wl != NULL) { log_debug("%s: wl=%u %d w=@%u %s", prefix, fs->wl->idx, fs->wl->window == fs->w, fs->w->id, fs->w->name); } else log_debug("%s: wl=none", prefix); if (fs->wp != NULL) log_debug("%s: wp=%%%u", prefix, fs->wp->id); else log_debug("%s: wp=none", prefix); if (fs->idx != -1) log_debug("%s: idx=%d", prefix, fs->idx); else log_debug("%s: idx=none", prefix); } /* Find state from a session. */ int cmd_find_from_session(struct cmd_find_state *fs, struct session *s) { cmd_find_clear_state(fs, NULL, 0); fs->s = s; fs->wl = fs->s->curw; fs->w = fs->wl->window; fs->wp = fs->w->active; cmd_find_log_state(__func__, fs); return (0); } /* Find state from a winlink. */ int cmd_find_from_winlink(struct cmd_find_state *fs, struct session *s, struct winlink *wl) { cmd_find_clear_state(fs, NULL, 0); fs->s = s; fs->wl = wl; fs->w = wl->window; fs->wp = wl->window->active; cmd_find_log_state(__func__, fs); return (0); } /* Find state from a window. */ int cmd_find_from_window(struct cmd_find_state *fs, struct window *w) { cmd_find_clear_state(fs, NULL, 0); fs->w = w; if (cmd_find_best_session_with_window(fs) != 0) return (-1); if (cmd_find_best_winlink_with_window(fs) != 0) return (-1); cmd_find_log_state(__func__, fs); return (0); } /* Find state from a pane. */ int cmd_find_from_pane(struct cmd_find_state *fs, struct window_pane *wp) { if (cmd_find_from_window(fs, wp->window) != 0) return (-1); fs->wp = wp; cmd_find_log_state(__func__, fs); return (0); } /* Find current state. */ int cmd_find_current(struct cmd_find_state *fs, struct cmd_q *cmdq, int flags) { cmd_find_clear_state(fs, cmdq, flags); if (cmd_find_current_session(fs) != 0) { if (~flags & CMD_FIND_QUIET) cmdq_error(cmdq, "no current session"); return (-1); } return (0); } /* * Split target into pieces and resolve for the given type. Fills in the given * state. Returns 0 on success or -1 on error. */ int cmd_find_target(struct cmd_find_state *fs, struct cmd_find_state *current, struct cmd_q *cmdq, const char *target, enum cmd_find_type type, int flags) { struct mouse_event *m; char *colon, *period, *copy = NULL; const char *session, *window, *pane; /* Log the arguments. */ if (target == NULL) log_debug("%s: target none, type %d", __func__, type); else log_debug("%s: target %s, type %d", __func__, target, type); log_debug("%s: cmdq %p, flags %#x", __func__, cmdq, flags); /* Clear new state. */ cmd_find_clear_state(fs, cmdq, flags); /* Find current state. */ if (server_check_marked() && (flags & CMD_FIND_DEFAULT_MARKED)) fs->current = &marked_pane; else if (cmd_find_valid_state(&cmdq->current)) fs->current = &cmdq->current; else fs->current = current; /* An empty or NULL target is the current. */ if (target == NULL || *target == '\0') goto current; /* Mouse target is a plain = or {mouse}. */ if (strcmp(target, "=") == 0 || strcmp(target, "{mouse}") == 0) { m = &cmdq->item->mouse; switch (type) { case CMD_FIND_PANE: fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl); if (fs->wp != NULL) fs->w = fs->wl->window; break; case CMD_FIND_WINDOW: case CMD_FIND_SESSION: fs->wl = cmd_mouse_window(m, &fs->s); if (fs->wl != NULL) { fs->w = fs->wl->window; fs->wp = fs->w->active; } break; } if (fs->wp == NULL) { if (~flags & CMD_FIND_QUIET) cmdq_error(cmdq, "no mouse target"); goto error; } goto found; } /* Marked target is a plain ~ or {marked}. */ if (strcmp(target, "~") == 0 || strcmp(target, "{marked}") == 0) { if (!server_check_marked()) { if (~flags & CMD_FIND_QUIET) cmdq_error(cmdq, "no marked target"); goto error; } cmd_find_copy_state(fs, &marked_pane); goto found; } /* Find separators if they exist. */ copy = xstrdup(target); colon = strchr(copy, ':'); if (colon != NULL) *colon++ = '\0'; if (colon == NULL) period = strchr(copy, '.'); else period = strchr(colon, '.'); if (period != NULL) *period++ = '\0'; /* Set session, window and pane parts. */ session = window = pane = NULL; if (colon != NULL && period != NULL) { session = copy; window = colon; pane = period; } else if (colon != NULL && period == NULL) { session = copy; window = colon; } else if (colon == NULL && period != NULL) { window = copy; pane = period; } else { if (*copy == '$') session = copy; else if (*copy == '@') window = copy; else if (*copy == '%') pane = copy; else { switch (type) { case CMD_FIND_SESSION: session = copy; break; case CMD_FIND_WINDOW: window = copy; break; case CMD_FIND_PANE: pane = copy; break; } } } /* Set exact match flags. */ if (session != NULL && *session == '=') { session++; fs->flags |= CMD_FIND_EXACT_SESSION; } if (window != NULL && *window == '=') { window++; fs->flags |= CMD_FIND_EXACT_WINDOW; } /* Empty is the same as NULL. */ if (session != NULL && *session == '\0') session = NULL; if (window != NULL && *window == '\0') window = NULL; if (pane != NULL && *pane == '\0') pane = NULL; /* Map though conversion table. */ if (session != NULL) session = cmd_find_map_table(cmd_find_session_table, session); if (window != NULL) window = cmd_find_map_table(cmd_find_window_table, window); if (pane != NULL) pane = cmd_find_map_table(cmd_find_pane_table, pane); log_debug("target %s (flags %#x): session=%s, window=%s, pane=%s", target, flags, session == NULL ? "none" : session, window == NULL ? "none" : window, pane == NULL ? "none" : pane); /* No pane is allowed if want an index. */ if (pane != NULL && (flags & CMD_FIND_WINDOW_INDEX)) { if (~flags & CMD_FIND_QUIET) cmdq_error(cmdq, "can't specify pane here"); goto error; } /* If the session isn't NULL, look it up. */ if (session != NULL) { /* This will fill in session. */ if (cmd_find_get_session(fs, session) != 0) goto no_session; /* If window and pane are NULL, use that session's current. */ if (window == NULL && pane == NULL) { fs->wl = fs->s->curw; fs->idx = -1; fs->w = fs->wl->window; fs->wp = fs->w->active; goto found; } /* If window is present but pane not, find window in session. */ if (window != NULL && pane == NULL) { /* This will fill in winlink and window. */ if (cmd_find_get_window_with_session(fs, window) != 0) goto no_window; fs->wp = fs->wl->window->active; goto found; } /* If pane is present but window not, find pane. */ if (window == NULL && pane != NULL) { /* This will fill in winlink and window and pane. */ if (cmd_find_get_pane_with_session(fs, pane) != 0) goto no_pane; goto found; } /* * If window and pane are present, find both in session. This * will fill in winlink and window. */ if (cmd_find_get_window_with_session(fs, window) != 0) goto no_window; /* This will fill in pane. */ if (cmd_find_get_pane_with_window(fs, pane) != 0) goto no_pane; goto found; } /* No session. If window and pane, try them. */ if (window != NULL && pane != NULL) { /* This will fill in session, winlink and window. */ if (cmd_find_get_window(fs, window) != 0) goto no_window; /* This will fill in pane. */ if (cmd_find_get_pane_with_window(fs, pane) != 0) goto no_pane; goto found; } /* If just window is present, try it. */ if (window != NULL && pane == NULL) { /* This will fill in session, winlink and window. */ if (cmd_find_get_window(fs, window) != 0) goto no_window; fs->wp = fs->wl->window->active; goto found; } /* If just pane is present, try it. */ if (window == NULL && pane != NULL) { /* This will fill in session, winlink, window and pane. */ if (cmd_find_get_pane(fs, pane) != 0) goto no_pane; goto found; } current: /* Use the current session. */ cmd_find_copy_state(fs, fs->current); if (flags & CMD_FIND_WINDOW_INDEX) fs->idx = -1; goto found; error: fs->current = NULL; log_debug(" error"); free(copy); return (-1); found: fs->current = NULL; cmd_find_log_state(__func__, fs); free(copy); return (0); no_session: if (~flags & CMD_FIND_QUIET) cmdq_error(cmdq, "can't find session %s", session); goto error; no_window: if (~flags & CMD_FIND_QUIET) cmdq_error(cmdq, "can't find window %s", window); goto error; no_pane: if (~flags & CMD_FIND_QUIET) cmdq_error(cmdq, "can't find pane %s", pane); goto error; } /* Find the target client or report an error and return NULL. */ struct client * cmd_find_client(struct cmd_q *cmdq, const char *target, int quiet) { struct client *c; char *copy; size_t size; const char *path; /* A NULL argument means the current client. */ if (cmdq != NULL && target == NULL) { c = cmd_find_current_client(cmdq); if (c == NULL && !quiet) cmdq_error(cmdq, "no current client"); log_debug("%s: no target, return %p", __func__, c); return (c); } copy = xstrdup(target); /* Trim a single trailing colon if any. */ size = strlen(copy); if (size != 0 && copy[size - 1] == ':') copy[size - 1] = '\0'; /* Check path of each client. */ TAILQ_FOREACH(c, &clients, entry) { if (c->session == NULL || c->tty.path == NULL) continue; path = c->tty.path; /* Try for exact match. */ if (strcmp(copy, path) == 0) break; /* Try without leading /dev. */ if (strncmp(path, _PATH_DEV, (sizeof _PATH_DEV) - 1) != 0) continue; if (strcmp(copy, path + (sizeof _PATH_DEV) - 1) == 0) break; } /* If no client found, report an error. */ if (c == NULL && !quiet) cmdq_error(cmdq, "can't find client %s", copy); free(copy); log_debug("%s: target %s, return %p", __func__, target, c); return (c); } tmate-2.4.0/cmd-if-shell.c000066400000000000000000000112331356407164200152470ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Tiago Cunha * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" /* * Executes a tmux command if a shell command returns true or false. */ 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 = { .name = "if-shell", .alias = "if", .args = { "bFt:", 2, 3 }, .usage = "[-bF] " CMD_TARGET_PANE_USAGE " shell-command command " "[command]", .tflag = CMD_PANE_CANFAIL, .flags = 0, .exec = cmd_if_shell_exec }; struct cmd_if_shell_data { char *cmd_if; char *cmd_else; struct cmd_q *cmdq; struct mouse_event mouse; int bflag; int references; }; 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, *cmd, *cause; struct cmd_list *cmdlist; struct session *s = cmdq->state.tflag.s; struct winlink *wl = cmdq->state.tflag.wl; struct window_pane *wp = cmdq->state.tflag.wp; struct format_tree *ft; const char *cwd; if (cmdq->client != NULL && cmdq->client->session == NULL) cwd = cmdq->client->cwd; else if (s != NULL) cwd = s->cwd; else cwd = NULL; ft = format_create(cmdq, 0); format_defaults(ft, NULL, s, wl, wp); shellcmd = format_expand(ft, args->argv[0]); format_free(ft); if (args_has(args, 'F')) { cmd = NULL; if (*shellcmd != '0' && *shellcmd != '\0') cmd = args->argv[1]; else if (args->argc == 3) cmd = args->argv[2]; free(shellcmd); if (cmd == NULL) return (CMD_RETURN_NORMAL); if (cmd_string_parse(cmd, &cmdlist, NULL, 0, &cause) != 0) { if (cause != NULL) { cmdq_error(cmdq, "%s", cause); free(cause); } return (CMD_RETURN_ERROR); } cmdq_run(cmdq, cmdlist, &cmdq->item->mouse); cmd_list_free(cmdlist); return (CMD_RETURN_NORMAL); } 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->cmdq = cmdq; memcpy(&cdata->mouse, &cmdq->item->mouse, sizeof cdata->mouse); cmdq->references++; cdata->references = 1; job_run(shellcmd, s, cwd, 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->flags & CMD_Q_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; } cmdq1 = cmdq_new(cmdq->client); cmdq1->emptyfn = cmd_if_shell_done; cmdq1->data = cdata; cdata->references++; cmdq_run(cmdq1, cmdlist, &cdata->mouse); 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 (cmdq1->client_exit >= 0) cmdq->client_exit = cmdq1->client_exit; cmdq_free(cmdq1); if (--cdata->references != 0) return; if (!cmdq_free(cmdq) && !cdata->bflag) cmdq_continue(cmdq); 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->references != 0) return; if (!cmdq_free(cmdq) && !cdata->bflag) cmdq_continue(cmdq); free(cdata->cmd_else); free(cdata->cmd_if); free(cdata); } tmate-2.4.0/cmd-join-pane.c000066400000000000000000000104511356407164200154250ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2011 George Nachman * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Join or move a pane into another (like split/swap/kill). */ 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 = { .name = "join-pane", .alias = "joinp", .args = { "bdhvp:l:s:t:", 0, 0 }, .usage = "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, .sflag = CMD_PANE_MARKED, .tflag = CMD_PANE, .flags = 0, .exec = cmd_join_pane_exec }; const struct cmd_entry cmd_move_pane_entry = { .name = "move-pane", .alias = "movep", .args = { "bdhvp:l:s:t:", 0, 0 }, .usage = "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE, .sflag = CMD_PANE, .tflag = CMD_PANE, .flags = 0, .exec = cmd_join_pane_exec }; 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_s = cmdq->state.tflag.s; dst_wl = cmdq->state.tflag.wl; dst_wp = cmdq->state.tflag.wp; dst_w = dst_wl->window; dst_idx = dst_wl->idx; server_unzoom_window(dst_w); src_wl = cmdq->state.sflag.wl; src_wp = cmdq->state.sflag.wp; 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); window_lost_pane(src_w, src_wp); 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-2.4.0/cmd-kill-pane.c000066400000000000000000000036461356407164200154310ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Kill pane. */ enum cmd_retval cmd_kill_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_kill_pane_entry = { .name = "kill-pane", .alias = "killp", .args = { "at:", 0, 0 }, .usage = "[-a] " CMD_TARGET_PANE_USAGE, .tflag = CMD_PANE, .flags = 0, .exec = cmd_kill_pane_exec }; enum cmd_retval cmd_kill_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct winlink *wl = cmdq->state.tflag.wl; struct window_pane *loopwp, *tmpwp, *wp = cmdq->state.tflag.wp; 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-2.4.0/cmd-kill-server.c000066400000000000000000000030661356407164200160100ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Kill the server and do nothing else. */ enum cmd_retval cmd_kill_server_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_kill_server_entry = { .name = "kill-server", .alias = NULL, .args = { "", 0, 0 }, .usage = "", .flags = 0, .exec = cmd_kill_server_exec }; const struct cmd_entry cmd_start_server_entry = { .name = "start-server", .alias = "start", .args = { "", 0, 0 }, .usage = "", .flags = CMD_STARTSERVER, .exec = cmd_kill_server_exec }; enum cmd_retval cmd_kill_server_exec(struct cmd *self, __unused struct cmd_q *cmdq) { if (self->entry == &cmd_kill_server_entry) kill(getpid(), SIGTERM); return (CMD_RETURN_NORMAL); } tmate-2.4.0/cmd-kill-session.c000066400000000000000000000037661356407164200161740ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "tmux.h" /* * Destroy session, detaching all clients attached to it and destroying any * windows linked only to this session. * * Note this deliberately has no alias to make it hard to hit by accident. */ enum cmd_retval cmd_kill_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_kill_session_entry = { .name = "kill-session", .alias = NULL, .args = { "aCt:", 0, 0 }, .usage = "[-aC] " CMD_TARGET_SESSION_USAGE, .tflag = CMD_SESSION, .flags = 0, .exec = 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, *sloop, *stmp; struct winlink *wl; s = cmdq->state.tflag.s; if (args_has(args, 'C')) { RB_FOREACH(wl, winlinks, &s->windows) { wl->window->flags &= ~WINDOW_ALERTFLAGS; wl->flags &= ~WINLINK_ALERTFLAGS; } server_redraw_session(s); } else if (args_has(args, 'a')) { RB_FOREACH_SAFE(sloop, sessions, &sessions, stmp) { if (sloop != s) { server_destroy_session(sloop); session_destroy(sloop); } } } else { server_destroy_session(s); session_destroy(s); } return (CMD_RETURN_NORMAL); } tmate-2.4.0/cmd-kill-window.c000066400000000000000000000042021356407164200160020ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "tmux.h" /* * Destroy window. */ enum cmd_retval cmd_kill_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_kill_window_entry = { .name = "kill-window", .alias = "killw", .args = { "at:", 0, 0 }, .usage = "[-a] " CMD_TARGET_WINDOW_USAGE, .tflag = CMD_WINDOW, .flags = 0, .exec = cmd_kill_window_exec }; const struct cmd_entry cmd_unlink_window_entry = { .name = "unlink-window", .alias = "unlinkw", .args = { "kt:", 0, 0 }, .usage = "[-k] " CMD_TARGET_WINDOW_USAGE, .tflag = CMD_WINDOW, .flags = 0, .exec = 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 = cmdq->state.tflag.wl, *wl2, *wl3; struct window *w = wl->window; struct session *s = cmdq->state.tflag.s; if (self->entry == &cmd_unlink_window_entry) { if (!args_has(self->args, 'k') && !session_is_linked(s, w)) { cmdq_error(cmdq, "window only linked to one session"); return (CMD_RETURN_ERROR); } server_unlink_window(s, wl); } else { if (args_has(args, 'a')) { RB_FOREACH_SAFE(wl2, winlinks, &s->windows, wl3) { if (wl != wl2) server_kill_window(wl2->window); } } else server_kill_window(wl->window); } recalculate_sizes(); return (CMD_RETURN_NORMAL); } tmate-2.4.0/cmd-list-buffers.c000066400000000000000000000035031356407164200161520ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * List paste buffers. */ #define LIST_BUFFERS_TEMPLATE \ "#{buffer_name}: #{buffer_size} bytes: \"#{buffer_sample}\"" enum cmd_retval cmd_list_buffers_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_list_buffers_entry = { .name = "list-buffers", .alias = "lsb", .args = { "F:", 0, 0 }, .usage = "[-F format]", .flags = 0, .exec = cmd_list_buffers_exec }; enum cmd_retval cmd_list_buffers_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct paste_buffer *pb; struct format_tree *ft; char *line; const char *template; if ((template = args_get(args, 'F')) == NULL) template = LIST_BUFFERS_TEMPLATE; pb = NULL; while ((pb = paste_walk(pb)) != NULL) { ft = format_create(cmdq, 0); format_defaults_paste_buffer(ft, pb); line = format_expand(ft, template); cmdq_print(cmdq, "%s", line); free(line); format_free(ft); } return (CMD_RETURN_NORMAL); } tmate-2.4.0/cmd-list-clients.c000066400000000000000000000043111356407164200161550ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" /* * List all clients. */ #define LIST_CLIENTS_TEMPLATE \ "#{client_tty}: #{session_name} " \ "[#{client_width}x#{client_height} #{client_termname}]" \ "#{?client_utf8, (utf8),} #{?client_readonly, (ro),}" enum cmd_retval cmd_list_clients_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_list_clients_entry = { .name = "list-clients", .alias = "lsc", .args = { "F:t:", 0, 0 }, .usage = "[-F format] " CMD_TARGET_SESSION_USAGE, .tflag = CMD_SESSION, .flags = CMD_READONLY, .exec = 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 idx; char *line; if (args_has(args, 't')) s = cmdq->state.tflag.s; else s = NULL; if ((template = args_get(args, 'F')) == NULL) template = LIST_CLIENTS_TEMPLATE; idx = 0; TAILQ_FOREACH(c, &clients, entry) { if (c->session == NULL || (s != NULL && s != c->session)) continue; ft = format_create(cmdq, 0); format_add(ft, "line", "%u", idx); format_defaults(ft, c, NULL, NULL, NULL); line = format_expand(ft, template); cmdq_print(cmdq, "%s", line); free(line); format_free(ft); idx++; } return (CMD_RETURN_NORMAL); } tmate-2.4.0/cmd-list-keys.c000066400000000000000000000123361356407164200154750ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * List key bindings. */ enum cmd_retval cmd_list_keys_exec(struct cmd *, struct cmd_q *); enum cmd_retval cmd_list_keys_table(struct cmd *, struct cmd_q *); enum cmd_retval cmd_list_keys_commands(struct cmd_q *); const struct cmd_entry cmd_list_keys_entry = { .name = "list-keys", .alias = "lsk", .args = { "t:T:", 0, 0 }, .usage = "[-t mode-table] [-T key-table]", .flags = CMD_STARTSERVER, .exec = cmd_list_keys_exec }; const struct cmd_entry cmd_list_commands_entry = { .name = "list-commands", .alias = "lscm", .args = { "", 0, 0 }, .usage = "", .flags = CMD_STARTSERVER, .exec = 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_table *table; struct key_binding *bd; const char *key, *tablename, *r; char *cp, tmp[BUFSIZ]; int repeat, width, tablewidth, keywidth; if (self->entry == &cmd_list_commands_entry) return (cmd_list_keys_commands(cmdq)); #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)); tablename = args_get(args, 'T'); if (tablename != NULL && key_bindings_get_table(tablename, 0) == NULL) { cmdq_error(cmdq, "table %s doesn't exist", tablename); return (CMD_RETURN_ERROR); } repeat = 0; tablewidth = keywidth = 0; RB_FOREACH(table, key_tables, &key_tables) { if (tablename != NULL && strcmp(table->name, tablename) != 0) continue; RB_FOREACH(bd, key_bindings, &table->key_bindings) { key = key_string_lookup_key(bd->key); if (bd->can_repeat) repeat = 1; width = utf8_cstrwidth(table->name); if (width > tablewidth) tablewidth = width; width = utf8_cstrwidth(key); if (width > keywidth) keywidth = width; } } RB_FOREACH(table, key_tables, &key_tables) { if (tablename != NULL && strcmp(table->name, tablename) != 0) continue; RB_FOREACH(bd, key_bindings, &table->key_bindings) { key = key_string_lookup_key(bd->key); if (!repeat) r = ""; else if (bd->can_repeat) r = "-r "; else r = " "; xsnprintf(tmp, sizeof tmp, "%s-T ", r); cp = utf8_padcstr(table->name, tablewidth); strlcat(tmp, cp, sizeof tmp); strlcat(tmp, " ", sizeof tmp); free(cp); cp = utf8_padcstr(key, keywidth); strlcat(tmp, cp, sizeof tmp); strlcat(tmp, " ", sizeof tmp); free(cp); cp = cmd_list_print(bd->cmdlist); strlcat(tmp, cp, sizeof tmp); free(cp); 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 (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); 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); } enum cmd_retval cmd_list_keys_commands(struct cmd_q *cmdq) { const struct cmd_entry **entryp; const struct cmd_entry *entry; for (entryp = cmd_table; *entryp != NULL; entryp++) { entry = *entryp; if (entry->alias == NULL) { cmdq_print(cmdq, "%s %s", entry->name, entry->usage); continue; } cmdq_print(cmdq, "%s (%s) %s", entry->name, entry->alias, entry->usage); } return (CMD_RETURN_NORMAL); } tmate-2.4.0/cmd-list-panes.c000066400000000000000000000072051356407164200156270ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * List panes on given window. */ 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 = { .name = "list-panes", .alias = "lsp", .args = { "asF:t:", 0, 0 }, .usage = "[-as] [-F format] " CMD_TARGET_WINDOW_USAGE, .tflag = CMD_WINDOW, .flags = 0, .exec = 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 = cmdq->state.tflag.s; struct winlink *wl = cmdq->state.tflag.wl; if (args_has(args, 'a')) cmd_list_panes_server(self, cmdq); else if (args_has(args, 's')) cmd_list_panes_session(self, s, cmdq, 1); else 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(cmdq, 0); format_add(ft, "line", "%u", n); format_defaults(ft, NULL, s, wl, wp); line = format_expand(ft, template); cmdq_print(cmdq, "%s", line); free(line); format_free(ft); n++; } } tmate-2.4.0/cmd-list-sessions.c000066400000000000000000000041031356407164200163610ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" /* * List all sessions. */ #define LIST_SESSIONS_TEMPLATE \ "#{session_name}: #{session_windows} windows " \ "(created #{t:session_created}) " \ "[#{session_width}x#{session_height}]" \ "#{?session_grouped, (group ,}" \ "#{session_group}#{?session_grouped,),}" \ "#{?session_attached, (attached),}" enum cmd_retval cmd_list_sessions_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_list_sessions_entry = { .name = "list-sessions", .alias = "ls", .args = { "F:", 0, 0 }, .usage = "[-F format]", .flags = 0, .exec = 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(cmdq, 0); format_add(ft, "line", "%u", n); format_defaults(ft, NULL, s, NULL, NULL); line = format_expand(ft, template); cmdq_print(cmdq, "%s", line); free(line); format_free(ft); n++; } return (CMD_RETURN_NORMAL); } tmate-2.4.0/cmd-list-windows.c000066400000000000000000000057451356407164200162220ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * List windows on given session. */ #define LIST_WINDOWS_TEMPLATE \ "#{window_index}: #{window_name}#{window_flags} " \ "(#{window_panes} panes) " \ "[#{window_width}x#{window_height}] " \ "[layout #{window_layout}] #{window_id}" \ "#{?window_active, (active),}"; #define LIST_WINDOWS_WITH_SESSION_TEMPLATE \ "#{session_name}:" \ "#{window_index}: #{window_name}#{window_flags} " \ "(#{window_panes} panes) " \ "[#{window_width}x#{window_height}] " 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 = { .name = "list-windows", .alias = "lsw", .args = { "F:at:", 0, 0 }, .usage = "[-a] [-F format] " CMD_TARGET_SESSION_USAGE, .tflag = CMD_SESSION, .flags = 0, .exec = cmd_list_windows_exec }; enum cmd_retval cmd_list_windows_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; if (args_has(args, 'a')) cmd_list_windows_server(self, cmdq); else cmd_list_windows_session(self, cmdq->state.tflag.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(cmdq, 0); format_add(ft, "line", "%u", n); format_defaults(ft, NULL, s, wl, NULL); line = format_expand(ft, template); cmdq_print(cmdq, "%s", line); free(line); format_free(ft); n++; } } tmate-2.4.0/cmd-list.c000066400000000000000000000054721356407164200145270ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" 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); } char * cmd_list_print(struct cmd_list *cmdlist) { struct cmd *cmd; char *buf, *this; size_t len; len = 1; buf = xcalloc(1, len); TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { this = cmd_print(cmd); len += strlen(this) + 3; buf = xrealloc(buf, len); strlcat(buf, this, len); if (TAILQ_NEXT(cmd, qentry) != NULL) strlcat(buf, " ; ", len); free(this); } return (buf); } tmate-2.4.0/cmd-load-buffer.c000066400000000000000000000101511356407164200157300ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Tiago Cunha * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include "tmux.h" /* * Loads a paste buffer from a file. */ 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 = { .name = "load-buffer", .alias = "loadb", .args = { "b:", 1, 1 }, .usage = CMD_BUFFER_USAGE " path", .flags = 0, .exec = 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, *bufname, *cwd; char *pdata, *new_pdata, *cause, *file, resolved[PATH_MAX]; size_t psize; int ch, error; bufname = NULL; if (args_has(args, 'b')) bufname = args_get(args, 'b'); path = args->argv[0]; if (strcmp(path, "-") == 0) { error = server_set_stdin_callback(c, cmd_load_buffer_callback, (void *)bufname, &cause); if (error != 0) { cmdq_error(cmdq, "%s: %s", path, cause); free(cause); return (CMD_RETURN_ERROR); } return (CMD_RETURN_WAIT); } if (c != NULL && c->session == NULL && c->cwd != NULL) cwd = c->cwd; else if ((s = c->session) != NULL && s->cwd != NULL) cwd = s->cwd; else cwd = "."; if (*path == '/') file = xstrdup(path); else xasprintf(&file, "%s/%s", cwd, path); if (realpath(file, resolved) == NULL && strlcpy(resolved, file, sizeof resolved) >= sizeof resolved) { cmdq_error(cmdq, "%s: %s", file, strerror(ENAMETOOLONG)); return (CMD_RETURN_ERROR); } f = fopen(resolved, "rb"); free(file); if (f == NULL) { cmdq_error(cmdq, "%s: %s", resolved, 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", resolved); goto error; } if (pdata != NULL) pdata[psize] = '\0'; fclose(f); if (paste_set(pdata, psize, bufname, &cause) != 0) { cmdq_error(cmdq, "%s", cause); free(pdata); free(cause); 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) { const char *bufname = data; char *pdata, *cause, *saved; size_t psize; if (!closed) return; c->stdin_callback = NULL; server_client_unref(c); if (c->flags & CLIENT_DEAD) return; psize = EVBUFFER_LENGTH(c->stdin_data); if (psize == 0 || (pdata = malloc(psize + 1)) == NULL) goto out; memcpy(pdata, EVBUFFER_DATA(c->stdin_data), psize); pdata[psize] = '\0'; evbuffer_drain(c->stdin_data, psize); if (paste_set(pdata, psize, bufname, &cause) != 0) { /* No context so can't use server_client_msg_error. */ if (~c->flags & CLIENT_UTF8) { saved = cause; cause = utf8_sanitize(saved); free(saved); } evbuffer_add_printf(c->stderr_data, "%s", cause); server_client_push_stderr(c); free(pdata); free(cause); } out: cmdq_continue(c->cmdq); } tmate-2.4.0/cmd-lock-server.c000066400000000000000000000036211356407164200160020ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "tmux.h" /* * Lock commands. */ enum cmd_retval cmd_lock_server_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_lock_server_entry = { .name = "lock-server", .alias = "lock", .args = { "", 0, 0 }, .usage = "", .flags = 0, .exec = cmd_lock_server_exec }; const struct cmd_entry cmd_lock_session_entry = { .name = "lock-session", .alias = "locks", .args = { "t:", 0, 0 }, .usage = CMD_TARGET_SESSION_USAGE, .tflag = CMD_SESSION, .flags = 0, .exec = cmd_lock_server_exec }; const struct cmd_entry cmd_lock_client_entry = { .name = "lock-client", .alias = "lockc", .args = { "t:", 0, 0 }, .usage = CMD_TARGET_CLIENT_USAGE, .tflag = CMD_CLIENT, .flags = 0, .exec = cmd_lock_server_exec }; enum cmd_retval cmd_lock_server_exec(struct cmd *self, __unused struct cmd_q *cmdq) { if (self->entry == &cmd_lock_server_entry) server_lock(); else if (self->entry == &cmd_lock_session_entry) server_lock_session(cmdq->state.tflag.s); else server_lock_client(cmdq->state.c); recalculate_sizes(); return (CMD_RETURN_NORMAL); } tmate-2.4.0/cmd-move-window.c000066400000000000000000000056231356407164200160250ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Move a window. */ enum cmd_retval cmd_move_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_move_window_entry = { .name = "move-window", .alias = "movew", .args = { "adkrs:t:", 0, 0 }, .usage = "[-dkr] " CMD_SRCDST_WINDOW_USAGE, .sflag = CMD_WINDOW, .tflag = CMD_MOVEW_R, .flags = 0, .exec = cmd_move_window_exec }; const struct cmd_entry cmd_link_window_entry = { .name = "link-window", .alias = "linkw", .args = { "adks:t:", 0, 0 }, .usage = "[-dk] " CMD_SRCDST_WINDOW_USAGE, .sflag = CMD_WINDOW, .tflag = CMD_WINDOW_INDEX, .flags = 0, .exec = 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 = cmdq->state.sflag.s; struct session *dst = cmdq->state.tflag.s; struct winlink *wl = cmdq->state.sflag.wl; char *cause; int idx = cmdq->state.tflag.idx, kflag, dflag, sflag; kflag = args_has(self->args, 'k'); dflag = args_has(self->args, 'd'); if (args_has(args, 'r')) { session_renumber_windows(dst); recalculate_sizes(); return (CMD_RETURN_NORMAL); } kflag = args_has(self->args, 'k'); dflag = args_has(self->args, 'd'); sflag = args_has(self->args, 's'); if (args_has(self->args, 'a')) { if ((idx = winlink_shuffle_up(dst, dst->curw)) == -1) return (CMD_RETURN_ERROR); } if (server_link_window(src, wl, dst, idx, kflag, !dflag, &cause) != 0) { cmdq_error(cmdq, "can't link window: %s", cause); free(cause); return (CMD_RETURN_ERROR); } if (self->entry == &cmd_move_window_entry) server_unlink_window(src, wl); /* * Renumber the winlinks in the src session only, the destination * session already has the correct winlink id to us, either * automatically or specified by -s. */ if (!sflag && options_get_number(src->options, "renumber-windows")) session_renumber_windows(src); recalculate_sizes(); return (CMD_RETURN_NORMAL); #endif } tmate-2.4.0/cmd-new-session.c000066400000000000000000000210001356407164200160070ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include "tmux.h" /* * Create a new session and attach to the current terminal unless -d is given. */ #define NEW_SESSION_TEMPLATE "#{session_name}:" enum cmd_retval cmd_new_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_new_session_entry = { .name = "new-session", .alias = "new", .args = { "Ac:dDEF:n:Ps:t:x:y:", 0, -1 }, .usage = "[-AdDEP] [-c start-directory] [-F format] [-n window-name] " "[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] " "[-y height] [command]", .tflag = CMD_SESSION_CANFAIL, .flags = CMD_STARTSERVER, .exec = cmd_new_session_exec }; const struct cmd_entry cmd_has_session_entry = { .name = "has-session", .alias = "has", .args = { "t:", 0, 0 }, .usage = CMD_TARGET_SESSION_USAGE, .tflag = CMD_SESSION, .flags = 0, .exec = cmd_new_session_exec }; 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, *as; struct session *groupwith = cmdq->state.tflag.s; struct window *w; struct environ *env; struct termios tio, *tiop; const char *newname, *target, *update, *errstr, *template; const char *path, *cwd, *to_free = NULL; char **argv, *cmd, *cause, *cp; int detached, already_attached, idx, argc; u_int sx, sy; struct format_tree *ft; struct environ_entry *envent; if (self->entry == &cmd_has_session_entry) { /* * cmd_prepare() will fail if the session cannot be found, * hence always return success here. */ return (CMD_RETURN_NORMAL); } if (args_has(args, 't') && (args->argc != 0 || args_has(args, 'n'))) { cmdq_error(cmdq, "command or window name given with target"); return (CMD_RETURN_ERROR); } 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 ((as = session_find(newname)) != NULL) { if (args_has(args, 'A')) { /* * This cmdq is now destined for * attach-session. Because attach-session * will have already been prepared, copy this * session into its tflag so it can be used. */ cmd_find_from_session(&cmdq->state.tflag, as); return (cmd_attach_session(cmdq, args_has(args, 'D'), 0, NULL, args_has(args, 'E'))); } cmdq_error(cmdq, "duplicate session: %s", newname); return (CMD_RETURN_ERROR); } } if ((target = args_get(args, 't')) != NULL) { if (groupwith == NULL) { cmdq_error(cmdq, "no such session: %s", target); goto error; } } else groupwith = NULL; /* Set -d if no client. */ detached = args_has(args, 'd'); if (c == NULL) detached = 1; if (tmate_foreground) detached = 1; /* Is this client already attached? */ already_attached = 0; if (c != NULL && c->session != NULL) already_attached = 1; /* Get the new session working directory. */ if (args_has(args, 'c')) { ft = format_create(cmdq, 0); format_defaults(ft, c, NULL, NULL, NULL); to_free = cwd = format_expand(ft, args_get(args, 'c')); format_free(ft); } else if (c != NULL && c->session == NULL && c->cwd != NULL) cwd = c->cwd; else cwd = "."; /* * If this is a new client, check for nesting and save the termios * settings (part of which is used for new windows in this session). * * tcgetattr() is used rather than using tty.tio since if the client is * detached, tty_open won't be called. It must be done before opening * the terminal as that calls tcsetattr() to prepare for tmux taking * over. */ if (!detached && !already_attached && c->tty.fd != -1) { if (server_client_check_nested(cmdq->client)) { cmdq_error(cmdq, "sessions should be nested with care, " "unset $TMUX to force"); return (CMD_RETURN_ERROR); } if (tcgetattr(c->tty.fd, &tio) != 0) fatal("tcgetattr failed"); tiop = &tio; } else tiop = NULL; /* Open the terminal if necessary. */ if (!detached && !already_attached) { if (server_client_open(c, &cause) != 0) { cmdq_error(cmdq, "open terminal failed: %s", cause); free(cause); goto error; } } /* 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); goto 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); goto 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. */ argc = -1; argv = NULL; if (!args_has(args, 't') && args->argc != 0) { argc = args->argc; argv = args->argv; } else if (groupwith == NULL) { cmd = options_get_string(global_s_options, "default-command"); if (cmd != NULL && *cmd != '\0') { argc = 1; argv = &cmd; } else { argc = 0; argv = NULL; } } path = NULL; if (c != NULL && c->session == NULL) envent = environ_find(c->environ, "PATH"); else envent = environ_find(global_environ, "PATH"); if (envent != NULL) path = envent->value; /* Construct the environment. */ env = environ_create(); if (c != NULL && !args_has(args, 'E')) { update = options_get_string(global_s_options, "update-environment"); environ_update(update, c->environ, env); } /* Create the new session. */ idx = -1 - options_get_number(global_s_options, "base-index"); s = session_create(newname, argc, argv, path, cwd, env, tiop, idx, sx, sy, &cause); environ_free(env); if (s == NULL) { cmdq_error(cmdq, "create session failed: %s", cause); free(cause); goto error; } /* Set the initial window name if one given. */ if (argc >= 0 && 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_MIN(winlinks, &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) { if (~c->flags & CLIENT_CONTROL) proc_send(c->peer, MSG_READY, -1, NULL, 0); } else if (c->session != NULL) c->last_session = c->session; c->session = s; server_client_set_key_table(c, NULL); status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s, NULL); gettimeofday(&s->last_attached_time, NULL); server_redraw_client(c); } recalculate_sizes(); server_update_socket(); /* * If there are still configuration file errors to display, put the new * session's current window into more mode and display them now. */ if (cfg_finished) cfg_show_causes(s); /* Print if requested. */ if (args_has(args, 'P')) { if ((template = args_get(args, 'F')) == NULL) template = NEW_SESSION_TEMPLATE; ft = format_create(cmdq, 0); format_defaults(ft, c, s, NULL, NULL); cp = format_expand(ft, template); cmdq_print(cmdq, "%s", cp); free(cp); format_free(ft); } if (!detached) cmdq->client_exit = 0; if (to_free != NULL) free((void *)to_free); return (CMD_RETURN_NORMAL); error: if (to_free != NULL) free((void *)to_free); return (CMD_RETURN_ERROR); } tmate-2.4.0/cmd-new-window.c000066400000000000000000000101511356407164200156400ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "tmux.h" /* * Create a new window. */ #define NEW_WINDOW_TEMPLATE "#{session_name}:#{window_index}.#{pane_index}" enum cmd_retval cmd_new_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_new_window_entry = { .name = "new-window", .alias = "neww", .args = { "ac:dF:kn:Pt:", 0, -1 }, .usage = "[-adkP] [-c start-directory] [-F format] [-n window-name] " CMD_TARGET_WINDOW_USAGE " [command]", .tflag = CMD_WINDOW_INDEX, .flags = 0, .exec = 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 = cmdq->state.tflag.s; struct winlink *wl = cmdq->state.tflag.wl; struct client *c = cmdq->state.c; int idx = cmdq->state.tflag.idx; const char *cmd, *path, *template, *cwd, *to_free; char **argv, *cause, *cp; int argc, detached; struct format_tree *ft; struct environ_entry *envent; if (args_has(args, 'a')) { if ((idx = winlink_shuffle_up(s, wl)) == -1) { cmdq_error(cmdq, "no free window indexes"); return (CMD_RETURN_ERROR); } } detached = args_has(args, 'd'); if (args->argc == 0) { cmd = options_get_string(s->options, "default-command"); if (cmd != NULL && *cmd != '\0') { argc = 1; argv = (char **)&cmd; } else { argc = 0; argv = NULL; } } else { argc = args->argc; argv = args->argv; } path = NULL; if (cmdq->client != NULL && cmdq->client->session == NULL) envent = environ_find(cmdq->client->environ, "PATH"); else envent = environ_find(s->environ, "PATH"); if (envent != NULL) path = envent->value; to_free = NULL; if (args_has(args, 'c')) { ft = format_create(cmdq, 0); format_defaults(ft, c, s, NULL, NULL); cwd = to_free = format_expand(ft, args_get(args, 'c')); format_free(ft); } else if (cmdq->client != NULL && cmdq->client->session == NULL) cwd = cmdq->client->cwd; else cwd = s->cwd; 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 (idx == -1) idx = -1 - options_get_number(s->options, "base-index"); wl = session_new(s, args_get(args, 'n'), argc, argv, path, cwd, idx, &cause); if (wl == NULL) { cmdq_error(cmdq, "create window failed: %s", cause); free(cause); goto 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(cmdq, 0); format_defaults(ft, c, s, wl, NULL); cp = format_expand(ft, template); cmdq_print(cmdq, "%s", cp); free(cp); format_free(ft); } if (to_free != NULL) free((void *)to_free); return (CMD_RETURN_NORMAL); error: if (to_free != NULL) free((void *)to_free); return (CMD_RETURN_ERROR); } tmate-2.4.0/cmd-paste-buffer.c000066400000000000000000000055251356407164200161360ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Paste paste buffer if present. */ 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 = { .name = "paste-buffer", .alias = "pasteb", .args = { "db:prs:t:", 0, 0 }, .usage = "[-dpr] [-s separator] " CMD_BUFFER_USAGE " " CMD_TARGET_PANE_USAGE, .tflag = CMD_PANE, .flags = 0, .exec = 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 = cmdq->state.tflag.wp; struct paste_buffer *pb; const char *sepstr, *bufname, *bufdata, *bufend, *line; size_t seplen, bufsize; int bracket = args_has(args, 'p'); bufname = NULL; if (args_has(args, 'b')) bufname = args_get(args, 'b'); if (bufname == NULL) pb = paste_get_top(NULL); else { pb = paste_get_name(bufname); if (pb == NULL) { cmdq_error(cmdq, "no buffer %s", bufname); return (CMD_RETURN_ERROR); } } if (pb != NULL && ~wp->flags & PANE_INPUTOFF) { sepstr = args_get(args, 's'); if (sepstr == NULL) { if (args_has(args, 'r')) sepstr = "\n"; else sepstr = "\r"; } seplen = strlen(sepstr); if (bracket && (wp->screen->mode & MODE_BRACKETPASTE)) bufferevent_write(wp->event, "\033[200~", 6); bufdata = paste_buffer_data(pb, &bufsize); bufend = bufdata + bufsize; for (;;) { line = memchr(bufdata, '\n', bufend - bufdata); if (line == NULL) break; bufferevent_write(wp->event, bufdata, line - bufdata); bufferevent_write(wp->event, sepstr, seplen); bufdata = line + 1; } if (bufdata != bufend) bufferevent_write(wp->event, bufdata, bufend - bufdata); if (bracket && (wp->screen->mode & MODE_BRACKETPASTE)) bufferevent_write(wp->event, "\033[201~", 6); } if (pb != NULL && args_has(args, 'd')) paste_free(pb); return (CMD_RETURN_NORMAL); } tmate-2.4.0/cmd-pipe-pane.c000066400000000000000000000076251356407164200154340ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include "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 = { .name = "pipe-pane", .alias = "pipep", .args = { "ot:", 0, 1 }, .usage = "[-o] " CMD_TARGET_PANE_USAGE " [command]", .tflag = CMD_PANE, .flags = 0, .exec = 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 = cmdq->state.c; struct window_pane *wp = cmdq->state.tflag.wp; struct session *s = cmdq->state.tflag.s; struct winlink *wl = cmdq->state.tflag.wl; char *cmd; int old_fd, pipe_fd[2], null_fd; struct format_tree *ft; /* 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); } /* Expand the command. */ ft = format_create(cmdq, 0); format_defaults(ft, c, s, wl, wp); cmd = format_expand_time(ft, args->argv[0], time(NULL)); format_free(ft); /* Fork the child. */ switch (fork()) { case -1: cmdq_error(cmdq, "fork error: %s", strerror(errno)); free(cmd); 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); execl(_PATH_BSHELL, "sh", "-c", cmd, (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); free(cmd); 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-2.4.0/cmd-queue.c000066400000000000000000000152211356407164200146710ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2013 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include "tmux.h" #include "tmate.h" static enum cmd_retval cmdq_continue_one(struct cmd_q *); /* 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->flags = 0; cmdq->client = c; cmdq->client_exit = -1; TAILQ_INIT(&cmdq->queue); cmdq->item = NULL; cmdq->cmd = NULL; cmd_find_clear_state(&cmdq->current, NULL, 0); cmdq->parent = NULL; return (cmdq); } /* Free command queue */ int cmdq_free(struct cmd_q *cmdq) { if (--cmdq->references != 0) { if (cmdq->flags & CMD_Q_DEAD) return (1); return (0); } cmdq_flush(cmdq); free(cmdq); return (1); } /* Show message from command. */ void cmdq_print(struct cmd_q *cmdq, const char *fmt, ...) { struct client *c = cmdq->client; struct window *w; va_list ap; char *tmp, *msg; va_start(ap, fmt); if (c == NULL) /* nothing */; else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { if (~c->flags & CLIENT_UTF8) { vasprintf(&tmp, fmt, ap); msg = utf8_sanitize(tmp); free(tmp); evbuffer_add(c->stdout_data, msg, strlen(msg)); free(msg); } else evbuffer_add_vprintf(c->stdout_data, fmt, ap); evbuffer_add(c->stdout_data, "\n", 1); server_client_push_stdout(c); } else { 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); #ifdef TMATE tmate_sync_copy_mode(w->active); #endif } window_copy_vadd(w->active, fmt, ap); } va_end(ap); } /* Show error from command. */ void cmdq_error(struct cmd_q *cmdq, const char *fmt, ...) { struct client *c = cmdq->client; struct cmd *cmd = cmdq->cmd; va_list ap; char *msg; size_t msglen; char *tmp; va_start(ap, fmt); msglen = xvasprintf(&msg, fmt, ap); va_end(ap); if (c == NULL) #ifdef TMATE if (cmd->file && cmd->line) cfg_add_cause("%s:%u: %s", cmd->file, cmd->line, msg); else cfg_add_cause("%s", msg); #else cfg_add_cause("%s:%u: %s", cmd->file, cmd->line, msg); #endif else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { if (~c->flags & CLIENT_UTF8) { tmp = msg; msg = utf8_sanitize(tmp); free(tmp); msglen = strlen(msg); } evbuffer_add(c->stderr_data, msg, msglen); evbuffer_add(c->stderr_data, "\n", 1); server_client_push_stderr(c); c->retval = 1; } else { *msg = toupper((u_char) *msg); status_message_set(c, "%s", msg); } free(msg); } /* Print a guard line. */ void cmdq_guard(struct cmd_q *cmdq, const char *guard, int flags) { struct client *c = cmdq->client; if (c == NULL || !(c->flags & CLIENT_CONTROL)) return; evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard, (long) cmdq->time, cmdq->number, flags); server_client_push_stdout(c); } /* Add command list to queue and begin processing if needed. */ void cmdq_run(struct cmd_q *cmdq, struct cmd_list *cmdlist, struct mouse_event *m) { cmdq_append(cmdq, cmdlist, m); 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 mouse_event *m) { struct cmd_q_item *item; item = xcalloc(1, sizeof *item); item->cmdlist = cmdlist; TAILQ_INSERT_TAIL(&cmdq->queue, item, qentry); cmdlist->references++; if (m != NULL) memcpy(&item->mouse, m, sizeof item->mouse); else item->mouse.valid = 0; } /* Process one command. */ static enum cmd_retval cmdq_continue_one(struct cmd_q *cmdq) { struct cmd *cmd = cmdq->cmd; enum cmd_retval retval; char *tmp; int flags = !!(cmd->flags & CMD_CONTROL); #ifdef TMATE if (tmate_should_replicate_cmd(cmd->entry)) tmate_exec_cmd(cmd); #endif tmp = cmd_print(cmd); log_debug("cmdq %p: %s", cmdq, tmp); free(tmp); cmdq->time = time(NULL); cmdq->number++; cmdq_guard(cmdq, "begin", flags); if (cmd_prepare_state(cmd, cmdq, NULL) != 0) goto error; retval = cmd->entry->exec(cmd, cmdq); if (retval == CMD_RETURN_ERROR) goto error; cmdq_guard(cmdq, "end", flags); return (retval); error: cmdq_guard(cmdq, "error", flags); return (CMD_RETURN_ERROR); } /* Continue processing command queue. Returns 1 if finishes empty. */ int cmdq_continue(struct cmd_q *cmdq) { struct client *c = cmdq->client; struct cmd_q_item *next; enum cmd_retval retval; int empty; cmdq->references++; notify_disable(); log_debug("continuing cmdq %p: flags %#x, client %p", cmdq, cmdq->flags, c); 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 { while (cmdq->cmd != NULL) { retval = cmdq_continue_one(cmdq); 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); } next = TAILQ_NEXT(cmdq->item, 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 > 0) cmdq->client->flags |= CLIENT_EXIT; if (cmdq->emptyfn != NULL) cmdq->emptyfn(cmdq); empty = 1; out: notify_enable(); cmdq_free(cmdq); 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-2.4.0/cmd-refresh-client.c000066400000000000000000000042471356407164200164650ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "tmux.h" /* * Refresh client. */ enum cmd_retval cmd_refresh_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_refresh_client_entry = { .name = "refresh-client", .alias = "refresh", .args = { "C:St:", 0, 0 }, .usage = "[-S] [-C size] " CMD_TARGET_CLIENT_USAGE, .tflag = CMD_CLIENT, .flags = 0, .exec = 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 = cmdq->state.c; const char *size; u_int w, h; 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')) { c->flags |= CLIENT_STATUSFORCE; server_status_client(c); } else { c->flags |= CLIENT_STATUSFORCE; server_redraw_client(c); } return (CMD_RETURN_NORMAL); } tmate-2.4.0/cmd-rename-session.c000066400000000000000000000035571356407164200165060ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Change session name. */ enum cmd_retval cmd_rename_session_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_rename_session_entry = { .name = "rename-session", .alias = "rename", .args = { "t:", 1, 1 }, .usage = CMD_TARGET_SESSION_USAGE " new-name", .tflag = CMD_SESSION, .flags = 0, .exec = 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 = cmdq->state.tflag.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); } 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-2.4.0/cmd-rename-window.c000066400000000000000000000030451356407164200163220ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Rename a window. */ enum cmd_retval cmd_rename_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_rename_window_entry = { .name = "rename-window", .alias = "renamew", .args = { "t:", 1, 1 }, .usage = CMD_TARGET_WINDOW_USAGE " new-name", .tflag = CMD_WINDOW, .flags = 0, .exec = cmd_rename_window_exec }; enum cmd_retval cmd_rename_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl = cmdq->state.tflag.wl; 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-2.4.0/cmd-resize-pane.c000066400000000000000000000107071356407164200157730ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Increase or decrease pane size. */ enum cmd_retval cmd_resize_pane_exec(struct cmd *, struct cmd_q *); void cmd_resize_pane_mouse_update(struct client *, struct mouse_event *); const struct cmd_entry cmd_resize_pane_entry = { .name = "resize-pane", .alias = "resizep", .args = { "DLMRt:Ux:y:Z", 0, 1 }, .usage = "[-DLMRUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE " " "[adjustment]", .tflag = CMD_PANE, .flags = 0, .exec = cmd_resize_pane_exec }; enum cmd_retval cmd_resize_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct window_pane *wp = cmdq->state.tflag.wp; struct winlink *wl = cmdq->state.tflag.wl; struct window *w = wl->window; struct client *c = cmdq->client; struct session *s = cmdq->state.tflag.s; const char *errstr; char *cause; u_int adjust; int x, y; if (args_has(args, 'M')) { if (cmd_mouse_window(&cmdq->item->mouse, &s) == NULL) return (CMD_RETURN_NORMAL); if (c == NULL || c->session != s) return (CMD_RETURN_NORMAL); c->tty.mouse_drag_update = cmd_resize_pane_mouse_update; cmd_resize_pane_mouse_update(c, &cmdq->item->mouse); return (CMD_RETURN_NORMAL); } if (args_has(args, 'Z')) { if (w->flags & WINDOW_ZOOMED) window_unzoom(w); else window_zoom(wp); server_redraw_window(w); server_status_window(w); return (CMD_RETURN_NORMAL); } server_unzoom_window(w); if (args->argc == 0) adjust = 1; else { adjust = strtonum(args->argv[0], 1, INT_MAX, &errstr); if (errstr != NULL) { cmdq_error(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); } void cmd_resize_pane_mouse_update(struct client *c, struct mouse_event *m) { struct winlink *wl; struct window_pane *wp; int found; u_int y, ly; wl = cmd_mouse_window(m, NULL); if (wl == NULL) { c->tty.mouse_drag_update = NULL; return; } y = m->y; if (m->statusat == 0 && y > 0) y--; else if (m->statusat > 0 && y >= (u_int)m->statusat) y = m->statusat - 1; ly = m->ly; if (m->statusat == 0 && ly > 0) ly--; else if (m->statusat > 0 && ly >= (u_int)m->statusat) ly = m->statusat - 1; found = 0; TAILQ_FOREACH(wp, &wl->window->panes, entry) { if (!window_pane_visible(wp)) continue; if (wp->xoff + wp->sx == m->lx && wp->yoff <= 1 + ly && wp->yoff + wp->sy >= ly) { layout_resize_pane(wp, LAYOUT_LEFTRIGHT, m->x - m->lx); found = 1; } if (wp->yoff + wp->sy == ly && wp->xoff <= 1 + m->lx && wp->xoff + wp->sx >= m->lx) { layout_resize_pane(wp, LAYOUT_TOPBOTTOM, y - ly); found = 1; } } if (found) server_redraw_window(wl->window); else c->tty.mouse_drag_update = NULL; } tmate-2.4.0/cmd-respawn-pane.c000066400000000000000000000052671356407164200161560ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott * Copyright (c) 2011 Marcel P. Partap * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Respawn a pane (restart the command). Kill existing if -k given. */ enum cmd_retval cmd_respawn_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_respawn_pane_entry = { .name = "respawn-pane", .alias = "respawnp", .args = { "kt:", 0, -1 }, .usage = "[-k] " CMD_TARGET_PANE_USAGE " [command]", .tflag = CMD_PANE, .flags = 0, .exec = 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 = cmdq->state.tflag.wl; struct window *w = wl->window; struct window_pane *wp = cmdq->state.tflag.wp; struct session *s = cmdq->state.tflag.s; struct environ *env; const char *path; char *cause; u_int idx; struct environ_entry *envent; 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:%d.%u", s->name, wl->idx, idx); return (CMD_RETURN_ERROR); } env = environ_create(); 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); path = NULL; if (cmdq->client != NULL && cmdq->client->session == NULL) envent = environ_find(cmdq->client->environ, "PATH"); else envent = environ_find(s->environ, "PATH"); if (envent != NULL) path = envent->value; if (window_pane_spawn(wp, args->argc, args->argv, path, 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-2.4.0/cmd-respawn-window.c000066400000000000000000000055571356407164200165440ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Respawn a window (restart the command). Kill existing if -k given. */ enum cmd_retval cmd_respawn_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_respawn_window_entry = { .name = "respawn-window", .alias = "respawnw", .args = { "kt:", 0, -1 }, .usage = "[-k] " CMD_TARGET_WINDOW_USAGE " [command]", .tflag = CMD_WINDOW, .flags = 0, .exec = cmd_respawn_window_exec }; enum cmd_retval cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct session *s = cmdq->state.tflag.s; struct winlink *wl = cmdq->state.tflag.wl; struct window *w = wl->window; struct window_pane *wp; struct environ *env; const char *path; char *cause; struct environ_entry *envent; 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); } } env = environ_create(); 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); path = NULL; if (cmdq->client != NULL && cmdq->client->session == NULL) envent = environ_find(cmdq->client->environ, "PATH"); else envent = environ_find(s->environ, "PATH"); if (envent != NULL) path = envent->value; if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, NULL, env, s->tio, &cause) != 0) { cmdq_error(cmdq, "respawn window failed: %s", cause); free(cause); environ_free(env); server_destroy_pane(wp, 0); 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-2.4.0/cmd-rotate-window.c000066400000000000000000000062161356407164200163540ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "tmux.h" /* * Rotate the panes in a window. */ enum cmd_retval cmd_rotate_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_rotate_window_entry = { .name = "rotate-window", .alias = "rotatew", .args = { "Dt:U", 0, 0 }, .usage = "[-DU] " CMD_TARGET_WINDOW_USAGE, .tflag = CMD_WINDOW, .flags = 0, .exec = cmd_rotate_window_exec }; enum cmd_retval cmd_rotate_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct winlink *wl = cmdq->state.tflag.wl; struct window *w = wl->window; struct window_pane *wp, *wp2; struct layout_cell *lc; u_int sx, sy, xoff, yoff; 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-2.4.0/cmd-run-shell.c000066400000000000000000000102311356407164200154520ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Tiago Cunha * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" /* * Runs a command without a window. */ 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 = { .name = "run-shell", .alias = "run", .args = { "bt:", 1, 1 }, .usage = "[-b] " CMD_TARGET_PANE_USAGE " shell-command", .tflag = CMD_PANE_CANFAIL, .flags = 0, .exec = 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 session *s = cmdq->state.tflag.s; struct winlink *wl = cmdq->state.tflag.wl; struct window_pane *wp = cmdq->state.tflag.wp; struct format_tree *ft; const char *cwd; if (cmdq->client != NULL && cmdq->client->session == NULL) cwd = cmdq->client->cwd; else if (s != NULL) cwd = s->cwd; else cwd = NULL; ft = format_create(cmdq, 0); format_defaults(ft, cmdq->state.c, s, wl, 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, cwd, 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->flags & CMD_Q_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) 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-2.4.0/cmd-save-buffer.c000066400000000000000000000077311356407164200157610ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Tiago Cunha * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include "tmux.h" /* * Saves a paste buffer to a file. */ enum cmd_retval cmd_save_buffer_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_save_buffer_entry = { .name = "save-buffer", .alias = "saveb", .args = { "ab:", 1, 1 }, .usage = "[-a] " CMD_BUFFER_USAGE " path", .flags = 0, .exec = cmd_save_buffer_exec }; const struct cmd_entry cmd_show_buffer_entry = { .name = "show-buffer", .alias = "showb", .args = { "b:", 0, 0 }, .usage = CMD_BUFFER_USAGE, .flags = 0, .exec = 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 = cmdq->client; struct session *s; struct paste_buffer *pb; const char *path, *bufname, *bufdata, *start, *end, *cwd; const char *flags; char *msg, *file, resolved[PATH_MAX]; size_t size, used, msglen, bufsize; FILE *f; if (!args_has(args, 'b')) { if ((pb = paste_get_top(NULL)) == NULL) { cmdq_error(cmdq, "no buffers"); return (CMD_RETURN_ERROR); } } else { bufname = args_get(args, 'b'); pb = paste_get_name(bufname); if (pb == NULL) { cmdq_error(cmdq, "no buffer %s", bufname); return (CMD_RETURN_ERROR); } } bufdata = paste_buffer_data(pb, &bufsize); if (self->entry == &cmd_show_buffer_entry) path = "-"; else path = args->argv[0]; if (strcmp(path, "-") == 0) { 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; } if (c != NULL && c->session == NULL && c->cwd != NULL) cwd = c->cwd; else if ((s = c->session) != NULL && s->cwd != NULL) cwd = s->cwd; else cwd = "."; flags = "wb"; if (args_has(self->args, 'a')) flags = "ab"; if (*path == '/') file = xstrdup(path); else xasprintf(&file, "%s/%s", cwd, path); if (realpath(file, resolved) == NULL && strlcpy(resolved, file, sizeof resolved) >= sizeof resolved) { cmdq_error(cmdq, "%s: %s", file, strerror(ENAMETOOLONG)); return (CMD_RETURN_ERROR); } f = fopen(resolved, flags); free(file); if (f == NULL) { cmdq_error(cmdq, "%s: %s", resolved, strerror(errno)); return (CMD_RETURN_ERROR); } if (fwrite(bufdata, 1, bufsize, f) != bufsize) { cmdq_error(cmdq, "%s: write error", resolved); fclose(f); return (CMD_RETURN_ERROR); } fclose(f); return (CMD_RETURN_NORMAL); do_stdout: evbuffer_add(c->stdout_data, bufdata, bufsize); server_client_push_stdout(c); return (CMD_RETURN_NORMAL); do_print: if (bufsize > (INT_MAX / 4) - 1) { cmdq_error(cmdq, "buffer too big"); return (CMD_RETURN_ERROR); } msg = NULL; used = 0; while (used != bufsize) { start = bufdata + used; end = memchr(start, '\n', bufsize - used); if (end != NULL) size = end - start; else size = bufsize - used; msglen = size * 4 + 1; msg = xrealloc(msg, msglen); strvisx(msg, start, size, VIS_OCTAL|VIS_TAB); cmdq_print(cmdq, "%s", msg); used += size + (end != NULL); } free(msg); return (CMD_RETURN_NORMAL); } tmate-2.4.0/cmd-select-layout.c000066400000000000000000000060751356407164200163460ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Switch window to selected layout. */ enum cmd_retval cmd_select_layout_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_select_layout_entry = { .name = "select-layout", .alias = "selectl", .args = { "nopt:", 0, 1 }, .usage = "[-nop] " CMD_TARGET_WINDOW_USAGE " [layout-name]", .tflag = CMD_WINDOW, .flags = 0, .exec = cmd_select_layout_exec }; const struct cmd_entry cmd_next_layout_entry = { .name = "next-layout", .alias = "nextl", .args = { "t:", 0, 0 }, .usage = CMD_TARGET_WINDOW_USAGE, .tflag = CMD_WINDOW, .flags = 0, .exec = cmd_select_layout_exec }; const struct cmd_entry cmd_previous_layout_entry = { .name = "previous-layout", .alias = "prevl", .args = { "t:", 0, 0 }, .usage = CMD_TARGET_WINDOW_USAGE, .tflag = CMD_WINDOW, .flags = 0, .exec = cmd_select_layout_exec }; enum cmd_retval cmd_select_layout_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl = cmdq->state.tflag.wl; struct window *w; const char *layoutname; char *oldlayout; int next, previous, layout; w = wl->window; server_unzoom_window(w); next = self->entry == &cmd_next_layout_entry; if (args_has(args, 'n')) next = 1; previous = self->entry == &cmd_previous_layout_entry; if (args_has(args, 'p')) previous = 1; oldlayout = w->old_layout; w->old_layout = layout_dump(w->layout_root); if (next || previous) { if (next) layout_set_next(w); else layout_set_previous(w); goto changed; } if (!args_has(args, 'o')) { if (args->argc == 0) layout = w->lastlayout; else layout = layout_set_lookup(args->argv[0]); if (layout != -1) { layout_set_select(w, layout); goto changed; } } if (args->argc != 0) layoutname = args->argv[0]; else if (args_has(args, 'o')) layoutname = oldlayout; else layoutname = NULL; if (layoutname != NULL) { if (layout_parse(w, layoutname) == -1) { cmdq_error(cmdq, "can't set layout: %s", layoutname); goto error; } goto changed; } free(oldlayout); return (CMD_RETURN_NORMAL); changed: free(oldlayout); server_redraw_window(w); return (CMD_RETURN_NORMAL); error: free(w->old_layout); w->old_layout = oldlayout; return (CMD_RETURN_ERROR); } tmate-2.4.0/cmd-select-pane.c000066400000000000000000000104751356407164200157530ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "tmux.h" /* * Select pane. */ enum cmd_retval cmd_select_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_select_pane_entry = { .name = "select-pane", .alias = "selectp", .args = { "DdegLlMmP:Rt:U", 0, 0 }, .usage = "[-DdegLlMmRU] [-P style] " CMD_TARGET_PANE_USAGE, .tflag = CMD_PANE, .flags = 0, .exec = cmd_select_pane_exec }; const struct cmd_entry cmd_last_pane_entry = { .name = "last-pane", .alias = "lastp", .args = { "det:", 0, 0 }, .usage = "[-de] " CMD_TARGET_WINDOW_USAGE, .tflag = CMD_WINDOW, .flags = 0, .exec = cmd_select_pane_exec }; enum cmd_retval cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl = cmdq->state.tflag.wl; struct window *w = wl->window; struct session *s = cmdq->state.tflag.s; struct window_pane *wp = cmdq->state.tflag.wp, *lastwp, *markedwp; const char *style; if (self->entry == &cmd_last_pane_entry || args_has(args, 'l')) { if (wl->window->last == NULL) { cmdq_error(cmdq, "no last pane"); return (CMD_RETURN_ERROR); } if (args_has(self->args, 'e')) w->last->flags &= ~PANE_INPUTOFF; else if (args_has(self->args, 'd')) w->last->flags |= PANE_INPUTOFF; else { server_unzoom_window(w); window_redraw_active_switch(w, w->last); if (window_set_active_pane(w, w->last)) { server_status_window(w); server_redraw_window_borders(w); } } return (CMD_RETURN_NORMAL); } if (args_has(args, 'm') || args_has(args, 'M')) { if (args_has(args, 'm') && !window_pane_visible(wp)) return (CMD_RETURN_NORMAL); lastwp = marked_pane.wp; if (args_has(args, 'M') || server_is_marked(s, wl, wp)) server_clear_marked(); else server_set_marked(s, wl, wp); markedwp = marked_pane.wp; if (lastwp != NULL) { server_redraw_window_borders(lastwp->window); server_status_window(lastwp->window); } if (markedwp != NULL) { server_redraw_window_borders(markedwp->window); server_status_window(markedwp->window); } return (CMD_RETURN_NORMAL); } if (args_has(self->args, 'P') || args_has(self->args, 'g')) { if (args_has(args, 'P')) { style = args_get(args, 'P'); if (style_parse(&grid_default_cell, &wp->colgc, style) == -1) { cmdq_error(cmdq, "bad style: %s", style); return (CMD_RETURN_ERROR); } wp->flags |= PANE_REDRAW; } if (args_has(self->args, 'g')) cmdq_print(cmdq, "%s", style_tostring(&wp->colgc)); return (CMD_RETURN_NORMAL); } if (args_has(self->args, 'L')) { server_unzoom_window(wp->window); wp = window_pane_find_left(wp); } else if (args_has(self->args, 'R')) { server_unzoom_window(wp->window); wp = window_pane_find_right(wp); } else if (args_has(self->args, 'U')) { server_unzoom_window(wp->window); wp = window_pane_find_up(wp); } else if (args_has(self->args, 'D')) { server_unzoom_window(wp->window); wp = window_pane_find_down(wp); } if (wp == NULL) return (CMD_RETURN_NORMAL); if (args_has(self->args, 'e')) { wp->flags &= ~PANE_INPUTOFF; return (CMD_RETURN_NORMAL); } if (args_has(self->args, 'd')) { wp->flags |= PANE_INPUTOFF; return (CMD_RETURN_NORMAL); } if (wp == w->active) return (CMD_RETURN_NORMAL); server_unzoom_window(wp->window); if (!window_pane_visible(wp)) { cmdq_error(cmdq, "pane not visible"); return (CMD_RETURN_ERROR); } window_redraw_active_switch(w, wp); if (window_set_active_pane(w, wp)) { server_status_window(w); server_redraw_window_borders(w); } return (CMD_RETURN_NORMAL); } tmate-2.4.0/cmd-select-window.c000066400000000000000000000064611356407164200163370ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Select window by index. */ enum cmd_retval cmd_select_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_select_window_entry = { .name = "select-window", .alias = "selectw", .args = { "lnpTt:", 0, 0 }, .usage = "[-lnpT] " CMD_TARGET_WINDOW_USAGE, .tflag = CMD_WINDOW, .flags = 0, .exec = cmd_select_window_exec }; const struct cmd_entry cmd_next_window_entry = { .name = "next-window", .alias = "next", .args = { "at:", 0, 0 }, .usage = "[-a] " CMD_TARGET_SESSION_USAGE, .tflag = CMD_SESSION, .flags = 0, .exec = cmd_select_window_exec }; const struct cmd_entry cmd_previous_window_entry = { .name = "previous-window", .alias = "prev", .args = { "at:", 0, 0 }, .usage = "[-a] " CMD_TARGET_SESSION_USAGE, .tflag = CMD_SESSION, .flags = 0, .exec = cmd_select_window_exec }; const struct cmd_entry cmd_last_window_entry = { .name = "last-window", .alias = "last", .args = { "t:", 0, 0 }, .usage = CMD_TARGET_SESSION_USAGE, .tflag = CMD_SESSION, .flags = 0, .exec = cmd_select_window_exec }; enum cmd_retval cmd_select_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct winlink *wl = cmdq->state.tflag.wl; struct session *s = cmdq->state.tflag.s; int next, previous, last, activity; next = self->entry == &cmd_next_window_entry; if (args_has(self->args, 'n')) next = 1; previous = self->entry == &cmd_previous_window_entry; if (args_has(self->args, 'p')) previous = 1; last = self->entry == &cmd_last_window_entry; if (args_has(self->args, 'l')) last = 1; if (next || previous || last) { activity = args_has(self->args, 'a'); if (next) { if (session_next(s, activity) != 0) { cmdq_error(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 { /* * 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-2.4.0/cmd-send-keys.c000066400000000000000000000052761356407164200154600ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Send keys to client. */ enum cmd_retval cmd_send_keys_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_send_keys_entry = { .name = "send-keys", .alias = "send", .args = { "lRMt:", 0, -1 }, .usage = "[-lRM] " CMD_TARGET_PANE_USAGE " key ...", .tflag = CMD_PANE, .flags = 0, .exec = cmd_send_keys_exec }; const struct cmd_entry cmd_send_prefix_entry = { .name = "send-prefix", .alias = NULL, .args = { "2t:", 0, 0 }, .usage = "[-2] " CMD_TARGET_PANE_USAGE, .tflag = CMD_PANE, .flags = 0, .exec = 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 = cmdq->state.tflag.wp; struct session *s = cmdq->state.tflag.s; struct mouse_event *m = &cmdq->item->mouse; const u_char *keystr; int i, literal; key_code key; if (args_has(args, 'M')) { wp = cmd_mouse_pane(m, &s, NULL); if (wp == NULL) { cmdq_error(cmdq, "no mouse target"); return (CMD_RETURN_ERROR); } window_pane_key(wp, NULL, s, m->key, m); return (CMD_RETURN_NORMAL); } if (self->entry == &cmd_send_prefix_entry) { if (args_has(args, '2')) key = options_get_number(s->options, "prefix2"); else key = options_get_number(s->options, "prefix"); window_pane_key(wp, NULL, s, key, NULL); return (CMD_RETURN_NORMAL); } if (args_has(args, 'R')) input_reset(wp, 1); for (i = 0; i < args->argc; i++) { literal = args_has(args, 'l'); if (!literal) { key = key_string_lookup_string(args->argv[i]); if (key != KEYC_NONE && key != KEYC_UNKNOWN) window_pane_key(wp, NULL, s, key, NULL); else literal = 1; } if (literal) { for (keystr = args->argv[i]; *keystr != '\0'; keystr++) window_pane_key(wp, NULL, s, *keystr, NULL); } } return (CMD_RETURN_NORMAL); } tmate-2.4.0/cmd-set-buffer.c000066400000000000000000000057661356407164200156240ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Add, set, append to or delete a paste buffer. */ enum cmd_retval cmd_set_buffer_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_set_buffer_entry = { .name = "set-buffer", .alias = "setb", .args = { "ab:n:", 0, 1 }, .usage = "[-a] " CMD_BUFFER_USAGE " [-n new-buffer-name] data", .flags = 0, .exec = cmd_set_buffer_exec }; const struct cmd_entry cmd_delete_buffer_entry = { .name = "delete-buffer", .alias = "deleteb", .args = { "b:", 0, 0 }, .usage = CMD_BUFFER_USAGE, .flags = 0, .exec = cmd_set_buffer_exec }; enum cmd_retval cmd_set_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct paste_buffer *pb; char *bufdata, *cause; const char *bufname, *olddata; size_t bufsize, newsize; bufname = args_get(args, 'b'); if (bufname == NULL) pb = NULL; else pb = paste_get_name(bufname); if (self->entry == &cmd_delete_buffer_entry) { if (pb == NULL) pb = paste_get_top(&bufname); if (pb == NULL) { cmdq_error(cmdq, "no buffer"); return (CMD_RETURN_ERROR); } paste_free(pb); return (CMD_RETURN_NORMAL); } if (args_has(args, 'n')) { if (pb == NULL) pb = paste_get_top(&bufname); if (pb == NULL) { cmdq_error(cmdq, "no buffer"); return (CMD_RETURN_ERROR); } if (paste_rename(bufname, args_get(args, 'n'), &cause) != 0) { cmdq_error(cmdq, "%s", cause); free(cause); return (CMD_RETURN_ERROR); } return (CMD_RETURN_NORMAL); } if (args->argc != 1) { cmdq_error(cmdq, "no data specified"); return (CMD_RETURN_ERROR); } if ((newsize = strlen(args->argv[0])) == 0) return (CMD_RETURN_NORMAL); bufsize = 0; bufdata = NULL; if (args_has(args, 'a') && pb != NULL) { olddata = paste_buffer_data(pb, &bufsize); bufdata = xmalloc(bufsize); memcpy(bufdata, olddata, bufsize); } bufdata = xrealloc(bufdata, bufsize + newsize); memcpy(bufdata + bufsize, args->argv[0], newsize); bufsize += newsize; if (paste_set(bufdata, bufsize, bufname, &cause) != 0) { cmdq_error(cmdq, "%s", cause); free(bufdata); free(cause); return (CMD_RETURN_ERROR); } return (CMD_RETURN_NORMAL); } tmate-2.4.0/cmd-set-environment.c000066400000000000000000000051401356407164200167010ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Set an environment variable. */ enum cmd_retval cmd_set_environment_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_set_environment_entry = { .name = "set-environment", .alias = "setenv", .args = { "grt:u", 1, 2 }, .usage = "[-gru] " CMD_TARGET_SESSION_USAGE " name [value]", .tflag = CMD_SESSION_CANFAIL, .flags = 0, .exec = cmd_set_environment_exec }; enum cmd_retval cmd_set_environment_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct environ *env; const char *name, *value, *target; 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 (cmdq->state.tflag.s == NULL) { target = args_get(args, 't'); if (target != NULL) cmdq_error(cmdq, "no such session: %s", target); else cmdq_error(cmdq, "no current session"); return (CMD_RETURN_ERROR); } env = cmdq->state.tflag.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_clear(env, name); } else { if (value == NULL) { cmdq_error(cmdq, "no value specified"); return (CMD_RETURN_ERROR); } environ_set(env, name, "%s", value); } return (CMD_RETURN_NORMAL); } tmate-2.4.0/cmd-set-hook.c000066400000000000000000000054341356407164200153030ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2012 Thomas Adam * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Set or show global or session hooks. */ enum cmd_retval cmd_set_hook_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_set_hook_entry = { .name = "set-hook", .alias = NULL, .args = { "gt:u", 1, 2 }, .usage = "[-gu] " CMD_TARGET_SESSION_USAGE " hook-name [command]", .tflag = CMD_SESSION, .flags = 0, .exec = cmd_set_hook_exec }; const struct cmd_entry cmd_show_hooks_entry = { .name = "show-hooks", .alias = NULL, .args = { "gt:", 0, 1 }, .usage = "[-g] " CMD_TARGET_SESSION_USAGE, .tflag = CMD_SESSION, .flags = 0, .exec = cmd_set_hook_exec }; enum cmd_retval cmd_set_hook_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct cmd_list *cmdlist; struct hooks *hooks; struct hook *hook; char *cause, *tmp; const char *name, *cmd; if (args_has(args, 'g')) hooks = global_hooks; else hooks = cmdq->state.tflag.s->hooks; if (self->entry == &cmd_show_hooks_entry) { hook = hooks_first(hooks); while (hook != NULL) { tmp = cmd_list_print(hook->cmdlist); cmdq_print(cmdq, "%s -> %s", hook->name, tmp); free(tmp); hook = hooks_next(hook); } return (CMD_RETURN_NORMAL); } name = args->argv[0]; if (*name == '\0') { cmdq_error(cmdq, "invalid hook name"); return (CMD_RETURN_ERROR); } if (args->argc < 2) cmd = NULL; else cmd = args->argv[1]; if (args_has(args, 'u')) { if (cmd != NULL) { cmdq_error(cmdq, "command passed to unset hook: %s", name); return (CMD_RETURN_ERROR); } hooks_remove(hooks, name); return (CMD_RETURN_NORMAL); } if (cmd == NULL) { cmdq_error(cmdq, "no command to set hook: %s", name); return (CMD_RETURN_ERROR); } if (cmd_string_parse(cmd, &cmdlist, NULL, 0, &cause) != 0) { if (cause != NULL) { cmdq_error(cmdq, "%s", cause); free(cause); } return (CMD_RETURN_ERROR); } hooks_add(hooks, name, cmdlist); cmd_list_free(cmdlist); return (CMD_RETURN_NORMAL); } tmate-2.4.0/cmd-set-option.c000066400000000000000000000333041356407164200156500ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * 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 *); struct options_entry *cmd_set_option_style(struct cmd *, struct cmd_q *, const struct options_table_entry *, struct options *, const char *); const struct cmd_entry cmd_set_option_entry = { .name = "set-option", .alias = "set", .args = { "agoqst:uw", 1, 2 }, .usage = "[-agosquw] [-t target-window] option [value]", .tflag = CMD_WINDOW_CANFAIL, .flags = 0, .exec = cmd_set_option_exec }; const struct cmd_entry cmd_set_window_option_entry = { .name = "set-window-option", .alias = "setw", .args = { "agoqt:u", 1, 2 }, .usage = "[-agoqu] " CMD_TARGET_WINDOW_USAGE " option [value]", .tflag = CMD_WINDOW_CANFAIL, .flags = 0, .exec = cmd_set_option_exec }; enum cmd_retval cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct session *s = cmdq->state.tflag.s; struct winlink *wl = cmdq->state.tflag.wl; struct window *w; struct client *c; const struct options_table_entry *oe; struct options *oo; const char *optstr, *valstr, *target; /* 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. */ oe = NULL; if (options_table_find(optstr, &oe) != 0) { if (!args_has(args, 'q')) { cmdq_error(cmdq, "ambiguous option: %s", optstr); return (CMD_RETURN_ERROR); } return (CMD_RETURN_NORMAL); } if (oe == NULL) { if (!args_has(args, 'q')) { cmdq_error(cmdq, "unknown option: %s", optstr); return (CMD_RETURN_ERROR); } return (CMD_RETURN_NORMAL); } /* Work out the tree from the scope of the option. */ if (oe->scope == OPTIONS_TABLE_SERVER) oo = global_options; else if (oe->scope == OPTIONS_TABLE_WINDOW) { if (args_has(self->args, 'g')) oo = global_w_options; else if (wl == NULL) { target = args_get(args, 't'); if (target != NULL) { cmdq_error(cmdq, "no such window: %s", target); } else cmdq_error(cmdq, "no current window"); return (CMD_RETURN_ERROR); } else oo = wl->window->options; } else if (oe->scope == OPTIONS_TABLE_SESSION) { if (args_has(self->args, 'g')) oo = global_s_options; else if (s == NULL) { target = args_get(args, 't'); if (target != NULL) { cmdq_error(cmdq, "no such session: %s", target); } else cmdq_error(cmdq, "no current session"); return (CMD_RETURN_ERROR); } else 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_error(cmdq, "already set: %s", optstr); return (CMD_RETURN_ERROR); } return (CMD_RETURN_NORMAL); } if (cmd_set_option_set(self, cmdq, oe, oo, valstr) != 0) return (CMD_RETURN_ERROR); } /* Start or stop timers if necessary. */ if (strcmp(oe->name, "automatic-rename") == 0) { RB_FOREACH(w, windows, &windows) { if (options_get_number(w->options, "automatic-rename")) w->active->flags |= PANE_CHANGED; } } if (strcmp(oe->name, "key-table") == 0) { TAILQ_FOREACH(c, &clients, entry) server_client_set_key_table(c, NULL); } if (strcmp(oe->name, "status") == 0 || strcmp(oe->name, "status-interval") == 0) status_timer_start_all(); if (strcmp(oe->name, "monitor-silence") == 0) alerts_reset_all(); /* Update sizes and redraw. May not need it but meh. */ recalculate_sizes(); TAILQ_FOREACH(c, &clients, entry) { if (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 = cmdq->state.tflag.s; struct winlink *wl = cmdq->state.tflag.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 oo = wl->window->options; } else { if (args_has(self->args, 'g')) oo = global_s_options; else oo = s->options; } if (args_has(args, 'u')) { if (options_find1(oo, optstr) == NULL) { if (!args_has(args, 'q')) { cmdq_error(cmdq, "unknown option: %s", optstr); return (CMD_RETURN_ERROR); } return (CMD_RETURN_NORMAL); } 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_error(cmdq, "already set: %s", optstr); return (CMD_RETURN_ERROR); } return (CMD_RETURN_NORMAL); } options_set_string(oo, optstr, "%s", 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 (value != NULL) { cmdq_error(cmdq, "value passed to unset option: %s", oe->name); return (-1); } if (args_has(args, 'g') || oo == global_options) { switch (oe->type) { case OPTIONS_TABLE_STRING: options_set_string(oo, oe->name, "%s", oe->default_str); break; case OPTIONS_TABLE_STYLE: options_set_style(oo, oe->name, oe->default_str, 0); break; default: options_set_number(oo, oe->name, oe->default_num); break; } } else options_remove(oo, 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 options_entry *o; switch (oe->type) { case OPTIONS_TABLE_FLAG: case OPTIONS_TABLE_CHOICE: break; default: if (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); if (o != NULL) style_update_new(oo, o->name, oe->style); break; case OPTIONS_TABLE_ATTRIBUTES: o = cmd_set_option_attributes(self, cmdq, oe, oo, value); if (o != NULL) style_update_new(oo, o->name, oe->style); 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; case OPTIONS_TABLE_STYLE: o = cmd_set_option_style(self, cmdq, oe, oo, value); break; } if (o == NULL) return (-1); 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) { key_code key; key = key_string_lookup_string(value); if (key == KEYC_UNKNOWN) { 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; if (value == NULL) { choice = options_get_number(oo, oe->name); if (choice < 2) choice = !choice; } else { 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)); } /* Set a style option. */ struct options_entry * cmd_set_option_style(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; int append; append = args_has(args, 'a'); if ((o = options_set_style(oo, oe->name, value, append)) == NULL) { cmdq_error(cmdq, "bad style: %s", value); return (NULL); } style_update_old(oo, oe->name, &o->style); return (o); } tmate-2.4.0/cmd-show-environment.c000066400000000000000000000067421356407164200170770ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Show environment. */ enum cmd_retval cmd_show_environment_exec(struct cmd *, struct cmd_q *); char *cmd_show_environment_escape(struct environ_entry *); void cmd_show_environment_print(struct cmd *, struct cmd_q *, struct environ_entry *); const struct cmd_entry cmd_show_environment_entry = { .name = "show-environment", .alias = "showenv", .args = { "gst:", 0, 1 }, .usage = "[-gs] " CMD_TARGET_SESSION_USAGE " [name]", .tflag = CMD_SESSION_CANFAIL, .flags = 0, .exec = cmd_show_environment_exec }; char * cmd_show_environment_escape(struct environ_entry *envent) { const char *value = envent->value; char c, *out, *ret; out = ret = xmalloc(strlen(value) * 2 + 1); /* at most twice the size */ while ((c = *value++) != '\0') { /* POSIX interprets $ ` " and \ in double quotes. */ if (c == '$' || c == '`' || c == '"' || c == '\\') *out++ = '\\'; *out++ = c; } *out = '\0'; return (ret); } void cmd_show_environment_print(struct cmd *self, struct cmd_q *cmdq, struct environ_entry *envent) { char *escaped; if (!args_has(self->args, 's')) { if (envent->value != NULL) cmdq_print(cmdq, "%s=%s", envent->name, envent->value); else cmdq_print(cmdq, "-%s", envent->name); return; } if (envent->value != NULL) { escaped = cmd_show_environment_escape(envent); cmdq_print(cmdq, "%s=\"%s\"; export %s;", envent->name, escaped, envent->name); free(escaped); } else cmdq_print(cmdq, "unset %s;", envent->name); } enum cmd_retval cmd_show_environment_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct environ *env; struct environ_entry *envent; const char *target; if ((target = args_get(args, 't')) != NULL) { if (cmdq->state.tflag.s == NULL) { cmdq_error(cmdq, "no such session: %s", target); return (CMD_RETURN_ERROR); } } if (args_has(self->args, 'g')) env = global_environ; else { if (cmdq->state.tflag.s == NULL) { target = args_get(args, 't'); if (target != NULL) cmdq_error(cmdq, "no such session: %s", target); else cmdq_error(cmdq, "no current session"); return (CMD_RETURN_ERROR); } env = cmdq->state.tflag.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); } cmd_show_environment_print(self, cmdq, envent); return (CMD_RETURN_NORMAL); } envent = environ_first(env); while (envent != NULL) { cmd_show_environment_print(self, cmdq, envent); envent = environ_next(envent); } return (CMD_RETURN_NORMAL); } tmate-2.4.0/cmd-show-messages.c000066400000000000000000000060321356407164200163320ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" /* * Show client message log. */ enum cmd_retval cmd_show_messages_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_show_messages_entry = { .name = "show-messages", .alias = "showmsgs", .args = { "JTt:", 0, 0 }, .usage = "[-JT] " CMD_TARGET_CLIENT_USAGE, .tflag = CMD_CLIENT, .flags = 0, .exec = cmd_show_messages_exec }; const struct cmd_entry cmd_server_info_entry = { .name = "server-info", .alias = "info", .args = { "", 0, 0 }, .usage = "", .flags = 0, .exec = cmd_show_messages_exec }; int cmd_show_messages_terminals(struct cmd_q *, int); int cmd_show_messages_jobs(struct cmd_q *, int); int cmd_show_messages_terminals(struct cmd_q *cmdq, int blank) { struct tty_term *term; u_int i, n; n = 0; LIST_FOREACH(term, &tty_terms, entry) { if (blank) { cmdq_print(cmdq, "%s", ""); blank = 0; } cmdq_print(cmdq, "Terminal %u: %s [references=%u, flags=0x%x]:", n, term->name, term->references, term->flags); n++; for (i = 0; i < tty_term_ncodes(); i++) cmdq_print(cmdq, "%s", tty_term_describe(term, i)); } return (n != 0); } int cmd_show_messages_jobs(struct cmd_q *cmdq, int blank) { struct job *job; u_int n; n = 0; LIST_FOREACH(job, &all_jobs, lentry) { if (blank) { cmdq_print(cmdq, "%s", ""); blank = 0; } cmdq_print(cmdq, "Job %u: %s [fd=%d, pid=%d, status=%d]", n, job->cmd, job->fd, job->pid, job->status); n++; } return (n != 0); } enum cmd_retval cmd_show_messages_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct client *c = cmdq->state.c; struct message_entry *msg; char *tim; int done, blank; done = blank = 0; if (args_has(args, 'T') || self->entry == &cmd_server_info_entry) { blank = cmd_show_messages_terminals(cmdq, blank); done = 1; } if (args_has(args, 'J') || self->entry == &cmd_server_info_entry) { cmd_show_messages_jobs(cmdq, blank); done = 1; } if (done) return (CMD_RETURN_NORMAL); TAILQ_FOREACH(msg, &c->message_log, entry) { tim = ctime(&msg->msg_time); *strchr(tim, '\n') = '\0'; cmdq_print(cmdq, "%s %s", tim, msg->msg); } return (CMD_RETURN_NORMAL); } tmate-2.4.0/cmd-show-options.c000066400000000000000000000122551356407164200162220ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Show options. */ 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 *, struct options *, enum options_table_scope); const struct cmd_entry cmd_show_options_entry = { .name = "show-options", .alias = "show", .args = { "gqst:vw", 0, 1 }, .usage = "[-gqsvw] [-t target-session|target-window] [option]", .tflag = CMD_WINDOW_CANFAIL, .flags = 0, .exec = cmd_show_options_exec }; const struct cmd_entry cmd_show_window_options_entry = { .name = "show-window-options", .alias = "showw", .args = { "gvt:", 0, 1 }, .usage = "[-gv] " CMD_TARGET_WINDOW_USAGE " [option]", .tflag = CMD_WINDOW_CANFAIL, .flags = 0, .exec = 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 = cmdq->state.tflag.s; struct winlink *wl = cmdq->state.tflag.wl; struct options *oo; enum options_table_scope scope; int quiet; const char *target; if (args_has(self->args, 's')) { oo = global_options; scope = OPTIONS_TABLE_SERVER; } else if (args_has(self->args, 'w') || self->entry == &cmd_show_window_options_entry) { scope = OPTIONS_TABLE_WINDOW; if (args_has(self->args, 'g')) oo = global_w_options; else if (wl == NULL) { target = args_get(args, 't'); if (target != NULL) { cmdq_error(cmdq, "no such window: %s", target); } else cmdq_error(cmdq, "no current window"); return (CMD_RETURN_ERROR); } else oo = wl->window->options; } else { scope = OPTIONS_TABLE_SESSION; if (args_has(self->args, 'g')) oo = global_s_options; else if (s == NULL) { target = args_get(args, 't'); if (target != NULL) { cmdq_error(cmdq, "no such session: %s", target); } else cmdq_error(cmdq, "no current session"); return (CMD_RETURN_ERROR); } else oo = s->options; } quiet = args_has(self->args, 'q'); if (args->argc == 0) return (cmd_show_options_all(self, cmdq, oo, scope)); 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 char *name = args->argv[0]; const struct options_table_entry *oe; struct options_entry *o; const char *optval; retry: if (*name == '@') { if ((o = options_find1(oo, name)) == NULL) { if (quiet) return (CMD_RETURN_NORMAL); cmdq_error(cmdq, "unknown option: %s", name); 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); } oe = NULL; if (options_table_find(name, &oe) != 0) { cmdq_error(cmdq, "ambiguous option: %s", name); return (CMD_RETURN_ERROR); } if (oe == NULL) { if (quiet) return (CMD_RETURN_NORMAL); cmdq_error(cmdq, "unknown option: %s", name); return (CMD_RETURN_ERROR); } if (oe->style != NULL) { name = oe->style; goto retry; } 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, struct options *oo, enum options_table_scope scope) { const struct options_table_entry *oe; struct options_entry *o; const char *optval; int vflag; o = options_first(oo); while (o != NULL) { 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); } o = options_next(o); } vflag = args_has(self->args, 'v'); for (oe = options_table; oe->name != NULL; oe++) { if (oe->style != NULL || oe->scope != scope) continue; if ((o = options_find1(oo, oe->name)) == NULL) continue; optval = options_table_print_entry(oe, o, vflag); if (vflag) cmdq_print(cmdq, "%s", optval); else cmdq_print(cmdq, "%s %s", oe->name, optval); } return (CMD_RETURN_NORMAL); } tmate-2.4.0/cmd-source-file.c000066400000000000000000000042751356407164200157710ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2008 Tiago Cunha * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Sources a configuration file. */ enum cmd_retval cmd_source_file_exec(struct cmd *, struct cmd_q *); void cmd_source_file_done(struct cmd_q *); const struct cmd_entry cmd_source_file_entry = { .name = "source-file", .alias = "source", .args = { "", 1, 1 }, .usage = "path", .flags = 0, .exec = 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(cmdq->client); 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); } cfg_add_cause("%s", cause); free(cause); /* FALLTHROUGH */ case 0: if (cfg_references == 0) cfg_print_causes(cmdq); cmdq_free(cmdq1); return (CMD_RETURN_NORMAL); } cmdq->references++; cfg_references++; cmdq_continue(cmdq1); return (CMD_RETURN_WAIT); } void cmd_source_file_done(struct cmd_q *cmdq1) { struct cmd_q *cmdq = cmdq1->data; if (cmdq1->client_exit >= 0) cmdq->client_exit = cmdq1->client_exit; cmdq_free(cmdq1); cfg_references--; if (cmdq_free(cmdq)) return; if (cfg_references == 0) cfg_print_causes(cmdq); cmdq_continue(cmdq); } tmate-2.4.0/cmd-split-window.c000066400000000000000000000117031356407164200162060ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "tmux.h" /* * Split a window (add a new pane). */ #define SPLIT_WINDOW_TEMPLATE "#{session_name}:#{window_index}.#{pane_index}" enum cmd_retval cmd_split_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_split_window_entry = { .name = "split-window", .alias = "splitw", .args = { "bc:dF:l:hp:Pt:v", 0, -1 }, .usage = "[-bdhvP] [-c start-directory] [-F format] " "[-p percentage|-l size] " CMD_TARGET_PANE_USAGE " [command]", .tflag = CMD_PANE, .flags = 0, .exec = cmd_split_window_exec }; enum cmd_retval cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct session *s = cmdq->state.tflag.s; struct winlink *wl = cmdq->state.tflag.wl; struct window *w = wl->window; struct window_pane *wp = cmdq->state.tflag.wp, *new_wp = NULL; struct environ *env; const char *cmd, *path, *shell, *template, *cwd, *to_free; char **argv, *cause, *new_cause, *cp; u_int hlimit; int argc, size, percentage; enum layout_type type; struct layout_cell *lc; struct format_tree *ft; struct environ_entry *envent; server_unzoom_window(w); env = environ_create(); 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"); if (cmd != NULL && *cmd != '\0') { argc = 1; argv = (char **)&cmd; } else { argc = 0; argv = NULL; } } else { argc = args->argc; argv = args->argv; } to_free = NULL; if (args_has(args, 'c')) { ft = format_create(cmdq, 0); format_defaults(ft, cmdq->state.c, s, NULL, NULL); to_free = cwd = format_expand(ft, args_get(args, 'c')); format_free(ft); } else if (cmdq->client != NULL && cmdq->client->session == NULL) cwd = cmdq->client->cwd; else cwd = s->cwd; type = LAYOUT_TOPBOTTOM; if (args_has(args, 'h')) type = LAYOUT_LEFTRIGHT; size = -1; if (args_has(args, 'l')) { size = args_strtonum(args, 'l', 0, INT_MAX, &cause); if (cause != NULL) { 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; lc = layout_split_pane(wp, type, size, args_has(args, 'b')); if (lc == NULL) { cause = xstrdup("pane too small"); goto error; } new_wp = window_add_pane(w, hlimit); layout_assign_pane(lc, new_wp); path = NULL; if (cmdq->client != NULL && cmdq->client->session == NULL) envent = environ_find(cmdq->client->environ, "PATH"); else envent = environ_find(s->environ, "PATH"); if (envent != NULL) path = envent->value; if (window_pane_spawn(new_wp, argc, argv, path, shell, cwd, env, s->tio, &cause) != 0) goto error; 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(cmdq, 0); format_defaults(ft, cmdq->state.c, s, wl, new_wp); cp = format_expand(ft, template); cmdq_print(cmdq, "%s", cp); free(cp); format_free(ft); } notify_window_layout_changed(w); if (to_free != NULL) free((void *)to_free); return (CMD_RETURN_NORMAL); error: environ_free(env); if (new_wp != NULL) { layout_close_pane(new_wp); window_remove_pane(w, new_wp); } cmdq_error(cmdq, "create pane failed: %s", cause); free(cause); if (to_free != NULL) free((void *)to_free); return (CMD_RETURN_ERROR); } tmate-2.4.0/cmd-string.c000066400000000000000000000157341356407164200150640ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #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, len + 1); buf[len] = '\0'; argv = xreallocarray(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, 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, *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, len + 1); buf[len++] = ch; } buf = xrealloc(buf, 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, 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, len + 1); buf[len++] = ch; } } } if (fch == '{' && ch != '}') goto error; if (ch != EOF && fch != '{') cmd_string_ungetc(p); /* ch */ buf = xrealloc(buf, len + 1); buf[len] = '\0'; envent = environ_find(global_environ, buf); free(buf); if (envent == NULL) return (xstrdup("")); #ifdef TMATE if (envent->value == NULL) return (xstrdup("")); #endif 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, *user, *cp; int last; home = NULL; last = cmd_string_getc(s, p); if (last == EOF || last == '/' || last == ' '|| last == '\t') { 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); cp = user = xmalloc(strlen(s)); for (;;) { last = cmd_string_getc(s, p); if (last == EOF || last == '/' || last == ' '|| last == '\t') break; *cp++ = last; } *cp = '\0'; if ((pw = getpwnam(user)) != NULL) home = pw->pw_dir; free(user); } if (home == NULL) return (NULL); if (last != EOF) xasprintf(&path, "%s%c", home, last); else xasprintf(&path, "%s", home); return (path); } tmate-2.4.0/cmd-swap-pane.c000066400000000000000000000071671356407164200154520ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Swap two panes. */ enum cmd_retval cmd_swap_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_swap_pane_entry = { .name = "swap-pane", .alias = "swapp", .args = { "dDs:t:U", 0, 0 }, .usage = "[-dDU] " CMD_SRCDST_PANE_USAGE, .sflag = CMD_PANE_MARKED, .tflag = CMD_PANE, .flags = 0, .exec = cmd_swap_pane_exec }; enum cmd_retval cmd_swap_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct window *src_w, *dst_w; struct window_pane *tmp_wp, *src_wp, *dst_wp; struct layout_cell *src_lc, *dst_lc; u_int sx, sy, xoff, yoff; dst_w = cmdq->state.tflag.wl->window; dst_wp = cmdq->state.tflag.wp; src_w = cmdq->state.sflag.wl->window; src_wp = cmdq->state.sflag.wp; server_unzoom_window(dst_w); if (args_has(self->args, 'D')) { src_w = dst_w; src_wp = TAILQ_NEXT(dst_wp, entry); if (src_wp == NULL) src_wp = TAILQ_FIRST(&dst_w->panes); } else if (args_has(self->args, 'U')) { src_w = dst_w; src_wp = TAILQ_PREV(dst_wp, window_panes, entry); if (src_wp == NULL) src_wp = TAILQ_LAST(&dst_w->panes, window_panes); } server_unzoom_window(src_w); #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-2.4.0/cmd-swap-window.c000066400000000000000000000046261356407164200160330ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Swap one window with another. */ enum cmd_retval cmd_swap_window_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_swap_window_entry = { .name = "swap-window", .alias = "swapw", .args = { "ds:t:", 0, 0 }, .usage = "[-d] " CMD_SRCDST_WINDOW_USAGE, .sflag = CMD_WINDOW_MARKED, .tflag = CMD_WINDOW, .flags = 0, .exec = 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 session *src, *dst; struct session_group *sg_src, *sg_dst; struct winlink *wl_src, *wl_dst; struct window *w; wl_src = cmdq->state.sflag.wl; src = cmdq->state.sflag.s; sg_src = session_group_find(src); wl_dst = cmdq->state.tflag.wl; dst = cmdq->state.tflag.s; 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-2.4.0/cmd-switch-client.c000066400000000000000000000066401356407164200163270ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Switch client to a different session. */ enum cmd_retval cmd_switch_client_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_switch_client_entry = { .name = "switch-client", .alias = "switchc", .args = { "lc:Enpt:rT:", 0, 0 }, .usage = "[-Elnpr] [-c target-client] [-t target-session] " "[-T key-table]", .cflag = CMD_CLIENT, .tflag = CMD_SESSION_WITHPANE, .flags = CMD_READONLY, .exec = cmd_switch_client_exec }; enum cmd_retval cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct cmd_state *state = &cmdq->state; struct client *c = state->c; struct session *s = cmdq->state.tflag.s; struct window_pane *wp; const char *tablename, *update; struct key_table *table; if (args_has(args, 'r')) c->flags ^= CLIENT_READONLY; tablename = args_get(args, 'T'); if (tablename != NULL) { table = key_bindings_get_table(tablename, 0); if (table == NULL) { cmdq_error(cmdq, "table %s doesn't exist", tablename); return (CMD_RETURN_ERROR); } table->references++; key_bindings_unref_table(c->keytable); c->keytable = table; return (CMD_RETURN_NORMAL); } if (args_has(args, 'n')) { if ((s = session_next_session(c->session)) == NULL) { cmdq_error(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; else s = NULL; if (s == NULL) { cmdq_error(cmdq, "can't find last session"); return (CMD_RETURN_ERROR); } } else { if (cmdq->client == NULL) return (CMD_RETURN_NORMAL); if (state->tflag.wl != NULL) { wp = state->tflag.wp; if (wp != NULL) window_set_active_pane(wp->window, wp); session_set_current(s, state->tflag.wl); } } if (c != NULL && !args_has(args, 'E')) { update = options_get_string(s->options, "update-environment"); environ_update(update, c->environ, s->environ); } if (c->session != NULL && c->session != s) c->last_session = c->session; c->session = s; server_client_set_key_table(c, NULL); status_timer_start(c); session_update_activity(s, NULL); gettimeofday(&s->last_attached_time, NULL); recalculate_sizes(); server_check_unattached(); server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; alerts_check_session(s); return (CMD_RETURN_NORMAL); } tmate-2.4.0/cmd-unbind-key.c000066400000000000000000000070231356407164200156130ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Unbind key from command. */ enum cmd_retval cmd_unbind_key_exec(struct cmd *, struct cmd_q *); enum cmd_retval cmd_unbind_key_mode_table(struct cmd *, struct cmd_q *, key_code); const struct cmd_entry cmd_unbind_key_entry = { .name = "unbind-key", .alias = "unbind", .args = { "acnt:T:", 0, 1 }, .usage = "[-acn] [-t mode-table] [-T key-table] key", .flags = 0, .exec = cmd_unbind_key_exec }; enum cmd_retval cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; key_code key; const char *tablename; if (!args_has(args, 'a')) { if (args->argc != 1) { cmdq_error(cmdq, "missing key"); return (CMD_RETURN_ERROR); } key = key_string_lookup_string(args->argv[0]); if (key == KEYC_NONE || key == KEYC_UNKNOWN) { cmdq_error(cmdq, "unknown key: %s", args->argv[0]); return (CMD_RETURN_ERROR); } } else { if (args->argc != 0) { cmdq_error(cmdq, "key given with -a"); return (CMD_RETURN_ERROR); } key = KEYC_UNKNOWN; } if (args_has(args, 't')) return (cmd_unbind_key_mode_table(self, cmdq, key)); if (key == KEYC_UNKNOWN) { tablename = args_get(args, 'T'); if (tablename == NULL) { key_bindings_remove_table("root"); key_bindings_remove_table("prefix"); return (CMD_RETURN_NORMAL); } if (key_bindings_get_table(tablename, 0) == NULL) { cmdq_error(cmdq, "table %s doesn't exist", tablename); return (CMD_RETURN_ERROR); } key_bindings_remove_table(tablename); return (CMD_RETURN_NORMAL); } if (args_has(args, 'T')) { tablename = args_get(args, 'T'); if (key_bindings_get_table(tablename, 0) == NULL) { cmdq_error(cmdq, "table %s doesn't exist", tablename); return (CMD_RETURN_ERROR); } } else if (args_has(args, 'n')) tablename = "root"; else tablename = "prefix"; key_bindings_remove(tablename, key); return (CMD_RETURN_NORMAL); } enum cmd_retval cmd_unbind_key_mode_table(struct cmd *self, struct cmd_q *cmdq, key_code 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_UNKNOWN) { 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-2.4.0/cmd-wait-for.c000066400000000000000000000153021356407164200152750ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2013 Nicholas Marriott * Copyright (c) 2013 Thiago de Arruda * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" #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 = { .name = "wait-for", .alias = "wait", .args = { "LSU", 1, 1 }, .usage = "[-L|-S|-U] channel", .flags = 0, .exec = cmd_wait_for_exec }; struct wait_channel { const char *name; int locked; int woken; 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 *); struct wait_channel *cmd_wait_for_add(const char *); void cmd_wait_for_remove(struct wait_channel *wc); struct wait_channel * cmd_wait_for_add(const char *name) { struct wait_channel *wc; wc = xmalloc(sizeof *wc); wc->name = xstrdup(name); wc->locked = 0; wc->woken = 0; TAILQ_INIT(&wc->waiters); TAILQ_INIT(&wc->lockers); RB_INSERT(wait_channels, &wait_channels, wc); log_debug("add wait channel %s", wc->name); return (wc); } void cmd_wait_for_remove(struct wait_channel *wc) { if (wc->locked) return; if (!TAILQ_EMPTY(&wc->waiters) || !wc->woken) return; log_debug("remove wait channel %s", wc->name); RB_REMOVE(wait_channels, &wait_channels, wc); free((void *)wc->name); free(wc); } 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(__unused struct cmd_q *cmdq, const char *name, struct wait_channel *wc) { struct cmd_q *wq, *wq1; if (wc == NULL) wc = cmd_wait_for_add(name); if (TAILQ_EMPTY(&wc->waiters) && !wc->woken) { log_debug("signal wait channel %s, no waiters", wc->name); wc->woken = 1; return (CMD_RETURN_NORMAL); } log_debug("signal wait channel %s, with waiters", wc->name); TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) { TAILQ_REMOVE(&wc->waiters, wq, waitentry); if (!cmdq_free(wq)) cmdq_continue(wq); } cmd_wait_for_remove(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) { struct client *c = cmdq->client; #ifdef TMATE if (!strcmp(name, "tmate-ready") && tmate_session.tmate_env_ready) return (CMD_RETURN_NORMAL); #endif if (c == NULL || c->session != NULL) { cmdq_error(cmdq, "not able to wait"); return (CMD_RETURN_ERROR); } if (wc == NULL) wc = cmd_wait_for_add(name); if (wc->woken) { log_debug("wait channel %s already woken (%p)", wc->name, c); cmd_wait_for_remove(wc); return (CMD_RETURN_NORMAL); } log_debug("wait channel %s not woken (%p)", wc->name, c); 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 = cmd_wait_for_add(name); 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; cmd_wait_for_remove(wc); } return (CMD_RETURN_NORMAL); } void cmd_wait_for_flush(void) { struct wait_channel *wc, *wc1; struct cmd_q *wq, *wq1; RB_FOREACH_SAFE(wc, wait_channels, &wait_channels, wc1) { TAILQ_FOREACH_SAFE(wq, &wc->waiters, waitentry, wq1) { TAILQ_REMOVE(&wc->waiters, wq, waitentry); if (!cmdq_free(wq)) cmdq_continue(wq); } wc->woken = 1; TAILQ_FOREACH_SAFE(wq, &wc->lockers, waitentry, wq1) { TAILQ_REMOVE(&wc->lockers, wq, waitentry); if (!cmdq_free(wq)) cmdq_continue(wq); } wc->locked = 0; cmd_wait_for_remove(wc); } } tmate-2.4.0/cmd.c000066400000000000000000000427221356407164200135550ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include "tmux.h" extern const struct cmd_entry cmd_attach_session_entry; extern const struct cmd_entry cmd_bind_key_entry; extern const struct cmd_entry cmd_break_pane_entry; extern const struct cmd_entry cmd_capture_pane_entry; extern const struct cmd_entry cmd_choose_buffer_entry; extern const struct cmd_entry cmd_choose_client_entry; extern const struct cmd_entry cmd_choose_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_hook_entry; extern const struct cmd_entry cmd_set_option_entry; extern const struct cmd_entry cmd_set_window_option_entry; extern const struct cmd_entry cmd_show_buffer_entry; extern const struct cmd_entry cmd_show_environment_entry; extern const struct cmd_entry cmd_show_hooks_entry; extern const struct cmd_entry cmd_show_messages_entry; extern const struct cmd_entry cmd_show_options_entry; extern const struct cmd_entry cmd_show_window_options_entry; extern const struct cmd_entry cmd_source_file_entry; extern const struct cmd_entry cmd_split_window_entry; extern const struct cmd_entry cmd_start_server_entry; extern const struct cmd_entry cmd_suspend_client_entry; extern const struct cmd_entry cmd_swap_pane_entry; extern const struct cmd_entry cmd_swap_window_entry; extern const struct cmd_entry cmd_switch_client_entry; extern const struct cmd_entry cmd_unbind_key_entry; extern const struct cmd_entry cmd_unlink_window_entry; extern const struct cmd_entry cmd_up_pane_entry; extern const struct cmd_entry cmd_wait_for_entry; const struct cmd_entry *cmd_table[] = { &cmd_attach_session_entry, &cmd_bind_key_entry, &cmd_break_pane_entry, &cmd_capture_pane_entry, &cmd_choose_buffer_entry, &cmd_choose_client_entry, &cmd_choose_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_hook_entry, &cmd_set_option_entry, &cmd_set_window_option_entry, &cmd_show_buffer_entry, &cmd_show_environment_entry, &cmd_show_hooks_entry, &cmd_show_messages_entry, &cmd_show_options_entry, &cmd_show_window_options_entry, &cmd_source_file_entry, &cmd_split_window_entry, &cmd_start_server_entry, &cmd_suspend_client_entry, &cmd_swap_pane_entry, &cmd_swap_window_entry, &cmd_switch_client_entry, &cmd_unbind_key_entry, &cmd_unlink_window_entry, &cmd_wait_for_entry, NULL }; int cmd_pack_argv(int argc, char **argv, char *buf, size_t len) { size_t arglen; int i; if (argc == 0) return (0); *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 **argv) { char **new_argv; int i; if (argc == 0) return (NULL); new_argv = xcalloc(argc + 1, sizeof *new_argv); for (i = 0; i < argc; i++) { if (argv[i] != NULL) new_argv[i] = xstrdup(argv[i]); } return (new_argv); } void cmd_free_argv(int argc, char **argv) { int i; if (argc == 0) return; for (i = 0; i < argc; i++) free(argv[i]); free(argv); } char * cmd_stringify_argv(int argc, char **argv) { char *buf; int i; size_t len; if (argc == 0) return (xstrdup("")); len = 0; buf = NULL; for (i = 0; i < argc; i++) { len += strlen(argv[i]) + 1; buf = xrealloc(buf, len); if (i == 0) *buf = '\0'; else strlcat(buf, " ", len); strlcat(buf, argv[i], len); } return (buf); } 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; 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); } static int cmd_prepare_state_flag(char c, const char *target, enum cmd_entry_flag flag, struct cmd_q *cmdq, struct cmd_q *parent) { int targetflags, error; struct cmd_find_state *fs = NULL; struct cmd_find_state *current = NULL; struct cmd_find_state tmp; if (flag == CMD_NONE || flag == CMD_CLIENT || flag == CMD_CLIENT_CANFAIL) return (0); if (c == 't') fs = &cmdq->state.tflag; else if (c == 's') fs = &cmdq->state.sflag; if (flag == CMD_SESSION_WITHPANE) { if (target != NULL && target[strcspn(target, ":.")] != '\0') flag = CMD_PANE; else flag = CMD_SESSION; } targetflags = 0; switch (flag) { case CMD_SESSION: case CMD_SESSION_CANFAIL: case CMD_SESSION_PREFERUNATTACHED: if (flag == CMD_SESSION_CANFAIL) targetflags |= CMD_FIND_QUIET; if (flag == CMD_SESSION_PREFERUNATTACHED) targetflags |= CMD_FIND_PREFER_UNATTACHED; break; case CMD_MOVEW_R: flag = CMD_WINDOW_INDEX; /* FALLTHROUGH */ case CMD_WINDOW: case CMD_WINDOW_CANFAIL: case CMD_WINDOW_MARKED: case CMD_WINDOW_INDEX: if (flag == CMD_WINDOW_CANFAIL) targetflags |= CMD_FIND_QUIET; if (flag == CMD_WINDOW_MARKED) targetflags |= CMD_FIND_DEFAULT_MARKED; if (flag == CMD_WINDOW_INDEX) targetflags |= CMD_FIND_WINDOW_INDEX; break; case CMD_PANE: case CMD_PANE_CANFAIL: case CMD_PANE_MARKED: if (flag == CMD_PANE_CANFAIL) targetflags |= CMD_FIND_QUIET; if (flag == CMD_PANE_MARKED) targetflags |= CMD_FIND_DEFAULT_MARKED; break; default: fatalx("unknown %cflag %d", c, flag); } log_debug("%s: flag %c %d %#x", __func__, c, flag, targetflags); if (parent != NULL) { if (c == 't') current = &parent->state.tflag; else if (c == 's') current = &parent->state.sflag; } else { error = cmd_find_current(&tmp, cmdq, targetflags); if (error != 0 && ~targetflags & CMD_FIND_QUIET) return (-1); current = &tmp; } switch (flag) { case CMD_NONE: case CMD_CLIENT: case CMD_CLIENT_CANFAIL: return (0); case CMD_SESSION: case CMD_SESSION_CANFAIL: case CMD_SESSION_PREFERUNATTACHED: case CMD_SESSION_WITHPANE: error = cmd_find_target(fs, current, cmdq, target, CMD_FIND_SESSION, targetflags); if (error != 0 && ~targetflags & CMD_FIND_QUIET) return (-1); break; case CMD_MOVEW_R: error = cmd_find_target(fs, current, cmdq, target, CMD_FIND_SESSION, CMD_FIND_QUIET); if (error == 0) break; /* FALLTHROUGH */ case CMD_WINDOW: case CMD_WINDOW_CANFAIL: case CMD_WINDOW_MARKED: case CMD_WINDOW_INDEX: error = cmd_find_target(fs, current, cmdq, target, CMD_FIND_WINDOW, targetflags); if (error != 0 && ~targetflags & CMD_FIND_QUIET) return (-1); break; case CMD_PANE: case CMD_PANE_CANFAIL: case CMD_PANE_MARKED: error = cmd_find_target(fs, current, cmdq, target, CMD_FIND_PANE, targetflags); if (error != 0 && ~targetflags & CMD_FIND_QUIET) return (-1); break; default: fatalx("unknown %cflag %d", c, flag); } return (0); } int cmd_prepare_state(struct cmd *cmd, struct cmd_q *cmdq, struct cmd_q *parent) { const struct cmd_entry *entry = cmd->entry; struct cmd_state *state = &cmdq->state; char *tmp; enum cmd_entry_flag flag; const char *s; int error; tmp = cmd_print(cmd); log_debug("preparing state for %s (client %p)", tmp, cmdq->client); free(tmp); state->c = NULL; cmd_find_clear_state(&state->tflag, NULL, 0); cmd_find_clear_state(&state->sflag, NULL, 0); flag = cmd->entry->cflag; if (flag == CMD_NONE) { flag = cmd->entry->tflag; if (flag == CMD_CLIENT || flag == CMD_CLIENT_CANFAIL) s = args_get(cmd->args, 't'); else s = NULL; } else s = args_get(cmd->args, 'c'); switch (flag) { case CMD_CLIENT: state->c = cmd_find_client(cmdq, s, 0); if (state->c == NULL) return (-1); break; default: state->c = cmd_find_client(cmdq, s, 1); break; } s = args_get(cmd->args, 't'); log_debug("preparing -t state: target %s", s == NULL ? "none" : s); error = cmd_prepare_state_flag('t', s, entry->tflag, cmdq, parent); if (error != 0) return (error); s = args_get(cmd->args, 's'); log_debug("preparing -s state: target %s", s == NULL ? "none" : s); error = cmd_prepare_state_flag('s', s, entry->sflag, cmdq, parent); if (error != 0) return (error); return (0); } char * cmd_print(struct cmd *cmd) { char *out, *s; s = args_print(cmd->args); if (*s != '\0') xasprintf(&out, "%s %s", cmd->entry->name, s); else out = xstrdup(cmd->entry->name); free(s); return (out); } /* Adjust current mouse position for a pane. */ int cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp, u_int *yp, int last) { u_int x, y; if (last) { x = m->lx; y = m->ly; } else { x = m->x; y = m->y; } if (m->statusat == 0 && y > 0) y--; else if (m->statusat > 0 && y >= (u_int)m->statusat) y = m->statusat - 1; if (x < wp->xoff || x >= wp->xoff + wp->sx) return (-1); if (y < wp->yoff || y >= wp->yoff + wp->sy) return (-1); *xp = x - wp->xoff; *yp = y - wp->yoff; return (0); } /* Get current mouse window if any. */ struct winlink * cmd_mouse_window(struct mouse_event *m, struct session **sp) { struct session *s; struct window *w; if (!m->valid || m->s == -1 || m->w == -1) return (NULL); if ((s = session_find_by_id(m->s)) == NULL) return (NULL); if ((w = window_find_by_id(m->w)) == NULL) return (NULL); if (sp != NULL) *sp = s; return (winlink_find_by_window(&s->windows, w)); } /* Get current mouse pane if any. */ struct window_pane * cmd_mouse_pane(struct mouse_event *m, struct session **sp, struct winlink **wlp) { struct winlink *wl; struct window_pane *wp; if ((wl = cmd_mouse_window(m, sp)) == NULL) return (NULL); if ((wp = window_pane_find_by_id(m->wp)) == NULL) return (NULL); if (!window_has_pane(wl->window, wp)) return (NULL); if (wlp != NULL) *wlp = wl; return (wp); } /* Replace the first %% or %idx in template by s. */ char * cmd_template_replace(const char *template, const char *s, int idx) { char ch, *buf; const char *ptr; 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, len + 1); strlcat(buf, s, len + 1); continue; } buf = xrealloc(buf, len + 2); buf[len++] = ch; buf[len] = '\0'; } return (buf); } tmate-2.4.0/colour.c000066400000000000000000000463431356407164200143200ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" /* * Colour to string conversion functions. Bit 8 of the colour means it is one * of the 256 colour palette. */ struct colour_rgb { u_char i; u_char r; u_char g; u_char b; }; const struct colour_rgb colour_from_256[] = { { 0, 0x00, 0x00, 0x00 }, { 1, 0x00, 0x00, 0x5f }, { 2, 0x00, 0x00, 0x87 }, { 3, 0x00, 0x00, 0xaf }, { 4, 0x00, 0x00, 0xd7 }, { 5, 0x00, 0x00, 0xff }, { 6, 0x00, 0x5f, 0x00 }, { 7, 0x00, 0x5f, 0x5f }, { 8, 0x00, 0x5f, 0x87 }, { 9, 0x00, 0x5f, 0xaf }, { 10, 0x00, 0x5f, 0xd7 }, { 11, 0x00, 0x5f, 0xff }, { 12, 0x00, 0x87, 0x00 }, { 13, 0x00, 0x87, 0x5f }, { 14, 0x00, 0x87, 0x87 }, { 15, 0x00, 0x87, 0xaf }, { 16, 0x00, 0x87, 0xd7 }, { 17, 0x00, 0x87, 0xff }, { 18, 0x00, 0xaf, 0x00 }, { 19, 0x00, 0xaf, 0x5f }, { 20, 0x00, 0xaf, 0x87 }, { 21, 0x00, 0xaf, 0xaf }, { 22, 0x00, 0xaf, 0xd7 }, { 23, 0x00, 0xaf, 0xff }, { 24, 0x00, 0xd7, 0x00 }, { 25, 0x00, 0xd7, 0x5f }, { 26, 0x00, 0xd7, 0x87 }, { 27, 0x00, 0xd7, 0xaf }, { 28, 0x00, 0xd7, 0xd7 }, { 29, 0x00, 0xd7, 0xff }, { 30, 0x00, 0xff, 0x00 }, { 31, 0x00, 0xff, 0x5f }, { 32, 0x00, 0xff, 0x87 }, { 33, 0x00, 0xff, 0xaf }, { 34, 0x00, 0xff, 0xd7 }, { 35, 0x00, 0xff, 0xff }, { 36, 0x5f, 0x00, 0x00 }, { 37, 0x5f, 0x00, 0x5f }, { 38, 0x5f, 0x00, 0x87 }, { 39, 0x5f, 0x00, 0xaf }, { 40, 0x5f, 0x00, 0xd7 }, { 41, 0x5f, 0x00, 0xff }, { 42, 0x5f, 0x5f, 0x00 }, { 43, 0x5f, 0x5f, 0x5f }, { 44, 0x5f, 0x5f, 0x87 }, { 45, 0x5f, 0x5f, 0xaf }, { 46, 0x5f, 0x5f, 0xd7 }, { 47, 0x5f, 0x5f, 0xff }, { 48, 0x5f, 0x87, 0x00 }, { 49, 0x5f, 0x87, 0x5f }, { 50, 0x5f, 0x87, 0x87 }, { 51, 0x5f, 0x87, 0xaf }, { 52, 0x5f, 0x87, 0xd7 }, { 53, 0x5f, 0x87, 0xff }, { 54, 0x5f, 0xaf, 0x00 }, { 55, 0x5f, 0xaf, 0x5f }, { 56, 0x5f, 0xaf, 0x87 }, { 57, 0x5f, 0xaf, 0xaf }, { 58, 0x5f, 0xaf, 0xd7 }, { 59, 0x5f, 0xaf, 0xff }, { 60, 0x5f, 0xd7, 0x00 }, { 61, 0x5f, 0xd7, 0x5f }, { 62, 0x5f, 0xd7, 0x87 }, { 63, 0x5f, 0xd7, 0xaf }, { 64, 0x5f, 0xd7, 0xd7 }, { 65, 0x5f, 0xd7, 0xff }, { 66, 0x5f, 0xff, 0x00 }, { 67, 0x5f, 0xff, 0x5f }, { 68, 0x5f, 0xff, 0x87 }, { 69, 0x5f, 0xff, 0xaf }, { 70, 0x5f, 0xff, 0xd7 }, { 71, 0x5f, 0xff, 0xff }, { 72, 0x87, 0x00, 0x00 }, { 73, 0x87, 0x00, 0x5f }, { 74, 0x87, 0x00, 0x87 }, { 75, 0x87, 0x00, 0xaf }, { 76, 0x87, 0x00, 0xd7 }, { 77, 0x87, 0x00, 0xff }, { 78, 0x87, 0x5f, 0x00 }, { 79, 0x87, 0x5f, 0x5f }, { 80, 0x87, 0x5f, 0x87 }, { 81, 0x87, 0x5f, 0xaf }, { 82, 0x87, 0x5f, 0xd7 }, { 83, 0x87, 0x5f, 0xff }, { 84, 0x87, 0x87, 0x00 }, { 85, 0x87, 0x87, 0x5f }, { 86, 0x87, 0x87, 0x87 }, { 87, 0x87, 0x87, 0xaf }, { 88, 0x87, 0x87, 0xd7 }, { 89, 0x87, 0x87, 0xff }, { 90, 0x87, 0xaf, 0x00 }, { 91, 0x87, 0xaf, 0x5f }, { 92, 0x87, 0xaf, 0x87 }, { 93, 0x87, 0xaf, 0xaf }, { 94, 0x87, 0xaf, 0xd7 }, { 95, 0x87, 0xaf, 0xff }, { 96, 0x87, 0xd7, 0x00 }, { 97, 0x87, 0xd7, 0x5f }, { 98, 0x87, 0xd7, 0x87 }, { 99, 0x87, 0xd7, 0xaf }, { 100, 0x87, 0xd7, 0xd7 }, { 101, 0x87, 0xd7, 0xff }, { 102, 0x87, 0xff, 0x00 }, { 103, 0x87, 0xff, 0x5f }, { 104, 0x87, 0xff, 0x87 }, { 105, 0x87, 0xff, 0xaf }, { 106, 0x87, 0xff, 0xd7 }, { 107, 0x87, 0xff, 0xff }, { 108, 0xaf, 0x00, 0x00 }, { 109, 0xaf, 0x00, 0x5f }, { 110, 0xaf, 0x00, 0x87 }, { 111, 0xaf, 0x00, 0xaf }, { 112, 0xaf, 0x00, 0xd7 }, { 113, 0xaf, 0x00, 0xff }, { 114, 0xaf, 0x5f, 0x00 }, { 115, 0xaf, 0x5f, 0x5f }, { 116, 0xaf, 0x5f, 0x87 }, { 117, 0xaf, 0x5f, 0xaf }, { 118, 0xaf, 0x5f, 0xd7 }, { 119, 0xaf, 0x5f, 0xff }, { 120, 0xaf, 0x87, 0x00 }, { 121, 0xaf, 0x87, 0x5f }, { 122, 0xaf, 0x87, 0x87 }, { 123, 0xaf, 0x87, 0xaf }, { 124, 0xaf, 0x87, 0xd7 }, { 125, 0xaf, 0x87, 0xff }, { 126, 0xaf, 0xaf, 0x00 }, { 127, 0xaf, 0xaf, 0x5f }, { 128, 0xaf, 0xaf, 0x87 }, { 129, 0xaf, 0xaf, 0xaf }, { 130, 0xaf, 0xaf, 0xd7 }, { 131, 0xaf, 0xaf, 0xff }, { 132, 0xaf, 0xd7, 0x00 }, { 133, 0xaf, 0xd7, 0x5f }, { 134, 0xaf, 0xd7, 0x87 }, { 135, 0xaf, 0xd7, 0xaf }, { 136, 0xaf, 0xd7, 0xd7 }, { 137, 0xaf, 0xd7, 0xff }, { 138, 0xaf, 0xff, 0x00 }, { 139, 0xaf, 0xff, 0x5f }, { 140, 0xaf, 0xff, 0x87 }, { 141, 0xaf, 0xff, 0xaf }, { 142, 0xaf, 0xff, 0xd7 }, { 143, 0xaf, 0xff, 0xff }, { 144, 0xd7, 0x00, 0x00 }, { 145, 0xd7, 0x00, 0x5f }, { 146, 0xd7, 0x00, 0x87 }, { 147, 0xd7, 0x00, 0xaf }, { 148, 0xd7, 0x00, 0xd7 }, { 149, 0xd7, 0x00, 0xff }, { 150, 0xd7, 0x5f, 0x00 }, { 151, 0xd7, 0x5f, 0x5f }, { 152, 0xd7, 0x5f, 0x87 }, { 153, 0xd7, 0x5f, 0xaf }, { 154, 0xd7, 0x5f, 0xd7 }, { 155, 0xd7, 0x5f, 0xff }, { 156, 0xd7, 0x87, 0x00 }, { 157, 0xd7, 0x87, 0x5f }, { 158, 0xd7, 0x87, 0x87 }, { 159, 0xd7, 0x87, 0xaf }, { 160, 0xd7, 0x87, 0xd7 }, { 161, 0xd7, 0x87, 0xff }, { 162, 0xd7, 0xaf, 0x00 }, { 163, 0xd7, 0xaf, 0x5f }, { 164, 0xd7, 0xaf, 0x87 }, { 165, 0xd7, 0xaf, 0xaf }, { 166, 0xd7, 0xaf, 0xd7 }, { 167, 0xd7, 0xaf, 0xff }, { 168, 0xd7, 0xd7, 0x00 }, { 169, 0xd7, 0xd7, 0x5f }, { 170, 0xd7, 0xd7, 0x87 }, { 171, 0xd7, 0xd7, 0xaf }, { 172, 0xd7, 0xd7, 0xd7 }, { 173, 0xd7, 0xd7, 0xff }, { 174, 0xd7, 0xff, 0x00 }, { 175, 0xd7, 0xff, 0x5f }, { 176, 0xd7, 0xff, 0x87 }, { 177, 0xd7, 0xff, 0xaf }, { 178, 0xd7, 0xff, 0xd7 }, { 179, 0xd7, 0xff, 0xff }, { 180, 0xff, 0x00, 0x00 }, { 181, 0xff, 0x00, 0x5f }, { 182, 0xff, 0x00, 0x87 }, { 183, 0xff, 0x00, 0xaf }, { 184, 0xff, 0x00, 0xd7 }, { 185, 0xff, 0x00, 0xff }, { 186, 0xff, 0x5f, 0x00 }, { 187, 0xff, 0x5f, 0x5f }, { 188, 0xff, 0x5f, 0x87 }, { 189, 0xff, 0x5f, 0xaf }, { 190, 0xff, 0x5f, 0xd7 }, { 191, 0xff, 0x5f, 0xff }, { 192, 0xff, 0x87, 0x00 }, { 193, 0xff, 0x87, 0x5f }, { 194, 0xff, 0x87, 0x87 }, { 195, 0xff, 0x87, 0xaf }, { 196, 0xff, 0x87, 0xd7 }, { 197, 0xff, 0x87, 0xff }, { 198, 0xff, 0xaf, 0x00 }, { 199, 0xff, 0xaf, 0x5f }, { 200, 0xff, 0xaf, 0x87 }, { 201, 0xff, 0xaf, 0xaf }, { 202, 0xff, 0xaf, 0xd7 }, { 203, 0xff, 0xaf, 0xff }, { 204, 0xff, 0xd7, 0x00 }, { 205, 0xff, 0xd7, 0x5f }, { 206, 0xff, 0xd7, 0x87 }, { 207, 0xff, 0xd7, 0xaf }, { 208, 0xff, 0xd7, 0xd7 }, { 209, 0xff, 0xd7, 0xff }, { 210, 0xff, 0xff, 0x00 }, { 211, 0xff, 0xff, 0x5f }, { 212, 0xff, 0xff, 0x87 }, { 213, 0xff, 0xff, 0xaf }, { 214, 0xff, 0xff, 0xd7 }, { 215, 0xff, 0xff, 0xff }, { 216, 0x08, 0x08, 0x08 }, { 217, 0x12, 0x12, 0x12 }, { 218, 0x1c, 0x1c, 0x1c }, { 219, 0x26, 0x26, 0x26 }, { 220, 0x30, 0x30, 0x30 }, { 221, 0x3a, 0x3a, 0x3a }, { 222, 0x44, 0x44, 0x44 }, { 223, 0x4e, 0x4e, 0x4e }, { 224, 0x58, 0x58, 0x58 }, { 225, 0x62, 0x62, 0x62 }, { 226, 0x6c, 0x6c, 0x6c }, { 227, 0x76, 0x76, 0x76 }, { 228, 0x80, 0x80, 0x80 }, { 229, 0x8a, 0x8a, 0x8a }, { 230, 0x94, 0x94, 0x94 }, { 231, 0x9e, 0x9e, 0x9e }, { 232, 0xa8, 0xa8, 0xa8 }, { 233, 0xb2, 0xb2, 0xb2 }, { 234, 0xbc, 0xbc, 0xbc }, { 235, 0xc6, 0xc6, 0xc6 }, { 236, 0xd0, 0xd0, 0xd0 }, { 237, 0xda, 0xda, 0xda }, { 238, 0xe4, 0xe4, 0xe4 }, { 239, 0xee, 0xee, 0xee }, }; const struct colour_rgb colour_to_256[] = { { 0, 0x00, 0x00, 0x00 }, { 1, 0x00, 0x00, 0x5f }, { 2, 0x00, 0x00, 0x87 }, { 3, 0x00, 0x00, 0xaf }, { 4, 0x00, 0x00, 0xd7 }, { 5, 0x00, 0x00, 0xff }, { 6, 0x00, 0x5f, 0x00 }, { 7, 0x00, 0x5f, 0x5f }, { 8, 0x00, 0x5f, 0x87 }, { 9, 0x00, 0x5f, 0xaf }, { 10, 0x00, 0x5f, 0xd7 }, { 11, 0x00, 0x5f, 0xff }, { 12, 0x00, 0x87, 0x00 }, { 13, 0x00, 0x87, 0x5f }, { 14, 0x00, 0x87, 0x87 }, { 15, 0x00, 0x87, 0xaf }, { 16, 0x00, 0x87, 0xd7 }, { 17, 0x00, 0x87, 0xff }, { 18, 0x00, 0xaf, 0x00 }, { 19, 0x00, 0xaf, 0x5f }, { 20, 0x00, 0xaf, 0x87 }, { 21, 0x00, 0xaf, 0xaf }, { 22, 0x00, 0xaf, 0xd7 }, { 23, 0x00, 0xaf, 0xff }, { 24, 0x00, 0xd7, 0x00 }, { 25, 0x00, 0xd7, 0x5f }, { 26, 0x00, 0xd7, 0x87 }, { 27, 0x00, 0xd7, 0xaf }, { 28, 0x00, 0xd7, 0xd7 }, { 29, 0x00, 0xd7, 0xff }, { 30, 0x00, 0xff, 0x00 }, { 31, 0x00, 0xff, 0x5f }, { 32, 0x00, 0xff, 0x87 }, { 33, 0x00, 0xff, 0xaf }, { 34, 0x00, 0xff, 0xd7 }, { 35, 0x00, 0xff, 0xff }, { 216, 0x08, 0x08, 0x08 }, { 217, 0x12, 0x12, 0x12 }, { 218, 0x1c, 0x1c, 0x1c }, { 219, 0x26, 0x26, 0x26 }, { 220, 0x30, 0x30, 0x30 }, { 221, 0x3a, 0x3a, 0x3a }, { 222, 0x44, 0x44, 0x44 }, { 223, 0x4e, 0x4e, 0x4e }, { 224, 0x58, 0x58, 0x58 }, { 36, 0x5f, 0x00, 0x00 }, { 37, 0x5f, 0x00, 0x5f }, { 38, 0x5f, 0x00, 0x87 }, { 39, 0x5f, 0x00, 0xaf }, { 40, 0x5f, 0x00, 0xd7 }, { 41, 0x5f, 0x00, 0xff }, { 42, 0x5f, 0x5f, 0x00 }, { 43, 0x5f, 0x5f, 0x5f }, { 44, 0x5f, 0x5f, 0x87 }, { 45, 0x5f, 0x5f, 0xaf }, { 46, 0x5f, 0x5f, 0xd7 }, { 47, 0x5f, 0x5f, 0xff }, { 48, 0x5f, 0x87, 0x00 }, { 49, 0x5f, 0x87, 0x5f }, { 50, 0x5f, 0x87, 0x87 }, { 51, 0x5f, 0x87, 0xaf }, { 52, 0x5f, 0x87, 0xd7 }, { 53, 0x5f, 0x87, 0xff }, { 54, 0x5f, 0xaf, 0x00 }, { 55, 0x5f, 0xaf, 0x5f }, { 56, 0x5f, 0xaf, 0x87 }, { 57, 0x5f, 0xaf, 0xaf }, { 58, 0x5f, 0xaf, 0xd7 }, { 59, 0x5f, 0xaf, 0xff }, { 60, 0x5f, 0xd7, 0x00 }, { 61, 0x5f, 0xd7, 0x5f }, { 62, 0x5f, 0xd7, 0x87 }, { 63, 0x5f, 0xd7, 0xaf }, { 64, 0x5f, 0xd7, 0xd7 }, { 65, 0x5f, 0xd7, 0xff }, { 66, 0x5f, 0xff, 0x00 }, { 67, 0x5f, 0xff, 0x5f }, { 68, 0x5f, 0xff, 0x87 }, { 69, 0x5f, 0xff, 0xaf }, { 70, 0x5f, 0xff, 0xd7 }, { 71, 0x5f, 0xff, 0xff }, { 225, 0x62, 0x62, 0x62 }, { 226, 0x6c, 0x6c, 0x6c }, { 227, 0x76, 0x76, 0x76 }, { 228, 0x80, 0x80, 0x80 }, { 72, 0x87, 0x00, 0x00 }, { 73, 0x87, 0x00, 0x5f }, { 74, 0x87, 0x00, 0x87 }, { 75, 0x87, 0x00, 0xaf }, { 76, 0x87, 0x00, 0xd7 }, { 77, 0x87, 0x00, 0xff }, { 78, 0x87, 0x5f, 0x00 }, { 79, 0x87, 0x5f, 0x5f }, { 80, 0x87, 0x5f, 0x87 }, { 81, 0x87, 0x5f, 0xaf }, { 82, 0x87, 0x5f, 0xd7 }, { 83, 0x87, 0x5f, 0xff }, { 84, 0x87, 0x87, 0x00 }, { 85, 0x87, 0x87, 0x5f }, { 86, 0x87, 0x87, 0x87 }, { 87, 0x87, 0x87, 0xaf }, { 88, 0x87, 0x87, 0xd7 }, { 89, 0x87, 0x87, 0xff }, { 90, 0x87, 0xaf, 0x00 }, { 91, 0x87, 0xaf, 0x5f }, { 92, 0x87, 0xaf, 0x87 }, { 93, 0x87, 0xaf, 0xaf }, { 94, 0x87, 0xaf, 0xd7 }, { 95, 0x87, 0xaf, 0xff }, { 96, 0x87, 0xd7, 0x00 }, { 97, 0x87, 0xd7, 0x5f }, { 98, 0x87, 0xd7, 0x87 }, { 99, 0x87, 0xd7, 0xaf }, { 100, 0x87, 0xd7, 0xd7 }, { 101, 0x87, 0xd7, 0xff }, { 102, 0x87, 0xff, 0x00 }, { 103, 0x87, 0xff, 0x5f }, { 104, 0x87, 0xff, 0x87 }, { 105, 0x87, 0xff, 0xaf }, { 106, 0x87, 0xff, 0xd7 }, { 107, 0x87, 0xff, 0xff }, { 229, 0x8a, 0x8a, 0x8a }, { 230, 0x94, 0x94, 0x94 }, { 231, 0x9e, 0x9e, 0x9e }, { 232, 0xa8, 0xa8, 0xa8 }, { 108, 0xaf, 0x00, 0x00 }, { 109, 0xaf, 0x00, 0x5f }, { 110, 0xaf, 0x00, 0x87 }, { 111, 0xaf, 0x00, 0xaf }, { 112, 0xaf, 0x00, 0xd7 }, { 113, 0xaf, 0x00, 0xff }, { 114, 0xaf, 0x5f, 0x00 }, { 115, 0xaf, 0x5f, 0x5f }, { 116, 0xaf, 0x5f, 0x87 }, { 117, 0xaf, 0x5f, 0xaf }, { 118, 0xaf, 0x5f, 0xd7 }, { 119, 0xaf, 0x5f, 0xff }, { 120, 0xaf, 0x87, 0x00 }, { 121, 0xaf, 0x87, 0x5f }, { 122, 0xaf, 0x87, 0x87 }, { 123, 0xaf, 0x87, 0xaf }, { 124, 0xaf, 0x87, 0xd7 }, { 125, 0xaf, 0x87, 0xff }, { 126, 0xaf, 0xaf, 0x00 }, { 127, 0xaf, 0xaf, 0x5f }, { 128, 0xaf, 0xaf, 0x87 }, { 129, 0xaf, 0xaf, 0xaf }, { 130, 0xaf, 0xaf, 0xd7 }, { 131, 0xaf, 0xaf, 0xff }, { 132, 0xaf, 0xd7, 0x00 }, { 133, 0xaf, 0xd7, 0x5f }, { 134, 0xaf, 0xd7, 0x87 }, { 135, 0xaf, 0xd7, 0xaf }, { 136, 0xaf, 0xd7, 0xd7 }, { 137, 0xaf, 0xd7, 0xff }, { 138, 0xaf, 0xff, 0x00 }, { 139, 0xaf, 0xff, 0x5f }, { 140, 0xaf, 0xff, 0x87 }, { 141, 0xaf, 0xff, 0xaf }, { 142, 0xaf, 0xff, 0xd7 }, { 143, 0xaf, 0xff, 0xff }, { 233, 0xb2, 0xb2, 0xb2 }, { 234, 0xbc, 0xbc, 0xbc }, { 235, 0xc6, 0xc6, 0xc6 }, { 236, 0xd0, 0xd0, 0xd0 }, { 144, 0xd7, 0x00, 0x00 }, { 145, 0xd7, 0x00, 0x5f }, { 146, 0xd7, 0x00, 0x87 }, { 147, 0xd7, 0x00, 0xaf }, { 148, 0xd7, 0x00, 0xd7 }, { 149, 0xd7, 0x00, 0xff }, { 150, 0xd7, 0x5f, 0x00 }, { 151, 0xd7, 0x5f, 0x5f }, { 152, 0xd7, 0x5f, 0x87 }, { 153, 0xd7, 0x5f, 0xaf }, { 154, 0xd7, 0x5f, 0xd7 }, { 155, 0xd7, 0x5f, 0xff }, { 156, 0xd7, 0x87, 0x00 }, { 157, 0xd7, 0x87, 0x5f }, { 158, 0xd7, 0x87, 0x87 }, { 159, 0xd7, 0x87, 0xaf }, { 160, 0xd7, 0x87, 0xd7 }, { 161, 0xd7, 0x87, 0xff }, { 162, 0xd7, 0xaf, 0x00 }, { 163, 0xd7, 0xaf, 0x5f }, { 164, 0xd7, 0xaf, 0x87 }, { 165, 0xd7, 0xaf, 0xaf }, { 166, 0xd7, 0xaf, 0xd7 }, { 167, 0xd7, 0xaf, 0xff }, { 168, 0xd7, 0xd7, 0x00 }, { 169, 0xd7, 0xd7, 0x5f }, { 170, 0xd7, 0xd7, 0x87 }, { 171, 0xd7, 0xd7, 0xaf }, { 172, 0xd7, 0xd7, 0xd7 }, { 173, 0xd7, 0xd7, 0xff }, { 174, 0xd7, 0xff, 0x00 }, { 175, 0xd7, 0xff, 0x5f }, { 176, 0xd7, 0xff, 0x87 }, { 177, 0xd7, 0xff, 0xaf }, { 178, 0xd7, 0xff, 0xd7 }, { 179, 0xd7, 0xff, 0xff }, { 237, 0xda, 0xda, 0xda }, { 238, 0xe4, 0xe4, 0xe4 }, { 239, 0xee, 0xee, 0xee }, { 180, 0xff, 0x00, 0x00 }, { 181, 0xff, 0x00, 0x5f }, { 182, 0xff, 0x00, 0x87 }, { 183, 0xff, 0x00, 0xaf }, { 184, 0xff, 0x00, 0xd7 }, { 185, 0xff, 0x00, 0xff }, { 186, 0xff, 0x5f, 0x00 }, { 187, 0xff, 0x5f, 0x5f }, { 188, 0xff, 0x5f, 0x87 }, { 189, 0xff, 0x5f, 0xaf }, { 190, 0xff, 0x5f, 0xd7 }, { 191, 0xff, 0x5f, 0xff }, { 192, 0xff, 0x87, 0x00 }, { 193, 0xff, 0x87, 0x5f }, { 194, 0xff, 0x87, 0x87 }, { 195, 0xff, 0x87, 0xaf }, { 196, 0xff, 0x87, 0xd7 }, { 197, 0xff, 0x87, 0xff }, { 198, 0xff, 0xaf, 0x00 }, { 199, 0xff, 0xaf, 0x5f }, { 200, 0xff, 0xaf, 0x87 }, { 201, 0xff, 0xaf, 0xaf }, { 202, 0xff, 0xaf, 0xd7 }, { 203, 0xff, 0xaf, 0xff }, { 204, 0xff, 0xd7, 0x00 }, { 205, 0xff, 0xd7, 0x5f }, { 206, 0xff, 0xd7, 0x87 }, { 207, 0xff, 0xd7, 0xaf }, { 208, 0xff, 0xd7, 0xd7 }, { 209, 0xff, 0xd7, 0xff }, { 210, 0xff, 0xff, 0x00 }, { 211, 0xff, 0xff, 0x5f }, { 212, 0xff, 0xff, 0x87 }, { 213, 0xff, 0xff, 0xaf }, { 214, 0xff, 0xff, 0xd7 }, { 215, 0xff, 0xff, 0xff }, }; int colour_cmp_rgb(const void *, const void *); /* Compare function for bsearch(). */ int colour_cmp_rgb(const void *lhs0, const void *rhs0) { const struct colour_rgb *lhs = lhs0, *rhs = rhs0; if (lhs->r < rhs->r) return (-1); if (lhs->r > rhs->r) return (1); if (lhs->g < rhs->g) return (-1); if (lhs->g > rhs->g) return (1); if (lhs->b < rhs->b) return (-1); if (lhs->b > rhs->b) return (1); return (0); } /* Work out the nearest colour from the 256 colour set. */ int colour_find_rgb(u_char r, u_char g, u_char b) { struct colour_rgb rgb = { .r = r, .g = g, .b = b }, *found; u_int distance, lowest, colour, i; int dr, dg, db; found = bsearch(&rgb, colour_to_256, nitems(colour_to_256), sizeof colour_to_256[0], colour_cmp_rgb); if (found != NULL) return (16 + found->i); colour = 16; lowest = UINT_MAX; for (i = 0; i < 240; i++) { dr = (int)colour_from_256[i].r - r; dg = (int)colour_from_256[i].g - g; db = (int)colour_from_256[i].b - b; distance = dr * dr + dg * dg + db * db; 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%d", 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; int n; u_char r, g, b; if (*s == '#' && strlen(s) == 7) { for (cp = s + 1; isxdigit((u_char) *cp); cp++) ; if (*cp != '\0') return (-1); n = sscanf(s + 1, "%2hhx%2hhx%2hhx", &r, &g, &b); if (n != 3) return (-1); return (colour_find_rgb(r, g, b) | 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 || strcmp(s, "0") == 0) return (0); if (strcasecmp(s, "red") == 0 || strcmp(s, "1") == 0) return (1); if (strcasecmp(s, "green") == 0 || strcmp(s, "2") == 0) return (2); if (strcasecmp(s, "yellow") == 0 || strcmp(s, "3") == 0) return (3); if (strcasecmp(s, "blue") == 0 || strcmp(s, "4") == 0) return (4); if (strcasecmp(s, "magenta") == 0 || strcmp(s, "5") == 0) return (5); if (strcasecmp(s, "cyan") == 0 || strcmp(s, "6") == 0) return (6); if (strcasecmp(s, "white") == 0 || strcmp(s, "7") == 0) return (7); if (strcasecmp(s, "default") == 0 || strcmp(s, "8") == 0) return (8); if (strcasecmp(s, "brightblack") == 0 || strcmp(s, "90") == 0) return (90); if (strcasecmp(s, "brightred") == 0 || strcmp(s, "91") == 0) return (91); if (strcasecmp(s, "brightgreen") == 0 || strcmp(s, "92") == 0) return (92); if (strcasecmp(s, "brightyellow") == 0 || strcmp(s, "93") == 0) return (93); if (strcasecmp(s, "brightblue") == 0 || strcmp(s, "94") == 0) return (94); if (strcasecmp(s, "brightmagenta") == 0 || strcmp(s, "95") == 0) return (95); if (strcasecmp(s, "brightcyan") == 0 || strcmp(s, "96") == 0) return (96); if (strcasecmp(s, "brightwhite") == 0 || strcmp(s, "97") == 0) return (97); return (-1); } /* Convert 256 colour 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]); } tmate-2.4.0/compat.h000066400000000000000000000142441356407164200143000ustar00rootroot00000000000000/* * 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 __unused #define __unused __attribute__ ((__unused__)) #endif #ifndef __dead #define __dead __attribute__ ((__noreturn__)) #endif #ifndef __packed #define __packed __attribute__ ((__packed__)) #endif #ifndef ECHOPRT #define ECHOPRT 0 #endif #ifndef 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 O_DIRECTORY #define O_DIRECTORY 0 #endif #ifndef INFTIM #define INFTIM -1 #endif #ifndef WAIT_ANY #define WAIT_ANY -1 #endif #ifndef SUN_LEN #define SUN_LEN(sun) (sizeof (sun)->sun_path) #endif #ifndef timercmp #define timercmp(tvp, uvp, cmp) \ (((tvp)->tv_sec == (uvp)->tv_sec) ? \ ((tvp)->tv_usec cmp (uvp)->tv_usec) : \ ((tvp)->tv_sec cmp (uvp)->tv_sec)) #endif #ifndef timeradd #define timeradd(tvp, uvp, vvp) \ do { \ (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \ (vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \ if ((vvp)->tv_usec >= 1000000) { \ (vvp)->tv_sec++; \ (vvp)->tv_usec -= 1000000; \ } \ } while (0) #endif #ifndef timersub #define timersub(tvp, uvp, vvp) \ do { \ (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ if ((vvp)->tv_usec < 0) { \ (vvp)->tv_sec--; \ (vvp)->tv_usec += 1000000; \ } \ } while (0) #endif #ifndef TTY_NAME_MAX #define TTY_NAME_MAX 32 #endif #ifndef HOST_NAME_MAX #define HOST_NAME_MAX 255 #endif #ifndef HAVE_FLOCK #define LOCK_SH 0 #define LOCK_EX 0 #define LOCK_NB 0 #define flock(fd, op) (0) #endif #ifndef HAVE_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 */ #undef b64_ntop /* for Cygwin */ 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_FPARSELN char *fparseln(FILE *, size_t *, size_t *, const char *, int); #endif #ifndef HAVE_SETENV /* setenv.c */ int setenv(const char *, const char *, int); int unsetenv(const char *); #endif #ifndef HAVE_CFMAKERAW /* cfmakeraw.c */ void cfmakeraw(struct termios *); #endif #ifndef HAVE_OPENAT /* openat.c */ #define AT_FDCWD -100 int openat(int, const char *, int, ...); #endif #ifndef HAVE_REALLOCARRAY /* reallocarray.c */ void *reallocarray(void *, size_t, size_t size); #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-2.4.0/compat/000077500000000000000000000000001356407164200141225ustar00rootroot00000000000000tmate-2.4.0/compat/asprintf.c000066400000000000000000000026721356407164200161230ustar00rootroot00000000000000/* * 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; } va_end(ap2); return (n); error: va_end(ap2); *ret = NULL; return (-1); } tmate-2.4.0/compat/b64_ntop.c000066400000000000000000000164141356407164200157270ustar00rootroot00000000000000/* * 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 #include "compat.h" #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(const char *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-2.4.0/compat/bitstring.h000066400000000000000000000103361356407164200163030ustar00rootroot00000000000000/* $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-2.4.0/compat/cfmakeraw.c000066400000000000000000000021631356407164200162300ustar00rootroot00000000000000/* * Copyright (c) 2013 Dagobert Michelsen * Copyright (c) 2013 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "tmux.h" void cfmakeraw(struct termios *tio) { tio->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); tio->c_oflag &= ~OPOST; tio->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); tio->c_cflag &= ~(CSIZE|PARENB); tio->c_cflag |= CS8; } tmate-2.4.0/compat/closefrom.c000066400000000000000000000055261356407164200162670ustar00rootroot00000000000000/* * Copyright (c) 2004-2005 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef HAVE_CLOSEFROM #include #include #include #include #ifdef HAVE_FCNTL_H # include #endif #include #include #include #include #include #ifdef HAVE_DIRENT_H # include # define NAMLEN(dirent) strlen((dirent)->d_name) #else # define dirent direct # define NAMLEN(dirent) (dirent)->d_namlen # ifdef HAVE_SYS_NDIR_H # include # endif # ifdef HAVE_SYS_DIR_H # include # endif # ifdef HAVE_NDIR_H # include # endif #endif #include "tmux.h" #ifndef OPEN_MAX # define OPEN_MAX 256 #endif #if 0 __unused static const char rcsid[] = "$Sudo: closefrom.c,v 1.11 2006/08/17 15:26:54 millert Exp $"; #endif /* lint */ /* * Close all file descriptors greater than or equal to lowfd. */ #ifdef HAVE_FCNTL_CLOSEM void closefrom(int lowfd) { (void) fcntl(lowfd, F_CLOSEM, 0); } #else void closefrom(int lowfd) { long fd, maxfd; #if defined(HAVE_DIRFD) && defined(HAVE_PROC_PID) char fdpath[PATH_MAX], *endp; struct dirent *dent; DIR *dirp; int len; /* Check for a /proc/$$/fd directory. */ len = snprintf(fdpath, sizeof(fdpath), "/proc/%ld/fd", (long)getpid()); if (len > 0 && (size_t)len <= sizeof(fdpath) && (dirp = opendir(fdpath))) { while ((dent = readdir(dirp)) != NULL) { fd = strtol(dent->d_name, &endp, 10); if (dent->d_name != endp && *endp == '\0' && fd >= 0 && fd < INT_MAX && fd >= lowfd && fd != dirfd(dirp)) (void) close((int) fd); } (void) closedir(dirp); } else #endif { /* * Fall back on sysconf() or getdtablesize(). We avoid checking * resource limits since it is possible to open a file descriptor * and then drop the rlimit such that it is below the open fd. */ #ifdef HAVE_SYSCONF maxfd = sysconf(_SC_OPEN_MAX); #else maxfd = getdtablesize(); #endif /* HAVE_SYSCONF */ if (maxfd < 0) maxfd = OPEN_MAX; for (fd = lowfd; fd < maxfd; fd++) (void) close((int) fd); } } #endif /* !HAVE_FCNTL_CLOSEM */ #endif /* HAVE_CLOSEFROM */ tmate-2.4.0/compat/daemon.c000066400000000000000000000041631356407164200155350ustar00rootroot00000000000000/* $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-2.4.0/compat/fgetln.c000066400000000000000000000027271356407164200155550ustar00rootroot00000000000000/* * Copyright (c) 2015 Joerg Jung * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * portable fgetln() version, NOT reentrant */ #include #include #include #include "tmux.h" char * fgetln(FILE *fp, size_t *len) { static char *buf = NULL; static size_t bufsz = 0; size_t r = 0; char *p; int c, e; if (!fp || !len) { errno = EINVAL; return NULL; } if (!buf) { if (!(buf = calloc(1, BUFSIZ))) return NULL; bufsz = BUFSIZ; } while ((c = getc(fp)) != EOF) { buf[r++] = c; if (r == bufsz) { if (!(p = reallocarray(buf, 2, bufsz))) { e = errno; free(buf); errno = e; buf = NULL, bufsz = 0; return NULL; } buf = p, bufsz = 2 * bufsz; } if (c == '\n') break; } return (*len = r) ? buf : NULL; } tmate-2.4.0/compat/forkpty-aix.c000066400000000000000000000047331356407164200165520ustar00rootroot00000000000000/* * 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" pid_t forkpty(int *master, unused char *name, struct termios *tio, struct winsize *ws) { int slave = -1, fd, pipe_fd[2]; char *path, dummy; pid_t pid; if (pipe(pipe_fd) == -1) return (-1); if ((*master = open("/dev/ptc", O_RDWR|O_NOCTTY)) == -1) goto out; if ((path = ttyname(*master)) == NULL) goto out; if (name != NULL) strlcpy(name, path, TTY_NAME_MAX); if ((slave = open(path, O_RDWR|O_NOCTTY)) == -1) goto out; switch (pid = fork()) { case -1: goto out; case 0: close(*master); close(pipe_fd[1]); while (read(pipe_fd[0], &dummy, 1) == -1) { if (errno != EINTR) break; } close(pipe_fd[0]); fd = open(_PATH_TTY, O_RDWR|O_NOCTTY); if (fd >= 0) { ioctl(fd, TIOCNOTTY, NULL); close(fd); } if (setsid() < 0) fatal("setsid"); fd = open(_PATH_TTY, O_RDWR|O_NOCTTY); if (fd >= 0) fatalx("open succeeded (failed to disconnect)"); fd = open(path, O_RDWR); if (fd < 0) fatal("open failed"); close(fd); fd = open("/dev/tty", O_WRONLY); if (fd < 0) fatal("open failed"); close(fd); if (tio != NULL && tcsetattr(slave, TCSAFLUSH, tio) == -1) fatal("tcsetattr failed"); if (ioctl(slave, TIOCSWINSZ, ws) == -1) fatal("ioctl failed"); dup2(slave, 0); dup2(slave, 1); dup2(slave, 2); if (slave > 2) close(slave); return (0); } close(slave); close(pipe_fd[0]); close(pipe_fd[1]); return (pid); out: if (*master != -1) close(*master); if (slave != -1) close(slave); close(pipe_fd[0]); close(pipe_fd[1]); return (-1); } tmate-2.4.0/compat/forkpty-hpux.c000066400000000000000000000041141356407164200167460ustar00rootroot00000000000000/* * 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 = -1; char *path; pid_t pid; if ((*master = open("/dev/ptmx", O_RDWR|O_NOCTTY)) == -1) return (-1); if (grantpt(*master) != 0) goto out; if (unlockpt(*master) != 0) goto out; if ((path = ptsname(*master)) == NULL) goto out; if (name != NULL) strlcpy(name, path, TTY_NAME_MAX); if ((slave = open(path, O_RDWR|O_NOCTTY)) == -1) goto out; switch (pid = fork()) { case -1: goto out; case 0: close(*master); setsid(); #ifdef TIOCSCTTY if (ioctl(slave, TIOCSCTTY, NULL) == -1) fatal("ioctl failed"); #endif if (ioctl(slave, I_PUSH, "ptem") == -1) fatal("ioctl failed"); if (ioctl(slave, I_PUSH, "ldterm") == -1) fatal("ioctl failed"); if (tio != NULL && tcsetattr(slave, TCSAFLUSH, tio) == -1) fatal("tcsetattr failed"); if (ioctl(slave, TIOCSWINSZ, ws) == -1) fatal("ioctl failed"); dup2(slave, 0); dup2(slave, 1); dup2(slave, 2); if (slave > 2) close(slave); return (0); } close(slave); return (pid); out: if (*master != -1) close(*master); if (slave != -1) close(slave); return (-1); } tmate-2.4.0/compat/forkpty-sunos.c000066400000000000000000000041411356407164200171310ustar00rootroot00000000000000/* * 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" pid_t forkpty(int *master, char *name, struct termios *tio, struct winsize *ws) { int slave = -1; char *path; pid_t pid; if ((*master = open("/dev/ptmx", O_RDWR|O_NOCTTY)) == -1) return (-1); if (grantpt(*master) != 0) goto out; if (unlockpt(*master) != 0) goto out; if ((path = ptsname(*master)) == NULL) goto out; if (name != NULL) strlcpy(name, path, TTY_NAME_MAX); if ((slave = open(path, O_RDWR|O_NOCTTY)) == -1) goto out; switch (pid = fork()) { case -1: goto out; case 0: close(*master); setsid(); #ifdef TIOCSCTTY if (ioctl(slave, TIOCSCTTY, NULL) == -1) fatal("ioctl failed"); #endif if (ioctl(slave, I_PUSH, "ptem") == -1) fatal("ioctl failed"); if (ioctl(slave, I_PUSH, "ldterm") == -1) fatal("ioctl failed"); if (tio != NULL && tcsetattr(slave, TCSAFLUSH, tio) == -1) fatal("tcsetattr failed"); if (ioctl(slave, TIOCSWINSZ, ws) == -1) fatal("ioctl failed"); dup2(slave, 0); dup2(slave, 1); dup2(slave, 2); if (slave > 2) close(slave); return (0); } close(slave); return (pid); out: if (*master != -1) close(*master); if (slave != -1) close(slave); return (-1); } tmate-2.4.0/compat/fparseln.c000066400000000000000000000123621356407164200161040ustar00rootroot00000000000000/* $OpenBSD: fparseln.c,v 1.6 2005/08/02 21:46:23 espie Exp $ */ /* $NetBSD: fparseln.c,v 1.7 1999/07/02 15:49:12 simonb Exp $ */ /* * Copyright (c) 1997 Christos Zoulas. 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Christos Zoulas. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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. */ /* OPENBSD ORIGINAL: lib/libutil/fparseln.c */ #include #include #include #include #include "tmux.h" /* * fparseln() specific operation flags. */ #define FPARSELN_UNESCESC 0x01 #define FPARSELN_UNESCCONT 0x02 #define FPARSELN_UNESCCOMM 0x04 #define FPARSELN_UNESCREST 0x08 #define FPARSELN_UNESCALL 0x0f static int isescaped(const char *, const char *, int); /* isescaped(): * Return true if the character in *p that belongs to a string * that starts in *sp, is escaped by the escape character esc. */ static int isescaped(const char *sp, const char *p, int esc) { const char *cp; size_t ne; /* No escape character */ if (esc == '\0') return 1; /* Count the number of escape characters that precede ours */ for (ne = 0, cp = p; --cp >= sp && *cp == esc; ne++) continue; /* Return true if odd number of escape characters */ return (ne & 1) != 0; } /* fparseln(): * Read a line from a file parsing continuations ending in \ * and eliminating trailing newlines, or comments starting with * the comment char. */ char * fparseln(FILE *fp, size_t *size, size_t *lineno, const char str[3], int flags) { static const char dstr[3] = { '\\', '\\', '#' }; char *buf = NULL, *ptr, *cp, esc, con, nl, com; size_t s, len = 0; int cnt = 1; if (str == NULL) str = dstr; esc = str[0]; con = str[1]; com = str[2]; /* * XXX: it would be cool to be able to specify the newline character, * but unfortunately, fgetln does not let us */ nl = '\n'; while (cnt) { cnt = 0; if (lineno) (*lineno)++; if ((ptr = fgetln(fp, &s)) == NULL) break; if (s && com) { /* Check and eliminate comments */ for (cp = ptr; cp < ptr + s; cp++) if (*cp == com && !isescaped(ptr, cp, esc)) { s = cp - ptr; cnt = s == 0 && buf == NULL; break; } } if (s && nl) { /* Check and eliminate newlines */ cp = &ptr[s - 1]; if (*cp == nl) s--; /* forget newline */ } if (s && con) { /* Check and eliminate continuations */ cp = &ptr[s - 1]; if (*cp == con && !isescaped(ptr, cp, esc)) { s--; /* forget escape */ cnt = 1; } } if (s == 0 && buf != NULL) continue; if ((cp = realloc(buf, len + s + 1)) == NULL) { free(buf); return NULL; } buf = cp; (void) memcpy(buf + len, ptr, s); len += s; buf[len] = '\0'; } if ((flags & FPARSELN_UNESCALL) != 0 && esc && buf != NULL && strchr(buf, esc) != NULL) { ptr = cp = buf; while (cp[0] != '\0') { int skipesc; while (cp[0] != '\0' && cp[0] != esc) *ptr++ = *cp++; if (cp[0] == '\0' || cp[1] == '\0') break; skipesc = 0; if (cp[1] == com) skipesc += (flags & FPARSELN_UNESCCOMM); if (cp[1] == con) skipesc += (flags & FPARSELN_UNESCCONT); if (cp[1] == esc) skipesc += (flags & FPARSELN_UNESCESC); if (cp[1] != com && cp[1] != con && cp[1] != esc) skipesc = (flags & FPARSELN_UNESCREST); if (skipesc) cp++; else *ptr++ = *cp++; *ptr++ = *cp++; } *ptr = '\0'; len = strlen(buf); } if (size) *size = len; return buf; } #ifdef TEST int main(int, char **); int main(argc, argv) int argc; char **argv; { char *ptr; size_t size, line; line = 0; while ((ptr = fparseln(stdin, &size, &line, NULL, FPARSELN_UNESCALL)) != NULL) printf("line %d (%d) |%s|\n", line, size, ptr); return 0; } /* # This is a test line 1 line 2 \ line 3 # Comment line 4 \# Not comment \\\\ # And a comment \ line 5 \\\ line 6 */ #endif /* TEST */ tmate-2.4.0/compat/getopt.c000066400000000000000000000071421356407164200155740ustar00rootroot00000000000000/* * 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 const char *place = EMSG; /* option letter processing */ char *oli; /* option letter list index */ if (ostr == NULL) return (-1); if (BSDoptreset || !*place) { /* update scanning pointer */ BSDoptreset = 0; if (BSDoptind >= nargc || *(place = nargv[BSDoptind]) != '-') { place = EMSG; return (-1); } if (place[1] && *++place == '-') { /* found "--" */ if (place[1]) return (BADCH); ++BSDoptind; place = EMSG; return (-1); } } /* option letter okay? */ if ((BSDoptopt = (int)*place++) == (int)':' || !(oli = strchr(ostr, BSDoptopt))) { /* * if the user didn't specify '-' as an option, * assume it means -1. */ if (BSDoptopt == (int)'-') return (-1); if (!*place) ++BSDoptind; if (BSDopterr && *ostr != ':') (void)fprintf(stderr, "%s: unknown option -- %c\n", __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 = (char *)place; else if (nargc <= ++BSDoptind) { /* no arg */ place = EMSG; if (*ostr == ':') return (BADARG); if (BSDopterr) (void)fprintf(stderr, "%s: option requires an argument -- %c\n", __progname, BSDoptopt); return (BADCH); } else /* white space */ BSDoptarg = nargv[BSDoptind]; place = EMSG; ++BSDoptind; } return (BSDoptopt); /* dump back option letter */ } tmate-2.4.0/compat/imsg-buffer.c000066400000000000000000000132631356407164200165010ustar00rootroot00000000000000/* $OpenBSD: imsg-buffer.c,v 1.7 2015/07/12 18:40:49 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 #include "tmux.h" #include "imsg.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 = ERANGE; 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; memset(&iov, 0, sizeof(iov)); TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { if (i >= IOV_MAX) break; iov[i].iov_base = buf->buf + buf->rpos; iov[i].iov_len = buf->wpos - buf->rpos; i++; } again: if ((n = writev(msgbuf->fd, iov, i)) == -1) { if (errno == EINTR) goto again; if (errno == ENOBUFS) errno = EAGAIN; return (-1); } if (n == 0) { /* connection closed */ errno = 0; return (0); } msgbuf_drain(msgbuf, n); return (1); } void ibuf_free(struct ibuf *buf) { 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; memset(&iov, 0, sizeof(iov)); memset(&msg, 0, sizeof(msg)); memset(&cmsgbuf, 0, sizeof(cmsgbuf)); TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { if (i >= IOV_MAX) break; iov[i].iov_base = buf->buf + buf->rpos; iov[i].iov_len = buf->wpos - buf->rpos; i++; if (buf->fd != -1) break; } msg.msg_iov = iov; msg.msg_iovlen = i; if (buf != NULL && buf->fd != -1) { msg.msg_control = (caddr_t)&cmsgbuf.buf; msg.msg_controllen = sizeof(cmsgbuf.buf); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_len = CMSG_LEN(sizeof(int)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; *(int *)CMSG_DATA(cmsg) = buf->fd; } again: if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) { if (errno == EINTR) goto again; if (errno == ENOBUFS) errno = EAGAIN; return (-1); } if (n == 0) { /* connection closed */ errno = 0; return (0); } /* * assumption: fd got sent if sendmsg sent anything * this works because fds are passed one at a time */ if (buf != NULL && buf->fd != -1) { close(buf->fd); buf->fd = -1; } msgbuf_drain(msgbuf, n); return (1); } 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-2.4.0/compat/imsg.c000066400000000000000000000153511356407164200152320ustar00rootroot00000000000000/* $OpenBSD: imsg.c,v 1.9 2015/07/12 18:40:49 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" #include "imsg.h" int imsg_fd_overhead = 0; int imsg_get_fd(struct imsgbuf *); int available_fds(unsigned int); /* * The original code calls getdtablecount() which is OpenBSD specific. Use * available_fds() from OpenSMTPD instead. */ int available_fds(unsigned int n) { unsigned int i; int ret, fds[256]; if (n > (sizeof(fds)/sizeof(fds[0]))) return (1); ret = 0; for (i = 0; i < n; i++) { fds[i] = -1; if ((fds[i] = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) fds[i] = socket(AF_INET6, SOCK_DGRAM, 0); if (fds[i] < 0) { ret = 1; break; } } } for (i = 0; i < n && fds[i] >= 0; i++) close(fds[i]); return (ret); } void imsg_init(struct imsgbuf *ibuf, int fd) { msgbuf_init(&ibuf->w); memset(&ibuf->r, 0, sizeof(ibuf->r)); ibuf->fd = fd; ibuf->w.fd = fd; ibuf->pid = getpid(); TAILQ_INIT(&ibuf->fds); } ssize_t imsg_read(struct imsgbuf *ibuf) { struct msghdr msg; struct cmsghdr *cmsg; union { struct cmsghdr hdr; char buf[CMSG_SPACE(sizeof(int) * 1)]; } cmsgbuf; struct iovec iov; ssize_t n = -1; int fd; struct imsg_fd *ifd; memset(&msg, 0, sizeof(msg)); memset(&cmsgbuf, 0, sizeof(cmsgbuf)); iov.iov_base = ibuf->r.buf + ibuf->r.wpos; iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = &cmsgbuf.buf; msg.msg_controllen = sizeof(cmsgbuf.buf); if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL) return (-1); again: if (available_fds(imsg_fd_overhead + (CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int))) { errno = EAGAIN; free(ifd); return (-1); } if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) { if (errno == EMSGSIZE) goto fail; if (errno != EINTR && errno != EAGAIN) goto fail; goto again; } ibuf->r.wpos += n; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { int i; int j; /* * We only accept one file descriptor. Due to C * padding rules, our control buffer might contain * more than one fd, and we must close them. */ j = ((char *)cmsg + cmsg->cmsg_len - (char *)CMSG_DATA(cmsg)) / sizeof(int); for (i = 0; i < j; i++) { fd = ((int *)CMSG_DATA(cmsg))[i]; if (ifd != NULL) { ifd->fd = fd; TAILQ_INSERT_TAIL(&ibuf->fds, ifd, entry); ifd = NULL; } else close(fd); } } /* we do not handle other ctl data level */ } fail: if (ifd) free(ifd); return (n); } ssize_t imsg_get(struct imsgbuf *ibuf, struct imsg *imsg) { size_t av, left, datalen; av = ibuf->r.wpos; if (IMSG_HEADER_SIZE > av) return (0); memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr)); if (imsg->hdr.len < IMSG_HEADER_SIZE || imsg->hdr.len > MAX_IMSGSIZE) { errno = ERANGE; return (-1); } if (imsg->hdr.len > av) return (0); datalen = imsg->hdr.len - IMSG_HEADER_SIZE; ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE; if ((imsg->data = malloc(datalen)) == NULL) return (-1); if (imsg->hdr.flags & IMSGF_HASFD) imsg->fd = imsg_get_fd(ibuf); else imsg->fd = -1; memcpy(imsg->data, ibuf->r.rptr, datalen); if (imsg->hdr.len < av) { left = av - imsg->hdr.len; memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left); ibuf->r.wpos = left; } else ibuf->r.wpos = 0; return (datalen + IMSG_HEADER_SIZE); } int imsg_compose(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid, pid_t pid, int fd, const 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, const 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-2.4.0/compat/imsg.h000066400000000000000000000060221356407164200152320ustar00rootroot00000000000000/* $OpenBSD: imsg.h,v 1.3 2013/12/26 17:32:33 eric 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" #ifndef _IMSG_H_ #define _IMSG_H_ #define IBUF_READ_SIZE 65535 #define IMSG_HEADER_SIZE sizeof(struct imsg_hdr) #define MAX_IMSGSIZE 16384 struct ibuf { TAILQ_ENTRY(ibuf) entry; u_char *buf; size_t size; size_t max; size_t wpos; size_t rpos; int fd; }; struct msgbuf { TAILQ_HEAD(, ibuf) bufs; 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, const 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 *, const 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 *); #endif tmate-2.4.0/compat/openat.c000066400000000000000000000030551356407164200155570ustar00rootroot00000000000000/* * 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" int openat(int fd, const char *path, int flags, ...) { mode_t mode; va_list ap; int dotfd, retval, saved_errno; if (flags & O_CREAT) { va_start(ap, flags); mode = va_arg(ap, mode_t); va_end(ap); } else mode = 0; dotfd = -1; if (fd != AT_FDCWD) { dotfd = open(".", O_RDONLY); if (dotfd == -1) return (-1); if (fchdir(fd) != 0) { saved_errno = errno; close(dotfd); errno = saved_errno; return (-1); } } retval = open(path, flags, mode); if (dotfd != -1) { if (fchdir(dotfd) != 0) { saved_errno = errno; close(retval); close(dotfd); errno = saved_errno; return (-1); } close(dotfd); } return (retval); } tmate-2.4.0/compat/queue.h000066400000000000000000000461151356407164200154260ustar00rootroot00000000000000/* $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-2.4.0/compat/reallocarray.c000066400000000000000000000025461356407164200167550ustar00rootroot00000000000000/* $OpenBSD: reallocarray.c,v 1.3 2015/09/13 08:31:47 guenther Exp $ */ /* * Copyright (c) 2008 Otto Moerbeek * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" /* * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW */ #define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) void * reallocarray(void *optr, size_t nmemb, size_t size) { if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && nmemb > 0 && SIZE_MAX / nmemb < size) { errno = ENOMEM; return NULL; } return realloc(optr, size * nmemb); } tmate-2.4.0/compat/setenv.c000066400000000000000000000026121356407164200155730ustar00rootroot00000000000000/* * 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-2.4.0/compat/strcasestr.c000066400000000000000000000043461356407164200164720ustar00rootroot00000000000000/* $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-2.4.0/compat/strlcat.c000066400000000000000000000032461356407164200157470ustar00rootroot00000000000000/* $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-2.4.0/compat/strlcpy.c000066400000000000000000000030671356407164200157740ustar00rootroot00000000000000/* $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-2.4.0/compat/strsep.c000066400000000000000000000047231356407164200156140ustar00rootroot00000000000000/* $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-2.4.0/compat/strtonum.c000066400000000000000000000033741356407164200161700ustar00rootroot00000000000000/* $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-2.4.0/compat/tree.h000066400000000000000000000610641356407164200152410ustar00rootroot00000000000000/* $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-2.4.0/compat/unvis.c000066400000000000000000000140231356407164200154320ustar00rootroot00000000000000/* $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-2.4.0/compat/vis.c000066400000000000000000000134651356407164200151000ustar00rootroot00000000000000/* $OpenBSD: vis.c,v 1.24 2015/07/20 01:52:28 millert Exp $ */ /*- * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include "tmux.h" #define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7') #define isvisible(c,flag) \ (((c) == '\\' || (flag & VIS_ALL) == 0) && \ (((u_int)(c) <= UCHAR_MAX && isascii((u_char)(c)) && \ (((c) != '*' && (c) != '?' && (c) != '[' && (c) != '#') || \ (flag & VIS_GLOB) == 0) && isgraph((u_char)(c))) || \ ((flag & VIS_SP) == 0 && (c) == ' ') || \ ((flag & VIS_TAB) == 0 && (c) == '\t') || \ ((flag & VIS_NL) == 0 && (c) == '\n') || \ ((flag & VIS_SAFE) && ((c) == '\b' || \ (c) == '\007' || (c) == '\r' || \ isgraph((u_char)(c)))))) /* * vis - visually encode characters */ char * vis(char *dst, int c, int flag, int nextc) { if (isvisible(c, flag)) { if ((c == '"' && (flag & VIS_DQ) != 0) || (c == '\\' && (flag & VIS_NOSLASH) == 0)) *dst++ = '\\'; *dst++ = c; *dst = '\0'; return (dst); } if (flag & VIS_CSTYLE) { switch(c) { case '\n': *dst++ = '\\'; *dst++ = 'n'; goto done; case '\r': *dst++ = '\\'; *dst++ = 'r'; goto done; case '\b': *dst++ = '\\'; *dst++ = 'b'; goto done; case '\a': *dst++ = '\\'; *dst++ = 'a'; goto done; case '\v': *dst++ = '\\'; *dst++ = 'v'; goto done; case '\t': *dst++ = '\\'; *dst++ = 't'; goto done; case '\f': *dst++ = '\\'; *dst++ = 'f'; goto done; case ' ': *dst++ = '\\'; *dst++ = 's'; goto done; case '\0': *dst++ = '\\'; *dst++ = '0'; if (isoctal(nextc)) { *dst++ = '0'; *dst++ = '0'; } goto done; } } if (((c & 0177) == ' ') || (flag & VIS_OCTAL) || ((flag & VIS_GLOB) && (c == '*' || c == '?' || c == '[' || c == '#'))) { *dst++ = '\\'; *dst++ = ((u_char)c >> 6 & 07) + '0'; *dst++ = ((u_char)c >> 3 & 07) + '0'; *dst++ = ((u_char)c & 07) + '0'; goto done; } if ((flag & VIS_NOSLASH) == 0) *dst++ = '\\'; if (c & 0200) { c &= 0177; *dst++ = 'M'; } if (iscntrl((u_char)c)) { *dst++ = '^'; if (c == 0177) *dst++ = '?'; else *dst++ = c + '@'; } else { *dst++ = '-'; *dst++ = c; } done: *dst = '\0'; return (dst); } /* * strvis, strnvis, strvisx - visually encode characters from src into dst * * Dst must be 4 times the size of src to account for possible * expansion. The length of dst, not including the trailing NULL, * is returned. * * Strnvis will write no more than siz-1 bytes (and will NULL terminate). * The number of bytes needed to fully encode the string is returned. * * Strvisx encodes exactly len bytes from src into dst. * This is useful for encoding a block of data. */ int strvis(char *dst, const char *src, int flag) { char c; char *start; for (start = dst; (c = *src);) dst = vis(dst, c, flag, *++src); *dst = '\0'; return (dst - start); } int strnvis(char *dst, const char *src, size_t siz, int flag) { char *start, *end; char tbuf[5]; int c, i; i = 0; for (start = dst, end = start + siz - 1; (c = *src) && dst < end; ) { if (isvisible(c, flag)) { if ((c == '"' && (flag & VIS_DQ) != 0) || (c == '\\' && (flag & VIS_NOSLASH) == 0)) { /* need space for the extra '\\' */ if (dst + 1 >= end) { i = 2; break; } *dst++ = '\\'; } i = 1; *dst++ = c; src++; } else { i = vis(tbuf, c, flag, *++src) - tbuf; if (dst + i <= end) { memcpy(dst, tbuf, i); dst += i; } else { src--; break; } } } if (siz > 0) *dst = '\0'; if (dst + i > end) { /* adjust return value for truncation */ while ((c = *src)) dst += vis(tbuf, c, flag, *++src) - tbuf; } return (dst - start); } int stravis(char **outp, const char *src, int flag) { char *buf; int len, serrno; buf = calloc(4, strlen(src) + 1); if (buf == NULL) return -1; len = strvis(buf, src, flag); serrno = errno; *outp = realloc(buf, len + 1); if (*outp == NULL) { *outp = buf; errno = serrno; } return (len); } int strvisx(char *dst, const char *src, size_t len, int flag) { char c; char *start; for (start = dst; len > 1; len--) { c = *src; dst = vis(dst, c, flag, *++src); } if (len) dst = vis(dst, *src, flag, '\0'); *dst = '\0'; return (dst - start); } tmate-2.4.0/compat/vis.h000066400000000000000000000063551356407164200151050ustar00rootroot00000000000000/* $OpenBSD: vis.h,v 1.15 2015/07/20 01:52:27 millert Exp $ */ /* $NetBSD: vis.h,v 1.4 1994/10/26 00:56:41 cgd Exp $ */ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)vis.h 5.9 (Berkeley) 4/3/91 */ #ifndef _VIS_H_ #define _VIS_H_ /* * to select alternate encoding format */ #define VIS_OCTAL 0x01 /* use octal \ddd format */ #define VIS_CSTYLE 0x02 /* use \[nrft0..] where appropriate */ /* * to alter set of characters encoded (default is to encode all * non-graphic except space, tab, and newline). */ #define VIS_SP 0x04 /* also encode space */ #define VIS_TAB 0x08 /* also encode tab */ #define VIS_NL 0x10 /* also encode newline */ #define VIS_WHITE (VIS_SP | VIS_TAB | VIS_NL) #define VIS_SAFE 0x20 /* only encode "unsafe" characters */ #define VIS_DQ 0x200 /* backslash-escape double quotes */ #define VIS_ALL 0x400 /* encode all characters */ /* * other */ #define VIS_NOSLASH 0x40 /* inhibit printing '\' */ #define VIS_GLOB 0x100 /* encode glob(3) magics and '#' */ /* * unvis return codes */ #define UNVIS_VALID 1 /* character valid */ #define UNVIS_VALIDPUSH 2 /* character valid, push back passed char */ #define UNVIS_NOCHAR 3 /* valid sequence, no character produced */ #define UNVIS_SYNBAD -1 /* unrecognized escape sequence */ #define UNVIS_ERROR -2 /* decoder in unknown state (unrecoverable) */ /* * unvis flags */ #define UNVIS_END 1 /* no more characters */ char *vis(char *, int, int, int); int strvis(char *, const char *, int); int stravis(char **, const char *, int); int strnvis(char *, const char *, size_t, int); int strvisx(char *, const char *, size_t, int); int strunvis(char *, const char *); int unvis(char *, char, int *, int); ssize_t strnunvis(char *, const char *, size_t); #endif /* !_VIS_H_ */ tmate-2.4.0/configure.ac000066400000000000000000000332601356407164200151310ustar00rootroot00000000000000# configure.ac AC_INIT(tmate, 2.4.0) AM_SILENT_RULES([yes]) AC_CONFIG_AUX_DIR(etc) AM_INIT_AUTOMAKE([foreign subdir-objects]) AC_CANONICAL_HOST # When CFLAGS isn't set at this stage and gcc is detected by the macro below, # autoconf will automatically use CFLAGS="-O2 -g". Prevent that by using an # empty default. : ${CFLAGS=""} # 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_CPP AC_PROG_EGREP AC_PROG_INSTALL PKG_PROG_PKG_CONFIG # Default tmux.conf goes in /etc not ${prefix}/etc. test "$sysconfdir" = '${prefix}/etc' && sysconfdir=/etc # Is this --enable-debug? found_debug=yes AC_ARG_ENABLE( debug, AC_HELP_STRING(--enable-debug, enable debug build flags), found_debug=$enable_debug ) AM_CONDITIONAL(IS_DEBUG, test "x$found_debug" = xyes) # Is this --enable-coverage? AC_ARG_ENABLE( coverage, AC_HELP_STRING(--enable-coverage, enable coverage build flags), found_coverage=$enable_coverage ) AM_CONDITIONAL(IS_COVERAGE, test "x$found_coverage" = 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 # XXX Static build are only doable with the musl library PKG_CONFIG="pkg-config --static" CFLAGS="$CFLAGS -flto" LDFLAGS="$LDFLAGS -flto -static -no-pie" PKG_CHECK_MODULES([ZLIB], [zlib], [ CPPFLAGS="$ZLIB_CFLAGS $CPPFLAGS" LIBS="$ZLIB_LIBS $LIBS" ]) PKG_CHECK_MODULES([LIBCRYPTO], [libcrypto], [ CPPFLAGS="$LIBCRYPTO_CFLAGS $CPPFLAGS" LIBS="$LIBCRYPTO_LIBS $LIBS" ]) fi # Is this gcc? AM_CONDITIONAL(IS_GCC, test "x$GCC" = xyes) # Is this Sun CC? AC_EGREP_CPP( yes, [ #ifdef __SUNPRO_C yes #endif ], found_suncc=yes, found_suncc=no ) AM_CONDITIONAL(IS_SUNCC, test "x$found_suncc" = xyes) # 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) # Check for various headers. Alternatives included from compat.h. AC_CHECK_HEADERS( [ \ bitstring.h \ curses.h \ dirent.h \ execinfo.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 \ ] ) # Look for library needed for flock. AC_SEARCH_LIBS(flock, bsd) # Look for library needed for backtrace AC_SEARCH_LIBS(backtrace, execinfo) # Check for some functions that are replaced or omitted. AC_CHECK_FUNCS( [ \ backtrace \ dirfd \ flock \ setproctitle \ sysconf \ cfmakeraw \ ] ) # Look for clock_gettime. Must come before event_init. AC_SEARCH_LIBS(clock_gettime, rt) # 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 ncurses PKG_CHECK_MODULES( LIBNCURSES, ncurses, [ CPPFLAGS="$LIBNCURSES_CFLAGS $CPPFLAGS" LIBS="$LIBNCURSES_LIBS $LIBS" found_curses=yes ], [ AC_SEARCH_LIBS( setupterm, [ncurses curses terminfo], found_curses=yes, found_curses=no ) ] ) if test "x$found_curses" = xno; then AC_MSG_ERROR("curses not found") fi # Look for utempter. AC_CHECK_HEADER(utempter.h, found_utempter=yes, found_utempter=no) if test "x$found_utempter" = xyes; then AC_SEARCH_LIBS( utempter_add_record, utempter, found_utempter=yes, found_utempter=no ) if test "x$found_utempter" = xyes; then AC_DEFINE(HAVE_UTEMPTER) fi fi PKG_CHECK_MODULES( MSGPACK, msgpack >= 1.1.0, [ CPPFLAGS="$MSGPACK_CFLAGS $CPPFLAGS" LIBS="$MSGPACK_LIBS $LIBS" found_msgpack=yes ], found_msgpack=no ) if test "x$found_msgpack" = xno; then AC_MSG_ERROR("msgpack >= 1.1.0 not found") fi PKG_CHECK_MODULES( LIBSSH, libssh >= 0.8.4, [ CPPFLAGS="$LIBSSH_CFLAGS $CPPFLAGS" LIBS="$LIBSSH_LIBS $LIBS" found_libssh=yes ], found_libssh=no ) if test "x$found_libssh" = xno; then AC_MSG_ERROR("libssh >= 0.8.4 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 fparseln, compat/fparseln.c used if missing. AC_CHECK_FUNC(fparseln, found_fparseln=yes, found_fparseln=no) if test "x$found_fparseln" = xyes; then AC_DEFINE(HAVE_FPARSELN) fi AM_CONDITIONAL(NO_FPARSELN, [test "x$found_fparseln" = 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 stravis, compat/{vis,unvis}.c used if missing. AC_CHECK_FUNC(stravis, found_stravis=yes, found_stravis=no) if test "x$found_stravis" = xyes; then AC_MSG_CHECKING(if strnvis is broken) AC_EGREP_HEADER([strnvis\(char \*, const char \*, size_t, int\)], vis.h, AC_MSG_RESULT(no), [found_stravis=no]) if test "x$found_stravis" = xno; then AC_MSG_RESULT(yes) fi fi if test "x$found_stravis" = xyes; then AC_DEFINE(HAVE_VIS) fi AM_CONDITIONAL(NO_VIS, [test "x$found_stravis" = xno]) # Look for cfmakeraw, compat/cfmakeraw.c used if missing. AC_CHECK_FUNC(cfmakeraw, found_cfmakeraw=yes, found_cfmakeraw=no) if test "x$found_cfmakeraw" = xyes; then AC_DEFINE(HAVE_CFMAKERAW) fi AM_CONDITIONAL(NO_CFMAKERAW, [test "x$found_cfmakeraw" = xno]) # Look for openat, compat/openat.c used if missing. AC_CHECK_FUNC(openat, found_openat=yes, found_openat=no) if test "x$found_openat" = xyes; then AC_DEFINE(HAVE_OPENAT) fi AM_CONDITIONAL(NO_OPENAT, [test "x$found_openat" = xno]) # Look for reallocarray, compat/reallocarray.c used if missing. AC_CHECK_FUNC(reallocarray, found_reallocarray=yes, found_reallocarray=no) if test "x$found_reallocarray" = xyes; then AC_DEFINE(HAVE_REALLOCARRAY) fi AM_CONDITIONAL(NO_REALLOCARRAY, [test "x$found_reallocarray" = 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 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 # Man page defaults to mdoc. MANFORMAT=mdoc AC_SUBST(MANFORMAT) # 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 AC_DEFINE(IS_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 MANFORMAT=man ;; *hpux*) AC_MSG_RESULT(hpux) PLATFORM=hpux ;; *cygwin*) AC_MSG_RESULT(cygwin) PLATFORM=cygwin ;; *) AC_MSG_RESULT(unknown) PLATFORM=unknown ;; esac AC_SUBST(PLATFORM) AM_CONDITIONAL(IS_AIX, test "x$PLATFORM" = xaix) AM_CONDITIONAL(IS_DARWIN, test "x$PLATFORM" = xdarwin) AM_CONDITIONAL(IS_DRAGONFLY, test "x$PLATFORM" = xdragonfly) AM_CONDITIONAL(IS_LINUX, test "x$PLATFORM" = xlinux) AM_CONDITIONAL(IS_FREEBSD, test "x$PLATFORM" = xfreebsd) AM_CONDITIONAL(IS_NETBSD, test "x$PLATFORM" = xnetbsd) AM_CONDITIONAL(IS_OPENBSD, test "x$PLATFORM" = xopenbsd) AM_CONDITIONAL(IS_SUNOS, test "x$PLATFORM" = xsunos) AM_CONDITIONAL(IS_HPUX, test "x$PLATFORM" = xhpux) AM_CONDITIONAL(IS_UNKNOWN, test "x$PLATFORM" = xunknown) # autoconf should create a Makefile. AC_OUTPUT(Makefile) tmate-2.4.0/control-notify.c000066400000000000000000000120071356407164200157710ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2012 Nicholas Marriott * Copyright (c) 2012 George Nachman * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" #define CONTROL_SHOULD_NOTIFY_CLIENT(c) \ ((c) != NULL && ((c)->flags & CLIENT_CONTROL)) void control_notify_input(struct client *c, struct window_pane *wp, 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; const char *template; char *expanded; template = "%layout-change #{window_id} #{window_layout} " "#{window_visible_layout} #{window_flags}"; TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) continue; s = c->session; if (winlink_find_by_window_id(&s->windows, w->id) == NULL) continue; /* * When the last pane in a window is closed it won't have a * layout root and we don't need to inform the client about the * layout change because the whole window will go away soon. */ if (w->layout_root == NULL) continue; ft = format_create(NULL, 0); wl = winlink_find_by_window(&s->windows, w); if (wl != NULL) { format_defaults(ft, c, NULL, wl, NULL); expanded = format_expand(ft, template); control_write(c, "%s", expanded); free(expanded); } format_free(ft); } } void control_notify_window_unlinked(__unused struct session *s, struct window *w) { struct client *c; struct session *cs; TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) continue; cs = c->session; if (winlink_find_by_window_id(&cs->windows, w->id) != NULL) control_write(c, "%%window-close @%u", w->id); else control_write(c, "%%unlinked-window-close @%u", w->id); } } void control_notify_window_linked(__unused struct session *s, struct window *w) { struct client *c; struct session *cs; TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) continue; cs = c->session; if (winlink_find_by_window_id(&cs->windows, w->id) != NULL) control_write(c, "%%window-add @%u", w->id); else control_write(c, "%%unlinked-window-add @%u", w->id); } } void control_notify_window_renamed(struct window *w) { struct client *c; struct session *cs; TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) continue; cs = c->session; if (winlink_find_by_window_id(&cs->windows, w->id) != NULL) { control_write(c, "%%window-renamed @%u %s", w->id, w->name); } else { control_write(c, "%%unlinked-window-renamed @%u %s", w->id, w->name); } } } void control_notify_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; TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) continue; control_write(c, "%%session-renamed $%u %s", s->id, s->name); } } void control_notify_session_created(__unused struct session *s) { struct client *c; TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) continue; control_write(c, "%%sessions-changed"); } } void control_notify_session_close(__unused struct session *s) { struct client *c; TAILQ_FOREACH(c, &clients, entry) { if (!CONTROL_SHOULD_NOTIFY_CLIENT(c)) continue; control_write(c, "%%sessions-changed"); } } tmate-2.4.0/control.c000066400000000000000000000045231356407164200144670ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2012 Nicholas Marriott * Copyright (c) 2012 George Nachman * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include "tmux.h" /* Write a line. */ void control_write(struct client *c, const char *fmt, ...) { va_list ap; va_start(ap, fmt); evbuffer_add_vprintf(c->stdout_data, fmt, ap); va_end(ap); evbuffer_add(c->stdout_data, "\n", 1); server_client_push_stdout(c); } /* Write a buffer, adding a terminal newline. Empties buffer. */ void control_write_buffer(struct client *c, struct evbuffer *buffer) { evbuffer_add_buffer(c->stdout_data, buffer); evbuffer_add(c->stdout_data, "\n", 1); server_client_push_stdout(c); } /* Control 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; struct cmd *cmd; 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", 1); control_write(c, "parse error: %s", cause); cmdq_guard(c->cmdq, "error", 1); free(cause); } else { TAILQ_FOREACH(cmd, &cmdlist->list, qentry) cmd->flags |= CMD_CONTROL; cmdq_run(c->cmdq, cmdlist, NULL); cmd_list_free(cmdlist); } free(line); } } tmate-2.4.0/environ.c000066400000000000000000000113461356407164200144700ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Environment - manipulate a set of environment variables. */ RB_HEAD(environ, environ_entry); int environ_cmp(struct environ_entry *, struct environ_entry *); RB_PROTOTYPE(environ, environ_entry, entry, environ_cmp); 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. */ struct environ * environ_create(void) { struct environ *env; env = xcalloc(1, sizeof *env); RB_INIT(env); return (env); } /* Free an environment. */ void environ_free(struct environ *env) { struct environ_entry *envent, *envent1; RB_FOREACH_SAFE(envent, environ, env, envent1) { RB_REMOVE(environ, env, envent); free(envent->name); free(envent->value); free(envent); } free(env); } struct environ_entry * environ_first(struct environ *env) { return (RB_MIN(environ, env)); } struct environ_entry * environ_next(struct environ_entry *envent) { return (RB_NEXT(environ, env, envent)); } /* Copy one environment into another. */ void environ_copy(struct environ *srcenv, struct environ *dstenv) { struct environ_entry *envent; RB_FOREACH(envent, environ, srcenv) { if (envent->value == NULL) environ_clear(dstenv, envent->name); else environ_set(dstenv, envent->name, "%s", envent->value); } } /* Find an environment variable. */ struct environ_entry * environ_find(struct environ *env, const char *name) { struct environ_entry envent; envent.name = (char *) name; return (RB_FIND(environ, env, &envent)); } /* Set an environment variable. */ void environ_set(struct environ *env, const char *name, const char *fmt, ...) { struct environ_entry *envent; va_list ap; va_start(ap, fmt); if ((envent = environ_find(env, name)) != NULL) { free(envent->value); xvasprintf(&envent->value, fmt, ap); } else { envent = xmalloc(sizeof *envent); envent->name = xstrdup(name); xvasprintf(&envent->value, fmt, ap); RB_INSERT(environ, env, envent); } va_end(ap); } /* Clear an environment variable. */ void environ_clear(struct environ *env, const char *name) { struct environ_entry *envent; if ((envent = environ_find(env, name)) != NULL) { free(envent->value); envent->value = NULL; } else { envent = xmalloc(sizeof *envent); envent->name = xstrdup(name); envent->value = NULL; RB_INSERT(environ, env, envent); } } /* Set an environment variable from a NAME=VALUE string. */ void environ_put(struct environ *env, const char *var) { char *name, *value; value = strchr(var, '='); if (value == NULL) return; value++; name = xstrdup(var); name[strcspn(name, "=")] = '\0'; environ_set(env, name, "%s", value); free(name); } /* Unset an environment variable. */ void environ_unset(struct environ *env, const char *name) { struct environ_entry *envent; if ((envent = environ_find(env, name)) == NULL) return; RB_REMOVE(environ, env, envent); free(envent->name); free(envent->value); free(envent); } /* * Copy 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_clear(dstenv, var); else environ_set(dstenv, envent->name, "%s", envent->value); } free(copyvars); } /* Push environment into the real environment - use after fork(). */ void environ_push(struct environ *env) { struct environ_entry *envent; char *v; while (*environ != NULL) { v = xstrdup(*environ); v[strcspn(v, "=")] = '\0'; unsetenv(v); free(v); } RB_FOREACH(envent, environ, env) { if (envent->value != NULL) setenv(envent->name, envent->value, 1); } } tmate-2.4.0/example_tmux.conf000066400000000000000000000032321356407164200162160ustar00rootroot00000000000000# # Example .tmux.conf # # By Nicholas Marriott. Public domain. # # Some tweaks to the status line set -g status-bg green set -g status-right "%H:%M" set -g window-status-current-attr "underscore" # No bells at all set -g bell-action none # Lock after 15 minutes set -g lock-after-time 1800 # Keep windows around after they exit set -g remain-on-exit on # Turn on xterm-keys so that additional function keys get escape sequences set -g xterm-keys on # Change the prefix key to C-a set -g prefix C-a unbind C-b bind C-a send-prefix # Turn the mouse on, but without copy mode dragging set -g mouse on unbind -n MouseDrag1Pane unbind -temacs-copy MouseDrag1Pane # Some extra key bindings to select higher numbered windows bind F1 selectw -t:10 bind F2 selectw -t:11 bind F3 selectw -t:12 bind F4 selectw -t:13 bind F5 selectw -t:14 bind F6 selectw -t:15 bind F7 selectw -t:16 bind F8 selectw -t:17 bind F9 selectw -t:18 bind F10 selectw -t:19 bind F11 selectw -t:20 bind F12 selectw -t:21 # Keys to toggle monitoring activity in a window, and synchronize-panes bind m set monitor-activity bind y set synchronize-panes\; display 'synchronize-panes #{?synchronize-panes,on,off}' # Keys to hide and show a window name from the status line bind '-' set window-status-format '#I'\; set window-status-current-format '#I' bind '+' set window-status-format '#I:#W#F'\; set window-status-current-format '#I:#W#F' # Create a single default session new -d -s0 -nirssi 'exec irssi' set -t0:0 monitor-activity on set -t0:0 aggressive-resize on neww -d -ntodo 'exec emacs ~/TODO' setw -t0:1 aggressive-resize on neww -d -nmutt 'exec mutt' setw -t0:2 aggressive-resize on neww -d neww -d neww -d tmate-2.4.0/format.c000066400000000000000000000722731356407164200143060ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2011 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "tmux.h" #include "tmate.h" /* * Build a list of key-value pairs and use them to expand #{key} entries in a * string. */ struct format_entry; typedef void (*format_cb)(struct format_tree *, struct format_entry *); void format_job_callback(struct job *); char *format_job_get(struct format_tree *, const char *); void format_job_timer(int, short, void *); void format_cb_host(struct format_tree *, struct format_entry *); void format_cb_host_short(struct format_tree *, struct format_entry *); void format_cb_pid(struct format_tree *, struct format_entry *); void format_cb_session_alerts(struct format_tree *, struct format_entry *); void format_cb_window_layout(struct format_tree *, struct format_entry *); void format_cb_window_visible_layout(struct format_tree *, struct format_entry *); void format_cb_start_command(struct format_tree *, struct format_entry *); void format_cb_current_command(struct format_tree *, struct format_entry *); void format_cb_current_path(struct format_tree *, struct format_entry *); void format_cb_history_bytes(struct format_tree *, struct format_entry *); void format_cb_pane_tabs(struct format_tree *, struct format_entry *); char *format_find(struct format_tree *, const char *, int); void format_add_cb(struct format_tree *, const char *, format_cb); void format_add_tv(struct format_tree *, const char *, struct timeval *); int format_replace(struct format_tree *, const char *, size_t, char **, size_t *, size_t *); char *format_time_string(time_t); void format_defaults_pane_tabs(struct format_tree *, struct window_pane *); void format_defaults_session(struct format_tree *, struct session *); void format_defaults_client(struct format_tree *, struct client *); void format_defaults_winlink(struct format_tree *, struct session *, struct winlink *); /* Entry in format job tree. */ struct format_job { const char *cmd; time_t last; char *out; struct job *job; int status; RB_ENTRY(format_job) entry; }; /* Format job tree. */ struct event format_job_event; int format_job_cmp(struct format_job *, struct format_job *); RB_HEAD(format_job_tree, format_job) format_jobs = RB_INITIALIZER(); RB_PROTOTYPE(format_job_tree, format_job, entry, format_job_cmp); RB_GENERATE(format_job_tree, format_job, entry, format_job_cmp); /* Format job tree comparison function. */ int format_job_cmp(struct format_job *fj1, struct format_job *fj2) { return (strcmp(fj1->cmd, fj2->cmd)); } /* Format modifiers. */ #define FORMAT_TIMESTRING 0x1 #define FORMAT_BASENAME 0x2 #define FORMAT_DIRNAME 0x4 #define FORMAT_SUBSTITUTE 0x8 /* Entry in format tree. */ struct format_entry { char *key; char *value; time_t t; format_cb cb; RB_ENTRY(format_entry) entry; }; /* Format entry tree. */ struct format_tree { struct window *w; struct session *s; struct window_pane *wp; int flags; RB_HEAD(format_entry_tree, format_entry) tree; }; int format_entry_cmp(struct format_entry *, struct format_entry *); RB_PROTOTYPE(format_entry_tree, format_entry, entry, format_entry_cmp); RB_GENERATE(format_entry_tree, format_entry, entry, format_entry_cmp); /* Format entry tree comparison function. */ int format_entry_cmp(struct format_entry *fe1, struct format_entry *fe2) { return (strcmp(fe1->key, fe2->key)); } /* Single-character uppercase aliases. */ const char *format_upper[] = { NULL, /* A */ NULL, /* B */ NULL, /* C */ "pane_id", /* D */ NULL, /* E */ "window_flags", /* F */ NULL, /* G */ "host", /* H */ "window_index", /* I */ NULL, /* J */ NULL, /* K */ NULL, /* L */ NULL, /* M */ NULL, /* N */ NULL, /* O */ "pane_index", /* P */ NULL, /* Q */ NULL, /* R */ "session_name", /* S */ "pane_title", /* T */ NULL, /* U */ NULL, /* V */ "window_name", /* W */ NULL, /* X */ NULL, /* Y */ NULL /* Z */ }; /* Single-character lowercase aliases. */ const char *format_lower[] = { NULL, /* a */ NULL, /* b */ NULL, /* c */ NULL, /* d */ NULL, /* e */ NULL, /* f */ NULL, /* g */ "host_short", /* h */ NULL, /* i */ NULL, /* j */ NULL, /* k */ NULL, /* l */ NULL, /* m */ NULL, /* n */ NULL, /* o */ NULL, /* p */ NULL, /* q */ NULL, /* r */ NULL, /* s */ NULL, /* t */ NULL, /* u */ NULL, /* v */ NULL, /* w */ NULL, /* x */ NULL, /* y */ NULL /* z */ }; /* Format job callback. */ void format_job_callback(struct job *job) { struct format_job *fj = job->data; char *line, *buf; size_t len; struct client *c; fj->job = NULL; free(fj->out); 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; fj->out = buf; if (fj->status) { TAILQ_FOREACH(c, &clients, entry) server_status_client(c); fj->status = 0; } log_debug("%s: %s: %s", __func__, fj->cmd, fj->out); } /* Find a job. */ char * format_job_get(struct format_tree *ft, const char *cmd) { struct format_job fj0, *fj; time_t t; fj0.cmd = cmd; if ((fj = RB_FIND(format_job_tree, &format_jobs, &fj0)) == NULL) { fj = xcalloc(1, sizeof *fj); fj->cmd = xstrdup(cmd); xasprintf(&fj->out, "<'%s' not ready>", fj->cmd); RB_INSERT(format_job_tree, &format_jobs, fj); } t = time(NULL); if (fj->job == NULL && ((ft->flags & FORMAT_FORCE) || fj->last != t)) { fj->job = job_run(fj->cmd, NULL, NULL, format_job_callback, NULL, fj); if (fj->job == NULL) { free(fj->out); xasprintf(&fj->out, "<'%s' didn't start>", fj->cmd); } fj->last = t; } if (ft->flags & FORMAT_STATUS) fj->status = 1; return (format_expand(ft, fj->out)); } /* Remove old jobs. */ void format_job_timer(__unused int fd, __unused short events, __unused void *arg) { struct format_job *fj, *fj1; time_t now; struct timeval tv = { .tv_sec = 60 }; now = time(NULL); RB_FOREACH_SAFE(fj, format_job_tree, &format_jobs, fj1) { if (fj->last > now || now - fj->last < 3600) continue; RB_REMOVE(format_job_tree, &format_jobs, fj); log_debug("%s: %s", __func__, fj->cmd); if (fj->job != NULL) job_free(fj->job); free((void *)fj->cmd); free(fj->out); free(fj); } evtimer_del(&format_job_event); evtimer_add(&format_job_event, &tv); } /* Callback for host. */ void format_cb_host(__unused struct format_tree *ft, struct format_entry *fe) { char host[HOST_NAME_MAX + 1]; if (gethostname(host, sizeof host) != 0) fe->value = xstrdup(""); else fe->value = xstrdup(host); } /* Callback for host_short. */ void format_cb_host_short(__unused struct format_tree *ft, struct format_entry *fe) { char host[HOST_NAME_MAX + 1], *cp; if (gethostname(host, sizeof host) != 0) fe->value = xstrdup(""); else { if ((cp = strchr(host, '.')) != NULL) *cp = '\0'; fe->value = xstrdup(host); } } /* Callback for pid. */ void format_cb_pid(__unused struct format_tree *ft, struct format_entry *fe) { xasprintf(&fe->value, "%ld", (long)getpid()); } /* Callback for session_alerts. */ void format_cb_session_alerts(struct format_tree *ft, struct format_entry *fe) { struct session *s = ft->s; struct winlink *wl; char alerts[256], tmp[16]; if (s == NULL) return; *alerts = '\0'; RB_FOREACH(wl, winlinks, &s->windows) { if ((wl->flags & WINLINK_ALERTFLAGS) == 0) continue; xsnprintf(tmp, sizeof tmp, "%u", wl->idx); if (*alerts != '\0') strlcat(alerts, ",", sizeof alerts); strlcat(alerts, tmp, sizeof alerts); if (wl->flags & WINLINK_ACTIVITY) strlcat(alerts, "#", sizeof alerts); if (wl->flags & WINLINK_BELL) strlcat(alerts, "!", sizeof alerts); if (wl->flags & WINLINK_SILENCE) strlcat(alerts, "~", sizeof alerts); } fe->value = xstrdup(alerts); } /* Callback for window_layout. */ void format_cb_window_layout(struct format_tree *ft, struct format_entry *fe) { struct window *w = ft->w; if (w == NULL) return; if (w->saved_layout_root != NULL) fe->value = layout_dump(w->saved_layout_root); else fe->value = layout_dump(w->layout_root); } /* Callback for window_visible_layout. */ void format_cb_window_visible_layout(struct format_tree *ft, struct format_entry *fe) { struct window *w = ft->w; if (w == NULL) return; fe->value = layout_dump(w->layout_root); } /* Callback for pane_start_command. */ void format_cb_start_command(struct format_tree *ft, struct format_entry *fe) { struct window_pane *wp = ft->wp; if (wp == NULL) return; fe->value = cmd_stringify_argv(wp->argc, wp->argv); } /* Callback for pane_current_command. */ void format_cb_current_command(struct format_tree *ft, struct format_entry *fe) { struct window_pane *wp = ft->wp; char *cmd; if (wp == NULL) return; cmd = osdep_get_name(wp->fd, wp->tty); if (cmd == NULL || *cmd == '\0') { free(cmd); cmd = cmd_stringify_argv(wp->argc, wp->argv); if (cmd == NULL || *cmd == '\0') { free(cmd); cmd = xstrdup(wp->shell); } } fe->value = parse_window_name(cmd); free(cmd); } /* Callback for pane_current_path. */ void format_cb_current_path(struct format_tree *ft, struct format_entry *fe) { struct window_pane *wp = ft->wp; char *cwd; if (wp == NULL) return; cwd = osdep_get_cwd(wp->fd); if (cwd != NULL) fe->value = xstrdup(cwd); } /* Callback for history_bytes. */ void format_cb_history_bytes(struct format_tree *ft, struct format_entry *fe) { struct window_pane *wp = ft->wp; struct grid *gd; struct grid_line *gl; unsigned long long size; u_int i; if (wp == NULL) return; gd = wp->base.grid; size = 0; for (i = 0; i < gd->hsize; i++) { gl = &gd->linedata[i]; size += gl->cellsize * sizeof *gl->celldata; size += gl->extdsize * sizeof *gl->extddata; } size += gd->hsize * sizeof *gd->linedata; xasprintf(&fe->value, "%llu", size); } /* Callback for pane_tabs. */ void format_cb_pane_tabs(struct format_tree *ft, struct format_entry *fe) { struct window_pane *wp = ft->wp; struct evbuffer *buffer; u_int i; int size; if (wp == NULL) return; buffer = evbuffer_new(); for (i = 0; i < wp->base.grid->sx; i++) { if (!bit_test(wp->base.tabs, i)) continue; if (EVBUFFER_LENGTH(buffer) > 0) evbuffer_add(buffer, ",", 1); evbuffer_add_printf(buffer, "%u", i); } size = EVBUFFER_LENGTH(buffer); xasprintf(&fe->value, "%.*s", size, EVBUFFER_DATA(buffer)); evbuffer_free(buffer); } /* Create a new tree. */ struct format_tree * format_create(struct cmd_q *cmdq, int flags) { struct format_tree *ft; if (!event_initialized(&format_job_event)) { evtimer_set(&format_job_event, format_job_timer, NULL); format_job_timer(-1, 0, NULL); } ft = xcalloc(1, sizeof *ft); RB_INIT(&ft->tree); ft->flags = flags; format_add_cb(ft, "host", format_cb_host); format_add_cb(ft, "host_short", format_cb_host_short); format_add_cb(ft, "pid", format_cb_pid); format_add(ft, "socket_path", "%s", socket_path); format_add_tv(ft, "start_time", &start_time); if (cmdq != NULL && cmdq->cmd != NULL) format_add(ft, "command_name", "%s", cmdq->cmd->entry->name); return (ft); } /* Free a tree. */ void format_free(struct format_tree *ft) { struct format_entry *fe, *fe1; RB_FOREACH_SAFE(fe, format_entry_tree, &ft->tree, fe1) { RB_REMOVE(format_entry_tree, &ft->tree, fe); free(fe->value); free(fe->key); free(fe); } free(ft); } /* Add a key-value pair. */ void format_add(struct format_tree *ft, const char *key, const char *fmt, ...) { struct format_entry *fe; struct format_entry *fe_now; va_list ap; fe = xmalloc(sizeof *fe); fe->key = xstrdup(key); fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe); if (fe_now != NULL) { free(fe->key); free(fe); free(fe_now->value); fe = fe_now; } fe->cb = NULL; fe->t = 0; va_start(ap, fmt); xvasprintf(&fe->value, fmt, ap); va_end(ap); } /* Add a key and time. */ void format_add_tv(struct format_tree *ft, const char *key, struct timeval *tv) { struct format_entry *fe; struct format_entry *fe_now; fe = xmalloc(sizeof *fe); fe->key = xstrdup(key); fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe); if (fe_now != NULL) { free(fe->key); free(fe); free(fe_now->value); fe = fe_now; } fe->cb = NULL; fe->t = tv->tv_sec; fe->value = NULL; } /* Add a key and function. */ void format_add_cb(struct format_tree *ft, const char *key, format_cb cb) { struct format_entry *fe; struct format_entry *fe_now; fe = xmalloc(sizeof *fe); fe->key = xstrdup(key); fe_now = RB_INSERT(format_entry_tree, &ft->tree, fe); if (fe_now != NULL) { free(fe->key); free(fe); free(fe_now->value); fe = fe_now; } fe->cb = cb; fe->t = 0; fe->value = NULL; } /* Find a format entry. */ char * format_find(struct format_tree *ft, const char *key, int modifiers) { struct format_entry *fe, fe_find; struct options_entry *o; struct environ_entry *envent; static char s[64]; const char *found; char *copy, *saved; found = NULL; if (~modifiers & FORMAT_TIMESTRING) { o = options_find(global_options, key); if (o == NULL && ft->w != NULL) o = options_find(ft->w->options, key); if (o == NULL) o = options_find(global_w_options, key); if (o == NULL && ft->s != NULL) o = options_find(ft->s->options, key); if (o == NULL) o = options_find(global_s_options, key); if (o != NULL) { switch (o->type) { case OPTIONS_STRING: found = o->str; goto found; case OPTIONS_NUMBER: xsnprintf(s, sizeof s, "%lld", o->num); found = s; goto found; case OPTIONS_STYLE: found = style_tostring(&o->style); goto found; } } } fe_find.key = (char *) key; fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find); if (fe != NULL) { if (modifiers & FORMAT_TIMESTRING) { if (fe->t == 0) return (NULL); ctime_r(&fe->t, s); s[strcspn(s, "\n")] = '\0'; found = s; goto found; } if (fe->t != 0) { xsnprintf(s, sizeof s, "%lld", (long long)fe->t); found = s; goto found; } if (fe->value == NULL && fe->cb != NULL) fe->cb(ft, fe); found = fe->value; goto found; } if (~modifiers & FORMAT_TIMESTRING) { envent = NULL; if (ft->s != NULL) envent = environ_find(ft->s->environ, key); if (envent == NULL) envent = environ_find(global_environ, key); if (envent != NULL) { found = envent->value; goto found; } } return (NULL); found: if (found == NULL) return (NULL); copy = xstrdup(found); if (modifiers & FORMAT_BASENAME) { saved = copy; copy = xstrdup(basename(saved)); free(saved); } if (modifiers & FORMAT_DIRNAME) { saved = copy; copy = xstrdup(dirname(saved)); free(saved); } return (copy); } /* * 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, *copy0, *endptr, *ptr, *found, *new, *value; char *from = NULL, *to = NULL; size_t valuelen, newlen, fromlen, tolen, used; long limit = 0; int modifiers = 0, brackets; /* Make a copy of the key. */ copy0 = copy = xmalloc(keylen + 1); memcpy(copy, key, keylen); copy[keylen] = '\0'; /* Is there a length limit or whatnot? */ switch (copy[0]) { case '=': errno = 0; limit = strtol(copy + 1, &endptr, 10); if (errno == ERANGE && (limit == LONG_MIN || limit == LONG_MAX)) break; if (*endptr != ':') break; copy = endptr + 1; break; case 'b': if (copy[1] != ':') break; modifiers |= FORMAT_BASENAME; copy += 2; break; case 'd': if (copy[1] != ':') break; modifiers |= FORMAT_DIRNAME; copy += 2; break; case 't': if (copy[1] != ':') break; modifiers |= FORMAT_TIMESTRING; copy += 2; break; case 's': if (copy[1] != '/') break; from = copy + 2; for (copy = from; *copy != '\0' && *copy != '/'; copy++) /* nothing */; if (copy[0] != '/' || copy == from) { copy = copy0; break; } copy[0] = '\0'; to = copy + 1; for (copy = to; *copy != '\0' && *copy != '/'; copy++) /* nothing */; if (copy[0] != '/' || copy[1] != ':') { copy = copy0; break; } copy[0] = '\0'; modifiers |= FORMAT_SUBSTITUTE; copy += 2; break; } /* * 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 = ptr + 1; found = format_find(ft, copy + 1, modifiers); brackets = 0; for (ptr = ptr + 1; *ptr != '\0'; ptr++) { if (*ptr == '{') brackets++; if (*ptr == '}') brackets--; if (*ptr == ',' && brackets == 0) break; } if (*ptr == '\0') goto fail; if (found != NULL && *found != '\0' && (found[0] != '0' || found[1] != '\0')) { *ptr = '\0'; } else value = ptr + 1; value = format_expand(ft, value); free(found); } else { value = format_find(ft, copy, modifiers); if (value == NULL) value = xstrdup(""); } /* Perform substitution if any. */ if (modifiers & FORMAT_SUBSTITUTE) { fromlen = strlen(from); tolen = strlen(to); newlen = strlen(value) + 1; copy = new = xmalloc(newlen); for (ptr = value; *ptr != '\0'; /* nothing */) { if (strncmp(ptr, from, fromlen) != 0) { *new++ = *ptr++; continue; } used = new - copy; newlen += tolen; copy = xrealloc(copy, newlen); new = copy + used; memcpy(new, to, tolen); new += tolen; ptr += fromlen; } *new = '\0'; free(value); value = copy; } /* Truncate the value if needed. */ if (limit > 0) { new = utf8_trimcstr(value, limit); free(value); value = new; } else if (limit < 0) { new = utf8_rtrimcstr(value, -limit); free(value); value = new; } /* Expand the buffer and copy in the value. */ valuelen = strlen(value); while (*len - *off < valuelen + 1) { *buf = xreallocarray(*buf, 2, *len); *len *= 2; } memcpy(*buf + *off, value, valuelen); *off += valuelen; free(value); free(copy0); return (0); fail: free(copy0); return (-1); } /* Expand keys in a template, passing through strftime first. */ char * format_expand_time(struct format_tree *ft, const char *fmt, time_t t) { char *tmp, *expanded; size_t tmplen; struct tm *tm; if (fmt == NULL || *fmt == '\0') return (xstrdup("")); tm = localtime(&t); tmp = NULL; tmplen = strlen(fmt); do { tmp = xreallocarray(tmp, 2, tmplen); tmplen *= 2; } while (strftime(tmp, tmplen, fmt, tm) == 0); expanded = format_expand(ft, tmp); free(tmp); return (expanded); } /* Expand keys in a template. */ char * format_expand(struct format_tree *ft, const char *fmt) { char *buf, *tmp, *cmd, *out; const char *ptr, *s, *saved = fmt; size_t off, len, n, outlen; int ch, brackets; if (fmt == NULL) return (xstrdup("")); #ifdef TMATE tmate_format(ft); #endif len = 64; buf = xmalloc(len); off = 0; while (*fmt != '\0') { if (*fmt != '#') { while (len - off < 2) { buf = xreallocarray(buf, 2, len); len *= 2; } buf[off++] = *fmt++; continue; } fmt++; ch = (u_char) *fmt++; switch (ch) { case '(': brackets = 1; for (ptr = fmt; *ptr != '\0'; ptr++) { if (*ptr == '(') brackets++; if (*ptr == ')' && --brackets == 0) break; } if (*ptr != ')' || brackets != 0) break; n = ptr - fmt; tmp = xmalloc(n + 1); memcpy(tmp, fmt, n); tmp[n] = '\0'; cmd = format_expand(ft, tmp); out = format_job_get(ft, cmd); outlen = strlen(out); free(cmd); free(tmp); while (len - off < outlen + 1) { buf = xreallocarray(buf, 2, len); len *= 2; } memcpy(buf + off, out, outlen); off += outlen; free(out); fmt += n + 1; continue; case '{': brackets = 1; for (ptr = fmt; *ptr != '\0'; ptr++) { if (*ptr == '{') brackets++; if (*ptr == '}' && --brackets == 0) break; } if (*ptr != '}' || brackets != 0) break; n = ptr - fmt; if (format_replace(ft, fmt, n, &buf, &len, &off) != 0) break; fmt += n + 1; continue; case '#': while (len - off < 2) { buf = xreallocarray(buf, 2, len); len *= 2; } buf[off++] = '#'; continue; default: s = NULL; if (ch >= 'A' && ch <= 'Z') s = format_upper[ch - 'A']; else if (ch >= 'a' && ch <= 'z') s = format_lower[ch - 'a']; if (s == NULL) { while (len - off < 3) { buf = xreallocarray(buf, 2, len); len *= 2; } buf[off++] = '#'; buf[off++] = ch; continue; } n = strlen(s); if (format_replace(ft, s, n, &buf, &len, &off) != 0) break; continue; } break; } buf[off] = '\0'; log_debug("format '%s' -> '%s'", saved, buf); return (buf); } /* Set defaults for any of arguments that are not NULL. */ void format_defaults(struct format_tree *ft, struct client *c, struct session *s, struct winlink *wl, struct window_pane *wp) { if (s == NULL && c != NULL) s = c->session; if (wl == NULL && s != NULL) wl = s->curw; if (wp == NULL && wl != NULL) wp = wl->window->active; if (c != NULL) format_defaults_client(ft, c); if (s != NULL) format_defaults_session(ft, s); if (s != NULL && wl != NULL) format_defaults_winlink(ft, s, wl); if (wp != NULL) format_defaults_pane(ft, wp); } /* Set default format keys for a session. */ void format_defaults_session(struct format_tree *ft, struct session *s) { struct session_group *sg; ft->s = s; format_add(ft, "session_name", "%s", s->name); format_add(ft, "session_windows", "%u", winlink_count(&s->windows)); format_add(ft, "session_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)); format_add_tv(ft, "session_created", &s->creation_time); format_add_tv(ft, "session_last_attached", &s->last_attached_time); format_add_tv(ft, "session_activity", &s->activity_time); format_add(ft, "session_attached", "%u", s->attached); format_add(ft, "session_many_attached", "%d", s->attached > 1); format_add_cb(ft, "session_alerts", format_cb_session_alerts); } /* Set default format keys for a client. */ void format_defaults_client(struct format_tree *ft, struct client *c) { struct session *s; const char *name; if (ft->s == NULL) ft->s = c->session; format_add(ft, "client_pid", "%ld", (long) c->pid); format_add(ft, "client_height", "%u", c->tty.sy); format_add(ft, "client_width", "%u", c->tty.sx); if (c->tty.path != NULL) format_add(ft, "client_tty", "%s", c->tty.path); if (c->tty.termname != NULL) format_add(ft, "client_termname", "%s", c->tty.termname); format_add(ft, "client_control_mode", "%d", !!(c->flags & CLIENT_CONTROL)); format_add_tv(ft, "client_created", &c->creation_time); format_add_tv(ft, "client_activity", &c->activity_time); name = server_client_get_key_table(c); if (strcmp(c->keytable->name, name) == 0) format_add(ft, "client_prefix", "%d", 0); else format_add(ft, "client_prefix", "%d", 1); format_add(ft, "client_key_table", "%s", c->keytable->name); if (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 window. */ void format_defaults_window(struct format_tree *ft, struct window *w) { ft->w = w; format_add_tv(ft, "window_activity", &w->activity_time); format_add(ft, "window_id", "@%u", w->id); format_add(ft, "window_name", "%s", w->name); format_add(ft, "window_width", "%u", w->sx); format_add(ft, "window_height", "%u", w->sy); format_add_cb(ft, "window_layout", format_cb_window_layout); format_add_cb(ft, "window_visible_layout", format_cb_window_visible_layout); format_add(ft, "window_panes", "%u", window_count_panes(w)); format_add(ft, "window_zoomed_flag", "%d", !!(w->flags & WINDOW_ZOOMED)); } /* Set default format keys for a winlink. */ void format_defaults_winlink(struct format_tree *ft, struct session *s, struct winlink *wl) { struct window *w = wl->window; char *flags; if (ft->w == NULL) ft->w = wl->window; flags = window_printable_flags(s, wl); format_defaults_window(ft, w); format_add(ft, "window_index", "%d", wl->idx); format_add(ft, "window_flags", "%s", flags); format_add(ft, "window_active", "%d", wl == s->curw); format_add(ft, "window_bell_flag", "%d", !!(wl->flags & WINLINK_BELL)); format_add(ft, "window_activity_flag", "%d", !!(wl->flags & WINLINK_ACTIVITY)); format_add(ft, "window_silence_flag", "%d", !!(wl->flags & WINLINK_SILENCE)); format_add(ft, "window_last_flag", "%d", !!(wl == TAILQ_FIRST(&s->lastw))); format_add(ft, "window_linked", "%d", session_is_linked(s, wl->window)); free(flags); } /* Set default format keys for a window pane. */ void format_defaults_pane(struct format_tree *ft, struct window_pane *wp) { struct grid *gd = wp->base.grid; u_int idx; int status, scroll_position; if (ft->w == NULL) ft->w = wp->window; ft->wp = wp; format_add(ft, "history_size", "%u", gd->hsize); format_add(ft, "history_limit", "%u", gd->hlimit); format_add_cb(ft, "history_bytes", format_cb_history_bytes); if (window_pane_index(wp, &idx) != 0) fatalx("index not found"); format_add(ft, "pane_index", "%u", idx); format_add(ft, "pane_width", "%u", wp->sx); format_add(ft, "pane_height", "%u", wp->sy); format_add(ft, "pane_title", "%s", wp->base.title); format_add(ft, "pane_id", "%%%u", wp->id); format_add(ft, "pane_active", "%d", wp == wp->window->active); format_add(ft, "pane_input_off", "%d", !!(wp->flags & PANE_INPUTOFF)); status = wp->status; if (wp->fd == -1 && WIFEXITED(status)) format_add(ft, "pane_dead_status", "%d", WEXITSTATUS(status)); format_add(ft, "pane_dead", "%d", wp->fd == -1); if (window_pane_visible(wp)) { format_add(ft, "pane_left", "%u", wp->xoff); format_add(ft, "pane_top", "%u", wp->yoff); format_add(ft, "pane_right", "%u", wp->xoff + wp->sx - 1); format_add(ft, "pane_bottom", "%u", wp->yoff + wp->sy - 1); } format_add(ft, "pane_in_mode", "%d", wp->screen != &wp->base); format_add(ft, "pane_synchronized", "%d", !!options_get_number(wp->window->options, "synchronize-panes")); format_add(ft, "pane_tty", "%s", wp->tty); format_add(ft, "pane_pid", "%ld", (long) wp->pid); format_add_cb(ft, "pane_start_command", format_cb_start_command); format_add_cb(ft, "pane_current_command", format_cb_current_command); format_add_cb(ft, "pane_current_path", format_cb_current_path); format_add(ft, "cursor_x", "%u", wp->base.cx); format_add(ft, "cursor_y", "%u", wp->base.cy); format_add(ft, "scroll_region_upper", "%u", wp->base.rupper); format_add(ft, "scroll_region_lower", "%u", wp->base.rlower); scroll_position = window_copy_scroll_position(wp); if (scroll_position != -1) format_add(ft, "scroll_position", "%d", scroll_position); format_add(ft, "alternate_on", "%d", wp->saved_grid ? 1 : 0); format_add(ft, "alternate_saved_x", "%u", wp->saved_cx); format_add(ft, "alternate_saved_y", "%u", wp->saved_cy); format_add(ft, "cursor_flag", "%d", !!(wp->base.mode & MODE_CURSOR)); format_add(ft, "insert_flag", "%d", !!(wp->base.mode & MODE_INSERT)); format_add(ft, "keypad_cursor_flag", "%d", !!(wp->base.mode & MODE_KCURSOR)); format_add(ft, "keypad_flag", "%d", !!(wp->base.mode & MODE_KKEYPAD)); format_add(ft, "wrap_flag", "%d", !!(wp->base.mode & MODE_WRAP)); format_add(ft, "mouse_any_flag", "%d", !!(wp->base.mode & (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON))); 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_cb(ft, "pane_tabs", format_cb_pane_tabs); } /* Set default format keys for paste buffer. */ void format_defaults_paste_buffer(struct format_tree *ft, struct paste_buffer *pb) { size_t bufsize; char *s; paste_buffer_data(pb, &bufsize); format_add(ft, "buffer_size", "%zu", bufsize); format_add(ft, "buffer_name", "%s", paste_buffer_name(pb)); s = paste_make_sample(pb); format_add(ft, "buffer_sample", "%s", s); free(s); } tmate-2.4.0/grid-view.c000066400000000000000000000113211356407164200146760ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Grid view functions. These work using coordinates relative to the visible * screen area. */ #define grid_view_x(gd, x) (x) #define grid_view_y(gd, y) ((gd)->hsize + (y)) /* Get cell. */ void grid_view_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc) { grid_get_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py), gc); } /* Set cell. */ void grid_view_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) { grid_set_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py), gc); } /* Clear into history. */ void grid_view_clear_history(struct grid *gd) { struct grid_line *gl; u_int yy, last; /* 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) { 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) { 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) { 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; 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; 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; 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; 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; 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; 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) { 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-2.4.0/grid.c000066400000000000000000000504351356407164200137370ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * Grid data. This is the basic data structure that represents what is shown on * screen. * * A grid is a grid of cells (struct grid_cell). Lines are not allocated until * cells in that line are written to. The grid is split into history and * viewable data with the history starting at row (line) 0 and extending to * (hsize - 1); from hsize to hsize + (sy - 1) is the viewable data. All * functions in this file work on absolute coordinates, grid-view.c has * functions which work on the screen data. */ /* Default grid cell data. */ const struct grid_cell grid_default_cell = { 0, 0, { .fg = 8 }, { .bg = 8 }, { { ' ' }, 0, 1, 1 } }; const struct grid_cell_entry grid_default_entry = { 0, { .data = { 0, 8, 8, ' ' } } }; int grid_check_y(struct grid *, u_int); void grid_reflow_copy(struct grid_line *, u_int, struct grid_line *l, u_int, u_int); 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); /* Copy default into a cell. */ static void grid_clear_cell(struct grid *gd, u_int px, u_int py) { gd->linedata[py].celldata[px] = grid_default_entry; } /* Check grid y position. */ 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); } /* 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(gl->extddata); } free(gd->linedata); free(gd); } /* Compare grids. */ int grid_compare(struct grid *ga, struct grid *gb) { struct grid_line *gla, *glb; struct grid_cell gca, gcb; u_int xx, yy; if (ga->sx != gb->sx || ga->sy != gb->sy) return (1); for (yy = 0; yy < ga->sy; yy++) { gla = &ga->linedata[yy]; glb = &gb->linedata[yy]; if (gla->cellsize != glb->cellsize) return (1); for (xx = 0; xx < gla->cellsize; xx++) { grid_get_cell(ga, xx, yy, &gca); grid_get_cell(gb, xx, yy, &gcb); if (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; 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; yy = gd->hsize + gd->sy; gd->linedata = xreallocarray(gd->linedata, yy + 1, sizeof *gd->linedata); memset(&gd->linedata[yy], 0, sizeof gd->linedata[yy]); gd->hsize++; } /* Clear the history. */ void grid_clear_history(struct grid *gd) { grid_clear_lines(gd, 0, gd->hsize); grid_move_lines(gd, 0, gd->hsize, gd->sy); gd->hsize = 0; gd->linedata = xreallocarray(gd->linedata, gd->sy, sizeof *gd->linedata); } /* Scroll a region up, moving the top line into the history. */ void grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower) { struct grid_line *gl_history, *gl_upper, *gl_lower; u_int yy; /* Create a space for a new line. */ yy = gd->hsize + gd->sy; gd->linedata = xreallocarray(gd->linedata, yy + 1, sizeof *gd->linedata); /* Move the entire screen down to free a space for this line. */ gl_history = &gd->linedata[gd->hsize]; memmove(gl_history + 1, gl_history, gd->sy * sizeof *gl_history); /* Adjust the region and find its start and end. */ upper++; gl_upper = &gd->linedata[upper]; lower++; 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 = xreallocarray(gl->celldata, sx, sizeof *gl->celldata); for (xx = gl->cellsize; xx < sx; xx++) grid_clear_cell(gd, xx, py); 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. */ void grid_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc) { struct grid_line *gl; struct grid_cell_entry *gce; if (grid_check_y(gd, py) != 0 || px >= gd->linedata[py].cellsize) { memcpy(gc, &grid_default_cell, sizeof *gc); return; } gl = &gd->linedata[py]; gce = &gl->celldata[px]; if (gce->flags & GRID_FLAG_EXTENDED) { if (gce->offset >= gl->extdsize) memcpy(gc, &grid_default_cell, sizeof *gc); else memcpy(gc, &gl->extddata[gce->offset], sizeof *gc); return; } gc->flags = gce->flags & ~GRID_FLAG_EXTENDED; gc->attr = gce->data.attr; gc->fg = gce->data.fg; gc->bg = gce->data.bg; utf8_set(&gc->data, gce->data.data); } /* Set cell at relative position. */ void grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) { struct grid_line *gl; struct grid_cell_entry *gce; struct grid_cell *gcp; int extended; if (grid_check_y(gd, py) != 0) return; grid_expand_line(gd, py, px + 1); gl = &gd->linedata[py]; gce = &gl->celldata[px]; extended = (gce->flags & GRID_FLAG_EXTENDED); if (!extended && (gc->data.size != 1 || gc->data.width != 1)) extended = 1; if (!extended && (gc->flags & (GRID_FLAG_FGRGB|GRID_FLAG_BGRGB))) extended = 1; if (extended) { if (~gce->flags & GRID_FLAG_EXTENDED) { gl->extddata = xreallocarray(gl->extddata, gl->extdsize + 1, sizeof *gl->extddata); gce->offset = gl->extdsize++; gce->flags = gc->flags | GRID_FLAG_EXTENDED; } if (gce->offset >= gl->extdsize) fatalx("offset too big"); gcp = &gl->extddata[gce->offset]; memcpy(gcp, gc, sizeof *gcp); return; } gce->flags = gc->flags & ~GRID_FLAG_EXTENDED; gce->data.attr = gc->attr; gce->data.fg = gc->fg; gce->data.bg = gc->bg; gce->data.data = gc->data.data[0]; } /* Clear area. */ void grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny) { u_int xx, yy; 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_clear_cell(gd, xx, yy); } } } /* 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; 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); free(gl->extddata); 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; 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; 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_clear_cell(gd, xx, py); } } /* 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 if (gc->flags & GRID_FLAG_FGRGB) { values[n++] = 38; values[n++] = 2; values[n++] = gc->fg_rgb.r; values[n++] = gc->fg_rgb.g; values[n++] = gc->fg_rgb.b; } else { switch (gc->fg) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: values[n++] = gc->fg + 30; break; case 8: values[n++] = 39; break; case 90: case 91: case 92: case 93: case 94: case 95: case 96: case 97: values[n++] = gc->fg; break; } } return (n); } /* Get ANSI background sequence. */ 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 if (gc->flags & GRID_FLAG_BGRGB) { values[n++] = 48; values[n++] = 2; values[n++] = gc->bg_rgb.r; values[n++] = gc->bg_rgb.g; values[n++] = gc->bg_rgb.b; } else { switch (gc->bg) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: values[n++] = gc->bg + 40; break; case 8: values[n++] = 49; break; case 100: case 101: case 102: case 103: case 104: case 105: case 106: case 107: values[n++] = gc->bg - 10; break; } } return (n); } /* * Returns ANSI code to set particular attributes (colour, bold and so on) * given a current state. 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[64], newc[64], s[128]; 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 colour 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 colour changed, append its parameters. */ nnewc = grid_string_cells_bg(gc, newc); noldc = grid_string_cells_bg(lastgc, oldc); if (nnewc != noldc || memcmp(newc, oldc, nnewc * sizeof newc[0]) != 0) { 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) { struct grid_cell gc; static struct grid_cell lastgc1; const char *data; char *buf, code[128]; size_t len, off, size, codelen; u_int xx; const struct grid_line *gl; if (lastgc != NULL && *lastgc == NULL) { memcpy(&lastgc1, &grid_default_cell, sizeof lastgc1); *lastgc = &lastgc1; } len = 128; buf = xmalloc(len); off = 0; gl = grid_peek_line(gd, py); for (xx = px; xx < px + nx; xx++) { if (gl == NULL || xx >= gl->cellsize) break; grid_get_cell(gd, xx, py, &gc); if (gc.flags & GRID_FLAG_PADDING) continue; if (with_codes) { grid_string_cells_code(*lastgc, &gc, code, sizeof code, escape_c0); codelen = strlen(code); memcpy(*lastgc, &gc, sizeof **lastgc); } else codelen = 0; data = gc.data.data; size = gc.data.size; if (escape_c0 && size == 1 && *data == '\\') { data = "\\\\"; size = 2; } while (len < off + size + codelen + 1) { buf = xreallocarray(buf, 2, len); len *= 2; } if (codelen != 0) { memcpy(buf + off, code, codelen); off += codelen; } memcpy(buf + off, data, size); off += size; } if (trim) { while (off > 0 && buf[off - 1] == ' ') off--; } buf[off] = '\0'; return (buf); } /* * Duplicate a set of lines between two grids. 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; 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 = xreallocarray(NULL, srcl->cellsize, sizeof *dstl->celldata); memcpy(dstl->celldata, srcl->celldata, srcl->cellsize * sizeof *dstl->celldata); } else dstl->celldata = NULL; if (srcl->extdsize != 0) { dstl->extdsize = srcl->extdsize; dstl->extddata = xreallocarray(NULL, dstl->extdsize, sizeof *dstl->extddata); memcpy(dstl->extddata, srcl->extddata, dstl->extdsize * sizeof *dstl->extddata); } sy++; dy++; } } /* Copy a section of a line. */ void grid_reflow_copy(struct grid_line *dst_gl, u_int to, struct grid_line *src_gl, u_int from, u_int to_copy) { struct grid_cell_entry *gce; u_int i, was; memcpy(&dst_gl->celldata[to], &src_gl->celldata[from], to_copy * sizeof *dst_gl->celldata); for (i = to; i < to + to_copy; i++) { gce = &dst_gl->celldata[i]; if (~gce->flags & GRID_FLAG_EXTENDED) continue; was = gce->offset; dst_gl->extddata = xreallocarray(dst_gl->extddata, dst_gl->extdsize + 1, sizeof *dst_gl->extddata); gce->offset = dst_gl->extdsize++; memcpy(&dst_gl->extddata[gce->offset], &src_gl->extddata[was], sizeof *dst_gl->extddata); } } /* 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 = xreallocarray(dst_gl->celldata, nx, sizeof *dst_gl->celldata); dst_gl->cellsize = nx; /* Append as much as possible. */ grid_reflow_copy(dst_gl, ox, src_gl, 0, to_copy); /* 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 = xreallocarray(NULL, to_copy, sizeof *dst_gl->celldata); dst_gl->cellsize = to_copy; dst_gl->flags |= GRID_LINE_WRAPPED; /* Copy the data. */ grid_reflow_copy(dst_gl, 0, src_gl, offset, to_copy); /* 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; src_gl->extddata = 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-2.4.0/hooks.c000066400000000000000000000114231356407164200141270ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2012 Thomas Adam * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" struct hooks { RB_HEAD(hooks_tree, hook) tree; struct hooks *parent; }; static int hooks_cmp(struct hook *, struct hook *); RB_PROTOTYPE(hooks_tree, hook, entry, hooks_cmp); RB_GENERATE(hooks_tree, hook, entry, hooks_cmp); static struct hook *hooks_find1(struct hooks *, const char *); static void hooks_free1(struct hooks *, struct hook *); static void hooks_emptyfn(struct cmd_q *); static int hooks_cmp(struct hook *hook1, struct hook *hook2) { return (strcmp(hook1->name, hook2->name)); } struct hooks * hooks_get(struct session *s) { if (s != NULL) return (s->hooks); return (global_hooks); } struct hooks * hooks_create(struct hooks *parent) { struct hooks *hooks; hooks = xcalloc(1, sizeof *hooks); RB_INIT(&hooks->tree); hooks->parent = parent; return (hooks); } static void hooks_free1(struct hooks *hooks, struct hook *hook) { RB_REMOVE(hooks_tree, &hooks->tree, hook); cmd_list_free(hook->cmdlist); free((char *)hook->name); free(hook); } void hooks_free(struct hooks *hooks) { struct hook *hook, *hook1; RB_FOREACH_SAFE(hook, hooks_tree, &hooks->tree, hook1) hooks_free1(hooks, hook); free(hooks); } struct hook * hooks_first(struct hooks *hooks) { return (RB_MIN(hooks_tree, &hooks->tree)); } struct hook * hooks_next(struct hook *hook) { return (RB_NEXT(hooks_tree, &hooks->tree, hook)); } void hooks_add(struct hooks *hooks, const char *name, struct cmd_list *cmdlist) { struct hook *hook; if ((hook = hooks_find1(hooks, name)) != NULL) hooks_free1(hooks, hook); hook = xcalloc(1, sizeof *hook); hook->name = xstrdup(name); hook->cmdlist = cmdlist; hook->cmdlist->references++; RB_INSERT(hooks_tree, &hooks->tree, hook); } void hooks_remove(struct hooks *hooks, const char *name) { struct hook *hook; if ((hook = hooks_find1(hooks, name)) != NULL) hooks_free1(hooks, hook); } static struct hook * hooks_find1(struct hooks *hooks, const char *name) { struct hook hook; hook.name = name; return (RB_FIND(hooks_tree, &hooks->tree, &hook)); } struct hook * hooks_find(struct hooks *hooks, const char *name) { struct hook hook0, *hook; hook0.name = name; hook = RB_FIND(hooks_tree, &hooks->tree, &hook0); while (hook == NULL) { hooks = hooks->parent; if (hooks == NULL) break; hook = RB_FIND(hooks_tree, &hooks->tree, &hook0); } return (hook); } static void hooks_emptyfn(struct cmd_q *hooks_cmdq) { struct cmd_q *cmdq = hooks_cmdq->data; if (cmdq != NULL) { if (hooks_cmdq->client_exit >= 0) cmdq->client_exit = hooks_cmdq->client_exit; if (!cmdq_free(cmdq)) cmdq_continue(cmdq); } cmdq_free(hooks_cmdq); } int hooks_run(struct hooks *hooks, struct client *c, struct cmd_find_state *fs, const char *fmt, ...) { struct hook *hook; struct cmd_q *hooks_cmdq; va_list ap; char *name; va_start(ap, fmt); xvasprintf(&name, fmt, ap); va_end(ap); hook = hooks_find(hooks, name); if (hook == NULL) { free(name); return (-1); } log_debug("running hook %s", name); free(name); hooks_cmdq = cmdq_new(c); hooks_cmdq->flags |= CMD_Q_NOHOOKS; if (fs != NULL) cmd_find_copy_state(&hooks_cmdq->current, fs); hooks_cmdq->parent = NULL; cmdq_run(hooks_cmdq, hook->cmdlist, NULL); cmdq_free(hooks_cmdq); return (0); } int hooks_wait(struct hooks *hooks, struct cmd_q *cmdq, struct cmd_find_state *fs, const char *fmt, ...) { struct hook *hook; struct cmd_q *hooks_cmdq; va_list ap; char *name; va_start(ap, fmt); xvasprintf(&name, fmt, ap); va_end(ap); hook = hooks_find(hooks, name); if (hook == NULL) { free(name); return (-1); } log_debug("running hook %s (parent %p)", name, cmdq); free(name); hooks_cmdq = cmdq_new(cmdq->client); hooks_cmdq->flags |= CMD_Q_NOHOOKS; if (fs != NULL) cmd_find_copy_state(&hooks_cmdq->current, fs); hooks_cmdq->parent = cmdq; hooks_cmdq->emptyfn = hooks_emptyfn; hooks_cmdq->data = cmdq; if (cmdq != NULL) cmdq->references++; cmdq_run(hooks_cmdq, hook->cmdlist, NULL); return (0); } tmate-2.4.0/input-keys.c000066400000000000000000000204521356407164200151160ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" /* * This file is rather misleadingly named, it contains the code which takes a * key code and translates it into something suitable to be sent to the * application running in a pane (similar to input.c does in the other * direction with output). */ void input_key_mouse(struct window_pane *, struct mouse_event *); struct input_key_ent { key_code key; const char *data; int flags; #define INPUTKEY_KEYPAD 0x1 /* keypad key */ #define INPUTKEY_CURSOR 0x2 /* cursor key */ }; 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_F1|KEYC_SHIFT, "\033[25~", 0 }, { KEYC_F2|KEYC_SHIFT, "\033[26~", 0 }, { KEYC_F3|KEYC_SHIFT, "\033[28~", 0 }, { KEYC_F4|KEYC_SHIFT, "\033[29~", 0 }, { KEYC_F5|KEYC_SHIFT, "\033[31~", 0 }, { KEYC_F6|KEYC_SHIFT, "\033[32~", 0 }, { KEYC_F7|KEYC_SHIFT, "\033[33~", 0 }, { KEYC_F8|KEYC_SHIFT, "\033[34~", 0 }, { KEYC_IC, "\033[2~", 0 }, { KEYC_DC, "\033[3~", 0 }, { KEYC_HOME, "\033[1~", 0 }, { KEYC_END, "\033[4~", 0 }, { KEYC_NPAGE, "\033[6~", 0 }, { KEYC_PPAGE, "\033[5~", 0 }, { KEYC_BTAB, "\033[Z", 0 }, /* * Arrow keys. Cursor versions must come first. The codes are toggled * between CSI and SS3 versions when ctrl is pressed. */ { KEYC_UP|KEYC_CTRL, "\033[A", INPUTKEY_CURSOR }, { KEYC_DOWN|KEYC_CTRL, "\033[B", INPUTKEY_CURSOR }, { KEYC_RIGHT|KEYC_CTRL, "\033[C", INPUTKEY_CURSOR }, { KEYC_LEFT|KEYC_CTRL, "\033[D", INPUTKEY_CURSOR }, { KEYC_UP, "\033OA", INPUTKEY_CURSOR }, { KEYC_DOWN, "\033OB", INPUTKEY_CURSOR }, { KEYC_RIGHT, "\033OC", INPUTKEY_CURSOR }, { KEYC_LEFT, "\033OD", INPUTKEY_CURSOR }, { KEYC_UP|KEYC_CTRL, "\033OA", 0 }, { KEYC_DOWN|KEYC_CTRL, "\033OB", 0 }, { KEYC_RIGHT|KEYC_CTRL, "\033OC", 0 }, { KEYC_LEFT|KEYC_CTRL, "\033OD", 0 }, { KEYC_UP, "\033[A", 0 }, { KEYC_DOWN, "\033[B", 0 }, { KEYC_RIGHT, "\033[C", 0 }, { KEYC_LEFT, "\033[D", 0 }, /* Keypad keys. Keypad versions must come first. */ { KEYC_KP_SLASH, "\033Oo", INPUTKEY_KEYPAD }, { KEYC_KP_STAR, "\033Oj", INPUTKEY_KEYPAD }, { KEYC_KP_MINUS, "\033Om", INPUTKEY_KEYPAD }, { KEYC_KP_SEVEN, "\033Ow", INPUTKEY_KEYPAD }, { KEYC_KP_EIGHT, "\033Ox", INPUTKEY_KEYPAD }, { KEYC_KP_NINE, "\033Oy", INPUTKEY_KEYPAD }, { KEYC_KP_PLUS, "\033Ok", INPUTKEY_KEYPAD }, { KEYC_KP_FOUR, "\033Ot", INPUTKEY_KEYPAD }, { KEYC_KP_FIVE, "\033Ou", INPUTKEY_KEYPAD }, { KEYC_KP_SIX, "\033Ov", INPUTKEY_KEYPAD }, { KEYC_KP_ONE, "\033Oq", INPUTKEY_KEYPAD }, { KEYC_KP_TWO, "\033Or", INPUTKEY_KEYPAD }, { KEYC_KP_THREE, "\033Os", INPUTKEY_KEYPAD }, { KEYC_KP_ENTER, "\033OM", INPUTKEY_KEYPAD }, { KEYC_KP_ZERO, "\033Op", INPUTKEY_KEYPAD }, { KEYC_KP_PERIOD, "\033On", INPUTKEY_KEYPAD }, { KEYC_KP_SLASH, "/", 0 }, { KEYC_KP_STAR, "*", 0 }, { KEYC_KP_MINUS, "-", 0 }, { KEYC_KP_SEVEN, "7", 0 }, { KEYC_KP_EIGHT, "8", 0 }, { KEYC_KP_NINE, "9", 0 }, { KEYC_KP_PLUS, "+", 0 }, { KEYC_KP_FOUR, "4", 0 }, { KEYC_KP_FIVE, "5", 0 }, { KEYC_KP_SIX, "6", 0 }, { KEYC_KP_ONE, "1", 0 }, { KEYC_KP_TWO, "2", 0 }, { KEYC_KP_THREE, "3", 0 }, { KEYC_KP_ENTER, "\n", 0 }, { KEYC_KP_ZERO, "0", 0 }, { KEYC_KP_PERIOD, ".", 0 }, }; /* Split a character into two UTF-8 bytes. */ static size_t input_split2(u_int c, u_char *dst) { if (c > 0x7f) { dst[0] = (c >> 6) | 0xc0; dst[1] = (c & 0x3f) | 0x80; return (2); } dst[0] = c; return (1); } /* Translate a key code into an output key sequence. */ void input_key(struct window_pane *wp, key_code key, struct mouse_event *m) { const struct input_key_ent *ike; u_int i; size_t dlen; char *out; key_code justkey; struct utf8_data ud; log_debug("writing key 0x%llx (%s) to %%%u", key, key_string_lookup_key(key), wp->id); /* If this is a mouse key, pass off to mouse function. */ if (KEYC_IS_MOUSE(key)) { if (m != NULL && m->wp != -1 && (u_int)m->wp == wp->id) input_key_mouse(wp, m); return; } /* * If this is a normal 7-bit key, just send it, with a leading escape * if necessary. If it is a UTF-8 key, split it and send it. */ justkey = (key & ~KEYC_ESCAPE); if (justkey <= 0x7f) { if (key & KEYC_ESCAPE) bufferevent_write(wp->event, "\033", 1); ud.data[0] = justkey; bufferevent_write(wp->event, &ud.data[0], 1); return; } if (justkey > 0x7f && justkey < KEYC_BASE) { if (utf8_split(justkey, &ud) != UTF8_DONE) return; if (key & KEYC_ESCAPE) bufferevent_write(wp->event, "\033", 1); bufferevent_write(wp->event, ud.data, ud.size); return; } /* * Then try to look this up as an xterm key, if the flag to output them * is set. */ if (options_get_number(wp->window->options, "xterm-keys")) { if ((out = xterm_keys_lookup(key)) != NULL) { bufferevent_write(wp->event, out, strlen(out)); free(out); return; } } /* Otherwise look the key up in the table. */ for (i = 0; i < nitems(input_keys); i++) { ike = &input_keys[i]; if ((ike->flags & INPUTKEY_KEYPAD) && !(wp->screen->mode & MODE_KKEYPAD)) continue; if ((ike->flags & INPUTKEY_CURSOR) && !(wp->screen->mode & MODE_KCURSOR)) continue; if ((key & KEYC_ESCAPE) && (ike->key | KEYC_ESCAPE) == key) break; if (ike->key == key) break; } if (i == nitems(input_keys)) { log_debug("key 0x%llx missing", key); return; } dlen = strlen(ike->data); log_debug("found key 0x%llx: \"%s\"", key, ike->data); /* Prefix a \033 for escape. */ if (key & KEYC_ESCAPE) bufferevent_write(wp->event, "\033", 1); bufferevent_write(wp->event, ike->data, dlen); } /* Translate mouse and output. */ void input_key_mouse(struct window_pane *wp, struct mouse_event *m) { char buf[40]; size_t len; u_int x, y; if ((wp->screen->mode & ALL_MOUSE_MODES) == 0) return; if (!window_pane_visible(wp)) return; if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) return; /* If this pane is not in button mode, discard motion events. */ if (!(wp->screen->mode & MODE_MOUSE_BUTTON) && (m->b & MOUSE_MASK_DRAG)) return; /* * Use the SGR (1006) extension only if the application requested it * and the underlying terminal also sent the event in this format (this * is because an old style mouse release event cannot be converted into * the new SGR format, since the released button is unknown). Otherwise * pretend that tmux doesn't speak this extension, and fall back to the * UTF-8 (1005) extension if the application requested, or to the * legacy format. */ if (m->sgr_type != ' ' && (wp->screen->mode & MODE_MOUSE_SGR)) { len = xsnprintf(buf, sizeof buf, "\033[<%u;%u;%u%c", m->sgr_b, x + 1, y + 1, m->sgr_type); } else if (wp->screen->mode & MODE_MOUSE_UTF8) { if (m->b > 0x7ff - 32 || x > 0x7ff - 33 || y > 0x7ff - 33) return; len = xsnprintf(buf, sizeof buf, "\033[M"); len += input_split2(m->b + 32, &buf[len]); len += input_split2(x + 33, &buf[len]); len += input_split2(y + 33, &buf[len]); } else { if (m->b > 223) return; len = xsnprintf(buf, sizeof buf, "\033[M"); buf[len++] = m->b + 32; buf[len++] = x + 33; buf[len++] = y + 33; } log_debug("writing mouse %.*s to %%%u", (int)len, buf, wp->id); bufferevent_write(wp->event, buf, len); } tmate-2.4.0/input.c000066400000000000000000001343641356407164200141550ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" /* * 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 terminals. */ /* Input parser cell. */ struct input_cell { struct grid_cell cell; int set; int g0set; /* 1 if ACS */ int g1set; /* 1 if ACS */ }; /* Input parser context. */ struct input_ctx { struct window_pane *wp; struct screen_write_ctx ctx; struct input_cell cell; struct input_cell old_cell; u_int old_cx; u_int old_cy; u_char interm_buf[4]; size_t interm_len; u_char param_buf[64]; size_t param_len; #define INPUT_BUF_START 32 #define INPUT_BUF_LIMIT 1048576 u_char *input_buf; size_t input_len; size_t input_space; 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; }; /* Helper functions. */ struct input_transition; int input_split(struct input_ctx *); int input_get(struct input_ctx *, u_int, int, int); void printflike(2, 3) input_reply(struct input_ctx *, const char *, ...); void input_set_state(struct window_pane *, const struct input_transition *); void input_reset_cell(struct input_ctx *); /* Transition entry/exit handlers. */ void input_clear(struct input_ctx *); void input_ground(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_rm(struct input_ctx *); void input_csi_dispatch_rm_private(struct input_ctx *); void input_csi_dispatch_sm(struct input_ctx *); void input_csi_dispatch_sm_private(struct input_ctx *); void input_csi_dispatch_winops(struct input_ctx *); void input_csi_dispatch_sgr_256(struct input_ctx *, int, u_int *); void input_csi_dispatch_sgr_rgb(struct input_ctx *, int, u_int *); 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_SCSG0_OFF, INPUT_ESC_SCSG0_ON, INPUT_ESC_SCSG1_OFF, INPUT_ESC_SCSG1_ON, }; /* Escape command table. */ const struct input_table_entry input_esc_table[] = { { '0', "(", INPUT_ESC_SCSG0_ON }, { '0', ")", INPUT_ESC_SCSG1_ON }, { '7', "", INPUT_ESC_DECSC }, { '8', "", INPUT_ESC_DECRC }, { '8', "#", INPUT_ESC_DECALN }, { '=', "", INPUT_ESC_DECKPAM }, { '>', "", INPUT_ESC_DECKPNM }, { 'B', "(", INPUT_ESC_SCSG0_OFF }, { 'B', ")", INPUT_ESC_SCSG1_OFF }, { 'D', "", INPUT_ESC_IND }, { 'E', "", INPUT_ESC_NEL }, { 'H', "", INPUT_ESC_HTS }, { 'M', "", INPUT_ESC_RI }, { '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, INPUT_CSI_WINOPS, }; /* 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 }, { 't', "", INPUT_CSI_WINOPS }, { 'u', "", INPUT_CSI_RCP }, }; /* Input transition. */ struct input_transition { int first; int last; int (*handler)(struct input_ctx *); const struct input_state *state; }; /* Input state. */ struct input_state { const char *name; void (*enter)(struct input_ctx *); void (*exit)(struct input_ctx *); const struct input_transition *transitions; }; /* State transitions available from all states. */ #define INPUT_STATE_ANYWHERE \ { 0x18, 0x18, input_c0_dispatch, &input_state_ground }, \ { 0x1a, 0x1a, input_c0_dispatch, &input_state_ground }, \ { 0x1b, 0x1b, NULL, &input_state_esc_enter } /* Forward declarations of state tables. */ 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", input_ground, 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, NULL, 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, NULL, 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)); } /* Reset cell state to default. */ void input_reset_cell(struct input_ctx *ictx) { memcpy(&ictx->cell.cell, &grid_default_cell, sizeof ictx->cell.cell); ictx->cell.set = 0; ictx->cell.g0set = ictx->cell.g1set = 0; memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); ictx->old_cx = 0; ictx->old_cy = 0; } /* Initialise input parser. */ void input_init(struct window_pane *wp) { struct input_ctx *ictx; ictx = wp->ictx = xcalloc(1, sizeof *ictx); ictx->input_space = INPUT_BUF_START; ictx->input_buf = xmalloc(INPUT_BUF_START); ictx->since_ground = evbuffer_new(); input_reset(wp, 0); } /* Destroy input parser. */ void input_free(struct window_pane *wp) { struct input_ctx *ictx = wp->ictx; free(ictx->input_buf); evbuffer_free(ictx->since_ground); free (ictx); wp->ictx = NULL; } /* Reset input state and clear screen. */ void input_reset(struct window_pane *wp, int clear) { struct input_ctx *ictx = wp->ictx; input_reset_cell(ictx); if (clear) { 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); } *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->state = &input_state_ground; ictx->flags = 0; } /* Return pending data. */ struct evbuffer * input_pending(struct window_pane *wp) { return (wp->ictx->since_ground); } /* Change input state. */ void input_set_state(struct window_pane *wp, const struct input_transition *itr) { struct input_ctx *ictx = wp->ictx; if (ictx->state->exit != NULL) ictx->state->exit(ictx); ictx->state = itr->state; if (ictx->state->enter != NULL) ictx->state->enter(ictx); } /* Parse input. */ void input_parse(struct window_pane *wp) { struct 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; window_update_activity(wp->window); wp->flags |= PANE_CHANGED; /* * 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; log_debug("%s: %%%u %s, %zu bytes: %.*s", __func__, wp->id, ictx->state->name, len, (int)len, buf); /* Parse the input. */ while (off < len) { ictx->ch = buf[off++]; /* Find the transition. */ itr = ictx->state->transitions; while (itr->first != -1 && itr->last != -1) { if (ictx->ch >= itr->first && ictx->ch <= itr->last) break; itr++; } if (itr->first == -1 || itr->last == -1) { /* No transition? Eh? */ fatalx("no transition from state"); } /* * 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; } /* Reset for ground state. */ void input_ground(struct input_ctx *ictx) { evbuffer_drain(ictx->since_ground, EVBUFFER_LENGTH(ictx->since_ground)); if (ictx->input_space > INPUT_BUF_START) { ictx->input_space = INPUT_BUF_START; ictx->input_buf = xrealloc(ictx->input_buf, INPUT_BUF_START); } } /* Output this character to the screen. */ int input_print(struct input_ctx *ictx) { int set; set = ictx->cell.set == 0 ? ictx->cell.g0set : ictx->cell.g1set; if (set == 1) ictx->cell.cell.attr |= GRID_ATTR_CHARSET; else ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET; utf8_set(&ictx->cell.cell.data, ictx->ch); screen_write_cell(&ictx->ctx, &ictx->cell.cell); ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET; 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) { size_t available; available = ictx->input_space; while (ictx->input_len + 1 >= available) { available *= 2; if (available > INPUT_BUF_LIMIT) { ictx->flags |= INPUT_DISCARD; return (0); } ictx->input_buf = xrealloc(ictx->input_buf, available); ictx->input_space = available; } ictx->input_buf[ictx->input_len++] = ictx->ch; ictx->input_buf[ictx->input_len] = '\0'; return (0); } /* Execute C0 control sequence. */ int input_c0_dispatch(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; struct window_pane *wp = ictx->wp; struct screen *s = sctx->s; log_debug("%s: '%c'", __func__, ictx->ch); switch (ictx->ch) { case '\000': /* NUL */ break; case '\007': /* BEL */ alerts_queue(wp->window, WINDOW_BELL); break; case '\010': /* BS */ screen_write_backspace(sctx); break; case '\011': /* HT */ /* Don't tab beyond the end of the line. */ if (s->cx >= screen_size_x(s) - 1) break; /* Find the next tab point, or use the last column if none. */ do { s->cx++; if (bit_test(s->tabs, s->cx)) break; } while (s->cx < screen_size_x(s) - 1); break; case '\012': /* LF */ case '\013': /* VT */ case '\014': /* FF */ screen_write_linefeed(sctx, 0); break; case '\015': /* CR */ screen_write_carriagereturn(sctx); break; case '\016': /* SO */ ictx->cell.set = 1; break; case '\017': /* SI */ ictx->cell.set = 0; break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } 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: input_reset_cell(ictx); 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_SCSG0_ON: ictx->cell.g0set = 1; break; case INPUT_ESC_SCSG0_OFF: ictx->cell.g0set = 0; break; case INPUT_ESC_SCSG1_ON: ictx->cell.g1set = 1; break; case INPUT_ESC_SCSG1_OFF: ictx->cell.g1set = 0; break; } return (0); } /* Execute control sequence. */ int input_csi_dispatch(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; struct screen *s = sctx->s; struct input_table_entry *entry; int n, m; u_int cx; 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. */ cx = s->cx; if (cx > screen_size_x(s) - 1) cx = screen_size_x(s) - 1; n = input_get(ictx, 0, 1, 1); while (cx > 0 && n-- > 0) { do cx--; while (cx > 0 && !bit_test(s->tabs, cx)); } s->cx = 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_WINOPS: input_csi_dispatch_winops(ictx); 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[>84;0;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: input_csi_dispatch_rm(ictx); break; case INPUT_CSI_RM_PRIVATE: input_csi_dispatch_rm_private(ictx); 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: input_csi_dispatch_sm(ictx); break; case INPUT_CSI_SM_PRIVATE: input_csi_dispatch_sm_private(ictx); 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 RM. */ void input_csi_dispatch_rm(struct input_ctx *ictx) { u_int i; for (i = 0; i < ictx->param_list_len; i++) { switch (input_get(ictx, i, 0, -1)) { case 4: /* IRM */ screen_write_mode_clear(&ictx->ctx, MODE_INSERT); break; case 34: screen_write_mode_set(&ictx->ctx, MODE_BLINKING); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } } } /* Handle CSI private RM. */ void input_csi_dispatch_rm_private(struct input_ctx *ictx) { struct window_pane *wp = ictx->wp; u_int i; for (i = 0; i < ictx->param_list_len; i++) { switch (input_get(ictx, i, 0, -1)) { case 1: /* DECCKM */ 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 12: screen_write_mode_clear(&ictx->ctx, MODE_BLINKING); break; case 25: /* TCEM */ screen_write_mode_clear(&ictx->ctx, MODE_CURSOR); break; case 1000: case 1001: case 1002: 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.cell, 0); break; case 1049: window_pane_alternate_off(wp, &ictx->cell.cell, 1); break; case 2004: screen_write_mode_clear(&ictx->ctx, MODE_BRACKETPASTE); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } } } /* Handle CSI SM. */ void input_csi_dispatch_sm(struct input_ctx *ictx) { u_int i; for (i = 0; i < ictx->param_list_len; i++) { switch (input_get(ictx, i, 0, -1)) { case 4: /* IRM */ screen_write_mode_set(&ictx->ctx, MODE_INSERT); break; case 34: screen_write_mode_clear(&ictx->ctx, MODE_BLINKING); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } } } /* Handle CSI private SM. */ void input_csi_dispatch_sm_private(struct input_ctx *ictx) { struct window_pane *wp = ictx->wp; u_int i; for (i = 0; i < ictx->param_list_len; i++) { switch (input_get(ictx, i, 0, -1)) { case 1: /* DECCKM */ 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 12: screen_write_mode_set(&ictx->ctx, MODE_BLINKING); 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 1004: if (ictx->ctx.s->mode & MODE_FOCUSON) break; screen_write_mode_set(&ictx->ctx, MODE_FOCUSON); wp->flags |= PANE_FOCUSPUSH; /* force update */ 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.cell, 0); break; case 1049: window_pane_alternate_on(wp, &ictx->cell.cell, 1); break; case 2004: screen_write_mode_set(&ictx->ctx, MODE_BRACKETPASTE); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } } } /* Handle CSI window operations. */ void input_csi_dispatch_winops(struct input_ctx *ictx) { struct window_pane *wp = ictx->wp; int n, m; m = 0; while ((n = input_get(ictx, m, 0, -1)) != -1) { switch (n) { case 1: case 2: case 5: case 6: case 7: case 11: case 13: case 14: case 19: case 20: case 21: case 24: break; case 3: case 4: case 8: m++; if (input_get(ictx, m, 0, -1) == -1) return; /* FALLTHROUGH */ case 9: case 10: case 22: case 23: m++; if (input_get(ictx, m, 0, -1) == -1) return; break; case 18: input_reply(ictx, "\033[8;%u;%ut", wp->sy, wp->sx); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } m++; } } /* Handle CSI SGR for 256 colours. */ void input_csi_dispatch_sgr_256(struct input_ctx *ictx, int fgbg, u_int *i) { struct grid_cell *gc = &ictx->cell.cell; int c; (*i)++; c = input_get(ictx, *i, 0, -1); if (c == -1) { if (fgbg == 38) { gc->flags &= ~(GRID_FLAG_FG256|GRID_FLAG_FGRGB); gc->fg = 8; } else if (fgbg == 48) { gc->flags &= ~(GRID_FLAG_BG256|GRID_FLAG_BGRGB); gc->bg = 8; } } else { if (fgbg == 38) { gc->flags |= GRID_FLAG_FG256; gc->flags &= ~GRID_FLAG_FGRGB; gc->fg = c; } else if (fgbg == 48) { gc->flags |= GRID_FLAG_BG256; gc->flags &= ~GRID_FLAG_BGRGB; gc->bg = c; } } } /* Handle CSI SGR for RGB colours. */ void input_csi_dispatch_sgr_rgb(struct input_ctx *ictx, int fgbg, u_int *i) { struct grid_cell *gc = &ictx->cell.cell; int r, g, b; (*i)++; r = input_get(ictx, *i, 0, -1); if (r == -1 || r > 255) return; (*i)++; g = input_get(ictx, *i, 0, -1); if (g == -1 || g > 255) return; (*i)++; b = input_get(ictx, *i, 0, -1); if (b == -1 || b > 255) return; if (fgbg == 38) { gc->flags &= ~GRID_FLAG_FG256; gc->flags |= GRID_FLAG_FGRGB; gc->fg_rgb.r = r; gc->fg_rgb.g = g; gc->fg_rgb.b = b; } else if (fgbg == 48) { gc->flags &= ~GRID_FLAG_BG256; gc->flags |= GRID_FLAG_BGRGB; gc->bg_rgb.r = r; gc->bg_rgb.g = g; gc->bg_rgb.b = b; } } /* Handle CSI SGR. */ void input_csi_dispatch_sgr(struct input_ctx *ictx) { struct grid_cell *gc = &ictx->cell.cell; u_int i; int n; if (ictx->param_list_len == 0) { memcpy(gc, &grid_default_cell, sizeof *gc); return; } for (i = 0; i < ictx->param_list_len; i++) { n = input_get(ictx, i, 0, 0); if (n == 38 || n == 48) { i++; switch (input_get(ictx, i, 0, -1)) { case 2: input_csi_dispatch_sgr_rgb(ictx, n, &i); break; case 5: input_csi_dispatch_sgr_256(ictx, n, &i); break; } continue; } switch (n) { case 0: case 10: memcpy(gc, &grid_default_cell, sizeof *gc); break; case 1: gc->attr |= GRID_ATTR_BRIGHT; break; case 2: gc->attr |= GRID_ATTR_DIM; break; case 3: gc->attr |= GRID_ATTR_ITALICS; break; case 4: gc->attr |= GRID_ATTR_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|GRID_FLAG_FGRGB); gc->fg = n - 30; break; case 39: gc->flags &= ~(GRID_FLAG_FG256|GRID_FLAG_FGRGB); 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|GRID_FLAG_BGRGB); gc->bg = n - 40; break; case 49: gc->flags &= ~(GRID_FLAG_BG256|GRID_FLAG_BGRGB); 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|GRID_FLAG_FGRGB); 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|GRID_FLAG_BGRGB); 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; u_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) { struct utf8_data *ud = &ictx->utf8data; if (utf8_open(ud, ictx->ch) != UTF8_MORE) fatalx("UTF-8 open invalid %#x", ictx->ch); log_debug("%s %hhu", __func__, ud->size); return (0); } /* Append to UTF-8 character. */ int input_utf8_add(struct input_ctx *ictx) { struct utf8_data *ud = &ictx->utf8data; if (utf8_append(ud, ictx->ch) != UTF8_MORE) fatalx("UTF-8 add invalid %#x", ictx->ch); log_debug("%s", __func__); return (0); } /* Close UTF-8 string. */ int input_utf8_close(struct input_ctx *ictx) { struct utf8_data *ud = &ictx->utf8data; if (utf8_append(ud, ictx->ch) != UTF8_DONE) { /* * An error here could be invalid UTF-8 or it could be a * nonprintable character for which we can't get the * width. Drop it. */ return (0); } log_debug("%s %hhu '%*s' (width %hhu)", __func__, ud->size, (int)ud->size, ud->data, ud->width); utf8_copy(&ictx->cell.cell.data, ud); screen_write_cell(&ictx->ctx, &ictx->cell.cell); return (0); } tmate-2.4.0/job.c000066400000000000000000000113211356407164200135530ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include "tmux.h" /* * Job scheduling. Run queued commands in the background and record their * output. */ 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, const char *cwd, void (*callbackfn)(struct job *), void (*freefn)(void *), void *data) { struct job *job; struct environ *env; pid_t pid; int nullfd, out[2]; const char *home; if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0) return (NULL); env = environ_create(); 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); close(out[0]); close(out[1]); return (NULL); case 0: /* child */ clear_signals(1); if (cwd == NULL || chdir(cwd) != 0) { if ((home = find_home()) == NULL || chdir(home) != 0) chdir("/"); } environ_push(env); environ_free(env); if (dup2(out[1], STDIN_FILENO) == -1) fatal("dup2 failed"); if (dup2(out[1], STDOUT_FILENO) == -1) fatal("dup2 failed"); if (out[1] != STDIN_FILENO && out[1] != STDOUT_FILENO) close(out[1]); close(out[0]); nullfd = open(_PATH_DEVNULL, O_RDWR, 0); if (nullfd < 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->state = JOB_RUNNING; 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|EV_WRITE); log_debug("run job %p: %s, pid %ld", job, job->cmd, (long) job->pid); return (job); } /* Kill and free an individual job. */ void job_free(struct job *job) { log_debug("free job %p: %s", job, job->cmd); LIST_REMOVE(job, 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 %zu", job, job->cmd, (long) job->pid, len); if (len == 0) { shutdown(job->fd, SHUT_WR); bufferevent_disable(job->event, EV_WRITE); } } /* Job buffer error callback. */ 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->state == JOB_DEAD) { if (job->callbackfn != NULL) job->callbackfn(job); job_free(job); } else { bufferevent_disable(job->event, EV_READ); job->state = JOB_CLOSED; } } /* 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->state == JOB_CLOSED) { if (job->callbackfn != NULL) job->callbackfn(job); job_free(job); } else { job->pid = -1; job->state = JOB_DEAD; } } tmate-2.4.0/key-bindings.c000066400000000000000000000164271356407164200154000ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" RB_GENERATE(key_bindings, key_binding, entry, key_bindings_cmp); RB_GENERATE(key_tables, key_table, entry, key_table_cmp); struct key_tables key_tables = RB_INITIALIZER(&key_tables); int key_table_cmp(struct key_table *e1, struct key_table *e2) { return (strcmp(e1->name, e2->name)); } int key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2) { if (bd1->key < bd2->key) return (-1); if (bd1->key > bd2->key) return (1); return (0); } struct key_table * key_bindings_get_table(const char *name, int create) { struct key_table table_find, *table; table_find.name = name; table = RB_FIND(key_tables, &key_tables, &table_find); if (table != NULL || !create) return (table); table = xmalloc(sizeof *table); table->name = xstrdup(name); RB_INIT(&table->key_bindings); table->references = 1; /* one reference in key_tables */ RB_INSERT(key_tables, &key_tables, table); return (table); } void key_bindings_unref_table(struct key_table *table) { struct key_binding *bd; struct key_binding *bd1; if (--table->references != 0) return; RB_FOREACH_SAFE(bd, key_bindings, &table->key_bindings, bd1) { RB_REMOVE(key_bindings, &table->key_bindings, bd); cmd_list_free(bd->cmdlist); free(bd); } free((void *)table->name); free(table); } void key_bindings_add(const char *name, key_code key, int can_repeat, struct cmd_list *cmdlist) { struct key_table *table; struct key_binding bd_find, *bd; table = key_bindings_get_table(name, 1); bd_find.key = key; bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find); if (bd != NULL) { RB_REMOVE(key_bindings, &table->key_bindings, bd); cmd_list_free(bd->cmdlist); free(bd); } bd = xmalloc(sizeof *bd); bd->key = key; RB_INSERT(key_bindings, &table->key_bindings, bd); bd->can_repeat = can_repeat; bd->cmdlist = cmdlist; } void key_bindings_remove(const char *name, key_code key) { struct key_table *table; struct key_binding bd_find, *bd; table = key_bindings_get_table(name, 0); if (table == NULL) return; bd_find.key = key; bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find); if (bd == NULL) return; RB_REMOVE(key_bindings, &table->key_bindings, bd); cmd_list_free(bd->cmdlist); free(bd); if (RB_EMPTY(&table->key_bindings)) { RB_REMOVE(key_tables, &key_tables, table); key_bindings_unref_table(table); } } void key_bindings_remove_table(const char *name) { struct key_table *table; table = key_bindings_get_table(name, 0); if (table != NULL) { RB_REMOVE(key_tables, &key_tables, table); key_bindings_unref_table(table); } } void key_bindings_init(void) { static const char *defaults[] = { "bind C-b send-prefix", "bind C-o rotate-window", "bind C-z suspend-client", "bind Space next-layout", "bind ! break-pane", "bind '\"' split-window", "bind '#' list-buffers", "bind '$' command-prompt -I'#S' \"rename-session '%%'\"", "bind % split-window -h", "bind & confirm-before -p\"kill-window #W? (y/n)\" kill-window", "bind \"'\" command-prompt -pindex \"select-window -t ':%%'\"", "bind ( switch-client -p", "bind ) switch-client -n", "bind , command-prompt -I'#W' \"rename-window '%%'\"", "bind - delete-buffer", "bind . command-prompt \"move-window -t '%%'\"", "bind 0 select-window -t:=0", "bind 1 select-window -t:=1", "bind 2 select-window -t:=2", "bind 3 select-window -t:=3", "bind 4 select-window -t:=4", "bind 5 select-window -t:=5", "bind 6 select-window -t:=6", "bind 7 select-window -t:=7", "bind 8 select-window -t:=8", "bind 9 select-window -t:=9", "bind : command-prompt", "bind \\; last-pane", "bind = choose-buffer", "bind ? list-keys", "bind D choose-client", "bind L switch-client -l", "bind M select-pane -M", "bind [ copy-mode", "bind ] paste-buffer", "bind c new-window", "bind d detach-client", "bind f command-prompt \"find-window '%%'\"", "bind i display-message", "bind l last-window", "bind m select-pane -m", "bind n next-window", "bind o select-pane -t:.+", "bind p previous-window", "bind q display-panes", "bind r refresh-client", "bind s choose-tree", "bind t clock-mode", "bind w choose-window", "bind x confirm-before -p\"kill-pane #P? (y/n)\" kill-pane", "bind z resize-pane -Z", "bind { swap-pane -U", "bind } swap-pane -D", "bind '~' show-messages", "bind PPage copy-mode -u", "bind -r Up select-pane -U", "bind -r Down select-pane -D", "bind -r Left select-pane -L", "bind -r Right select-pane -R", "bind M-1 select-layout even-horizontal", "bind M-2 select-layout even-vertical", "bind M-3 select-layout main-horizontal", "bind M-4 select-layout main-vertical", "bind M-5 select-layout tiled", "bind M-n next-window -a", "bind M-o rotate-window -D", "bind M-p previous-window -a", "bind -r M-Up resize-pane -U 5", "bind -r M-Down resize-pane -D 5", "bind -r M-Left resize-pane -L 5", "bind -r M-Right resize-pane -R 5", "bind -r C-Up resize-pane -U", "bind -r C-Down resize-pane -D", "bind -r C-Left resize-pane -L", "bind -r C-Right resize-pane -R", "bind -n MouseDown1Pane select-pane -t=\\; send-keys -M", "bind -n MouseDrag1Border resize-pane -M", "bind -n MouseDown1Status select-window -t=", "bind -n WheelDownStatus next-window", "bind -n WheelUpStatus previous-window", "bind -n MouseDrag1Pane if -Ft= '#{mouse_any_flag}' 'if -Ft= \"#{pane_in_mode}\" \"copy-mode -M\" \"send-keys -M\"' 'copy-mode -M'", "bind -n MouseDown3Pane if-shell -Ft= '#{mouse_any_flag}' 'select-pane -t=; send-keys -M' 'select-pane -mt='", "bind -n WheelUpPane if-shell -Ft= '#{mouse_any_flag}' 'send-keys -M' 'if -Ft= \"#{pane_in_mode}\" \"send-keys -M\" \"copy-mode -et=\"'", }; u_int i; struct cmd_list *cmdlist; char *cause; int error; struct cmd_q *cmdq; cmdq = cmdq_new(NULL); for (i = 0; i < nitems(defaults); i++) { error = cmd_string_parse(defaults[i], &cmdlist, "", i, &cause); if (error != 0) fatalx("bad default key"); cmdq_run(cmdq, cmdlist, NULL); cmd_list_free(cmdlist); } cmdq_free(cmdq); } void key_bindings_dispatch(struct key_binding *bd, struct client *c, struct mouse_event *m) { 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_error(c->cmdq, "client is read-only"); return; } cmdq_run(c->cmdq, bd->cmdlist, m); } tmate-2.4.0/key-string.c000066400000000000000000000165501356407164200151060ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" static key_code key_string_search_table(const char *); static key_code key_string_get_modifiers(const char **); const struct { const char *string; key_code key; } key_string_table[] = { /* Function keys. */ { "F1", KEYC_F1 }, { "F2", KEYC_F2 }, { "F3", KEYC_F3 }, { "F4", KEYC_F4 }, { "F5", KEYC_F5 }, { "F6", KEYC_F6 }, { "F7", KEYC_F7 }, { "F8", KEYC_F8 }, { "F9", KEYC_F9 }, { "F10", KEYC_F10 }, { "F11", KEYC_F11 }, { "F12", KEYC_F12 }, { "IC", KEYC_IC }, { "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 }, /* Mouse keys. */ KEYC_MOUSE_STRING(MOUSEDOWN1, MouseDown1), KEYC_MOUSE_STRING(MOUSEDOWN2, MouseDown2), KEYC_MOUSE_STRING(MOUSEDOWN3, MouseDown3), KEYC_MOUSE_STRING(MOUSEUP1, MouseUp1), KEYC_MOUSE_STRING(MOUSEUP2, MouseUp2), KEYC_MOUSE_STRING(MOUSEUP3, MouseUp3), KEYC_MOUSE_STRING(MOUSEDRAG1, MouseDrag1), KEYC_MOUSE_STRING(MOUSEDRAG2, MouseDrag2), KEYC_MOUSE_STRING(MOUSEDRAG3, MouseDrag3), KEYC_MOUSE_STRING(MOUSEDRAGEND1, MouseDragEnd1), KEYC_MOUSE_STRING(MOUSEDRAGEND2, MouseDragEnd2), KEYC_MOUSE_STRING(MOUSEDRAGEND3, MouseDragEnd3), KEYC_MOUSE_STRING(WHEELUP, WheelUp), KEYC_MOUSE_STRING(WHEELDOWN, WheelDown), }; /* Find key string in table. */ static key_code 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_UNKNOWN); } /* Find modifiers. */ static key_code key_string_get_modifiers(const char **string) { key_code modifiers; modifiers = 0; while (((*string)[0] != '\0') && (*string)[1] == '-') { switch ((*string)[0]) { case 'C': case 'c': modifiers |= KEYC_CTRL; break; case 'M': case 'm': modifiers |= KEYC_ESCAPE; break; case 'S': case 's': modifiers |= KEYC_SHIFT; break; } *string += 2; } return (modifiers); } /* Lookup a string and convert to a key value. */ key_code key_string_lookup_string(const char *string) { static const char *other = "!#()+,-.0123456789:;<=>?'\r\t"; key_code key; u_short u; int size; key_code modifiers; struct utf8_data ud; u_int i; enum utf8_state more; wchar_t wc; /* Is this no key? */ if (strcasecmp(string, "None") == 0) return (KEYC_NONE); /* 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_UNKNOWN); return (u); } /* Check for modifiers. */ modifiers = 0; if (string[0] == '^' && string[1] != '\0') { modifiers |= KEYC_CTRL; string++; } modifiers |= key_string_get_modifiers(&string); if (string[0] == '\0') return (KEYC_UNKNOWN); /* Is this a standard ASCII key? */ if (string[1] == '\0' && (u_char)string[0] <= 127) { key = (u_char)string[0]; if (key < 32 || key == 127) return (KEYC_UNKNOWN); } else { /* Try as a UTF-8 key. */ if ((more = utf8_open(&ud, (u_char)*string)) == UTF8_MORE) { if (strlen(string) != ud.size) return (KEYC_UNKNOWN); for (i = 1; i < ud.size; i++) more = utf8_append(&ud, (u_char)string[i]); if (more != UTF8_DONE) return (KEYC_UNKNOWN); if (utf8_combine(&ud, &wc) != UTF8_DONE) return (KEYC_UNKNOWN); return (wc | modifiers); } /* Otherwise look the key up in the table. */ key = key_string_search_table(string); if (key == KEYC_UNKNOWN) return (KEYC_UNKNOWN); } /* Convert the standard control keys. */ if (key < KEYC_BASE && (modifiers & KEYC_CTRL) && !strchr(other, key)) { if (key >= 97 && key <= 122) key -= 96; else if (key >= 64 && key <= 95) key -= 64; else if (key == 32) key = 0; else if (key == 63) key = KEYC_BSPACE; else return (KEYC_UNKNOWN); modifiers &= ~KEYC_CTRL; } return (key | modifiers); } /* Convert a key code into string format, with prefix if necessary. */ const char * key_string_lookup_key(key_code key) { static char out[24]; char tmp[8]; u_int i; struct utf8_data ud; *out = '\0'; /* Handle no key. */ if (key == KEYC_NONE) return ("None"); /* Handle special keys. */ if (key == KEYC_UNKNOWN) return ("Unknown"); if (key == KEYC_MOUSE) return ("Mouse"); /* * Special case: display C-@ as C-Space. Could do this below in * the (key >= 0 && key <= 32), but this way we let it be found * in key_string_table, for the unlikely chance that we might * change its name. */ if ((key & KEYC_MASK_KEY) == 0) key = ' ' | KEYC_CTRL | (key & KEYC_MASK_MOD); /* Fill in the modifiers. */ if (key & KEYC_CTRL) strlcat(out, "C-", sizeof out); if (key & KEYC_ESCAPE) strlcat(out, "M-", sizeof out); if (key & KEYC_SHIFT) strlcat(out, "S-", sizeof out); key &= KEYC_MASK_KEY; /* Try the key against the string table. */ for (i = 0; i < nitems(key_string_table); i++) { if (key == key_string_table[i].key) break; } if (i != nitems(key_string_table)) { strlcat(out, key_string_table[i].string, sizeof out); return (out); } /* Is this a UTF-8 key? */ if (key > 127 && key < KEYC_BASE) { if (utf8_split(key, &ud) == UTF8_DONE) { memcpy(out, ud.data, ud.size); out[ud.size] = '\0'; return (out); } } /* Invalid keys are errors. */ if (key == 127 || key > 255) { snprintf(out, sizeof out, "Invalid#%llx", key); return (out); } /* Check for standard or control key. */ if (key <= 32) { if (key == 0 || key > 26) xsnprintf(tmp, sizeof tmp, "C-%c", (int)(64 + key)); else xsnprintf(tmp, sizeof tmp, "C-%c", (int)(96 + key)); } else if (key >= 32 && key <= 126) { tmp[0] = key; tmp[1] = '\0'; } else if (key >= 128) xsnprintf(tmp, sizeof tmp, "\\%llo", key); strlcat(out, tmp, sizeof out); return (out); } tmate-2.4.0/layout-custom.c000066400000000000000000000146651356407164200156440ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2010 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" 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 layout_cell *root) { char layout[BUFSIZ], *out; *layout = '\0'; if (layout_append(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-2.4.0/layout-set.c000066400000000000000000000352201356407164200151130ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * Set window layouts - predefined methods to arrange windows. These are * one-off and generate a layout tree. */ 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 }, }; 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-2.4.0/layout.c000066400000000000000000000435011356407164200143230ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * 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); } /* 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 if (insert_before) size2 = sx - size - 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 if (insert_before) size2 = sy - size - 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-2.4.0/log.c000066400000000000000000000065301356407164200135700ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "tmux.h" static FILE *log_file; static int log_level; static void log_event_cb(int, const char *); static void log_vwrite(const char *, va_list); static int is_log_stdout(void) { return fileno(log_file) <= 2; } /* Log callback for libevent. */ static void log_event_cb(__unused int severity, const char *msg) { log_debug("%s", msg); } /* Increment log level. */ void log_add_level(void) { log_level++; } /* Get log level. */ int log_get_level(void) { return (log_level); } void log_open_fp(FILE *f) { if (log_file != NULL && !is_log_stdout()) fclose(log_file); log_file = f; setvbuf(log_file, NULL, _IOLBF, 0); event_set_log_callback(log_event_cb); } /* Open logging to file. */ void log_open(const char *name) { char *path; if (log_level == 0) return; xasprintf(&path, "tmate-%s-%ld.log", name, (long)getpid()); FILE *f = fopen(path, "w"); free(path); if (f) log_open_fp(f); } /* Close logging. */ void log_close(void) { if (log_file != NULL && !is_log_stdout()) fclose(log_file); log_file = NULL; event_set_log_callback(NULL); } /* Write a log message. */ __attribute__((__format__(__printf__, 1, 0))) static void log_vwrite(const char *msg, va_list ap) { char *fmt, *out; struct timeval tv; if (log_file == NULL) return; if (vasprintf(&fmt, msg, ap) == -1) exit(1); if (stravis(&out, fmt, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL) == -1) exit(1); gettimeofday(&tv, NULL); if (is_log_stdout()) { if (fprintf(log_file, "%s\n", out) == -1) exit(1); } else { if (fprintf(log_file, "%lld.%06d %s\n", (long long)tv.tv_sec, (int)tv.tv_usec, out) == -1) exit(1); } fflush(log_file); free(out); free(fmt); } /* Log a debug message. */ void log_emit(int level, const char *msg, ...) { va_list ap; if (log_level < level) return; va_start(ap, msg); log_vwrite(msg, ap); va_end(ap); } /* Log a critical error with error string and die. */ __attribute__((__format__(__printf__, 1, 0))) __dead void fatal(const char *msg, ...) { char *fmt; va_list ap; va_start(ap, msg); if (asprintf(&fmt, "fatal: %s: %s", msg, strerror(errno)) == -1) exit(1); msg = fmt; log_vwrite(msg, ap); exit(1); } /* Log a critical error and die. */ __attribute__((__format__(__printf__, 1, 0))) __dead void fatalx(const char *msg, ...) { char *fmt; va_list ap; va_start(ap, msg); if (asprintf(&fmt, "fatal: %s", msg) == -1) exit(1); msg = fmt; log_vwrite(msg, ap); exit(1); } tmate-2.4.0/logo/000077500000000000000000000000001356407164200135775ustar00rootroot00000000000000tmate-2.4.0/logo/LICENSE000066400000000000000000000013561356407164200146110ustar00rootroot00000000000000Copyright (c) 2015, Jason Long Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. 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. tmate-2.4.0/logo/favicon.ico000066400000000000000000000145661356407164200157340ustar00rootroot00000000000000 (&  (N(  Ãl½ûÁÿÀÿÀÿÁÿÀÿÅÿÅÿÀÿÁÿÀÿÀÿÁÿ½ûÅk,Ž*â*’)î-)ç+‘*è+*è,*æ,)ó%¢#«%¢#«,)ó,*æ+*è+‘*è-)ç*’)î,*â;;ù; ;÷:;ø:;ø:;ø::óBBÿ<<\<<\BBÿ::ó:;ø:;ø:;ø; ;÷;;ù;A<ÿ;B<ÿ;B<ÿ;B<ÿ;B<ÿ9?9üAHBÿ9E9n9E9nAHBÿ9?9ü;B<ÿ;B<ÿ;B<ÿ;B<ÿ;A<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ;;;úBBBÿ999j999jBBBÿ;;;ú<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ;;;úBBBÿ666k666kBBBÿ;;;ú<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ;;;úBBBÿ666k888hAAAÿ:::õ;;;ú;;;ú;;;ú;;;ú;;;ú<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ;;;úBBBÿ888h999wIIIÿAAAÿBBBÿBBBÿBBBÿBBBÿBBBÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ;;;úAAAÿ999w777 999x777i666k666k666k666k666k<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ;;;úAAAÿ999w777 999x777i666k666k666k666k666k<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ;;;úBBBÿ888h999wIIIÿAAAÿBBBÿBBBÿBBBÿBBBÿBBBÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ;;;úBBBÿ666k888hAAAÿ:::õ;;;ú;;;ú;;;ú;;;ú;;;ú:::þ<<<ÿ<<<ÿ<<<ÿ<<<ÿ;;;úBBBÿ666k666kBBBÿ;;;ú<<<ÿ<<<ÿ<<<ÿ<<<ÿ:::þ<<<ÿ:::þ<<<ÿ<<<ÿ<<<ÿ;;;úBBBÿ666k666kBBBÿ;;;ú<<<ÿ<<<ÿ<<<ÿ:::þ<<<ÿ:::÷>>>ÿ:::þ<<<ÿ<<<ÿ;;;úBBBÿ666k666kBBBÿ;;;ú<<<ÿ<<<ÿ:::þ>>>ÿ:::÷888h:::÷<<<ÿ:::þ<<<ÿ;;;úBBBÿ666k666kBBBÿ;;;ú<<<ÿ:::þ<<<ÿ:::÷888h( @ ¸K¹Ë¸ù¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¸ù¹Ë¸K¸K¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¸K¹Ë¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¹ÿ¸Ê<<<Í<<<Ì<<<Ì<<<Ì<<<Ì<<<Ì<<<Ì<<<Ì<<<Ì<<<Ì<<<Ì<<<Ì<<<Ì<<<Ì<<<Ì<<<Ì<<<Ì<<<Ì<<<Ì<<<Ì<<<Ì<<<Ì<<<Ì<<<Ì<<<Ì<<<Ì<<<Ì<<<Ì<<<Ì<<<Í<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ;;;ù<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ;;;ù;;;Ë<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ;;;Ë:::K<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ:::K:::K;;;Ë;;;ù<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ<<<ÿ;;;ù;;;Ë:::Ktmate-2.4.0/logo/tmux-logo-1-color.eps000066400000000000000000000517661356407164200175340ustar00rootroot00000000000000%!PS-Adobe-3.0 EPSF-3.0 %APL_DSC_Encoding: UTF8 %APLProducer: (Version 10.10.3 (Build 14D136) Quartz PS Context) %%Title: (Unknown) %%Creator: (Unknown) %%CreationDate: (Unknown) %%For: (Unknown) %%DocumentData: Clean7Bit %%LanguageLevel: 2 %%Pages: 1 %%BoundingBox: 0 0 608 160 %%EndComments %%BeginProlog %%BeginFile: cg-pdf.ps %%Copyright: Copyright 2000-2004 Apple Computer Incorporated. %%Copyright: All Rights Reserved. currentpacking true setpacking /cg_md 141 dict def cg_md begin /L3? languagelevel 3 ge def /bd{bind def}bind def /ld{load def}bd /xs{exch store}bd /xd{exch def}bd /cmmtx matrix def mark /sc/setcolor /scs/setcolorspace /dr/defineresource /fr/findresource /T/true /F/false /d/setdash /w/setlinewidth /J/setlinecap /j/setlinejoin /M/setmiterlimit /i/setflat /rc/rectclip /rf/rectfill /rs/rectstroke /f/fill /f*/eofill /sf/selectfont /s/show /xS/xshow /yS/yshow /xyS/xyshow /S/stroke /m/moveto /l/lineto /c/curveto /h/closepath /n/newpath /q/gsave /Q/grestore counttomark 2 idiv {ld}repeat pop /SC{ /ColorSpace fr scs }bd /sopr /setoverprint where{pop/setoverprint}{/pop}ifelse ld /soprm /setoverprintmode where{pop/setoverprintmode}{/pop}ifelse ld /cgmtx matrix def /sdmtx{cgmtx currentmatrix pop}bd /CM {cgmtx setmatrix}bd /cm {cmmtx astore CM concat}bd /W{clip newpath}bd /W*{eoclip newpath}bd statusdict begin product end dup (HP) anchorsearch{ pop pop pop true }{ pop (hp) anchorsearch{ pop pop true }{ pop false }ifelse }ifelse { { { pop pop (0)dup 0 4 -1 roll put F charpath }cshow } }{ {F charpath} }ifelse /cply exch bd /cps {cply stroke}bd /pgsave 0 def /bp{/pgsave save store}bd /ep{pgsave restore showpage}def /re{4 2 roll m 1 index 0 rlineto 0 exch rlineto neg 0 rlineto h}bd /scrdict 10 dict def /scrmtx matrix def /patarray 0 def /createpat{patarray 3 1 roll put}bd /makepat{ scrmtx astore pop gsave initgraphics CM patarray exch get scrmtx makepattern grestore setpattern }bd /cg_BeginEPSF{ userdict save/cg_b4_Inc_state exch put userdict/cg_endepsf/cg_EndEPSF load put count userdict/cg_op_count 3 -1 roll put countdictstack dup array dictstack userdict/cg_dict_array 3 -1 roll put 3 sub{end}repeat /showpage {} def 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin 10 setmiterlimit [] 0 setdash newpath false setstrokeadjust false setoverprint }bd /cg_EndEPSF{ countdictstack 3 sub { end } repeat cg_dict_array 3 1 index length 3 sub getinterval {begin}forall count userdict/cg_op_count get sub{pop}repeat userdict/cg_b4_Inc_state get restore F setpacking }bd /cg_biproc{currentfile/RunLengthDecode filter}bd /cg_aiproc{currentfile/ASCII85Decode filter/RunLengthDecode filter}bd /ImageDataSource 0 def L3?{ /cg_mibiproc{pop pop/ImageDataSource{cg_biproc}def}bd /cg_miaiproc{pop pop/ImageDataSource{cg_aiproc}def}bd }{ /ImageBandMask 0 def /ImageBandData 0 def /cg_mibiproc{ string/ImageBandMask xs string/ImageBandData xs /ImageDataSource{[currentfile/RunLengthDecode filter dup ImageBandMask/readstring cvx /pop cvx dup ImageBandData/readstring cvx/pop cvx]cvx bind}bd }bd /cg_miaiproc{ string/ImageBandMask xs string/ImageBandData xs /ImageDataSource{[currentfile/ASCII85Decode filter/RunLengthDecode filter dup ImageBandMask/readstring cvx /pop cvx dup ImageBandData/readstring cvx/pop cvx]cvx bind}bd }bd }ifelse /imsave 0 def /BI{save/imsave xd mark}bd /EI{imsave restore}bd /ID{ counttomark 2 idiv dup 2 add dict begin {def} repeat pop /ImageType 1 def /ImageMatrix[Width 0 0 Height neg 0 Height]def currentdict dup/ImageMask known{ImageMask}{F}ifelse exch L3?{ dup/MaskedImage known { pop << /ImageType 3 /InterleaveType 2 /DataDict currentdict /MaskDict << /ImageType 1 /Width Width /Height Height /ImageMatrix ImageMatrix /BitsPerComponent 1 /Decode [0 1] currentdict/Interpolate known {/Interpolate Interpolate}if >> >> }if }if exch {imagemask}{image}ifelse end }bd /cguidfix{statusdict begin mark version end {cvr}stopped{cleartomark 0}{exch pop}ifelse 2012 lt{dup findfont dup length dict begin {1 index/FID ne 2 index/UniqueID ne and {def} {pop pop} ifelse}forall currentdict end definefont pop }{pop}ifelse }bd /t_array 0 def /t_i 0 def /t_c 1 string def /x_proc{ exch t_array t_i get add exch moveto /t_i t_i 1 add store }bd /y_proc{ t_array t_i get add moveto /t_i t_i 1 add store }bd /xy_proc{ t_array t_i 2 copy 1 add get 3 1 roll get 4 -1 roll add 3 1 roll add moveto /t_i t_i 2 add store }bd /sop 0 def /cp_proc/x_proc ld /base_charpath { /t_array xs /t_i 0 def { t_c 0 3 -1 roll put currentpoint t_c cply sop cp_proc }forall /t_array 0 def }bd /sop/stroke ld /nop{}def /xsp/base_charpath ld /ysp{/cp_proc/y_proc ld base_charpath/cp_proc/x_proc ld}bd /xysp{/cp_proc/xy_proc ld base_charpath/cp_proc/x_proc ld}bd /xmp{/sop/nop ld /cp_proc/x_proc ld base_charpath/sop/stroke ld}bd /ymp{/sop/nop ld /cp_proc/y_proc ld base_charpath/sop/stroke ld}bd /xymp{/sop/nop ld /cp_proc/xy_proc ld base_charpath/sop/stroke ld}bd /refnt{ findfont dup length dict copy dup /Encoding 4 -1 roll put definefont pop }bd /renmfont{ findfont dup length dict copy definefont pop }bd L3? dup dup{save exch}if /Range 0 def /DataSource 0 def /val 0 def /nRange 0 def /mulRange 0 def /d0 0 def /r0 0 def /di 0 def /ri 0 def /a0 0 def /a1 0 def /r1 0 def /r2 0 def /dx 0 def /Nsteps 0 def /sh3tp 0 def /ymax 0 def /ymin 0 def /xmax 0 def /xmin 0 def /setupFunEval { begin /nRange Range length 2 idiv store /mulRange [ 0 1 nRange 1 sub { 2 mul/nDim2 xd Range nDim2 get Range nDim2 1 add get 1 index sub 255 div exch }for ]store end }bd /FunEval { begin nRange mul /val xd 0 1 nRange 1 sub { dup 2 mul/nDim2 xd val add DataSource exch get mulRange nDim2 get mul mulRange nDim2 1 add get add }for end }bd /max { 2 copy lt {exch pop}{pop}ifelse }bd /sh2 { /Coords load aload pop 3 index 3 index translate 3 -1 roll sub 3 1 roll exch sub 2 copy dup mul exch dup mul add sqrt dup scale atan rotate /Function load setupFunEval clippath {pathbbox}stopped {0 0 0 0}if newpath /ymax xs /xmax xs /ymin xs /xmin xs currentdict/Extend known { /Extend load 0 get { 0/Function load FunEval sc xmin ymin xmin abs ymax ymin sub rectfill }if }if /Nsteps/Function load/Size get 0 get 1 sub store /dx 1 Nsteps div store gsave /di ymax ymin sub store /Function load 0 1 Nsteps { 1 index FunEval sc 0 ymin dx di rectfill dx 0 translate }for pop grestore currentdict/Extend known { /Extend load 1 get { Nsteps/Function load FunEval sc 1 ymin xmax 1 sub abs ymax ymin sub rectfill }if }if }bd /shp { 4 copy dup 0 gt{ 0 exch a1 a0 arc }{ pop 0 moveto }ifelse dup 0 gt{ 0 exch a0 a1 arcn }{ pop 0 lineto }ifelse fill dup 0 gt{ 0 exch a0 a1 arc }{ pop 0 moveto }ifelse dup 0 gt{ 0 exch a1 a0 arcn }{ pop 0 lineto }ifelse fill }bd /calcmaxs { xmin dup mul ymin dup mul add sqrt xmax dup mul ymin dup mul add sqrt xmin dup mul ymax dup mul add sqrt xmax dup mul ymax dup mul add sqrt max max max }bd /sh3 { /Coords load aload pop 5 index 5 index translate 3 -1 roll 6 -1 roll sub 3 -1 roll 5 -1 roll sub 2 copy dup mul exch dup mul add sqrt /dx xs 2 copy 0 ne exch 0 ne or { exch atan rotate }{ pop pop }ifelse /r2 xs /r1 xs /Function load dup/Size get 0 get 1 sub /Nsteps xs setupFunEval dx r2 add r1 lt{ 0 }{ dx r1 add r2 le { 1 }{ r1 r2 eq { 2 }{ 3 }ifelse }ifelse }ifelse /sh3tp xs clippath {pathbbox}stopped {0 0 0 0}if newpath /ymax xs /xmax xs /ymin xs /xmin xs dx dup mul r2 r1 sub dup mul sub dup 0 gt { sqrt r2 r1 sub atan /a0 exch 180 exch sub store /a1 a0 neg store }{ pop /a0 0 store /a1 360 store }ifelse currentdict/Extend known { /Extend load 0 get r1 0 gt and { 0/Function load FunEval sc { { dx 0 r1 360 0 arcn xmin ymin moveto xmax ymin lineto xmax ymax lineto xmin ymax lineto xmin ymin lineto eofill } { r1 0 gt{0 0 r1 0 360 arc fill}if } { 0 r1 xmin abs r1 add neg r1 shp } { r2 r1 gt{ 0 r1 r1 neg r2 r1 sub div dx mul 0 shp }{ 0 r1 calcmaxs dup r2 add dx mul dx r1 r2 sub sub div neg exch 1 index abs exch sub shp }ifelse } }sh3tp get exec }if }if /d0 0 store /r0 r1 store /di dx Nsteps div store /ri r2 r1 sub Nsteps div store /Function load 0 1 Nsteps { 1 index FunEval sc d0 di add r0 ri add d0 r0 shp { d0 0 r0 a1 a0 arc d0 di add 0 r0 ri add a0 a1 arcn fill d0 0 r0 a0 a1 arc d0 di add 0 r0 ri add a1 a0 arcn fill }pop /d0 d0 di add store /r0 r0 ri add store }for pop currentdict/Extend known { /Extend load 1 get r2 0 gt and { Nsteps/Function load FunEval sc { { dx 0 r2 0 360 arc fill } { dx 0 r2 360 0 arcn xmin ymin moveto xmax ymin lineto xmax ymax lineto xmin ymax lineto xmin ymin lineto eofill } { xmax abs r1 add r1 dx r1 shp } { r2 r1 gt{ calcmaxs dup r1 add dx mul dx r2 r1 sub sub div exch 1 index exch sub dx r2 shp }{ r1 neg r2 r1 sub div dx mul 0 dx r2 shp }ifelse } } sh3tp get exec }if }if }bd /sh { begin /ShadingType load dup dup 2 eq exch 3 eq or { gsave newpath /ColorSpace load scs currentdict/BBox known { /BBox load aload pop 2 index sub 3 index 3 -1 roll exch sub exch rectclip }if 2 eq {sh2}{sh3}ifelse grestore }{ pop (DEBUG: shading type unimplemented\n)print flush }ifelse end }bd {restore}if not dup{save exch}if L3?{ /sh/shfill ld /csq/clipsave ld /csQ/cliprestore ld }if {restore}if end setpacking %%EndFile %%EndProlog %%BeginSetup %%EndSetup %%Page: 1 1 %%PageBoundingBox: 0 0 608 160 %%BeginPageSetup cg_md begin bp sdmtx [ /CIEBasedABC 4 dict dup begin /WhitePoint [ 0.9505 1.0000 1.0891 ] def /DecodeABC [ { 1.0 0.0 3 -1 roll 1 index 1 index le { exch pop} { pop } ifelse 1 index 1 index ge { exch pop } { pop } ifelse < 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000001010101010101010101010101 0101010101010101010101010101010101010101010101020202020202020202 0202020202020202020202020202020202030303030303030303030303030303 0303030303030304040404040404040404040404040404040404050505050505 0505050505050505050506060606060606060606060606060607070707070707 0707070707070708080808080808080808080808090909090909090909090909 0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c 0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f10101010 1010101010111111111111111112121212121212121313131313131313141414 1414141414151515151515151616161616161616171717171717171818181818 18181919191919191a1a1a1a1a1a1a1b1b1b1b1b1b1c1c1c1c1c1c1c1d1d1d1d 1d1d1e1e1e1e1e1e1f1f1f1f1f1f202020202020212121212121222222222223 2323232323242424242425252525252526262626262727272727282828282829 292929292a2a2a2a2a2b2b2b2b2b2c2c2c2c2c2d2d2d2d2d2e2e2e2e2e2f2f2f 2f2f303030303131313131323232323333333333343434343535353535363636 36373737373838383839393939393a3a3a3a3b3b3b3b3c3c3c3c3d3d3d3d3e3e 3e3e3f3f3f3f4040404041414141424242424343434444444445454545464646 4647474748484848494949494a4a4a4b4b4b4b4c4c4c4d4d4d4d4e4e4e4f4f4f 4f50505051515151525252535353535454545555555656565657575758585859 59595a5a5a5a5b5b5b5c5c5c5d5d5d5e5e5e5f5f5f6060606061616162626263 63636464646565656666666767676868686969696a6a6a6b6b6b6c6c6d6d6d6e 6e6e6f6f6f707070717171727273737374747475757576767677777878787979 797a7a7b7b7b7c7c7c7d7d7e7e7e7f7f7f808081818182828283838484848585 86868687878888888989898a8a8b8b8b8c8c8d8d8d8e8e8f8f90909091919292 9293939494949595969697979798989999999a9a9b9b9c9c9c9d9d9e9e9f9f9f a0a0a1a1a2a2a3a3a3a4a4a5a5a6a6a6a7a7a8a8a9a9aaaaabababacacadadae aeafafb0b0b0b1b1b2b2b3b3b4b4b5b5b6b6b6b7b7b8b8b9b9bababbbbbcbcbd bdbebebebfbfc0c0c1c1c2c2c3c3c4c4c5c5c6c6c7c7c8c8c9c9cacacbcbcccc cdcdcececfcfd0d0d1d1d2d2d3d3d4d4d5d5d6d6d7d7d8d8d9d9dadadbdcdcdd dddededfdfe0e0e1e1e2e2e3e3e4e4e5e6e6e7e7e8e8e9e9eaeaebebecededee eeefeff0f0f1f1f2f3f3f4f4f5f5f6f6f7f8f8f9f9fafafbfcfcfdfdfefeffff > dup length 1 sub 3 -1 roll mul dup dup floor cvi exch ceiling cvi 3 index exch get 4 -1 roll 3 -1 roll get dup 3 1 roll sub 3 -1 roll dup floor cvi sub mul add 255 div } bind { 1.0 0.0 3 -1 roll 1 index 1 index le { exch pop} { pop } ifelse 1 index 1 index ge { exch pop } { pop } ifelse < 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000001010101010101010101010101 0101010101010101010101010101010101010101010101020202020202020202 0202020202020202020202020202020202030303030303030303030303030303 0303030303030304040404040404040404040404040404040404050505050505 0505050505050505050506060606060606060606060606060607070707070707 0707070707070708080808080808080808080808090909090909090909090909 0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c 0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f10101010 1010101010111111111111111112121212121212121313131313131313141414 1414141414151515151515151616161616161616171717171717171818181818 18181919191919191a1a1a1a1a1a1a1b1b1b1b1b1b1c1c1c1c1c1c1c1d1d1d1d 1d1d1e1e1e1e1e1e1f1f1f1f1f1f202020202020212121212121222222222223 2323232323242424242425252525252526262626262727272727282828282829 292929292a2a2a2a2a2b2b2b2b2b2c2c2c2c2c2d2d2d2d2d2e2e2e2e2e2f2f2f 2f2f303030303131313131323232323333333333343434343535353535363636 36373737373838383839393939393a3a3a3a3b3b3b3b3c3c3c3c3d3d3d3d3e3e 3e3e3f3f3f3f4040404041414141424242424343434444444445454545464646 4647474748484848494949494a4a4a4b4b4b4b4c4c4c4d4d4d4d4e4e4e4f4f4f 4f50505051515151525252535353535454545555555656565657575758585859 59595a5a5a5a5b5b5b5c5c5c5d5d5d5e5e5e5f5f5f6060606061616162626263 63636464646565656666666767676868686969696a6a6a6b6b6b6c6c6d6d6d6e 6e6e6f6f6f707070717171727273737374747475757576767677777878787979 797a7a7b7b7b7c7c7c7d7d7e7e7e7f7f7f808081818182828283838484848585 86868687878888888989898a8a8b8b8b8c8c8d8d8d8e8e8f8f90909091919292 9293939494949595969697979798989999999a9a9b9b9c9c9c9d9d9e9e9f9f9f a0a0a1a1a2a2a3a3a3a4a4a5a5a6a6a6a7a7a8a8a9a9aaaaabababacacadadae aeafafb0b0b0b1b1b2b2b3b3b4b4b5b5b6b6b6b7b7b8b8b9b9bababbbbbcbcbd bdbebebebfbfc0c0c1c1c2c2c3c3c4c4c5c5c6c6c7c7c8c8c9c9cacacbcbcccc cdcdcececfcfd0d0d1d1d2d2d3d3d4d4d5d5d6d6d7d7d8d8d9d9dadadbdcdcdd dddededfdfe0e0e1e1e2e2e3e3e4e4e5e6e6e7e7e8e8e9e9eaeaebebecededee eeefeff0f0f1f1f2f3f3f4f4f5f5f6f6f7f8f8f9f9fafafbfcfcfdfdfefeffff > dup length 1 sub 3 -1 roll mul dup dup floor cvi exch ceiling cvi 3 index exch get 4 -1 roll 3 -1 roll get dup 3 1 roll sub 3 -1 roll dup floor cvi sub mul add 255 div } bind { 1.0 0.0 3 -1 roll 1 index 1 index le { exch pop} { pop } ifelse 1 index 1 index ge { exch pop } { pop } ifelse < 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000001010101010101010101010101 0101010101010101010101010101010101010101010101020202020202020202 0202020202020202020202020202020202030303030303030303030303030303 0303030303030304040404040404040404040404040404040404050505050505 0505050505050505050506060606060606060606060606060607070707070707 0707070707070708080808080808080808080808090909090909090909090909 0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c 0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f10101010 1010101010111111111111111112121212121212121313131313131313141414 1414141414151515151515151616161616161616171717171717171818181818 18181919191919191a1a1a1a1a1a1a1b1b1b1b1b1b1c1c1c1c1c1c1c1d1d1d1d 1d1d1e1e1e1e1e1e1f1f1f1f1f1f202020202020212121212121222222222223 2323232323242424242425252525252526262626262727272727282828282829 292929292a2a2a2a2a2b2b2b2b2b2c2c2c2c2c2d2d2d2d2d2e2e2e2e2e2f2f2f 2f2f303030303131313131323232323333333333343434343535353535363636 36373737373838383839393939393a3a3a3a3b3b3b3b3c3c3c3c3d3d3d3d3e3e 3e3e3f3f3f3f4040404041414141424242424343434444444445454545464646 4647474748484848494949494a4a4a4b4b4b4b4c4c4c4d4d4d4d4e4e4e4f4f4f 4f50505051515151525252535353535454545555555656565657575758585859 59595a5a5a5a5b5b5b5c5c5c5d5d5d5e5e5e5f5f5f6060606061616162626263 63636464646565656666666767676868686969696a6a6a6b6b6b6c6c6d6d6d6e 6e6e6f6f6f707070717171727273737374747475757576767677777878787979 797a7a7b7b7b7c7c7c7d7d7e7e7e7f7f7f808081818182828283838484848585 86868687878888888989898a8a8b8b8b8c8c8d8d8d8e8e8f8f90909091919292 9293939494949595969697979798989999999a9a9b9b9c9c9c9d9d9e9e9f9f9f a0a0a1a1a2a2a3a3a3a4a4a5a5a6a6a6a7a7a8a8a9a9aaaaabababacacadadae aeafafb0b0b0b1b1b2b2b3b3b4b4b5b5b6b6b6b7b7b8b8b9b9bababbbbbcbcbd bdbebebebfbfc0c0c1c1c2c2c3c3c4c4c5c5c6c6c7c7c8c8c9c9cacacbcbcccc cdcdcececfcfd0d0d1d1d2d2d3d3d4d4d5d5d6d6d7d7d8d8d9d9dadadbdcdcdd dddededfdfe0e0e1e1e2e2e3e3e4e4e5e6e6e7e7e8e8e9e9eaeaebebecededee eeefeff0f0f1f1f2f3f3f4f4f5f5f6f6f7f8f8f9f9fafafbfcfcfdfdfefeffff > dup length 1 sub 3 -1 roll mul dup dup floor cvi exch ceiling cvi 3 index exch get 4 -1 roll 3 -1 roll get dup 3 1 roll sub 3 -1 roll dup floor cvi sub mul add 255 div } bind ] def /MatrixABC [ 0.4124 0.2126 0.0193 0.3576 0.7151 0.1192 0.1805 0.0722 0.9508 ] def /RangeLMN [ 0.0 0.9505 0.0 1.0000 0.0 1.0891 ] def end ] /Cs1 exch/ColorSpace dr pop %%EndPageSetup 0.60000002 i /Cs1 SC 0.23529412 0.23529412 0.23529412 sc q 2 15.003872 m 2 7.8149743 7.8157911 2 14.998466 2 c 145.00154 2 l 152.17584 2 158 7.8244047 158 15.003872 c 160 15.003872 m 160 6.7174625 153.27803 0 145.00154 0 c 14.998466 0 l 6.7150416 0 0 6.7065849 0 15.003872 c 2 14 m 0 16 l 160 16 l 158 14 l 160 14 m 0 14 l W 0 0 608 160 rc -5 21 m 165 21 l 165 -5 l -5 -5 l h f Q q 83 90 m 83 160 l 77 160 l 77 14 l 83 14 l 83 84 l 160 84 l 160 90 l 83 90 l h 0 144.99352 m 0 153.28137 6.7219648 160 14.998466 160 c 145.00154 160 l 153.28496 160 160 153.27509 160 144.99352 c 160 14 l 0 14 l 0 144.99352 l h 0 144.99352 m W* 0 0 608 160 rc -5 165 m 165 165 l 165 9 l -5 9 l h f Q q 242 12 m 230.85426 12.165432 222.63789 15.032893 217.12346 20.7679 c 211.60902 26.502909 208.85185 34.995007 209 46.244446 c 209 98 l 189 98 l 189 113 l 209 113 l 209 146 l 225 146 l 225 113 l 262 112.91358 l 262 98.024689 l 225 98 l 225 46.079014 l 225.39507 39.571983 226.99422 34.802074 230.1926 31.769136 c 233.39096 28.736198 237.58186 27.219753 242.76543 27.219753 c 245.19179 27.219753 247.89381 27.49547 250.87161 28.046913 c 253.8494 28.598356 256.88229 29.425508 259.97037 30.528395 c 263.27902 15.97037 l 259.52921 14.646907 255.86215 13.681896 252.27777 13.075309 c 248.69341 12.468721 245.19179 12.165432 241.77284 12.165432 c 242 12 l h 295 14 m 278.15918 14 l 278 112.91358 l 295 112.91358 l 295 96.370369 l 295 96.370369 304.76617 106.29628 309.67401 109.60493 c 314.58185 112.9136 320.34436 114.5679 326.96167 114.5679 c 332.80695 114.5679 337.79745 112.94117 341.93326 109.68765 c 346.06909 106.43414 349.01926 101.99509 350.78387 96.370369 c 350.78387 96.370369 361.92294 106.29628 366.99622 109.60493 c 372.06952 112.9136 378.08014 114.5679 385.02832 114.5679 c 392.74854 114.5679 398.92459 111.72801 403.55673 106.04815 c 408.18884 100.36829 410.50488 92.730911 410 83.139999 c 410 14 l 394 14 l 393.96167 80.488892 l 393.96167 87.106209 392.69336 91.820976 390.15674 94.633331 c 387.62009 97.445694 383.98062 98.851852 379.23822 98.851852 c 374.49579 98.851852 369.78104 97.418121 365.09375 94.550621 c 360.40649 91.683113 356.24316 87.878212 353 83.135803 c 353 14 l 336.06042 14 l 336.06042 80.488892 l 336.06042 87.106209 334.79211 91.820976 332.25549 94.633331 c 329.71884 97.445694 326.07938 98.851852 321.33698 98.851852 c 316.59457 98.851852 311.87979 97.418121 307.19254 94.550621 c 302.50525 91.683113 298.34192 87.878212 295 83.135803 c 295 14 l h 438.9505 21.264198 m 433.10519 27.440361 430.18259 35.656738 430 46 c 430 113 l 447 113 l 447 49.220001 l 446.7258 42.16375 448.3801 36.814835 451.68875 33.175308 c 454.99741 29.535784 459.85004 27.716049 466.2468 27.716049 c 471.32007 27.716049 476.36569 29.177351 481.38382 32.099998 c 486.40195 35.022648 491.39243 39.35141 496 45.086418 c 496 112.91358 l 513 113 l 512.89862 14 l 496 14 l 496 30.197531 l 496 30.197531 485.13364 20.271622 479.89493 16.962963 c 474.65622 13.654305 468.78345 12 462.2764 12 c 452.57101 12 444.79578 15.088035 438.9505 21.264198 c h 588.65784 14 m 564.50476 51.041977 l 541.84052 14 l 523.4776 14 l 556.56403 63.449383 l 524.63562 113 l 543.164 113 l 566.15906 77.180244 l 589.48499 113 l 607.84796 113 l 574.43066 64.441978 l 607.18622 14 l 588.65784 14 l h 588.65784 14 m W* 0 0 608 160 rc 184 151 m 612.84796 151 l 612.84796 7 l 184 7 l h f ep end %%Trailer %%EOF tmate-2.4.0/logo/tmux-logo-1-color.svg000066400000000000000000000101611356407164200175240ustar00rootroot00000000000000 logomark + wordmark copy 3 Created with Sketch. tmate-2.4.0/logo/tmux-logo-huge.png000066400000000000000000001340211356407164200171670ustar00rootroot00000000000000‰PNG  IHDR àíÞzwsRGB®Îé@IDATxìÝ €]ey0þ÷Ü;“…%$$s'3Q[÷ZµTÁº}þ3 JùhÝPq×B-¶¸µnÕRpG•Zmc‹¥@2qû\P¨KU¬u«Ú™ÉÜIšdæžÿ{C‚ d™åÜ{Ï9÷wÛ˜;çžó¾Ïó;‡œ;ç9ç}“£×‘GÙ»`Á‚§iúÐ$ICkþy`|¿8.;8¾ßþ'þ ùÞ[o½õí×]wÝí]-!y @€ @€ÌP 2Ãõ§µz|êñ™±øøãX|<7n ø8-5+ @€@ŽjžÃ>øàÿ>öØcŸ•£¸„B€ @€ @€Ü ôda¼H;ŸùP¼h»:ËvµE€:!Ïgƒ±ßËâ<Ækãû—Å'“G;‡>  @€ @€ P$Ìž€ŒgŸÿ¾âc‘v¿X  @`:;Îmßßq®›Î&Ö!@€ @€ @€@× Ì¹çzìdÏg/Їv­¤Ä  @ ì‡6ÏuÍs^óÜWödåG€ @€ @€Ù Ì©ù¸Ç=î~q®Ç¯Æ ²gÍ6Û @€" 4ÏyÍs_óX¤¸ÅJ€ @€ @€v ̺ٜﱷ·÷êx!öèv« 湯ylž ó @€ @€ 'Y ãðsˆI|#^€}hž’ h—@ó˜¦é7wœÛÕ­~ @€ @€ { w\hýZÌlyî³ h¡@,BöÇæ¿¦ÙBdM @€ @€ P8 ›CÍÅ‹­ÃñÏa…ËTÀ @ ÍsbóÜh8Öàj’ @€ @€B L»ù¸Ç=î~q¨¹õ1KO>rW šZ(°¼yŽlž+[؇¦  @€ @€ Pi <òÈÞyóæý{|ÂÜ…Ø­‚$@€v 4Ï‘ÍseóœÙî¾õG€ @€ @€< L«¹páÂwÆ Ÿ§ÀÅB€r(ð„ç̆&$ @€ @€´G`¿ÈcŽ9æ1”³ÛŽ^ @€@áÎÞqî,|" @€ @€ @€ÀlöY€<öØcãrŸ˜Mö!@€],ðñæ9´‹ó—: @€ @€],°Ïdtù`üshûHÌX Þ¼sXܨyõ"@€ @€ @€@× ìµyôÑG?3j×u"&@€Ù·ã\šMkZ!@€ @€ @€@AöX€<òÈ#¨T*$a @€\ 4Ï¥Ísj.ƒ @€ @€Z$°ÇäÂ… ÏÃÇ™»ªEèš%@€îhžK›çÔîÈV– @€ @€¸[à>Èc=öÐ4M_ ˆ˜»@óœÚ<·Î½%- @€ @€ @ ÷)@6WÇ'6.Fø¢$@€ùhžS›çÖ|G): @€ @€d'°[ò˜cŽ98ÎWõšìš× Ð<·6ϱ$ @€ @€ Ð » ãSgƤ × {^Ž ÐNæðæ/ng‡ú"@€ @€ @€@§v+@Æ‹£§u*ý @€2 Ä›|^TæüäF€ @€ @€÷ >úèÇÄ‹£Øù¿  @€ìšçØæ¹6»µD€ @€ @€| ÜS€ŒóSšÏEE€Ê!à\[Žý(  @€ @€ö-°³Ùüû9û^Õ§ @€Àž·ßyîcS6'@€ @€ @€@>¶_=ꨣ‡†;"Ÿ!ŠŠ”C žk—6ϹåÈF @€ @€سÀödµZ}êž?¶”ÈRÀ97KMm @€ @€ GÃÀ)@æqÊ(àœ[ƽ*' @€ @€îh {âpO¼g‰7 @€@Ëvœs{ZÖ†  @€ @€ ÐaÊ1ÇóÃAŽC÷ @ [Úqîí–|åI€ @€ @€@— 4Ÿ€l ½ @€ö 8÷¶ÏZO @€ @€´Y@²Íàº#@€Q@Òa@€ @€ @€@i K»k%F€9P€ÌñÎ @€ @€sh gnMØš˜¡€sï Á¬N€ @€ @€@q*I’,.N¸"%@€Åpî-þ>” @€ @€{èIÓôàx!tïkø„ÈT yîÍ´Au­@ߺ³*•ÊùEØ–N>úú¡ñ=ñ @€ @€Ü-ЂÕEPGh¯€so{½õF€ @€ @€@ Ûˆ­+ °C@Ò¡@€ @€ @€@išs@Î+mv#@€9pîÍáN @€ @€™ 4Ÿ€ô"@€ @€ @€ @€@& ™0j„ @€ @€ @€¦€¤ã€ @€ @€ @€Ì 3£Ô @€ @€ @€ Ž @€ @€ @€2P€ÌŒRC @€ @€ @€(@: @€ @€ @€ÈL@23J  @€ @€ @€  é @€ @€ @€ @ 3ÈÌ(5D€ @€ @€ @€€¤c€ @€ @€ @€Ì 3£Ô @€ @€ @€ Ž @€ @€ @€2P€ÌŒRC @€ @€ @€(@: @€ @€ @€ÈL@23J  @€ @€ @€  é @€ @€ @€ @ 3ÈÌ(5D€ @€ @€ @€€¤c€ @€ @€ @€Ì 3£Ô @€ @€ @€ Ž @€ @€ @€2P€ÌŒRC @€ @€ @€(@: @€ @€ @€ÈL@23J  @€ @€ @€  é @€ @€ @€ @ 3ÈÌ(5D€ @€ @€ @€€¤c€ @€ @€ @€Ì 3£Ô @€ @€ @€ Ž @€ @€ @€2P€ÌŒRC @€ @€ @€(@: @€ @€ @€ÈL@23J  @€ @€ @€  é @€ @€ @€ @ 3ÈÌ(5D€ @€ @€ @€€¤c€ @€ @€ @€Ì 3£Ô @€ @€ @€ Ž @€ @€ @€2P€ÌŒRC @€ @€ @€(@: @€ @€ @€ÈL@23J  @€ @€ @€  é @€ @€ @€ @ 3ÈÌ(5D€ @€ @€ @€€¤c€ @€ @€ @€Ì 3£Ô @€ @€ @€ Ž @€ @€ @€2P€ÌŒRC @€ @€ @€(@: @€ @€ @€ÈL@23J  @€ @€ @€  é @€ @€ @€ @ 3ÈÌ(5D€ @€ @€ @€€¤c€ @€ @€ @€Ì 3£Ô @€ @€ @€ Ž @€ @€ @€2P€ÌŒRC @€ @€ @€(@: @€ @€ @€ÈL@23J  @€ @€ @€  é @€ @€ @€ @ 3ÈÌ(5D€ @€ @€ @€€¤c€ @€ @€ @€Ì 3£Ô @€ @€ @€ Ž @€ @€ @€2P€ÌŒRC @€ @€ @€(@: @€ @€ @€ÈL@23J  @€ @€ @€  é @€ @€ @€ @ 3ÈÌ(5D€ @€ @€ @€€¤c€ @€ @€ @€Ì 3£Ô @€ @€ @€ Ž @€ @€ @€2P€ÌŒRC @€ @€ @€(@: @€ @€ @€ÈL@23J  @€ @€ @€  é @€ @€ @€ @ 3ÈÌ(5D€ @€ @€ @€€¤c€ @€ @€ @€Ì 3£Ô @€ @€ @€ Ž @€ @€ @€2P€ÌŒRC @€ @€ @€(@: @€ @€ @€ÈL@23J  @€ @€ @€  é @€ @€ @€ @ 3ÈÌ(5D€ @€ @€ @€€¤c€ @€ @€ @€Ì 3£Ô @€ @€ @€ Ž @€ @€ @€2P€ÌŒRC @€ @€ @€(@: @€ @€ @€ÈL@23J  @€ @€ @€  é @€ @€ @€ @ 3ÈÌ(5D€ @€ @€ @€@ @€b Ä;É’bF.jäW`ÑðòÕ¾øol_ÿ$i²,„ôÀø/î‚øî‚æßqù‚Æ?ÛNñý!iÜy÷ßÉö¿“nNÓ0ÿ©ÞÐ[7lÝ8^¶å7s‘Và¼ÐsøcŽ˜7¿²¬‘†e•J#³Éñx\sš¿Ûq’ù;ŽÝx(§·¦!¹-$émqÙmIšÞÖ¨$ñïÆmP½%™Üúˉ;&~N [ k#p @€Ž (@vŒ^Ç @€¹ 4âqÃYÌÍÐÖt§À’«–,ë­.ü½XH|D,Îü^¬Ä<,¤I|ßßÏß©Þq«Çöw;ÇUv.ß±hû‚]n Ù±B²c³jl²vÿÁ4 '›bÑg4ö{]HÓï%¡ñ½É&¯Ûtê¦ÛïiÜ{X²fÉA=.xhZM´‡ÇU¯¤Íy’ž„»¶êÎc±ÙÆŽãogs÷ü¸Ë›»ßÆÿm¾‰Mlÿ^‘TCµ¹Qï¼P[48•¬#±ŸŸÅc÷§išþ,®÷³»S?¹iõø†mû› @€À½ ï-âg @€(ÀÒË–‘,˜ÿG•$üQ¬®<*I›Çä°f‚÷ÔaîóCöéo/%¡ùdÚ²Øïãî®UBõˆj£6¼âg± ùݸü [’äó7Ld‹"°øÊÁCê ÇÄ'h1?:+‹÷ ì,2îšÇÝeÇÝŽä]?žóû$‰ÕÈûy`ޏ£Ö·#¥œ¸Û£Jv&_\©ÎKãq{]áKÍ¢derÛWê'Ôï(âþ3 @€@6 Ù8j… @€:$ÐUÿƒÕÊÉqÉ“cÑæ1!ÖôòX®™6OŸÔ É£ª¡çœþEƒ7¥ÃégÒ©ð‰‰ãƾ3í6¬˜gdÙðòÇVÒʳbñYñX}Èoظçóù^bÛñ„ï£b5ýQ1³ÓÞÞÍñ ÉÏ4&Ó‹·{A³˜ PräØc7×y 7«¯¾:“ß;ý7ž·=+w dõß8Ïû Ä‹]_ºïÒ’.IÃ`¼pù»…Ï.M¿/VÞVø<º(t*}W}õØç‹œrmÝŠÓ“Júœ"çÐŒ=Ý:ù'ñ)£‹žÇlâﻲïa•žy'ÇmOŽÿ†üþlÚ(Ú6qþ½Å÷?±mòŽOÝpÜ ‹WÇ{^è©5øä» Žé‰ñ Çþ®ñHÃui’^|û¶ð©[ŽýM×ä]ÐDû× þß8\õK þ=aOnIÎØôŒ‘_ݳÀs¨ ^or:hŽÍttóFH¿9±rì â·'qúÅ'ÿWüvQ™Þ%?_9rv™2’KkâŒÿ<¤5­w¸ÕÉ©?÷d‡÷î  @€¬’§fÝbnÛËäV•d—$ËAB˜@£>9ƒÕs¹j¼€ÔœÏ¬ðÿ^l •æˆ]óZzéÒ+Kç?·’&/EÇGuMâ;crÆ¡9û{{¾£xðÓi2ùŽúÊúOºÍ¡Hù.[;øðJ5¼$Î=ú¼X|Ü>÷hü·§H)Ì=Öxƒ@|Bò½q^Ë¿=pxÅe1ýÔWŽ|sî k¡%I:B¥ðçÇP*t¡¨%ûV£sˆçà?Š 2§F:¼q<ûÜÞáví>>•~:ž¿°ë½jmxà«õ¡±ËK”“T2è_7ðÂø½èeeünoœüçúqã߯dl¦9 @€ ©@sˆÕXp» gé‚ñ8¸êEÝX|Ü4szb1ëÔöü(>ùÿÙÚÚÚcvýÜû |<,è[7ø¼ø´Ì׫ÕäGq½ú·ÅÇÇÖÙîÄ›?ž/€#Ú\±ôªåìl8z'@€N Ô‡F¿ç ¾¤“1´¶ïä}‡_~øÁ­íCëEXzÙÒ#ÒJòž¢Æ¿¯¸ãݛ¶ÉW6×Q€Ü—”Ï @€ @ cˆW ŸO{ª?‰œ×Ä@w,˜vŸÆˆ¿Ó'''ÕÞÿì_¿bm,Ô>:‡avMHK¯|`Üç÷÷ ŽW*É?Äýsl×$?ÃD£ÍñÕjåûñÆ‚O.[·ìþ3ÜÜê PäŽÉsb²”ÃÊÇsÝÀü”²ÀT’ï£iô°àCñ&µûu4ˆuÞ˜š:mç4! -BÖ, @€ÌN ¶vàÿ‹…‰ÿ¨&áÊxñæéñ—ó.³rVn«Òjõ»µõ+>°hxùŽ¡>gÕŽf(pÄÚ߉ó÷\Rí ?›žåiÇéÆÿ¶ãÍÉ©ÕdþO›O8/[»lÉô¶´”E`ü¤ñ_§éÔ+ʒϽóˆO‚½¸¶vð˜{/÷sw lŸo9 '•Q¡‘¦Þ¸zú¹)@î”ð7 @€tT`ÙÚåOj[™T+ëcaÂü°3ÜÍ‚N¬Ô¾ü ¤ò³¾õƒg†óŒz4C­¾äŠåª­üDO%ùi,‘Ÿ¶}hܵ`åíI˜ÿ{M¥2ÿÑóUq™è"‰U.‹OAþ[SÞ~]%ùXXºjÞö2îˬrZ|åà¡qèÕ÷eÕ^žÚICú«Æõ[^»kL »jxO€ @€mè»jàcáñ‹Õjõ+±ˆfØÊ9îæpN͹2kG~ëˆuËŽÍÙü^Í'ãÜ›ÿÐÛ[ùq´~AOáq® {Þ>º>}þüä‡Û‡'Ûó*– @€@Én™HáÏK–Ö=éÄ1*þ|éUËyÏoºR oÝÀÊæwÈ2&ïxçøªñkî›ä½EüL€ @€-¨­øÓ…‹ý¤9dh,8øÝ´UâI˜_I’ úׯ¸jéeKhU7eo·¶~à™µEÕÇCõ±”kµVïð$9,T’5Í'M›7*´º;í @€@çê«F.‰C8~­ó‘´$‚ÞžjåbÃã·Ä¶.Y³$Î’PùH!‚qéÇ7¼yO›ù%oO*– @€ @€@K– /ÿÝ8Äâçã/àŸ‰CR-kI'Ý“Àªê ®ë^>´§-Û³Àöy‡W¬KBåsñx]±çµ,m@ò¼…‹]W[7hhæÖ!k™yH“É©‡Þ™—€2#Îo^{üŠÒ>噩U ›·xá{â—ƒ¥K- ['·¥Ï §„8’ò}_ ÷5±„ @€²Hâ°CgÍKª?l±˜}óZÜŸ@¼è±4>Á·6€_·¿u»þó5¡Ú¿~àõóz«ÿ ж< šëâ¿_ŒO¡þIÃÐ5´A`ü¸ñŸ…4}cºêLIzÞÒ_ñ€Ît®×N ,[·üÉigvªÿVö›&áõ›Žû¯½õ¡¹7Ë  @€ @ ÇWôÅ¢×úJ¥r~lpA&jdVq~–æë±˜sq¸(ôΪ‘’oT[W{Hÿ¢Áo†Pù›8Üê¼’§[Œô¶{›|¦ÝÀÙÅX” 0[ñkÇ.ŒÅšïÏvûäs‡’qÓ…n.ÎMúò¤Òó½xèq…N¤„Áo¿pW©ü]ÿðàæÐ*á–v œ&“ÉÉÓbqcjç¢2ý‹O¯­[qz™r’ËÞúúV¼-„äA{_£˜ŸÄùZoŸš ÏßÉûÊ@r_:>#@€ @€Ù œ*}ëÞZ©V®Š…ƒûÍ®[µV yê _c(° /?,>¥ûoqnÒÄ‹D [ë®õ9 $Ékúüç°6ÌŸS;6&@€Ü Ä¡X¿Ÿ†ä¹ pŽ%Iú·G\~ÄÒ96cóœ ,[7ðØ$¤¯Éy˜³ ¯‘¼fÓñ£¿Ü߯ ûò9 @€ÌH`ÙÚeKjG ~¾*o(ãpC3ÂÈùÊq0¨‡Æ¡À¾ºôÊÁæ<Ô–…ŸÒ}ÒÁIåºøD‰-ëDà $'×*ƒ_X|åà¡7¬9ȉÀÄÄÈߤiøYNÂÉ6Œ$9¬wþ‚xÓ“WiÖ„yÕJò±øý²ZÂ×ÖW|l:y)@NGÉ: @€ 0-ÚÚÚc*Õù߉¿l?mZX©ãq_ T{ÂWº°™ô œç&ýR|걿ã;B3ˆÇíìI®ª]Q;`FZ™Š!pZ¸«Ñ˜:3õ§„,á+Iþ¸¶vð„f&¥(P[4ð†øýò‘¥ÃHÓ›¶¤áŒéæ¥9])ë @€ @€À>âüyÏNª=ßHBÜçŠ>Ì@·!û/ë¿_ÿúWU’ÊÛbîe¼3=wÇX+ŠOðôö¬‰óõ´¢}m @€@g6®ÞðÕXü`g£h]ïñȇ[{Ø¢Öõ åN,½rà÷’¤ò—è»å}¦á¥7LL·ÈéJY @€ö,ç{Œóç½+Ο÷ióç홨K·!{“ÿWö9!—^µüñéÕïÅ}²ªûEŒûH’ãjG^¼Ÿµ|L€¸sóm¯EÈ  ?a'ý *½g?+ù¸HkBµÚ›|"†Ü[¤°§kš^:¾jô³ÓZwÇJ 3Ѳ. @€ì&°ôÒ¥Æù/‹Å«svûÀ…h>½Ú3?]¿hxùa…L`?A÷¼´§ZýZ³ØºŸU}\ 8×ì š7A(d¡ @€À4~sÊonIÓôÓ\½x«%áŒ8$ü‹¸ˆ÷$P[<øñ{É‘{ú¬ØËÒñÛïÜü𙿠9S1ë @€ @€ÀvÚúÚ@Ï ¾‹9'")“@ò ƒ“êeaM˜Wš¬>ÄÕÇCRùPHJ”WivÐÜiÞÑ·nହ·¤È›@}ÕØ¿Ç™ ãHå{ÅbUQ¼òѰ6Ì/_vݕђ+–?(ÞÌ÷¦²eÝœ‡5NÄzÚÍϺùæ™æ¦9S1ë @€ @€@X¶nà±!íýV,æ< G)žïàþH2[¶nÙýk}+¾/ï½° ùÈaïI%ù»Úðòçì} Ÿ @€@Q¦î¸ë¬X¹±¨ñï+îX|H­2ø–}­ã³Ü $½½•KÊ8E’†÷ÖWŽ~a6{@r6j¶!@€ @€@ ÔÖžPI’¯Ä‹%˺˜¡ô©7‡µŒC‚[äD—­[þäJ2ïÛñX}L‘óûô¶?E’T?çù|äô¶°E`ÓI›®¡1ã! ‹’_¼©ïì#†ûÝØW˜¶{ µõƒ¯Œ7»»ûÒâÿ}üYºmrÖ¿(@ÿ @€Ú&P[7ðòP Ÿ‹¿`жNuÔ1¸Ÿÿ¦6  ÐBúІOÇ‚Èp »èXÓñ&šžžP½$_íX:ž•@s´&o›ÕÆ9Þ(>q<ÕHÏ«ŸP¿c¶a*@ÎVÎv @€è.$Ρ÷®¤Rù@,è¸0Ò%ûþîy‰’K[Û¿¼0)ǹ+kë.ŽO=¾¯y1¯0q 43øoÔÃCoÏ…™5¨!È@#Ýò²8'Ýí¹ (Ã@âùëj‹_›a“šjƒ@mãÃñ{çÁmèªÝ]¼e㪱oÏ¥SȹèÙ– @€Ý °6̯­_ñéxQäœnHWŽ÷H’ÃV{>΋Ͼæüµäª%Ëj‹V|% •ÓsªðZ,ÿ½:#¢ÿ¤ÅÝhžÚ,°qÕÆÿO›½®Íݶ³»7-½rðíìP_³ˆß5^¿s¬œ} ùÜ2>ýøŸõ‘ÑwÌ5ºÜÿò0×mO€ @€³X|åࡵêàç“þtö­Ø²Oê?zà¯òœÇ²u׳ð;ñô£ó§ØÚ(V>²ôßW< =êŠÚ P¿väC±›kÛÐUÛ»ˆÅ¬ª=á#mïX‡3hÞø–¤É{f¼aþ7¸«ÑÏ/ Ûæªä\mO€ @€’ 4ç39°7ùFÆòJš¢´f †ä¼¥kûšÁ&m[uÙðà©ÕJåk!$ýmëTG¹ˆÅèE=óÒ …ÞÜ+@˜¾Ày¡1•¦g„4lþFÅY3!ŸÖ7<øââDܑΫð$‡–-û8Äñ9Wþwy)@f¡¨  @€”LàˆáþGU’ù×Ä'V²Ô¤3Kæ|Š=•ê§ÂÇÂY6‘ýfqXØÚðŠ÷T“䓱ñüÄ•}¦Zœ­@’<®ÿþƒožíæ¶#@€| lýQHoÉgtsª’${øðо¹·¤…Vlæ= 'µ¢íN¶‡^ýR}åèû³ŠA2+Ií @€ @ $}W üQoÒóÕøôв’¤$¬’äwj}çfÕÜ\ÚY²fÉAµ£/ÇéŸÏ¥Ûvƒ@òÚÚºÚCº!S9 @ ›Æo{w,˜ü¨¤92?Iß[ÒÜ Ö¢á凅P)ݾIÓ°9l›|QÜ9iV;H2+Ií @€ @ ñnÞgVz*ëc*‹KŽZ „Ê9ýWõ?¸MO»É¾«úVÌ[¼ð›qˆ²ã§½‘»Y 7$½t3€Ü  PJSÂÖ©ÆÔ±Ù(e~!9¹oxÅ—3·âfuP¨¾/ŽsDq3Øsäñ¿£WÔO¨îùÓÙ-U€œ›­ @€ P:8”eœK'ù—˜˜¡,K·w3L( óÓj5³¡™fYmýŠ'$=ó¾ç{|äL·µ~÷ Ä'e‡jkOè^™ @ œ›V_¿ü}9³‹ÏÙŧ ]s¨s²ƒ—­[±:~§xNNÂÉ,Œøôã¿M¬ýTf îhH2kQí @€ @ €}ÃçÆ_¦?Ÿ(«0|!·Y 'Oß>÷M›ûí|~’†/—ñ®ó6SvewI%ü}Xæweò’&@€@‰¦®¿ë¯cåË™bR[¸ø ¿+gnÅÊê°µ‡-ª&éEÅŠzÿÑÆñV¯o4¶œ¹ÿ5g¾†äÌÍlA€ @€2 $µõƒV’ÊÛÊ””\Z/„äüÚµZßÓö’þõƒo¯$É¥!>Ù¦>uS68‡i_eÀœ¡eÛ¯ò!@ ë6ºéö8mÝ‹Ë ‘¼hÙpÿSÊ›_12[P‰…à$Y^ŒhgåTzÆÆÕo˜ÁÓ^UrÚTV$@€ @€@É. ½qØÕŒ…¤W—,3é´E ©…žÞW´º«¥—.=0»ú¯qxµ¿ju_Ú/¿@|z÷Üû]±¼¿ü™ÊÝ%Pýb+cÖñ»zRMª…5Ë–1¿"ä´½œ„Ó‹ëLbŒó>~´¾zôŠ™l3“u g¢e] @€”D ùäZÿýW\‡]}vIR’F'’ôœ%k–Ôª®¿| V=bþ×â«ÏjUÚí.x÷Àù½Õ¿í®¬eK€î¸-MωC±n,g¶Éƒj‹+o.gnùΪù{S5T?Ú,ç;Ò™E—†ôW[·ÜÑÒ‘! g¶O¬M€ @€Â ºæÐÅ¡·g}LdUá“‘@GâÓd‡Ï[¼à5­¢ÿªþGÏ_|+öñ­h_›Ý,þi<¾ÜÍr'@€@6m¸) iK¾—äÄë¬æ÷£œÄÒ5a$½=oµÇß)SÂñÉÇF:™¾ðÆo¼µ•y)@¶RWÛ @€È™ÀÒË–±pñ¢¯Ä¢Î±9 M8…¨¼v{Q;ÃøkkOH{ªWÇaW •™¡«¦îˆÿþUÒjõµ< @ |ãC£kBšÆ¡ÛË÷ŠOàõÄó×ÇšP-_vùÌhéUËŸ†ðª|F7§¨Þ3qܨ׿ÔÂ46V€œ’U @€ P8|Ð`õ€_c¹sº ;4?9²pÑA™ ßÔ7<øšP ÿÖ*3?)Фlñø:uÉUK–•-/ù @€@[¶¤ÍùÍo)£EsdˆÚâÁ¿(cn¹ËiM˜W­V.iÞ¸”»ØæPú_õÆè›æÔÄ47.Ü4³¶ @€ºL`ÉËæõ~=Î\bØÁ.Û÷íH7s^ÙœgN}Å»ùkÃï¯$Éå»Ð3'·B óçõ,l^ ö"@€’ ÜxâX= ³K–Ö=éÄ› ß´dxùïÞ³À›–ô/Zñ×ñ;éÃ[ÒxçÝ–NMžV‡-íA²Êú @€ @€@ŽX·ü÷{çU›O>v0 ]—Y IM{{ž;Û—¬YrPÿ¢ÁËã æ¯˜m¶#0säe‡_~øÁ3ßÎ wúʱ‡4|%ïqÎ.¾dá¼PýHÜ6~½÷j…ÀÃýŠº¥{Ò4ÎýøÆúêú÷Za¶§6 ÷¤b @€J"М·¤7©|%^XZ’”¤‘SJ2»ùq¿| Ö»ø€¯‡$9.§© «¼‡ÌŸ·ðÅåMOfèjtë¶©3£À]¥THÂSúÖ:‡µbçžzzBõ’Øto+šï`›×Ö7¾§ý+@¶S[_ @€h£À²uËŸ\í©|1vmc·ºêZä‘Ícn&é7ŸÎ??ùX 7/éLଛ@’œ.*ÝÆì|´D€ Üp†Ÿ‡FãõNaŸ¡WBò®Ã‡Wôís%ÎX ï¨sšsmÎxÃoŸ|¼cëÖ©SÃ)aªaö´³3} @€ @€@{–­]¾ªZ©ük™ia{zÌW/ñ—ìFœïr"æ?Bº!M“Í!4îˆÅØ;C#Ä¿Ã1â4þß¡ÿ¤itª—߯ˆ¯ˆóÞ/_Yå?šjR}UŒò+Ó‰´oÝÀÊ8äêg£µ!0w‚¥ÍùxÒ Íã6 ¡ßß¾ýXÇl¼vGÛ´‘ö†JÙʱp»0$Í÷ÉÒæ1Û-Ùõ¯$Mžî[€\æÕlÞUÞ,P–úŸp¼->Ǹ6>}øù´±õ‹ÇMŒt2á›V߇ ¾i]Œa]X^ºô Oꩦ§ÄãöyñbìŒ-}7 ç= œcy_âd/0Q{[­oÅÿß×’}ëm1æ´ì€…‹Ï¿9Üü¢ÎFRÜÞ—þûŠÄ‘#ÞVÜ öùËn‰ÓRtæ¥Ùw½ @€ @ S8¤å+â–ï‹Å„Â?V‹7ñÿ“kãÿü󶬹qeç~iÞßNš8~âÇq·Æ¢è;j89T’?û Œs íâ·Ÿ'á÷— >bãÐèv.ŒwåràÂÅ—ÅâãSv.+áßq8àfÑ1ý§ú­+Ã)šóŒæï‡¢ÛF¾ûò¢áåç’—Æ¹Žšwü/Ë_°í‹(þwÛ,Œ+@¶\Oh¯Àiá®ôªÆ™¡'ùJ¾/ß/žÇO[6Üÿ‡Æÿß½?óóþªóâMœ%»)+ÎþõU£köŸ}ëÖ(Í]±­#Ò2 @€ò-пnàìxÑáýÅ¿˜’þ2ÿ*Üö€úÐÈ&†F/ìä»3ÚëñÉÈúª±ª¯}ìTcê)1æ’]ûŠÇbó)Èí¯æœ,\tu‰‹_m4Òço¹ëö#ƇFþx|Õègs[|ܹSvü½yhÃMCco¯oYÑéKbé¿9e·¾¿ôÊÁvkòò&@€@7L7öµx£ÐËšk5©^Ö,_XÖüZ•Wsnò8ÂÿiUûi7M7Üq׿Wv¤ï]:U€ÜÃ[ @€M oÝà_ʼn_þ®hqïŒ7êâ(aÝTŽ¿fôA±€÷ÎNW¹3¶Ùþ½qÕ†¯Ô¯}üö‚NH=Ûvм]œ+v{òˆáþG%¡çÚxQçEÎçÞ±Çãö¶xä~hr[ã‘ã+Gž<±jôS7žxã­÷^¯0?Ÿ¶N¬ýÈwÞòà4m| yÜ•s3÷T“çfŸ ”f%pWãösãynlVç~£äµEÕ·æ>Ìx¿+–÷Ç9ߣ2 %N]ñ¢<Ì ª™ÉîÔ @€Ú/°`^åM•Jòöö÷<÷cñæÖFΟœJ 8«7\ÕѹçžÒî-Äy*›äö©‡ÄbÕ'wÿ°~JØ,Ž÷„ž¯…ÔJ“qšþOÜŸ¯¾kê¶þø”îË7?ö_¥É-&Ò¼PU{e25õ‡ñ΀ï—)·éä’V‚ät ¬C€ 4çEn¤ÉK œÂ~BOÿ¬oýà‘ûYÉÇ;æÏ«|(¾=¤L ñf²÷Ç›:¿‡œ ó°Ä@€ @€Y $gÎj³n ñ¢OxÛmaêþC#~ýê±_t0œ–w=~Òø¯ã€¦¡qzìì®–w˜£šÅñxGù¢…4‡PÒŸO¥é Æ7>4îÏ÷5/^Ρ±Üo:~Üø÷ëõ‘£ã¯—ä>Ø Œè>¬ÿªþGgؤ¦ @ ‡W¬mΗÃÐæRu¢‡Â¿8ÞØ×3çÆJÞ@mxùs¢Õ åJ3ýyØ6õº¼ä¤™—=! @€”[à–x7î[oŸL›…Ç74çž+wº»gW_9vI:µí ñé¹_íþ‰Ÿò, p?mÎï8~ËèÃ6^N Ý34éiá®ø”çéi#œ÷Q×ÏÕÊê<“b#@€léÖ³â÷²R•o¨ytßQçd#UÎVjWÔ¡zA™²k¡?95ujý„úyÉK2/{B @€(£@¶Ä;Ìßyû·Ü?íø¦[ŽýMÓœNNõÕõïÝ–6âЖéw§³¾u:(¦izjýÚ‘‡7çwìªÂã½Øë«F>‡c}Zóéå{}TÊã“O-eb’"@€Ý6®ÞxCH¯Þma‰~¨$•7Ƨú\¢”²M¥·çýq¤Ž%Ù6ÚéÖÒ·oZ=~m§£ØµÈ]5¼'@€ @€ìÒô_'·†‡ÕWþUsn¹ì.nKÍ'?ï¸cóÿ iúíâfQæÈÓ;ãÝãoI·M>dbhôJ5/év[}åÈ7aê鱉[æÐL!6#ŸÖ†ù…V 0'úІOÇl†çÔH~7^zz>ËDzí*P[;xBªöOv]Vô÷ñ8þ^}dì­yËC2o{D< @€(¸@|ZêûSSSO=yÓ3F 9z¯ýÙ,ÆÞ±ùÖf1ç?îõ‘;(/Ü|& “©þuž†®ê Én]oÚð­Æd£Yñø£X4?&ÎwøœúÊúؾÖíöÏ&ŽûN¼Àub¼P;Yf‹J¥bÖ2ï`¹ @`‰ã&FB#}Ý.‹Jõ6 •w~ù@­TIÖ‘åì@IDATÍ!™…‹ íŸC¹Û4~7ûË8WùrX H2{EL @€(˜@,â\¾å®Fs¾¼‹ [9½×|r[Ú8)ÚÝ1½-¬•©@¶ÆBÚyõÍ£Ðb4Ó¶KÜØÄÐØ×ãÓ¢Q⛩)@–|K» ÔÿcìÃñÜvÍ®ËÊò>-¾hÞüÊÊ’Ï\òè_7ø´èñ¢¹´‘·mãwÙ¯M\;ú޼ŵ3Èþ&@€ @€ Ä'Ç6Å»ÆO‰ÃV>óÆÇê3n Ë7¸~Õ†ëâ/Ccµÿ8¸v*¤±ð8úæpJØÚþî‹Ýcœó‚X8ÿçbg±÷èã¼P_zéÒ÷¾†O @ Tç…FÅãŒoN*U^;’‰E·göžRÆÜ¦›Só¼žVÂG§»~Ö‹¿CÜÚhl}AžoþT€,‘$F @€äQ M/½-zøøªÑÏæ1¼¢ÄŸýT,æ”ê‚H~íÓ;iúgã׌“סªòk·{dS×o9=„ôç»/-ÍO½•%óŽ-M6!@€ý l\=úßÐxó~W,è iH.\4¼ü°‚†?ç°«Kæ¿# ÉæÜPŽHÒôUWmüß…tŸP ïCb @€ìGàææSãC£/Ø<´á¦ý¬ëãiÜ>^‡Púõ4VµÊ,âÓºßolÛvd|zïÂ<ß)>ËôÚ¾Ù¦S7Ý>5ÕxMÛ;nS‡ñ)ȧ´©+Ý @€@N&FÆÞo®ùaNÂÉ4Œø䲃Båï2m´ ÕÖ¯xBHÂ+ î´ÂlNoýä´VîàJ Ä×5 @€Š&Мg$ݺíQžzÌvÏÝrüèoâ]ÌoȶU­5â1káïëS#GM?ñc*Ù l\½a]ä½*»sÔR“£h„B€íxIØ6•6ΈÅF;ºkwñæš6çAlw¿íom˜¿ ~,æ^šZXüf{ÃÔ[Îì¨ë4;/ ú4óµ @€ÌB q&C#¼±~ÍèSê'ÔGgÑ„Mö#0~íØGšOéíg5Ï@ ^ Ù˜6ÒUã+GΫÖljÕi lÝÖ8«Œsf%!üÞ4 ¬F€%Ø8´á[ñæšóK”Òî©TÂGjWÔØ}ayª%ƒçŧ?Zª “Æ™›NÚt}rR€,Â^# @€:*Ž'©?_5ò7†®láŽ8/4éäÙ-ì¡«šŽO/|±ÑØòû«ÆÖwUâmNö†6ü<>&òþ6wÛ†î’Z7Ï•Õ`] @ ·õ[Ó7Å"ä/ràœK˜öö¾uNMdãÚðŠ?•ðÚ‚„;­0ãÍu—ÔWŽýÛ´VÎÁJ 9Ø B @€ @€@^šC®n»ë®#ÇW_“×Ëׯ¡ñÿ gÿY¦œ:‘K~, IÏô7Ê÷šñ˜½«qëYùŽr÷è w÷ð @€ìHÓ ã«O»þÄë7í\äïÖ Ä‹ ´¾—rö‹`·…FzJ}åè9á”0UÎ,ó—U}e},ÃzYþ"›[Dq¶(ȹÚš…¨~1ÞvqaØGàq>Äj¥Z¹8ŽlRšâܽÓíüŠ¿ŒÃ©?úÞË‹úsüŽg˜|áM«oÚ\¤ ‹´·ÄJ€ @€¶¤wÆ!Ÿ7>4úgñÂÄd[ºÔÉ=›Gÿ¹9wá= ¼™¦@úóFŽ_5úÙin`µ,’äÂ,›ËE[IE2;BèŒÀí“áœxcX9oÄK£úx]gd[Ûkß•}‹O?¾¡µ½´¹õ49¿9RJ›{sw s&Ô @€J$¦iš;±räK”U±R9%l !ýP±‚îl´ñ …/ÝqË­Ý84ú£ÎFÒ½½×WŽ|3Þœÿír ¤ åÚ¡²!@€ÀŒn9~ô7ñ¦°WÌh£"­œVÞX[W{H‘BÞo¬ç…J¥wÞ%±9¿ëd…ø=÷¿ë‘×$ÜÝÂT€Üà @€èfô¿Òdòñõ¡svú0Ø6ù‰N‡P”þãTŸ¬Œ®úÍ)¿¹¥(1—5ÎxìeÊ-ݦY¦*ÌB`bhä_ã 6ÿ:‹Mó¿I³H—ô|4Oyåxõ?~àÏb&G•#›íYl‹»çùauØRÄœ ‹¸×ÄL€ @€ŒvÈ'ÖÖ ¼l?«âã¥W>0M’·"ØiÙxs‘o-í$£ÓÞV$Pr«¯¾º4w°”|WI Ð94½4>Av†"NçvÁžzNBú¹xÇó‘{ú̲Xpl4ά¯û‹üÜxâX½6¼âÚøÅÑù‰jn‘ôö„ÅÆçÖŠ­  @ È7LÔ†ÎN’Ê%EÎc¯±'•wÄn.ÿõ Š|¾Kzz’‹ãwöšgÑ>HÓoo{gÑÂÞ5^O@îªá= @€ºM o}âcþv|cÛäeù‹ªóÅ'ìnŽW|ìüÎØSiˆ…óò¼*!é+O62!@€Ù Ô‡Æ>Þ1d¶Ûçy»$ ‹æÏ«zþñ¾uƒgÆâãSòì<“Øâ±vÇÖmç…SÂÔL¶ËÛº yÛ#â!@€ @€@›ôÜñ¡‘×·©;ÝÌP`âø‰ÇÁ,2ÃÍJ½zôØ/ìŒÃ®~¡Ô‰8¹É´QªÂyRH²ÀǣР ¥Àd#}IéY¶™—¶’œ‡býÓ¼Ä3“8šÃ¥Ç¡dÿv&Ûä}Ý$I_{à ~ž÷8÷Ÿäþ„|N€ @€’ 4çh‹sç½fbÕè;J–ZùÒIÒÏ—/©Yf”¦7¥S§ÕW~c–-ج ׯûE¼8ûË6tÕ–.â„– m‘Ö ò/°ý×HßÿHga¥rá¢áå‡ÍrëŽm¶ ·rQó)ÎŽqÇñ7µ/Œ¯ûpÆÍv¤9ÈŽ°ë” @€h–CšÄ¹óFßÛ™ô:3ô›3[¿œkÇ"ЦɩƓ'ŽûN93,]V%:n ÁZº£SB˜ƒÀø­cÆ›ù¾;‡&r»i|êÿˆƒCåïsà‹C¯>/$Éq{ø¨¨‹nÞ²­qZ >~ý-þK²øûP @€˜–@,>Æ9Dϯ\<­ ¬Ôy­S×t>ˆNGŽ‡Æ¶'m:nÃ;‰þ§'Ÿ°.Íq›T<9½½n-t‰@œ“o2:=f»­”'É©µõƒO/BnK/[zDÎH-+×o«_oz¸=ÁÌ1ˆ89 çhhs”Q Þ{[š†RÞ ‹zHz{Þ–çýV[?ðÌ’ÿ›çg[šN„Û§^6£m °²dv’  @€ 0'´ñÚñ¡±Ï© wL M’k;Öy‡:ŽônJÓ¡ëWm¸®C!èv.qxºxñ²,óuÖ,_8Û @€@ V‡-¡‘žÞe¤„Ù5' |õ²áåËcn‹¯<4I“æ1¶ÙÆÔŸ4þëÙnŸ×í óºgÄE€ @€ âE‘óbññï2hJh¤áÇêº#ÝÆ¹JïHC㸫ƾݑtš‰@#MKsÜ.]P]– ŠF @ Tõգ߈õÇ”*©É$IR©&•‹ÃE¡7oùØÎIy†H%ìM mΛsñ(@f¡¨  @€äP ®Î¯¯}sCÒ ¦¶Mýl«}Õm±yÒÄÐØ×‹žH×ÇŸ¦¥9n+ÕF‰æ—êú#2غåÎsãÓ‚£™6𛯒Gö¯Xñ—¹ 'Ò·n`e,޾0O1Í)–4ýŸ©ëïú‹9µ‘ã s¼s„F€ @€Ù ¤Ÿyíì··e^nøî†_Å'Y'óOKãh4ΜX5¶¾¥}h¼-•Jòó¶tÔ†N’J²  Ýè‚ (pã‰7Þoú{yCŸnȯ¯­¯=tº+·r½%k–̼¨•}´³íxÓ]#Nµð‚M§n*żÙ{²S€Ü“Še @€(°@,V}c¼>zjL¡”sÒx×Ì.ôóÂd’†ÿÝÆÅÙ*^‚yóøª±O'b‘îS`²DOî&ArŸ;Û‡ènC#Wůݟ*¥Bæ‡ÐsqÌ-ét~½‹xW’„Ž#«þã|ÙאָùfVíå±È<î1 @€ @`–q‘Ÿ%·ON wͲ ›åQ IJ3œåžxcÑü“õU£çíé3ËŠ)0¾aüWñÎþ©bF¿{ÔÄ‹¯^ @`ïéÖɳâyïÆ½¯QÜOb¡ì˜ÚðŠŽ>åY[7xl,ò¾¬¸Š÷Š< ?ßÏÃIò±XíøS˜™äŸ†-“SSϧ„­™´—ãF s¼s„F€ @€i Ä_d§“ϸ~õØ/¦½‹$0Q¤`§k#øÇw5n;)ÄbÕt·±^qâþ-ÇqkÖât"%@€@ê«Æ>‹q8Öò½béïà$íùP'2«Õßûp'únEŸiÞ°é¸ ?lEÛykS2o{D< @€˜…@š4^¾iõøµ³ØÔ&Eh$7!ÌÆxK29ùÌ›Vß´y†ÛY½0I)†¢Kž€,Ì!'PtX Ý6ùò8rÅm£5Ý'ÉqµuÏnMã{nµïª?Œ³ÚŸ½çO‹·4Žüquýš‘ó‹ùì"V€œ›­ @€ ø”Ñë+Ç.ÉM@i@£TÈxñ¥1•†çŽ7^ê¹-[p ¬É´Ç­' vÜ —ˆC±Ž&!=§s´¸ç¤raÿeý÷kq/w7Qè­ô$—$IRmK-î¤Y˜žÚšœÎ w•›æ{r‰@h‰À±Ç¯Gy 7«¯¾ºãÖç V<èBíwÑŽŒþY¦Þ])W*¥x’lçNKÒä¯7®)åe;sôwq®¦R “Ð0¤š¦-0¾rìõõƒÏçÁc¦½QAVŒC¡. V/ˆá>¿Õ!×î?xnü6ñÈV÷Ó¾öÓ×lzÆè¯Ú×_ç{òdç÷ @€ 0Kt|rË]'›?o–|ÚlÛÖ©RršäñÉϯy[ø…:KFÚ(Gáܳ<lF€®ˆ_w&ψ_z¶”S y^ߺ•­Ìmé•¿oX‹Èr¼âM£Wvãˆ5 å8~eA€ @€@ LM5ž{ý‰×oêÂÔ».å­IIž€LÓ‰­wÝþ‚¸ã…9¯ò ”ã È4 óË¿¯dH€Y ÔWÖÒ·dÙfžÚJ’ÊEK/]z`KbZª=qèÕ8”¼–´ßæFcññÆxÓèmî6Ý)@æb7‚ @€3Ø6þgæ[Ù¢ˆ·?zssÞÄ"ƾkÌi’lºñÄoÝu™÷åHCrS²KãzeÈC Ð^‰‘±wÇ[®~ÐÞ^ÛÓ[ŠuEuéü··¢·Úâg…$yl+ÚîD›ñ+üKºõ¦QÈNqú$@€ @€3ˆµ»dëÌ6±6Î TCZÒ¡ç:ëªw(ˆÀK¶ÆTãŒxÙTA"žY˜ixåÒ«–?~fí{í%ÃË7 iyžMÓK'Vm¸lßY—÷SÈòî[™ @€ @€@‰â0 %ÚŸÝJ#I³Ý°£åH€{˜8nì;ñ9úó÷ºB?ˆ7ÇUªÕÊÇÂE¡7£4’yIõâ’…µ×ÑfbáyìŽÍ·¾º£At¸sÈïÝ @€ @€é (æLÏÉZyH=™—]!è¤ÀÖÉóâô׿ìd­ê;!Q»ÿà¹Y´ß¿~ॱ'eÑV§ÛˆßÒ$ §ýæ”ßÜÒéX:Ù¿d'õõM€ @€¦)çÚ1œå4­¬–ꔢy>ö„( @ “õêwLN%/îd ­ì;I“s—­|ø\ú¨­¯ ¤iå]si#OÛÆâã…ã«F¿”§˜:‹d'ÔõI€ @€f(C°ÎÌê˜Jª†`íð>Ð=äC`Óê‘/Ç!9?šh2Ž" ó*•äâp^˜}½)íýH¼ÙîàŒ#ëHsqÚ„ŸŒondòThGȰÓÙ¡) @€ @`?i¢˜³"çK aÖ|íÑ @€@Gî¸só9qdΉŽÑ¢ÎcñðèÚу¯˜MóˆOÛÍfÛ¼m‡^L§Ï§l¸3o±u"ÈN¨ë“ @€ÌP I g9C2«wX gjj[‡CÐ=ÈÀÍϺùæFÚ˜U‘.7Iì;·×®¨ î{•Ý?=âò#–VCøûÝ—ø§4¼u⸱ï8ƒLCW€Ì”Sc @€ @ EiœMÆ‹@‘æÇl‘ö—X  @ å«6|.„ô_ZÞQ:HBrP2¯÷Ã3éºwþ‚„$9l&ÛävÝ4ývýÚÑ·ç6¾¦Ùt] @€ @€ @€Ý'°uòÎWŬo.iæ«úÖ¯xîtr[¶vðäX|üã鬛ÿuÒ;ÓdòÔ8ædþcm_„ í³Ö @€ @€ ÐÅ7wÃÆÐhœUV‚$M/ˆC±¾¯ü /?¬RMÞ¿¯uŠôYòáuõ•õŸ)ævĪÙe} @€ @€ @€¢Àøª±O¤iú¥2b$Irxèé¹`_¹*$!,Ý×:Eù¬¹ë+GKSLÍÒ]2KMm @€ @€ @€ö#05Όū;ö³Z!?N*És—­]¾jOÁo_ž$ÏßÓg\vKH&O‹q›÷z;Or( @€ @€ @€Z%°éøÑ_ÆäZÕ~§Û­T«^²fÉA»Æqøå‡\©T.ÚuY‘ß7BxEzu¬È9´2vÈVêj› @€ @€ °‰[ÇÞÒôÛ{ø¨ð‹â«ƒ½/|û®‰ôÎ?àoã­».+ìû4ý׉•#ÿXØøÛ¸duA€ @€ @€ØMà”059Õ8=.Û¶Ûò²ü„Wô¯ë?º™NzõII^R†ÔÒ4lL·M¾´ ¹´2ÈVêj› @€ @€ °MÇmøaŠõ{ù¸Ð‹ãÓŽ•4©^|Èç9¤Z©\œ„X‚,Á+IÒ×O¨ßX‚TZš‚dKy5N€ @€ @€Ø»@½1úö4„ï}â~‹?ð€EßµÇß-n¿<‹?:¾rôÊß.ñno {“±œ @€ @€ ÐjÕaKìâŒ4NÙê®:Ó~òÀÎô›m¯qÿüjÛæ;ÏζÕò¶¦YÞ}+3 @€ @€ @ õ•#ß ðþ„Ú•!Æ'qÿœzÃ)7ÜÖ•³HZrh6!@€ @€ @€d)°íÖ;Ï@ŽfÙ¦¶2xw}ÕèÕ™µÖ )@vÁN–" @€ @€ oæÓu©©—æ;ÊnŒ.ýa}ó蛺1ó¹ä¬9=Û @€ @€ @€2ظzú8ä?dÔœfæ*†­ÛÒ©ç‡SÂÖ¹6ÕmÛ+@vÛ—/ @€ @€ [tÛäÙinÈm€]XÒ7]?4þƒ.J9³T 3£Ô @€ @€ @`nõê7†´ñê¹µbë¹ ¤iúÍúµ£ïžk;ݺ½d·îyy @€ @€ @€¹¨¯û§8ëU¹ ® ‚ŠO>Þ>ÙHO ç…F¤Û’ [ªQ @€ @€ @€ÀìîlL½4Åzëì[°ålÒξ~õØ/f»½íBP€t @€ @€ @€r&pÓêñ Ihœ“³°º!œµ+G?Ò ‰¶2GÈVêj› @€ @€ 0Kñ¡±‹â\„WÏrs›ÍP ½úë-i8c†›Y} {@±ˆ @€ @€ X›<#¤aKb)}I#¼ìÆ¡‘‰Ò'Ú† Û€¬  @€ @€ @€Àlê«ê?m¤é›g³­m¦/6Ò_5úÙéoaÍ} (@îKÇg @€ @€ÀÿßÞÀÇY•‹?çIºÒ²´M2“LðzÅ AvÔ{m’RPôÆ+‚" « Ѝõ²ÉUQÁ²q½• BÛ,ê·¿,.¨ˆŠz¯d’™$Mé’îIæ=ÿgRJÛd&3óÎ{ÎûËGIfæ}Ïyžï™¶“÷yÏ9 € €TY ÿÑôdä惘»ÝÓ·eÛð‡ÝM0øÌ(@oN € € € € € €@áKÔ˜¯Í™²d®ð“8²YãÖ­Þ¿þ-ë×r<Ç&@²0'ŽB@@@@@ª&п0ýkéüKU ÀÕŽùjvaú‡®¦W­¼(@VKž~@@@@@@"²Ãþ™°÷·"NáÐ ŒQQ£¹K'8„—¦(@rŠpœ† € € € € €*ÐÞ·5§rÌ/h¿v–_ÎÖW¹÷fg·8˜^ÕS¢Yõ! @@@@@@ 0–ÌCÊ¨Û ;š£&¸j ¥ï± ^ç¥(@–€Ç© € € € € € €@Ð[‡7~L)“ º_Wú“¤¿Îö¤¯t%Ÿ0æA2Œ£BL € € € € € €À~Öµ¯Û E´óöó2OO,°ÍŒŽ¾W­F'>ŒWK YŠç"€ € € € € €UÈ.ìý¾Ì‚ü^º¶ºKߘOöŸÜÿ'«“° x  !"€ € € € € €{ ŒnÛv¾2fÝÞÏóx?F=Ôß’¾~?¯òt(@–“¦@@@@@@ VŸºzP —ÕŸÍý£†eéÕ÷IÆæékO {zð@@@@@pB ¾«ñ¥ÌEN$SdZé/%ïMRäi^& e‚¤@@@@@@ 4²ôª§¼üÒ«±ÐÄ` 2ës¾™¿)À.éj7 »að# € € € € € à‚@B7])ÅÇÃ]Èeª9h¥ÞÑÐÕØ2Õó9oê §nÇ™ € € € € € €@èê;›ŽVZ_ºÀªV±¯Í_>vºŽt— #=ü$ € € € € €N ,WµžÖ‘]zuï±”¥X›kæÌüϽŸçqe(@VÖ—Ö@@@@@@ÀsRWÈÒ«/ ¬C+:2g';“Ç[ª#AR€td I@@@@@¢-ÐÐ:JiuI´þ1{)ÈzFÇnW2;ô_å™JP€¬„*m"€ € € € € €A ä—^Uê.­t<ÈnméKЇ'ç4Ö–xm“¤í#Hü € € € € € yÄ©Ï)¥_yˆ‰´úXÝÊ&Œ&2*Ók ËI3 € € € € € €@5]ÍG*O}´}[ÖgM¼Æ»]--¾**pEyi@@@@@¨ ÀRU£µaéÕ‰MÛü‘ÂçÈ©P€œŠç € € € € € €!H67/‘¥W_‚Pì Á3Ÿ­ï¬?Ôž€í‹”¤}cFÄ € € € € € €€Jt$^i´ù8Å h¥gyºöÖâÎâèb(@£Å± € € € € € €@òK¯z5ˤ˜C8¶Å µþWÙ;ó,Ûâ¶%^ ¶Œq"€ € € € € €Ï 4Úô¥Õ+™º€VæÚ÷/¨›z œ¹? û“áy@@@@@@ „ º’¯ð”wiC³+$­®™>ýF»‚¶#Z vŒQ"€ € € € € €JÉÒ«q¿K(jà(‡€þ·†®æ·–£%ÚØ%@r—?!€ € € € € €¡H47}Z+uD¨ƒ´,8O›Z~Ð\ËÂu¸ C=<‡ € € € € €ìXÐÙør­½OàQn˜1gÎuån5ÊíQ€Œòè“; € € € € €Ø!°DÅã:v—ËÒ«•1mΨïh|]%šŽb› £8êäŒ € € € € €V $ŽM]®µz¥UA[¬VZÇbÞmj™šnQØ¡ •dh‡†À@@@@@@¥êV5¾LkýI,*- _hh¾²Ò½D¡} QerD@@@@@;déÕXÜ[¦´ªµ3Ë¢ÖæÂDG‚™¦%È9@@@@@¨”@â¸Ôe²<èQ•jŸv÷ë¸òjîPRøÝó#@²-ŽE@@@@@¨[ÙôR)ˆ}* îèæYü^›Éã›.dê §nÇ™ € € € € € €@e–«X¬F³ôjet hÕ»|þŠÆp ‡ìC€ä>Px @@@@@¨¦@ò€æOÊìÇWU3†ˆ÷=½¶6v›èˆ;L)} Sbã$@@@@@@ 2õ©Ã¥ìuyeZ§Õ"^—èj>·ˆã9ôY ¼@@@@@@°ä—^õÔ]R€œ–"ÇÕ‡¬hLFÜ èô)@MÆ  € € € € € €@eæ4]ª´>º2­Ój±Z«9Ój¼›‹=/êÇS€Œú;€ü@@@@@@  +^ì)ï3¡† žÐZŸ’èj|×sOðä '%â@@@@@@ Â²ôªWS»Ìæ¥W1¹ +U¯yûòœ®Æƒ«€]=S€´k¼ˆ@@@@@HÌM}LÒ:ÖÚÔŒ‘ä"£Ìfks˜ p­Ô‚ÙÆ»a‚Cxi7 »að# € € € € € ´@¢;ñ"­ôgƒî·œýIññšþÖÞnå«ËÊÙn˜ÚÒž~w¢£éMaŠ)¬±P€ ëÈ € € € € €¸/°DyZÕ,“D§Ûš¬1ª'»Ñ¿6öÑôMòí[s™4nÏ[ZwwݬI‹ø #þ }@@@@@¨ž@â¸ñ¥W«^eèÙø¨ö¾­ã--Q~Θ³”,ÉZ†–CׄÖêÐØ‚Úχ.°D2dB8 € € € € €  Dgâ0­õ›³•Ùß϶ö>°{-é'e/È«vέŸõ¹u«íݯ3€Á 2] € € € € €@0&f¸æ 5½ €@©²ôªÒv/½ª”Ùê›íï‹";œ–Y‚æûzÍöç¤hìÅbÞj¹ªµ=—JÅÏ?Æ•’¥]@@@@\@®æO ¼S:D¦ 8¶ù#²œçñS854§ø¾ºb uàé}Ô®FÆÆü³Œ1þ>_·üI)Bž˜Ót¹åiT,| £¥a@@@@Z@çôô û¤?@ Xäªä µg>Wìyá:Þüµß¤¯›(¦ÁE}J¡îú‰Ž±ù5­¼Kë;R‡ÛœC¥b§Y)YÚE@@@@Àd6ÈÀÕ銥WMúF@J0Æ%XsZqq¨„÷§"€ €å0¾3 ó‰üT¹\h'š Ý©Jéà3ÑÌž¬Ë  ãµêNÛ—^•Ù‰k7ÿ²2xì¿ ™Y9æûùBÿèþ²÷©@¾0Ñúœ½L=r S·ãL@@ ªÌ€¬*?#€ €Î Äõ˜3ûp]óR爄Ht6¢º9°éÈ9DWóyJ«“lOÌhó±á–¾µ•Îcukßï}å_[é~ªÖ¾V—$W%¨ZÿUê˜d•àé@(UÀÄܘ)wTÎ+Õ‚ó@@JÕjKé­„£­ Èp …uQ$;“ÇkOWf?Ƭ ž€C!PßY¨ÒæšPSZfö.+­‰ÂÏîÏõ^)7Zÿ¹ð3ì9RfÂÆM,v‡Z®"õ÷ H{Þ£DŠ €ì!wd VÙO"±Gb<@@¨Š@MÎ%Xå3&Ȫ¼‹ìî4Ñ™8Ìx±Ji¶‰°{(«½ŽyµwHÁiv5ƒ(µoÙòÅ—bà¹ÒŽ)µ­‚ÏoSÛ•oÎ’ý ƒë³ààJ?Pnj8217õ±Ò[²§ öŒ‘"€ €{ Œ9rH{ ÷Y!€ €U0qãάFS€¬Ê»ÈÞNV54+¯æR8:ÄÞ,ˆ¼Ú²|ï9RÀ~Cµã(µ¹‰ãÆlKÏoJm§Øó³méÿ'õǯ{ž-Ç‹ëgæw5þ³-ñ–'ÈR9@¨’@n4·­J]—µ[mL²¬ Ò € €ÀÔF¹Ám<{óÕ¡¦M ‚³¢&0¯«¹Á‹ÕüHŠ©¨åN¾å/bkïóåk±:-ÉôÃÕ›·/©NïJoû¤Ä®Vÿ•íWϨU±Û¤ùëÆý/ î1"€ €€£þ˜ïÊ=Ì€tô=JZ € `—@vMv“]ï?Úüþ} ¼ä‹ö¯ °C ±"1¯V™)­#3+‰±¯Œ€ŽÕÜ¡µ: 2­تÉ]¼þ-ëרã] µmòs¹íñ¤K´:)ÙÕt¶K)í/ û“áy@@ äùå²/ƒõEH£U½Z¢ø\ò÷á!€ €@ÎPÛdï­g\É4fbǹ’ yTFàÀûöÌs;dÆÚ+íÍ‚Èà _zÕ(ï?ÃK)1È (c¾¯Â3ópsî"cÔP)9…øÜ¹µÓôÍ!ޝ,¡Q€, # € €ÕÙƒÖ órµ5êeÕ¤W@@=ŒIïñØâráúõ~$öÙ²x˜ªúòƵsf> ÅÇ㫽º$ K¯ÞîÆÒ«êº¶ôÃ26™Ó2Ï(ã_–xʇ̘=5ÑÝôör·¦ö(@†i4ˆ@(Z@;Q€ÔÊ{EÑ©s € €@Œ33 åâî¼]É—W‰&-=g&æx+¤4/Pó…@I Ý©Êß5ÿRR#¡8ÙdF‡·^ŠPv "ÛÚû]YvånO9ö£wýÜ•©ƒKê¹t(@>GÁ € €€}rW·H¹1¤}o?"F@'´33 óÃ3Þœ&’š’@~ÙUUïta¯¾)pRY݉&môÊÚh•ó}ÿ‚¡ö¡MUê~Ân·ù¹säÚÇð„Yú¢Lѯ›W_±4üIæ9) € €áð”ïHÒP€ ïÛŒÈ@@ JÆùaó´fÈ(½'ÈõàŽƒçÔÌ™Ñ-{пv‚Ãx ÂLü6YzuNá'„óH™aøÃþÖ¾{ÃRkÛ2}Zù—†5¾’ãÒúôDWÊY´ÿ(AòMx@°FÀê0µLM·ž@@@WŒ[3 eÏô×å—Ütu¸È«0ï;ðÀÞìÉÌÇ ;ƒ£˜X ¡+õy?-œø( ^5jdtÔ?/ì‘fZz—e~ö8§ŸV·ºøoÈ)¿#8@¨¾€¯Lõ£(=ùÅ-V· ‘Y¥SÒ € Pš@nÌ™= ó2Ûm¶ªñÞ\ gÛ,¼7yÈÌsTZmsÄüÒ«2»Ú‰¥W¥¨÷ù¡Å} î~#1z,÷yuÛ~°øù·êyº&~µÅ)ì3t ûdáI@@À˜oY‚U©x\;¹äˆï$¢D@vdÍÊr|9·®Œ…à}k£/kXÙðâ}½fÛs m1âE@öÐæ÷{?eëcYqÄ¥llâF@D@å^Rë#ë:’Ç1Àn Ôw¥N÷bÞrsã,·3%» ]MgHŸmA÷[‰þ|_]1xJÏß+ÑvPmÊÞ•K¤êÔlýçì´ªÕñšÛÕe}ýÎúž~@@"*`|ó;gR×ú—ö;pf\H@"%06æ^2?€q/vy¤2bÉ6t5]æiuW~e•ˆ¥Nº˜wSBn–ýr…» ¤yÙÂå©þiû—‘•½+ý1s–,Åj ¸™Á}BâØÔ‡î¶ìÝQ€,;) "€ €Á xJ»3R©º†Tãâ`é @@`wÁ“Óÿ'×t×îþœ?k½(¹*y„¹Ä.5-Ùú†,»z•eµ]¾(¯@í4½TZœ[ÞV«Ôšï_ ÚÕH•z/k·ý‹z*åÇüظùå©«+)›“£ióè; € 93âL2? ZÇ>ÀÀ"€ €TW@¦”üººT¨÷xìSj™f« °àþuÉXóå·ˆ÷T¡{ºŒ€@²3õï2ídR5ÆüW¶­÷.ä²3‡íþ¦Ke)ÖÌÎÇ.}—*fëÚš¯ÙœH›GØ@@h’»þÜÁ0ojXÕÐìN>d‚ €Ø' óÈœÛ2? ò¹ù´†• /¶oDˆxoülÖø´é¿0!Ðb7³IDAT”çÙÛso—E`^WsƒÒÊ¥W•Ù´ÍÏ}´,0!jdmÛÚa“Sç„(¤r‡ÒšßÛ¶ÜÕÈ ¤é@¨¨€;û@ÊÝ¥žŽÕ¼¿¢\4Ž € 0¡€ïûN óŸ5½ššÿ˜0y^ ½@Cgãi&û¹ŒgSèƒ%@kjµY*Kôdm»®}óÙµm™¾ÝžræÇl[z…ÌÚÿ®3 핈ìm{]}Gýü½ž¶â!H+†‰ @@I´rjVYBéƒjyãŒI²æe@@ è17 ;¸ôÛê;›Û*DG³•X®bɮ櫤Œ|,O8«’]Ñv´ºRï•÷Øb7Ì™G{op#—}g‘Û¼íB£Ì3û~Õîgå}xˆçÕÞhc m5bF@ö0¾[HYò«>1×»p¯4yˆ € @vq6-Ë•Ô]àÝÈŒ’›ë€¸üÔ;œ¿j~}rNódIÌË䂼žzKœ‰ÀÄù÷šN¾2ñQö¼šËùç«%jÌžˆ‹tð´ÁÕòoÖÅÅŸiÇ2Ûûíõ]Í‹ìˆvW” wYð € `­@NùŽÍ€”9J_:weʉån¬}c8 €D]àÿ¹ å«æØ‚éŸs5?×òªïJ¾¾&>ó·òKÂI®åF>á¨‰ÍøšÔ¸_dÅG$³¿>ÐÖ÷“âÏ´ïŒþ–ô7¤Ùe_ä…ESækóîŸw@aG‡ã( á¢@@JXîû“2j¤¤FÂwò³âêá ‹ˆ@@ˆµÒíLÍEÉUÉ#ÜÎÑúìt²»éSžŠýP¦<ÖYŸ „^ ¡3õ™mvjè-,ÀõcÛ¶]ZØ¡ner#’¢ë&7²Ù+ ­k¦ÍüϽž õC ¡‚C@ 8[Ê‘¿(ðhk“;ÓÏ?dEcÒš€ @pH`Ôß²J.ä‡RÚ#)2ÄTÑÕüm¹Æþ_®‚¢7Švfœèj|—,óû;£ß3êüÌõœ?v®jW¹=_‰È£%Ê7c£g9¸MÍøJòEÉXê³6Œ&HF‰@@úN?.wù­)àPëÑžþb¢;ñ"ë'`@@ÀrQßD 9>«äŠúÎæ6ˇËêðMoš]£ž‹ëï´:‚·N îÞºJ{7Xø~–Bêmƒm™Göór$žî?¹ÿOR€¼ÂÝdõGt6¾<ìùQ€ û € P¨€Üå'‡þO¡‡Ûuœž¡Lü[j©ª±+n¢E@ìXÝÚ÷{Ù²Çî,&^µóbžúvݪƗM~4G”S`NWãÁ²×ãm:æuK)˜ýßˉK[ ÄgN¿E–¶<¤ ƒC~Ì~|f£ñݱþLºçZ¥ÌÅœcѱ5qÏ»S-W±0ÇL2Ì£Cl € €@±ÚÅ} w ÈE¡#‡¦n“GrC'_ € €Á ø+ƒë«ª=ÍŽ‡‰WV5Šu^ß•:}¶ŠýY>럡´I5D²ÝÇ;ä7ÌÓBR‰¡˜·ô­-±7N?[æ|s¦¬åäR´R4?*q@ó%a, abC@ŠØ–ó]Ýr\B>`ÿ{CWó‹dáp@@R|õ@)§Ûtn~”ŽÕ_0í¯rC᧤øXkcÄìŽ@²+Õ.³ßêLFZ}5³(ó[gò)c"ÙÅÙ-c9ý26®¦´z}¢«9”ËXS€ ×[…h@@’r¾ß]r#V4àÝœ¿{ZBeOH+Æ‹ @@ÀfQ“»Åæø§»,Ç:[+ï>¹°ûEµDŧÒç(%{ì’˜Ûü„§ôR)ø4`‚@µòK¯¥oªvåê_¦<nݰQfùñµ?Á¶žòïØßë¶?/7Í|a^Wsèþ~¥iû;‹ø@@½ëû¡ü²z¯§|˜¿{:Ñ•úŽZ¦¦;™ I!€ €!XÝ’ù,ñ÷‹„hra÷#‰ãR?9¸#ÙhÇ–w–ìL/ŸÕª=ï~¹cðÅ–§Cø Äbµ_uiïQùýÿ#ëÚ×mphˆ*’Ê–-?ªŒé¯HãÕoôÀZ™[ý0öŒ€äžús=÷£†öùbž”bÚ<ÓÈÞq+ç¯h|AR.<Å%ÊËv¤øø+Yºöò òõ…ŸÌ‘#X‘˜'¿Ü8³œ´Qf,gŒ³µJ¼+ú[ûî“Yÿ]‰¶ÃЦçé¼ïÀÃK> a â@@Ê(0Ð’~R>T?VÆ&Cß”!ëcZ­Lv§–Î_>vèa€‡¬hL6t¥.’}Ždyµxöôõr%Ëe…p¬ @ª´©íJ™;«Òw˜:ÕzQmMì²¼èç£þ¹sÞýóHv5}(q\óe{„{¤øxT˜†ª±Èò–’÷ýÖr´EU¨‰ßäÒÒ«²òÑuã¿ûW™Õ¶îGr[?,×KÖÙw!ñ毋̜1÷ºBŽ â A(Ó € PhÍ‚ÜE¬?X;wÆïê;OÚõ?íO`|¦cwê|¹€ö³iµ^¯§õ—ó3JåâÑøÌG¹ƒò„ýËó € =܈^*{AúÑË|¯Œµª•‘—ÖΙñ—dwÓ92³jæ^G8ý°¾«ñ˜DwÓíµÓgö+íÝ"Ÿs1a™aöÓÍ£æDYDŠï|Ù,ÐÐÙxšü™}»Í9ì»1}£Ã[¯Øã9$0´hh@þl¤ ƒ->#Ù™zcB§†Q @@ [†7}GšÝV¦-hRÿSÌ‹=$K@= 3ø· ààB\®b²wщ2SôjÙ›çwZÕ¤¥Øxƒü2þêEÇ=ƒÉ_pá @Ø!0xJÏßå.¥N<žкA™»Y×Ä{eùúkò+J¸j“_¾2ÑÙtn¢»ùñ˜Ž=ª•w¦|~œåj¾2óñ»Ù\úMNN;9SÊÕqÛW^É{“‡h/æÔ¶¾Ò µmÚW¾<7¹@¶¥w™ÜLó£É´ô­–†áƘ¸¥|„ € 0‰Àºöuft͹Wî~{×$‡:û²\Yìy¦Mf÷-Ùn>»æÔÞ¬³ÉNX}Gý|O×ü‹Ü¹½H6ah—CÆŸã8Á‰;bädD¼Ž €@ÄrFß,Kß³÷öîã®õÁò¹û²¢ÄGe9ûï)ß|5Û––eíe‘D‹¿æ¯š__Ÿþcô[%“䦵˜ÅéºñÍç³­éËä«Ç¯à„?ÐÌ’¥W•ªs%M)œu÷·¤ÝÇ0¨q’ýŒÕš?ÈßkîÍ`×úù¦¦&?C¶ª3=)@õn¦@@ *ùeXud yòg/’œ5mº~"¿kræ«ý‹zU•á¨ÓüÝé~,~’_Oü…¢—Lµk¹öÂüÙÓ2ÏLµ ÎC@·Z{:’]©ÇäƒÖ1neVz6r£W\iõNÓï”'²R¸»_is_öéôÕÙj´ô*Þ‚^ÐÙø²˜Òo”Ïo‘ÞòËÊ*ýï7ȲŒcRq<¯¿5}k("ˆ’d©à7ËÛ÷%7–ŒõÏK86Ç‘ŸÑßÐÙt¹ü]š=Ëé©•¹°¾³é»­½¿,g»Å´E²-ŽE@,ȶ¤ÿGîÀî‘ Í–…^‰p§Ë/ïÓqý¾ü3Ù¸è¦þþô÷ÔÖ/S«‰?Zks¬äx’\4y‰\4’ß³Ës¥ÈLågA®¨Ä Ð& €Ø)3þ¥² çCvFTÔ:!ŸÈΑÏdç$m^o:Í*_«xzì±ìÂìS…|l«þW}Wê%^þ¦5e^/½N>OΫ~TÁG`ŒÚh”ßÞßÒ×|ïôX 9]ËÉ_«DÛÕjSÞ£×-îûkµúw­ßþG{¯O—z‡‹7ÔÈßå1¹.p»Zª^U­`(@ºö'†|@@=ò5¾(ÿ¿qϧ#þHîÖ—ÍÐïN$R7ë.Õ-ÅÈû7+ÕpKßÚËè†U )åÅŽÒÚ;F.h-´Ž’˜çî(5îþß2f¢È2rÒ €. ´öý8ÙÝœß ²Õ…|Èá@™GønY¿ôÝJÕ(¹IpXnû•ñÕcRPø¥,kû»Õ›z{U»©T,uw×ÍR‡Ô¼Ô‹y/—bãËå‚»|×/—þÜѧ<ªTçaoט¾1ã/ZÝÚ÷û°‡J|… ÌV±å=íÐÒ«êéì°¹¦pŽœT`‰òÇVš3ã5ú7rlͤÇÛv€V/O¦š/ͨž+«:Èj¨Ó' €  @Öï¹-á¥>.w¿5Ø­]É%–Ùr•å­RŒ|ëlãåènþ¹òÕ|¹´Y›_V­ ¹DyõÇÖ§”šv¸ÄöY¶ëpùÅù%F«Ç´®7^€ º×Êö§E3º—Ø*kKë €‘õsŸˆko¡|Δ,|# 7’Í‘Ï"o¹7h“mº•JÌMÕ¥¥8˜–)ißè´L’L{Æl0žÚªrz‹¸U ‡[Tnt«œ¶57‹ë¸™)ŸagH[3dNå Y1u¶|ÐIȹ2û«QÚKJóòé§^>KÊKù¯g¿íxéÿÊÌÇÇe¿ø“לÚÉýâ]üDgÓ)òn—KùÉM ç«ö¾­.å†\Oîýƒl×rü[ö™0ÄSö´º<ѸGfÞÿ¹ìmOÒ ÈI€x@°^ Mm7ê*ùåË©¥gÊ=.ùåI¤Í×ÉE×yòŸäÁ]©ÿ5ZÿÒÿ ¹Ó£ŒŸV¹±tÿ/û3j‰›R KUÍüÆù‡xfú<3ó´\’%¯5ž¨2úP¹Ô,„RrM¨vWû;.Uë2‘ô{´,ÛRS­e[v9”ñ'£ ×ÝÊèIS €‘ÈÏ“¥í¿)ÉŸI€2'=^”"¡|”ÿ«c¼ñòù ?þcþÓêί؎‰:ñóuv¾6~à΃v>Øñ}磯ò]J´Æ¬ÞúÎ5íC›ðpG`îÊÔAòçf©;¿Wï—-VVº”S˜rɧ¯JÌI½M® ¦¸Ê‹VÓ”Šß.m½FþŸ_%+°/ QÓ € P=þtúÎDsó'äÚšՋžµ~¾\¨y¾ÜÔÿŽñèó7÷ÇkUâ¸TNw«©` ËU‹ü¨[äªÐù(¿UŒGä.jù€¯¦É¥¢iòé^~6ò³š+¿ç÷Ó™;ÞÖ^ÿ‘B¤\\ÚëÉÐ<Ô3êSMG ¨êm^ A@=üÜèg¼XíÛåsLþó_X#`”¹!ûHúb¹±PvdàË%Ù5êÉ'_ÈwäËlõÍÈEŽ$Î4dùkÝ™;˨ØÏ]œÕ/×&N”¥¿ÏͶô|5ÈÈÏîç @@Àu³Õ¨Ç®t=Í òÛ1[R'¥^øbùùHùÿ«åý›äû©REü7ù.Ëýè…r!î$)H/?)ÅÇçK|û,>w)ýH'–r>ç"€ €€›ý‹ú{¤zèM7%É*(™õ˜“ÏÏ.L_Hñ1(õàúIt¤Ëïdï ®ÇÊ÷äså@ëÀÓ•ï)Ú=dZ3Ëïð79¬p,ÅèÖ< ~7‘ € °»@öÑž¯Ël½ÿÝý9~F PYì„Bå8@ˆ–€·eìjÉxC´²&[¤ð¸Ñ÷ýÅ2 Èå"ƒCS–˜¼ïÀec¯•¥±Ð4bþÚï÷~)4á8Hnpûeò÷D‹iÊÍÑh¿%ÈÜ(@©M_ € €@5ò{uE5C o‹Œ:Þâè @*(9-óŒ\°½¼‚]Ð4% ÈÖé\.wâ@[_gÉÑ@(f͘s½Ì~L„2¸)%ïÛóT›Ú>ÅÓ9­HÁÓ7ãŸ]äiö®õ¢†îæw0È ¤é@@fcú›rqè/!…lк1±"‘²-lâE@`òûJÉÒ–ÿLoô‚@ÑŽmÛzÌࢾ'Š>“¬¨ïj^$Û^œnE°)§.—¥‚XàáV&þÖÞnY=êî25ºf´1_‘ßíçÈ ”é@‹@»Ê)å<,á‡eñ˰Z6d„‹ €@€Fé±3¤?–b ®&dßÍd{NZ}êêÁÉæòK¯Æ´¹ÕÆØ÷³QfÓöQÿ’ý½Îó•بü‹åïŽÕ•í¥:­k­ç©š™-\ù/ •7¦@@ TÙ–Þû󿄇*(‚±D@S€´d¤@jdf{•o.¬Fßô‰À¾Œo>—]ØóNu†Ú¶¯×yÎ ™3æ|Ùµ¥We–Ú’g÷eÜ!û²né[+cp¾}‘±ìù®úŽÆÖÂŽžúQ §nÇ™ € €€½#£ç»z7Ÿ½ƒbA䞢iÁ0" €ÕÈ´¦¿.ËÞ_Íè¥ÌVãûï̶¦— á¶@¾ˆ"3ºÞçV–æ™Gz™¡æ–[y³É´¤—Ë¿g”·Õð´æyÞÒùËçÏ®dD +©KÛ € €@H²‹³küœ9/¤áVx^!{EÌ oxD† €aÈmÙþAÙw|( ±CLÖ3¯Í¶ö²ê‹ãÃÐòƒæÆ<Ï©¥WóC&ïßóÔ5æøðY‘ž,ƒ{®êäÒâR¸oª™3ýó• •Ô¥m@@ Ämé{äÎàï…8DB ™€V:î×ècCá € €@ÈO\mLîC! ‹p¢ `Ì/·o3G÷/êýUÒzŽ3æÌ¹NiÝ蔃1wËû÷§Nådq2ùep}e>nq “„®ÏIt7Wl¥# “ðó2 € à²@.7rž,)²ÆåÉ­¼1íUì—“òFJk € PMþÖ¾{åf·oV3úŽ–€Ìºýv¦?ýÚ5§öf£•y4³mèlZ(ûؽ߱ì7Œnßæp±ËÎÑê_˜¾Mõc;£Ÿ8j™™¯Þ®:Ô´‰œÚ« §æÆY € €€mCJ›;‘ I$@2 hºA@Àz‘ [Ï‘‹¶¿³>µ€QF¶{4—e[zÞ­ÎPÛB,Á•EààŽƒçHáä¶²4¢F¤ˆþ©Õ§® QH„²CÀŒ¨Üò{˺¢•zqC¬éòJäF²ª´‰ €X$]Øû_raHîPç Œ9NŽ’ßQøB@&jÚ´Õ;YêCýÉ«LM@Š›”oÞÜßš¾fj-p–Óc³®Ëï_gcìû‹YŠg‡{¾¶¿×y¾ºC-}“1ZRÝ(*×»§¼KëV5¾¬Ü=P€,·(í!€ € l~¿QêO†NÈA h}pÃʆÝ-ý!€ €€kÛ2}¾V‹eÙÿ-vf@ÔabÀÓ¹1ÿ„lkïa‘¸Ê/èhz“VÞ™åo¹z-ægñæü±sU»ÊU/ zžL ;œþ’ü[ö›É޳ôõšxÌ»]J¬e­–µ1Ka @ˆ¼ÀºöurÛÕ"ù%~(òL* cµì9© € €ÀNÙ?ëײìÿ»å­¿ó9¾#P’€Q©ÑÑ£õ=QR;œl•À¼ûç bžsK¯ÊŠD·¶e±j0¢¬ˆu.w¦ŒÇœL_ëc’Ç6]TÎÜ(@–S“¶@@‹Oéù»,¬ùfùåg»ÅizZQ€ ™>@pH@–ýÿ¾ÒúR‡R"•ê |93Üó¯ÙÅÙ5Õ ž«!P;mæ—d/ˆT5ú®TŸRÌzFoÉ}²RíÓny2‹2¿•÷àÊÛjxZ3Z_Q÷@óóÊÈrIÒ € à€@vaÏ/äÆô3H…*+@²²¾´Ž €€“òYó‹2 Ò½ÙKNŽV“2[e í{2 {.a©Ê0ŽOecJt¥þEö}ü@e{©Bë¾¾4sZæ™*ôL—SÈdÓÿ!«G=5ÅÓC}šü›«5·–+H å’¤@@ÀÙCå;ræGÒ!JhsØœ®Æƒ+Ñ4m"€ €€ÛÙGÒçJòGngIvåÈï÷¨Ær'ô/ìùV¹Û¦½ð Œ/½ªôíá´èɶöÜYôYœP]3Ô6£üä÷î¬n •é]Šÿ’èj*Ëé +3F´Š €X-]˜þœñ ¿Ü[=Š• ^ËúihY•#¦e@ÜX¢Æ¶o|›\¶}ØÝ$ɬœR°þ½eìUù¥ËÙ.mÙ#0múÌ/h­ší‰xòHå}3¹ÑsåH'‹X“ Ø}DKïϔҷØÅþ£—ßù¿´àþuû?¢°W(@æÄQ € €@ä²&ß\ý§‘Kœ„  Y¨Ç!€ €ÀëÚ×mÞò&¹ìþÐ/ð½¤Pý¥ìpz!KTÉÎÔ¥B÷AçR6êæl[öqçòŠPB#Û6B ɽN¦¬õA5Ó¦µÔÜ(@–*Èù € €€«mj»k•ô:]M‘¼JÐ Káã\@".0Ô>´)ÓßÓ&+Ø­Š8éïC@.êo1¾ÿÎlKÏGÙïq@yjþòù³Ö·çW`q)e)¨nݸñÓ.åÅ\Öœºf£¯ô9Îæ®õ[ÝMo.%? ¥èq. € à¸@vqvKæéžSåÎão;ž*é) …ŽVKT¼ÈÓ8@Ø% ûhezÒo‘¿·ëI~Šº€üîñ”¯Ô1²7ýw£nõükæÌüO)=ꚃlwòÑüLp×òŠb>-=«\¾^"Åÿ¯´ü ¹S[ S•ã<@@ *g«Q¹óø=òKÒQI™<'ég6ÝtÄäGr € 0€|ÖÌlH¿Snnºk‚£x)2æ{#Û7=Ð’~22)“è>ê»’¯WÚ|hŸ/Züd~›“þÖô7-NÐ÷½Pþ [³÷Ón<Ö‰søâTs¡9U9ÎC@¢%`²­é dŸžÏD+m²H@{,Ã:‘¯!€ €@í*—mI¿ß¿äý¦ ì‘ÃÂ'0êsQfaº=¿¬aøÂ#¢ êî®›å騮-½*†ò>WçiI_••£ÖÈRÁU¾§*õ Õ™õ'M¥w SQã@@ ¢™–ž+”òÏ•»ûdU$¾"/ û@FþM €e0Ù–ÞËr¬×”­E²B@f„ý=gr¯îoI_oEÀYqØ‚Úk¥øø¼Šwpò^ÿ ³{F¨»þ…=ß’®:ê.Ðnò7Ä´w›ZÞ8£ØŽ)@+Æñ € €@Ä2 {oÑJ½S¶Eœ"òéËûàÄÈ#€ €ep—)cÞ.{j1 ®¬²!m̘ÿÞ²eøÈ–¾ÇB!a,0>ÓJk÷f Ó7ºaëÌIw 5ú!gÿíÒúŸs½ÏËI²X1ŽG@P™–ôòœ1¯’%YG„´nLt'š",@ê € PügM{•̆|¢ÍÓd8¶É…úËX¿mý[Ö¯GHDQmüÒ«1ÏsqéU•óÕÅCíC›ªmLÿ•È.Ìö*ã¢r=T¹e£.ièNUL ‹ÑâX@@çòKÇdüžcäõëe)óÜ ü1˰FlÄI@ 2‹2Élð•Ï™_¢?úP@nbÌß̘méaÏÏÙmèJ–^ý¼RúŸlˆµ˜åï± ´¥ï)æ޵S ÛÚ{‹lYós;£Ÿ8j_‰UéÛÕŸøÈ]¯R€ÜeÁO € €Å ´©ír×òEÆ7­R‚(ötŽwA@S€taÉ@0 ´÷mÍ.L¿Ov?KÂcùÿ0ŽQ1åoZôº.#ûà‘CV5½V¶š;ϹtÚ>:âËþ¶|ED@þª;KV‹Úîb¾² Ë Ǧ>Vhn •â8@@ý ô·övûþö—Ë~+÷{/¸*@ÒÕ‘%/@B"mí¹C/õ«¿…$$Â(ZÀüŸïûoèoéùˆ’›‹>œH¬HÌôbúN™]%õ ·¾|íahqß_ÝÊŠl&ȶfŸò•ÿcókž§?“\•|a!9P€,D‰c@@&hʶ¤ç÷r‘ýz¶Nz¸"pDþ‚+É €„S@–dýíVÓQòYóÎüLºpFIT{ äÇJ¦=Þ86¸ýå­}?Þûu#0.P¿FjÏwMCÞÿïß`®v-/ò™\ ÿ‘Þÿ”Y¿›üH+˜nb±Û%òIo iåø4 € ^ü^.9äp¹,ôm.…wœÊ™Ü¥÷cÞÑåjv@@ý ¬m[;,Ÿ5ÏÔ~îDù¬ùøþŽãùpÈï¿Êår'd[Ó ž>¸9QEغš^#1¶¸ÊON]¨d)é²´E#v ,Qc¾6gÊ-9»/,ZÙò5É®¦³';šädB¼Ž € P´À@ëÀÓrqèÝÊèWÉlÈ‹n€¬ÐÚcV«FŒ`@°[ Óšy8;Üs´!óûª­·;÷¢—é©«òÏ”ý;lË<â^†dT6å3<åæÒ«Rxz Û–^Q6+²N aú×2Gð:ë/0`£¼kYјœèp éð € €@IR„üMfaúrçs›"Ÿ(©1N­€§Õ‰¡ ŽÀ@@ÀMv•˯¼1¶yÛa2Óî물Šaõºn[nã ² {X*7ÃÞ ’s¼«eéÕo„SÌlõÍÈ…S=›óÜÈnð?ëêþŲcëœiµÞ-ȉtx @@ ,m}™‡ÓGÈ0ï—뙲4J#Õ0æåÎÞkU.wyõƒ!@ˆ¢Ààiƒ«e¦ÝûTN½Æáý¶B?´R^aÔèËû[z>’_*7ô`Õ©¥B}AÕ©Hæªüª@išFí%xs*÷AWo’‘-Y'º›Þ¾¿A¡¹?žG@(¯Àåg[z—e6ø/PÆÿ¨"ÿ¯¼ÐZ0æ¯2vטÜè‘™–ô?g[ÒŸÈ,Êü6˜¾é@Ø·€,uøÿ2Ã=GùÆœ.ŸUXycßLe}6A] H÷å?Jø”ìÂìŸËÚ¹+ K¯ê˜Z&ûÈ9WŸ?Élèý‚»ƒGfÅ ´d’dn/ö<{Ž÷nHÞ›i¸$ù%úÏrqé¿sjì{«[2¿+©1NF@*% ˲ö«ô7¤ùoÔw4¶Æ¼ØÇeÿ­“*Õ]TÛ•²£/¹ß“ËùW.ê£ØÕ7B y7̉])§¿ „&Â{ª6Víj$¼Y5¶oüØÌ¹,RJ'ªÑ%ûÔJ-P3c_–>ä =¿(@îéÁ#@@àLK_—tוèN4û€,ßq–ìÒ\ô´o³U~1zH ¹1Ó1xršÙªû†âY@©@~ ­³¾³é蘧?.ŸkNsq¶U üFm—Ù¥ÿeÆF?ßrÿŸí›ÎœHt7Ÿ “g/’ß7œÉiW"æ{2ø‡»ó;Öµ¯Û0£kö¹òïÐ÷4Ñú½ MßêoííÞ=?ç¦8ïž?#€ €Ø! Ë5õÊò¬ŸÉ<’NåræßäÂÆƒvDîR”²´ª1×û¾ß’ɦÎ,ìY”m鹉â£KcL. €DO` µ÷—™…éUþa²<ë×D`[ôJË8¿¤äø [Æ’²ÿ¿S|,Í3Òg/SÓe)Ê;]¼@–$Þ´mÄ¿8ÒãKò È5ûåZÇ÷&<ÈâeEå¥uw×ÍÚ=f@î®ÁÏ € €ÕX¢ÆTú ➆U ÍÚ‹/Òž'Ë”˜×˲3ªœ[½Ë…¤§µ2?3JÿtÌ÷ZÝÖû¿neH6 € €À.¡–¾¿É£sæt5~j¶‰½Eióv™€õzYƒë£»˜výdÔˆT¾ï«Üׯ÷/Ûõ ?!0eD}ÓZ«Ã¦Ü@ˆOÔÆ,yfqŸì6ÂûݶíüšiÓß(+?¼ÿ£ì|Eþl7Çê¦]-Ñ_¸3ƒøß?–uºõËv>Áw‡@R5ËVK¥ý]õ–Þ- €@™Ìja™›¤9è_Ôß#iÝ<þÿå3êçÄÞ Ëw,’ E‹d±ž”ƒ)W,%¹x$ÿS’_~&[öüTiÿgÙ–,*&Nà € Vá–¾µÃJÝ!ñÝ‘X‘˜çׯO“Ϙo—OK¯‹úžäòqL>3þXksoνg m`(¬ãH\ö Ôu$“¢Ë%öE>yIJ/ê“ÙGz¯ŸüHŽˆºÀêSW&;S—Èu»œ´0êÃògý;ƒm™GòùéDWêgòë«L–¤@¡€|0ýy¶%ýš†FHX#P·²é¥±¸–™‘ùb¤>F>¼O³&øÍÏn“ǵ¯~““ïÞèè£ÙÅÙ5tmOKU:¨Ñþí}ùeäÊrãš=ƒáH;äïºMvo¥2»ÏWmù=ÄøŠˆ€V˧[ŸëûF•¬Ò`}$0¡@ݽu â³jߪŒ×.…¸×º¸Dä~¶Éï¨?ÐFÝ»)§ØprzÝ~Ž‹öÓù¥Cg5Z¾ia_Nµ«‘ª ¤+Ÿ¿÷¸®oL­F÷õÏ!°O¹ÉzŸÏ»ðänt²+õu¹óàtò"@l_f¿.›’¿Ï†X‰+äÙdcò%&æ½Râ=Rî±;RŠo¯Âä{X‘KñAJñÉüÕý„VþoÆ|ïñm¾yœ GÅCr € °S ¾£~¾«9Q>Wž Ï ~•+7¼I±Ñ—kÁ¿—å"âÿ'c·ÿp¨}hÓÎÜùŽ €@¹â¾2Oyò/(_ € Œ€üº÷T0=Ñ ;M3*ó[É6ÿÿeãY/Q^âøÄ åv)HÆŽ”e¤^){¾TŠuó¥0iÕ‡_¹H”“ˆ{¤ÈøÙ«è/êÙïflô/²LmZòeöÛø ó@@ <Ï.=ú}i-ÿ¥–«ÚäÉ£”ö¤ é ¾NÏgõ㯅ý?²—£\ú}\>SþD*?Ý>¼ñçëÚ×m{ØÄ‡ `¿€nèj~«§Õ=ö§B €Ø!àû¹·ö·öÝkG´D‰€c²|aŸJzq“”e¦åR£òL£ÜÕÞ(f’RËk”åKëÚÿgT.^­“òá:),Ja1#ßû¤ÿŒÜmßççr}£ÚdÖnÊôËRI9ÇF‚t@@«êh~žW“;B&v<Ïhõ<¹ÉíyòÙîy’Ô¡òYrfÐÉ=»ã_¤0ú¤Äñ¤Ü¼ö­GŸÌ>œýK=ô‡ Ðùýsâ5Þp € €@09c^:Ð’~2˜ÞèŠX®bóæ4/ð|¦ì¡8CnÖ›®bjFþg)RNWÞ®Ÿ}#Ï)=]{ªVî*ÕÚÉ÷´1¾7âkùÙ˜).n6žYï©ucƬ‹¯]?xúàæ¢cã@@Ð ,¸A®©y^L{Ï'UƒÜø&Û˜ü3¥H(Êüv&_¨œ%ÅÂñïùÇR¼ô冴üÏÛä³çV¹Ym›,ß‘_v›:×Ëóýò8+EÆ~?§ú½˜Îæ¶«þÁþž>ö ¾@B# •왓84µ."{䄞@@¢) w¥nÊ>œ>ˆ;P£9þd € € € €@¼ñ;cŒþY’%G@ª- 7üüŒâcµGþ@@@@@ ’ÞŽÆÍC•ì„¶@@`‡€,“ó  € € € € €€ËãH“3\ uy”É @ 4²+7ý„f4@@@@*!0^€ìÿeïodsã¡Jt@› € °C@f?®î8ý8 € € € € €€Ë;–`]¢|¥Íw\N”Ü@¨¶€6æÛ²ÿ£_í8è@@@@¨¤À³{@*eÆÌ7*Ùm#€ u£4ÿÖFýM@þ € € € €D@@ïžc¢»ùòÄ‹wŽŸ@@ tcٖ̓ôKKo‰@@@@@p <7r;R J@Â)`”JëÑÑ«ÃQ!€ € € € €•Øgr¨}hSÎWW¦KZE@ *þ…ÙÅÙ-QÉ–<@@@@@¼€žˆ!Ñ•êÒZ/œè^C@} tdö,Úç+<‰ € € € €8,°Ï;óÍèsäç ;ó@ 0fÍÿÊ € € € € 9 ƒ§ôüÝ÷sïœ #€ P‚€1æ}²ôjº„&8@@@@°V`Âd>«þÖ¾{on´6CG@ X/g[{¶KzC@@@@Â#0i2jvcú£Ê˜ÇÂ6‘ € >cÔÙ§{. _dD„ € € € €'PPRµ«‘œ?r²Ræ¯Á…FO €Ø# ÅÇ?ë-c‹ÕÙjÔž¨‰@@@@(¿€.¦ÉúÎúCcºöJë†bÎãX@Ü032vû>º=Êd‡ € € € €… 6òÙ¶Zž5~‹<\_Xó… €€ãƬ͵â£ããLz € € € € P°@QÈ|««[û~?êç^'{BöÜ "€ ऀÉä”zí@KúI'Ó#)@@@@@` E-Áº{ûã˱zµ?PJ¿`÷çù@(ä÷|T££Ì|ŒÂ`“# € € € €%Pô È­ç—cÍåFN”™í|Žï €DA@Šë-c¯fÙÕ(Œ69"€ € € € €@±S.@æ;hÊ §_c|sc±s< €– |9ÛÓóºÌi™g,Ÿ°@@@@@Š Ly Ö½£jèl<ÍóbwÊós÷~Ç €X/`Ì:cÌû²­½XŸ € € € € €(iäîqõ·öÝ;¶]½Ò(#ûBò… €€SftìŠN)É € € € € €@…Ê6r÷øê;Ro‹yêËJëÆÝŸçg@l0J¥Ÿ»Hn²¹Ï¦¸‰@@@@¨¦@Ùf@îžÄ@[úž‘á­/–¥ê®•‘›wŸ@»€üÛµIuunpÛáÃ>Zć € € € €a¨È ÈÝ“LÞ›<ÄŸå]ä)ïÃòü»¿ÆÏ €„J ¿Ï£R7nRþõÃ-}kCÁ € € € € €–T¼¹ÓáàŽƒçL÷fH–e}ŸtúâÏó@j ÈŒý?jeîܾ}ë­kN]³±ÚñÐ? € € € € `³@`ÈÝ‘V5½J{út)F¾Ck5÷×ø@ d¦ãjmÌwŒÒwg[z~DŸô € € € €DA *Èç`—«Xì¦Wê˜~ƒ<÷¥Õ«µÒ³ž{@(“ÀøžÄFÿLöv|ÐxæÁþ‡Ó«%Ê/Só4ƒ € € € € ð¬@u {ÃRUS—l:Ì‹{‡)åæ)}˜Ñêùä\côl¥ÍÚè¤PY»÷©sçÜçyÎs¾w™çžU g›µ`Á‚£•RK ~¸”2‚Oý?Í™úä’Z»v­ÌU"0R¹Ò« m'ʘÂõ‘Âçïp}¬£'±oWAÙYD  pM g ‘µ²ÿþû×E£Ñ‹ñý ü¨Î˦Wû'°ÂW‚±Mº=™L.ß°aÃî”   ê!`å+ê1ÇsRccã ¼ncð•ÓóÐ׌¾vô5¤¯¥|rL'  j$+“hJû2~< MÕ…eö”@“¾–ô5«ãÖ¸zš+‘ € ŒÀtðõ}ø{­}¦k•IàÚ¡k‹AXež?zM$@$à!½0ü@ÞÛ§xhŸ¦H`$S†®±‘iÜ'  ª#0€ õÓaÍWÕ]e/ðµìVvæÌH€HÀg2˜íˆ~:ßô™otg’Ðך¾æ&iñX,  (H € M5Á÷qQÀ#MCלGæh†H€H€*‹€Àôÿ•å6½ô5—y˜eaH€H€HÀÌ ÷hâ$«®°QØ”€¾æÐ!ÿhS;Ô'  J$` -/T‰¾Óç 'Àk¯ÂO Ý' (š€n:¼hm*’€^{fü¨M$@$P¡,4éEµ¹‘@Ù ðÚ+;rfH$@$àAøÁÌ''£ÚÜ@dx2”9ÚѬLÊ‘léäê&©K$@H@7AN«@¿éò$ €°é“ , ¸&Ài\#£ ˜`fÆÚ$@$@$@$àš0×Ȩ@$@$@$@f€™ñ£6 ¸&ÀÌ52* €`fü¨M$@$@$@® 0sŒ $@$@$@$`F€˜?j“ €k À\#£ ˜`fÆÚ$@$@$@$àš0×Ȩ@$@$@$@f€™ñ£6 ¸&ÀÌ52* €`fü¨M$@$@$@® 0sŒ $@$@$@$`F€˜?j“ €k À\#£ ˜`fÆÚ$@$@$@$àš0×Ȩ@$@$@$@f€™ñ£6 ¸&ÀÌ52* €`fü¨M$@$@$@® 0sŒ $@$@$@$`F€˜?j“ €k À\#£ ˜`fÆÚ$@$@$@$àš0×Ȩ@$@$@$@f€™ñ£6 ¸&ÀÌ52* € ™:µI€H ² DVGb*x›b_)Å,[Ȩ́P–Ü*„ýö·{æ§’ÏŠeH¯´m™°"GEÞlÛ¡7Xž)-”Ó–³PÞ™B©Ê¹S*µSÙ%Òé”­^H˜ÚUiŤ¿$PI€UÒÙ¢¯$@¦dC{ã±–e+•8BIy¯yøÞ¬ì—ì§@CöCøŒÙ¼]ÄÕ:©äÚ´ý“'týeXÑG;³™5kÊÔ™‹-¥ŽPB¼ þ"…œÈ´y 6|XÙöDa™-ûÄÏ‚RéhGóÓ¶¿ÀчS­Ïø¨xt…&¹`ÁÜŸÜÜX»víð£y¤Yޤál?KgÚÅIá‡Åw×¼R*[«2è›D 6üh*ìeýÝë˜þ•TÙcCòP”QÐÜcŒÒ×ZCÇTöXÖŽÎ~pç ÉõÇRÈ8P”òRª¥³ÞD¤îÜÕC3Àâ4ܼ±‘ÇŒö•ø•­ÒË{vt¯KEÚÈ–©r›¨ ËÆÅ– |¦ãâ©55™ÕÇEñG\6wö<•x5€Ùô‰úôâN¶tæ|ŽOT™ŠÉ7oÞ„[^‡Î:œÆãBïâ&VøŽ·„LšÒ©¨óÄç Œ>®å‘jM8¾Oç¬jŠÔÖYÏCm&þÍ6%¾˜lí¼ÑÌH™µï¡èþ±ßñA¦9ã\Ï0SŠÔ'dùìèÇfÎM?osÐâüÛ¥a±áüjnŽàÑ?&'üùÒÐÞtD@Z_÷ºñß±¬Ç!à9.23ö¢ŠÛô´vÇëz$8gÕœé¡Ú}®FÍÝ…09Ë#³{™Á¹<×èÝÑ#cW‰¸¸6ÙšX¹—¿LÜÊssÝ샷øÐM=ô1ò™€s™ñï]Ù—1GþoYÒ• ÇcWZ¸)Œ/tm¤#ò£TKê¯ã‹ùçh¤9ö9/‚/`:õTâæl%´JHOH€HÀÀ~mÑÆhGì»–%×—,øá#BስG:b+f>ÛwÄ¡Òíâm5 ÕÔîóO_×"£’_{@Ê7!êÿa4ÞüãèÃÑÙ{ã—ª ÐÓš¸µi¿2.¬®¡UÁ{`'"›,¥†xìÿà>×÷™é¶[ÉÓuM20S”Ô'ð‰Ç®®³ÃsýÔqêKâ3ò;cŸ |¾!Þøî’d0d4Ú{_´9ö<*2–Ö‚”2·¶¥8IL <‹ð°G™T)†»¸rXõ‰ô¹hú|Í•VaÔÄ-ˆ´7Ÿã¿’0ˆ% å}kL׫³µ~ ÀLiRŸH` "®š6Æ("¡þáúy¨Šã¡~+þ§aÂD –´~@ðxO Ž4²RÔ è¹=xCMÔ›F*û¾”aœæ'ÂñÆÖ²çÍ '”ÀæÖî ñr™'NHë]c퉭‰Îoº¦ç››W¿L-L,ÏÚa–%ÁO Š%0/}g`Ÿº§ø}À…€Óð¿&oþˆWþ„ׄ›Ñ×ì7ð.‡m_4ÛèrZ"°ª$Á¦Wàh§$ЇéëÂþ`jWòŒº@àNS;¥ÒŸ×ÖôF s0,VÛUßÀ™ðsøå”X©Îí’ ”…š0>”=4Ù»Ñ^xŽæ )ÕÂkšþÍÔœ®e²¡gðnjËsýL³Œ|˜Í‘ž“õ·A=Öî?­˜Æ£bq]Ÿéhú˜ ,C–u5êàtæÖKŒ,#°‘4¸O$à âú–¸Î;Ü;•Aèš×ÊeP€_ˆ ­êáûÅf‡Ú¥«¤°Ö œûk£Ôz¨Å˜ŽæÈ‡ê¬ß§ÔyѾ§†kcбšZ”ú¦%¾½ݱ:Ö3⾺ÆÃ±s½ăگŸ`ðÂwG—™Øh"üN$P¢íÍ×a”ããíÔ×Ï1' 5µòG£]}@Ô鑜(ßWü^F].›æÖÞ⪌®x©ÞėЪöwÓ‚ »~š°¾fjÇ+ýÙ«£˜Jí«¦öán²í=ŸÊeÇ×®\3H "½Y(!F:6-ÇT“Æý2 äãÙaPGG÷oºÊ©Á9ñæp4Òü+=’Ó©Ž/ä0Y¤-vŒ/|¡å!p¦ØNÛç¢ÒÛøžÇ}òI¿ô'¬ Yx¹3Œ!Úö¹½‹z7ç²Ã,¦‘ ø“€žûª£ùû¨ôºÈŸæ÷ ‘_3oÕ¼‚³û£ÏØáµR= KŒºÊïO)Ždjê,y¿@í])ìÓ¦? ô.ê~5X^LΪ‡—Ü…õY'l³&‰7þ®åLi#$½?µ°ë§ùì0ËG†é$@þ"°²q f¢^ý)þrÌ™7ºŸZ°¶nÜZ;ÌïõQ+(š¯¨3«þ“Âè›Ãáæëüç=C »Ù˜îv½²ãj4E&ÝkŽÖo¡Ð £SËõ½¡­« Xw˜æ‡àkCßžWõôy7`yÑð @Ñ<|°k殜;-:3ÐŽ·Ò–¢}ò‡âYáGÃoËå |ó{ýÁ×”\Ç+) Aò%û®Ü×|½ÀJ*t•ûºmé¶W0ÉèÞ`P—êš`ol¹³bY5˜ÜXÎq§µ·´n޵íô'·,Ù²cï#{c¶7~#ðýC^3sêcpëXŸ¹æÚ<Ø2Ú{æïÁ%…îÇ€‚›QK†Ø¥ò7”bú”ÓÏ«ü’°nèæ6,2m¼N¨¾O¬€u/¦z ºÉßTVOiƒ¼Í§ÃPòëºY¶?e-\!gxœH`,‚[ Où0~çØQµUÆ—ëbÉRôºŠSBâgÈÀümX©mˆoÖaØü:[¨ xGÝŠŽü[ÑÜð*쇗Ö9Xd}´äQÒw<>‹ž:b< NNmâJ±HìÙ¯m¿uÖ´# ùy³áämBYÖÁÚ:L Ù-l{ ʼÕVb·´Ôliæ Êk@sÑ{ ó>Ó·ýü^KhÞ–ÿ8LF{v_ª­;ÞxÚ)Þž»²G$Ê2²V¿èIKþ—é9Aú\ª'ᨠž˜)mêWdK§§µ ÑŽf£€ÅküqBôˆ(3Ì:øÚ'(~Ž€°èõáDÁÈ [ˆ•½­‰çÁÆÉ¹úoÍ0º&zˆ >‡b|ÔÓš))÷Ȧïn³×N ÚÀé ÓsÚ/ n~ _ÙéebÚû6äd´=z¤°‚_ÀþB‡zŽÄTî_¿¦qþÆÅÝë)P¨ìJñ²µiɦÑö¦Ë€=`Z Ô qîêÆ‡6ŸØm<ÍE!_¦Ìœ†)0Œ_ºúaãt‘¡…òÓÇÙé„eH€ÊN |Éâ‚/¼…®ÂÿûSë:Hµ&®Gðõ à$ø.grqòÐý˜H†JÆß ðbGZW!øz 뢃/”q¥úê˜Tkç[R-‰[]_ÙR¨äÂä:¼,Bÿ÷ Ã"æÞmÁ`À¼9Ç;wh©L’ »VàúÔÝL·ºššÀ=0bö6WÀ ½¸=&;>»€Xáö¸÷â3…%€9%E9 ÇðCî*ØÉeMcÅÔ|=10>Ó‡ðÿs± o†›ÄDßÀ±øAÉ;œÜm¨:¿)Q·zYyà]PïL¶$NKµt>™M7ùÄ"ÁkwŠôÑ(çZ;#uqp¡î‘@ªh?Ý'Ïõ´Ëƒ"›™‘ÞC¹Lè)/0øEyf›R¿M®ï¼Õ`nhQ–HÀ—ðmH§Ó‹P“s\)š¼°†Û®ÔöÄIhÎüñÄPQiµµ]ÜxB×_¼öe{k÷K);q<¼_{c[½?pF#ʼñƒVÊM`ã;_Dæ¨/T!ß,!o3YÎk<û2Ôƒ_O¦ð1õšH§Oà ß@aÙ×%€½Î‚{$@Hù{ú·ï:£ŽÚKêþR‘»Òx«½%Í'‡q”ÑÆÿmÉtâðÔ¢Äorˆx—„Á¶Ýw‚°¦F3}çBcLíP¿D<¨©ϳžõ]wà¥å·ãÉ8<6³¦ÖÒ}=ÝÐÿñ(ÔÒ^ljT)yjÊ_pk‡˜[b”'(L`ÔÈÊ î%t ¤k½ÐÔø©ÍK7 Neê7-ší?Ž7ÄÔΰ~›¨UVð>tu0Šƒðbôsôû**84Êx¸ Ü! 2@ðõ‡ÝöÀ%¯õÊQ¦Tk—îà¯;õ—c{b‡²ðªŸ—‡“OuÝ‹ÚMntr˪s§3µ KÀ³¨ÀuÕ7*K¨å³™5+ßq7éa«ù èÙÿ67:9d_Þm§ÏDzQ}^€å Ê$ cE=œä ä7í~ÏK‹’ÝNäK"#劒ØaAæýÉ ï×ý²F$—oýYðõ=ã ¥df ±² ¤ì®›pßþ¯q)¤ O:ývS;óâÑwJ©®6µƒ>™<‡‚¦Pÿuk×®ÅóŠ @É(õM4Å}öKà9ñ½`×ÿ„SnEóEÀ‰¼{û:Ô´Ýä^Ï[~;½"d.3±ŠÚ¢G{šäK]GÊsé¦óqºœáG‰›ž.¢!ý^okòñ¢Ì`vý Ü? ãõPOKÂè…5`EA*‘ ”›š1¾•lMè` wjÖ?«—€­lÛ¸æ(CPŠÏFÚ"‡º ©[ï…| 1¢ú>ÅÈŽ9PD°" Q…H L”ø³èë?5(Æ3Ú{í1šD½ À”z½^赞ØSf˜öa³¨M{â xK  ÓÅŒv¸wagšÙúNi›™>\ª•ÂQ?ÌH¼ùB4=.í›ïxéz±oÏ.ÝÕ“˜'i„H`/j«^î·í“ô,ô£lûâkz@$=qD©—ìtÿGÅRÑç‰=`ÅIórnëv5C¸ÇE 9¿¸5IÆMxº/WdzsÁ€(¼&ÜŒÅêo1ÁõdÈglY²e‡‰‘º†£Fšâþ‚ ¼øÑ©*9ZU§ÛUaÓJœºiQ×?])•SØÚƒ±yËZÚ¶Oí]ÜÓYN×Ýä%eå4|W?ÏÝ-nü£låÐý§"ñÆÏ0¯ ³Ô—æµ5=<Þ³BCw£Ælš))¾ÖÓÒõÿŒlŒR6¼«FYãW  47~émí\ãg˜ÖVË“7ቘLÖ ×~Ë“r_n|¦¬ÿ ¤Z»¿/ÛÌ=•SByw>;Ñö¦O"øú@¾ãÎÒÕ_RéÄœÉ:—bæœ%I€H`˜ÀÆÓ7¾ª›%†&éŽÜÓçI 9IñTx±0Ýïnª¯ÿ|ô«ò`1ù¦³Feîš¹ xüúèt—ßûÅÚ.Á2` À\ž Š“ Àë䫯ïOν d69Oí„— M‘ ¡ä5^8"•¼=p0V˜úm`ûŽHr½k+{Úþ£kE À@¢ €kúfíÚÛ"ÐÔ:ékÀR[R“¾ŒEž~ªy@ õTç¡&ùIcS´j‚S–gí4´ÅNÆPÉ“²ß‹ü|ªg{×WŠÔ-¨Æ¬ " ”„¦˜Qýç ×èsûòäH¼iÉŒxã~V@~ËÄ‚Â]}}éÓ1:¹dS¨039CÔ% ÉN Þ|@ÅdGT©åC0_ÔT÷œÐ£ê¾É Ž˜šâÎéÒº NÖÙSêÊÍ'vÿÝÈFe`ñ0 @ðúX„UH€ª”@²³óV,vñ¬yñeã°O6±ƒ¸ôg©…]wšØp¢ËÌ %Ê ”ŽÀy¢?­ìs&|d±RÛöôÙg•® ¯[fö: î‘ xG€5`Þ±¤%¨ ½­Ý¿EâYX€n=±Û|õ…`æEH€H€H`Òð`Âd¯™`n°ëôš‹^ÛubÁ×J4=þ_'²^È0ó‚"m Zûõ|³Îé(I€|G Õ’x µQ+ÊéXZÉs’'%·–3O`å¤Í¼H€H€H€ Ø)ìËÑ‘tcAAOÔݽ ;=X—Ò3 ÀÜñ¢4 Tþ˜+ªº —­´¾,³½µû%i«‹KOBý«ï•×./}>cs`6– SH€L ød‚GÓbPŸH`â$&~„¦ÈU¥ò¶m•§o^ºÙƒÁÝ{ÉÌ=3j ”@ߥ;Æ¿R¢¬¾šZ”øM‰l4ˬ " ¸' }Û´á¾,Ô ˜([–ta5xu•÷ù«gSÛ_ôÞ®s‹ Àœ³¢$ Lª2^”zZ÷úžW¢¯_¥OÃBÛ}žÙ,°" Q…H€H€H lT__ú\ä¶Û‹ÑEõÁM­É?yaËÄ0zÔ%ÈM€ó€åæÂT ¢l>±ûïè4ÿÝ¢”G)a¹£Î^Ý•\ö¯ ÀÊŽœ’ ¸!^nBžâFgÙ™µ!ëÎqŽ—å°²`f&$@$P¡¶q° =sNÜ®”Á2RB÷I)¦;)”Ô‚}0ÒÞôq'²¥’aV*²´KÕM RìÕ}–Xz¨”ÎGÀô>Ï]•Öч£³=·ëÐ 0‡ (F$@$@$P^ í û K~¥¹¢Fm®š\^ ÛNl2sB‰2$@îp&|w¼(M$‹€ ÈÚû¥Órô" AØÇÚ›yaË­ `n‰QžH€H€&¾î*‰7_(¤xo©Q¤ºkΪ9žõ/sê/0§¤(G$@$@$PõÆÞ ¤ºµ,™IÙX[7õ«eÉkD& ÀFÀà. €7ðbíë7koJI+$@%" ƒ!©›÷)‘ý1fñÀúTxMÓ¿9PÂ`%„KÓ$@$@$@îDÚcCãXwZfÒö¤”÷Š•SÌ,9×fæœ%I€H úôø»ŸPõOK컚êymMo–¸ÙÓR:6&ŒL—787dfê$@9ø¼so™D$0ñd0PÞ¦Ç1E–ò²pGì]cÒKÀ¬Pi’H€H€HÀp'OŸq)¥88ÚÜ|±€ÄÃ$@$@$0) jñE¹– +$­¨}šZ¬?Þ–§Z»¿/¤}C±6Fé]ÛÐ{û¨4O¿2ó'‘ T241U8žßô4=S¼+*µgÏ®/jý䆮ÛP ö§âm iJQkY‰àÐØV%3œ'?&“ T.ET g¹¢Ë8§aN]E`’8?wuãtn2*Ž—mY²eGÆÆy¢ß–êlÝ$idÊX)à¨ð‘±KLíäÓg– ÓI€H€&-`(Èl¢Ï®nz Y+ê=÷Þõ~–lM¬Y”ž–ÄïQ æÉÌö¾\ÿÓæFÚ÷jŸ˜W$i§j ˜¾iÍ|4¶oÕÂcÁI t´¶° ,=²é2ôû:º˜K ££Äž~e_˜K?Õ›øz¸ý-×17iz6þ@ºÛŽSY`NIQŽòÀ¤?Ï!Gɵ»É‘` ¡g¯?:÷V3ߺúvÿKÔzl7å%Нu1ÍÛ ýpGÓ^Ø™(‘öÈ[ÐÆgT¼üÞº¹µû9Ëp¦Ø-l4Ez°,žñÇG:šÎÊ™A"0xT%MoYFXȲb$I$à‚€ƒý}\¨Œ Þ<&­Bt0` ëº qw¬›ºc» =`Òôˆï?PË5îbÝ©E‰ß[|k¬îS¤°¾6'Þv¯™_ƒX~6û27Hµ$ƒÞýntòÉ*K.ŸoÜ/ßq7é ÀÜТ, ä €æ9’'¡)dÖ#³f9W¨IÎV'©¢]|ÑÔ{t¬nª´é¦vÊ¡‰7Zc¶÷yåȯTyDfÄ.‡í#Mì+e_¿õÄn×5 »^{årôë1É[ëâÔOÖ7Líh}`^P¤ª& „mtSã‡Àš:uúqU ‘…'7”ýw7âùd!ëü|Çü’^ß=µ^¿D³ãl¿øTŒáGÃoCÌš1Ã}ÏŽ®ÿ,&ÿ—?üòËJª ŠÑ£#åéáxcë˜t— À\£8 Œ%`ýulšËe}Ð¥ÅI j ôíO{Rx%N›»rî4Ol•Àˆîpƒé™%0¯'‹)O_M4=ZÁÐ TÕ[ŽÌtjà|±T=Ã}ª¥ë'˜ºb¯I[‹õGÊÀ]¦×°béS†Ø*ýS¨ÖþÄœUMS;>Ò/σÝGž´®<_¦i·,éJ!zpÝ 5: ÝŸ*4½îôÑé~øéh¾BX² µF¾ rŠÌŒ]‰9¿ÞíT>§œ÷%&×å<æ"1½kÏÅ涺PÉ)Šgv,4£nÜi0r*ŽHd6wI Ö€m€áͰ&TkéþÜH€œPr½±B2èpãìÕÑBre;¾²q ú|}?ð_…o²å[¢ŒÚboG?Ùe&æQkµe§°¯6±‘ÕÝxÒÆMJÈÏd¿}JyA¤-vL±6€KŽz$0D ubj‹óÌ`4ä§æ®™Û@°$@… ØBÅ K9r¿ºëAH"æ™ØM+‘™'Q3÷ñ‰õÄ£ÜuÓc@|ǤéqÐuÕöÖî—<òJô´t~òטÚCí$æg•÷‰¶âšV€™žê“À _˜‚ÐM ¡à”‡ÄJùþ*|“I¹‘@ ¤_[éäIòß#ñØUž˜*ÆÈ2Œ¶7_°äRŒ‰âtJ;__xFÓÕx®^œoƒZ8Ç¿Iµv­0±‘K÷5;ýi<¥¶ç:æ& !Ø["2¶ÌNV–X–?IÀ„€²uGYã «c¢3cË Ñ Lr›oîÅýâI3d•·„ÛcŸ+76=ÅD䍿§1'Áº+B¹ó/U~zŠ,—t½‰}_é[Tõü…î¥EÉnŒŠ¼ÒÄ¿a]K\i‹:üÝá0‡ (Fã°Uÿ/ÐO¡èÑ9{Û–ŸŠtÄnCMXÅöÿÀ[¼ç̽ñ h¶n:ôdC0'-KÞŒu﨑òÄè8FôbÔº¯n•§Ë[ë5ŽS^¿@HêQf¥’wl\Üý¬Wn¶ÓÓ’¸OªÇG§»ýŽk'(¬Ð}n¯`nISžrè]Ô»f-‘C}Lnè+£3š>oÕ¼ú1KY™moú¤ÛH \¡IpL ï•ÝßE3ÒÇ Ñ©çìèQÍkâÑ÷:w-¢¯h<ö ~³ŸÓ}½ô<€® *ô©Wrµèüækð {—QFJu÷oßµÌÈFaeÕoÛçâåyWaÑñ%p. ÏÇhO[±'ÞE%ê N«<-©Çë¦ü)Ò»D`d”§¶a¬¡½aÿp<ö™h¼ùq öb¹æ7¹®F÷Ú/Ú#§2ëüyX 6"ßùü¥¾7ÂkšŒ— ‹>ioº ÚѼNZ¡¿¢²í4“ŽhšÛ©Òö #ü-n·kAê%“Póõ…âz]ËVögÏïëi¥ØÛ´¨ëŸè²jì¯öÍ’òz`;õ³äÕ¬N¡ T:Þ‰ŸFf4£&LÌõª,hš¨Ç]}FF]«âMwˆGzNèù_×öu'ß#¢ïP 2­ÿ†ŸÍY;x[ÍìâGáhì˜Or‰_ˆ!“Ù,øI¥!П¾M„¬sp½=Ég^Çðd­'PcÕûº›Á/ÑÝàñÞõ½ ÔÛùôꮟ˜Rƒì­ùXKïÈ÷à å“w“޶ýø_ܳ¨ë÷èܨ–^V7=ß+³¦G!Ú{v?\z‡sH®ïú&j=—âÛ|£<3×`ð^ØÐA;NÓø°ñùð( 8'°Tô‰¸ºÛ¯8Wr&‰‡÷<<Ìo¡š›ÐoD/þý8ìJb ŒôVaË­¶´ðð³g(%§#¸š… êü`Ä÷7@'4fåÏÇuvG~ ©:ËðCâYãº÷ô0 L÷Ä·pí^î½õ!‹R†±w*^PN ÈZ92fƒÉ6ܘÐSmÁýµ÷ݾÙ÷ålÜ{#&O-t×9÷Í­/¤ÔÂ'$þå\«|’àòy°0­Eß=Я.*Ÿ×È Át:®ÎùŒiðˆkd®Ç S­ß*T`…ñ8 ¸ Þ´çÛúºËñÈçBÍ•(pz®°#°ú8hÍ@_}t&ìO`é™i†7< †÷ì@Z`ÜH ¢ìé›§+ë,\ü:*ù†ûJßn³qÿÍÆøfáë·š»{Ω³z:¹+½dãIIãYÜæéFn^{ãÁàrœ²¶¸i"ÌÞÖÄshrþ2|º!§_n¥ºýjª_ÆS|f'Ác$@Ž l<}㫨/¸Ñ±‚ߥlŒtDš3Ù’ø<ÊãMMU ÀD×DAϵ¦¦QÀ Ðü¹©S}Ì ö{Øøš©­Î!ßo*!`^P¦ E ¹8ù‚Rö9£’+å+kÀ*åLÑϽè&:;-N@â+{¨°/hŠÛl§í÷'v­ðµëhz öÑh„'Ê»½O‰Ïú¥¬©žÄõº9ÔØ¬3ª«ËÛŸ˜1a Üt“ÞÆ—å>êëÔCÐtª¯=¤så$àÛÚ—\z%žGÒɨ3š¸¦¬\Ž9MSâO*ÝwDÏâ®_;U)Z¨¢u¡iŽ]‡ï4±‘Ñ•êº-­=Æv¼2 ›{•:xŒø º#O·7~8—k ÀrQa xD Õ’øjÂò¾y”§fМ´ƒÖ»MŒbH¾.¨[ÍtS–-ÒK*­€îl¯úûîYÜÓé÷ó—YûP Ýÿt:]vŒÞÁ„¬oÌï$P‰ôÉÉÖÄXÀº?ü/ø  ëÓéôqX®æ¬-K¶TBé 2Ýô(ÕwL›µ1<¯xå„Ä6œ‹‚.¼: >‹ëF/g´ÛÁyµ7e0Ë’à' ”‹ú;`®™»6í9жÕçˆu—+ëBùàM¯ UîßFSéRRÿ,$?Îq×oÖãØâ!ð„@jQ×ÏRÛ;ÂüZX7R%=1êÂ~ÄÿjÛé$[:Ì4ºÐõƒhtFóõ¨‡©/x¾üZ·˜Ú)—¾17ë…^ä‡ãÅõmQ,Ð.„Q:/œ¡ ¨V™e‹„¸E¬·Ef6(…u.…¿ µedÒðx(´ãÁº €Ÿ)cÞÌŠÊO3­'E××Å]by4Öô eÉ Q3qx éGÀÇüdöîL<â×ç…Ê^Ót8Êq5X-t¼,Î/$ä·ã= »Žvİ҂<ÙÄ7½ŽhÀ ܇çþ¡í¹Cç°Ìb¢&F©Kn è¦TÁ³™k8Ý? P_ó~¡äb$ë~XoÅ ‹ •Í7<<ñ'1¢G­GM×ziYëS©Îgü¼Ä‰y©i ¨4ö†@¡‘x’ï2 R¯á¥æiÜo+Eÿ1²qKa/(Qm$Vÿ~ÛqÕVp–×”øU²µó½>ðÄ·.d&D­ ¢lu%eLHÃ=†·°¹ˆ§¦á]tš’¢?C!^Eú«è£±2zQðèdû"úZlÊ~1¥RÄ"±Ç·…¥c$àm¢6*£‡)8BXâpéÜgûã~ÚûS…”Sñ&Ä÷—Qy±MâÜ™•-Ÿ¿ÛôTò9±L ø¡8ôÁ¿‚xPÿÎqþu‘žMVèkô»ÉZ6¯Ê54'Г°§ÿ¹‘ ”š^R’"3*q]©³¢ýê&`á-ú'Õ€¥Ÿ8rÕÄåÍœI€H€H`âX©õ‰'Q ¶iâ\`ÎÕH@_súګƲ³Ì$@$@$`¡]DÔ˜)ò‰†JJÀ·g®½’fBã$@$@$àO™yÀR=‰ÿDÖíOéÕd# çšJõv.ŸlåbyH€H€HÀ)L¦‡¡§mq™S%Ê‘€ t¾¿”S˜¤. @¥ ÀPŠÞE‰‡0œöæJ/ý÷7[‰›ô„vþö’Þ‘ @i `н6LÌû&~\ºW*¿€Ðñþ©–Îÿ€)ìr#  ê%0\6„@a)’SXV½D©J®k¾|•Š.í’ TÑ5`Ãþ7´ÅNXâ˜õ·q8‘;$à’ÀÐâΗ²ÙÑ%8Š“ Ljy°L©u‘pìa—CpÞ¤&ÁÂyJ 3·¦šÈŒvÄ OÓ T8ñ°lá– +2?v4±Aá]øq`Mº-–ágõÀºh;±èl ×Fjpy!¹*3ɪžcŽ ÀÿF£ Õ6IJIEND®B`‚tmate-2.4.0/logo/tmux-logo-medium.png000066400000000000000000000124301356407164200175160ustar00rootroot00000000000000‰PNG  IHDR1PqºB#sRGB®ÎéÒIDATxí] pÅ™îž]=l#Ù€õØ]í ;¹„”“: c_$‡V~Æ>0y.Ã9!>H BÀ$Ž “çæ°/§ ‚‰¹"°´ yÀ¡‹ rGŽË= °+íXòÛ²-{µÚéûz±Äìj¦g_Ò®¤ª¤íéÿï¿ÿþfæ›~M7g©‡6wîÜåœó/ão&Dµøs¥ªŒÜY{{;ÈmÞ¼yb <¿ ä·_±×0Œí»wïþ%Îö²#IbÖ¬YÓÊËËÿzÙèŽxt‰XJ¹AfÁ_ ßþ’" B€( šÌõ ½Š`ÉXQÐÈ SØ…PÛƒšáô ÔI… †IbÚ™XÍ0ç5–Ì× F&k­É—ÀX*•…m$ûÀà4ÕÀ²¼r²F6gΜ«³LFê„!P`4<Œ_.°ÍqcNÓ´/›ÂRA E@’˜…¤#»@£$„@pÞœFAGnŒv¾¶Fåô’hSxp”9·bP*B`ô" ;¦‹6lôÂ6è9a7â @£kÅÁr%!@$V É !@"±âàN¹„@ +d† Šƒ‘Xqp§\ B @‰H2CÅA€H¬8¸S®„!P ˆÄ $™!â @$VÜ)WB€(Db’Ì„@q +î”+!@"±IfB 8‰wÊ•  „‘X€$3y#À§îœZU³£æ,X©¥…¸ÌÏû¼wbÞÞ“¢! ×£ƒYÖ3·w–ÿrìP°|õ×`¬‚³)œqÎ*ó†Øw€ÊÞÄ^­ñXïS—<ž“’¬Ê«'Ì\\Á™6¶ÎLLNæ‰äÙ‹üº±ß˜&žï;}ª%ß<óñ·èiw°r_•ÀòÅ*_b}‰í‡uFU:¹Ê|mþ¯ ¦MR¦ï‹ï S"4¼B§Å3ÍÝÛ&z–à‚x <øòÏ(ÎþË0~Mö¸ÐàBC¬†€Ên=‘÷ƒYϔ䃠“ÏÊñ`xª7 ¿µð)ecsÕ +æÊ§Qþ!¼´¼râ½ÞÖÆ;õãá{Ùr&÷Íø˜ºÓï-«àßÓ_‰W ïÁ´)aÎemlˆuô+å•“ö{['­Õ›Ã!^‰É A‹@^xYØË%*'–³>â~ õMUžeÚg!_¤ÒÉE†ë½ éþðjYZy9ú†~¯’e-“QdÉ!€‡œÀÝx>Ëá\%Î&â|"«ðW¸Éø›bú›,ã¡s–Ô•éð;¬÷‚g—¦¯:ð6ÖCß„¼S ~)¤Ÿû±¯ºñE¶MÖÕ2:¸/ä¿¥¢’¿ƒ<¿& ,£Tg”ððÔ‘­xA„ØŽ† Ù¤+ºýûcßÅËî}Uypß,ô¶ú¿¨ÒÉV65Ôèö?Q¥ƒ_ÇFìZ¶žÃz㪜 ÙøAÀÓ¸FsiíàÚæUjÎ.ózO±êÕˆ«C ç ö±‹q Ï‹€@ Wøª]-uש›5y¬4w¯è>É cµ£wšv¿ÄÜQ/C…r&ª|é*㦮æ®÷¥‘˜&åj2·i"Ûš]Îxó/ñV¶BnÙÒ¨fTqíuÈ›íldÏÙ¥îÚŠ‡³N7èó;~«Š"k­ULÛ¤ÒÉTæmó_-¯±Jþ<§;¶ è‰ A¿…F͹À&Ôdî.¸aί…í«ÒíúÚ Ñ÷µü6=]–÷9ç+¤ý¼íŒB'˜q :»•®o‹ÿ ¥Žƒ£ÄS™ÐT©¡y ѻެC$fFƒÂ9w`§@‡æj`ÛÐ|Tv §¤Éòã·™“Èþ/Üà;e? 9¾ aÁa[XYAmŽc=ÁÎÃÜÿè誦mÉ«Ù]Vö€c)7þ¡{Y÷~³/Dbf4(œ?rúDuà¨ý½ÊØRÞˆ­²©‚¿ŸáüWø‹¨Ò˜e¸Ù/L¾ùA˜žPàÙÿå88!Ä>&Ä®Á<{™Oà¼Áð_iöc¼„£Í‘§ÛNUyqMÎC³û.•ŽÌÛX„ôÊͨåuÓ›:pÍR9¤NG‘PNG0ù”Ó¹)ýH}sÛÑœ³|Ðq„/aÞÇã]ÁÈÛV~Õ‡f¹˜ëGèñºÌJž§ñïù&7Þ„¸ù)ñ¦c7ìQ!âëÍúÿ˜DƒAok`òÛœ;iàšv#D(ãø;úbâŒö^Š’O¶+=ð¾×p{W°ó5;ôø³wœ=™»PËU¨e‡O'äµrPMl$‘&X¦®mè‹õNÇ\´ïؘÌWÞüÑ=áÏc²Ûf'?@:ó cC`âcÆwݧ? †ï°#0™‡Þi×÷D>‹ZÚNyB>ûÜç|è9•ƒK:t\—o« &kÃ.®=šM³{Bu•E¶Ås…!מ¸Ç*o"1+T(®`à|ÏH—`4i]Æ3à1÷g_Od jn¯ääˆïÄ cv´©ãÉi™YÏú£áÈ*odJooÏEûtìÍÚfæóDB6ݲ:Ðìx¶·çøÌýÍoe•P*¯fñ„kÓ &k€ãõ},ÑAYÓUwx^ð|B¥!¿Yu¹„œ.c{àú¯¨^k«Á¨ékoo·œ¤*ÉŠˆ€÷£é˜×èdtAôß1­áwx_žQI gWsÇëøŽò7¨|Þ>O®|8íÓ É`ç;Àh=:â7Ú–H~âå.“% ]eC^æþ!®í´¡’Á˜8ä_e+ÙéÁ‹ÕÄ,@¡¨<ì™èžÈÍyZI&7 ñB&vðÆÞ F¾•‰®³2–š†lvzªÝÑs¦÷Dîæoª<Æ‹`®7Ôxƒ•ޝÕ7̦ž¶!Ø=Væ!m‰Y!Lq9#€æÜîhOâ†~­œ˜,“~±—u#²É,ßøfs™„EjŠƒ³sÒñ!ú ¹¿ }žý¾ÛÛæõ§è´° ÁÝÉA€”øÔ“W£=aÔÔœQÓœœ7o^AnPgH²Ó fn^ñøb¶\wê/IKdïgz…ê.,vòÔ±/°¥,fo%;I<ï¬(+·M„~ Û)¶‰Æ àLsÿŸP´Û튇&gH‘Ó'èø´Æuè­?à<ý5¼^ô‡®ÈtŦۦsBÀ}‘~ÐQ) …ƒ]TêX,~téÑ£*leŒ¢Úw\£¶IÏÍP£zäNàñ¿JŒ9_€•.’Y½-Þ qÍnUê3~+RmÓd€.† – 1,ŽW¼`5»âø?B¹¢Ó]0ã:9—K™#×î¯ÙUSÏ\eaÊ„mݵ°Ñ–ÕÇöDbJäIHNì v¼ Û¢ÒC³²¦Ü=a/šâ(ôŽÆâÆJÈÕ„˜f€H, :%ìˆ'¾ƒÊX§:¥ý¬|™NˆÄ7rYêšHL:I B ’Ÿqöõ TmTÄÓz°óI¡2šHL  B S¢M‘Ð|*SýA=¹ºÈÉDÎH$6ˆ$B _ŒDìFtòÊÆNÂ0VE—E³Jc¶O$fFƒÂ„!]ó» “?‹/'Ä–®ù­ùdJ$–z”– † €•.ž‘…††D½¿;vËè,#ˆÄ²ŒÔ B #¿yÑÕ¹j+?ž‘5…‘˜„@ö$÷dbƒSJ|;éÂçG2,i’‰©Ð!!@d@9[°IÌÙ™$”“_=”«Å:Ù!sBˆä„!1Ø´å«ø¬hQÆ  ˆ}I×ùvù>–M³.‘˜ „@ÎÈo#A(÷å` R¸]?C:T̲?ˆÄ²ÇŒR„€e® ›ÑŒÌiÁHÔÞ>ƒ½CW[˜uŒ"s„ˆBÀ O[ãWÐQ¿ÄN3ùÿì´ÒV8Ú˜ËNRDbv¨S¬tQ]Q®É³:ˆÄ²‚‹” #°UØâˆ›sWNÀìŠf$÷èMú÷ÅN:®t!¼mþ«³)‘X6hKK0«²–[_©ä$+"ÉZˆ}þS¦L©´—æ/©{ÁÿÉü­Ø[+¶¢*¹ÔNÍȈèë¿KÊ“+æf´Ò…ö@u¨!ã¾5"1;ôK(7Âw¦:ÈI\$p펩²ž8aòt•<YM¨á£.·öb>6Tiëž©«eP¥ƒ]ØoÂ’å½:É•.Û>pnõ R¬­bÚ&+™U‘˜*¥§\CÞpiÓJÏeòH"À™èq@â"yNâÚÿGʘ ûg²úœ dÈ=±ò4ÿT;?µèMϦ›2ŒØMX†Z½ç+<­þ¦ô´VçDbV¨”XnD%‰a²àçJÌerç ‚;Õ¢Åà.@…ͳË?ÓíÒþ€û¦±P6Óí$û­8[–o:?Ý–ûJ&Wºàü›&]Ë vtÛ\÷xÝ$K¡)’HÌFÉ…pØùE» ߟѵ,Í ø¶ƒ[ ‡ìËè@%–äÂÝü%Ù$Séå#K6#He5­»»Fþb§³¯)ü È”Kð€„ÏsÕU8î=I7¾Ê%/v«ÜÁÅ>ß3§1¹%–JdE@À¯©rM~-ÜJBP¥”mc•ÞÿAδ§ÐÄs¬½ ¦Ë!àžTñSømß+Ä»ØÌx£“iÑÿæŽPê ¶¦®Å7[¥C$¦B§Td c“+˜§ó`>ߟ9Ù'yn`ô×R‚ûÚüw8éÙÉë[.õyo¡ùµÆN§Pñ¾P`9zú®TÙ3˜±†ÍW¨Ëôèð`îØm*[ÀFsi®­l³Ý͘HL…`‰È¢'£Â(W·Ò¬ \®ßÔ‡f)õ¬„x‹OÝé÷Z‰ÆqZdù]Ç;ÞĺYagKÚ]ÞPàŸ§üjÊgÝ4¼¡Æ‹¼mçðÿÄòWVéœfÉ[¥±‹«o©¯LÝŒÄfkÏì v†ìl¤ÇË=&á㿥ǛÏAd3¼Uµæ8s˜HÌŒF©†—³ö³zØÉ=\l¿‹»Ú}mÍèg±Ý&vxÝ®†Oá[µ[ðà„ð?\^Áïu²Oò׎9_;i×ïúI'¿‡k²±>仌áå’’ãVVßêÿ´¯Õ3Èk/ºÞ“CStRNÄÿ@oN‰ÊãÄå*G3’ÕØ™<îØaŸ–ïçþë@~ʹÀæöúP`FZÚäi^‹‘Y¤¸áA ÑÛŒ!íµ³wšá]†Ça5ge«QõïÄ›ó?9Çp¶`–µ-/ªØIVϪ˜ pÉðxNVO0cëYBû>ÄL&%OÞ­.æ¾Õçmd"Ää ̲ŸëlÈ\Ñë&ÃVå_OcéÃu–La¥•y\}KMH~•2…`ô Þ¡Ô±ÊÙühNoÀ¢<ÉI±*²ÀåXAq+°.ÁZéTû‹’u/ëÞïÉÊIÎðæ ⸯù¿Cxþ,Åt 9J–b{tž †P˜£'Øy/‰œÖ’Çõª–× ä5u€Àœ¼ÂÈàÏõc‘¿•ù2‘8é¤ï$Ç!S5ÿ©J`ýYGr®ÍGßïø1^´o©ò€l¶gv`È´ "1ÔJIÝþ>nÐöáóÉ5wøloËÑ`ÇfÔ¦žNpo$ !¾¥#+ÙrùÑuòèÍ;Ï2÷C¨ùÕªì¢ÿl5‹«t”2¤5Æ*Y•ýõ­õç™uˆÄÌh”zx=ëÅ/â­W9‘Øð›´Ú{ìøõ¼:Y ùxˆ Ö„†î3ÛœçEbžÖ†e¨*?ÈF¿Û“]ÁèïÍùæÞ· c/ú~•ŸÉé#šV¾ÅlŸHÌŒÆ(ZÔMœ<ýi¼±^w‰Ä†Ô“G–9Õ׺FÁ<Âþ £Í‘ßä5ðkhZÎ$æ{Æw.&8<<`Ëêy÷Ľ95•­ìéÇÅ:Ù»V²8Ùèä_1pN$6€Ä(ú•ýcèøœ0ÄäˆP]¿€VÄ( šV¦V²ÓøújÔ¦¿ ñQ+•Lã@ Çñ2»Mï ÿ :Ç-;Ô==9“˜˜èÆäYV§ò÷ߺ t©t²’-ï<•` YcU.Æ6%¿€‘˜ª¢AoŽÜÎO&q#߉:§ 7!ž'ñ'tªþÐÆ%˜€xª„K=’®9 æã‹Ð›Â?9ÓqíäÌvåJé!ÍA\µ{X<>ý_ÑÿeÛtäÈ‘œH Ÿ/}ýOê¯@û}=‘‡ÒýË÷\6MQÆ­J;X¿Ì5±â©Ã1 ¯cäÊ£L@B„Ž·ªÏF8âÑÞVïÇwÕí9È܃±sð{F#åÐþ!Üøáä݈ßæêb†øcoìøkG—Í«F0âkbßEÏlÿiA¼Tf<ê„à5ø=×ì®ßQôw½+¸ØkþrW8üë¼:Ñ3ÀϳËÓhw•JÕ%]xé©W£PPÈd‹ Á]Ó*IQ÷ÂŽ·9&ÖíDÇÝb'e’Eo‹çð&\2TB1„!0RhhMl©ÌÆ\>\<9æÊD"FšþjÇÔ(Þe~Ý]TûßÐww 뼟¢’ FšœÂŸèãW¢í}`ø[.¢?i"†O0Ò>( çÈ B`œ!ì^~/ÑÇ.¦™óÕ—%bl¶ÄÌY›4B`¸HFÆê Þ‹±]’Æ¿„Ñ™¨qÔfú½Öp;Z,û ­@Úe†÷b4ïIÙü¦X±®åK Eàÿ’¯²ˆpIEND®B`‚tmate-2.4.0/logo/tmux-logo-small.png000066400000000000000000000052151356407164200173510ustar00rootroot00000000000000‰PNG  IHDR™(íÊXjsRGB®Îé GIDATxí\ lÅžÙ½;ÿ; ¶÷ì5RŠTŠZ©Â8$ü“¢@TÒ6)T)$@T¹¨¤-Ai‹ÂO)mDC ¶I¡T”Ò&ö]HÓ9 mQÔŸ;ûîì8jŒ“8öùv§ß¬½{³{kÇ1¾óÝiG²æ½7oþÞ~÷fvæ­)Ajnnþ„$Iƒl¢”VrÙlRww7åõV¬XÁfSŸ×aŒ #;¦ëú½===oͶ¯^þX@šØQ€kÕØ\M‰ ?º|ùòÏU»^;óg&ü=â^êI É> ¶)|ñc{ÈÁªÆ”9',¿#ñÖh…É{yî- Á[Ìz–íáæóز=÷bj_*¦ÉxsÉO x ËÏçRT£ò@VT3?'ã,?ŸKQŠ¿]z–”’$ÑÈ"¢“uȽ”S <È”P#¿!˜2á µ<¨4ž·¸UÀ½x2ªÊt0Z¼'ºˆl'ÆÑG0ÜhÝXàDÈÍNÁß/eår¡äBè24X†vN Ù®äØùžZsêŒÕŸHàxE¹¤á*I·bË(%*rè²cŒè»­}!QÝdž~Çb­‘R7½©dJ¸q×2—™å±–ˆqKÃy%¤þöZg–ñó_…ùeSѵ¡úkd*w£ÜŸÖaû ~¹ÄƒºÄù—ž ;E LI¨ )HÊ@/p×Ì”*]j«#há~Ô»O¨U‚þ$¥Ò¶@iÅ{5ë?æ¬YjX¬RO`Ï¡¬u®@^Š| ÚúŒDå.%¬îtÖË%ŸíªN‹}b|ONn§éÓâÎÅ•25æ&ìßc£#[ dÓO}K·¼æÓT¢;¨)w9è|>©«úåÅì]éjØ  —š2·ívà w»•åBvrÍÉÆè7í}Ñ+Tý¶]–É•J v‹óÃj‘J¥ôõÜ«{ Ë´W¦ä…ú²`sãËøU9³ÐMBƒRz/J(–·Çà½v@¶%×­Ö„ŒÞGøržR¼-ò4–ῈÝã²5x0È=¯kª ©› c[f)aÛV÷ãf8q×¶óB(î)ÌaÙ9…‡z)ççàZ‰*•Ò!4µÂh°s”‘Ýš®ÿ>%É'dM«–e©ý}obùåZFº=j¬µv’ç{Á׈Î~J$íí±¤tÆ/“«%YÞï'PE©k¼=N"»Ìz¹Î“ãÚæ@@~ýN€Û &ËO€ÿ´s,µ!õ£%Îeþ¯±žÞMÝ‚™9‘låø…ro?0Æþ ëÉ;úÛû…þ ÿ`¿‰< °iä °Q€ë¶x[ô·B=Nưß9”Ô¿£Ú²tûèyÙà}ïÕu©ß—$ºÃìpC]¸qC¢%²ß”xx™’çù¾Ö’aOÇhêV²oò“É[.MK\ ‡GÜŠ·¬5€Yµâ-ÑW`ìŸY“`$©§ô–X&À&4ÚÉ\ܦ:ÏÒ‰ü|ЉhôQŒ‹{3+a\?ZøÒÂ…¦ X%ý£½Êäy®º)ÞïeYñdfð¢ØQAÓ:Ù/ă:§MºÆvÉ2Ý"*1ʾ‘XÝûª(sÒº6þº$,1£d©ÅÌq'×BÚ&‰I=“Þœƒ¿¦¬¬òÁ!2tw°K½Û,?Ä=‰Öè‹¢ŒÓž'sZÄÁÃpZ¬-bó4‹í?=Îߪ,ˆøûÑ'EÞN &þ3yvgc©5ö“nº¹”õ·ö½`=.ö ~3Þ–¿@$Š·ÉtÂyßqíäØ=iIšÊŠ'û á×é¡MPåùmB˜Œbä3>w3f¹uÂÆMDVž‡Ó¦Ã'‡Ïßç¯*¿àRy=ëQúk[Ø€_?pÛÀ9›|’ñ<™›U<™eÁuƒgu\àìNÿN¼5Â_|\“2W³xBÑým‘N,‡ü¦"#a;ñJ¬µ÷±ŒAàL0†GNmmdô:þOÔÀN% ýdÖ]¯XnÒÈLKxù´ý2$°Î¾ eJ5ydô‚Q-Ȧ5­WhZ€|?Ʀ¿ÚäyÎ4|¥{'H.qOÈÜíâI Ôu5´R_D"Ù†»Ë¯Š'íÌi·Y æ—5¸Yû¹Mè`$BºpÿjW(N1jíÌÓjû´³ÊaÉYÊz_¾ËJÀmìåæŒø¡1Þ47!OŸ‰è¾ç”?*妞˜%Èf2,L²´2T¿Xà šo°) pO3ã !&Ž2&N_“Gºb³ßa×bÏàLlÑ©m‰¯$~Ÿ3èZ” c„ž ³€Ò›D¾i\9‰ãgÕZ‘Ÿ’æ¡ßתûqd¿hJ±àIâG¤ëq%€<ËØ·¸šwF˜-ªºwÔvªŸ›átQ‚ ñöoØ'JQÂJƒ]–g~3ô¨ç“IF¨·XnЂT*Õý-°0CO/o˜¨­mxѯ·öYgeçF†7ÃÓE…jD–ÈSX6+(S^œ #ä€9AžÎÌ‘øðÞ–\RËã¤wy€áü%¬ñ3ꜰ"$J¤­Ó-™5® *êØç„jÓ’J—ò¤mv%v¿eC7 !Nn6ié³2î)ý¾}žÓÔ-JñØ.LüMs’<Çžá DÆ×:ýø*çÕ`H=†<ð•'`„}%’Ü&ê眞¡'ÿä[“¯¦ä%ÜpSu¨±Ž—ñ˜¯Ú®úë1¿çe?Åÿx›ˆù‚ü¯mÃîhh’ÅoÒ·¿Ì«_E%“ú]/êÆ˜lQ*ôJxÎïšjE 2cr,µF_ 1Ö$|]DWu× ·–Pˆ~5 “ó|Æž,õl&PèRx´—J(‰ã›‚óåU§eIþ3÷^1“s9Š«¡&.ýVè2IDÄn6ì#”¡¿}È©kãâÄRã ³ö‹ªŒ•c?÷ªE 2n„þöè»ñ£Ñ«u][‹‡ò n0<›Ó S aüwñ‡¸}ýk4¥­µ.™X[ï^|Ìòy Ñz¨®ÃåÚº¾aã ó¾@ ”HB…/“<&ÎøƒíîÂW÷<^lÚ”XˆÀ¶wBɪ‹-JMEYåNŠo ßS9m óT ãL¦jžºÏûn«¨‹*ü„?Xì'é•ÞB¸·¥ì_ø.þE’ÒöÆoŒ8&â|‹Ê#:ªÍžÈÔCpm«fßDöjÂãÂ&¾%{=x-çÂÑSü#Ôñ\tvQ} ¤K›— ÜR¼=þÓÆ›°7ÁkæÛX®çÇÇ`ŒE¿6¶:öv®û÷ú›{ ü?q4»Ó—½IEND®B`‚tmate-2.4.0/logo/tmux-logo.eps000066400000000000000000000526051356407164200162530ustar00rootroot00000000000000%!PS-Adobe-3.0 EPSF-3.0 %APL_DSC_Encoding: UTF8 %APLProducer: (Version 10.10.3 (Build 14D136) Quartz PS Context) %%Title: (Unknown) %%Creator: (Unknown) %%CreationDate: (Unknown) %%For: (Unknown) %%DocumentData: Clean7Bit %%LanguageLevel: 2 %%Pages: 1 %%BoundingBox: 0 0 608 160 %%EndComments %%BeginProlog %%BeginFile: cg-pdf.ps %%Copyright: Copyright 2000-2004 Apple Computer Incorporated. %%Copyright: All Rights Reserved. currentpacking true setpacking /cg_md 141 dict def cg_md begin /L3? languagelevel 3 ge def /bd{bind def}bind def /ld{load def}bd /xs{exch store}bd /xd{exch def}bd /cmmtx matrix def mark /sc/setcolor /scs/setcolorspace /dr/defineresource /fr/findresource /T/true /F/false /d/setdash /w/setlinewidth /J/setlinecap /j/setlinejoin /M/setmiterlimit /i/setflat /rc/rectclip /rf/rectfill /rs/rectstroke /f/fill /f*/eofill /sf/selectfont /s/show /xS/xshow /yS/yshow /xyS/xyshow /S/stroke /m/moveto /l/lineto /c/curveto /h/closepath /n/newpath /q/gsave /Q/grestore counttomark 2 idiv {ld}repeat pop /SC{ /ColorSpace fr scs }bd /sopr /setoverprint where{pop/setoverprint}{/pop}ifelse ld /soprm /setoverprintmode where{pop/setoverprintmode}{/pop}ifelse ld /cgmtx matrix def /sdmtx{cgmtx currentmatrix pop}bd /CM {cgmtx setmatrix}bd /cm {cmmtx astore CM concat}bd /W{clip newpath}bd /W*{eoclip newpath}bd statusdict begin product end dup (HP) anchorsearch{ pop pop pop true }{ pop (hp) anchorsearch{ pop pop true }{ pop false }ifelse }ifelse { { { pop pop (0)dup 0 4 -1 roll put F charpath }cshow } }{ {F charpath} }ifelse /cply exch bd /cps {cply stroke}bd /pgsave 0 def /bp{/pgsave save store}bd /ep{pgsave restore showpage}def /re{4 2 roll m 1 index 0 rlineto 0 exch rlineto neg 0 rlineto h}bd /scrdict 10 dict def /scrmtx matrix def /patarray 0 def /createpat{patarray 3 1 roll put}bd /makepat{ scrmtx astore pop gsave initgraphics CM patarray exch get scrmtx makepattern grestore setpattern }bd /cg_BeginEPSF{ userdict save/cg_b4_Inc_state exch put userdict/cg_endepsf/cg_EndEPSF load put count userdict/cg_op_count 3 -1 roll put countdictstack dup array dictstack userdict/cg_dict_array 3 -1 roll put 3 sub{end}repeat /showpage {} def 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin 10 setmiterlimit [] 0 setdash newpath false setstrokeadjust false setoverprint }bd /cg_EndEPSF{ countdictstack 3 sub { end } repeat cg_dict_array 3 1 index length 3 sub getinterval {begin}forall count userdict/cg_op_count get sub{pop}repeat userdict/cg_b4_Inc_state get restore F setpacking }bd /cg_biproc{currentfile/RunLengthDecode filter}bd /cg_aiproc{currentfile/ASCII85Decode filter/RunLengthDecode filter}bd /ImageDataSource 0 def L3?{ /cg_mibiproc{pop pop/ImageDataSource{cg_biproc}def}bd /cg_miaiproc{pop pop/ImageDataSource{cg_aiproc}def}bd }{ /ImageBandMask 0 def /ImageBandData 0 def /cg_mibiproc{ string/ImageBandMask xs string/ImageBandData xs /ImageDataSource{[currentfile/RunLengthDecode filter dup ImageBandMask/readstring cvx /pop cvx dup ImageBandData/readstring cvx/pop cvx]cvx bind}bd }bd /cg_miaiproc{ string/ImageBandMask xs string/ImageBandData xs /ImageDataSource{[currentfile/ASCII85Decode filter/RunLengthDecode filter dup ImageBandMask/readstring cvx /pop cvx dup ImageBandData/readstring cvx/pop cvx]cvx bind}bd }bd }ifelse /imsave 0 def /BI{save/imsave xd mark}bd /EI{imsave restore}bd /ID{ counttomark 2 idiv dup 2 add dict begin {def} repeat pop /ImageType 1 def /ImageMatrix[Width 0 0 Height neg 0 Height]def currentdict dup/ImageMask known{ImageMask}{F}ifelse exch L3?{ dup/MaskedImage known { pop << /ImageType 3 /InterleaveType 2 /DataDict currentdict /MaskDict << /ImageType 1 /Width Width /Height Height /ImageMatrix ImageMatrix /BitsPerComponent 1 /Decode [0 1] currentdict/Interpolate known {/Interpolate Interpolate}if >> >> }if }if exch {imagemask}{image}ifelse end }bd /cguidfix{statusdict begin mark version end {cvr}stopped{cleartomark 0}{exch pop}ifelse 2012 lt{dup findfont dup length dict begin {1 index/FID ne 2 index/UniqueID ne and {def} {pop pop} ifelse}forall currentdict end definefont pop }{pop}ifelse }bd /t_array 0 def /t_i 0 def /t_c 1 string def /x_proc{ exch t_array t_i get add exch moveto /t_i t_i 1 add store }bd /y_proc{ t_array t_i get add moveto /t_i t_i 1 add store }bd /xy_proc{ t_array t_i 2 copy 1 add get 3 1 roll get 4 -1 roll add 3 1 roll add moveto /t_i t_i 2 add store }bd /sop 0 def /cp_proc/x_proc ld /base_charpath { /t_array xs /t_i 0 def { t_c 0 3 -1 roll put currentpoint t_c cply sop cp_proc }forall /t_array 0 def }bd /sop/stroke ld /nop{}def /xsp/base_charpath ld /ysp{/cp_proc/y_proc ld base_charpath/cp_proc/x_proc ld}bd /xysp{/cp_proc/xy_proc ld base_charpath/cp_proc/x_proc ld}bd /xmp{/sop/nop ld /cp_proc/x_proc ld base_charpath/sop/stroke ld}bd /ymp{/sop/nop ld /cp_proc/y_proc ld base_charpath/sop/stroke ld}bd /xymp{/sop/nop ld /cp_proc/xy_proc ld base_charpath/sop/stroke ld}bd /refnt{ findfont dup length dict copy dup /Encoding 4 -1 roll put definefont pop }bd /renmfont{ findfont dup length dict copy definefont pop }bd L3? dup dup{save exch}if /Range 0 def /DataSource 0 def /val 0 def /nRange 0 def /mulRange 0 def /d0 0 def /r0 0 def /di 0 def /ri 0 def /a0 0 def /a1 0 def /r1 0 def /r2 0 def /dx 0 def /Nsteps 0 def /sh3tp 0 def /ymax 0 def /ymin 0 def /xmax 0 def /xmin 0 def /setupFunEval { begin /nRange Range length 2 idiv store /mulRange [ 0 1 nRange 1 sub { 2 mul/nDim2 xd Range nDim2 get Range nDim2 1 add get 1 index sub 255 div exch }for ]store end }bd /FunEval { begin nRange mul /val xd 0 1 nRange 1 sub { dup 2 mul/nDim2 xd val add DataSource exch get mulRange nDim2 get mul mulRange nDim2 1 add get add }for end }bd /max { 2 copy lt {exch pop}{pop}ifelse }bd /sh2 { /Coords load aload pop 3 index 3 index translate 3 -1 roll sub 3 1 roll exch sub 2 copy dup mul exch dup mul add sqrt dup scale atan rotate /Function load setupFunEval clippath {pathbbox}stopped {0 0 0 0}if newpath /ymax xs /xmax xs /ymin xs /xmin xs currentdict/Extend known { /Extend load 0 get { 0/Function load FunEval sc xmin ymin xmin abs ymax ymin sub rectfill }if }if /Nsteps/Function load/Size get 0 get 1 sub store /dx 1 Nsteps div store gsave /di ymax ymin sub store /Function load 0 1 Nsteps { 1 index FunEval sc 0 ymin dx di rectfill dx 0 translate }for pop grestore currentdict/Extend known { /Extend load 1 get { Nsteps/Function load FunEval sc 1 ymin xmax 1 sub abs ymax ymin sub rectfill }if }if }bd /shp { 4 copy dup 0 gt{ 0 exch a1 a0 arc }{ pop 0 moveto }ifelse dup 0 gt{ 0 exch a0 a1 arcn }{ pop 0 lineto }ifelse fill dup 0 gt{ 0 exch a0 a1 arc }{ pop 0 moveto }ifelse dup 0 gt{ 0 exch a1 a0 arcn }{ pop 0 lineto }ifelse fill }bd /calcmaxs { xmin dup mul ymin dup mul add sqrt xmax dup mul ymin dup mul add sqrt xmin dup mul ymax dup mul add sqrt xmax dup mul ymax dup mul add sqrt max max max }bd /sh3 { /Coords load aload pop 5 index 5 index translate 3 -1 roll 6 -1 roll sub 3 -1 roll 5 -1 roll sub 2 copy dup mul exch dup mul add sqrt /dx xs 2 copy 0 ne exch 0 ne or { exch atan rotate }{ pop pop }ifelse /r2 xs /r1 xs /Function load dup/Size get 0 get 1 sub /Nsteps xs setupFunEval dx r2 add r1 lt{ 0 }{ dx r1 add r2 le { 1 }{ r1 r2 eq { 2 }{ 3 }ifelse }ifelse }ifelse /sh3tp xs clippath {pathbbox}stopped {0 0 0 0}if newpath /ymax xs /xmax xs /ymin xs /xmin xs dx dup mul r2 r1 sub dup mul sub dup 0 gt { sqrt r2 r1 sub atan /a0 exch 180 exch sub store /a1 a0 neg store }{ pop /a0 0 store /a1 360 store }ifelse currentdict/Extend known { /Extend load 0 get r1 0 gt and { 0/Function load FunEval sc { { dx 0 r1 360 0 arcn xmin ymin moveto xmax ymin lineto xmax ymax lineto xmin ymax lineto xmin ymin lineto eofill } { r1 0 gt{0 0 r1 0 360 arc fill}if } { 0 r1 xmin abs r1 add neg r1 shp } { r2 r1 gt{ 0 r1 r1 neg r2 r1 sub div dx mul 0 shp }{ 0 r1 calcmaxs dup r2 add dx mul dx r1 r2 sub sub div neg exch 1 index abs exch sub shp }ifelse } }sh3tp get exec }if }if /d0 0 store /r0 r1 store /di dx Nsteps div store /ri r2 r1 sub Nsteps div store /Function load 0 1 Nsteps { 1 index FunEval sc d0 di add r0 ri add d0 r0 shp { d0 0 r0 a1 a0 arc d0 di add 0 r0 ri add a0 a1 arcn fill d0 0 r0 a0 a1 arc d0 di add 0 r0 ri add a1 a0 arcn fill }pop /d0 d0 di add store /r0 r0 ri add store }for pop currentdict/Extend known { /Extend load 1 get r2 0 gt and { Nsteps/Function load FunEval sc { { dx 0 r2 0 360 arc fill } { dx 0 r2 360 0 arcn xmin ymin moveto xmax ymin lineto xmax ymax lineto xmin ymax lineto xmin ymin lineto eofill } { xmax abs r1 add r1 dx r1 shp } { r2 r1 gt{ calcmaxs dup r1 add dx mul dx r2 r1 sub sub div exch 1 index exch sub dx r2 shp }{ r1 neg r2 r1 sub div dx mul 0 dx r2 shp }ifelse } } sh3tp get exec }if }if }bd /sh { begin /ShadingType load dup dup 2 eq exch 3 eq or { gsave newpath /ColorSpace load scs currentdict/BBox known { /BBox load aload pop 2 index sub 3 index 3 -1 roll exch sub exch rectclip }if 2 eq {sh2}{sh3}ifelse grestore }{ pop (DEBUG: shading type unimplemented\n)print flush }ifelse end }bd {restore}if not dup{save exch}if L3?{ /sh/shfill ld /csq/clipsave ld /csQ/cliprestore ld }if {restore}if end setpacking %%EndFile %%EndProlog %%BeginSetup %%EndSetup %%Page: 1 1 %%PageBoundingBox: 0 0 608 160 %%BeginPageSetup cg_md begin bp sdmtx [ /CIEBasedABC 4 dict dup begin /WhitePoint [ 0.9505 1.0000 1.0891 ] def /DecodeABC [ { 1.0 0.0 3 -1 roll 1 index 1 index le { exch pop} { pop } ifelse 1 index 1 index ge { exch pop } { pop } ifelse < 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000001010101010101010101010101 0101010101010101010101010101010101010101010101020202020202020202 0202020202020202020202020202020202030303030303030303030303030303 0303030303030304040404040404040404040404040404040404050505050505 0505050505050505050506060606060606060606060606060607070707070707 0707070707070708080808080808080808080808090909090909090909090909 0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c 0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f10101010 1010101010111111111111111112121212121212121313131313131313141414 1414141414151515151515151616161616161616171717171717171818181818 18181919191919191a1a1a1a1a1a1a1b1b1b1b1b1b1c1c1c1c1c1c1c1d1d1d1d 1d1d1e1e1e1e1e1e1f1f1f1f1f1f202020202020212121212121222222222223 2323232323242424242425252525252526262626262727272727282828282829 292929292a2a2a2a2a2b2b2b2b2b2c2c2c2c2c2d2d2d2d2d2e2e2e2e2e2f2f2f 2f2f303030303131313131323232323333333333343434343535353535363636 36373737373838383839393939393a3a3a3a3b3b3b3b3c3c3c3c3d3d3d3d3e3e 3e3e3f3f3f3f4040404041414141424242424343434444444445454545464646 4647474748484848494949494a4a4a4b4b4b4b4c4c4c4d4d4d4d4e4e4e4f4f4f 4f50505051515151525252535353535454545555555656565657575758585859 59595a5a5a5a5b5b5b5c5c5c5d5d5d5e5e5e5f5f5f6060606061616162626263 63636464646565656666666767676868686969696a6a6a6b6b6b6c6c6d6d6d6e 6e6e6f6f6f707070717171727273737374747475757576767677777878787979 797a7a7b7b7b7c7c7c7d7d7e7e7e7f7f7f808081818182828283838484848585 86868687878888888989898a8a8b8b8b8c8c8d8d8d8e8e8f8f90909091919292 9293939494949595969697979798989999999a9a9b9b9c9c9c9d9d9e9e9f9f9f a0a0a1a1a2a2a3a3a3a4a4a5a5a6a6a6a7a7a8a8a9a9aaaaabababacacadadae aeafafb0b0b0b1b1b2b2b3b3b4b4b5b5b6b6b6b7b7b8b8b9b9bababbbbbcbcbd bdbebebebfbfc0c0c1c1c2c2c3c3c4c4c5c5c6c6c7c7c8c8c9c9cacacbcbcccc cdcdcececfcfd0d0d1d1d2d2d3d3d4d4d5d5d6d6d7d7d8d8d9d9dadadbdcdcdd dddededfdfe0e0e1e1e2e2e3e3e4e4e5e6e6e7e7e8e8e9e9eaeaebebecededee eeefeff0f0f1f1f2f3f3f4f4f5f5f6f6f7f8f8f9f9fafafbfcfcfdfdfefeffff > dup length 1 sub 3 -1 roll mul dup dup floor cvi exch ceiling cvi 3 index exch get 4 -1 roll 3 -1 roll get dup 3 1 roll sub 3 -1 roll dup floor cvi sub mul add 255 div } bind { 1.0 0.0 3 -1 roll 1 index 1 index le { exch pop} { pop } ifelse 1 index 1 index ge { exch pop } { pop } ifelse < 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000001010101010101010101010101 0101010101010101010101010101010101010101010101020202020202020202 0202020202020202020202020202020202030303030303030303030303030303 0303030303030304040404040404040404040404040404040404050505050505 0505050505050505050506060606060606060606060606060607070707070707 0707070707070708080808080808080808080808090909090909090909090909 0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c 0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f10101010 1010101010111111111111111112121212121212121313131313131313141414 1414141414151515151515151616161616161616171717171717171818181818 18181919191919191a1a1a1a1a1a1a1b1b1b1b1b1b1c1c1c1c1c1c1c1d1d1d1d 1d1d1e1e1e1e1e1e1f1f1f1f1f1f202020202020212121212121222222222223 2323232323242424242425252525252526262626262727272727282828282829 292929292a2a2a2a2a2b2b2b2b2b2c2c2c2c2c2d2d2d2d2d2e2e2e2e2e2f2f2f 2f2f303030303131313131323232323333333333343434343535353535363636 36373737373838383839393939393a3a3a3a3b3b3b3b3c3c3c3c3d3d3d3d3e3e 3e3e3f3f3f3f4040404041414141424242424343434444444445454545464646 4647474748484848494949494a4a4a4b4b4b4b4c4c4c4d4d4d4d4e4e4e4f4f4f 4f50505051515151525252535353535454545555555656565657575758585859 59595a5a5a5a5b5b5b5c5c5c5d5d5d5e5e5e5f5f5f6060606061616162626263 63636464646565656666666767676868686969696a6a6a6b6b6b6c6c6d6d6d6e 6e6e6f6f6f707070717171727273737374747475757576767677777878787979 797a7a7b7b7b7c7c7c7d7d7e7e7e7f7f7f808081818182828283838484848585 86868687878888888989898a8a8b8b8b8c8c8d8d8d8e8e8f8f90909091919292 9293939494949595969697979798989999999a9a9b9b9c9c9c9d9d9e9e9f9f9f a0a0a1a1a2a2a3a3a3a4a4a5a5a6a6a6a7a7a8a8a9a9aaaaabababacacadadae aeafafb0b0b0b1b1b2b2b3b3b4b4b5b5b6b6b6b7b7b8b8b9b9bababbbbbcbcbd bdbebebebfbfc0c0c1c1c2c2c3c3c4c4c5c5c6c6c7c7c8c8c9c9cacacbcbcccc cdcdcececfcfd0d0d1d1d2d2d3d3d4d4d5d5d6d6d7d7d8d8d9d9dadadbdcdcdd dddededfdfe0e0e1e1e2e2e3e3e4e4e5e6e6e7e7e8e8e9e9eaeaebebecededee eeefeff0f0f1f1f2f3f3f4f4f5f5f6f6f7f8f8f9f9fafafbfcfcfdfdfefeffff > dup length 1 sub 3 -1 roll mul dup dup floor cvi exch ceiling cvi 3 index exch get 4 -1 roll 3 -1 roll get dup 3 1 roll sub 3 -1 roll dup floor cvi sub mul add 255 div } bind { 1.0 0.0 3 -1 roll 1 index 1 index le { exch pop} { pop } ifelse 1 index 1 index ge { exch pop } { pop } ifelse < 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000001010101010101010101010101 0101010101010101010101010101010101010101010101020202020202020202 0202020202020202020202020202020202030303030303030303030303030303 0303030303030304040404040404040404040404040404040404050505050505 0505050505050505050506060606060606060606060606060607070707070707 0707070707070708080808080808080808080808090909090909090909090909 0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c 0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f10101010 1010101010111111111111111112121212121212121313131313131313141414 1414141414151515151515151616161616161616171717171717171818181818 18181919191919191a1a1a1a1a1a1a1b1b1b1b1b1b1c1c1c1c1c1c1c1d1d1d1d 1d1d1e1e1e1e1e1e1f1f1f1f1f1f202020202020212121212121222222222223 2323232323242424242425252525252526262626262727272727282828282829 292929292a2a2a2a2a2b2b2b2b2b2c2c2c2c2c2d2d2d2d2d2e2e2e2e2e2f2f2f 2f2f303030303131313131323232323333333333343434343535353535363636 36373737373838383839393939393a3a3a3a3b3b3b3b3c3c3c3c3d3d3d3d3e3e 3e3e3f3f3f3f4040404041414141424242424343434444444445454545464646 4647474748484848494949494a4a4a4b4b4b4b4c4c4c4d4d4d4d4e4e4e4f4f4f 4f50505051515151525252535353535454545555555656565657575758585859 59595a5a5a5a5b5b5b5c5c5c5d5d5d5e5e5e5f5f5f6060606061616162626263 63636464646565656666666767676868686969696a6a6a6b6b6b6c6c6d6d6d6e 6e6e6f6f6f707070717171727273737374747475757576767677777878787979 797a7a7b7b7b7c7c7c7d7d7e7e7e7f7f7f808081818182828283838484848585 86868687878888888989898a8a8b8b8b8c8c8d8d8d8e8e8f8f90909091919292 9293939494949595969697979798989999999a9a9b9b9c9c9c9d9d9e9e9f9f9f a0a0a1a1a2a2a3a3a3a4a4a5a5a6a6a6a7a7a8a8a9a9aaaaabababacacadadae aeafafb0b0b0b1b1b2b2b3b3b4b4b5b5b6b6b6b7b7b8b8b9b9bababbbbbcbcbd bdbebebebfbfc0c0c1c1c2c2c3c3c4c4c5c5c6c6c7c7c8c8c9c9cacacbcbcccc cdcdcececfcfd0d0d1d1d2d2d3d3d4d4d5d5d6d6d7d7d8d8d9d9dadadbdcdcdd dddededfdfe0e0e1e1e2e2e3e3e4e4e5e6e6e7e7e8e8e9e9eaeaebebecededee eeefeff0f0f1f1f2f3f3f4f4f5f5f6f6f7f8f8f9f9fafafbfcfcfdfdfefeffff > dup length 1 sub 3 -1 roll mul dup dup floor cvi exch ceiling cvi 3 index exch get 4 -1 roll 3 -1 roll get dup 3 1 roll sub 3 -1 roll dup floor cvi sub mul add 255 div } bind ] def /MatrixABC [ 0.4124 0.2126 0.0193 0.3576 0.7151 0.1192 0.1805 0.0722 0.9508 ] def /RangeLMN [ 0.0 0.9505 0.0 1.0000 0.0 1.0891 ] def end ] /Cs1 exch/ColorSpace dr pop %%EndPageSetup 0.60000002 i /Cs1 SC 0.10588235 0.72549021 0.12156863 sc q 0 44 m 160 44 l 160 15.003872 l 160 6.7174625 153.27803 0 145.00154 0 c 14.998466 0 l 6.7150416 0 0 6.7065849 0 15.003872 c 0 44 l h 0 44 m 160 44 l 160 14 l 0 14 l 0 44 l h 0 44 m W* 0 0 608 160 rc -5 49 m 165 49 l 165 -5 l -5 -5 l h f Q 0.23529412 0.23529412 0.23529412 sc q 83 90 m 83 160 l 77 160 l 77 14 l 83 14 l 83 84 l 160 84 l 160 90 l 83 90 l h 0 144.99352 m 0 153.28137 6.7219648 160 14.998466 160 c 145.00154 160 l 153.28496 160 160 153.27509 160 144.99352 c 160 14 l 0 14 l 0 144.99352 l h 0 144.99352 m W* 0 0 608 160 rc -5 165 m 165 165 l 165 9 l -5 9 l h f Q 0.10588235 0.72549021 0.12156863 sc q 241.77284 12.165432 m 230.85426 12.165432 222.63789 15.032893 217.12346 20.7679 c 211.60902 26.502909 208.85185 34.995007 208.85185 46.244446 c 208.85185 98.024689 l 189 98.024689 l 189 112.91358 l 208.85185 112.91358 l 208.85185 146 l 225.39507 146 l 225.39507 112.91358 l 261.79013 112.91358 l 261.79013 98.024689 l 225.39507 98.024689 l 225.39507 46.079014 l 225.39507 39.571983 226.99422 34.802074 230.1926 31.769136 c 233.39096 28.736198 237.58186 27.219753 242.76543 27.219753 c 245.19179 27.219753 247.89381 27.49547 250.87161 28.046913 c 253.8494 28.598356 256.88229 29.425508 259.97037 30.528395 c 263.27902 15.97037 l 259.52921 14.646907 255.86215 13.681896 252.27777 13.075309 c 248.69341 12.468721 245.19179 12.165432 241.77284 12.165432 c 241.77284 12.165432 l h 294.70239 13.654321 m 278.15918 13.654321 l 278.15918 112.91358 l 294.70239 112.91358 l 294.70239 96.370369 l 294.70239 96.370369 304.76617 106.29628 309.67401 109.60493 c 314.58185 112.9136 320.34436 114.5679 326.96167 114.5679 c 332.80695 114.5679 337.79745 112.94117 341.93326 109.68765 c 346.06909 106.43414 349.01926 101.99509 350.78387 96.370369 c 350.78387 96.370369 361.92294 106.29628 366.99622 109.60493 c 372.06952 112.9136 378.08014 114.5679 385.02832 114.5679 c 392.74854 114.5679 398.92459 111.72801 403.55673 106.04815 c 408.18884 100.36829 410.50488 92.730911 410.50488 83.135803 c 410.50488 13.654321 l 393.96167 13.654321 l 393.96167 80.488892 l 393.96167 87.106209 392.69336 91.820976 390.15674 94.633331 c 387.62009 97.445694 383.98062 98.851852 379.23822 98.851852 c 374.49579 98.851852 369.78104 97.418121 365.09375 94.550621 c 360.40649 91.683113 356.24316 87.878212 352.60364 83.135803 c 352.60364 13.654321 l 336.06042 13.654321 l 336.06042 80.488892 l 336.06042 87.106209 334.79211 91.820976 332.25549 94.633331 c 329.71884 97.445694 326.07938 98.851852 321.33698 98.851852 c 316.59457 98.851852 311.87979 97.418121 307.19254 94.550621 c 302.50525 91.683113 298.34192 87.878212 294.70239 83.135803 c 294.70239 13.654321 l h 438.9505 21.264198 m 433.10519 27.440361 430.18259 35.656738 430.18259 45.913582 c 430.18259 112.91358 l 446.7258 112.91358 l 446.7258 49.222221 l 446.7258 42.16375 448.3801 36.814835 451.68875 33.175308 c 454.99741 29.535784 459.85004 27.716049 466.2468 27.716049 c 471.32007 27.716049 476.36569 29.177351 481.38382 32.099998 c 486.40195 35.022648 491.39243 39.35141 496.35544 45.086418 c 496.35544 112.91358 l 512.89862 112.91358 l 512.89862 13.654321 l 496.35544 13.654321 l 496.35544 30.197531 l 496.35544 30.197531 485.13364 20.271622 479.89493 16.962963 c 474.65622 13.654305 468.78345 12 462.2764 12 c 452.57101 12 444.79578 15.088035 438.9505 21.264198 c h 588.65784 13.654321 m 564.50476 51.041977 l 541.84052 13.654321 l 523.4776 13.654321 l 556.56403 63.449383 l 524.63562 112.91358 l 543.164 112.91358 l 566.15906 77.180244 l 589.48499 112.91358 l 607.84796 112.91358 l 574.43066 64.441978 l 607.18622 13.654321 l 588.65784 13.654321 l h 588.65784 13.654321 m W* 0 0 608 160 rc 184 151 m 612.84796 151 l 612.84796 7 l 184 7 l h f ep end %%Trailer %%EOF tmate-2.4.0/logo/tmux-logo.svg000066400000000000000000000107201356407164200162530ustar00rootroot00000000000000 logomark + wordmark Created with Sketch. tmate-2.4.0/logo/tmux-logomark.eps000066400000000000000000000445411356407164200171260ustar00rootroot00000000000000%!PS-Adobe-3.0 EPSF-3.0 %APL_DSC_Encoding: UTF8 %APLProducer: (Version 10.10.3 (Build 14D136) Quartz PS Context) %%Title: (Unknown) %%Creator: (Unknown) %%CreationDate: (Unknown) %%For: (Unknown) %%DocumentData: Clean7Bit %%LanguageLevel: 2 %%Pages: 1 %%BoundingBox: 0 0 160 160 %%EndComments %%BeginProlog %%BeginFile: cg-pdf.ps %%Copyright: Copyright 2000-2004 Apple Computer Incorporated. %%Copyright: All Rights Reserved. currentpacking true setpacking /cg_md 141 dict def cg_md begin /L3? languagelevel 3 ge def /bd{bind def}bind def /ld{load def}bd /xs{exch store}bd /xd{exch def}bd /cmmtx matrix def mark /sc/setcolor /scs/setcolorspace /dr/defineresource /fr/findresource /T/true /F/false /d/setdash /w/setlinewidth /J/setlinecap /j/setlinejoin /M/setmiterlimit /i/setflat /rc/rectclip /rf/rectfill /rs/rectstroke /f/fill /f*/eofill /sf/selectfont /s/show /xS/xshow /yS/yshow /xyS/xyshow /S/stroke /m/moveto /l/lineto /c/curveto /h/closepath /n/newpath /q/gsave /Q/grestore counttomark 2 idiv {ld}repeat pop /SC{ /ColorSpace fr scs }bd /sopr /setoverprint where{pop/setoverprint}{/pop}ifelse ld /soprm /setoverprintmode where{pop/setoverprintmode}{/pop}ifelse ld /cgmtx matrix def /sdmtx{cgmtx currentmatrix pop}bd /CM {cgmtx setmatrix}bd /cm {cmmtx astore CM concat}bd /W{clip newpath}bd /W*{eoclip newpath}bd statusdict begin product end dup (HP) anchorsearch{ pop pop pop true }{ pop (hp) anchorsearch{ pop pop true }{ pop false }ifelse }ifelse { { { pop pop (0)dup 0 4 -1 roll put F charpath }cshow } }{ {F charpath} }ifelse /cply exch bd /cps {cply stroke}bd /pgsave 0 def /bp{/pgsave save store}bd /ep{pgsave restore showpage}def /re{4 2 roll m 1 index 0 rlineto 0 exch rlineto neg 0 rlineto h}bd /scrdict 10 dict def /scrmtx matrix def /patarray 0 def /createpat{patarray 3 1 roll put}bd /makepat{ scrmtx astore pop gsave initgraphics CM patarray exch get scrmtx makepattern grestore setpattern }bd /cg_BeginEPSF{ userdict save/cg_b4_Inc_state exch put userdict/cg_endepsf/cg_EndEPSF load put count userdict/cg_op_count 3 -1 roll put countdictstack dup array dictstack userdict/cg_dict_array 3 -1 roll put 3 sub{end}repeat /showpage {} def 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin 10 setmiterlimit [] 0 setdash newpath false setstrokeadjust false setoverprint }bd /cg_EndEPSF{ countdictstack 3 sub { end } repeat cg_dict_array 3 1 index length 3 sub getinterval {begin}forall count userdict/cg_op_count get sub{pop}repeat userdict/cg_b4_Inc_state get restore F setpacking }bd /cg_biproc{currentfile/RunLengthDecode filter}bd /cg_aiproc{currentfile/ASCII85Decode filter/RunLengthDecode filter}bd /ImageDataSource 0 def L3?{ /cg_mibiproc{pop pop/ImageDataSource{cg_biproc}def}bd /cg_miaiproc{pop pop/ImageDataSource{cg_aiproc}def}bd }{ /ImageBandMask 0 def /ImageBandData 0 def /cg_mibiproc{ string/ImageBandMask xs string/ImageBandData xs /ImageDataSource{[currentfile/RunLengthDecode filter dup ImageBandMask/readstring cvx /pop cvx dup ImageBandData/readstring cvx/pop cvx]cvx bind}bd }bd /cg_miaiproc{ string/ImageBandMask xs string/ImageBandData xs /ImageDataSource{[currentfile/ASCII85Decode filter/RunLengthDecode filter dup ImageBandMask/readstring cvx /pop cvx dup ImageBandData/readstring cvx/pop cvx]cvx bind}bd }bd }ifelse /imsave 0 def /BI{save/imsave xd mark}bd /EI{imsave restore}bd /ID{ counttomark 2 idiv dup 2 add dict begin {def} repeat pop /ImageType 1 def /ImageMatrix[Width 0 0 Height neg 0 Height]def currentdict dup/ImageMask known{ImageMask}{F}ifelse exch L3?{ dup/MaskedImage known { pop << /ImageType 3 /InterleaveType 2 /DataDict currentdict /MaskDict << /ImageType 1 /Width Width /Height Height /ImageMatrix ImageMatrix /BitsPerComponent 1 /Decode [0 1] currentdict/Interpolate known {/Interpolate Interpolate}if >> >> }if }if exch {imagemask}{image}ifelse end }bd /cguidfix{statusdict begin mark version end {cvr}stopped{cleartomark 0}{exch pop}ifelse 2012 lt{dup findfont dup length dict begin {1 index/FID ne 2 index/UniqueID ne and {def} {pop pop} ifelse}forall currentdict end definefont pop }{pop}ifelse }bd /t_array 0 def /t_i 0 def /t_c 1 string def /x_proc{ exch t_array t_i get add exch moveto /t_i t_i 1 add store }bd /y_proc{ t_array t_i get add moveto /t_i t_i 1 add store }bd /xy_proc{ t_array t_i 2 copy 1 add get 3 1 roll get 4 -1 roll add 3 1 roll add moveto /t_i t_i 2 add store }bd /sop 0 def /cp_proc/x_proc ld /base_charpath { /t_array xs /t_i 0 def { t_c 0 3 -1 roll put currentpoint t_c cply sop cp_proc }forall /t_array 0 def }bd /sop/stroke ld /nop{}def /xsp/base_charpath ld /ysp{/cp_proc/y_proc ld base_charpath/cp_proc/x_proc ld}bd /xysp{/cp_proc/xy_proc ld base_charpath/cp_proc/x_proc ld}bd /xmp{/sop/nop ld /cp_proc/x_proc ld base_charpath/sop/stroke ld}bd /ymp{/sop/nop ld /cp_proc/y_proc ld base_charpath/sop/stroke ld}bd /xymp{/sop/nop ld /cp_proc/xy_proc ld base_charpath/sop/stroke ld}bd /refnt{ findfont dup length dict copy dup /Encoding 4 -1 roll put definefont pop }bd /renmfont{ findfont dup length dict copy definefont pop }bd L3? dup dup{save exch}if /Range 0 def /DataSource 0 def /val 0 def /nRange 0 def /mulRange 0 def /d0 0 def /r0 0 def /di 0 def /ri 0 def /a0 0 def /a1 0 def /r1 0 def /r2 0 def /dx 0 def /Nsteps 0 def /sh3tp 0 def /ymax 0 def /ymin 0 def /xmax 0 def /xmin 0 def /setupFunEval { begin /nRange Range length 2 idiv store /mulRange [ 0 1 nRange 1 sub { 2 mul/nDim2 xd Range nDim2 get Range nDim2 1 add get 1 index sub 255 div exch }for ]store end }bd /FunEval { begin nRange mul /val xd 0 1 nRange 1 sub { dup 2 mul/nDim2 xd val add DataSource exch get mulRange nDim2 get mul mulRange nDim2 1 add get add }for end }bd /max { 2 copy lt {exch pop}{pop}ifelse }bd /sh2 { /Coords load aload pop 3 index 3 index translate 3 -1 roll sub 3 1 roll exch sub 2 copy dup mul exch dup mul add sqrt dup scale atan rotate /Function load setupFunEval clippath {pathbbox}stopped {0 0 0 0}if newpath /ymax xs /xmax xs /ymin xs /xmin xs currentdict/Extend known { /Extend load 0 get { 0/Function load FunEval sc xmin ymin xmin abs ymax ymin sub rectfill }if }if /Nsteps/Function load/Size get 0 get 1 sub store /dx 1 Nsteps div store gsave /di ymax ymin sub store /Function load 0 1 Nsteps { 1 index FunEval sc 0 ymin dx di rectfill dx 0 translate }for pop grestore currentdict/Extend known { /Extend load 1 get { Nsteps/Function load FunEval sc 1 ymin xmax 1 sub abs ymax ymin sub rectfill }if }if }bd /shp { 4 copy dup 0 gt{ 0 exch a1 a0 arc }{ pop 0 moveto }ifelse dup 0 gt{ 0 exch a0 a1 arcn }{ pop 0 lineto }ifelse fill dup 0 gt{ 0 exch a0 a1 arc }{ pop 0 moveto }ifelse dup 0 gt{ 0 exch a1 a0 arcn }{ pop 0 lineto }ifelse fill }bd /calcmaxs { xmin dup mul ymin dup mul add sqrt xmax dup mul ymin dup mul add sqrt xmin dup mul ymax dup mul add sqrt xmax dup mul ymax dup mul add sqrt max max max }bd /sh3 { /Coords load aload pop 5 index 5 index translate 3 -1 roll 6 -1 roll sub 3 -1 roll 5 -1 roll sub 2 copy dup mul exch dup mul add sqrt /dx xs 2 copy 0 ne exch 0 ne or { exch atan rotate }{ pop pop }ifelse /r2 xs /r1 xs /Function load dup/Size get 0 get 1 sub /Nsteps xs setupFunEval dx r2 add r1 lt{ 0 }{ dx r1 add r2 le { 1 }{ r1 r2 eq { 2 }{ 3 }ifelse }ifelse }ifelse /sh3tp xs clippath {pathbbox}stopped {0 0 0 0}if newpath /ymax xs /xmax xs /ymin xs /xmin xs dx dup mul r2 r1 sub dup mul sub dup 0 gt { sqrt r2 r1 sub atan /a0 exch 180 exch sub store /a1 a0 neg store }{ pop /a0 0 store /a1 360 store }ifelse currentdict/Extend known { /Extend load 0 get r1 0 gt and { 0/Function load FunEval sc { { dx 0 r1 360 0 arcn xmin ymin moveto xmax ymin lineto xmax ymax lineto xmin ymax lineto xmin ymin lineto eofill } { r1 0 gt{0 0 r1 0 360 arc fill}if } { 0 r1 xmin abs r1 add neg r1 shp } { r2 r1 gt{ 0 r1 r1 neg r2 r1 sub div dx mul 0 shp }{ 0 r1 calcmaxs dup r2 add dx mul dx r1 r2 sub sub div neg exch 1 index abs exch sub shp }ifelse } }sh3tp get exec }if }if /d0 0 store /r0 r1 store /di dx Nsteps div store /ri r2 r1 sub Nsteps div store /Function load 0 1 Nsteps { 1 index FunEval sc d0 di add r0 ri add d0 r0 shp { d0 0 r0 a1 a0 arc d0 di add 0 r0 ri add a0 a1 arcn fill d0 0 r0 a0 a1 arc d0 di add 0 r0 ri add a1 a0 arcn fill }pop /d0 d0 di add store /r0 r0 ri add store }for pop currentdict/Extend known { /Extend load 1 get r2 0 gt and { Nsteps/Function load FunEval sc { { dx 0 r2 0 360 arc fill } { dx 0 r2 360 0 arcn xmin ymin moveto xmax ymin lineto xmax ymax lineto xmin ymax lineto xmin ymin lineto eofill } { xmax abs r1 add r1 dx r1 shp } { r2 r1 gt{ calcmaxs dup r1 add dx mul dx r2 r1 sub sub div exch 1 index exch sub dx r2 shp }{ r1 neg r2 r1 sub div dx mul 0 dx r2 shp }ifelse } } sh3tp get exec }if }if }bd /sh { begin /ShadingType load dup dup 2 eq exch 3 eq or { gsave newpath /ColorSpace load scs currentdict/BBox known { /BBox load aload pop 2 index sub 3 index 3 -1 roll exch sub exch rectclip }if 2 eq {sh2}{sh3}ifelse grestore }{ pop (DEBUG: shading type unimplemented\n)print flush }ifelse end }bd {restore}if not dup{save exch}if L3?{ /sh/shfill ld /csq/clipsave ld /csQ/cliprestore ld }if {restore}if end setpacking %%EndFile %%EndProlog %%BeginSetup %%EndSetup %%Page: 1 1 %%PageBoundingBox: 0 0 160 160 %%BeginPageSetup cg_md begin bp sdmtx [ /CIEBasedABC 4 dict dup begin /WhitePoint [ 0.9505 1.0000 1.0891 ] def /DecodeABC [ { 1.0 0.0 3 -1 roll 1 index 1 index le { exch pop} { pop } ifelse 1 index 1 index ge { exch pop } { pop } ifelse < 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000001010101010101010101010101 0101010101010101010101010101010101010101010101020202020202020202 0202020202020202020202020202020202030303030303030303030303030303 0303030303030304040404040404040404040404040404040404050505050505 0505050505050505050506060606060606060606060606060607070707070707 0707070707070708080808080808080808080808090909090909090909090909 0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c 0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f10101010 1010101010111111111111111112121212121212121313131313131313141414 1414141414151515151515151616161616161616171717171717171818181818 18181919191919191a1a1a1a1a1a1a1b1b1b1b1b1b1c1c1c1c1c1c1c1d1d1d1d 1d1d1e1e1e1e1e1e1f1f1f1f1f1f202020202020212121212121222222222223 2323232323242424242425252525252526262626262727272727282828282829 292929292a2a2a2a2a2b2b2b2b2b2c2c2c2c2c2d2d2d2d2d2e2e2e2e2e2f2f2f 2f2f303030303131313131323232323333333333343434343535353535363636 36373737373838383839393939393a3a3a3a3b3b3b3b3c3c3c3c3d3d3d3d3e3e 3e3e3f3f3f3f4040404041414141424242424343434444444445454545464646 4647474748484848494949494a4a4a4b4b4b4b4c4c4c4d4d4d4d4e4e4e4f4f4f 4f50505051515151525252535353535454545555555656565657575758585859 59595a5a5a5a5b5b5b5c5c5c5d5d5d5e5e5e5f5f5f6060606061616162626263 63636464646565656666666767676868686969696a6a6a6b6b6b6c6c6d6d6d6e 6e6e6f6f6f707070717171727273737374747475757576767677777878787979 797a7a7b7b7b7c7c7c7d7d7e7e7e7f7f7f808081818182828283838484848585 86868687878888888989898a8a8b8b8b8c8c8d8d8d8e8e8f8f90909091919292 9293939494949595969697979798989999999a9a9b9b9c9c9c9d9d9e9e9f9f9f a0a0a1a1a2a2a3a3a3a4a4a5a5a6a6a6a7a7a8a8a9a9aaaaabababacacadadae aeafafb0b0b0b1b1b2b2b3b3b4b4b5b5b6b6b6b7b7b8b8b9b9bababbbbbcbcbd bdbebebebfbfc0c0c1c1c2c2c3c3c4c4c5c5c6c6c7c7c8c8c9c9cacacbcbcccc cdcdcececfcfd0d0d1d1d2d2d3d3d4d4d5d5d6d6d7d7d8d8d9d9dadadbdcdcdd dddededfdfe0e0e1e1e2e2e3e3e4e4e5e6e6e7e7e8e8e9e9eaeaebebecededee eeefeff0f0f1f1f2f3f3f4f4f5f5f6f6f7f8f8f9f9fafafbfcfcfdfdfefeffff > dup length 1 sub 3 -1 roll mul dup dup floor cvi exch ceiling cvi 3 index exch get 4 -1 roll 3 -1 roll get dup 3 1 roll sub 3 -1 roll dup floor cvi sub mul add 255 div } bind { 1.0 0.0 3 -1 roll 1 index 1 index le { exch pop} { pop } ifelse 1 index 1 index ge { exch pop } { pop } ifelse < 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000001010101010101010101010101 0101010101010101010101010101010101010101010101020202020202020202 0202020202020202020202020202020202030303030303030303030303030303 0303030303030304040404040404040404040404040404040404050505050505 0505050505050505050506060606060606060606060606060607070707070707 0707070707070708080808080808080808080808090909090909090909090909 0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c 0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f10101010 1010101010111111111111111112121212121212121313131313131313141414 1414141414151515151515151616161616161616171717171717171818181818 18181919191919191a1a1a1a1a1a1a1b1b1b1b1b1b1c1c1c1c1c1c1c1d1d1d1d 1d1d1e1e1e1e1e1e1f1f1f1f1f1f202020202020212121212121222222222223 2323232323242424242425252525252526262626262727272727282828282829 292929292a2a2a2a2a2b2b2b2b2b2c2c2c2c2c2d2d2d2d2d2e2e2e2e2e2f2f2f 2f2f303030303131313131323232323333333333343434343535353535363636 36373737373838383839393939393a3a3a3a3b3b3b3b3c3c3c3c3d3d3d3d3e3e 3e3e3f3f3f3f4040404041414141424242424343434444444445454545464646 4647474748484848494949494a4a4a4b4b4b4b4c4c4c4d4d4d4d4e4e4e4f4f4f 4f50505051515151525252535353535454545555555656565657575758585859 59595a5a5a5a5b5b5b5c5c5c5d5d5d5e5e5e5f5f5f6060606061616162626263 63636464646565656666666767676868686969696a6a6a6b6b6b6c6c6d6d6d6e 6e6e6f6f6f707070717171727273737374747475757576767677777878787979 797a7a7b7b7b7c7c7c7d7d7e7e7e7f7f7f808081818182828283838484848585 86868687878888888989898a8a8b8b8b8c8c8d8d8d8e8e8f8f90909091919292 9293939494949595969697979798989999999a9a9b9b9c9c9c9d9d9e9e9f9f9f a0a0a1a1a2a2a3a3a3a4a4a5a5a6a6a6a7a7a8a8a9a9aaaaabababacacadadae aeafafb0b0b0b1b1b2b2b3b3b4b4b5b5b6b6b6b7b7b8b8b9b9bababbbbbcbcbd bdbebebebfbfc0c0c1c1c2c2c3c3c4c4c5c5c6c6c7c7c8c8c9c9cacacbcbcccc cdcdcececfcfd0d0d1d1d2d2d3d3d4d4d5d5d6d6d7d7d8d8d9d9dadadbdcdcdd dddededfdfe0e0e1e1e2e2e3e3e4e4e5e6e6e7e7e8e8e9e9eaeaebebecededee eeefeff0f0f1f1f2f3f3f4f4f5f5f6f6f7f8f8f9f9fafafbfcfcfdfdfefeffff > dup length 1 sub 3 -1 roll mul dup dup floor cvi exch ceiling cvi 3 index exch get 4 -1 roll 3 -1 roll get dup 3 1 roll sub 3 -1 roll dup floor cvi sub mul add 255 div } bind { 1.0 0.0 3 -1 roll 1 index 1 index le { exch pop} { pop } ifelse 1 index 1 index ge { exch pop } { pop } ifelse < 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000001010101010101010101010101 0101010101010101010101010101010101010101010101020202020202020202 0202020202020202020202020202020202030303030303030303030303030303 0303030303030304040404040404040404040404040404040404050505050505 0505050505050505050506060606060606060606060606060607070707070707 0707070707070708080808080808080808080808090909090909090909090909 0a0a0a0a0a0a0a0a0a0a0a0b0b0b0b0b0b0b0b0b0b0b0c0c0c0c0c0c0c0c0c0c 0d0d0d0d0d0d0d0d0d0d0e0e0e0e0e0e0e0e0e0f0f0f0f0f0f0f0f0f10101010 1010101010111111111111111112121212121212121313131313131313141414 1414141414151515151515151616161616161616171717171717171818181818 18181919191919191a1a1a1a1a1a1a1b1b1b1b1b1b1c1c1c1c1c1c1c1d1d1d1d 1d1d1e1e1e1e1e1e1f1f1f1f1f1f202020202020212121212121222222222223 2323232323242424242425252525252526262626262727272727282828282829 292929292a2a2a2a2a2b2b2b2b2b2c2c2c2c2c2d2d2d2d2d2e2e2e2e2e2f2f2f 2f2f303030303131313131323232323333333333343434343535353535363636 36373737373838383839393939393a3a3a3a3b3b3b3b3c3c3c3c3d3d3d3d3e3e 3e3e3f3f3f3f4040404041414141424242424343434444444445454545464646 4647474748484848494949494a4a4a4b4b4b4b4c4c4c4d4d4d4d4e4e4e4f4f4f 4f50505051515151525252535353535454545555555656565657575758585859 59595a5a5a5a5b5b5b5c5c5c5d5d5d5e5e5e5f5f5f6060606061616162626263 63636464646565656666666767676868686969696a6a6a6b6b6b6c6c6d6d6d6e 6e6e6f6f6f707070717171727273737374747475757576767677777878787979 797a7a7b7b7b7c7c7c7d7d7e7e7e7f7f7f808081818182828283838484848585 86868687878888888989898a8a8b8b8b8c8c8d8d8d8e8e8f8f90909091919292 9293939494949595969697979798989999999a9a9b9b9c9c9c9d9d9e9e9f9f9f a0a0a1a1a2a2a3a3a3a4a4a5a5a6a6a6a7a7a8a8a9a9aaaaabababacacadadae aeafafb0b0b0b1b1b2b2b3b3b4b4b5b5b6b6b6b7b7b8b8b9b9bababbbbbcbcbd bdbebebebfbfc0c0c1c1c2c2c3c3c4c4c5c5c6c6c7c7c8c8c9c9cacacbcbcccc cdcdcececfcfd0d0d1d1d2d2d3d3d4d4d5d5d6d6d7d7d8d8d9d9dadadbdcdcdd dddededfdfe0e0e1e1e2e2e3e3e4e4e5e6e6e7e7e8e8e9e9eaeaebebecededee eeefeff0f0f1f1f2f3f3f4f4f5f5f6f6f7f8f8f9f9fafafbfcfcfdfdfefeffff > dup length 1 sub 3 -1 roll mul dup dup floor cvi exch ceiling cvi 3 index exch get 4 -1 roll 3 -1 roll get dup 3 1 roll sub 3 -1 roll dup floor cvi sub mul add 255 div } bind ] def /MatrixABC [ 0.4124 0.2126 0.0193 0.3576 0.7151 0.1192 0.1805 0.0722 0.9508 ] def /RangeLMN [ 0.0 0.9505 0.0 1.0000 0.0 1.0891 ] def end ] /Cs1 exch/ColorSpace dr pop %%EndPageSetup 0.60000002 i /Cs1 SC 0.10588235 0.72549021 0.12156863 sc q 0 44 m 160 44 l 160 15.003872 l 160 6.7174625 153.27803 0 145.00154 0 c 14.998466 0 l 6.7150416 0 0 6.7065849 0 15.003872 c 0 44 l h 0 44 m 160 44 l 160 14 l 0 14 l 0 44 l h 0 44 m W* 0 0 160 160 rc -5 49 m 165 49 l 165 -5 l -5 -5 l h f Q 0.23529412 0.23529412 0.23529412 sc q 83 90 m 83 160 l 77 160 l 77 14 l 83 14 l 83 84 l 160 84 l 160 90 l 83 90 l h 0 144.99352 m 0 153.28137 6.7219648 160 14.998466 160 c 145.00154 160 l 153.28496 160 160 153.27509 160 144.99352 c 160 14 l 0 14 l 0 144.99352 l h 0 144.99352 m W* 0 0 160 160 rc -5 165 m 165 165 l 165 9 l -5 9 l h f ep end %%Trailer %%EOF tmate-2.4.0/logo/tmux-logomark.svg000066400000000000000000000023671356407164200171360ustar00rootroot00000000000000 logomark copy Created with Sketch. tmate-2.4.0/mdoc2man.awk000066400000000000000000000206401356407164200150450ustar00rootroot00000000000000#!/usr/bin/awk # # $Id: mdoc2man.awk,v 1.9 2009/10/24 00:52:42 dtucker Exp $ # # Version history: # v4+ Adapted for OpenSSH Portable (see cvs Id and history) # v3, I put the program under a proper license # Dan Nelson added .An, .Aq and fixed a typo # v2, fixed to work on GNU awk --posix and MacOS X # v1, first attempt, didn't work on MacOS X # # Copyright (c) 2003 Peter Stuge # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. BEGIN { optlist=0 oldoptlist=0 nospace=0 synopsis=0 reference=0 block=0 ext=0 extopt=0 literal=0 prenl=0 breakw=0 line="" } function wtail() { retval="" while(w0;i--) { add(refauthors[i]) if(i>1) add(", ") } if(nrefauthors>1) add(" and ") if(nrefauthors>0) add(refauthors[0] ", ") add("\\fI" reftitle "\\fP") if(length(refissue)) add(", " refissue) if(length(refreport)) { add(", " refreport) } if(length(refdate)) add(", " refdate) if(length(refopt)) add(", " refopt) add(".") reference=0 } else if(reference) { if(match(words[w],"^%A$")) { refauthors[nrefauthors++]=wtail() } if(match(words[w],"^%T$")) { reftitle=wtail() sub("^\"","",reftitle) sub("\"$","",reftitle) } if(match(words[w],"^%N$")) { refissue=wtail() } if(match(words[w],"^%D$")) { refdate=wtail() } if(match(words[w],"^%O$")) { refopt=wtail() } if(match(words[w],"^%R$")) { refreport=wtail() } } else if(match(words[w],"^Nm$")) { if(synopsis) { add(".br") prenl++ } n=words[++w] if(!length(name)) name=n if(!length(n)) n=name add("\\fB" n "\\fP") if(!nospace&&match(words[w+1],"^[\\.,]")) nospace=1 } else if(match(words[w],"^Nd$")) { add("\\- " wtail()) } else if(match(words[w],"^Fl$")) { add("\\fB\\-" words[++w] "\\fP") if(!nospace&&match(words[w+1],"^[\\.,]")) nospace=1 } else if(match(words[w],"^Ar$")) { add("\\fI") if(w==nwords) add("file ...\\fP") else { add(words[++w] "\\fP") while(match(words[w+1],"^\\|$")) add(OFS words[++w] " \\fI" words[++w] "\\fP") } if(!nospace&&match(words[w+1],"^[\\.,]")) nospace=1 } else if(match(words[w],"^Cm$")) { add("\\fB" words[++w] "\\fP") while(w") if(option) add("]") if(ext&&!extopt&&!match(line," $")) add(OFS) if(!ext&&!extopt&&length(line)) { print line prenl=0 line="" } } tmate-2.4.0/mode-key.c000066400000000000000000000620121356407164200145160ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * 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 * (any matching MODEKEYEDIT_SWITCHMODE*) are special-cased to do this. */ /* Entry in the default mode key tables. */ struct mode_key_entry { key_code 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; }; /* 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_SWITCHMODECHANGELINE, "switch-mode-change-line" }, { MODEKEYEDIT_SWITCHMODESUBSTITUTE, "switch-mode-substitute" }, { MODEKEYEDIT_SWITCHMODESUBSTITUTELINE, "switch-mode-substitute-line" }, { MODEKEYEDIT_TRANSPOSECHARS, "transpose-chars" }, { 0, NULL } }; /* Choice keys command strings. */ const struct mode_key_cmdstr mode_key_cmdstr_choice[] = { { MODEKEYCHOICE_BACKSPACE, "backspace" }, { MODEKEYCHOICE_BOTTOMLINE, "bottom-line"}, { MODEKEYCHOICE_CANCEL, "cancel" }, { MODEKEYCHOICE_CHOOSE, "choose" }, { MODEKEYCHOICE_DOWN, "down" }, { MODEKEYCHOICE_ENDOFLIST, "end-of-list"}, { MODEKEYCHOICE_PAGEDOWN, "page-down" }, { MODEKEYCHOICE_PAGEUP, "page-up" }, { MODEKEYCHOICE_SCROLLDOWN, "scroll-down" }, { MODEKEYCHOICE_SCROLLUP, "scroll-up" }, { MODEKEYCHOICE_STARTNUMBERPREFIX, "start-number-prefix" }, { MODEKEYCHOICE_STARTOFLIST, "start-of-list"}, { MODEKEYCHOICE_TOPLINE, "top-line"}, { 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_APPENDSELECTION, "append-selection" }, { 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_OTHEREND, "other-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_STARTNAMEDBUFFER, "start-named-buffer" }, { 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 }, { '\n', 0, MODEKEYEDIT_ENTER }, { '\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 }, { 'C', 1, MODEKEYEDIT_SWITCHMODECHANGELINE }, { 'D', 1, MODEKEYEDIT_DELETETOENDOFLINE }, { 'E', 1, MODEKEYEDIT_NEXTSPACEEND }, { 'I', 1, MODEKEYEDIT_SWITCHMODEBEGINLINE }, { 'S', 1, MODEKEYEDIT_SWITCHMODESUBSTITUTELINE }, { 'W', 1, MODEKEYEDIT_NEXTSPACE }, { 'X', 1, MODEKEYEDIT_BACKSPACE }, { '\003' /* C-c */, 1, MODEKEYEDIT_CANCEL }, { '\010' /* C-h */, 1, MODEKEYEDIT_BACKSPACE }, { '\n', 1, MODEKEYEDIT_ENTER }, { '\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 }, { 's', 1, MODEKEYEDIT_SWITCHMODESUBSTITUTE }, { '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 }, { '\n', 0, MODEKEYCHOICE_CHOOSE }, { '\r', 0, MODEKEYCHOICE_CHOOSE }, { 'j', 0, MODEKEYCHOICE_DOWN }, { 'k', 0, MODEKEYCHOICE_UP }, { 'q', 0, MODEKEYCHOICE_CANCEL }, { KEYC_HOME, 0, MODEKEYCHOICE_STARTOFLIST }, { 'g', 0, MODEKEYCHOICE_STARTOFLIST }, { 'H', 0, MODEKEYCHOICE_TOPLINE }, { 'L', 0, MODEKEYCHOICE_BOTTOMLINE }, { 'G', 0, MODEKEYCHOICE_ENDOFLIST }, { KEYC_END, 0, MODEKEYCHOICE_ENDOFLIST }, { 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 }, { KEYC_MOUSEDOWN1_PANE, 0, MODEKEYCHOICE_CHOOSE }, { KEYC_MOUSEDOWN3_PANE, 0, MODEKEYCHOICE_TREE_TOGGLE }, { KEYC_WHEELUP_PANE, 0, MODEKEYCHOICE_UP }, { KEYC_WHEELDOWN_PANE, 0, MODEKEYCHOICE_DOWN }, { 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_STARTNAMEDBUFFER }, { '$', 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 }, { 'A', 0, MODEKEYCOPY_APPENDSELECTION }, { '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 }, { 'V', 0, MODEKEYCOPY_SELECTLINE }, { '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 }, { '\n', 0, MODEKEYCOPY_COPYSELECTION }, { '\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 }, { 'o', 0, MODEKEYCOPY_OTHEREND }, { '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 }, { KEYC_WHEELUP_PANE, 0, MODEKEYCOPY_SCROLLUP }, { KEYC_WHEELDOWN_PANE, 0, MODEKEYCOPY_SCROLLDOWN }, { KEYC_MOUSEDRAG1_PANE, 0, MODEKEYCOPY_STARTSELECTION }, { KEYC_MOUSEDRAGEND1_PANE, 0, MODEKEYCOPY_COPYSELECTION }, { 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 }, { '\n', 0, MODEKEYEDIT_ENTER }, { '\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 }, { '\n', 0, MODEKEYCHOICE_CHOOSE }, { '\r', 0, MODEKEYCHOICE_CHOOSE }, { 'q', 0, MODEKEYCHOICE_CANCEL }, { 'v' | KEYC_ESCAPE, 0, MODEKEYCHOICE_PAGEUP }, { KEYC_HOME, 0, MODEKEYCHOICE_STARTOFLIST }, { '<' | KEYC_ESCAPE, 0, MODEKEYCHOICE_STARTOFLIST }, { 'R' | KEYC_ESCAPE, 0, MODEKEYCHOICE_TOPLINE }, { '>' | KEYC_ESCAPE, 0, MODEKEYCHOICE_ENDOFLIST }, { KEYC_END, 0, MODEKEYCHOICE_ENDOFLIST }, { 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 }, { KEYC_MOUSEDOWN1_PANE, 0, MODEKEYCHOICE_CHOOSE }, { KEYC_MOUSEDOWN3_PANE, 0, MODEKEYCHOICE_TREE_TOGGLE }, { KEYC_WHEELUP_PANE, 0, MODEKEYCHOICE_UP }, { KEYC_WHEELDOWN_PANE, 0, MODEKEYCHOICE_DOWN }, { 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 }, { KEYC_WHEELUP_PANE, 0, MODEKEYCOPY_SCROLLUP }, { KEYC_WHEELDOWN_PANE, 0, MODEKEYCOPY_SCROLLDOWN }, { KEYC_MOUSEDRAG1_PANE, 0, MODEKEYCOPY_STARTSELECTION }, { KEYC_MOUSEDRAGEND1_PANE, 0, MODEKEYCOPY_COPYSELECTION }, { 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 (-1); if (mbind1->mode > mbind2->mode) return (1); if (mbind1->key < mbind2->key) return (-1); if (mbind1->key > mbind2->key) return (1); return (0); } 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, key_code 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: case MODEKEYEDIT_SWITCHMODECHANGELINE: case MODEKEYEDIT_SWITCHMODESUBSTITUTE: case MODEKEYEDIT_SWITCHMODESUBSTITUTELINE: mdata->mode = 1 - mdata->mode; /* FALLTHROUGH */ default: if (arg != NULL) *arg = mbind->arg; return (mbind->cmd); } } tmate-2.4.0/names.c000066400000000000000000000074621356407164200141170ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include "tmux.h" void name_time_callback(int, short, void *); int name_time_expired(struct window *, struct timeval *); void name_time_callback(__unused int fd, __unused short events, void *arg) { struct window *w = arg; /* The event loop will call check_window_name for us on the way out. */ log_debug("@%u name timer expired", w->id); } int name_time_expired(struct window *w, struct timeval *tv) { struct timeval offset; timersub(tv, &w->name_time, &offset); if (offset.tv_sec != 0 || offset.tv_usec > NAME_INTERVAL) return (0); return (NAME_INTERVAL - offset.tv_usec); } void check_window_name(struct window *w) { struct timeval tv, next; char *name; int left; if (w->active == NULL) return; if (!options_get_number(w->options, "automatic-rename")) return; if (~w->active->flags & PANE_CHANGED) { log_debug("@%u active pane not changed", w->id); return; } log_debug("@%u active pane changed", w->id); gettimeofday(&tv, NULL); left = name_time_expired(w, &tv); if (left != 0) { if (!event_initialized(&w->name_event)) evtimer_set(&w->name_event, name_time_callback, w); if (!evtimer_pending(&w->name_event, NULL)) { log_debug("@%u name timer queued (%d left)", w->id, left); timerclear(&next); next.tv_usec = left; event_add(&w->name_event, &next); } else log_debug("@%u name timer already queued (%d left)", w->id, left); return; } memcpy(&w->name_time, &tv, sizeof w->name_time); if (event_initialized(&w->name_event)) evtimer_del(&w->name_event); w->active->flags &= ~PANE_CHANGED; name = format_window_name(w); if (strcmp(name, w->name) != 0) { log_debug("@%u new name %s (was %s)", w->id, name, w->name); window_set_name(w, name); server_status_window(w); } else log_debug("@%u name not changed (still %s)", w->id, w->name); free(name); } char * default_window_name(struct window *w) { char *cmd, *s; cmd = cmd_stringify_argv(w->active->argc, w->active->argv); if (cmd != NULL && *cmd != '\0') s = parse_window_name(cmd); else s = parse_window_name(w->active->shell); free(cmd); return (s); } char * format_window_name(struct window *w) { struct format_tree *ft; char *fmt, *name; ft = format_create(NULL, 0); format_defaults_window(ft, w); format_defaults_pane(ft, w->active); fmt = options_get_string(w->options, "automatic-rename-format"); name = format_expand(ft, fmt); format_free(ft); return (name); } char * parse_window_name(const char *in) { char *copy, *name, *ptr; name = copy = xstrdup(in); if (strncmp(name, "exec ", (sizeof "exec ") - 1) == 0) name = name + (sizeof "exec ") - 1; while (*name == ' ' || *name == '-') name++; if ((ptr = strchr(name, ' ')) != NULL) *ptr = '\0'; if (*name != '\0') { ptr = name + strlen(name) - 1; while (ptr > name && !isalnum((u_char)*ptr)) *ptr-- = '\0'; } if (*name == '/') name = basename(name); name = xstrdup(name); free(copy); return (name); } tmate-2.4.0/notify.c000066400000000000000000000106541356407164200143210ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2012 George Nachman * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "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) server_client_unref(ne->client); if (ne->session != NULL) session_unref(ne->session); 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; /* * notify_input() is not queued and only does anything when * notifications are enabled. */ if (!notify_enabled) return; TAILQ_FOREACH(c, &clients, entry) { if (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-2.4.0/options-table.c000066400000000000000000000611761356407164200155760ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2011 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * This file has a tables with all the server, session and window * options. These tables are the master copy of the options with their real * (user-visible) types, range limits and default values. At start these are * copied into the runtime global options trees (which only has number and * string types). These tables are then used to look up the real type when the * user sets an option or its value needs to be shown. */ /* Choice option type lists. */ const char *options_table_mode_keys_list[] = { "emacs", "vi", NULL }; const char *options_table_clock_mode_style_list[] = { "12", "24", NULL }; const char *options_table_status_keys_list[] = { "emacs", "vi", NULL }; const char *options_table_status_justify_list[] = { "left", "centre", "right", NULL }; const char *options_table_status_position_list[] = { "top", "bottom", NULL }; const char *options_table_bell_action_list[] = { "none", "any", "current", "other", NULL }; /* Server options. */ const struct options_table_entry options_table[] = { { .name = "buffer-limit", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SERVER, .minimum = 1, .maximum = INT_MAX, .default_num = 20 }, { .name = "default-terminal", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, #ifdef TMATE .default_str = "screen-256color" #else .default_str = "screen" #endif }, { .name = "escape-time", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SERVER, .minimum = 0, .maximum = INT_MAX, .default_num = 500 }, { .name = "exit-unattached", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SERVER, .default_num = 0 }, { .name = "focus-events", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SERVER, .default_num = 0 }, { .name = "history-file", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, .default_str = "" }, { .name = "message-limit", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SERVER, .minimum = 0, .maximum = INT_MAX, #ifdef TMATE .default_num = 500 #else .default_num = 100 #endif }, { .name = "quiet", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SERVER, .default_num = 0 }, { .name = "set-clipboard", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SERVER, .default_num = 1 }, { .name = "terminal-overrides", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, .default_str = "xterm*:XT:Ms=\\E]52;%p1%s;%p2%s\\007" ":Cs=\\E]12;%p1%s\\007:Cr=\\E]112\\007" ":Ss=\\E[%p1%d q:Se=\\E[2 q,screen*:XT" }, { .name = "assume-paste-time", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, .default_num = 1, }, { .name = "base-index", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, .default_num = 0 }, { .name = "bell-action", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_bell_action_list, .default_num = BELL_ANY }, { .name = "bell-on-alert", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "default-command", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, .default_str = "" }, { .name = "default-shell", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, .default_str = _PATH_BSHELL }, { .name = "destroy-unattached", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "detach-on-destroy", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SESSION, .default_num = 1 }, { .name = "display-panes-active-colour", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_SESSION, .default_num = 1 }, { .name = "display-panes-colour", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_SESSION, .default_num = 4 }, { .name = "display-panes-time", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SESSION, .minimum = 1, .maximum = INT_MAX, .default_num = 1000 }, { .name = "display-time", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, .default_num = 750 }, { .name = "history-limit", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, .default_num = 2000 }, { .name = "key-table", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, .default_str = "root" }, { .name = "lock-after-time", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, .default_num = 0 }, { .name = "lock-command", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, .default_str = "lock -np" }, { .name = "message-attr", .type = OPTIONS_TABLE_ATTRIBUTES, .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "message-style" }, { .name = "message-bg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_SESSION, .default_num = 3, .style = "message-style" }, { .name = "message-command-attr", .type = OPTIONS_TABLE_ATTRIBUTES, .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "message-command-style" }, { .name = "message-command-bg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "message-command-style" }, { .name = "message-command-fg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_SESSION, .default_num = 3, .style = "message-command-style" }, { .name = "message-command-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_SESSION, .default_str = "bg=black,fg=yellow" }, { .name = "message-fg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "message-style" }, { .name = "message-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_SESSION, .default_str = "bg=yellow,fg=black" }, { .name = "mouse", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "prefix", .type = OPTIONS_TABLE_KEY, .scope = OPTIONS_TABLE_SESSION, .default_num = '\002', }, { .name = "prefix2", .type = OPTIONS_TABLE_KEY, .scope = OPTIONS_TABLE_SESSION, .default_num = KEYC_NONE, }, { .name = "renumber-windows", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "repeat-time", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = SHRT_MAX, .default_num = 500 }, { .name = "set-remain-on-exit", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "set-titles", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "set-titles-string", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, .default_str = "#S:#I:#W - \"#T\" #{session_alerts}" }, { .name = "status", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SESSION, .default_num = 1 }, { .name = "status-attr", .type = OPTIONS_TABLE_ATTRIBUTES, .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "status-style" }, { .name = "status-bg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_SESSION, .default_num = 2, .style = "status-style" }, { .name = "status-fg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "status-style" }, { .name = "status-interval", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = INT_MAX, .default_num = 15 }, { .name = "status-justify", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_status_justify_list, .default_num = 0 }, { .name = "status-keys", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_status_keys_list, .default_num = MODEKEY_EMACS }, { .name = "status-left", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, .default_str = "[#S] " }, { .name = "status-left-attr", .type = OPTIONS_TABLE_ATTRIBUTES, .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "status-left-style" }, { .name = "status-left-bg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_SESSION, .default_num = 8, .style = "status-left-style" }, { .name = "status-left-fg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_SESSION, .default_num = 8, .style = "status-left-style" }, { .name = "status-left-length", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = SHRT_MAX, .default_num = 10 }, { .name = "status-left-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_SESSION, .default_str = "default" }, { .name = "status-position", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SESSION, .choices = options_table_status_position_list, .default_num = 1 }, { .name = "status-right", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, .default_str = " \"#{=21:pane_title}\" %H:%M %d-%b-%y" }, { .name = "status-right-attr", .type = OPTIONS_TABLE_ATTRIBUTES, .scope = OPTIONS_TABLE_SESSION, .default_num = 0, .style = "status-right-style" }, { .name = "status-right-bg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_SESSION, .default_num = 8, .style = "status-right-style" }, { .name = "status-right-fg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_SESSION, .default_num = 8, .style = "status-right-style" }, { .name = "status-right-length", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SESSION, .minimum = 0, .maximum = SHRT_MAX, .default_num = 40 }, { .name = "status-right-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_SESSION, .default_str = "default" }, { .name = "status-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_SESSION, .default_str = "bg=green,fg=black" }, { .name = "update-environment", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, .default_str = "DISPLAY SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID " "SSH_CONNECTION WINDOWID XAUTHORITY" }, { .name = "visual-activity", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "visual-bell", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "visual-silence", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SESSION, .default_num = 0 }, { .name = "word-separators", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, .default_str = " -_@" }, { .name = "aggressive-resize", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, .default_num = 0 }, { .name = "allow-rename", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, .default_num = 1 }, { .name = "alternate-screen", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, .default_num = 1 }, { .name = "automatic-rename", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, .default_num = 1 }, { .name = "automatic-rename-format", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, .default_str = "#{?pane_in_mode,[tmux],#{pane_current_command}}" "#{?pane_dead,[dead],}" }, { .name = "clock-mode-colour", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_WINDOW, .default_num = 4 }, { .name = "clock-mode-style", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_WINDOW, .choices = options_table_clock_mode_style_list, .default_num = 1 }, { .name = "force-height", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_WINDOW, .minimum = 0, .maximum = INT_MAX, .default_num = 0 }, { .name = "force-width", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_WINDOW, .minimum = 0, .maximum = INT_MAX, .default_num = 0 }, { .name = "main-pane-height", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_WINDOW, .minimum = 1, .maximum = INT_MAX, .default_num = 24 }, { .name = "main-pane-width", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_WINDOW, .minimum = 1, .maximum = INT_MAX, .default_num = 80 }, { .name = "mode-attr", .type = OPTIONS_TABLE_ATTRIBUTES, .scope = OPTIONS_TABLE_WINDOW, .default_num = 0, .style = "mode-style" }, { .name = "mode-bg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_WINDOW, .default_num = 3, .style = "mode-style" }, { .name = "mode-fg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_WINDOW, .default_num = 0, .style = "mode-style" }, { .name = "mode-keys", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_WINDOW, .choices = options_table_mode_keys_list, .default_num = MODEKEY_EMACS }, { .name = "mode-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_WINDOW, .default_str = "bg=yellow,fg=black" }, { .name = "monitor-activity", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, .default_num = 0 }, { .name = "monitor-silence", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_WINDOW, .minimum = 0, .maximum = INT_MAX, .default_num = 0 }, { .name = "other-pane-height", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_WINDOW, .minimum = 0, .maximum = INT_MAX, .default_num = 0 }, { .name = "other-pane-width", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_WINDOW, .minimum = 0, .maximum = INT_MAX, .default_num = 0 }, { .name = "pane-active-border-bg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "pane-active-border-style" }, { .name = "pane-active-border-fg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_WINDOW, .default_num = 2, .style = "pane-active-border-style" }, { .name = "pane-active-border-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_WINDOW, .default_str = "fg=green" }, { .name = "pane-base-index", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_WINDOW, .minimum = 0, .maximum = USHRT_MAX, .default_num = 0 }, { .name = "pane-border-bg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "pane-border-style" }, { .name = "pane-border-fg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "pane-border-style" }, { .name = "pane-border-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_WINDOW, .default_str = "default" }, { .name = "remain-on-exit", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, .default_num = 0 }, { .name = "synchronize-panes", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, .default_num = 0 }, { .name = "window-active-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_WINDOW, .default_str = "default" }, { .name = "window-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_WINDOW, .default_str = "default" }, { .name = "window-status-activity-attr", .type = OPTIONS_TABLE_ATTRIBUTES, .scope = OPTIONS_TABLE_WINDOW, .default_num = GRID_ATTR_REVERSE, .style = "window-status-activity-style" }, { .name = "window-status-activity-bg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "window-status-activity-style" }, { .name = "window-status-activity-fg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "window-status-activity-style" }, { .name = "window-status-activity-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_WINDOW, .default_str = "reverse" }, { .name = "window-status-attr", .type = OPTIONS_TABLE_ATTRIBUTES, .scope = OPTIONS_TABLE_WINDOW, .default_num = 0, .style = "window-status-style" }, { .name = "window-status-bell-attr", .type = OPTIONS_TABLE_ATTRIBUTES, .scope = OPTIONS_TABLE_WINDOW, .default_num = GRID_ATTR_REVERSE, .style = "window-status-bell-style" }, { .name = "window-status-bell-bg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "window-status-bell-style" }, { .name = "window-status-bell-fg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "window-status-bell-style" }, { .name = "window-status-bell-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_WINDOW, .default_str = "reverse" }, { .name = "window-status-bg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "window-status-style" }, { .name = "window-status-current-attr", .type = OPTIONS_TABLE_ATTRIBUTES, .scope = OPTIONS_TABLE_WINDOW, .default_num = 0, .style = "window-status-current-style" }, { .name = "window-status-current-bg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "window-status-current-style" }, { .name = "window-status-current-fg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "window-status-current-style" }, { .name = "window-status-current-format", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, .default_str = "#I:#W#{?window_flags,#{window_flags}, }" }, { .name = "window-status-current-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_WINDOW, .default_str = "default" }, { .name = "window-status-fg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "window-status-style" }, { .name = "window-status-format", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, .default_str = "#I:#W#{?window_flags,#{window_flags}, }" }, { .name = "window-status-last-attr", .type = OPTIONS_TABLE_ATTRIBUTES, .scope = OPTIONS_TABLE_WINDOW, .default_num = 0, .style = "window-status-last-style" }, { .name = "window-status-last-bg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "window-status-last-style" }, { .name = "window-status-last-fg", .type = OPTIONS_TABLE_COLOUR, .scope = OPTIONS_TABLE_WINDOW, .default_num = 8, .style = "window-status-last-style" }, { .name = "window-status-last-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_WINDOW, .default_str = "default" }, { .name = "window-status-separator", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW, .default_str = " " }, { .name = "window-status-style", .type = OPTIONS_TABLE_STYLE, .scope = OPTIONS_TABLE_WINDOW, .default_str = "default" }, { .name = "wrap-search", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, .default_num = 1 }, { .name = "xterm-keys", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_WINDOW, .default_num = 0 }, #ifdef TMATE { .name = "tmate-identity", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, .default_str = "" }, { .name = "tmate-server-host", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, .default_str = "ssh.tmate.io" }, { .name = "tmate-server-port", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SERVER, .minimum = 1, .maximum = 65535, .default_num = 22 }, { .name = "tmate-server-dsa-fingerprint", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, .default_str = "obsolete" }, { .name = "tmate-server-rsa-fingerprint", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, .default_str = "SHA256:Hthk2T/M/Ivqfk1YYUn5ijC2Att3+UPzD7Rn72P5VWs" }, { .name = "tmate-server-ecdsa-fingerprint", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, .default_str = "SHA256:8GmKHYHEJ6n0TEdciHeEGkKOigQfCFuBULdt6vZIhDc" }, { .name = "tmate-server-ed25519-fingerprint", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, .default_str = "SHA256:jfttvoypkHiQYUqUCwKeqd9d1fJj/ZiQlFOHVl6E9sI" }, { .name = "tmate-display-time", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SESSION, .minimum = 1, .maximum = INT_MAX, .default_num = 15000 }, { .name = "tmate-webhook-userdata", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, .default_str = "" }, { .name = "tmate-webhook-url", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, .default_str = "" }, { .name = "tmate-api-key", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, .default_str = "" }, { .name = "tmate-session-name", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, .default_str = "" }, { .name = "tmate-session-name-ro", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, .default_str = "" }, { .name = "tmate-authorized-keys", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, .default_str = "" }, { .name = "tmate-set", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, .default_str = "" }, { .name = "tmate-foreground-restart", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SERVER, .minimum = 0, .maximum = 1, .default_num = 1 }, #endif { .name = NULL } }; /* Populate an options tree from a table. */ void options_table_populate_tree(enum options_table_scope scope, struct options *oo) { const struct options_table_entry *oe; for (oe = options_table; oe->name != NULL; oe++) { if (oe->scope == OPTIONS_TABLE_NONE) fatalx("no scope for %s", oe->name); if (oe->scope != scope) continue; switch (oe->type) { case OPTIONS_TABLE_STRING: options_set_string(oo, oe->name, "%s", oe->default_str); break; case OPTIONS_TABLE_STYLE: options_set_style(oo, oe->name, oe->default_str, 0); break; default: options_set_number(oo, oe->name, oe->default_num); break; } } } /* 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; case OPTIONS_TABLE_STYLE: s = style_tostring(&o->style); xsnprintf(out, sizeof out, "%s", s); break; } return (out); } /* Find an option. */ int options_table_find(const char *optstr, const struct options_table_entry **oe) { const struct options_table_entry *oe_loop; for (oe_loop = options_table; 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; /* Bail now if an exact match. */ if (strcmp(oe_loop->name, optstr) == 0) break; } return (0); } tmate-2.4.0/options.c000066400000000000000000000123171356407164200145020ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" /* * Option handling; each option has a name, type and value and is stored in * a red-black tree. */ struct options { RB_HEAD(options_tree, options_entry) tree; struct options *parent; }; static int options_cmp(struct options_entry *, struct options_entry *); RB_PROTOTYPE(options_tree, options_entry, entry, options_cmp); RB_GENERATE(options_tree, options_entry, entry, options_cmp); static void options_free1(struct options *, struct options_entry *); static int options_cmp(struct options_entry *o1, struct options_entry *o2) { return (strcmp(o1->name, o2->name)); } struct options * options_create(struct options *parent) { struct options *oo; oo = xcalloc(1, sizeof *oo); RB_INIT(&oo->tree); oo->parent = parent; return (oo); } static void options_free1(struct options *oo, struct options_entry *o) { RB_REMOVE(options_tree, &oo->tree, o); free((char *)o->name); if (o->type == OPTIONS_STRING) free(o->str); free(o); } void options_free(struct options *oo) { struct options_entry *o, *o1; RB_FOREACH_SAFE (o, options_tree, &oo->tree, o1) options_free1(oo, o); free(oo); } struct options_entry * options_first(struct options *oo) { return (RB_MIN(options_tree, &oo->tree)); } struct options_entry * options_next(struct options_entry *o) { return (RB_NEXT(options_tree, &oo->tree, o)); } struct options_entry * options_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) options_free1(oo, o); } struct options_entry * 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); memcpy(&o->style, &grid_default_cell, sizeof o->style); } 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 %s", name); if (o->type != OPTIONS_STRING) fatalx("option %s not a string", name); 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); memcpy(&o->style, &grid_default_cell, sizeof o->style); } 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 %s", name); if (o->type != OPTIONS_NUMBER) fatalx("option %s not a number", name); return (o->num); } struct options_entry * options_set_style(struct options *oo, const char *name, const char *value, int append) { struct options_entry *o; struct grid_cell tmpgc; o = options_find1(oo, name); if (o == NULL || !append) memcpy(&tmpgc, &grid_default_cell, sizeof tmpgc); else memcpy(&tmpgc, &o->style, sizeof tmpgc); if (style_parse(&grid_default_cell, &tmpgc, value) == -1) return (NULL); if (o == 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_STYLE; memcpy(&o->style, &tmpgc, sizeof o->style); return (o); } struct grid_cell * options_get_style(struct options *oo, const char *name) { struct options_entry *o; if ((o = options_find(oo, name)) == NULL) fatalx("missing option %s", name); if (o->type != OPTIONS_STYLE) fatalx("option %s not a style", name); return (&o->style); } tmate-2.4.0/osdep-aix.c000066400000000000000000000043411356407164200146760ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2011 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include "tmux.h" char * osdep_get_name(__unused int fd, char *tty) { struct psinfo p; char *path; ssize_t bytes; int f, ttyfd, retval; pid_t pgrp; if ((ttyfd = open(tty, O_RDONLY|O_NOCTTY)) == -1) return (NULL); retval = ioctl(ttyfd, TIOCGPGRP, &pgrp); close(ttyfd); if (retval == -1) return (NULL); xasprintf(&path, "/proc/%u/psinfo", (u_int) pgrp); f = open(path, O_RDONLY); free(path); if (f < 0) return (NULL); bytes = read(f, &p, sizeof(p)); close(f); if (bytes != sizeof(p)) return (NULL); return (xstrdup(p.pr_fname)); } char * osdep_get_cwd(int fd) { static char target[MAXPATHLEN + 1]; char *path; const char *ttypath; ssize_t n; pid_t pgrp; int len, retval, ttyfd; if ((ttypath = ptsname(fd)) == NULL) return (NULL); if ((ttyfd = open(ttypath, O_RDONLY|O_NOCTTY)) == -1) return (NULL); retval = ioctl(ttyfd, TIOCGPGRP, &pgrp); close(ttyfd); if (retval == -1) return (NULL); xasprintf(&path, "/proc/%u/cwd", (u_int) pgrp); n = readlink(path, target, MAXPATHLEN); free(path); if (n > 0) { target[n] = '\0'; if ((len = strlen(target)) > 1 && target[len - 1] == '/') target[len - 1] = '\0'; return (target); } return (NULL); } struct event_base * osdep_event_init(void) { return (event_init()); } tmate-2.4.0/osdep-cygwin.c000066400000000000000000000036011356407164200154130ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "tmux.h" char * osdep_get_name(int fd, __unused char *tty) { FILE *f; char *path, *buf; size_t len; int ch; pid_t pgrp; if ((pgrp = tcgetpgrp(fd)) == -1) return (NULL); xasprintf(&path, "/proc/%lld/cmdline", (long long) pgrp); if ((f = fopen(path, "r")) == NULL) { free(path); return (NULL); } free(path); len = 0; buf = NULL; while ((ch = fgetc(f)) != EOF) { if (ch == '\0') break; buf = xrealloc(buf, len + 2); buf[len++] = ch; } if (buf != NULL) buf[len] = '\0'; fclose(f); return (buf); } char * osdep_get_cwd(int fd) { static char target[MAXPATHLEN + 1]; char *path; pid_t pgrp; ssize_t n; if ((pgrp = tcgetpgrp(fd)) == -1) return (NULL); xasprintf(&path, "/proc/%lld/cwd", (long long) pgrp); n = readlink(path, target, MAXPATHLEN); free(path); if (n > 0) { target[n] = '\0'; return (target); } return (NULL); } struct event_base * osdep_event_init(void) { return (event_init()); } tmate-2.4.0/osdep-darwin.c000066400000000000000000000041461356407164200154040ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Joshua Elsasser * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include char *osdep_get_name(int, char *); char *osdep_get_cwd(int); struct event_base *osdep_event_init(void); #ifndef __unused #define __unused __attribute__ ((__unused__)) #endif char * osdep_get_name(int fd, __unused char *tty) { struct proc_bsdinfo bsdinfo; pid_t pgrp; int ret; if ((pgrp = tcgetpgrp(fd)) == -1) return (NULL); ret = proc_pidinfo(pgrp, PROC_PIDTBSDINFO, 0, &bsdinfo, sizeof bsdinfo); if (ret == sizeof bsdinfo && *bsdinfo.pbi_comm != '\0') return (strdup(bsdinfo.pbi_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-2.4.0/osdep-dragonfly.c000066400000000000000000000057231356407164200161070ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include struct kinfo_proc *cmp_procs(struct kinfo_proc *, struct kinfo_proc *); char *osdep_get_name(int, char *); char *osdep_get_cwd(int); struct event_base *osdep_event_init(void); #ifndef nitems #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) #endif #define is_runnable(p) \ ((p)->kp_stat == SACTIVE || (p)->kp_stat == SIDL) #define is_stopped(p) \ ((p)->kp_stat == SSTOP || (p)->kp_stat == SZOMB) struct kinfo_proc * cmp_procs(struct kinfo_proc *p1, struct kinfo_proc *p2) { if (is_runnable(p1) && !is_runnable(p2)) return (p1); if (!is_runnable(p1) && is_runnable(p2)) return (p2); if (is_stopped(p1) && !is_stopped(p2)) return (p1); if (!is_stopped(p1) && is_stopped(p2)) return (p2); if (strcmp(p1->kp_comm, p2->kp_comm) < 0) return (p1); if (strcmp(p1->kp_comm, p2->kp_comm) > 0) return (p2); if (p1->kp_pid > p2->kp_pid) return (p1); return (p2); } char * osdep_get_name(int fd, char *tty) { int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PGRP, 0 }; struct stat sb; size_t len; struct kinfo_proc *buf, *newbuf, *bestp; u_int i; char *name; buf = NULL; if (stat(tty, &sb) == -1) return (NULL); if ((mib[3] = tcgetpgrp(fd)) == -1) return (NULL); retry: if (sysctl(mib, nitems(mib), NULL, &len, NULL, 0) == -1) return (NULL); len = (len * 5) / 4; if ((newbuf = realloc(buf, len)) == NULL) goto error; buf = newbuf; if (sysctl(mib, nitems(mib), buf, &len, NULL, 0) == -1) { if (errno == ENOMEM) goto retry; goto error; } bestp = NULL; for (i = 0; i < len / sizeof (struct kinfo_proc); i++) { if (buf[i].kp_tdev != sb.st_rdev) continue; if (bestp == NULL) bestp = &buf[i]; else bestp = cmp_procs(&buf[i], bestp); } name = NULL; if (bestp != NULL) name = strdup(bestp->kp_comm); free(buf); return (name); error: free(buf); return (NULL); } char * osdep_get_cwd(int fd) { return (NULL); } struct event_base * osdep_event_init(void) { return (event_init()); } tmate-2.4.0/osdep-freebsd.c000066400000000000000000000106001356407164200155220ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include struct kinfo_proc *cmp_procs(struct kinfo_proc *, struct kinfo_proc *); char *osdep_get_name(int, char *); char *osdep_get_cwd(int); struct event_base *osdep_event_init(void); #ifndef nitems #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) #endif #define is_runnable(p) \ ((p)->ki_stat == SRUN || (p)->ki_stat == SIDL) #define is_stopped(p) \ ((p)->ki_stat == SSTOP || (p)->ki_stat == SZOMB) struct kinfo_proc * cmp_procs(struct kinfo_proc *p1, struct kinfo_proc *p2) { if (is_runnable(p1) && !is_runnable(p2)) return (p1); if (!is_runnable(p1) && is_runnable(p2)) return (p2); if (is_stopped(p1) && !is_stopped(p2)) return (p1); if (!is_stopped(p1) && is_stopped(p2)) return (p2); if (p1->ki_estcpu > p2->ki_estcpu) return (p1); if (p1->ki_estcpu < p2->ki_estcpu) return (p2); if (p1->ki_slptime < p2->ki_slptime) return (p1); if (p1->ki_slptime > p2->ki_slptime) return (p2); if (strcmp(p1->ki_comm, p2->ki_comm) < 0) return (p1); if (strcmp(p1->ki_comm, p2->ki_comm) > 0) return (p2); if (p1->ki_pid > p2->ki_pid) return (p1); return (p2); } char * osdep_get_name(int fd, char *tty) { int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PGRP, 0 }; struct stat sb; size_t len; struct kinfo_proc *buf, *newbuf, *bestp; u_int i; char *name; buf = NULL; if (stat(tty, &sb) == -1) return (NULL); if ((mib[3] = tcgetpgrp(fd)) == -1) return (NULL); retry: if (sysctl(mib, nitems(mib), NULL, &len, NULL, 0) == -1) return (NULL); len = (len * 5) / 4; if ((newbuf = realloc(buf, len)) == NULL) goto error; buf = newbuf; if (sysctl(mib, nitems(mib), buf, &len, NULL, 0) == -1) { if (errno == ENOMEM) goto retry; goto error; } bestp = NULL; for (i = 0; i < len / sizeof (struct kinfo_proc); i++) { if (buf[i].ki_tdev != sb.st_rdev) continue; if (bestp == NULL) bestp = &buf[i]; else bestp = cmp_procs(&buf[i], bestp); } name = NULL; if (bestp != NULL) name = strdup(bestp->ki_comm); free(buf); return (name); error: free(buf); return (NULL); } static char * osdep_get_cwd_fallback(int fd) { static char wd[PATH_MAX]; struct kinfo_file *info = NULL; pid_t pgrp; int nrecords, i; if ((pgrp = tcgetpgrp(fd)) == -1) return (NULL); if ((info = kinfo_getfile(pgrp, &nrecords)) == NULL) return (NULL); for (i = 0; i < nrecords; i++) { if (info[i].kf_fd == KF_FD_TYPE_CWD) { strlcpy(wd, info[i].kf_path, sizeof wd); free(info); return (wd); } } free(info); return (NULL); } #ifdef KERN_PROC_CWD char * osdep_get_cwd(int fd) { static struct kinfo_file info; static int fallback; int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_CWD, 0 }; size_t len = sizeof info; if (fallback) return (osdep_get_cwd_fallback(fd)); if ((name[3] = tcgetpgrp(fd)) == -1) return (NULL); if (sysctl(name, 4, &info, &len, NULL, 0) == -1) { if (errno == ENOENT) { fallback = 1; return (osdep_get_cwd_fallback(fd)); } return (NULL); } return (info.kf_path); } #else /* !KERN_PROC_CWD */ char * osdep_get_cwd(int fd) { return (osdep_get_cwd_fallback(fd)); } #endif /* KERN_PROC_CWD */ struct event_base * osdep_event_init(void) { /* * 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-2.4.0/osdep-hpux.c000066400000000000000000000021171356407164200151000ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" char * osdep_get_name(__unused int fd, __unused char *tty) { return (NULL); } char * osdep_get_cwd(__unused int fd) { return (NULL); } struct event_base * osdep_event_init(void) { return (event_init()); } tmate-2.4.0/osdep-linux.c000066400000000000000000000042461356407164200152600ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include "tmux.h" char * osdep_get_name(int fd, __unused char *tty) { FILE *f; char *path, *buf; size_t len; int ch; pid_t pgrp; if ((pgrp = tcgetpgrp(fd)) == -1) return (NULL); xasprintf(&path, "/proc/%lld/cmdline", (long long) pgrp); if ((f = fopen(path, "r")) == NULL) { free(path); return (NULL); } free(path); len = 0; buf = NULL; while ((ch = fgetc(f)) != EOF) { if (ch == '\0') break; buf = xrealloc(buf, len + 2); buf[len++] = ch; } if (buf != NULL) buf[len] = '\0'; fclose(f); return (buf); } char * osdep_get_cwd(int fd) { static char target[MAXPATHLEN + 1]; char *path; pid_t pgrp, sid; ssize_t n; if ((pgrp = tcgetpgrp(fd)) == -1) return (NULL); xasprintf(&path, "/proc/%lld/cwd", (long long) pgrp); n = readlink(path, target, MAXPATHLEN); free(path); if (n == -1 && ioctl(fd, TIOCGSID, &sid) != -1) { xasprintf(&path, "/proc/%lld/cwd", (long long) sid); n = readlink(path, target, MAXPATHLEN); free(path); } if (n > 0) { target[n] = '\0'; return (target); } return (NULL); } struct event_base * osdep_event_init(void) { /* On Linux, epoll doesn't work on /dev/null (yes, really). */ setenv("EVENT_NOEPOLL", "1", 1); return (event_init()); } tmate-2.4.0/osdep-netbsd.c000066400000000000000000000061131356407164200153730ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #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-2.4.0/osdep-openbsd.c000066400000000000000000000072001356407164200155440ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include /* MAXCOMLEN */ #include #include #include #include #include #include #include #include #include #ifndef nitems #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) #endif #define is_runnable(p) \ ((p)->p_stat == SRUN || (p)->p_stat == SIDL || (p)->p_stat == SONPROC) #define is_stopped(p) \ ((p)->p_stat == SSTOP || (p)->p_stat == SDEAD) 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) goto error; len = (len * 5) / 4; if ((newbuf = realloc(buf, len)) == NULL) goto error; buf = newbuf; mib[5] = (int)(len / sizeof(struct kinfo_proc)); if (sysctl(mib, nitems(mib), buf, &len, NULL, 0) == -1) { if (errno == ENOMEM) goto retry; goto error; } bestp = NULL; for (i = 0; i < len / sizeof (struct kinfo_proc); i++) { if ((dev_t)buf[i].p_tdev != sb.st_rdev) continue; if (bestp == NULL) bestp = &buf[i]; else bestp = cmp_procs(&buf[i], bestp); } name = NULL; if (bestp != NULL) name = strdup(bestp->p_comm); free(buf); return (name); error: free(buf); return (NULL); } char * osdep_get_cwd(int fd) { int name[] = { CTL_KERN, KERN_PROC_CWD, 0 }; static char path[MAXPATHLEN]; size_t pathlen = sizeof path; if ((name[2] = tcgetpgrp(fd)) == -1) return (NULL); if (sysctl(name, 3, path, &pathlen, NULL, 0) != 0) return (NULL); return (path); } struct event_base * osdep_event_init(void) { return (event_init()); } tmate-2.4.0/osdep-sunos.c000066400000000000000000000042551356407164200152700ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Todd Carson * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include "tmux.h" char * osdep_get_name(int fd, char *tty) { struct psinfo p; struct stat st; char *path; ssize_t bytes; int f; pid_t pgrp; if ((f = open(tty, O_RDONLY)) < 0) return (NULL); if (fstat(f, &st) != 0 || ioctl(f, TIOCGPGRP, &pgrp) != 0) { close(f); return (NULL); } close(f); xasprintf(&path, "/proc/%u/psinfo", (u_int) pgrp); f = open(path, O_RDONLY); free(path); if (f < 0) return (NULL); bytes = read(f, &p, sizeof(p)); close(f); if (bytes != sizeof(p)) return (NULL); if (p.pr_ttydev != st.st_rdev) return (NULL); return (xstrdup(p.pr_fname)); } char * osdep_get_cwd(int fd) { static char target[MAXPATHLEN + 1]; char *path; const char *ttypath; ssize_t n; pid_t pgrp; int retval, ttyfd; if ((ttypath = ptsname(fd)) == NULL) return (NULL); if ((ttyfd = open(ttypath, O_RDONLY|O_NOCTTY)) == -1) return (NULL); retval = ioctl(ttyfd, TIOCGPGRP, &pgrp); close(ttyfd); if (retval == -1) return (NULL); xasprintf(&path, "/proc/%u/path/cwd", (u_int) pgrp); n = readlink(path, target, MAXPATHLEN); free(path); if (n > 0) { target[n] = '\0'; return (target); } return (NULL); } struct event_base * osdep_event_init(void) { return (event_init()); } tmate-2.4.0/osdep-unknown.c000066400000000000000000000021061356407164200156110ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" char * osdep_get_name(__unused int fd, __unused char *tty) { return (NULL); } char * osdep_get_cwd(int fd) { return (NULL); } struct event_base * osdep_event_init(void) { return (event_init()); } tmate-2.4.0/paste.c000066400000000000000000000145471356407164200141320ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" /* * Set of paste buffers. Note that paste buffer data is not necessarily a C * string! */ struct paste_buffer { char *data; size_t size; char *name; int automatic; u_int order; RB_ENTRY(paste_buffer) name_entry; RB_ENTRY(paste_buffer) time_entry; }; u_int paste_next_index; u_int paste_next_order; u_int paste_num_automatic; RB_HEAD(paste_name_tree, paste_buffer) paste_by_name; RB_HEAD(paste_time_tree, paste_buffer) paste_by_time; int paste_cmp_names(const struct paste_buffer *, const struct paste_buffer *); RB_PROTOTYPE(paste_name_tree, paste_buffer, name_entry, paste_cmp_names); RB_GENERATE(paste_name_tree, paste_buffer, name_entry, paste_cmp_names); int paste_cmp_times(const struct paste_buffer *, const struct paste_buffer *); RB_PROTOTYPE(paste_time_tree, paste_buffer, time_entry, paste_cmp_times); RB_GENERATE(paste_time_tree, paste_buffer, time_entry, paste_cmp_times); int paste_cmp_names(const struct paste_buffer *a, const struct paste_buffer *b) { return (strcmp(a->name, b->name)); } int paste_cmp_times(const struct paste_buffer *a, const struct paste_buffer *b) { if (a->order > b->order) return (-1); if (a->order < b->order) return (1); return (0); } /* Get paste buffer name. */ const char * paste_buffer_name(struct paste_buffer *pb) { return (pb->name); } /* Get paste buffer data. */ const char * paste_buffer_data(struct paste_buffer *pb, size_t *size) { if (size != NULL) *size = pb->size; return (pb->data); } /* Walk paste buffers by name. */ struct paste_buffer * paste_walk(struct paste_buffer *pb) { if (pb == NULL) return (RB_MIN(paste_time_tree, &paste_by_time)); return (RB_NEXT(paste_time_tree, &paste_by_time, pb)); } /* Get the most recent automatic buffer. */ struct paste_buffer * paste_get_top(const char **name) { struct paste_buffer *pb; pb = RB_MIN(paste_time_tree, &paste_by_time); if (pb == NULL) return (NULL); if (name != NULL) *name = pb->name; return (pb); } /* Get a paste buffer by name. */ struct paste_buffer * paste_get_name(const char *name) { struct paste_buffer pbfind; if (name == NULL || *name == '\0') return (NULL); pbfind.name = (char *)name; return (RB_FIND(paste_name_tree, &paste_by_name, &pbfind)); } /* Free a paste buffer. */ void paste_free(struct paste_buffer *pb) { RB_REMOVE(paste_name_tree, &paste_by_name, pb); RB_REMOVE(paste_time_tree, &paste_by_time, pb); if (pb->automatic) paste_num_automatic--; free(pb->data); free(pb->name); free(pb); } /* * Add an automatic buffer, freeing the oldest automatic item if at limit. Note * that the caller is responsible for allocating data. */ void paste_add(char *data, size_t size) { struct paste_buffer *pb, *pb1; u_int limit; if (size == 0) return; limit = options_get_number(global_options, "buffer-limit"); RB_FOREACH_REVERSE_SAFE(pb, paste_time_tree, &paste_by_time, pb1) { if (paste_num_automatic < limit) break; if (pb->automatic) paste_free(pb); } pb = xmalloc(sizeof *pb); pb->name = NULL; do { free(pb->name); xasprintf(&pb->name, "buffer%04u", paste_next_index); paste_next_index++; } while (paste_get_name(pb->name) != NULL); pb->data = data; pb->size = size; pb->automatic = 1; paste_num_automatic++; pb->order = paste_next_order++; RB_INSERT(paste_name_tree, &paste_by_name, pb); RB_INSERT(paste_time_tree, &paste_by_time, pb); } /* Rename a paste buffer. */ int paste_rename(const char *oldname, const char *newname, char **cause) { struct paste_buffer *pb, *pb_new; if (cause != NULL) *cause = NULL; if (oldname == NULL || *oldname == '\0') { if (cause != NULL) *cause = xstrdup("no buffer"); return (-1); } if (newname == NULL || *newname == '\0') { if (cause != NULL) *cause = xstrdup("new name is empty"); return (-1); } pb = paste_get_name(oldname); if (pb == NULL) { if (cause != NULL) xasprintf(cause, "no buffer %s", oldname); return (-1); } pb_new = paste_get_name(newname); if (pb_new != NULL) { if (cause != NULL) xasprintf(cause, "buffer %s already exists", newname); return (-1); } RB_REMOVE(paste_name_tree, &paste_by_name, pb); free(pb->name); pb->name = xstrdup(newname); if (pb->automatic) paste_num_automatic--; pb->automatic = 0; RB_INSERT(paste_name_tree, &paste_by_name, pb); return (0); } /* * Add or replace an item in the store. Note that the caller is responsible for * allocating data. */ int paste_set(char *data, size_t size, const char *name, char **cause) { struct paste_buffer *pb, *old; if (cause != NULL) *cause = NULL; if (size == 0) { free(data); return (0); } if (name == NULL) { paste_add(data, size); return (0); } if (*name == '\0') { if (cause != NULL) *cause = xstrdup("empty buffer name"); return (-1); } pb = xmalloc(sizeof *pb); pb->name = xstrdup(name); pb->data = data; pb->size = size; pb->automatic = 0; pb->order = paste_next_order++; if ((old = paste_get_name(name)) != NULL) paste_free(old); RB_INSERT(paste_name_tree, &paste_by_name, pb); RB_INSERT(paste_time_tree, &paste_by_time, pb); return (0); } /* Convert start of buffer into a nice string. */ char * paste_make_sample(struct paste_buffer *pb) { char *buf; size_t len, used; const int flags = VIS_OCTAL|VIS_TAB|VIS_NL; const size_t width = 200; len = pb->size; if (len > width) len = width; buf = xreallocarray(NULL, len, 4 + 4); used = utf8_strvis(buf, pb->data, len, flags); if (pb->size > width || used > width) strlcpy(buf + width, "...", 4); return (buf); } tmate-2.4.0/presentations/000077500000000000000000000000001356407164200155355ustar00rootroot00000000000000tmate-2.4.0/presentations/tmux_asiabsdcon11.odt000066400000000000000000001056021356407164200215760ustar00rootroot00000000000000PK»[L>^Æ2 ''mimetypeapplication/vnd.oasis.opendocument.textPK»[L>Ï£€ï[[-Pictures/10000000000000200000002000309F1C.png‰PNG  IHDR D¤ŠÆPLTE€€€€€€€€€€€€ÀÀÀÿÿÿÿÿÿÿÿÿÿÿÿ3f™Ìÿ333f3™3Ì3ÿ3f3fff™fÌfÿf™3™f™™™Ì™ÿ™Ì3ÌfÌ™ÌÌÌÿÌÿ3ÿfÿ™ÿÌÿÿÿ333f3™3Ì3ÿ333333f33™33Ì33ÿ33f33f3ff3™f3Ìf3ÿf3™33™3f™3™™3Ì™3ÿ™3Ì33Ì3fÌ3™Ì3ÌÌ3ÿÌ3ÿ33ÿ3fÿ3™ÿ3Ìÿ3ÿÿ3f3fff™fÌfÿf3f33ff3f™3fÌ3fÿ3fff3fffff™ffÌffÿff™f3™ff™f™™fÌ™fÿ™fÌf3ÌffÌf™ÌfÌÌfÿÌfÿf3ÿffÿf™ÿfÌÿfÿÿf™3™f™™™Ì™ÿ™3™33™f3™™3™Ì3™ÿ3™f™3f™ff™™f™Ìf™ÿf™™™3™™f™™™™™Ì™™ÿ™™Ì™3Ì™fÌ™™Ì™ÌÌ™ÿÌ™ÿ™3ÿ™fÿ™™ÿ™Ìÿ™ÿÿ™Ì3ÌfÌ™ÌÌÌÿÌ3Ì33Ìf3Ì™3ÌÌ3Ìÿ3ÌfÌ3fÌffÌ™fÌÌfÌÿfÌ™Ì3™Ìf™Ì™™ÌÌ™Ìÿ™ÌÌÌ3ÌÌfÌÌ™ÌÌÌÌÌÿÌÌÿÌ3ÿÌfÿÌ™ÿÌÌÿÌÿÿÌÿ3ÿfÿ™ÿÌÿÿÿ3ÿ33ÿf3ÿ™3ÿÌ3ÿÿ3ÿfÿ3fÿffÿ™fÿÌfÿÿfÿ™ÿ3™ÿf™ÿ™™ÿÌ™ÿÿ™ÿÌÿ3ÌÿfÌÿ™ÌÿÌÌÿÿÌÿÿÿ3ÿÿfÿÿ™ÿÿÌÿÿÿÿÿ¸ÿ¸ÒOIDATxœcà'FŒ*U0RËU<4àðIEND®B`‚PK»[L> content.xmlí}Û’ãF²Ø»¿Ñçxcf—dà}´3ŠÑhµÒ ]&vFÞµ HIh@€‹K÷pþ ûÝ~ðgùKœ—*\HHÝì®=qFM P•Uy­¬¬Ì?ùyéw"ß{}c¶Ú7†ð&þÔñæ¯o~þøMsxóå›ÿôg6s&âÕÔŸÄKáE͉ïEð_¾öÂWüöõMx¯|;tÂWž½á«hòÊ_ O}õ*ÛúÅOÂhí–þœg¿ŽÄç¨ìÇØ6÷­=.?25Î~= ìû²c[XÔìç3¿ìÇŸC·9óaÕ—+;r6 øì:Þ§×7‹(Z½º½½¿¿oÝwZ~0¿5G£Ñ-½Mž$íVqàR«éäV¸ oÍ–y«Ú.Ed—…ÛfAòâåX¥—ÆŽì-¬†wóÒq7ß±4“…”¦ jœGogZ½iöÛ¥-vàdxû¼¤~ø>¥…`Yv,l›[ªIà¬JO“[g¿÷}??`%p­v»{Ë¿3­ï÷6¿œH™æ“½Í'¶;IVÜ_-´3o¡ESÜ!™&„ îøÀºå×Iãpº³ëüðý‡ÉB,í´±s¸qÓñÂÈöÒ•  ;gÚ» ÄÊ¢dafå&`ËJ`[DKw7»ã[ÕtL§…MœÎ-°>0^óÎ÷ÿ–“‡ûéatKÂu„«¸$i+§#>¯DààLl ¡¹ aÑ€8üÕ«Ì×L‹òËŒ.‚)Ó ^Eí…ˆMàp|%aƒe [´|Mšä^@þC7ºõ§3+˜ÎZðãæRcÌámò`ê¬9³'¢97|ógGÉcƒã¬^ß|ï/œÈøÖñ¦Žyc€ðQí–Ž»~}ó{å‡_dñƒ›Ûý½þ3ø°^Ž}· ÏÌËݼ Û-‹^ܹoñMs.<@ðhà/m/×båDbw6|‹(?0öG@uhü(î¿É®Š×f£\Ÿ³‚¦f_}UÂ{' Oúkñ»ý_bãðÎÉ´)±á:ŒÄò˜2ÔY‚O‡év¿ÉçvùÈä“&õ“0"ý››Á{3KB½²{Ø«…zÐT¥Mþê½B r£úM¾i®@&‰ r€0g>™‰MÛuæ l&Â#uÆ_ü‡‘3[7C°å Û{?á7³Ýç—wìÖ1°˜~³Ú¿ýé:™š‡ÖVZÔÊÌ<}s— 4moÏ{4§\ñ9iQ~nÚæV7rëŠu/E ¨ag²{n;ß's“-ÊÏ­÷h©¡äúgŸÀÒæŽ×tÅ,"£§gN@ަ/g¾€7mù”æ BÆ”¹{ÍÜ˪S<©>V³¶_³Ëà¡Y`t ì`g4µƒé³“æfû²lÔ~ iae€=¢y>Z:Ý:”Í\'Ì7úÞL'^4òé¶ÛΑ­ý#Ÿní¹³äÓ-”#w÷|ºi±säÞþ‘O×ô;Gîïù(=XnäÁþ‘ÒbeG~¼¸UŸ:z|¢ÒÚ£ƒjT.òi䯶žý(BŸµzì:žh.o¶Ûÿùf—S¤´*Ì|’¸Ò™?C°ÿ ½ß$™ø®¼¾‰‚X¨u ý8B8”–Ͼ"ø¢EàÇóEJ²žÈdÞ‡š¸ó/x:\E7»h^M)\ØS<ÉvL/b˜k@ ä†VÝÝËuËóˆ+"X°æ'xt›n®b¯¼›1Ãc¼jCS¨ÀŸ Öž@¿y¯âI«b 6…@;šÐúŠåja3ž¶×³¤øwE8)j é-¥½*¬ºÇªÒ¬zV5­ëçUóQ0ë(ž0·îÙ‰hnÕŠU3ëcbÖ=›÷GǬGú{¼}ŠZðhÁó$ÏÇ-¿výx½@·¼UོcçãÖ–éxx÷1ëg´eVî‘–‘ÚëqÝrË2Óô®m‚§Lñ³`Ë( ›<Õäç,ßðŸ„+dûó[ñ<\{ŠUpʘÃßÞT½¾hòku‡@Ml·¹òC SµõPðúfl‡WS½]øó/cÀùÃYà/i: ¨ƒÌ L`eOù6’šÑئx¹„@«€Ÿ-#è)à'ÓH½àË7M|¿Óf²š!.HÈ¥c”“AëeGW³ÒÃÕò¾7o² \q'\‰¨qì‚5ø%>ÑsÃ?³˜üŠšá™&Çч™Õj†ñlæ€ÀiɹÏ&^z}óÿþÇÿN( 3z†6ƒÅr0̸i{Óf¸Â0ç¥? \{ ¯Ék‡—"ŠºÝh"û¦‡3ßuý{Àèh?‰ìñMfpø k⯒ñ)ÆnP!Ól·ú^ž“¨¼AÎAQ8ex»peÕ‹«ÿõ®W£v¯*²ø“‹a«S/¶þçÿ½ZlY­^·²äÃUWKA‰«NËTä,õÉŰÕÓr0ÁÖЬŠ,úâb¸êk)(qÕmu»9K}r1l ´”ØêµÚÃjÈ’_\ WC-\ ÌŠœ¥>¹¶FZJlõ[ŠÈ’_\ Wx-E‹A‰¬Ñ°2¶ø“SÑ•y]Æëai¯‡özž)º.îü¨Œ¬"çdzEÖŽ•ÑUäýx¦èº¸û£2² ÝÏ[÷(|Uô ´ÿCû?ž>²´ÿãŠÐ¥ýW„,íÿ¸2tiÿÇÕ Kû?® ]ÚÿqUÈÒþ+B—ö\¶‘ÿƒ{Ûö}Ìw—‰V=Æ2ÃG”Îæ×›å„'€”¨%< ì{‚0:scáÁ¥˜÷ñÉþ,áœ,0PPóÞ‰MD*_ºbô|wÅè=¨"`Ã(ð?AS;Tðó£&>y}ó³öÌñrè×ðHàzßÍU«{g-–ÒDz<ù¿µé²cÀì'ÝÙ̱àaÒ[«ƒým¿gD¨õÉ5Þt³Ox´§G|[ÔßÌq©¡ï:ÓÌ“dF3ú_ö ,íÔ²øü뛿ʟ¸`ƒl»…M²Ñ·ø7¶0ÛÙ&ÎÒž'¡qN´´W´îæv›ìºo¾[ˆ —ÑK¤*;v3C»$ÌRÚηKD¶"Þ¡&vù0e W5Œ\¼YRG[òsË´”(S/¦.x'åEkë…’ò Óèžú÷¯oÎ$gîaÓŸÍB5?籞¹.|©pÖ쟟5MÍšu²fW³æó`ÍÁùYÓzĬù6z¹Bþì]…E«ØS3ànžŸ;škgÀ¾fÀ'€£ó3`÷3àòÞàyO§bM³}~Þì=bÞ¼Vå8ÔÊñ©pà19Ô‡Ä+qáèSÓôäpžƒ*ÒÒµ2æCûn´ùz)Þ<,OGIÖÊ›íÖѼy)Þ<9šç0oê3ÉZyS»}ž ož?”§ó˜%¯7Úã£yóR¼yr,v=Ž®ƒGµCè4=DOç1\cvú^—Vž—âÍóûtt¬A­¼©ow=Þ<Pç1Ç\!o>ôµ.Í›âMëüq@èù×¼Yoê»]Ï…7OŽ:Ì›ƒGÌ›W£×}èð £Wž?ýûškæÀ‡ŽÒX^ Ðç1á]+>t´æÀÚ8ðüá<]jW+óépžç›ççéêP»ZyS‡ó<Þ<jž®µ«•7¯$ŒGóæÉ¼yr$µ{<ôÆRóè¥xôü=ýÇì{½BÞÔûÎç›çèé?f¯ìò¦Þw>Þìœ?¢g ýµµò¦Þw>Þ<DÏ@ûkëäÍ¡¾Aò\xóü±>í¯­•7õ ’ç›çè›—µò¦¾Aò\xóüñAƒÇ|óòÃz¹QàLÒ+ ×>ô®W;ž?$hð˜/[> vÔ7Hž ;ž? hð˜ïW> vÔ×Iž ;ž?…Ïà1_©|ìøÐ!@škcÇóÇøh—k­õG®Ã«Ãü¤½:'pæù#|Fú¤²VÖl_ÉQ¥ÎBygvÏß3ÒJ³^쵩CNfÎóøŒôAe½Ì©O*Ÿ sž?Âgô˜O*¯‘9õÁä“á¾óÇðŒóÁä5rŸ>‡|2Üwþ(Ñc>‡¼FîÓÇŽO†ûΔ3ÒÇŽçæG}îødøñüQ9£Çœ[àiðãC§ÐüX?ž?,g¤ó ÔË}:¡ÀsaÎóéÐÙõ£åΧ¡-:É€Ö–µ1äùcsèÈúÑ2䲟y%Á9Z]žÊ½óÇçЙµæÎ¹Sè<î<€Zk;u„ÎsáÎóGèСö£åÎ'±Õ4uÐΓaÈóíÐ9÷£eÈkd¿‡ŽÚÑêòRÜyþ :õÖÜY#w>tTæÎKqçùƒ~è \sgÜùÐ1>š;/Åç¢3rÍ5rçCGühî¼wž? ˆNÄ5wÖÈ:"è¹pç"‚ÌÇtÜùÐñ?š;/Å2uxP­Üiéð gÂý „™:<¨^îÔáAÏ…;/dêð z¹S‡=î¼@xù˜Ãƒ®‘;:Hg¥¼k^ PÈÔBõ²æC é8½ÚØï‘@¦Žª—ý:H³_mìwPS‡úÔË~ê£Ù¯6ö»@,©cyêe¿‡ŽåÑìWû] XÇÔÁ:õ²ßCëhö«ý.c=æhœ'qiÒzèø͵1äp,€S+ûutÎ3áÎÁp,€S/wêœçÂÀ±sΓ0f;’£ÙÚò17–޹©—ý®$æF«Ë“¹óa7Öc»yêRâ<†¼@ Žõ˜qžCêМ'ÃͱthN½ì÷С9Ú~½w^ rÇÒ‘;õrçCGîhî¼w^ °ÇzÌ=OØա>O†!/êÓÑ¡>çfHêódò¡>êS+ûuu¨Ï3áÎáB}:Ô§Vî>t¨V޵±ßɱ<žï‰ýÜ÷˜Cyêã¾Ì:œ—ù:nÙa§Æ‹‚x[Ê¡Sm¸t¼tÌ–Ù³$cásÙ¸ÛÚä6Í»›¼{°Ÿû©—{:ìG«ÎÚØï丞êó1‡õ\¡ê|èžË¨N³Õï´ò<À½<æ  kTž:äçɰßÉ!?‡•çóˆø¹˜ò|耟Kí;;®Vž¸÷!AT/ÿ>tHVžµ±ßb~9æçÙOGø<ö»@„Ïð1Gø\#ûéxž'Ã~ˆçêxžZÙoôÐñ<šýêb¿Ñv†:`§^öÓ;O†ý.|gø<"v.Ç~²£Ù¯6ö»@ÌÍPÇÜÔË~:ææÉ°ßré ŸGÐÍåØï¡£n4ûÕÆ~šê ™zÙOÍ<ö»@žœá󈚹û=tØŒf¿ÚØïQ/CõR/û騗'Ã~ˆzꨗzÙOG½<ö»@ÔËHG½ÔË~:êåɰߢ^F:ê¥Nö³Ú:êåºØï:w&‚®,mX¤&½ßüY¾ûÓuòÔÀ^Å¡h†þ,j®EcXäO¡¼“òæÏÔ"ÿŒ…7–7,zÈ=MpåÚë¦G®ã XÓ;$ S¡×L=ß¹n ÌúNâ¤Î>Úc@þɽÀŸ'wò5`è…ð²{Õü aF±œ°‘Ð$;ÑëÌ›7B‚·²½í>>BC -Þìmlݼ‰–ñçÒí7;¶\:žíËØœ•+>‹@¾_ÈžVÛݼïÞ¼ùÑ™,|×ì p€†Õ6Mùñj÷Lj"\!¤a†ÞpBÃ6@_€˜[÷ <†íºþ}˜€eH"—оøøñ¿¾Ä/&" AÁD¾1Æ>SÃö¦ÆÒö€¦Æ,ð—ÐyhÙì¥e* `bßeú.õ µáÀú!pK{OEdOªÇh!’~ƤãÅ"÷-´Û“OÌã‚';€mcêÌf"Ù˜NìÞ‰@2Ð< â²VËø.Bøîœ)NúµÇŽëDkìE´­à;ߣ'60e `ÓÇ…´=áÇ¡»&`lõƒy2ÿÀÐ?È%¹xK„ ^MŽ;M;kUEñwR¿¿¢´Ò4ÒµðZpIÀ·oîºVè› Õ,Lò§•ð¾úð5Í„;íÑ„NGƾ á° ?ö¯Úí¦¼#±¬<  Ó¨Ë !ÌÉv8bf„ñÀ\«á–~äÜ‘\¤õD(áãÈO[ÐL%É7 áƒ:¼sÄ}® T£$h ª©-ƒ@Ÿ²DDá$CJ~)?Yåàc\…ƒëä/…j3ñ—+ßÃ)4$5df°´ ]géD4‡›ù2y8K¤F¢ÍEÜ/;ÅB±sóæ[^â§@z ™í¤k„”E|Ĭâ,%{Oã ²&,µí1{0µ;,„ÍþúãÏFòÉFÉ!ì‚Öl lãlAÕâJÖ™Q™Ž2c‘àûÃ?c?úbƒm%ƒö+B²aÀï15 ¾på£ý7)à*œŠ9¶Ã_¬_+Õ,¦’£É"Î|”Ƹz™yãZƒD˜¾ÚÇTŸ—î+¬eü1èt‡V¯o)%›…à{ó&óYӾܣvLPwß ?Ô €_šÍ`…‚‡\¿0QãuªøËÌâÜ_œ¤yykLÓ‹Ì Å´Î†IŒ ¢ á;˜sd ƒ¦r¨²#ãÞAZLÀ­¤! W؇ìƒ'}PA¤£M¡±A! &¤™û -S‡9ì[Ã!ޒߦ«u€ùМöb ŒJ ™ ê/¬'0ÊW‚0”ÄBÅãÄ1‰^3ç³1Žç¡ñÏØ™|r× ålÇ…BRÏ Öý€%*IX`#ù©Òá(’ ”íi ¿gûhiåÿA ;cfOx Ö€» ¡áB@3¤:IØ!Þ¼6X>D®Ú&I§À¼Jà × u?4ŠªaíÁNh`À¦'c”1ÜÔó'†H‚:A—ô€Üã8S£øeÍãÆ«>¤½ Í*’#‚Øp“Ô¡äê ‘¸Á´E™ £Í(Äòa“ç°8V•“AÜIŽÖé`føR©K-]0%\÷ÿˆAø¢í‡öÐ L¨%Øã ´{p“M_v[}0¿]‰¢Ë°š¥ÂdmñÂ|™¨~âŒÐð@;˜®‹»ŠÐ¸ÃàHêa¬¬_¿É» #™é}থS“ÌvæM{„Ÿê]zšûÂæÝ›Ìëœy³`1ÀÔ }¹°¼¬êÛ¶¸N p“â’Pƒ%Å¥œ,loŽ–&ÚŸ.?ØŸÎ '!)’÷".Ù‰SMuÌž Ø$~3Ç•탲k<¹^hÞ3PSF$ð?è8 [Ô>¹Å è·Wè‰ 0i€Rd¡5ˆÞ1¬, BŒ‹æ,Ã9H€àÞ>)«šùÍ.Ñh ·’A S`;؆¬z™ò™giOÊZ%¼OÅAUh­Ñ Ûb¿m]Ø©¤ a‡þs¨¦âð»÷ïêKÐñ@ nGìt¯–ãÂ}A{L$¥Ä> /F/_ÖNFÐÐRz°ÿµƒÚ32â°ÓAÿîw²Z¿è¼lðßvó–2 "ß‹—ð»60Hiß}xgH+±¶¾3ŠB: ¶­Ÿ/ ûÎwp7v¢„ämr#ÈF‰bîûä°}¯¾ÀíK’€ñ¢uÅ<År#Fƒ(dìƒL]Û®ôU°aÏÎPb{ßMÑê'ªÙº-äU ×­äs©’ݽHO…„Š˜,<4TÜ5…Ìœ mU€Ù Ük3ÔhQÀmÅ*à#m’hÂå2Î|‹#¼ßý5Ù‰1'X¿£M9k¡!7¨wV×–]²~ÞÝ|ûækÞ‹ý$ÝUµ?9Y¤¨5æÀ׸C†ý¡©“#ëAoÌ¡z—(À¼·$‘õ,ù]ß_·G¦]{~*í+ã-ÚÖä/KŠ cŠxê'6=ù gÎm¥ú˜À‚Þu:äÞÙ¼Ùžâ2Þ¿ÜÎOý¦íg Q™÷õÚ¤ÿäÉÍ9{,p!¬€tÑ‘ÜÌXŠ0ÄsAÚ¦œUa%7:=% ds:£ ›^–Fâ £©ã¡|о_ö€OEp½´*AU¥ñǬû¦Ê‡/2¾ÕÁËJ=8tŠæeÝo»f\B÷J¦ôT®"têxòÎ0eèÝ+ ]Ùp¢“œt³‘ˆòÇp¶·Þœ.úæàM&tR:¿€‰,!²òCŽb€îr!àðŸ%:¢å©i †ïñR$½æ½Ž¥Â Jøƒ”Éøƒ¬ƒþ P!ÍnËê%vœÔEýV»ÝßvY‡D¼½Û=“w69£òun …{Y/‘iîÖÝÜô-˜þ–ý fMo¸o—ÚÌmS{ƒQnŸŠK Ýòÿ@kíS÷¥½›7Ÿ‘F%¡ÎüëVá‡ð·ÇÍ×ÍZYýœ•Õ?heeŸ}F"Î`Ÿ÷žÃS÷ž€)>£¨{GÑú7$€§ü=®¾î† ý ™‡ŸùwçŠp¼‹¬Þ31¹ö¸s&W7grm2se“ë±³òó0¹ö¸[sÈä?È!?aéB>—è·¤õ•Zc½Á8ƒ‡FÿÓÆüvó½æ{¥0ß߃xô‡CºµÇIžCz'‡ôÎéìÞ‘Çóp6böŠ}6Y#§Æ6=Ë*{>’×áV¦EÑI'÷øàI7ópÇI4:0ä¨mïI‰EÙïjÚ¹(V«km, ï:3‡ŸòAÑ¢ôª-JÇ¢X‹bP Tê]s€Ñä§-ÍaGLáÒ˜{iÅܵ$ƒz–d“NLÖiÉTo‹9ëëì4ßÙ>1SƒÑ­EŒ1ZÄ\»ˆ)í¼­,bjrèh1£ÅŒ3W̨àÃw2¹ÐÆÕL™Ûí!#¿Ë‚ÐHCépò ?¼Ýí‰jÑsxí42»™JœTå²cÁéà!“¥­ò‘¡”U®bâôÂП8in«jsÛt •˜6YXNÓQ¥/‰ß*ŸDEW}|Ó2þBwM£ux×ñ>…r[eµ¸£ðŒéÐÊ%È¥ô@˜jB”ß!½Õ;ó_ô^2o&©Kè²*å~p×”SEÈHa•ð±Ú]gºêíÉëý”‹€6…”SBåÑ‹ |ûþ;NFB÷æ‹OUã`Àp•o0dÓ5Ì›À`º‚%ƆÆt݉Œ0ÆÌmò¾s%¦©Ôzåšð'4‘Y¥¦¾À\:‘á –OÂq²X2\KÌ$²Z¹xŘtPä-®ãvkÅøÛ¼X%â–¬„ĵ- Ñ´ D‹Y^qL- ËÎÿ`ƒ*ü"<Øà+àˆñ­nsªÔ™(øs*HÉ;®+3)¹&|\…‹e&½J„l«\“™1ò¡e|`ð8¿Ä\«òõÝ96²k ˆ¿(ÅRL¼‘žMzYq4ñ*3hHÄÑÚ‘ÌÇÈi¸R°3±Ð<­Ú¯Ç„CwÊ„C'»–ÌUù‚Hèî¡HèN«×9Á0<*:=#8*Ú*Ñ¡Ž€“_¹àk°¼Óiµ3gtÒχXöÚYoöíI»¶GurZ6Pb˜[ÓáæÍè‘uhMÛá´gMÛÉi‘|¾ºÓÓCØ/,‘ÜBKnà+LªÝôò<ÕïåCÓû§^ÿë%’Þh?i!‹NULX±éë­ò¹Ô¥±k•<«ð¢ÚiE%¨úêa&éQÐIgwâžI—Æ<½¬tÔÙlgÿ9bîv¸€‹Ó23n/γ™]<í—:ˆ‚ý‡y9Ïã`0ÚDâ%‚Á`˜s<›ƒ”Wd!?íL"Ýý§u9D´ûýmpæáV0z§WCr ROûÈ´[:c^)$t[£7àûºQû‘è£ÈÀÜ-w9/fÈ)<ÈÝ·EÕ°álÄâ”NÂÜ-wØjõSäÁ”oÀy™Íá±@•;RØ W®·¹R›¶½c¡*ëÓ·ò0Y™ä8Ö&ßL½ÑÑ+UÎÁ¾“Õê´‡{a*ƒ½ƒòæ°sûycµºÃºäõ4åÍa ”Ù¨cš{IVÈœáðdò(mœ¡¬)Šß0³Ôú­öF†@«[CüÆê ÙBDöŽZO^ëÃÿwrÔC7Ôœí®{jÝÎ{jê6Ú÷tC­ÜSÃ[hê¿ Duo-{S ïhñ7ÌUºê÷×ÝFÛ¾¿vè:Ù®ûk‡FÚqíàx[××h¹·uq ü#Þ[+sÿlëöšºl×;0êÖÅ5 í¡Õàáx² ´£]Ð/x]Žá,hó8nåö*l„¬¢¤™]+/ô½|ªµá°†ÈDr1!F:­®ZÙÄði»¬Mµ¶iõÔàø)8˹6$ì²xzåvX½VoÓâi÷s‡|‹ƒ[,Íðw>é¦:›|~pÞP†téxTÀ„²ð&B–M‹-8²,7TS4ƒ<’3|¬d_c!SyHoã=ŒPElèo‰eÙ§êÆ¯*¯Kq‘k,šéL°ü9<ðHT>Êi.émå"³›•]qJXâÚSµ(’êåôoS¾”Õë—è ‡Xâ9)#Jµ?œ {Ì–©G› Bá)PíÖ1Ýä1¶Ïß¡¿eËø*ßË­§Ð!rø(×É Î\™.KòÈtSSÅxØj\®KºQ€„î½ÙÓ)iÎFv\à@?˜txOÔ6îìÀ€¤ ÀjÀ§Œ²øk`õ‹2%+òˆùºŸmDmC‡m̰Æ;ÝW[Åòºxc™É.©Ë0¶'Tѹàl½ààd;ÑU¦òFï˜ãŒÞqWÍzí45\r¨0,qœaPáÇg(óìÈãŒ^…”ýýnÁ}²Œ@ýÌáõf½zèîמ¶ëéwiƒåíèùJñAª©O¯ÛlSM»»±Éåí]ëнÌRv)‰—3 ¸ðÜúÉá¸t2³œH-Þî U­X/Ìc«ÓÎ%‰ÇAÒ'µ$‰'­ÔÔÓÆeé¬Q`·‹œrùnFê”ë¶7ª3ÖåGÓ¹ž*ûÊt®§}.³g•멜p(} SJ8t[–ejá …ƒOA8”w3[ÃAÁio·ÝˇÍÜVËl×PÒm_Õ&méÄq…xºB÷;{óŸœ7¨ñä0sø®ßˆhú¥¼Ð@èY YÁ€”5ȹºý¶´Õ ­†+´ú¥OKw ‡Îá`µz½¥ÐÂA ‡G&Ôù7[g†ç:(§óp×¹Ã#P]l¯é·œ×—U¥ß„9aDÓwÂÅŒoèÄAP§½²ßEY\%§ÄxcÏ;x¶oø3w811¡G|Úü£#`ÆïÿÎyùáÛßÁ$Æn= :<³¦”ÊŒ1jHpÿèL>?€4÷¡ïµŒwɪý´ÞW¾²™¸1à==Ð†í« @â0Âu‰eåƒuŽvlâ-EÄzºžA'V»=b ² f)cå»î ë¥áúþŠgJÇñ¨¨€€8]´Œä€9VcƽÑd€^¨ j8Ÿ¦ŠáŸðdåŠÏØÉTØ®:zwí`. /^Ži0"DEZÈl¶íÀWhÉÔÒ€;ŠB OV"?¨Û73LnAÙ’bOÔ«pa˘“%PÆ?ø„}«\Ô*‚HÞߌ§Þóó Œ’¹sü8tñ"&%¿Æ[ºÕRd·I¸ôŽ9.ÀQ ùZdoŠ“«GYüÌ鵓Y./Âã¨Âx‚át35c+7YØ2í0˜3g‚¹y¬õ(xm _lÁÎáÚ2±ÁÊ"{ì¸N´>Žr1Év /ßbuP ÃŽ­å'*õyÆL$áJ€ts¡ÕƒB×xø™ÃSÌ©Í9‰‰>Âx…3a¾FRX€±Ø\‰U-*'IeÉíZ¼È.W¢&Ð22q.¯6d}”ä5ÃæpØu¬~p³½Vß'uú°iÓÙ°/¾£wóF?ÂÂ}ïx°²˜;Üãô‘š q KsëÅ®Ûâ{Ç”%Áúš’ Z<Šg 9˜ØFª…0š:|@*1­ªÀÊd:%¥¹ÌФ ‰ÜN­ìT¿w>‰{' ã(ØXକ¤:Ó=Ó® 5ÎõЭø·„¹ C±æÙ+ÅŸèÔÄkK'$yoñеöËV2 h gO0a¶š æo¾ûéÇ¿ýåí×i K„µÍñƒñ5)ŠODúšúÄ< $˜æhD?+xîÄÑìƒ)}PÚ4óYVÛ13ꪆÔH(Ž\’¶¡LèCô +7É ª8€éLYˆ!߀ø˜ˆD8dý5Ç'S€1]/·¤ò±Vª]*7Bc8¡ªföØÓLõôKïWæíp‰ü‹u ¨ÃTŠm0]0V$¹CëE‰çÅ‹V|÷þá#ÿì9(EÈ2`´‚Nø†ò7(£I–P@Œç«©2¸Ø ³1.ÒVIAGŽÛ‰ `ŸŽJI°ê´ÀÐÏ Ʀ¡\žÚb‰ÑزwxâÈ›=$·"UXd¦Â‹P”欤`’^jè‘§Êbk(,!Æ­^¯kYÃv‘ïUãýÄÿ»™¿ãOøšÆd´xÚ”ã!µÈ6Ã) Y½ ,¼áÙùXÚÄø7"Öù †#Í,"÷ñ–†t/<¨qà·l‰ "gœæ†”Q˜¯$lŽåÞ÷©åS•mßÊåe+é®ÁdM2?ˆ½0Ñâ ヶÊ7¥eh¿!"úêÛ÷ÍŸÿAOÞ~÷–ñ6€ïþË®á„Ùm ôûÁA‚2—l¿ÆÍ7å1Ñ2–SP?ë£) ¶9ËiDdiXû{îÃÎvó Ær£à´ÀR“Õ{©Îå­¾G€«`£ßÂŽ´+ѬE=•dZÚ¿S¤mXæÉu…7dÊÏì@îkhKF{a`¯£íL¢!ÄΧb…âÔ›àö[i´Ù^‚¹ƒ‡É<“|LJý¼$m“trfì*Ы³™¿)Œw²ZËLO ½(Ýë£ÁðÚä¢í€ëÀ$³sX¸œ:ë/9É?fy½Ù\”Tz¼õ {L\ ¶Ô²¸"¸µÃmK$%cÃ¥ù‚ì­U´Ä—)JR¦¿e‹ ¶Ú}Y}«³2bØaÐ!¢µÚf;­ê’,ý8ÆÌ7¼µ¦]*Æ};¼éd™¹ó )8 ï4 Xùë?ÓcÞ~ʇÈø°!£ëåç ëpÿ@ˆÚOÌœ9ìMYLTûèd¯‡ŒƒíÄš.™Ý¬’‘xG’¨ÜÁÈyÐ5Š¢Û 7ÁúŸWhµä¶àjßM¬¬ %«ï)?n±¬,,‹9ÆœAÝv»¥]„¼Ý±\Ù üÈJ0Kàý4#!B:ñW^8@¯„3 wº,z>|ø–'@ŽEPȸ÷š yé‚Çd‡NªØ—r¬¨ÿ€ÛJØ!_³ßî/¼¿ «›kä°c–šùÚûæ]‚”¶LݱUNJÛ˜‰{dè,Àzñè *BaÓ•¡j3îÏ4í; ÛÞiå2ó±q&Žž ÷S‡ ²êš¤%ya\´È"Ã… «3ùb «Jp¶{W]d,ô%$ž¸oÊË9Í©ñ‡Æ~ô…XÚü7äy~R¢C³}óæ$≂ž >¹³À\£§Â›;OAàß/¥øèoSl)˜IcÕ¬2›~çIIH3—ûà•bòB-m¶›ã3ƒ¥îcÐ3Jr †o¿}/(g-˜ëÿŒÉ'èd NsâÂh,†øâ˜ÃN•ö±")Q†͸ù(cA£O§ÒN´SXÈ,ðm) TŽ;å‡õ„õ¢Ò›jù/pnXV œPz)%ÒXq’zG1냉ˆ\$<Ú’ÍV¼x–èõµT”!M—î¬-ü„ËX$iØÈïÏ:ü3ìž"‘ÎÑCh"D1K½41NOu@Î@é¤_/Æ”Òí“€ešEâm-)™—ôK• J dÿÄãi{¤Š«SÏ[3cruûÃöRS þJL_”^V1è‚TjÆ—^ åí&€ñLè v3Î\{*FQîô‚òDõ2â³n& É¸\¡Ì“G –]ßÿÃQ:~ÁðQ/²ôâÆ!» QžK •“i§ˆÉäœPåÌC/n”‡¡æ1 j`7J×M%Ñk6UÇD@ShÃö£…?UÅÑl*6ˆªäj°'=—™ŽZöpq=¾*8YVKSÈÉH“o3z¹—®m’ÿ“ðâ \<òÕF nü§en(Ú³Ÿ ¾bØ¥™ümäO3ñŸž‘MDEhK"G;2Zÿ?Gnh ¹.ìÍðÈ­8tt!r~·”ÓÑ fm+™ƒm+>s¢™í[ åjñ¡ðú'sÐ;j¹ªâJ;U“·Qp§U1,”(áOG$mÃÑš%BI·²¶9³ Ü"èöËŠyÛ8YÜa€Í-’É l Z惛;ˆ¨ÄèÅaÆ$èoqí|¿;ÜØ*ƒù«Z‚p‹®ßW[lp8´^Ökw6-š\jß^kÐ17mÒ4µÍfq±^îùáâbýÌÓlkiºpq±vªe.LËÞõÍî¶5µUwíN-]±tשƒÍd7‚$n¹¼ñ=Øßæ¯,YƒR“ÚÁüîºÍž½ä[nûÙoõ‡9òmÃùö[íÎÖ–j7ùv’¯U‰|»)ùöD ºëú‹f¿Ûƒ?†'V”Ûçy·h-¬ÍÒ\“²pyú;—gX¸<ÃËÓN—§ €ug ÌEÿSïß’gø{˜çïáŠRÖô‡ù$™¼ãët ONÁëÒ˜×"©;AØL´Œï¢4²”¾H*³g"Óó…Ó9 e‡Ô€…ìP™cÙ ‹ä(ÕäS¼¸”¶´9‰?"ŽëbÀaV½õÌ):|àÍÍ@Ì9p4”¥ "{lP˜šU¸µ4tð†“’ù\@à­¢˜±{%̦ ësŒ íí °×|_ßÃûö}¾ê€ÌK±Ä¸Ñ8”åBç_œïƒ`Éê HW²é‰)"Ž€H¦t-‡ï•ȸgJ~`‡ëœ{Ëø ~OEåñ· }æd"˜¢`¼ÆPTªŒ"jŠy ðÞ^Æ `^Œ3wýX¦l@‚ªLYSÈ3d‹½®©ÐÛ;‡½é: ÐGsWA5šÐ•PÝ<ÂÐS #„+#§t‘xÕÁ1ñª}Ø0›ñªÖáxU.aU¼jç¦hw™t¶kk<<œŠPÕ͹ r&ùÀ4‹ßîÛï@€³ç„6¹ÓÉV:MwýŒ#ƒ’mø¶û² ïÆ^l'6¬Þá-,1*^¨,¦×½û¾ýÈ)W»i 9½½È9Tìu7röœ?æ‘Óm™ý­ÚáEÈÉ'»îË´•u"gAšåÊ:úx¹§ú²vT•¯Ï›CU»Õétö¢Kµ8e{Žüj@YûLèú“âõ$SâûyÐV6¸Óéïá°ìÛ’(Û Q9®C öR¾E ¾ß Q9ïéÖu³ZA”['Õâ(¨ûMw@U°NP½Q9uº Õ~ˆŽ…¦\¼Ó4ƒ˜ëœ€¹Q9­VÕ!ÌuNÁ\Ù»›Pí‡èXhÊÞ騀Æ:ˆ9ëÌ•½Ó±Õ!ÌY§`îHn\+ó”µ*{§cªöA¨Ú§@u¤,7÷¯Ó±Ð)Ã̓ôdî§'ujóW´ÜÉ=äÚk?ñ!Ÿ#Ó*\˜’Ž“Î‘—G]U§ÈˆéʦèÃw6ûbùF§ÊºÆÉ!ïêsj?Úp'©5Ђj¥Ž®ŸÒždj•;5IEáMìU( ¿¡Ùî^±£†'Ó ŒXxŠ€}VSá þ‹<[¾‘nï˧ØJAÅLº}­ò˜qV…taU0}–¼µË³nÉ.p·oO"!¯a/1E$º‡Ú;vÒ˜K'Z4(³¤Ê9„x¢”¹‚¨<ºìÀaÝÓÍzø$𧱺†?……ÄÏ袮LiBI§þòÏS6 ˜¾>e•Ÿw)Tº‘wo©!(9iâ`ÃâåJ¦ä Ï÷šÃ6§å)ÎÐÝéŒÝ(«{0ß%Ö…¿ͱÃ%³t"%sfå4u&³zð'æ<ù„9‚`zœ Í*snùœÃÉä”BÀUNk5«˜ÓPȬ0*!Œ1…d牒NL)“€8–‰œ‰Õ>¦Ü3%máZÑJôpæfœ„Ð “¤tþ$ì%6VêAž#q¬Å8ÍÍËrX?ñßr…fþ‹ÞKƒîvJ.¦ a†I΄.óõ`êrÎ&Ê=—ÿœ4÷=ç¶‘ ÒVy  SPRʈ°@Ô–Ï)ˆF*N7¦#3 áŒ<2[W: þê^¥žÉiFמ«L§³ïlÇ¥w˜¶-—Ú8ÉÏV›¤‰V\Æ UTÊŽ|ò„\™vGÕô¦z¨Ém>µs£8µ=Õlu8‰6*§IÈ7§\ DF(Ã@×Î0Q=¦9ÊœÏ#›½kLïLN[軩kï>|·•³ŸÁzûþ}÷´È¼JÎk3úŽˆ›Ê¬SÂ3✆L@-)xAÏ[—2¬eÁIUïíc“Â|Ö§”RÁ“¨$þ”·3øg{9Š‹¥ FÌÖJÏX ÓðñoØ$sP—¯Lÿ=í ML)•4­åšC¶döÁ ­›K³Ì벑¯˜ÓÚ ÙAœS¦á”‡ÈS søì: ó"'HÁf³>D°1Ó­ÄiöôT•÷•=ù4çtÇÿáK̆ɚ²©¤e,ŠUFàPŠ*¹Uš<œ” íÉî}%ðKdÌícmæŽÙ5‹2æ&¥òÂd"åY“A§ÌI±'©\yvCŰ¡1Cè¢ä_¹|Fü™JYTKæZ áft‹Üè<Éë)[(¡K¶†$ë²@=ÂÎ3RµþíEÊwõfÙ¦lXI>JrªŒX<+N«,uX«ŸdnÎ48'gd r*iˆ“¥c: ”[ÓW€Î|j§(î, Ô¤Q_d´f²Ûañ÷’#PD’öyBð¡4€%\ùH—f”SÒI‰°cµUÈÒþÄþªËPw+w‚×RN±!ççQÖtÀñ8eðßÁiÍ“½ÍCdà± Üá­˜,8³A³Ò”1d=‰D¡¾Öh Å!ùIåã“i¥#ÊF̶˜JÅ­âvAÏáÖóÃw}÷í÷_³ŠŸçNM‚8Ÿ’^Úåd€) !¥ã¾ßT˜¡‘…5ÙmÈ)Qb8”¹ª®:Æ¡Ø*xæÃgüf¥4n(zâäïá÷2‘²ïà£ÄŒ¦h £ œ¸>¥}ã°šHÆôÚ)TÕsì¥[B'L]C_³¬ZЬ_)¨sfâêª|û{ª‘Ê¥6²Ä J³2—Vê÷¯œÆÝÆ ïœtö%#-õ T vô*chZ{dÛY~¶HV1›ÁV¦Ò*¿ª4@\¥±«³»s,6ƽ™j‰9´¯rÓMÂ*x‰Çé*“1Ók´W9¨‡8®M,Y$ˆåfvÍ9òìä£fbÂñžmæû¥óÁÂ0ib×2ùcMRœÀXf’ÜÂg¤ÜÍeTª%¨e|«²‚"›.˜Å¶Hj¤[MÏ7æ1n"³Y¥Uá­ªR÷¬ÙXä¥nf{+¢ÒkÙ\Œ›WûÁ…-³iŠi*¯=ÜD&Ƶœ¦/ä¬k² ßư¯¸&Æß~V9kò½L)`7Tð •2—á<ÌjK†åj’›ïf’|T¦ESC•MHÒ¥*Ÿ‰Ô¨…=.­Å^mEïrɶïôå¡‹EQ#ÉØNÞ!¬RçÎÊ)( C™]%³Ø]¨"€«j¸ŸVÇ!7 »-C$"ýlí©Ôí¦RçáWeŠL°Êºà¼ó¼Þ°L˜Ö—vC¸£,±ËoÌNg0êÖÅTÚånÞ¼·=éš9ŸY… PÛÔÁ5l|“š ¶JË?YøH•t µYq;]?zJà ›D|¾ ûrü†’YíL‡mÊߪCmÌ2†PàÀ‡=áÇO~Çf¡ºŠFG®k¯¤;o¥ªJ%—¸íd™7ßã”ãùóL/{ðFÛ´¥˜¢ß¼[¢j2ÉBRíódmן£@ÚÐøòSÁÇ @ø,#ù:³ØžpÈ’Ô-¤ª,XQ*­ Ái§¹;-(?Ÿ÷Ù©Ë8ç‚P)â7×|‹îûÖhÔ±z¦0¸]¥¡LøÀ@¯oÐþ:+†âEÁêü{•»);) 2¦‹c¬’¨f ˜+‰NÚoz ¾W›Ý “Èš³èÂÁN:ì–„©JÊçô·‚NUüÚðHºÉž”ƒkª3OÒgd{‚ª„Eû*âºtF¥x‚Æ‘K"/8¥Ž×™’‰ÒÍÀž'ô;À%˜×BžðQ„03é–”áTÈJ\VÜ*å¡—ké”n’'%)¯ÎËÊ’÷|‡,^¡».È8¨Ã) „!¿F¼r‰äͤ¤ !U‚-XÞqE:Môßû:È|f¯Ó3ÿzí³sè$>¹ åñ¯ R?ùVÙanÆ µ†»õ2HCúúéÚ˜Ã7$e•ôK\Ï”‡§AöÖ¥cÉSùò%i$·Åß‚Mºâª«4Ž´Ù•Üe£SžçÊ:X+;Ì\ËߢÜ-¢°ÀŽÌþpp ¢ø8±Õ:ÕO”@æ£,âl¶³¦#Ôì´ÙkÈR;dS"ë5Ôù"QäO}ÚkaéÒœˆ€zäù²ÜýÓF ™Õg%µ]gd×ebÿèGÕë'ýbþJµ¹2—X“ÓnÚ¨,¢hõêööþþ¾5÷â–Ìo•A}ËÝV¶˜~±~5>HßIfpéÊ“œåÀsî!Àäyö"`Oåè-ü«é݆Áäöuñ½?¯SçWªÝ‘”3 ˆv‘vtП,A]úÞ'±&ÿûŠ ™ßªÞnÏzuó—î¯éiÍ71V”%æ¸Dê¹éµSñHøÉJßÁî¢ÝòDt+–ñíTL~ÃX߸íIðcåT”_ÙKÇ¡Ä?»ü  oÄýù=˜™€ ödEY:ôa¯6§ŠšcÇ»…[ð÷—hͯ_'V%ŒÙõ1nMPnú ߦ¿¦þ„JÌ4)hËÞüPKНÀG^:PK»[L> layout-cachecd`d(ðf``àd``‘1x$´€ŒŒPQ[˜¨1ʉrôa2¥@†PK²w­%2OPK¹[L>ß=<1ó manifest.rdf¥‘±nƒ0Ew¾Ârfü‚YŠdh”¹j¿ÀCPÀù™’ü}]ŠÒ¢UÕÑöÕ¹÷Èùá:ôìÍ8êÐ<{ÎŒ­°îl[ðÉ7ñ?”QîêF=O,¤-©p*øÙûQÌó,æT k!ɲ ö¤ŒC"¦›õú[Úñ2b,·”¨#VÓ`¬_Aáꪱ"š:Šq4v!Zlš®2 ƒñÆK»ãìc~ÅÉ|¯ø³¦'íüòì áä*Sð ­"trø5MþÖú çð}øj"Õã'ôÔõæ.#ÿ*ƒu³•ù15t¥êe™´©Jÿ]µõÌaýâ2zPK»[L> styles.xmlík“£Æñ{~¥”ó!UH¤6^§üˆãTÏ©œ“¯©Œ$|¼ Ðj׿>ó†ô¢}x`ãû೦{fú5ÝMÏã¾üëC÷¸(£,½[XËõÂÀi…Qz¸[üûçïMoñׯþðe¶ßG¾ ³à”à´2Ëê1Æ¥A:§å-Þ-NEz›¡2*oS”àò¶ n³§u§[û–MÅ[Ø`c»3d¹w…ª±)n§/ÚŸ™!˽ÃÇv¦¸D¦r÷}6¶óC›ûÌ ²$GU¤PñGéç»Å±ªòÛÕê|>/ÏÎ2++Ë÷ýƒ6 ^~*b†+c:Y¹²–ÖªÆMp…ÆÒGqe’ÒS²ÃÅhÑ  ]hµ¼?Œ¶ˆûÀh‚#*FÛCîª× Ç«× å¾ ªŽ:ñV? ûÏZ[(’±sQÜŽ¨‚"ÊG³É±åþY–5¤Ò|2ríõz³â¿%ì3ˆ~.¢ z¢(‰gIŸÐžµ"&¾§fZc”éÁ‘·«çYQ5„ìÇ;("»Y^Ç*‰‡—…Ö¨‡" {Q 9Ί,5bèæ}„ÏìøXþþŠ!- á7%_MhdSÞVJKjd PŒðU.¿&—ÈG!+Ý^¯R­²poá~I~,¾ªÝü>#.~l†8ˆË¯¾ä˳i6øo*ĻŇìUÆQFÖ ‹±ÆK¢øñnñ'”gå_$$Þ°XÁ£þDþô˜ì²¸gL øÄ0_ŠûÈb€…ÑéK!槸ˆˆÍY‚ÒFUYÕ÷ˆô¥*ybîŸ#bcÆG|6þ%†ê—‚'äó¦¤ÕÜ_/•ò•åK¦þÿ‚þs2>‹”ˆ„3BåcYáä%4IÖy…¿œ¦ÕÐzí<ËúêKš<x)ƒýâ„ÿ]´ý×^ÿ×]pH•yŒÍ.†QƒE²EÒŒŠ¦„>CãŒí?®Ù"Þ±ìÙŸNŸ(­pZF“µ^!uꇠô@g¯GßeEH_ 6ˆˆIæñÚ@´zd`ÃêrWFtÑÖˤB)ÍZ×˵e‰h/²J8Ñõ’ÖMk]DÁTi# TàZ› m,[YH–£‹¬ HÖFY[¬­.²n@²nt‘Õ+j²\]dy Yž.²|,_Y 7ÕåLဨ-">!,mÒC¥+ôX`è±t… =–®Ðc¡ÇÒz,0ôXºB†KWè±ÀÐcé =z,]¡t[º¼– †[Wè±ÁÐcë =6¬CmJC­+ôØ`è±u… =¶®Ðcƒ¡ÇÖzl0ôغB †[Wè±ÁÐcë = Ðå0ô8ºB†GWèqÀÐãè =lZÚl =Ž®Ð〡ÇÑz0ô8ºB†GWèqÀÐãè =z]¡\ˆºÖá =]¡ôºœèt¹Ð3èr  _Ðå<0ñt¥3˜ÎxºÒLg<]éŒzQO—õ@‡åéòXè²<]>Ë–§ÍkÁnK›ßÓOW:ベƒ¯+sðA/ïëòò>èå}]^Þ½¼¯ËËû —÷uyyôò¾./ïƒ^Þ×åå}ÐËûº¼¼zy_——÷A/ïkòò6x°…þÆdE-]5-°¤¥«¢´tÕ³Àr–®jXÌÒUËKYº*Y`ÍHWÉhå}MißÊú6š’¾ ”óm4¥|È£o4yô äÑ7š<úòèM}yô&¾<úF“Gß@}£É£»M¹šlÊ…lÊÕdS.dS®&›r!›r5Ù”e ž¦,ÜеîèÚëÿºÊÿ`õ_Wñ¬ýë*ýƒ•ÿ·-üï£86£°LÙ7Q• _Äô*&{tâöXàýÝâŸQP \®¬µüÇ–þvÖþ÷Ö·Ë<=Ô]«Çœ±•ä”-ÞVéÃ8Ùá°nBdhTÌ,ý¡°á AÅg\ÈÔ]Ù™ßí¥ð¿ÉÇÆÚ°×=žEÛûEb‘6Ó¢mG{ý+0Þ§Ç$ÁUTbð™4hÆ–åX†OoÖ4lo6„ú_Ò|¤ð–²*²ÏØ QÙ±šïÈoà:*_ÜF-pPÕØYUZw KþiÆ8=Ð7,ºöBavÕB½0¹Ñ\€l̈þ…"ø°æÃG||Øóá£?$ >œùðÑE›ùðÑxÛùðÑ«7óá£ÿƒMðá·þo<Á‡7>ú? þlø€Âà|¢ ˜\Í(»‚Õ1#}@Y‰z qÊ|@Y‰z¥eÊ|@Y‰z>zÊ|@Y‰zvyÊ|@Y‰zÜyÊ|@Y‰zBzÊ|@Y‰z¨zÊ|@Y‰z%}Â|@ác>ÑcàÖ»`c>YÉÀ5yÁÇ|²’{õ‚Ù”•¨÷ð§Ì”•¨÷§Ì”•¨7ý§Ì”•¨OL™(+Qߘ2PV¢>>0e> ¬D}­`Â|@nw>^wàAÁÆ|²’óÉJ  8Ÿ…ÀùD@ÊÝùdˆ.äªÜùø*Zî|V‡ -wFëÊÕ³ŒSæÊÕãSæÊÕ“SæÊÕC–æcà\&çC=—9e> ÜJ=Ê9e> x®žþœ2P2´<Ÿ]dhy>{È@ЛM̃ʛó©nDñMtª2:ÑB⌣¢8?¢zFÆ®ÀèóÝ¢¤ÏaU5ä\D¥-ÉBÒ=.Ìj×PE¸2£4Ä9á§]ÕDÖ4VhG4å%¡z5LvƒNé¾àæTb"†”j•M.^R«Š^ûŒSTF¿J-;¯X[ŒÒà HNYCÒª æð÷oöqUáÂüŒ‹”‘Δ¸¤cš¨ŒPJF^/·y#Ÿzøú뱆ˆyjÀ·/G 2ú~ÚCM­2b=Fê˜ è­@;+jÌ2k4°M‰íø˜qÊ^¢3c†DZŒ–»Eš½'QCþH‹ËO){Žx&`Â%á;¢/Ì=e’µ)™­s±–öÖj×K×hs"Ìv±<ò$µq§ús”žñÙøW– ômmNYÑwøôŸ“ñ ¥åÛ'›µ1³Ù1ªŒˆ€¢7²bÙÈpíö‘õ”3KPA< YN#íÆf‘¶mßeU•%,[¶1³œ²Œâ3z,Ÿò*’ËøºˆP|á(6í‚yÞZïàÚeKi æ Cé~7夆 ~‰òŸ­×§uÊ#Uˆ 9•U´o(?Mþn«y&ë[漮 ê‰NÏ‘`Ý1r„ÉuLÄk"ÖxÞ¾E9õµ¯¨`ÂL®\¹­*‡´Let›ž’ñÎ4¡(›È ,Çàà;þ¾n¿hÂ;oã_RIðÉËh-p‘hOg@Kã°4KóŒVó?H^ÿðŠJŽØx ’_Ic¿åb ¡¿íZ¸NLajCMbVFü±io¹mÓiþØs@¨'l®žìk¹j×":å#Ñ~ ^#ê2c¼¯úcÇ1d„ߥCQ$ ŠʇÔD0 ë9JRRÎQicg§Š©"Æ÷4…·EwÒ'¢ƒµý¢gL%z·Øeq8X È{Õàþ¾­ÓèÝ °þWy3¡$V’­YIö ”´BøuºS3ð§ûuºí] èo¢}Ö¾£YûÎkiÿyj~™_MIXIÍJÚ<_IžìF_²B=Å=¾êíüíÖçw:?³¬¢|³uR˜§ 9Tá[ØØ¶šmûJÆö,«z‘Õ¼š;¸5t£YC7Ï×{µ†Ü—hh óË5äÂr5kÈý]C¬!O³†¼ß5äÃò5kÈÿ]Cô_[€TÔÂu}à¯ÿO•ô}Aþ¢B ÈXDÀe¿¢š¡â¼Ò6Æ“9çßÒ0Í*üœi_c{V ÙΠïw÷ ®ëµ¼±³/w ³ÁçsÐmX³ƒ!×ý^}Ob­ :'ú€í×éúšý¿oNqŒ™f?=&Äö ˆ£ N³¹ ­6‰P>ÖO9Nù@ X³¡4ŒJH£Ùý[’É×q0boˆ™ìðG(‡?{ãæ#³áŒA=5˜ ªž^«íDýó4CQÒõ“ 48f§ÛäµxÙÛÝn„ß¡ÿÿx±¿]»,{mõ»¬KH½‰ª¶K›¨ëΙ*zH‹Ev£@áÇ(0Ç¡ÙÐ\^ £8&(Ù©h\թ艞²üPРݺm˜j´öŒ%Aì€\B‡†!Üç( Ù¬õÒÚ ‰ðs=ììêÚ£Ìâ(4ê¸õÂ&˜ëDãÒ4~â…‚È2Î#€²Adlî³"AD Ñ£¢«jE2H˜²I‚";™‰ëƒœhGÀìðCB%Ð3¬‚"Æfû,޳3Ñ÷î‘J¨Ðn!MÞœÐk—ÒzéÞØq³…(ÖÌ­œ{™&Ð!yŽ”´ý®$m-×ÖM¯¤eHGÒ5àÍ%í¼3IÛ€"gÖþæbÞ¼31oíÍ€œ[ˆ"hxsIoß™¤]×t Q$Ío.é›w%i{¹vúá éHº¼¹¤Ýw&iÛë‡2D‘4¼¹¤½w&ém¿›–Šœ·¿‰“öß™˜]HÎ-D4¼¹¤Û²ê»µ³\oú㡠鈺¼DÔ],ZÝ(égë>:œ vßÅh¦¨u’˧²*,Á1¿Ñx⽚)뎥$vyJî“Ö#“ŽfÁ‰ébu …¸®ï2HUüæ/z –•ºÈ´®<Ô£®ÿ° *é+ʈÙ[^–åõ˧æŽ*¤@—ƒu]~ U*++G Ò*[e°( œ0#Ü2##ãF(A³?iõ™Œf àÓ1)Äè‘W§6ñcžX‹$¥ªÉ ç(¤}mòÕØÔŽà(öBlér(¦ ¬Ì¬ˆ'HXZVTŠª‹ÕÀ!ÿþ$©·úe·ÔðKÁ´:•òûe;|>Ù‰øšæÒJK¾µÐÖ®€[°|ј zh$@«sµ4¥ ¢„ˆ©)s’éNIZò{ô»wÚDÛñ2ÝV’ƒqB%ŽíÞ¸f½¹×þª®Ò+ÇMÛÒbƒ®ÆŒêõŽ*F¸¹ãÿ›õ,¼J\ŸHóЛï^«©úv§¹ÃÄ|>›R.J68hO×x ée¾bäã߃v»Ã&Là²Ä׿&j¦Žìx9_k+™S©ñr ‹Òþ¿X”/Y^=ö4i•¯=´ˆxÕõÛR똈ºÝ)Ó,yöÕSã(!±w[Ž7 •usÔ€ÿPK°Zî%¿ÐPK»[L>2©[ßmeta.xml 2010-12-20T02:19:042011-02-12T11:29:50P1DT39M34S362OpenOffice.org/3.3$Unix OpenOffice.org_project/330m18$Build-9556The tmux terminal multiplexerPK»[L>Thumbnails/thumbnail.png˜UTÔíQ¤»»CbÉ” AJr``ˆ¡;Gé¤DBa¤¤cèøC‡Ä\ïZ÷ñ>|ë;/ç<ìýrÖyø«§£FBÈHˆ……E¢¡®¬ÿ¯ÿÄÂz‚…ÿôßøæk}†ò›÷þyGðÍvÒó§»VcÑBäʱEnŽ‹’0€~Ónˆ‰l—TPxæ:¤ï6 å&U=Ý^¹zÉ—¥¯fBEâ˜ é æÌû+Á5‹-iË`Ç×ÿK{§ïÔü޽ÚLTÔìžšmÇÇñÊ”òzȵþ zZ·%‡éï]q}’?Kߦ;©Éã\v>Ë5׊+àœM½-Ý[5Ø#™!Ñ…ÓÕÝÿ.˜¸ÇÊÑÒà–ˆV@^¼öí`¬:¶Íè¾6fB+2&ÑxB¦ò`ÑÜñ%¼ ŽG»yÏe;ùm“ÅÛ'¾˜à´L xkò>"ú“+[ÌÆž éûz6a{È7SÒv#Yíð-TS‚9“¿õѶþ•ïÓÂnÑeî>å¾àÓŒÞkù®fDò˜œ`˜Òÿ*ÄÆå}Ë9¸KÑ Ì.¤'µÆÚ8wm)D:.•ù’ˆ“F¿jo×÷°JïñR8/×qm©É[ÂŽ¤ˆ@xïNa7ÀJ±Ÿ– \Âåp»¿ý’-]¿†Š®È£+UýÄ̽¬ãŠuK™eÍ„õ)ãæ6~‹vØÎŸ šð¶Iˆ:‡n5K”?l²-ðúl's±[´X»¡j¾úúyoºˆïÕèÿó6­o(¬­söñåB8R YÕÝèí-¥ƒ:%1 C–×Q[ îkî[™þÚ¥?Æ'VZeW»bœÜ2S#Î^¾ÙAù˜³ÐžO7]7bóPýyø4CÇû}®ùz[äŠÞí$.ï{&Õ^Ç©’©å¨Ò¸˜æóÖû§ÎT©`øx<÷Û¼K΢f5¬²\åêàÉTzS~f šzÑ%]µVºû9¤1Hù_›å¡%]fìø[¤ ¦ö8¥&gO}ØI9f“­Æ0×ôe–:dz˜Þ;ê”:ß;‹Á7Œ+a«ž½Ë÷ØûèzràièÀ‡t_±/¡”&†ÐKý¦T© è uSÓER:ß‘èàfZoì,¶Ö!¼òXL¥ŸTt3ðqÑmʹÍ`€Q’(+Ø“&ZšŠ*%¢¼sâ~¡2§¢• s¶G‘B¡žtÎQ$§­rie0,å‚OÍ©È6 ,þ²¨MØÈjË‘¹å9w³oBn¥î€ŸE¦¡Ú'“üêkˆ)êð÷$ò„?ð8<×9®iÿÝ̉Ý/qNH˜¸Ö©ö‡†tñÃÃf$  p5Åh§¦rì™ z…!P2I+öV´ÐL¨lQv¹—L+X\åvÛGX‹ 0+jpÝÓ§~Á޾;£§|&ÉÔTåý‰bÖTÜ`¼"3%‘F²uôŸÜŸcxí10ä¸U‚¤¹¡‹ÿòáåL _¡Ý kõWÀM(ùG#iÀ7ÊLˆ·àߟL½Í CÖÆ ·ìÐiB„lãÞ—heR¤0ÇýY¬_@\«‰)çÁ ®s)™¼ñ YŠæ¦+‰>©°Ô’¤ró°ìùÉðÍKçôy ¾ÑŽˆ[ð"DøB2iSØ[Û¬}¸'enU¶4›ú®0‘jÐzp.êxÏ‹»Ú[Eo–Bëf:ÇM±ƒ5yXÉ×Óý³R%raë¯U_@^£ ,ðÑ^rÏÈǶ¤Ã †¦ŸWã‡<+uXìK Sº"ð‘ž>œ¶™i–ΟR*»£àY7 ¢ÇG­½iÞÄ,€ð~°ÐЇD^ìå:”Iñ ä'|8;ëçA‚Ì+ùþNô2øeBQÆŠèr:SAÕ—Ý|œä-ñüõª=ÕÔΠ÷]³ð´Lò𢬇ùñ ~~ݰ›eÏQA^aÑB[Ù¯Ñ饯SX…tŽEzW P3š¹%B\ƒ´\²É÷ôèÇ_"9§:˜'fˆÓ¡ZûŒÈÏ¢òÃ×u²ßù=—á%Våà©ÆhlÐÂ+Ór=Ü9˜.1/êçÉ:62¸ìý—–‘Ý‘,­¦v^^¯šWÍšÙóòÁþð'BÓ êµ0Žsáõé‚AÍ9éyÂàyÇá´ø ÷ŒòN&Ì#sy8zš§^/»zZª±ý6g¢˜G^gÁÒof²>QW6ÓÀß½ÜÚµì³Ë}^NùÈ«ý•¿& F ŒR2Š—­ÙÌÜe Ž2†ý„šffT½Ö£•ÒþÓ¤è¯o#©½Ôøˆ)µ4V-ÄÛ²=QÄc˜¥Îgšxy8ÔŠÇÝOY"cdmvX„²ì‰ÆS˜'ªÀÆÊ¢SŠ báµÈ¶M"a½ïhÆ=à ¥je«Š›ñ‘…(}høMjÈHDw‚˜l[±åB£‘¼,T·}ýSëÝ£Ú<  ”QÚžˆç_)‰gÜ'S?‹1íù`S;=`’zžïO©Pm»Î ¯q(Ÿë‡Vøû%¦k-Îä†YÀºÄäàrI¿“¶Ñà7\j8mäðõUH£êã1¡D#Æ….&¸k¢ßm—=#R 3«ÂÞ¸¶¸p©ÁÜ ŽBj šbQ)¶1¹¿# üÅ%©u«®æ[šê¶Ó²ÞCˆýü/¹îw²DTÊêyS:Ö²ÙT/÷2Îúl*>HQúü™§ Åd´à©îž­-S_ìé*"ïêeœ]>ûñì¿›yq9¤9ZEŠ`nwýS¢LI^¡ zHÝ0«¡{ÑÏ£‡S^™ ¾@V»ë铸äEÝÑO Ö6Wê –¥k´†b]=>ð½ÈÉDÚ-íÕÆŸ·žMI[Îtšã@fdÝ[íkÚøtlÛjÑÒéR/FôOŸ†x.˜Í Q~ }ìÉw¼ÌHÐŽ'Ù=Å}|Ñh"<ÑIèS#ÿËSlΆhRa]±¥¦l‚@šV|r©ÛÕÛ¤xqù HÉ.~lO VªÍ!ãW™·â2(é¤ßœ_-äÄ݈œøy°?µíT݆bËËÉ´‹2=õŒ©Ï„ÆvÐŒEOãËËÖd»ü0ã;=­Kòö;7x©ÀÚKîÕ™oˢäeÙc|¥s˜‹\\µÀS}³=¨$vì­û‡@\LÇVó_ª—¾ 4Ÿ.övD€F¬;\ÑŸnGt‰dOOÊx¦­¦9d. #p¼;º_þº®æ g“|*ð„_F½Þõônìm*Ø%“–®&×ÄZê@;¸šy—Å=¡ »„AhNü&#úϸjt ÷QÓå®&ê{ïgz³GÝ |Yxvl! ªÔ¾›;TW„Έø8˜Ò”7xfþÅ$½;°ªTqtRÿùR4s¿Œ¡ccÐ s`65“uÑ1û»¾ýëöìIìí–}W³=¹¬Œsæ É\ á(°þx?Ø“Î-´šŒ„·Û¸³ÅC¯(¬Õœ2ëò„öC™ïð‰³¤¿Ô8‹¼1$•™‚d½²sâ·L¾å^qpÔiÚIÍlhÕA‘ \âŽÃìµyµ„êCÞ%™UŽ&즇gC&ß3éa°Üý÷VW³¾­‡Cˆô…ÿ,Ú5äú`Ü_‰].‹W 8ï¹ï›Ô¹‰/9¶È«i²5Š™u¦ƒ¨0|J5”¹ ÿ¼t;-Ô³ïhµåBÏ·„nCd™ÛˆûÈÔ¾ •Ÿ­™#v“ ©Vn;‡Þ8~¬Ëj¸Éijš!Z8%"@yæ÷­Ï«ÓÕÇÖŒ­ò/ÃlbÛ˜Œ¸êx§u ÑÁŸ)ä{ä5&3ø¾øî\DÛFÆhô ¼ÎûÐ8ðLüh ïBB¬R¸ê“Á¡EfÑà³bk?FV½¢Îʦ4x (øÀÉm¢ÁHÚú³;¶ì™6B‹’>PHȲ¡Zò_;\¾îÇÐôFúæD¹=϶I‡R–…ݧµÖøeñMB¹!•ô©%mœúSžA— |Œ?{ ÖöÔX­Ð_)Ú‹Q¯tÚçoE×gçûkãs¯Áßz°+^'ËÍoZÂ'³p“L™–d9 4ý•–Nª]мš Жx]ábwwbðån;­EˆïÑ5Ió>áì6üÑ@ µ<;¯4ó¾™0z÷ÎÑ>þ‚çHÈäöül!<µ#Õãv¯NÉß“´L`Lò²[V82 *0lÕêÿ¡^Zê-Iaf1GÈ´Fžø˜mr{7xC‘;O)LÜà°ŽæÈÃÿ€l4Eœ×øž$eÀ0F“å1‚FÍ!ó¢Î=OØ® ƒäÂ%œÇÆž§)5·ùþ~.þñÚ&Öœw2M×ä÷( ÄŠè ME¦AèÜèï(>”i•E<åü±1ª¹*ðçÈM‹ˆxasÿ" _ÊB›E¹( yÆ7½×faGd!NŒFèl + UU°!§îÓ¯BaøŽ›oÚ1/uš>Åš©¹¤7­ ¤B¶[ºP‘¦AÎ]^ä#j¼çMØð#™â› Ë=ð^+òšå§Ùb6Ÿ'÷ÒtÞrzás—;,þL‹2=^äãÛ¨ØC?¦üiz¥¾=eª®ú¾w•úð!D°ßª"Û°mWýÞ^äÍNE®pW\$mÐ÷ì ”>~›= 1¡mcü½£±­áD»š¯’ŸÿEŠzΊΔÀØ34§HåÌÛ÷Jµº¾ÅeÛ²ùò:ÍŠ®Õƒo…s†o°h˜—ÙT•ñòTÀ«ÀXiŽ£®½ö¾?Æs+EúG7öDÙô•}4 õñ[^Ç£‰ŸÉ¢öÄ$a"ñ•T:Q÷uÉ“0â“Q†ʼn9¦tZÝâ¡—ñ8@×ø}ƒŒ@î_ßè‰r•[ì}[°ý0nÚóYƒ¿t‹·±ä†ÃWsêTœö0£ ?1]Dwðf¹;}^O4Õ2¢k‰üöØÖÕ<\/MI›2«6"RÏ z×ø$»Rhs¶‹PWç/³8:ìŠx_&ã……¦EëØÀÄdˆ$Ýîù¢óù‚Û±ª³‰@C.¬Vç÷Ê‚ ¨ÃOâLK¥VÒA m(tÖzÀ¡ûÅR¸""öº”{]—a¾ü/y—›’›ó€¹²Åì³’½ví3·ÂïÛË}ý+$Ž£ùÂà»ë´¿WÉ´q9±SôÞÐÅ¢Y“»àñÝ{oÏØêXL‰7ƒŒ<º€õ•òÿÿ'0v”åS‚0$Ö¿ÒPÑQ®V´û?PK–X—ô€´PK¹[L>'Configurations2/accelerator/current.xmlPK»[L>Configurations2/progressbar/PK»[L>Configurations2/floater/PK»[L>Configurations2/popupmenu/PK»[L>Configurations2/toolpanel/PK»[L>Configurations2/menubar/PK»[L>Configurations2/toolbar/PK»[L>Configurations2/images/Bitmaps/PK»[L>Configurations2/statusbar/PK»[L> settings.xml½ZÛrÚH}߯péÝÁ8ñ²8$ÄØP€ãÚ¼ R³M«fFþ>=°^@‘4ûä².Ý=}9}ºÅõçE$Ž^AiŽòÆ«8ñŽ@r9½ñžFíãKïóí_×8™ð!IÒk0†ÑGôºÔìö—(Ù@¦¹nHn˜ 1Èõk·O7ReÙ•…àò寛7jµù|þaþñªi­~uuUKï® PNøôPUÙÓoU!âF‘}!3&Uvzrò©–ýï­Œ|ãšSïví‡õño¯W ²?ÇÜ@d}s´ºlM»ñHeã•Ã|ã5oß{ÿ}ç=ï+`#Œ½õ³ŒéŽ@9õnë'õËó³ëÚ®œÃewabö ?)'÷™‡f¶WðÇÓ³úU9á߀OgûÍ®Ÿ_ž“>œá|!%´fLNAoi# `Ò»5*b::²©p®áCÈ“>aB,þ8bñ1—!, ÜuÖþ,Kß¡úPËÃ\Þ ·LÕFqël›Ð§ÅC™›{g%2$¿\®ÎO §µæcÕWK*ÖM§¢¹•bëð¢”ì&ƒÑ~Ëë—E«ü'b4"YÛI7CeJáR—-11-I$·k»*éMÄ—ÊŠ{×/mTûm¯Ÿ´¾£‡ 0¶](`úž‹oA&ïö ·ö?@òðÖš]H3Ô¨ÿ¤ÇúBà¼Opf¾ã¸Åd¢zÔOå÷Y ª­0‚I¶ë½ŠüðÃ,~½É„Û¼JhHÏa1°ÏÜtG}Júš3ÙOd`’4œ®2Kì`»¹U&Ä#x©¯ÐPµ¶Qmco¦w‰g?Å!3û8ÊiJx¥…„ (œø…ăÚ[^LÃù§&—L-½Ú¢¾D±Y¾›ée3dH %€€Ç…†V¢áï3Ì¿…QÄd“;À%7ÍCP#X˜gÅâžìÿé£ Ëý8Ë' ÊÚ^}žNö™biycXT®„&eüÄ`VK°r*QA›+m¨d¡C­TšŽ|L¢1¨wŽT]FÙ¿CLT°ƒ'UY8¢K7q;Ò¾ŠLõñhs¡Ë€Ó)ôµÄW²x»J+«Ë—aS0ù¢©ÑØ k1$ÂiGö¥D“jÈï=9nšœß¸à^©ùm¶xÜåχWCÆ_û‚0CApëÀÖ;|Dó=цO–6°ú™›Ù“ MìÅJsKWþí±„êè.ƒ¸[-Ýœ¤'ÂÕxŠmðÿ5ï=S1ÙQ¸&V§(•³OrA‘ à'(ü² á€…nB›ÍP uLÜ×ÙH`·%ކËhzEÁŒh»R®|Í‘žâ+Øì¼,AŸJ‚Ò¡Á˜¸¯”$òßxHÎâ…+É–øÆ(Ûåˆ;µÑjdøgñö7œ³tPú6Yð2U˜ìðýjè3:ÍéLü¦Ú‹ëè%Æ®á»4¡‹¿-±Ò.yí&:4Ѹ¬»*eù+zÒ:NÓ|ƒ åö]&§ ¥@ÎÊdAô¢d"ý9R¿6 îM™âl§âï>M˜rikª°„/2|÷ý²›Íßé²Wø*pÌ6¤Å.`œmÖ t0êº`-‚ǾÞ$³/jf>+zTµÅ2]O:;(;Ùï¿Ö‹é6>']gÔæ©£Hl¡Ó¹úT¸ÚÆ‘˜{Ø™aÿp픑K—P—:ŠÆºàÅv:'Ë›læ^5¹¦ƒW „³ßbótÙ_¹ø”%´XlV µÝ“ý]p­Âoä'?Ý­g“=µëÆ©¥rºC§]ð‹>?²_7ôdK vq²l!b¿|Œ ŠE¡åHî¢Úί1jy¿S¹ýPKƒClé"PK»[L>META-INF/manifest.xmlµ–Ínã Çï} ‹ëÊ&É^v­8•¶RÏ=t`‚Ç UýöÅѦñîf?œ_ høý‡aØÞ¿¦xA´¥F¬«•(”m5õøþüX~÷»»í¤; \ŸEšGá½Ûˆè©¶t¨  5«Ú:¤Öª8 qý³}=)íîŠ3¸ÓËdèÇâ,†­†’G‡çŒVÀÉOùBmuÔªæã+‹óìÙª6³á.S:àC#¤‹|Ðô(õ—qOZqôäz5ÿ6³ÿçÕ×ÇõC5!–IÿCq!mŠ”L;r™ª,ñÏÉ`w¾I{MàÇË F¹T ø ßvŸþ¸ŠÓX•¬nžÀ£ÁpEtþŽá èeØó!{m‚äSófY7ƒß„÷`©Ó}ôÇM  J¡ÁÔµ^ªèýu¹¸\+‹€ó¶OöI 3syo]tépyðl­q@hòà'dzÅ}ò=üxÓùMó.S™ýÐÈ ¯Éù_¯ùiªý*êJÍ5þÏÛžûÈœžEï'ÿVþö*Ú½PKÔÁæišP PK»[L>^Æ2 ''mimetypePK»[L>Ï£€ï[[-MPictures/10000000000000200000002000309F1C.pngPK»[L>НÀG^: ócontent.xmlPK»[L>²w­%2O ìKlayout-cachePK¹[L>ß=<1ó XLmanifest.rdfPK»[L>°Zî%¿Ð uMstyles.xmlPK»[L>2©[ßÒ_meta.xmlPK»[L>–X—ô€´dThumbnails/thumbnail.pngPK¹[L>'Ü|Configurations2/accelerator/current.xmlPK»[L>#}Configurations2/progressbar/PK»[L>]}Configurations2/floater/PK»[L>“}Configurations2/popupmenu/PK»[L>Ë}Configurations2/toolpanel/PK»[L>~Configurations2/menubar/PK»[L>9~Configurations2/toolbar/PK»[L>o~Configurations2/images/Bitmaps/PK»[L>¬~Configurations2/statusbar/PK»[L>ƒClé" ä~settings.xmlPK»[L>ÔÁæišP Š„META-INF/manifest.xmlPKg†tmate-2.4.0/presentations/tmux_asiabsdcon11.pdf000066400000000000000000003331661356407164200215710ustar00rootroot00000000000000%PDF-1.4 %äüöß 2 0 obj <> stream xœ\M‹ë<–Þß_Që†ÔX’mÉqU²˜]Ã…Y ³ëž^4ô»é¿?Òù–äX©— ¹e[’¥Gçûyútÿþõ¯écÊ-Ûòé?Òì>ÓÇÿõ_ùø'>Ëÿþø¿_ûï_ËšÅ%åÆ¿ÿöñO÷áÖò÷ÿþ÷ur·Ë|üÍ]§pûŸßÿùëñû×_›þiù\lÏýK÷ßÿ8ê²M¯^8ß.ù]KyáZ~â«·:Ÿ>ç¾Öÿ¹½~ó\yñ×)•?XúVþ¼—™ì·KºN_åúZé$¿à§Ðo‡[(Ë?Ó«á>„˜Ñ„É…çiò@÷<þ<-ù¥ù*æ‘Ê ÛtÏïð7þBã%¿Þ_ñ'Ï7–†éé¦[\®ù÷t.~FœÄ¬“¸¹©Œ}Y¦ˆ ž" åÜz¥i•‹ o–‡©\ÏÚ(O÷–`¦±Ì.t °‰‡wÞkïïòÁ3ÑðÏÛe|«7çÎeM¦û•ÉL^Û!ŒØÈ•Ÿ–³a_×LV wç²ØTî¼€Íosæ²·2Ø1-"ÐSÈytymžÂ§\–î<@FK½øi™úŒƒ yrn.=èw¹åM΋sÛ&­jèŸe|I¼Ñ\çö^ûáÎïtÅïŒå¹ƒgÈÊ«p{îôˆv Æk—š9Á¶—¥9xxo‡ÖÞu›˜Ó^PT@i§ËgA—%/“AÊGÈç ÈzYå Z¢Aa<&*ÓP‘ÈŒ\„kä½·íùúŽ#-0˼ݹݣt›“ eö²~Çî¶sˆ{ÄFÇõ3´ÿ„Ð`n±,98ï¼]ó=2·ðÆ(¢'º¹»¶w‘Þ‡WI©Óqs–+L¥Ô´Zˆ@×!1‚nõ,ê-ó{wÀZ9k­¦„‹?$/ìAŸ »¨G„š'ÙFM¶ ‹˜[/æë¢Õ}[a/x|Ljë{˜F¸Î©×^œ¹dSçZXýÞ «ž0jùPõ†¶{¹Øô&4Òk :Äo»•)lEéåéí"\ÝïMePuAë5›lO`^pŽvÞí0÷* §X-î| KÄb!Î"Ô¾íêÉ[xbYÑ=ÍBƆ›‹¶_¼v©hÓ‘v(½]!QaºUp¹½ž‹ª?·D!m(ðÈ54"XøœƒÜa6Ù»^‘åÅ%nð‡åä]Å%Ò{;W¤m±¼T¾˜AÞ9hgIGÕ†l·–=›,_M4rÓ&¨†‰÷@…Œjësa€!;Å^eæ(ÊVšã•ÔT qnõmôÜô-´ ¡¶ZÐÌpå÷ŠŠ×ÄCg ´—mZWyÙ™ýâh—‰²ÎÒfdFõð0œ0Ç`„—-©¹wÄÚBÐÞO½•%†NÞ;`{UU•ÚñAN$¼‹T"oà[Øîˆ·Â¬>Ù°!–‰×l4ýé‹Û£1!t0'–›  ,]STݧvÁN7«‡f´YËÖKóorŠ2:§ŸE&3«—_åCR;6Ç^ïm„U¶«¼p7[š¯=ºë?n™Ÿ}‘Wþôí,––Ç]½ôç¸v0pמˆj1‰€òäÈE'÷¡Ð“W–Ÿ‚MëtÙìo[#Ý3Ž[ÔÁö‚ÕV¿ÀPâ´ò(cl·Y†`sÈöTD÷PŒ° ¾Ñð¦]´++€‘ázÓ›\)¢f t2?6'˜n*§ïôD[TB|m¤ˆïÇ,D˜6¹Î Þ‘ ¡ãÖŒË Äf–Ù@Y&¯-÷®¯µ„Î7§f°9óv`l{ Âh„Å=m qàdr³‹}¼ïgTC໕¥Ñ"«{vvŸVµÀ#¡aÚúžÚ&$—Yìì6ºeÆ9 [1É}ËŒ©´éÀ²Dh{A§‹MÌÅSé¨Ho":ÑÿIMåý„&/jÆ(ȈI¾±¸ÅÒû"…K„˜|/q£š!B¿“‰›P×bö$¦lvJ³=N#`ã5­„”Ý`òúà2ˆ{Lý%²“XxK"““jCî‰qsR_Èf¨ÝˆeåÙòêæ ö"Í.ÖjÀUmŸª–‰) Æ“;Ñm‡ÖÞ¥7©%Z¸"ëzÃÕd2{(Ô­ƒ“Giªð˜Fì ê{®8—rÃ$ÄßÁw’Ƽ(Uo8“Âç0v¨Œ`œìæŒÂ´¨ à7#ù¦%QêV«ua¢&è­Û_t~ˬ‰î5ÁŠØ¡[™£4Äîž½‚ªù\ÏÓ–9%3ãZŸ¨p/à ¬ …@¦Ü“2Ù³1CÚ>×·’Ö.&amìzrk¡ .^Á¼[¯¨ÆÄÇ…È=š“zõÝ&bƒ º…R»§ Gϯ¼ˆ÷n}#ÒÛÃ3 ÇÞ£ãH/R ËÎ&ˆh­v^9ÿk0¡‰;X7×Zs$“UíœÝŽ\ RG’´<ö÷Ûrõ{ùùÒ|t$Ù]g2XŠœª¢£¨«? ž%Èf²ççäƒ}øŽU»ŸžìžMdVÖ@¬}Më{õË«þ§æÀ9ZÍâGXÍé³E««ZÊÁóm b Œd–ë÷ÔÔ2“A@ŸR‹îEœU_fŠðÌh&åÿ´òßeK$d?>þä5LFÉ&LŠžoG·îц¸¹—°§éµ2yÿíþJÔz"v,K‰ÐEëbq›‰U•x øcs=Mþvj">e X.D¡½8[|§´ªÃ@æA¼çÃ’†ÓÔKÕÊI)âÍ?}*¹QØÂ*:'dŸ:+wäæÔÚ/,òàÄn7Î*!ßÇ;NX±*Ú,¾o5‰¾äOðþ©Fláìœ_c'Ž8ˆ´´!M˜kx)ã\œ%¸ð ^ÐøºƒÌ #ïÁ™Fs!°)K™¢ÀÆ,cfbq¥Þ%“X1Ù)6Éjjn a’ц¾I €WH`s<UsV«‚,×£ðzã¶pw=™.V•$`%¿õuù@!hÝ4ð¯0öõÒB÷B6bI'ê ÉÆ9^Λd£lðk±XOmã@8’sê¾ÔÂ@p*À¬£RˆP…N@ŠŠ”®'¿“Eè{ë1ºõ˜¤ÝÒÚËnå\×*y·$ÛZ³†k›J·®<;’Ó0F¤ãí¼¸l–cØÒu”›¦Oª;Ó¦§¬bÀ .®½òÑpêNFˆ¦jaÖÊ"_ˆ}vk·0-g¨^$ðìðkb$¢8ƒõ*ÿÙuÉš M 2¿æKgˆ¨Žz7ùµwŽÍ÷>uêGšÁ…Ÿ1D8`‡¢ËÚ@ eD∠2ì+µM±&k`mÆG©ò½ã$.Ñ-ezs‘ÚA­ô™å0;zɱ­ÊÙÞ­„î•O•- Y#h§pÓÝÚE9 ¾)Óßl†)÷#™b@§FJ8XöˆÇ'× 3zêl6ç~³„_#ˆ,Ï ;Iè$D¨=týC–ù¦·aoÆr«w%`TjW˃$ãuQc‰ƒ8kˆH5ró9fÌRì Iò–£<3âÅÔØ6ØäÊ_;¶ûQqÅ{Öõg&€¶Wš€¼Þ ÈÆ‘Ÿ ZªpC W£B+¯Uõ«Ôì(5ljøj5ŠÖÀP Pî’Ó.’€çYÄž|ΫEZ^ÓE%|3ÚO%‰?W—uu E)תfYLö+I;ò¸Ú7+£Œ ù’,‚ÛÊ|—ð¶T¥j«H˜Q¢Q9“¤]¦(FÔ¼qxˆ e)D7c…ð(x•^ "uø^kÕ%$8óЮu¸ÿaëSÑ#ÒöDÚð?æ­ë Ú…‚ÛÜŽKЍp‹%­aU5ÕBlb/¡&Lé+@‰ÂHМ#Õ­{ˆ”_^9Y&°Ñ:Z <·¤×åAe6H­Î–rÁ%6#‹$ââè– ë¾â<Û׸›unYcâ/ó>ºxĺìŸuölT¾³þéOB©ïN?¦Ç椚ûy³—ë¨Kue8äÒÚ¸‚•(Ť!d-(Ç(¯1Ås5IË_·Ÿ._{¨ª\5dE¥+#ÈsŽ}ol*ÊSfžlŠÌÝÄÛ£Ä lëb4v/²F„IÍiâ÷µ‰|Þ¯vD¬ëÒùÆ«ÕrëÌQ^Ð䉤òq¦Ú­©…£`’ ´æL2´ÞW/Í3·…â%S´sŽÐò®‡MøÌ¾þé⥖ì:%)š-éÌZ(X•g¹[5_ºOàV‰Ðª¢WPì†rf(Á™!P)§XRêÕàñù0›âkê· ŒIß°Á³C®îT©5sD Ô‡$Q¨äA´aéÂfß+kCÂè19! D1“Š<% -mÄ:`XÅéLMj2§B"Û ¨J™åº%FE¹ûéa껣-å@ƒ%þ«ªÖÜ=¼ÌFzCôÖ©/<3‘\µ{xYÛ2J:æ$ Ë/)þž4°ÙË9:®ó:W"_Á?ápžNâ é+¥e|rí}&yÀ"þÔé!‚<¯] Z¦ŠRDʇ`-)j Uªõ¾Qç¶Œ$ÜM |$I”®ù¶Çã<2Ü9(K;5xp>vþL ¡g˲«¼ˆ©-²·wëÐWÚTNÄ Œ“‚ÂY[4¹6ÛG¦¸Z%‰6dSÎ ¡eH.t? ÑEe(ê×*1pYQf‡²ùT{Esz!o†ÃöJöL_‘m,sš°®0+Út,ÈÈ…’|]”f^ò^DäX‡ÓÙìÓže£$ànDZuã¹ð#j|ØÒÆÙLIQe…¾ªéw­û§Tœ"Øã1D0-½&0§±°ª ìæ¥ó«ÍÍ ÁFˆÁì`‘äûSýœå^XoÁÕÇCÕ\upö.Îï9jCԢ뫋ýI”cO>­z¥I´ù{1%üÌç=69cdŠBZ·íË´™"Ë•ý+·J{o2[©wç3þõ\Çç!QTö0 ÕÏKr|áÛIL]£2œz«¨ð@| ×Jº\#N̦AT1ôJͨ"Ù™|w(Ö)G‚쎊ìs í`9Ï}æb¥é¾ë¨«…’ˆt`̾7Àó2EG¨‰óøWUÙþºv¾ü§gyb”›GüAn.¹=æÀ -–·Ù'6¥(É•ä'X› uWk¾éå↱á&ºÞL¼ 'Ø+ÚdjµµæøCÛîÔFv¥çÚb¦/êH€É[WAFGçDß­~„WØ,n#8À¦Ç=Lí{ ®û ªù96JbD¿àâM8þËt•cëbt©n—€{9ìoßvp(ÂêZyfíL‰2O1îbœúGk.h<Ú&¦$7ÞÙ›¶šÞ5ÝN¢ nÝtt!IaǧԫlÖ¡-î8UBGòÒùÈŒ‹Þ>ëýs<;t†x®Y‘ú[ !¥\–PÃ" ÂÇÇ%K-'Se FèëlÆ~Kùðü£Ø¯é¡² ?꤄°0MYˆQøXóÀ kø©³'øÓ‰jMi¾ø¡g˜Èª#0|Ð…œ?w麵Ó bÓx>–ÛÍAšÆå rZ×…WÇîÕö<ÕT=$£HmÈ®[Ë~·H N³œµDz’[ó¿Á–ûͳžxsËM·ò´ž± •/”¯ÃÀÊ4fõY½‹ý¤ƒ”€–ŸðÝŒ¢I½ê6t­µlÉ~ñ£Íõ1õjN>ࢎyý,LUËŠ ×Ÿ²b7»îÇúŽDô¤ÈâîBxdÒøºÕç†L(&˜L|#‰ÖÁΘºEaZû¢ªÃ“a•ƒe2€å«BdàêF„?Ï?,P0=΄rSÊ÷`û2Ë_0aê³rT3PÕ¼ }\€œßywÚãGÅ™…ò¦N€:½›œÀkJv<%Å<§l\Ûg»ü¨Æ/¼'Ò¥(.Å©ªíª5>JhJjÄÅóÿA꤇iìvpDÞÖÜ™O1wë²õÁð¤eUá§r¦©¯0]Scš¯ˆăëö̗͘Â)0"ƒp[ÎѱB#†Ö·5vßïÔ¡ÿsX"‡M¿Y}{R³Á‹³ŸªD–°wìw*ƒ×ïê @ÐñQ‚¾eIa,=Ýâö¶T­ú eÍ.§²¡h„h|u³ýŠ€ñJK©³¸–eM&‰»îȧ´©¬lj ͪŒ¿~ü?"ÅT endstream endobj 3 0 obj 5025 endobj 5 0 obj <> stream xœ½=Ën$7’w}E†of‚•ZZ`n^7°cn3ÞÅÀ^`}™ß_ƃdð‘ɪnµaX]¬$ƒd0ÞÌROúòï‡ÿ»¨‹JŸüîŸÌesúi»üñχÿúËåéYúïÿ~¸~}ð!=ŠÑ¤Î_ÿqù뇾hwùúë/Ï:¾ège_³VjyÔÏz{y´Ïêÿ~Áo¦o7ÿúò÷¯{xÿúðÓtrohjwц¦v/æY½«ðòèŸUL3õ¬<ü ­Ý³Ö/éóþÕ/:=§ÞWm¸_îÿö²ñCý‘>òÈêÿºÒN{¥®i.š‘çzåyöŬW@‹ö[ýF㦦˜ˆ¾6ðÇÂ7ÏÊaËì.f.€ôõ_ÇØµÛÓ€ßMâ!äM¢p†—§ñ‘¡/u¨_šyü+`×”ƒÒ[íE(èÇ©÷ŒYcë“k2¨ hïU_Ûñz£šû¤/ÅJÓô†§ÀEY$KI!ôÍ«Üü9ªÜ­mÜ“ë KV„B&+å-ÀîÒ¿‰—>胋Ä^+\÷U]ñË}ÅIÊ=…~zÚ:¡•à“2V-ua3«UýX™OÛ@(ùÌy­ • މ„d^,qmNÊÓ¡Í á1!i^ ~å~©¿IÌaœiŒZP0Šeõا9`gqšaWéo‡ÎˆëÊÄ¥>8Âoiùy;Då¢FZ´ŽÜœ­2Fb Üë<¢$‰"½ ŽÎTŸY§Z†)ÁЙ7¤zŠÎ;+tÆ8“C@hY4¤ÆózÜ.h$-ßVQ™h°ÈqãlÂ’CwŠXQ—$òHʃv3N—!:¡âà PÛ–Úˆø‚Žïwv@Å sÁ2üBLÌd6âãcÖª¸ƒ÷]§€H<à÷ùùœñk˜b±˜Ô9V€UÄ#†œdsàyœ#oÀÆ }nÏRÙôÅtöæ™þÄ´ó´)—ðÁôû–Œ$¬žÈ$3w]…)PIûX¾É„¤™ðÙª™Üð<“+sé ŽÄ- ‘Ô)V(ùhxÔ‰´:%Z3Ùù ïÖêȾ¯Ûdî“\GòöëÌ/ØáššCzòJtH‰ ZêéQ<¢LaùR4Èõ%D«‘&äÓŒez’¢¼bb}´s3Wœ#pÀÇ F ¨±¡Ø !£Rä½%C êhÃû<¢—öL{!ÛK™é‰G5$`1J ‹u]>ÞnH_YM%`»Ügª¸.ð: i…WuŽU浬ªð«äøj%2›‰~:ïîJ¨¢Ý·´Åh†xwò«+iñr¾Èꃺ„bÜmÍ—·˜rÍB.•½º ÁM-²7vwZÖ¾W[ˆâ½ì q`ýk«fM«ŠÁQ& &Í‚(^ùÍVff©D«JGµÖ5Èvd«ºRü΢‰kž‹*÷QÎé³#ªV¸ÝÔ¨€>ŠaF–ÏNR¡dW<â"u³ß–úX^yñµØêÖ¡º d|f×,TìÔ¹ª[a'XÐ\&±©¢¢QGêûT´Ññ)NQ¤ØCuÅkETQ’1aªë #‰Óßç;¬eu¾!9qW&;&°$²·Êƒ™–ùFw‰Ìc™Ä7Ø€ä­tßr˼½;ðQ: ŒlwâUa\WÚåÒ€,²qìÿ¥JÒ–ùïI#¢V˜õfT§¶5ƒÙ—»MUÎŽ‡’Ë<›wûžxf"Ùß[ebì è;oœêz60ƒ)…IƒÖIVïÙ+š=[D5´ñåÜ|±°B›Ýo i಼Œ'Ñ.öÆó:öZ‰­6ê¤Ù“e+ÎŒÆ=çÈwA25™+ Çò© Óì§‹ÆÿÒcHtd£J2C'ëÞ_´ßÓç?þyùõ/*6ý´§®6a°S–üò·¥¨r¼ÀÓöÜaOÀUî©ÛNCÙDß÷¶N}¡Á £Æííè/3\ñãN:Q6ÓíÁ`ëmÂD{âHº.ÈåfÏÝ÷bÿ¼ÏlO³©fpqC7³vjšÁÅÜÊ¢¢q2 ø}Q ¤»èŽ7%rõIy2‘ å©ÃÐ|çrŽÉm¦SñìýöίPôÉG†]zöš· n%ùØ#§2n'AæÈ01»ÙåAå2Œ¡°P&§(4@fØš—”õð‘•bäj'hËnÞ‡z»£9”I™1t+ÊvQµPR…¹Pü‰8 W"t‘ЧlZ¼XPÍ|Ž@Y5ߤè²WtT¥q4†$Íf`©>ADn(À9‰w4Yè+8Ò8™ou¤~ÛÆ˜1’È9 ùËëèÃ[CÚ´£ðR963L¤Â+ÎöìÅîF®«žl©h8v(œró=årUɇ#¶ÚRëdú/ÒáôìÆU/Ï.žû@úÔoµF% À8ϵ‘qwÙt]7އä0 ù.ŽB)ÝG¡¥LIøçp̵ù~YU T»jýE¤òKM²J£S<|æñÖ(+ŸxЃ!P´{ÕÉ ÞÉ#Ô{‚2Õ]·ü£sŒï­X„4ø°<×&‘Ö,¡­Úõ ÇèœiÞwnØ|ýÄgæ°wcóf×¶‰™rPÂÖÒ!–´ˆhœɰ¿å¡¸8jáC‚×;ç}¬S³á ê\IÒU Ö:“"\’<¶‹*É}§r˜*‡™%¼,×b‚÷±D©_qHaýbJ‡„@úZbySîtŽÕ[¢åo‘)æ$Ôc;‚í¹„x(5l9ÃE­¤$R5•„ë‚0$ÐÔÌu6.dÚ°MI‡î¢<]a~I•¬ ÀÃòTô>êY*ìáÚ³>í¼qųÅ̰~¯u?™?Ûb!¡P·ểS~ë*‡Î˜îÈbð<•Ù^<:ºÔz3+ˆ¬ÖÌm‡þ&„Ør‰’•fBu³øj*ÎOn˜gyrjÂM2zêZy‘K¬Û3å¬~„Cõt¦Šu/ÃßY Õî>NBÈØ±ƒ+ýÖ<¯!S_¾CMnÁåP'QA3ñX'EÁ­Cô‚“Ñ# ~Ç ãúŸ†Ûͤ˜Œ–X4Y¥ò*Nòµ‚ØeíŽnkèºtd…XK]êáZ_',%k9Ž–«GšuÊð*¦0EXö¢õ 'DÇÊcÓ×ðeõrÊ#—8Û¨³ªóY´såÇŽ"Ó65’..dRô"nÜUä7¬D¤d´ª‚½Öœ>ç2‘eÖ¨)ü60Oìè&jqâp¼$Ê;7œŽÜLçV†A#Œ6%_Wày£»Å¥<H9Šýv泌~Ñ_D­×DzA©DsS š]¼9Gˆ$’Ή{˜bIÜÎ-ʪ™íâ@ͳÓ|\ìèú}QÎÚÆb0ÂÇ]ÂŒTžäN‹hÎ ÜzΪƒÝêšqHàK$‘å“í#‰³Ë"ÐÁU3Úš'YÀã’—Gj‡„N§!eaÊ¡&D9F'·&€ÊΖ*n@çSV„c!ÚŽ*Õæ¯dM¡!™}¼Í_³¡‡±3(™-[®!ï/—©œÇXÆÈæg}Zõ¬“ÊÞšÿáƒÒqTæ2ˆ–‹¢täô#ɯo¸Ô›LJk×4™ñ ‰ó44ßÓÉËžFDÚÛfŽ‘¥(çØ°·Ä7Ƈ„Ûc»0¾„˜kËð"/o¬Å‚äöÆ-´å%^]mÊC•g”Õì/ ½Ühí’²” *qÊíµà|«ô4HUŒäsì¸Za×&?¦W˜… Eþ ½€$fõ‰è³®ƒX¥õ[#dãáµÏ.óžm…È>Ã_]ZªØ˜(8j¥Œ*W%!?¡ $kúâ½/"«Q³Üç{è@Fd,$úQµŸ$4…å¸Ò±±Dd¾Ò¨´Jã!Ÿ UÄᢵ}ÚçùJçŸ|ê[êë*Ù|ëw¼¤" ö½—ò·§/ÕH}wä CÕE+DeTcccšÎåï‡l4#Ø´bµÃ2µÖV fn}?A¨åˆ2K‰ˆlE…çÊ”¸°\D5Ê– 0ó}ë#Ûð4µ8ârEØÆ˜±pBÞf1Ì|,lÁæ]VZ Ê “Q”·/­`8w< °ÞPõ gVŒ±šÞ°¼nWÈ*šI×3Šb+¡•XMC§bhÜÝò´Ô6Öj4‰p "A:»D¬[x\{©zÛ†b É.æµ­¸Œ˜‰…ò´c±.Ñk¾W§º93v’ =a§#~).È,*Xsï9ê\j˜S-{»—ažJí¾êÁòÚÓ†%ZoòžkÛ{‚Ÿõ¸8»@ ãHÅGhÞRœsé–÷•÷qÍ8©£ŠŽf Ó:gáétÙåE=9¹#¯»~O±FRúGsÂi6×òO_ÂC†ähyJv¼Ý÷mñËeÃÌ0çï=÷;Üpcœ® Ë åm.9Ç ºÐ~<ÍX¹¥´b¶¾LIJ{[B±\:zo«G¿¢ºÕïÙW×Ëtåmy¦zríîæºÃÃx„Ÿ‚>¾h½š/…}©*)Ì–š‹éËJ>éÕYd”"ówáæô#‡Š#9ïÓ¿ÔØZy)Ó9ŸOŽaAjrÅÅèGeÍYPýÜÙ¼]zêDÐÔ™ÍÔàoåÎ-+#ÎóÚZᔓt2¥ªe{Ć¿L¸ŠiîN®‰±5¾xБ hYÙ>¹ðgY Ë—¾7´²±z!§Uº5Â×Õ*TAß^@ÙËU‹r‘¢OrD¼O'Ƭ‚’íâÎ)¸S2ÁE’{}¤ÔDÓª3xŒ¹Sa<Œ[bœÞ¢ç‘ „©X®£+¾´"‰ïðe7’ì÷òÒ«M”¾µƒD÷z˜cÅ«{ÁIoŽ;;F…‹jŠˆþJ°ØæÌ‹ïʸB㨕 ½cÙ\Å?Þï­µþ:ÖçÛ3âûq zçÐ4FKev¥wú·†M’qt~Þ}9™y³Í*}p¾#ç ¾oyc%æF±U™¢%Q+Þ®Ãräa—ˆ½6 ÈƒtÏï¹¥ãå·Ë/ë‘”Žývá>m Çÿöð?¿þå–òŸÿ«úwúÿoiª]~¹è'çû?OÏÿñ髸yÀI2Ç.:5u"ìÆ +üþð×pÙ9Ów³¦ïf¦Ý˜$²›v÷ÊnnÖÍaD\vóÓnò;²[˜uóÃâ´Û°…mN-1!8(“‚‹+ÔJg@­`à ’å¾%•ßµ¨gå·Ë Íü ùFêÐÉœM°œÊ96Â×Õ:Ï©(ò¿CšìÊ+AÈ1&à™~eÃ[EË¥1mK}?C}Þ*Ö EWR– µ›¾Û”¡0’µf(|OÁ’¡¼Â·c­Ê«A&ÌŠ\Ÿ%Cy=lᘡ6_É{ó…¸=žA&ç¶ÅýÊøSZùäyŽÈ+ýÝì³YäD@^Z*¾ß–ZiØ‚j:ZFY¬ •-îÙ@YH˜2ßAÑ#ÒU‡ +‹BÆÏaîÿç–WM_ØF˜9Ê!BÏÁi˜zyx*aZÓi‰s*XÔ6Š‚ˆOÂæðk§½ÿüP&ôÙLsI½@—@”å! êvE‚¼ôë &qøB£2‰÷(Æi¨9膠Q›ÁD8­W(ídOžªÐqX£iwÔ4Æ(þœN:}¦¦Ï/Â5­ÒO@8gÆÏŸk¤7c í:¡¬÷c6´@-ÚæÆxÊ@å :,\»Ï­æp ¥0(˜Ù[aÇS«ýzô'Æÿë÷ÜÚ<”W¾ýl6D÷j[áÛõæç¯c¢9Ÿ”ÒѲHê‚n‡:™¸m”¦nΆĭWn¯Ë!¡·CÍrˆî5­] {¯NÝrˆéö¢×CT¿—åöýÖkæåö½øgCtoÅ/÷â\¿¿ÒcŒm–“!¶ß¾^Îb»9ÞGKÆá»½"zB‰s܆?Bb˜[‘ԣÌ}×¢ž ”sÅýcæ¹jê#Ǫ̀'_¹øÛAèø’¿wé°Ê[ã ñúÂo²$«­$ŠíoÍoàÔ:» <݈µá¿?Ø[£Öonñví:¢kQÏÊ9²Ì|#²á¥¢Å@üdÛ¯¼ˆõÍ”Eù©{lQ^¯rhYð>±UvÍ„Ãûl[Ô³r–?y¾C,›rê»°¬í÷ã~Ç"–=S+ï9ì-nÚe×¢ž ”[püÙóá8l3óún‡hzÁq–É( ÑK³F9äQ4eÄçà(6ÀcÚBø^óê3×qŸy¥lgÅè}¡úÂ> Ë!®W°+c~@ÈÜgÅ„­ BèõÛW×Õ¸w6ÌÒ 1ô†âraÑõÆÕÊ qÀ×ʼn¹Ã}öXªßËÊR ¾ÇØÒ ¾ßþÒ ~p-²à=º™dQ+[HY¸‘MÔµ¨gå‹ì³ç;²ÈBŒßo‘…°}’Eæ4\(’´{²~;m×dïH´ØB’PÈþ!óM­Ì¡ñûH?$¸a|lâ{a£5›DÒÐÉÛÌ~7m¬m±I$¡Ü„ÖOžï­z{êã¥ß†VµåòŠ»ÐZ¬ iÛdKgÛʶšÏÜKB8v}þ\st „»Ñ¹ “0/7›5vËŠ ’±, 6Š&s+j),D‹[&4¼µú{v_±•ÆQË´”äÔ­¯ð' P F¯¸uâóÔ96f·åwY;Á¾"¾')ï2z#tцÁ· ;°Gë Òì0F)GïÆŒ;Žç'Q“äY‰ ñYa/<…?m ïÁaBì½`2öñ7‹>äÎqðÛ«¾,›ZyAYbÑÒ£Qò™6äBr‹ÈŠa¶-†¹FàŸ¹–#DF=І[QÄâƒXëS^zÐõ‰7âüs ˜¡1µWØ7¡ðOXÃ!êü¨¤–¨s¢’&PRÍb–™Z ò¡eñMJI ñÛ¶¸gåMç.çYËÏ ¥ oZ¦0ª¼P+wÞv­<…„² Š2ß„,䞬.?)ø1 zÃÖ÷ 0·øHÿy®¹ Mʲ£(kŸ¢‡…a N[.²Z³kQÏÊÂü!óÍ —ˆ¿.ÓGwvŽíðU¼rS,'u±V ¦åo\õ ¸ïh%Ђ©U–o)|^°lqÏÊ-èúìùѥʫ¾aïÇ‹}WÞ¶òàUåZ,¹iQÏÊMûäùfK,^vï[Ðõñ‚÷'8à p”à‚:ú1lUè·¡ ˜§AM5Ÿs/aáD|ú\\m³¶ö{3=›C \Ý‚ôRñQ 7ä¯luÎrž¶™å5·˜Wh«]+«.eA”?d¾#D›Í?šM}ÉŽh.H`%©ÁK¼¸yþÜø5l*”NKµÚ‘„£–bÛ^ô](TŽêf¸•1LÚ¶üÖ—»äŸð ‰ÏïLá­™]Ò»#,‚y+P³•årËö •hÉEËYöò©@öò¨eÙË=ç 1›Å` MM­¼ÒÂ…äÖ° ‚rª€Zq¯äËp?]þVH>õ endstream endobj 6 0 obj 6619 endobj 8 0 obj <> stream xœÍ]K$7r¾÷¯¨óÝæ+™$Ph ª§ËÀÞd àƒá›-ÂÈ€÷²ßdD0|d²F«ŒÅÎ +™||ŒwSêM_þþò¿uQé_[ÜÞÌ%8ý.ûÏ—ýËåðYúßßþëåþõeóéѾ»Ôùë\þéá.Ú\¾þòoWõåýÕ\Õç{¸jý®¯Ú¼¿ºxU[úK_Uxÿ÷¯}ùüúòS7`4i¨é€éÍFŒé]­`¼=ýjë¨þýu»ª==ÙóoShë­c"ÍqKcø«Jƒ‡x6ÐŽ ïçÍ«{^'¢‘ל·p<`´oÛ|À;¨€¯.pK$i½>/Þä=ä H°«VG“š´‹ƒ3$Èð˜y&Ãu :?ºDDãøùͯ¿ÎÞ@¢òêÍö zàNÊö aípVÞÁbv'Á+øÍ{D!wÛpØ~ÀNˆJÓ?õá6´SÓU]õí`ÚÙ£}¤%iã®Úå…|N— „Ë»áFy·Ž;ÁFÂ>xøkÝS>¨Ü™°AÆ:§Žò©|÷ü@G$ê|"îˆû„­) *ÓH íhü È-ëè ÄG9ÅôÒžðù·Ü@&·$Õåñ¼í±7H«7MÖ¶-5&Íú1Ðe¤“A ¬Í]‡ò§ÑÑòP_ MëÊìÁSæ»zB+Óc„j…m²Äæ&ºÐ̱–¤/YŽk´G¶Ñ©I¿E”Á¦ÙyîºMpÙÊŒ¦LˆÐÙßâŠgÑ>Û¢±ƒ™)ä Õ-5ÃP39æ"k¤ØKVÊ—ìG’f“E2äj'Ô`êðiî±g ·66jåµryœ½ZåúO¬ªø˜ëÑBg®f?GKÅQk€ x6a‰T-zŸ·jŸ‚Ò¿IH„TÙ®ZK–¬Ð ²ðf4¨ïew¨ø›%ž…æ(6ÍÌùAþ¨®¹iî5Ž-LÔjÀîE¡ß’xÈà Ì25îÊ‚ "ÃÄêê.]žr·ŽAMfB>…Y žQÀüˆÒFÚ¿jÖÆvnƒ%Ô”¶¹—”]Áƒ© H‘‘áøMÔ¡‚½Ép«þ’F»×|+ë]b•¤sÒBŽ¥ƒ Égúžb8BjREf?dLéXìÖ²Kcaù‘8Ñ8èßäÃ̦4¥–fU‹ÄqJð(iw˜Õ7}³mUP9VñÓ"žàüŽR4‚sR‹V¢#ÂÛ°QÙHÆ0ýq´2šç8œˆ4Í)X YKm¤¬ñTñ {‘štdŒOËûÈoð!Ô‘IÙ7–ÅPÒS£²ÿfBgŽ"³åб- :éåj3L<¸°œƒRD¿á;9<…YlâOO©W lÂ÷Ä:·ú<ïÖ5¤ºøÚÚOc¦FL/ª˜îÐl—ÝŠï,×4+ 9w€j®ñ¨¤»äxu‰X# Ò>º”X`91h·²Û&ì˜Ðõç¦B„ ÏÍ.†©ÅP}Ïü¤†ÞÙÒ¦.¹?înÜSÈG—'v°ÙDV 1Lòl+’—¢kOñ”$ÕÀJgLq½ãˆ«îR–SMßzêäƒå¿¥šàèè^ŸÏÜ,ÞúMçÈ8-‘M>ë ísç-GÚ¹~š³ Ù¶4É£9¿sžKñj©Ÿü娠s¬Ùh^›(„Zy;}E³Ò’RÚ‰âÃÆ¸9GvÀi‰lò[ývoL£6î7¯Öaµöœ•)‰0{&´Ó<¶v™%OõWo ­äÕÕ@äAI“ ~°LK`ëA¢`šœ”EùËõ-¸Æ"Õ¼kŸJw£–ü{ß ËZ¹þ¤U'æ1œþæ:>dQ£ŸEµ˜˜+!Ä`­a•+SpÄm‰´U3M·ÕŒj¹2íñ‰,@Wo1F/ŠþöáÈäF4…ÏSGBuI¸h]?©_ ›Ä¸ïª_èö‡ÖÉøaå|=‰SÕrŠTêë¡1+de±a±ÜX¼AñQa%²xïÈ‚Ì6¬Êxèç)\(5/Å # ›³ØÁ8€²„QMÂURiR«”Ú¤Yz«õ†SSØQhÍšd^ Ã;Ç”ŽëÙ"Ý«hVkU¥ÂÆ+0æ£ê϶üëN÷Ùˆiž“uµþë“í)™;¯–+wZ~1ÕR2Ý'µLIbá.,Y®Yˆ,¬¢ËœÜ@ú\Ñßæ'—e>«§ H:©ZB_µDë ×Él‰”¦D:%*£kÎʲiëixžüw àÈX9y¯ÐQfWà‡¾ì‘‰‰`Í}×u¡¨žF –ÐnÛ"è,³©ŒU¶p•¨rdNùçð%GÐô.r|²H¯%Ÿ³ž]—y˜ƒÌ§ée½ö6ݳ,¹ñ€Ó›v2uyoæ¿‹š9|ý½ŠxÎUFË£7·iŸ+¸ÈY™%¥«Hç¸uq}™¡o¢o]¡ZYEæÎEN—kßš”èw=S ’©Y†ñh+fyçœ6€·„[M|™¾¨õ0%%ƒ)WѺjXùl¦/šYfR+,EKðÌ0ÜiQ[·¨>Ù¶OfŸ8 ç<à³BÔÅщ¡]»bH¤dm,‘b_ vW2Tv¨XJ2&PU5$¡Tµ  ÊV½”?ïÇ<´3O·½*L|﻽[“òrײz äHúó¹ð0–ø[‹æ? *êWî½Ü™¢EþjK6’µÏ Û Jú%»·oTyH÷MﵪaãÂ]|t"µmï a!ñ™¥3MNSˆÌ +-ʲÞ[ÛÏÞš’ÅŒtú©ž¦AèÓÜQ˜ pB]ˆ››» 8NNúâCQ+«:¤Ø­p4§v\nÕ[uÐdLGå«Ã%Ê…uˆMGcðÒ€O «qÏK”ì¨mÏë&š=OBEI5öü¡[ÞÖž¨nC::t^G‰øêaΣÒÄî’Ÿ(m\€:@´U¯uj¨'°h · #’YpØž3O3t †lô¥òæI1$Þà=5¢å ç„M#§i‰¢’%+ô[ÚÇ;æPј:’WEœ± ~)éÜVhÃU,Hì*Èìšâ}I_ 5-Ý.²íôüǯÎ߆Á-˜ë8Ü$Í $ Ëêgd$ª7ßw¤¥ Ø÷­&Êg°jʘk Ë»EüU,ú- °©—þ¬Ã £R„vâ*Å'óM3åy% Û]ž¢ cM¯1|ˆ[ãk?ýd(L[!€À¨–/ß›KèQ?é{ ()œQ¶+œ£2ìr‰‹uce®¸t_ëOòuóE ú"ÜOH>Êž³+(+µ‡8‡H¢µô+ =4ÝŇÞÝ•ÚU€wÜð"£†:Û&ˆˆ7Qæ×Äž²®¦×Ééàf I¬œrÎ>ÒÜÖÃOžýß«²>Ehé³YË¢Š¶»HÚ¤cg,vQö³‰CŠwò¤to´òˆJ|ƒÄumý¥çÃõš!ßeBdíÀ,QO×þù“)`îˆ/¸H§Xv>P:§„q׫£4»™^?üÑïLcEoJï…#(Ü®è+@²–Yp =MËl4ÔB ×Þ§¸†ƒÖ1v³™™+áËQˆ›ôÈO è¬Îš'˜kC²£Ô\õ÷ e™ˆ¼85‰|J å*á‚aw£‡Óz÷þr#”#_4%j€Yùü&½7øÓ˜é.|ˆ¼¿6 ÒO| ¿¥pT§á6H”·³×DRtƯ"ÝBžËà€ˆo ðù&MsF à—À7ù–Q7ŸŠê—]†‹tÈ”ìÊÒwan„ÇÞpf½/³â¶´ˆ"‚j¹ÎµãJÀ-•‹Gò°mF“.ßS)yâ‹÷QÁkÂ×(/Ç[žêÃïÑZv5è©ZL\bOy$üA¬ü€Tà+ܶàÒyb>]Ú;|K§‹^Ôhþ*"|âÖË–ïæ£€åÁÉP8%²qó+"K‚£'1ᦠlº;òÄPMIã«ÄHSúÑÝ€%jMãfˆL CNˆþã"¦¯ÉäK‹‚AÜkþDðìÈ”{èUìœÇö¨%°Î¾ˆùx‡ˆnÖåu“ÜÆôº:r*ÏàÎ[IGñWkfê¶d ©ƒ>ëòU€ìQS9‘©ú°ûF_ø/×R¬Z¹™'«`F–°š8¸2OøÀåÓ9Þ“²Åè{•^Ü‘’›“—<8lÌ•äŽ4Ù.6ä|`wýLÍL¢éѰ¶‹Â™sP–0êmæîÈÏ=É‹"7ùÑä" …ÈêªÙGEŒ©+¹Ât§R”»ô®–/i–ú]Ôcÿ‘ŽœU”ÈMŒÐ'Š÷GÈ– +=ºL"¤É—œ'ßšuêüÅWÖËcÁIS£ð;êöÙ•œ­`Zµß*Eüˆ¦¬Ç¯vÉ9ªF+Té¨*¥H?—Òq#;r¹‡™=D–egyJßù)qRdùºlÁ×zÍΕìrÎ5Ø]”’,5 Gß·>…8|¾»ïŠoÊ8hÑW[kUª‰> ˆŽ`H]°úƒíøú¹Ø­)Y·ˆúeþŽ¿¡]Žç{ÓŸ¿½$²Mò[ß.ØJVFÚlj…Ðü›{‰¾½ü÷Ë/yA¬þ埶¿§ÿÿ5Möë˜ëçáh4´¼vÅÎö—@uåú2vNÂÂ=Ì2vOb̃o‘V«MZiÐÚUþZEjùüYŒ¶E=›Q¾]NÑù!óM2ù.ÓnÁGâóL^;Ââß[¦alöýM_ü®ÒB½|Ks¦%)XR4o]»ðkçHü£ûö™$|0ýƪÑ“Þðè IŠ÷ntŽ*{Óp¿½•¿—ƒ­ojAò?µ¬…[ M {6£œ£ñcæñIÖöÅ{x=p²ŽˆÉ8æ%%¢FR¸À²„J{{UGöŠzêÍÀõ‡-?‹;<óðŒ·LØùÒ%ß*ɹIßzD,c¿ çD“8'£cþ枇[o³‰ c§n «‘ycÎj{øÔ+O%…EOvIôùJÐâ÷lLûäƒo[سå)²ûƒç; ;öÍ3dw›]’jéìðÀ#ü3Ø·îÄ£¡KY;¥‰÷ˆÇ Tz°Ã!ˆ^ÃàÞ€dŒE’ÙˆÙ AËEXLÛ‚ž/f³@µ[}²9dfl Óxòߥ¯á\Ùþÿ[ëäì-°Ì¡Ð\ñ>“Á»)˜ÿ³/¿Á27j¥I±EhB¹a&ZسeÁ ?d¾ >_ëL¢ªÄ3ÙzQ†²µÚY°|U¸í%Úáj© .ÛáÀWê‘ÌòCU³ªeQñb«ixÒm‹z6£<%sþàù¦2zì%ÂÏFÐ#b)@L2'‰ŸQÜÐ5Ui¹AôóR°›±äò’!!-ѯ:>ý–Ôeõᲃ­lç–…¯§ƒ¥œж¨g3ÊÂý!óMìPü°¶=‘ Ý(4CO7H¹ñ4A‚ M‹ÿ<Ì=èM¥•š¸õ¢ê Ë­¢† =Æ£'lã³:3Þá©»ÚÊ‚3·ìF„4 ([Ô³eÁ6?d¾ Û¸ÔU¡÷­à˜ÚÀ4ºq®Ž$”˲ÉÄØ ºrL,¦þ­D׊Mf¸›üyi›™¹Ü©ñ}·ZÂQ Ü©hCç¹õíRZ;²½1LRøÄĵ#öSôŽØ¶hÄSÒÿ“Wòsƒ½C“çÿÄÞÎ]K '' EÓã³2=õ,â˜më þôµ4PàŠt5~Ï•D6"Íf@‹·-ÒÔbˆó­þñs¢d–ÙûØ¢èÔVöžÔï¡QT[Rç>ö~å?¢_`õYØllvÛyÃÍ¿©“xýÜuûc癀l TH+_ðË£¯þ¤Z&€ñeÕ3üÑ@ü†?CÙY㯎 ñrî³(6Qüß PZh9û& ²V>3c§Ô¢÷p̶EcžG¹ÿìµü> stream xœµ]Io$»‘¾÷¯ÐÙ€4É-@P%© øæq>s{ ãy€yÿ}3V—LªínUfrýŒ|Ë‹{úÇ·ÿ{Zž–ü+éÅ?íѽìO¿þùÛóô¿ô-ÿï×ÿùvÿþm‹ùÓ¶ù—ãéû?ýÇ#=9ÿôý/¯‹{ûþ·oŸß¿ý¾©pøºB¤ z ïoûër{Þ^Ýž:÷öì]|]Rþ»l¯Ë_®ô} ËrOþuY៻óXýöö쎣ÔÄùSz]¶üêuyo ìoîÕ-¹þ³Ü—(¦Õ…¹ð £ÜšôÏíÿ×÷ßfìøS†¯0JG^#ê[0rqIî ·R~üÌB°[2®K’1&·i 's¶ðÀO笥óЂÜÎfHDöL+?B¥‚N0ƲÈ<6®ÏSÃgû Gy[iÙ‡ëF‹¾{ó™¿l›¾“U/Äð¼ò•+K΃‚µXÞ2ÐÐ$Ð+–øäyx¶ãÊ¡:¤­k¢é š/±%‡=:3:YÚõk+»iô‹+[*”í½¾¥ x¢˜Å9k&Ë·´CѼH¾¯îˆ[9ƒÐ÷¡„ÀSC’Þ è¹ø—8ÄWû (¤©<˜ƒHDGFldy¿„&­y¦ëz¼l4S÷ä"Î4o>œO€µ~ÎÿFl=Ó(*¼8`fÈ„äw|‹ÐëeWñxI=¨4ðçÀ☊#Ñx<^u¾Ráxõá —kàÌ=ìBøû¼l¯îêrŽÏ ŒÔ¯é€6(â™gœ6Ûm nBHZ 9wi$ÙP Ø‘t³ÃH.7LÒù†!TC’TTóÌW᲋ìt¢<³ÅïY>å½Ôºñ¶0}T s – PCY²q¬-ßüðv*ØL©î2ÿð’ö‰ß<||Éôí3fÓC1Ãλ^Š#E¢üÁ¡0föç‚ÇÎEà{»¹-cU¶à˜Céð·ÔRovX~¬<ÇÖ›@­å?ïÍææÇüçðk0º¹ÍÀX]oQP ÍZ@éI× ¿spG=bÈM)^; ](%d@"Jà7R¬(®(¶CY^Ìï˜Â¡õ!é×Fn% ÄéENŠKµ¢GikÜ:¾NNÌ&Vó·K\Pj"?µGJ]ä MÉ>ÌĚ܃7âNËf\Yv`ÆûRl·SLÓñò¥€‡@4Ã4„‘KËÿÀjYåf+´eôðF-›–²8ÜdˆsU´8%ëRßê3©?cg0Á¡æ–2:zîp”>üüЦX»À¦>ˆ#ñ¢^ƒÚa4Õ/WF#ñBK/¦î&ŽlâÜ8±Ä[Òú0?‡üÍ—†Ò%wa±q­ˆ„¥×é™qÿ?p!ô’åP³6J0p›²þ`ÙZ‰4Q,X}uh|h×;×0C {Ölh6E.ZÇsqá˜ØÉ¨õš…u L(Ãï½Æ^‘Ã1ðnVÐ7lB-·ž90ßYg—R¾Ø¥âZw¼Éð;ª*n1†ï•OáoÅSU5\Ë Ç;³é…Å8ãh=b3ˆ·^ûϲñVÔQQ;b½óÕéÊ_YÙ×,÷0Ö§½†-Ñ‹Ëν c¢w1¢®bÛU½Ó‘'_’c?õViﹿaK6©E<õ.‰;쨽ø"ô8‡Ë&TÞ÷:’ÛBcö±ãø”ðžö±­Õ†ÊÇ8†aZökì`šáÓX¨ìôÅÐÙ‰?ƒ5xgkÓJM ±^3çÌÁ$;©äìdº‰¸It‹â•“sü¢d~ûÆç¸àÂ_(×ç†ÿbO¡GLüAI*íSâU¾iÖ¿º ¹Nÿ ̆Ãjã`ãÜÈÀaÒ –M%A Ur/ dyþ%oj­‡Sl¶OÂZM"A;Š/ç§øÂrÛÖ'íh=jŸbjˆÖcc ¬Â<¸Mª7ÚõÙ!÷Z"ª@·Q#ô#›ž‘ö\/ö/yZ?ãDkxY¯E¬˜qóO4G‹V¨ã²×ÛÑe…ÑëvĈ;˜ÃÉõWþ÷þÁf»Æ…>„2Ô Äà@ÿ{‘yè¥7fЉÁ•,;ØîÅ(lÒÄ$+”àjPŒPÖÎ!‰˜0´?u¤á]Õâ0Þ¹Kõ< À™PÈqœDIlŽP2z’Õ¦4v!ê LBc§dÝG+S¡±OÛ>yñPÆÂÌc«üwP)ÍT{ZÌ”µ+;Nf>ž† lYëdš±¸ve:ÖnY_éwåðµö¡‚*sÄIIÜB¤Žz”§¢"ùB1˜¼Ã’ß8ÿax¢ŸšúL>¢xó´s¬ù³I„Åa*|6 w͹´9,Éø#N÷ή ɦ=4¥JmÜ6[ÌB†¥Àƒ&}Ó’*YÝß41ÞŠ¥÷]PúŒ=…=o¾~úç´Ä€­ƒ‘I\ÎK¦T>ZõÔ,ú®kž×zµNE·ª’Åà”töÊ?]v¬Iø-Éú20xh:,¯ƒÃ.}ž{¤¢}òtÅž¥§sØËç1ÿß´¶ÉM¸™ õ* û® n•V“¥qÎnìÇÅRæDIÍ:ÅÊJV‚ !™/×4Õ¡4¥)?ÈU ƒx‘LÁ[¡|%§LZ¢ K_Ò¾QO ÁÔ$ÅÝW5µG:¶bS–lª›]¡ðQ ïÓœãqeƒ¿¼~¯®×w€M!vƒSN‘Ž.Q—òÆ«ÿÔÉàñœŽ2ފ똚ðp±R¬K†Ð–­îZK"i÷{‰äRr°j:’‡·TꊨÊ{ÓS$áê¸Ôë1ˆX—v“o‹N8¶YÀdñyAÞMhÄfú"ñšçFñîûâ“6ë=U%ƒñhÍ&@Ü$×zbUóÅ}€ÉŽKY=BÒö9vdå+2ºÂg“;к»öò‰m…¼34Ñ\£‚Õ‰?Ù› ¼,Û8wùZuM!uCÙ«·§ÛJ»€Œ/'&]üÔS‹®Ž°b xñ')§-™–iWùíu™ñé®p͘\ Ò-(Õa­Wê£ÈÏKªÝ€M!^|o—m2φZ½'Ï‹ª·¿ß]y=Êñ}í½©v’ä  øÄÀlkýñìOÎí<÷j¢>3DÓ¾_H4“h(ûjn<§µY®á–Uz®3ЫÑÏÈ\“õ©à8 SÑ9+Ñ‚CÓr䈈+ôuy”dÆÛí-4™ÃFo7 Ÿ†qÞ¡=s!9ªnHe½Ä²GfŠå vU{Êaíb[?M•ÄbõØúXÙ—xE%׺#g65¦ù”Ë @÷^àv¡ŒÑF¬Šþ%]¯GlŠñæO":0Û®»¥¦°?CLm")’óÉÉ«Þk lÙÕÕL°«8¯î-5ІåD­U;S,¶RSlÓ>9¢*®ªc@)V'è’L6sM}£ûoNíõÙW­Ý{ÕʈD4­³j…—ø Q·àÉ)Å'Ú£+²˜n<2aýçz}ºÖ@à\®OŒÂÝKÿá Së1ÈjÓø¦z‘¥7UÂ*·0ûL|ìCNr–•E™÷ZòQ‚v ùé‡Y–-{•ÅE®Çõm…òY=¯J8å"ÔñÀÚZ½^œén K'9ÅÝóyµÑMTœò¿›QÝQå®:“r¦Æ£gfçDçØMóxKmuâýv\(ì[S¶œF”[„޲ ¯y}‡×a·õò´"šµ“ûæl‘5iX»<õ92uªÄXF‘@6fÔòÊ(w®óÞJÔj“^*>^»w¸ÄWÒúО) KèÃTsB³Ÿ*6Å*âÍ=œˆ ±«ÎÞ)°“5J%±¥ºC¹ƒ©a$±)¦G÷mkÅ*ø¸vôïœfÈÂI¶Þ:œX²sr„³>kÜ”±>y}ê{®îFcfÂ÷xÊS¶[¼=ÈrÍbôËwÓ(_ö…Kˆ½öøLÝúûåz]Ì6ÖD¨r*ü@Æå…á#_ŠòŒrIÙ[t•8m‘r Ì®á,»å«ÔàȄư6K°¦9ÊsI©=JS\×þî9{Ì® §u]þþÉáÿr«ÁA¬;Öü¯wp,À%HöøõÏOùMÛ}Zà{¶ª|£Ã8H_ÜE‹¡K7—²ÌGö"NQÝÙ2À²2É1gõ )بP9¶îŸž‰/ÝÑKˆ¹íè/å,wî^¦1UžIc¤°˜ÞbÙ…%îdö =J_|“t±u›6›£I âÒÕ%^â*Ýý™ÁåïqšÑ¢[S—D}u§‚l©´•ÝfofêVzFÚñ¡h¼¿é‡\m¶æè¨ÌÅ”ÑѾeñÁ3Òv jÑÔ=Š9‹IT´c“7Sùx/5~“ÁTkü•#ð$E{—ü¡“YG÷þ˜9Ô–NÞî¢0†Ip:õÂe^%øZ¸Ç…1æ- @3ž[Pašã#׫Ö57]µ0^³{íSŽË€®šÃÁ&S‡³© iVtiò,˜*oØaoÅ×}êÞ³òâøžì/ä^µ8Lqó±;é3=2`/`ù¢tåo YÑ CÏÞöÒŒ|‰õáÞLuîj§dIëÇó7KmÓ»l˜j¹õœÑ¡0Ù`Œdºvyë îÜö…Óé§½ JÜŠD_¤ƒó6wù|Í=\µÕÅ©ˆœ­‡Îfo– :¡RŽÖÎdÉH€u¤Rs¦ÿ‚˜éÚœ-ê|Íl+O¦å&ÈìÇ{RT«F6«Èê¢uÒivFt ‡Ž)Œ¨¢ÓéÜkR%6Iì‡ô«3ñNÞÑhîFÕ£x“<_®W×Ôl¹vß+_uœ™ôjsçg Á–œ:ç>ÿ€yµÝH`…ÇC;|TÈ’ù¬Â:¬p NDé?è!žV´ÿ§Jéä=~F˜á8Y¶&ñ¿çõÊQ —ù80Sjòù¯Ïså­?®d˜ÿ ǹÐ٠ʧÄKÅ1¬[]×Ù¶sÕψ[QÏþS¾Ù•¸H œ5íGŠ-ºÒ"_64K›<÷©3=z0Ð1 ôw6Úéé—'z )ÐÓæº'.YµòËÓ_¿e Ÿ†óŸ¿Å‘ý#ÿÿw¹Ã¿ý?õ÷‡A\rg {ë2yrŠH!²ˆGÔÅ㺂œ:#¢ñW7‡vàBMx/6ÔR]étM134]|úåI&Ô•vÒÒøtQzÙJÙüû¼¤ÏzjÔ²ôtQzæ«¥ñ‰J‡ôÝà#-À'ø®xÒí^¥¬RðÀCòJóaK2e)íF‡k¼ÀvhåàßX o"pbt|/‘yâr¶…_¾]ÑíÏïk@³Ç†p­?‡bAâåîÝ*\YiÖ—Ú764Щ¨ ˆÓ®tæðÎtø¯ÑÈÊÓDõIë\³‚¿í¸õní.…ë&Ä¥ƒ“]çw®ÅúÓk[uît·*xya¾Äº-¾ïw=“{ÜÊŽÀßη»_/pÇy.¾Ã1Õ€É;Pdxy’ríÎHK®"vwà%[Ëãï˜pŸ%äQ?a9©}½#~^ƒeDÿkk»f ³°Â@¡¼Œ ù…vãaÜæi>}Pø7þ§ ÊS)×Ò9,ŒðHü½­ÈÌúßÔV³ö~݉TvH-ð[â psy’rW„/!-~Ã6Âß\p©~S®9á„?¥ýÁš'àš>¥Ž]=,—Zßtã«väBœñã”ùrH¸ =)Ÿq‹aÖúdêMàøI=Œx„c¼×¼½š¿ã5yöÄ‘X¹æj.J¨íßçj… ð—SŽVýÇ€¾,âm>./Qy[yRpÚÍåÀÓá½G @3Þ71r¨û•ÆDOncÙ"åÚMBV* ûNɺñ“¬°Ãë™ê'*iÚ˜¨Ä?½¯jg´ô/«ÃÈÆFµͨzÁäZÅ ÿ ‹ëvj endstream endobj 12 0 obj 6237 endobj 14 0 obj <> stream xœ]I‹,¹µÞß_‘kC–CCLP$dVU¼kû‚Æ;·˜÷¸7þûÖpFE(Ë4]·"B¡8úttf©¦7wù÷]¦Ë”~›÷ùÍ_¶èÞ¶Ëo¿þøËï.ÿ_Ÿ¥ÿ~ûŸŸ?æ%=Zטÿüûå÷Ïxqþòó}wëm{Ÿ–Ûu~wþv]ßÝvs«º3}Ü®Ëò>ÍõwóïÓ#=ˆåVþçžúp.=~ŸöÜÝ£¼vOM¯«Ç¡Á½´¦&kº¹QßKê»|4êÏ}å—7§oúü#”~"Q˜ß\o¥IÀ”“uSié–wýA7å>êW+}îãö·ŸüñõóÇ/ „~o¡ƒaîëç?{oTЗ)½¦AŸ>o×<*†~Ê$ŠAæaÔ‘´â~^˜/ïf”=}æ Ž' hn%NåÓµ»õæ"€ 8¨¦n—³æiZ‡d~AoçôãZßAjm\ÞæZ øØ Ð23/!vå^fŽ:°9s‡¯CJl’gçtu?@%Ç-•wqS&,/¯Å=o Œ-a[.«åº€±¯ok·¯Ä5yj¹‹PîøÌ؉f÷ᣟO)\öåmi Ë-ìeÒêêX2 •AÓø `™ƒÜB ‘w®=¸—Xƺ*Œx/‹«_~«}–œRsÍ Ü¹\ݺ3XÒ>÷ “r¦{&ÒEAY•!žoÕ9®3Ž£L§‘ûʼozʺØë.›O?ÖMCv¡. —©¿WNÝ뚌•€ràG™(@(wvèï´*üîhÖ)Æ<_΋~­˜­½P×Ás'DQþ^óý$]@ÔŸCmA½ì§â—˜òK©¹Â¾(:ù¾[…¼]Ä2IXyøžßšà#2{‰Tõ§øVý € äiù@«}_™*_Ø%x^åç¨6 0³ÁÑ z.;¶:ÿ›²c]éžPU(x·žÞ]„aÀe¢P‹zPé…!ï.!º;ñݢ緥Iò­}P©zhZ]¨ý‡*rAO"·žCj0šì·heŒ´ÊH½ŽÄ+]ˆBj2qSa¢}~ì a¿•Þk»¡d$é·H¹µyœ)jä_‹L[rë ”4YÙuŽºq„º_N”h=¥Jb®+ Â.kÊoå_×µƒ¤Îv~T8ÚØËÌgÚZ‚UâÃÌ­H»ÈåfÂ(VËë’!†p¨>äÙkÚËÞØtÀXÍùã~¨¢ÏgÍt<š5¬þLËúæÁA¦²ËBúê@$»d|1d¶ ³Û:Üò^ü{–-„‹#å@Hå'Õ¨GKT²û]tœuJ¼-uÕ©ÏyöM`p§Ž(e«Aië4µ úªp} æò3FÏ¢(U&š,ñLÏI9xÕ܉³rÙÄǪRgsó‹ŸöU/“²§§È´4Â÷ëºÒó¶X Ä£w¥t%8­k”24Ù†JTJS•åM_… ûíÈåBG¯¨¹ŠÔ}ÐØú湇‰+zSr@ŠY6F°Пø\Õ·8Ž€_½Ñ²áI"œ¹ƒéú —Ò"Û‘Úb°ÕOëWèRa6ó´’áÏ&;¥ŠŽ§ñ­“ú– žü¨rXIcñ4Øá;XÀ7« Zƒv>Pu³pe@T’WÒµzÐYÚLseY¡…?à¸ôGyþÈÊ…K»¼óL¸ ‰ sôÜéŸÃfQ—P¦|T¡ŽLʯ]Üc¹£ «Ûuæöž=Fˆv¸……ÜwO©]ý:GŠ‚&ʧ}OìÍœf ݈»¼‰öÞ:úä“ýBù“˜GÄÑÒl†eÙ0ü œ‹Rùæ´5ÄL±êØ­ÌìÇ­0i±Î’Ü>êkÙ蚪ŸÓÌΪú—‹+ÿ¥¾¶ém¿„9¤ŸnÛ}nñ©çß~½üãw–þÔ’bolKÆB¸´'S&áWDzN{%$I¯‚Ov:Bèõ€vËÛÚ´ca »HXB$áù yÂËd¯fÎ]šY=Žr­À%{¶3ÝŽÑ´ËUh†;–nH"±TQ³Í!8©üKáeVŒ? Î`yïßL¨¤l7&ÖÐTÅóHº+nô΃$â-#Xä•Ü®àúD'DW¤ï×`Œè»5.ÀIЍÁ9‹NO«Qñ½åÜ!*Ö‡…l„ñ´¤`Tð…¨oãë>Ð}ÎÅ‘ìb ‹N„ëZ ä² Ç}—ipÊ}Þ–(ã¥ä|æèó\—Neï^4r•K(S#õ„¢áœ¡ v°}’"&õ2ˆpÏ+¹eèÓƒQ½Dz¢\~ô8¥Aó€æÚ#t/‚ßiåBÚÅPÁQ;“® zBŠT™õÐòK¬Ås€C¯À›³ ˜ Òcfð;0Û¾Ã}¾t¹£x>LÀÇ»³¶øöÊË+½lÝn¿oÜõiÌ̳±´Ã²×KÂl”%ô`FH.«ÑdÒnCw¿.2«Ó³¼I&„tâÒ ÖܲõKÞká$XÇPè‘W£.ÜǤw´µ5È\p€B —ïðÜñÅÊéÞ÷Í~ N79M—*°º¬ PÓ•Ù…˜Çùb1ÃMq°Ù÷IæU‹¥Íóæ)Æ©ÌÜŨ‘§ÝõM’ëÒµIÕËûµÞÊ3+§4¯.úp»L¹­]eJÊž»ô¾ÔYoSìÄ)>3ÊÕeo$ÉŠ, ¦•éÿ C{2B$3-ª ”Þ´6ªÁcö1÷ÈU U¬óò¯fX½ÔÄ»QþLÅ%ã\†"}lŸø2#('›R—\)£ß µs.uy&XyëéÖL8ç"¢, ºñ‰+`á$ ÎjÂÝSDtZ47wRϲ¯×6À v»·Ÿƒm¤zóGñäÔfå(ôg6Ç‚Û^t4À+-X€€ØCAtñíN,K×H´Óa_–QÊSájÁ¡¹nÖw‘¥0¤(tÊ^ZZKQwµ„™™9±:ñ;ÊÞy‘/JÍjÆf_DØàìË´‘(r»v“Ç%…Ï58€]f«µjòjCTPñò˨f[õR;á@¿Mü¼FöSC«×Á.®õ&êç¬ß{®›Ï8ÇÍÀ0ÂmvÖ÷P‘YQr,Ú1•gÈVϹ<Àù¢ÄºFP2ÛBÇR„tÓ™ç‚5â!ˆRºáêœZ•!fƒ [ª*ÁÀö—ôñE]Ãgu1m°Lô„íþÀ7d˜ SW;ål7³¸‘ÏyÂ7â ×1òÈ_É —QÖB%¸ÜvþÁ´a5iט5‹tëÓ‹½ØµðÔ;K4¥¢Øg½è™g§L:#ß›HùÒ"¡Žš@„ðñÆÃÚ$åÚŠ Î|ghÑ¡X!ÀcLi¢ª%óE—‰=gSÝ€M÷ÉJ|g¨‰ü1P‰óú"ª7šP•zrü4Áš;ÛóªSc|[Ó‘nm¤LEV?³5êõSL DH×¥ˆâ’Û^<™=æI·•m0‹ãÅÝ.‚ÄvîЇ&‹Ë-ÁQN&P®ÇÒ`Žc k@ë Hb¹icêä´”­Ak€nITÛ~nÕÞþe…J™ `´Á6 %[·«þk ƒß®+I{ÅWáŠy¹ÏžÀ?0L®[¤ž¥%æB_Õ çŸ˜£ai“z•ý`7†ó¹Òâ᤻ŒvÞI·+ MÔ”ôûÆV€™Bœ²:ÊÎÂWÞ® c~¯/a\—ƒÔ´¦Å瘉Ñ8Y; Â=¢þÔ#ÍÚ4aŽÿªf´R»ò– Ù‚lwê^^̘=kQÕ9ª£!ªÞ¦mQå¢Åà6¡˜©âRd¼vé” É~%jê* žÀIêöq•ÐòmÏ5F"äÐJ<ØÀ5Øu²†Âæ|ª©&Ǽ{UÑ•]­²Ä55o ŠXÝׯ™¡ú ºšõ‡È€žÇ„¾äç»–N]wÆÜ¨åàÂtñd§@xíÀ5xÙmÊÓ§h=9Å¿.”“ÈŽŠM0œ÷ä@öòK±žŠ¼È¶b%›øö&‰â38~Ù_éõ^%Øa:˜sºëih©r¸…s8['¨Æ$Enåm.X²è%KŠx)m# "%ѱT×£à(nòÁºä¶S<‡!ñ áœÂ±a 謆è®bOÌ¢ ÙÉ6U3ûfñš°E{”ëzù(Ä…M °I%e Ši¤I†ˆÿ©¬°Ð Áœ;È7³È`ç  ^méò˜ªw‹”¡%.‡Þ>§í)r®v{+ ºÖxC½Š‰Ž¼Àû¨hPúZ‘­,2eÊ9¼¬!¼ÑF!Ž£Ì(D}Ê9[‰ˆð‰¡.¸ ¶¤h±èTlˆ¨2èÞ˜ÃNšcÓî•ÝïÁ` LC`C7æð¢1Ëjã’¾®¸0sÒv—¥_ÀØŸ–)– –î«nºÜ W¥oâFÎC ÿm²%Þ½­ˆ¸ßho*˜òˆ ÷[ °å–(¾ïÜi£TŽË¯:!F±«(z±GJ”tïD—¦nгE/†®:Àܧ¬b1±Ê¼,¦~[Ú&¢Òºd±½7̳MÆ5CþèÇÔY lF‹¡Ù_Æ¿0CówýWØ•jóiÉÈ)/ªìz)ü¤Œj£wqižjd‹×áÙ›"n5jU_tg–on‘ø`Ô{¿ŠcšÄVH°)ãÜô¦ò(;½¢ŠO/w^=OÔ"?æÁ9̆(w Ç{E(IÙ·´{‚Øé5±hïî³ð̨tÔIÞcÅ,,Ð¥¢ J:e…5ï¿$·Ïyö•ဦ)€.µ-²/,?Ù-_[ÚKJÍÐÖlÞí‚u#¬Î…ûÎRU$ Äžg–W='6¥£Ôo$¾'4%°ç\ë¿g^ÎÎõ7-iv!Û :*‰”-0"¶s3·­9=SD•¢_Ž‚?‰Ã Ý~ÁGjƒm™”ŒœÃià÷Õnù#ñŽL& ?axÒtp²Å# ªYú‹wÒ}eθŸýº à"±þ©%W»ïS¤ˆEpçˆá½ÝK,2€nÁr²a¦°o1©“(hÁì*^)΄hŒ%‘!W$ôµ@&SÇI€$qŸ ­ƒ °ÅqÈ KgŸ«Ø)$šo<°šÆ„Œ?:cCûl°¹XûWÜhZÍa ú¡Ø‚h*my3—0ÉèSÓ V™Ç ™^ØêPžEiˆëÜntõŸ§E‹þS@pRϹ§ìÆò+2Ô s÷P–xë䊭—ͺn—$IK>$í´±!9Š‹µˆ  vko'ƒ n*ǽ«”`Pt ¡Àóz}Õ{$ˆöQÉ=Ê—žd~ÛbYr h…‹—áªÓ‚Ðgù†|k0¢îíîUƒ¤¨˜ß…P XÀšëˆÏwúUk(àÚ¤Z_}äÖÉ—vâÈ[ˆ$¾xYSÝC¤Ï4«°NÏŠ?Üž€x†€NÝ«, ?\Þ´?#$P²·|Jª7]¾+–ï¸ÍsãǬÄK̯vÛ•;KÒžÂϬv¤0à+gõ­  N#díê²Ezr‡ —ÈȂխWK䱓&ËZ9x°BOÒª¯™ãw \òû¶QU鮣UoB¹™àØQUÓyI®®Fè£Þ¡'\NZóÏåUôB± ÚwWXè4 Xî«å1?Á‘4¿©& %‚S{ÓÆzOùµ…lñ²öN4ê…ä­²yfÇá°ÞIók†vˆÖèWä¡ý»tĿܪÖÝÈ•;ËÄ-›sDN3ÖÚ3ÝñÖ8!ÊâËàX»çFíZëdÃôÐ^v½,pà Á þ÷:BýNl‰´Gƒ€rœ؈Lt]³;b5wðBÊË·¤BÅb£Ô r·í™ŒìÈ%å/äC-CäÜnûÎÅÀoÙ¤-øBDÈËrh%1¤ê6UºU4ñmTF¤e=Çrómh Šé“ ×qÙW"-[³!ÊÓÁArÖuƒ +K÷·9‡Êw}p}Ì¢ô ·]õ%“]ýÈ´ðš…§Bñ(^å-4¨Ûã!Å*;±N¥Eãù,Lßãt¿{ëãìIñÃnƒn€,µÈ›õ0·¿‡ï8“¶[SD5}Êëα.jÿê5®ÔTxÆõ›µxC}y®©®S8× 1‰šô útcI‹_)Àd\tbP,ÛRwªhê­&;f¬óšmâ´’Ïâ•£ œWÛªöºA87œ™[EÙž$xYp„ÊŒªb/v¥t"pd6Xè†`Ï“U~6ÔÂ…üà4p¢çæ ‹©ÓC=1áÐOQþÉà„ÙÉáA¨ÔJ¯ eX³bñ"Ì.UnnzWÐ=AÜž7Œ¾© ª/ÑN×—(•GI„#åÉÕºÕrPJtëk¸cqŽdø^qJ>ãÑx0œw‹Yžåt“Jïúq±¹oÒÕÝ.Myg)Ïà¾_™¢r nÊ> stream xœ]I‹-;rÞß_QkC•SRŽP8§†…w zqé» ^Üÿ}K1‡¤Lj¼ªÊÔ”ŸbŽîô^þï×ÿ¾L/Sþm9–·ø²Ïámùçßýõß^þßåÿþù_¿¿-k~µmsnüû?_þý{~ ñå÷?þ¼Oß·ð>=¦x‹ïù¯Ëû´ç'!ÿº½‡ý¶¿OÛm}÷ò÷´ÜB~:—¶_Ó ­ËÛiñ$ìn¯ùÿû´„#ÏpÏ/Â-LïÓçí5–9—éÈMïùÿ}»…zå0BŠ·¿ýþ__¿ý¥úžü%ù{–ù-¹ïyßy¥¿ÿ»×åˆÝ.ø{ÊÄK:ÊG¾.seÍÃq{Ýßã†8”eå_é˦òÍðÛÍÁ—_E}uŸ¾áWš‹8Ö¼i‡€»’ÿ—¡ù x”ïê’‡º‚cžÞ–‘">2ë k…ŸŸ —‡y-Ÿ—Ÿ.›¶‚¡è[J·჆A6#nI'O!y¤{y³ëÈH[BÍŠëÛV .`fÀß3\#¬ƒ/ù{ñ'îuK;…:ñ6à^!ž‰ú’¦õí¨a0BÓÅ1BØqKÂÑ¥Âü>ê{dŒÀëÆßxÃrÓEšâcÅfÏ8Õ «Ôô{(Í»"€ØƒêšÖ#µBÖ³ÞQòầ’3Rî|Áãn…’_¢4@4-š )"U(âQ^χôxŠ8Ü£®(!IRÞxú’gZFmG#dó¾0íãb>r üdÜQàŸ¼ðe~„Vb#%Íëp|å#ñïS"=¸.G#õæHâj—!ãD‚+ðn“t#•Dm€Ë‡õV¯²ú[‘VˆnòÓß–VÝ”i„p*»E[a~Ö¨e‘4-bä¦ÜñnX*$v°9ßls^i¶iº‡oÝù{a]ä ào¶”€õ·Y%ÆÆlC[\ø4â¡ü$­J*`eµœMmú²íL.kçÒªË>Ó³üÓ 4‚4…F6»%bZFÁ8"€ÇQd:ÓµØh¯! Þ„v_( îDOð¸üi«Âê·Iöad<XZ¹Áö-’4Ȳ1—GŒÚŽí+^-ü6Ë=¯ µi„jØlý¾’˜‰KauƶøVn÷¤mPÌ7<’..A´¬¼©ìß71KªV)žUuGQU”@@ŒWæ¡Éô42ôuÄpê˜Ïk‘E4¾­ÀióΉ2ÔÄÁB±¯IUá…ØàñU—%âU7D”Åx%ƒŒn«uŽ‚#ˆÝc³¶!­jíX¬×ìÞ`4 Ìåèá–À‚ƒ)°È\F»m‚‘œ‹Pæn•løÈoŒ4!Y°×Dµãxè·¡ !G {ô¤ô@ /ÛÔšÖómÅšIùÂO´ÿTI0mÌBÝ¢¡UÊͤ‚gJºõ[¥fˇ~TS*æI¦‰/“~#lÉQ(nží‘…Â¥ôj¿}D$KÇ^ßP ©Vä­ÒoeÝèôjÖ‡ñ€½ ›ˆðm«õ2Û2îš«lü>hì .aN±®Îœ–ŠõŽz¸-~‰F_Ù\jÛØj„ìÜ1ðe›Õ«Q;Lþô!Eò@Œ£üäÊß]ÿîšéìôð„¡^OFHX¯^ÚUâÔßÖÌ%b?çV”k-N#`cë´fvYÙÖ‘w&a"¥F.ÈÁ”"¾ÎûŸDXœË}äh§ ¨_FUØ5=6(Œ` K«ê>hK‹Hê{˜Æ(ÄfÜ\”‚˜{å)…¦\K¦x׆3} £Ü5 :‚Ä'´ÛÐÈD–#Œv¢ðE^n5û5Ø v#°§Ðè X&Ãùc 2½§y-?ç”?)æ_I9µîò̸ºâ’’:U£F¼8ŽÞGÙ?TØìÄVóKæ•|–m6o¨Ï6»½¢ú¶ìø 57x ž÷­Õ[,*YØodªRÀþV¦$Š¥¥ÚxÉG5+yQÎŽÂÀa¯n›bÆ€µ%z«'q’&ÐF•7gªÌîÆ%¸-V#pGý®£È^Ýzî숸!׌ ‡@:Ç`´=¬ÃÏ…þ7ÜQuãW·–BÃýцž>mŸ@ˆ³¹º†ºAnõ:5ÊÌØPfMjµLü @åé½bÿ»]z–;«‹­âtìdpìæã¶nH™v^¤æ šS·ýÔàíc?sè’ò%îƒØÖæ=c·þyü8GøÏk£ÿ9€ÜÈ5â(ÛBšü!Þ7¼þÒ [äÚ5Y(’’ߢœ‚–pã”Û$@]Ea¶›Šk2p æNn"¦2qÕ/ã ¨ƒðU6‹hG³.†v×I_†ß…4ŽÜ:aøçd­a[ËŽwÛ‹Nâ”<õ3¹¨ìÀoÓÇu}[Ϧ¿Nm'ý–2ç\üÈ™7½Gdbú3äÄIáRˆÄñ©ð?kÀbgÏW›¶‚Jg:§ÆÎUVHg”µ”½óñü‚ô—aE–eÕuŒ7ïðg¥çõ½T£@OôÖÍ=|²AÛ’ÀÅòC-x½Å̓-NÇÑ*íUSå¡2ƒtf19ì²I°[APЌʹÐ*¿V‹\ÕºÊG&þJ•1•õÈrÅm1d²è~ãΔúý`{ˆdFƒþÎѸܥvàÑ.íK›ÃÚn¬ß9v–•]ólíçsî©Ûá)èãVÒô·XsÜæL•½‹œkÎúÆRMÒ[}:Y}H¡ÛãO1mÀ(9q@.hÊ•-Å*Úú@äß±|”z£ºLÝX2îeê©ôkJùÁ®!¥l¡±L‚ 7‚âZLŒ™¼eYÉñ&±?LHÍ8Ô”y)ßIj²ˆªCME18„ß§Zƒc¯T5!á i¦8¢&!e ™ˆâY´h`È:i=@¶j„ì²µº7j^-’•.¾}X\¸ei£D¢42@iVÌ98¥QÞóZ¤#f‚I©õsŸ© Ò±*„u!ôʦ¿é‹â6}h<>ÐlH/U†=7­|7ëfDFÊÃ~Bð%óM¶vkò+¾Ë&RDÉf˜ý™ÈRê|Ú‹Nšê'ö˜Tè)ß„Æ$ éPf—<‰õX}¤R UþÄ'Zµª$PEC˜ƒÆ8üðÏEéZhFXvrWXéÉ–š-\ *©cq¬µá Jð¼_flUIxªg®Ì°Ó„—……7 ªyÄi&Ͷà¶ßfYE§Fø†ÇŸEôÐ#•wl¸PqÏåäë’Ýç&¤·8íaC‡5%æÁQXæˆ×CjÄØºÖvaCÛaˆU`ôU÷蘹»Md¬^'o¶®[:±ƒÒ€.ÃRŽFT<~Z°n“«ÎÇ!óâH×ïžp¾” LH€’‰mÅhh–¸€7-U²æƒ:²\—ˆV׸Ào0¦¬É5Ä b#ˆS °Í¿IF‡ ÁLLó‹3O®ê†søJ’LM.ÃÿÈ’ö¬k/ÛlòBnYŠŽÖ'ÎþË£Ec®¶ê&¯þÇô3céÔ%Ô qÆÕ éâh•CüJšŒTÜa©ð.)“ÌpÑ0ˆˆvÉiŸ4iJ6.Uz ÊÅ,¸çÅê ²¾Š}uh™(á½jòðÕÆP|lÆd¦µÙõR‘bœÇ(ð8[Í6íÓuÚÓ¤”: 6ä5-íšTÔ0k£«ºRÿêü L[éÓ ÝÍ$ß8ïqÆÚŒRƒ9x]Ñš´Î£dâ‹à Ê0¥×"Èt·˜\C{ìh“©#È[‹Ûª¡¦"äÓd2\Vª;r·²úî)üÜëc,5bƒ­Z ×™Q¤¡ð„ >„u%‡(~àªá?j‹ÔÊXw–OÌÖø¦•IûpõC¸§l8„n^É™&»[¯=ŽuoŒŠÒ *ÍŸD:»6åˆY¯(£&QSμk´/.lü¨NüH ±›ªr •ðß¼3æ`×N˜ðî'\L‰›?v¤¹;s²ÈÑ‘ WK»{Ù%~q&Òþ¶uÀÂß)Ôp Û(C®Æ"A™â¨„¨„¨T õdTRkФíÄ_6’jñ%Îìühœf—5ÒQ“Aµ%g»¸„FVÕyÓ Ð÷„CÐ;Õœžc»Ô«’‹°vé0¼`×d5|a–ÆäýjÚˆÖ†7õð¾¶Ïˆû)¨¦Ø«ÌRYeº£­w²=U·4p'í]ŸêΔCjªâÚ€°s×^#‰Üèjí¸ØGÕ‹¢/›´.‚8 $ÆÝDDÒâ @É©‚ˆZ:EÚÆ²¯·ì¹}Dví%°ŸRA\{µYA[p>Tì.Ît~ˆ¹Ù8­—Y\´åy@¦ªNwx®ãši÷î©lòv¹–í4䎷ˆ 1î¥æÕ«79Ö“ ”k~uˆ]Ðñ™—o›iҢŶƒÔË‘b7…v,ÍÍ£FWå„mýÞ¥mBÜÏicÕ.Ôå¬ß…õ·6Ç1ǰùB”°ªÕ¢ºO*Èç&6î Ü©ÂÞ¹ÅöÈ…íß@¹ïÁQô™l´“ÊžW´~)Gz’áJìX͟Ĺ˜é&Q÷³eØÄNŠN™mṙ‰Ù–-9SÕ6¥$O̾^c× 1ÄnNÝò扚íÙ…Ö§UAL\ÆI~ΕYi[œwÏeRE™W{ÇeÜ5Mïƒó;YzÃ:£ÏEŒ8¾lršZÅupºÏ›Î+­b%M é.¯L6C:A0ûM ò½ª]ÜhƒÆæe-nþ2J­Ää!£|wÃÒåÆW᯻ƸAlˆqX[ÅÌNÚ©±r-™pŠeoš¾˜‘W>n©Œ”С_wÔ(é†yùPκ ãËTÜ¿ë¹ÖØDÙ WÑ§îæ„0‡@‚–q–ôú2Ævã :Ã: ß#pŸ½ÃÈ>¼cÞ ­š±w»š:Éâ^=ßüÔuH7-l#ºYæ½ ´i‘»«,Á›=‰pš&$È䑌ÝG>@[²×ðäMꓚ8‰Ij—ÊxÔ7a“;]Ü€µ4M±2ð~m‹ãù4·y#àXðý§Ã5b tšøä;¦4mØNôÊ¢£¬¡,ÍÔ¹âîR`À^-t£FŸ2Db~ FhnByÊ,qeWýr“ún!rÈ”HqZ°L~˜³À'b\ʦ ºÊžó¨ÒNæÄÀÀ!½L¡MßÈÍlƒp伤0…’xŸ÷À’"ÑüaЧ+H[·ÏŸBްpÆõ'sÙ-ññÄ#H zØ¥¼ Ríóê™îy#;%Ðx)GxGªb^C#Áš€ƒ9¨èÏŸ›ã–ÅgKü—M—p2ª.Ôj”µql³=H–ÉÄ›0»¶25A<6ѦD‹æ•ôî"²#øæ£² Ô-Z#º›ç­•SæLA}K‹í‡Ã00&ÂL‘"J^q¦uÂÝ7|¦ŒÏ Ü»i©½DÌ#¼dâœ!œ©¶k\¾Ô” ó ¨&fPùÿx#âèІtÎÎf®KuB_¯ôxvì©vúó Él Ãh£#­DÊég¦Î§F€’¯‹Ók¿$3jC°ÓâÐUEZ=”ͤrïd'nÅ´{-Ǧ6±ì#÷­¸iCË…€X±!Òæqs·©[ÞŒ©Ûð÷~™Ä¯úؼw¥)r+XײsXRp]*SæzטFûžöN.ùªz‰ãóÕýƒZ*.k«vU<‡\Á'ø6Û¹®AsŒæoqM¹¯¿Øot²Jó¯Ìn—ø¶h ñÝb« (ðcB-ÍJtšè,[õeÖÉÛ]‰ŽÙ_ aË\iN~kë¢WWä]-óv¤ïÎçk™ŒÛ±[è^¡eÏ lVLUam>:âÊQZaGÕªÕØŒ° GÇp·þ*—.ÿ¡BòIŠD¥«»ôr)z Þ O ¸Â9a[Zø©ƒãJ@8)%Q(=ØÖ1˜2K l•˜Î'¾^ÞÝèÈ¡Àg)ó{óÄÞÚ«‚åù+‘6ZІÔÑ‹þ«¯¦Ç=]áŽ-ä)¦»½ ]Ü®»"7\†²·àÔ7‚ØÓ¸Þ `‡%¬íà}þ͘<Õ{ºÛi·[}ÙúåÕ8üÓá4-p—ÒâÓð‘7AïËFwmñÞLz~< ±^ \¬ùýw!(±*7ͳËBW*JãaÔÕä^äßÐ{Ã馘â19šdP'Ý#Ër¶äëyœ'ÿ¥«1îp蜋w¡²žæ,Ì6¿Ìß›ÿr/øVO¾"“ááR_Q„)'ÛƒüÏEtF» ×¼Ôà6Dºú¿4@•UgšÝ…ì>fôlež«àK„9c¹:c…®ï>±ôOö$“›.J•ÐòË6¿GðC?àz›Ïü1àÂ7' AËåUD„³†dôSOu ULMüú.8\™ÐªÃȾ·²g²ROyfÝ¥yþê…þi”‹"?RèLÚÄÆ;µßQn¹5vÕÒà8.¢®q²Ôü——ÿx„ endstream endobj 18 0 obj 5903 endobj 20 0 obj <> stream xœ•XI«ãF¾¿_¡sÀNwõ¦!°üìCn††Ü²@3—üýÔÖ‹$?ë Ùꥺ–¯¾ª¶9Ûá¿·ïƒ þ 9œa½=ÿÞ~ÿeø&søùñÏÛòx §Rò¸øñçðë݆Çß_'¸Î'˜¬Ÿarn>Ù ÒmA1´ˆM•EdÞpÂNæ2ÿñøííöxû²ñdvOœE‚ÿ>Û!Þ·­½kF9IÔô~RŸØ« h·.8d°5Åø; ³»ÙaºLœ·p‚YlƧ˜ÔN%Ž˜Ž]p´¸+éAeŠvf>ŸÏ ƒeÊ~è 1×›sؘ‹¢‰‚h”‰}ëy~.'qŒL›äDÄ¢ÓhÒ–ŠMœCѱIÀì—HÀ+.I¸žNðtme…§#7­@׳ܦ­yç£ (±¯Ñ²óÆZ žÓ-ϳLÜCÑDôXu©u8œ ýH8¾Ú6üQðKÌOq?{ßI`@.’Ÿ9Ú*2·zQ:·Hð–Œâ_;pç#Z8ç'éf„"i5R:ˆæâSû‚c½î- Å|å €)æ#;aò¦Ù«LA‡˜]a§¦Á >2.ÃÓ-_9‹Å«Î 2!Ðçe–Æäv¤ä"Û‰ÖÆ‘c‰‰’/ï…uªÙäÑ–i0`á–2¶mêÐëµ]…Åp09Lþã‘ ,»»º‰‹…¦Få¡Õª~õä9m\E©P„'>;€b yG…[(ž¼¸!”"Õ%‘Z ¬|ªë³bŒ~è¾Õ‡Ø.E ‡¯ûè‹÷-š©6ño=&pQXÛ>]¤°®†J™>ùíBË»ªž–;)›I<$Øðšöž= …{ZÝ–ÈK-•ïsØÀŽ@‡¼Êꩵž¡€÷Rq‘E„`J5.ãW²”zë¦uŽ0¿È+jÔÝhmá=;X/MÜ¢Æ9=I›ã‹šÅ‚B¦vqÓ Þ0×0÷¢¢•¾á‚NÂ×ã Î(î«lZ4a-¤#ºÐFþ÷5a1µ3ðcÝ¢½ŠÔsŸ½‰ð iës“ðð#;N2w¡nùÂi#)Ï€\d0É´>8|_D]âÜ%~¨}’Í"[D4†ŒrÌ“¬:ÀL#òÚ.Ô†B]{4,Ü@\…š´pë΢Fp ÚRÛÑk^dŠÖ<]–ÉŠ[#ÌTiäºPyjÑ+Åk¥zIì{7ÁÁû³ÿ @€h^¸”iô¢mEƒz±Rmåß6#1…tý]Õ*—q録FÂáxA©Å»Ý%Kö±=5N¿RT?¾+‰›œ9Ç­›ˆõÅ/W î6¶qò®&=8Æš'„ån„âåAø%ù”žP’)˜Ú¿YmaðU/–åB,…¢ë¹kMµ¥±2­¦”Ú3Ö²ïwéf> \=Eå£f·íÚ]`\· žúÛTO*w|iOKoÜÝ‘¾ÂµÚÞ‰GŠî' Q@Ôìc»¡‡z¹,èp× ’¤Ü.}î)@Ê;ÏçõŸ +'…CNðÞí¨nž+›¶øÅ´¿%Ú„vgXƒ[×uæåZë^¼´>•[OÚ xÛ×Én0¶k³TJ:4]›ÃJâ®5íP!hç §Æ¢Ø¨¿ÉÎEG ¼ãÙOvjõ/àƒÂìdš׿ÕÃømpF†¤ «­®èW¥2Ô)ë%q· qÏx»RŠ/^ÎnáÆTX®3…y+©"-É­ôúw*üw˜«÷’Åßê¤csõ¹;¤«Ý}z|þ²Ë»@ endstream endobj 21 0 obj 1519 endobj 23 0 obj <> stream xœåTËoEÿfíµ“æe'nHÙÐÎv“ÔkÇyPJUcB¢ÄI%ì–HíÆÙ8‰ü’óPƒˆZ!e !*!ÁUP4v/=pà•ˆPe¢·^è¥D¾Y/ITå?`´žýý~ó=æÏ~«…5já ¸ šÌù!€ãGÒ˜\_¥bõX â?P{a!ŸÊÜë,=Zñg¦Ò FùZ€û-\ÿjÑ4æy”ê#ÈO/¢ðæö«^ä‹ÈÛ3«—®“ïÝÈßC^•Î% fŠãäÉ—òiW+n@¼œfŒ¹úlÕÈ¿Á=|Ï­¬`yÀ;Æ×ó3ß|t yyìícE@<6ÿŸùÝ®²xV¼ Á»p †ásør° ¸ÝÐ!è× ž„Aˆ…Ë…›¢€Ê )ÎNMjlt]g œmaž vF·µMÞc¤)ÜbD¥¿±Ú`ˆ j<¡½¬èrˆ¹Ô¥Ê¢“šÌ¢zˆ¹Uî*+òëÚ}iK—ÐNÛ–ê’"31¨±¡uÝ^ÐuŒ'ªu³çCÌ£O’«˜^•`¯Zl³¥èžT¥6úéóÝ!V­ÒMžä; C™«}D¡ÌÝ1Ê`R³LË <'ɲ.Y6KTOx¤²;Ÿä“1bJ¶Ë©Ui7óg5J‡•!c™jt~®‚ÛÕñ̘šZtØ2‹ZŠNáÁY-±>.°¨É úÔۙΔ[dY¢e Fp7ÓÎÞdÛ¬AUhÙI®P->%Ɍ蚅(–B­K1¸CÅ…¿BÌÇÿ†FÜ·ŸÀAãcXü¥ËVÂ]›T,Âz‡Ûè¼byÔ¤oq% Þ†(‰Æb$~ÇI°gn<­ñ9¡)s¸{%&á‹(1<ùhB+á=z)+JðÅh’3[ÿËuTe¨â¹àâ—TÀ›¼8]È á"î’×Ýü°·è(¹„PtqYärÉëiýg D¸Þç—ýí²_èNùdgQœþûËA÷ð¯”Ü.ºîC=ÇëŒG±…a ÝÌ]f5[ø}$‘ròéŽgúO÷õ>Ñ|4àõó™Ä+Ÿe3cãcqr÷Üx.{cb<›;gwaa¡ö‹ráBÃÀ_p¢ÊþÎ~ø)vâàwgWƒ=k« ôóÎl_>`òxo„?aPøFWÚ¢ œrš¹ìÒŸzøz/NÃ^LGÇË ÇìB:Øø”ƒE¨Ã>PÁÔ_DKâ®Fvâ&€% ˜wÓÁ.Ô¯9ØøS‹ØOn;؃úVg²‹öFz"t¢`$ÓæDÞÌNmdæré„™ZK…}a͘…•¥\–ö†ûÃ}û2tâ=ìÂRz!‚Í+‚h ` šqç,LÁd`›]› )XCd åa‡i3¨` Ïá Ïƃ Cß¡ÖX­=vSXï!ãÙ}›‘÷!Ϊ&µ"!×õâï:̇ 5@pE »Ã¬¦³@à_üSwÄ endstream endobj 24 0 obj 1154 endobj 25 0 obj <> endobj 26 0 obj <> stream xœ]AOÄ …ïüŠ9î6´œ›&fÍ&=è«?€Â´’ØLé¡ÿÞ)VM<@òxïƒ7èk÷ØQÈú…£ë1ÃÈ3.qe‡0àHÕ|pùPew³MJ ÛoKƹ£16Ò¯â-™78=ø8àYé;{ä@œÞ¯½è~Még¤ •j[ð8Ê=O6=Ûu¡.;äí"È_àmK¦èú»Š‹—d²¥ USU-4·[«ü?ï †Ñ}X–d-ISS²ÇéNícý´·2K“2{©°?¿'Å´Se}~‹my endstream endobj 27 0 obj <> endobj 28 0 obj <> stream xœÝ{m`ŵèœÙ]IkYÖJ²ü%ËZYþ”˱lÇ_²6‰-˘Ärb'¶ƒc;þ·íØJ‚J „8H !@Ò6¤„¯(!$¡”âr¹PJ¸¤…r/…Þ¤-ðn iò¸”W ±ßÌJv> ¼÷î{¿ÞÊ»{æÌ™3gΜ9sffÚÐ…¢Ñ(bÔ±®}ðWϽý „Є@ß±1(^¬z(•ÀçÂq݃=ë²Üïÿ!æo)¹žµ#Ý¿07Æ ¤&Erõvµw®Tr"Tzˆ { bÓÔíJ’þ€¤Óz×oNÓþ~Be$‰®èh_Šl@ÒIzɺö›‹… INÒbûº®¢ígDÈ‹Ê180œ@öi„*%š?8Ô5X¸òå$ÝFäû ÁùÑ+š€ šÆ Ë)”*>J­‰Ñ :½!ÖŸ˜dJ6§XDkª--=#ý{qg¸3è{ÜmȈFäç5[‚bÑ&„¦?¥©+Ï©åÿo¥P…_'Ћè(:xMÖt+y>u î%ôOèIÚîþ¶Ï£'"Ð^´ÝõéV£;ŸC¤þ+WÁŽ Iͧщ¡¤‚›Ôº&’û>zýÛYÁïáutzœPÞ‡N‘ç~by[ðgè>¼õãenC·£¤ ú6tV •¾V¢.4pÓ1´=†6“Q8{q·Mÿ'Ò|ýc"ùNÂgõ¡õW•x¾¤/ÆBd='ãn›ÉTú™Õø$Æ—ï'‰{Q¹Ûá="çÝÌ|TÁéàBReScCýÒ%uÚÅ‹n¬¹¡Ú_嫬X¸`¾ä-÷”•–Í+,˜›ëÊ™ãÌÊÌHO³¥Z- ±:A£QGñ*¥‚c ÈYi󵉡Œ¶›aóûçд­ Ú¯B´…D‚ò]KÛd2ñZJ‰Pv_G)…)¥YJÄ2T6Ç)VÚÄЛ6ñ44×5øî [“º Ëd˜Í’°ZI ±2¡·B A›Xòmì«l« üŽ©£ÚvEÍq¢cQjª ʲ ƒ¬rœUYr #•†VbÒ+Û;CºÆÊ “ÕÚ4ÇYбUÈYh¡Ì2¤XRÊ,Å>*:Ú%sNŽí>- UmŽèN[gûM!¦”c*ÇÆî é¡l[E({ó‡ ¤å]!§­¢2ä \k–ÌÖSs¥Jqé‚Mû+"ͱ]øôZL{£HþŠ(è#êóÙDßXÛXûééÑU6Q°‹Ž¬$FFRêôôOv™B¾ÝM!¡­J"õ-© êV4†pºOìm'òçµY‹LV]Ó Màe#¢¢¢S«•6|×i ­"‰Ðh]c8-¢U¦ãHr9šB¸æLÎähÎèLÎlñ6éÍš¥c!6½ºÓVIt¼«=4ºŠØÓjÚ6!ó…ÉjÓëÄbW“L+©ª;ûÄ—AÔBJ]]€X -2&ȉ˜/¯ &RA†N/ÛʧÒVÙùÛØ›@ˆsœ!¿#Üõõ!©‚R{¤*åºH‰ö6ÒE}r÷…\¶ÁP¬mÁlR±*û–6ÊE"ÅB± C¨­#R*䪬 5‹•cma(/[]ãóÈ=}þX¾hzÖòQS%Ž[Hì*£r¬±³;di3u’‘Ö-6š¬!©‰tp“­±«‰ÑPöyRU®1„Ö7Ö,µÕÔ57E gPvlzåull¦0br!UºJlÄ&¦‰ !ú`[PFž!eºŠÜQ¸Œ¥¦º Llš¡&b„²ÅÊ®ŠM_Ô£æ´Ð?ÃMA“„ÏB¿ÉÚd _sœ˜d‹‘ŠI Uª&‹I'ž€à0a#£¨.¨Í‹¶.[“­W IFÚ6ªYËeÈ:ôUý5©«”EÔ„¬${&A•ò9LW+7T%§g“þ벫g²Å1•­féen‹0DDòê¢&,éLòè§ãÙæk'ƒ˜Œhy<“$:–{é°³UwŽÙ–6–ÉÔă|Ï´™Ö¥G5PS¿`Ž“8³Çl°£î˜;–67>/jG}ãq xaÛ‚¦ci$¯ñy‘Ì2S,EÒ„H”Ó’PÉô¦ç%„Få\VFÈéŽÓ€dœj¨ã4ã„&86Œ“d½H/%ôÿ])vÒþ¹¥©w¬­‰Ú8Š#![9ÑŽ­ü`Et(ÊÖµ ¤¶- x/Å{ÃxÅ+‰e@Ìqn*mM˜C'KŒ*È£“k °åä*;®dUòŽ)¸ÊŽ3˜€èCÑEW*ø¯ËŽÅ»uV]ºUg­ÀâT<8ÕË5üýÉ öM( _ 1—Þ‘¦y^çßã¿ä™x¨æ›ø~'Ï–òÀó‰<þ‚‡}üë<ÞNWó}<ûÚ{üŸxü'yÈ&úH}hYŸt!Ïuann jiØìÎ+œÃØR±.¿œ¤R°2sÀ–ƒ•k« l¸;ñQ§{ßÚK_ݸ-Ô¹ãÔ€ë'Ú=wÍé¨/aá4Œ÷¯ôÏ™³¢Ú)ôàÛÛJ÷ÿzsÂØ“˜oغJ¶í¤‘;Hû,¨\ÊŒ¹Fl4Z£-¾IºþP.:.!N…³ qþÄh½ Ô’µ‡×ëu¿é×Ê–õòtnWËÜ\·.?gºS£»œqçÅÅÃ’u÷Æð0 °GI 8.K­ÏM)_>/‰)O­ZP_Z^[¾¢Ô¬d㸢Žu—ÏDdS؈l¥øåçÉjñü³*µ_<=}^š&@j©!MŽï=×—.|ÒÙ®&×N£pÁa×I×»®]ìNltA“ ®8—ÏÅ(]‰Ñ¾W5 ÐÄi 5k¾Ðp*ÍWxÝóžçOæìóÀ.ôyFC…Ÿ©]=ß4§á{u—ïf–¦Ö,ÌUrÎâR’]`^µ†>7ÝÛyÙ¦sÔߺôòÝÄ¿í$ÍÑçÌaIÃ(ɨb6—eT,u_vc‚ŸeU|Ø™Ÿã!ÄOòøƒü(-²ÿ¿$gð”\—šî¯•çNËÑRâDˆ¯óñÜW4«#Zq¬lq_È››k(p[·óĉœøÔS?Ï–|õ*šñMÌ_ÈøOGëžG©dܧ)ÕþtŸ@pMá3G‘ìiÏg²“™ Í„ÑL®hˆ'ÎÊ7©¤4¹šóšKt¬GŒ%l+áYEî12±¬Šx๹ßÑU´˜âøü¯ÔSuâJÇ$•/Yík¾½>ó»º!ÜWæ•aÒ¶<4ý@Ê!nDïŒÆ8‹×ø£¸$;xŸK6&ãôôŸ”Ãm-/bŠŽÆVi3cÉ~£Ñ[ea€É]8¹\ åö“IÇZ—W\ÇóIîÖXpÅŽÇâØXm IÈqÈ8!Ž{=í 2Ñ™‡Ü.Ǫˆ¼<:‘©ÈÑâ ³—šQ@¦ /D4£Ì”-ר‹£³”1†øz¢([ “™WŽ= ŒaŒ±qðèÕÝñøòÿL.Y^š__ž¡øiTQÏþþ3ÿb/զĤ.ÌpWç$0 såMlËnk°ÿó‚MÍ­±OM¬Ù¹8³¥ W–˜´™ Ý:iÍbÇ Ç¦ru,3¨R™æÕæ×—ŠwyW šXÐå5W7¶Q›#rÙž¤|†Ncggf²KˆU¡óÜ%ŸãÈ{’Ã8äF9¬å,¾ÄÁsT}&jÅL‡³'¹³ÜyŽaDç> £{Öž© ]  [7v‚;ó÷|*Gïô§Ü72ÑÓRíHÌμAs§oIß•ŽWgÀ-i»Óðê4X JhfÀn^mÆÛãÁ¿:s*£ s؈1·"Ô–„&½”„Å$Ð&ARª K¥Æ/Ùb6ÔÚÀfC­i-ÎÕJÚAí¨vR{V«Ðj£Zîhù -d¶v€´€Fô¢ÑÆì¨Ù˱G9i¸ __˜æÎcã•9 “póóß“*o{aÃ’»Ö-³>œ1øÀKŸœš~zÙŠ£€ýrªž‹­èÞÉþ=°÷ìÖ­o?XïX¼fþâÚÅëþ ¢<Q/t…ž.Ë[á³=Y¦/a;ç$¹EZž}1TYL–ú4#šf ¬H†ÄZööaö)–%©hÿ@ÜÖ8­‰c¯çq'rÇ*¹ÑÐ*ÑÞ(ˆâµ†#‡[oRw-´³Üî ñy4dAŽbö-ë[Ö§Sû-ÐÙ ÜóÜF·Ñ1klÏn(ú·ïm+¸ù¿p{“æšUjÍ_ñ¯ïøì³;.7,öªa?õ(éël²>HD;$OS|_u·z»šâ^%¼JMTô\‚Å¯Æ å¤æà™bG1²r–‚çþäߨN ,Ó@… 5¦8 ° þµȬp\Û4{5‡4Ì?"~í |¨w5ðŠNjà }šešíš ÍaÍ«šwIÉOkh¯½pjÒ¿2êÖlÔ0„Y 11aô(ò°æ$¡¦BðÓêa#­´^Ó©a®®ø›õn”ëd:Ãk†,×sEš°,ª}š÷4ø[Ûò®\+ó e@¥ñiØyݲ*=· öäæô¾yøÀ*GN÷ftšI<ƒRQP*wŠ8˜¼-oŒÛ‡Gô;õx"úp4f£c£±š7ñXÍ™8ÌâXŒ•¨JÚ£mÚhn¤É ‰_Î¥Ab•EªØ@”qã€(‚ØÕÕÄKÓ¤€h#+g¸ˆXóßÿ²þø–ùðÑ­§6½˜Y³¶¢r`q¶sQ_yåàb;N™úpêÏ»ßǹ¾Ý¿Þ}ë¡U™Ù‡¶Üúت¬ÌU‡©/&}~’­AóЋRC0g[0n5Ž™5q^vóAž²Õ)¦œVMæd?‰Cs xá !d` Å£êê()1Ååôך[ÍX4ƒ¹­x²C±<«gÚýÞbŠÁàä²"Jƒ=i—ÒpZš(ĸ6õ ªA­æˆ6è¤.\ˆ¼ˆ¥ƒe”_­d|á•Ü%²~c-¯ÓG«fZÃj5ap«†+ÖHK—ùÛˆ×>¨¡Ÿ;GMá4^=I‘LºŠâ•ˆoWi9DÖwòN7¾¨Ë¢Ž—†ÃÄèté]ä&*/r·éŠuîÙ`“tH‹”diåÖÉŽ7wê¾m'NÀûoOUÿÀ_ÖMmåÎ|ÝŽ5S®ËÈ:k!¾åo$–ö¢?HýåPQ‡Ka{!l› dÁ+¨­&«ÃºßÊ6™˜ñ.ìRÂÙ»l+¶Bè3ÂFØ›³³Q³"–4”oVI‚Á¯Rå7#‹`‘,ŒÒb qþ› ; ˜RäÓÃEP›òïÊ Ÿ)ÉC>çj°C“jì °ƒ=imãa ¤s8›¹IC/ÈÏ:0é¸l‘wËfGfd`ÊŽ®™Íä*¦‘ñÉ\Ÿ\Ad0€BäCx;š@¸D{ƒ?¤…>íˆv§–)`*ü}²Jía61w1LŒ ÑùYªÜrây¬‡°EÀ¬~TõÂ6a¯ðŠðŽ z_€+iÎ$+€J`0e1­Æ+0¶cµÞ¤—5úú]úýú7ôïëUÓzxEÿŽÔÃ6ý^=nÓC…¾^E=°úX=~íüŠ ™”P1ÐL…‰fÂû”öSN°‚ò0þoÔ~1„îúúÎSž™jÙž« TªTc®VêW¬˜wµ ¯¾£Îkdº>ôàÒÒ z¬Ôb-á"»êWEf‘hìúàíúPî*Òpšú «…7âášÇå&^Âá 5¬—We-ë­6pR˜ørfžÁ»~3µiò/JC¬N¡0ÄU_¼Ä–\–â¼^£Ñ»À‡_ûX3 Hʸ_"ú¤f¢ QQ¬&Šn¯VEû“´Ä‰ø8„A-f—™ÎÇ[Íãæf¥Öì%àQóKæsæ‹fei+p81KË:ýf)Óé͹æ63sT&b$3h lD“HTh©Ê¼nÂ…· Tt‡àè!G‰Ã¤‡dÙß³›æfp¡ïăÆ•v׉•Iº9ú,·Yý6sêëjæÔ›K»j ÅN†‹Ë.Ël¿ƒ´ùN2‡~BÆc:‘*'H²Ú­%V&1Æ'¹Ôãjü’ÆÕÔÓjF9 ¾siÓ0JÒrIxÁªÒBá‹Pæ¥L< ƒ‘}4 ÝGS,qc4ÒÎlš9"ý·þÂ̲3\_³ÜŒÄ¡¶È¤ªƒ’ïÚ7#=¹dfç ýôõ;g{ºð¯hßrSË™¯I;-ø-©ô~îǰO€ w X^Žn6™23}fx$Rà¿ÏÛ 0d€e†n¾OŒž:Ž4’% ùéR,Â> l·@“|H´€Â*‹^'êVPX3¬…VŸµÛºÑºÝzØzÒúªõcëÖè×è[©¾¦ßû“ÿ+ÐL¼íÚ"ŠX^a#Y>ë2’E3ÂhõŸ[á¼^¶þÚŠOXá n·ÞgÅA+´Yau‰ç[A´¶ê­øCëçV,“²ž°b™²Ó´b™0ÍšoÅßM·Œò™0Žò„™ô]*È´TøvâZé0¡&¢†hó÷Zq›uЊ+¬õV,Zs­˜µÆZñyë%+~ÅúŽ7Ý<ÒøDˆ BFßÈÇÈJ¬lÀ:jÝc´².+ «`ÅJÒÓHLÑi£œIÞ>ѹɟìÒ"›û×ïúG¼Xë?8¸Î ^É–“y¸Ï¸¸–y1êq%¸t²—k¬h¯l¿Yé@ÉÈ, qy¡®v|7eÔ®ºyqj YNèjw¸uSK'?Œ²X0oN‰zçg«(e•d^Ýx›ƒ-¸ü„©¹Ùϫ疤àÕ¦?ÕÇŽM}‚d“â܆GñA|s¸DF!B?ä"¡1‰’çæ2V£…=2Õwûí¤ìeúå!ov昔öQ<”dß7ge?”Í•Þ Ü)|_` Í>3.$ë:^âȤ^œ\Œ‹“!™Nõr`€h¯&)OÞ5¹$¹IJ@ PL5Y§Æ¼Î£‰3'+Ù²lÐhƒ8¥Í¦Œc´ÙvÁNÇYµ+Ï_m‡|;dØáK;¼jÿØŽÙaÂ#v(´ûìÝv&ÑŸÛá$ÍÚfßkÇÝöv\,‰•#7•]ÐʯmÒÒH…Ò¾êüØù…“9ä„ 'Œ8¡Û õN(túœ8Ñ Ÿ;ác'¼â„“NØç„íNÊ$ÅNˆu¦9±Â ¿ü’=餌ؾHQÞ™èĤäóNXæìvnw2¤„ƒRäC'¼;Ãõ‡NØ+3rB'¥†|g…§ÎÐîû /;íÄ'œpØ Ûœ°‘JØéÄ ()Ä93œ˜uœŸ9ñ;NxÕ ¤-÷É”ÝÎN<Óš4J ,m“ô›H«ŽËÄT¾ 'Sá¬wâ™zû¾ <á™Æ1Aç6ší#ÍaÒ(IœN›ð±ïurbÒ†>¹4·Ð‰g›y˜pÀ;å&B•!TÅr¾â|Çù¹“•ÕZã„܈Z¿’‹”U³%¬‘N'crÂ%YyoPUmsîužp²^'qNÁ‰UJ:ÝgÅèü ”¯„T%(“³­Ö–­óÏ!6%¿ãâlL ™¬ãÝô°ƒ¾h°>óÅÀõ§~­ß>ð¿Å'\ç®Ý³º†ïÊoâ×_]Kî(¢i—˵~Hö`îˆCYïÿZèý‘è‰ÉÌŒL…2”ŒJA|\|á¼r ^åš;ñ«§U:UÏG© ªãg§~uü”2FIVѼJP¼òó•U*¥VùRÿÄÈpºæ83–X.ß@fnküB1=3#Í"ñ»œ˜´Àœj#©…Iø³{Èš7š¬ß²H<¶h ’îL›…1w¥Ã²tȶ6Yû¬LŸ ’mh„ ¦;MXa‚Ls¿¤’2ì~Iã*2\Gõý†`ƶ lÈ({”IÉ’’áGªŒ{ôp“~­þ=¥OÒc½f8A Aâô½нh¯ê‹Áåhq»[\áÀÅ‘DO Ãû"3û#î‚rîÚC®²ØŠQ(­Æ÷ý?<0Z›VÑZRØzƒKyš_üÑš¾CëËÜ ƒ›oY·,ŸÛºáÙ{o¹eDz²å–”²¦RÝÛ»JòVíYY5\ÛÓÕÝW¼/£.¡ûeÜmȉž“’é®ñv Ó€»0nfV3¸ÁÑåÀ ö.;Î8=ýoR±b£ Q˜¶/ ïL{/ 3$¾°‰$ǦFý’Å.ã#ÞccÎhš%½ß*Šêþ³Q@ÏA’†³²„´ U! «ïPã5Ä©AÍ%άUåƒjïÑu&°Ó;ææ¶’)pH^¸;äÕ»|_ÙGb®ÞfTÚ V£•)ª8Ö,Ýú–džÆœTgUvù}CuNûâõþ97Î/ˆ¹ëå;L¹{:úß(Á«C[}ù+6ûŒY5¥6GýæÚùëjBrz,þbßÔüôiÃ#û¯$¼ÈÖ ¹¨½'ù7Ï›‹é+î*‡†è®hÜ\²ºg0… ÎÐë>>1~$~g<«0Ç™7š·›YÞå“òRsc`k̹S5ªðÉ'uñÉ~Ž+«Ò&¥‰UR~« P•Xµ§*TÅÎUÁdÔVÁhÕÁ*¬­rUá³U—(ªlmê¼€EÐÎãø@2  kož¬`y/äO”"Ÿ*]½°>²±{•+€«vé<`›Ý›’× nòžçŽaŒºX…áºÍNœÖ²§[ŠyN¿¥ÓÓéËÀ±¥ ƒþžû[ŽöýÃGr0ðøIƒŸsÎ ôVvÌ·X¤U…=Kò¦–gT­*Kª©K­¹yÙ3Ù5%¶Ê±7ïºý콋úÚËçe1¼£¬:óëþãGÌ«ëЛÛóƒÁ VÙs: ÷M€ØµŒuÊDƒR )½/‡C[f™©Û„åã1²€Ý¨Û®Ãô€«£A­‚åN%ÞÈlg0‹AI zOìÁX›=šÒO 8qX«´ S{ìsXÞ;ÁòA4ÐKÙAÿ­`Û¢;Ot÷¿£¦fÛ‰Õ]ǶÝx*‹XäÁÚ¬ìڡꪡZþùSŸñÑW ûd½·½ë‰ÞÓPú™Ôw$~¤†ïG?ŸÐÁ>mÛl8(n÷ŠÌf˘å! ³Ù4fzÈÄlIÚ•´?‰iÎZ>²¢4þB¤£~—ÁkÀµ†— è^¶d8¥¡D=åêåpoR2‰~³!¸5a '$pöáTeÌpü¬ƒ!]¦+¦¹Q÷K·]…ä3s·ü}ÁìÞôÌs¦g­ÞÕ¶ÿÍÕWm{qcÝíÝþ¸§’7­¨iÈÅÆê–µóÚö¯-õÞ|tà“Ï^I¯^ã›ßëÏ´ùz+óz–àßýt꣟®´úkM+*ÇÎîvÝà6UÞrtͺЖS‡Ž.ë.ÍiØ\Sµ¹Éê[¶ímSËY3»e ´WúŠFŠpŸ}ÄŽ·§M¤á4o”QþjK“W+›”x;3ÁÐsÊó’—àQ•t¬«ËFç&k}H„\á’Àª„PxË`°lO¶”ÁtL–/ÃÉÎ@ª§ÕšT….|$/¾ÃDÞeÍ›ŽÈR"ì”32m)Ì·|ºòÍC‘¬–}ƒÁgr8€ÈÐ3Àç(-éòîkÉz1¡tÕ e«ks2ª×új:Jpê–³ XÌ-5O5qŠL©gÒÜ%IùÕ.càÞ7oë|xmQjÛ‘»èáPIÿ:×ÝC¿ãáÎ +ýŽGE¸•j¿Ú'¢á@ôt4޶"Û¤í¬í¼´Ö£6°Í|Ç“œà›L”($æ&žO¼”È©“P¢ÚˆôNï·ÇßòY•-|®OН¾³¼÷ÎEæçt¹>y_âY sÛ¼EyñE]»ëéÇR•½•¶œú›k.ßΙºÕº (S¶yþŸÞ'ñ}sGæâXhg'ÎÞž2‘‚«MM&\Í6±x;Lpµ%ˆ ŒÚ9ƒî²åê/éY•>TÞøÆ±`zÀ,èQ´‘sðÿ†%PSÁJ퀶ÔVÎÈû1 ¥Bé¦NQÏ|憶üÈMæ†X1ƒÔ Xù¼ðÅÙÃAbôp§^þeSGRQn*Kæ&{aª)ež11®·yêÓ©?ÈgƒÝ‡o>Ú92O#æ$YCò –?‚ŸÆxÙÕܧü¡•۔ا\¦ìV2Yô”oQÏ«HSRœP¼£øP¡$²3j(…ÀD”9Ï’.ÖòiGÖMþI5œPÃA5ìUè‚jhSC½$5ä«+ÔêmjV&«—øE5Ī©ÁÎ9¤fYušL&=ë.÷ËÄzK¦ÿ¼š,(Ô¢: T³J­bý &ÀjA G áOòɶ#¼é鸲 p¸ôc7wøÓ“Öõ‘ƒiýê¬Æ¹øû—ïgŠ.¯Æ/ìd2víüú·»d½mjÄ‹C ¤9wià.c¡ƒ.„}} APŒ*°BýýÈI$k|ÁÄ!ú‘q¸gZŠòäUrf"†™¹­öæÝíϬÜÙèp4î\ùLûîf;ŽÝ5õçúú~÷ÉÔ®]SŸèƒ?_Þ-ËMdqȲø%Í ìàay,,'²œžþg©8ä}B–ˆÎt‚E1N¤BŸ“Y@@tÇ…Jtùj‰`Æ:güv|‹HÂîËT¤>¥"}ò;*ÒÔ®p ݉N°Uì£HMÆSÎÛj¸S °0ÆO(,bŒßÃP„w±Oóï*ažò.%VrÏÊq/ t‰®ð—ˆ²HáOÁut¦ÅîŸj„Çï‡ÇqÛT=©aÉïVÍgÚÝÚ'tgôËôŸ^Ž-Š½Õ˜k<Ww&¾7þ™ø¿%ä',‰h!KÞ[ ÏÐr¡fâmå&IïÐÜdX6««•³z¤%)ˆ”R¢u˜AI(YBsOæP4z$+P :•h3:U(æF`Å@eV–Îþ7s D` Ú 3ÔEÛ?¼aÕꮎ  ´]âZÒŽ~R´½g¨«kméYâM½}½âÈÀ±½££k0H4BÉÿçœïvíl!YRâÌÐ AC¨õ ^2ðD2°D”‡rÉo– .âäEäGí$×I jÔ:P棵ä'^UzXNu‘wyo”ËRÊI©¨’p›ê \‹lŸLßNî ¡n'´]Ä ´x Á  îï¬ÿFR~•\Íé#ôý$w)Iõ¾´\Ú@ä£üæLÁôËu º9²TßÅá»s—É9óø¹D¢\Tø­¥¾›ãÿÂúeÞaÊ>™w¡X*Sä’TA¹¶~™ªþ[j¬%5v“òT_W(;dÞA’s poD—«‰ž‡d :år3m&5SóÔæ†ˆÕ \§#*ÝF¹ÎE2>(ÛÍë•SƒdÉç"¿Mò/‡Ð\˹#Â7G†ÖÊÿj¹ ƒ²»äî!´áÞΑy®#VÕ ÛøLÿP]„ûñÛû¸[~SÍË%‚D’v¹¯fú˜èpÑd—¬?Êq —ҬôG¤Öv"-Mûm¦O7\¥ãM²<ä)’¶ óeYþ»Ïn¹äsXZ/n½ˆµk/¶^¿xô"§þèÃËÿà³hÿÒ|q–ߟ÷YÞ:îüÅóŒtÞ]è;ïK°üÎs®áß=LÃ9`>`¦-ÚßX~ƒå‡ô˓ﭗáÅÉ2ËÏ–Ÿþ,Ë2ý<Nž=ÍÈЧõy>Ë)ï©ÚS§¶ž:pêè)åàñƒÇCÇíqØó„žís Ò>ë}öâ³ÌhhO‡B“¡³!ÆuÔ{|:ô4ž|úìÓØõ”÷)|àI˜|âì¸öÈøì:2pä¥#ÓG؇÷§Yûa`^š€ ŸÙòý½ñ–­{Ç÷Nïerï•îÅ£÷Âàøè8Þ3“ãgÇqíîÖÝ»™í¾iË;aÛs-Áa¯e˜´` ¿ÌÒï+°$ABC¢;¡Aéf¤Ím$¯•Ü7ùæZV4û-ÍämÈÓ7pD'lÓ0À€–ñ2øbÝt–ê Š|R]z–ï-©>Õ>Ñâ'<«È}Ôç|}xÔqyÆh„> endobj 31 0 obj <> stream xœ]’Ínƒ0 €ïM_0€YD३h¡sužêé¹À§¬}Ùºå~Y÷.å/à}@(KViÆæ©nÀÖæ^©HŠ"õÀ´ÿÖô‘S®]óY[*]h„‡Ô±"ŽBdM+äÏçÈ!ó9b¾ ÇÌù‘9C>4ò‰kÒ^gÞ—öÊxþ9ç\й0ÈÇ`ð<1ûGgdöi~óÙ_È›?ͳŒ’ýc<—dÿ÷•ìÇÈì¯ñN$ûkÊeÿˆbØ_Ó^ì¯ñN$ûk¬¯Ø?ƵùãÙÕvÿX_mþ'äÍŸbØ_QöWxvÅþ*§Gß^Ÿûó§­Ds·Öµ51õvQoà·Ï§qÂ,ú¾¤eÆg endstream endobj 32 0 obj <> endobj 33 0 obj <> stream xœÝ{i`[Źè|st´[»Ž6Û:²¼Ë²lËKÛñ‰ãENV¼a;8¶ã%vÛ±„°%@J6BPÊZBX a‰)M ÊÒB[nàBÛÛRJÚrÛÒ’Bo¡—[°ü¾9’mß»÷½_ïØÒ™ùæ›93ßþÍÍLm&z²“pDÜ40™«™ !?",ƒ[gÄï´¬ÍÅòB¨42¹nSnø­?¢@¿nãö‘§Ò·ÜJˆq ^ÊÔU ©øÇ(EÀOã·¨YT„õÌÑM3—|G±ë~¬·cýÛ'6Î~òB*MXïß4pɤƒßÌa}ëâøÀ¦áwÜïÁú „¨§''¦g2¶Å ©S°öÉ©áÉo«?¾뙄pï" ð]z,*Yr ^©Rk´:}ŠÁh2[¬6»àpºÜžÔ´t¯èËðgfeçäæå ‚…äÿ¿‹?@ìø]CŒd‚¯áW~¾•{„¸Ø}îýó¿ã¿‰¯œûÛÿËY¨ç cä&ò3ò½d½‘Dɹ!ç_ß%/#Þ6l‹’rôû9‰í슒~r5¹íàýTŸ"ÎäÄìrÙDà“£8‹.(Fòäq²žüœ¼„¥ hs¿!w’7©Äu¤$!#rq„ Ûl+ý8 §ÁßПüß:êÄÄ`A,H°¾½+&ÕcAHò¨áxQ{ ô#‹ÆêeöÅBþɘÍ_·ÀO6­†±¶.¹K²[̶,Fú“½b¡†zöd±a_}b l,ÿª®S$}e*DNJµ3…» éLpWŽ@çö¸nî2a¿@W P%€Îî±ì\³ ªl dGšÌyMÎ̇ÍÔÌ#i §dwFÌN³S)JwD‹ŸU‚ÒœÇeGÓ~áƒRø|i&}”•YµYu•JáSR݃DÅ¡$¢Ùl:›4=–ÊJ$"£!–B¡@a¦ÏË'Ø”þŒìœŠ°#bZ¨HȧàP% “É© 'ž{¡çú¢¼ ÙÝWŒVgTu­ëªªèݾ,rí’¬!õ¢pëÚªžu=U`ûÚ¡<û¯÷¦XòÂ¥%K{êW¬¯K·^ßèò®h)®«(«ïc>ñ÷˜ÅªÑ':ÉqiÂaQ_¤§V=(ô°‡@»qÈHf³¥Ç¡‡V Ð“¢·¥è·§Àh –¡>ªR@‘bKÉLáRRÔNƒÃáf†C¡°)2Èä^µYÓç4詎sX8uŸýØ•)@”æòg)až‘I„Ð-¢Ç¿P8l©t…4Úídœ¦³hÕz“äóùu°à"­Àœf¢J_÷ÿî™s^òÊ¡p£6†åÙ3Ú“Ï(®úôô‚«nÝçZ¹^Ld}nC{íG™È2)ãœsN°˜uú&ƒ”M¡)†“1ÅnQ¶TñçžDMâyb"µ%¡N¯VVcyMŒíèÓlLW6S´¾ôM[‘® ]ÐÕºRÜðÀmhqíÞÜîžÕÜK<÷×™5Ñàì­Ìa¾Yݳ8Í©å|­â*â%¤ú!ÇŒc—ƒK‘Ð}^ÝáûÀGUQ7Ýàv§ºH“Q0ÚlÖ"#£ŠÚèD.ZS£.§Sp•Jn•6á ‘ê!£:R‹æ0VJ˜à†™£ôf1ÃY^á˜Wõò «¿–†Ua•Ÿ3¨ƒþ@$ËÞ~A¾°k•ƒ—©¿²WÊú©é§jícÁ ÉÊ {Š–å¦-Ï Á`ÎÜ>ûV^#[O}ü%ØAÞ v’uBCî30RJz iiPÞÉßk>Bj?BJ†Q+Ñ2_U¶P9Ø<äxcGæÛÚV÷å„rêËR‹3mùU/^î¼ ­½"55-_^iÏ—’;•Üe’K¥œÁ hÌ€]>˜òÁ :|-ˆn››f*á6óÌxŸ”²ÍC ÈÌL×X­0¡Ù¡¡M6€ÕŒ‘U?íPQÿVpÙV"Õ ÄB K%#ihÁV&¯<¨XÂ'=üÖ%ô? •+ûìGî‰ó:3‰ìÀØŒ‰r6:þ‚‚²×Ê G™@Bó-99B(šaj1õ™&LœÉ”²J˜SÑM„äð¥d³ìnQ~溇ó(Ëtñs¡+,„n\_ë]¿û*pÖ¢ÖÚž[ëÒÄÎáÉÅW¶ççF†¶\S/ 7fÍÞµé¾Må5‹«FÛÓO–ÎôÖj5Ùîâ‹#šœ|ÏâPzqÏÕ«ºï=0ÝôV¬ˆŸT—]Ð^Y±õ&Y¾–ÌýQÁcœã%%äF©2otæŒäP 7¶f…:ti Im¤\Õ¨¢*e2¨D4š¸ m?£O.b¦¤”Ö–+}¶ôt©"?2!ì¨à¦¡Õh5 Ú–»²EM’9HI"¼Û¼„Y”„„êí×4$E­à~98‘ƒ{ÅBpo¥ZÅnWCý‡óÑIõÚÆœìÆÁËEKVéüËËÊ×Dk ”x]SÃ=ª?rÿvJŽN /º²¥óÞ¶]T„fÀ]زþÒ«%•4×:w!Š? |‰…\#™ F“Ñ’¢7é+Њ…•Ò¦R)ù*M`2)ˆÊ¤¢‹T RÙlŒ,%{O‘¥sgžD÷£Ñ*‘˜#>’Ñ”÷¤¾á‡[üà÷—© ÇœäÀ*n6/ë_¡e°¸2\ÚT^¶¸ µÖTÞ²È\f÷Û -f;q·ò¦„=­­•Ù.'ÊèJåйU\v¯œÐ”0–Ê_ÎdÌÙ~®’ð¼ˆÌù.líÈkP¨XR£‰¿±Ž%5ùAǢŅ"?ø&9j¾ äXTUbu柅Tß…íy]ô¡h•oð1¿¨DˆS&:öb–èT8óÊ«¥´Ùç¾`žüyÖkbÁ~=ŒtÌ!ëNÒÆ†Äóºš2¸ˆ`&HšPÛ˜ÊÂLX ¥„ä jM“.й¯·%ÃnTs­6“®Å`Ô`¸.“ªä\äñ…Ì/ ÙÝ>ŸþÑ¿°œ®µÑ9žX¬'»­»·èË™}?¸(gIKÎì>n+ø\qt‘÷ï$}¸FG|%—Å!nrµÔ²B ú1ý³úÓz.ªï×Oê9½>Õ˜zw*K…¾Ô‰Ô©ï¤Î¥òR*¸¤‰§YP ÀuGy‡ÑÕ$CÔðšáŒ70qÒ¤û"æZ\= qË‘ #Q^6OÉ»+,ëµúYØ"{,Š%}01Ï<ð«#ê£úÜgî Ö»ýÅÖ´ q–'%íuÃëñ•¼¾8§î×uê§ìÞ¥Cw¶ãºFæ~Ï=É=‚¼Û$Y",:m²f¯.A“'ë.JºF“—Aš¼QŒº.qíqq«\à:9÷š$hS",{[|F#²P@Ú5Æ$ e¢1c1±,ð½É¼+•Ha+¬á„y›wIäô ŸÏÊã8ƒ*¢kB .™ƒßM$äô’Ïnw¿[­D¶,Òç…wgŸ’(ï—ýåôJ\k!ù¡dq‚3Ï‘ÿ[Ç_ôûŽŸ:h‡'ç>”¶¸Ó"*lu\ë Ã+´mïÿ+H_þ[ö!/Xlrê ŠÇXp{po æ?cƒ‡lp™m¿ŽØ`(&W>gË·å§d7¯J…ºTHM-ÚQ|I1aÐ]hÍs*„üMОakŒÌq"Ã-ŽÊù¬¿SU–bÉN]Ãfv™1ëb[ndÒÎncrP2ý²8L½¬‰Ð‹éèjÀŒ%÷‰˜çÔ8¬êSJ­Q,Íó#ÔŸº\*ÕYìõq~ßOž›QÏ‘o¼ï-ô§êM>ˆÒjðå—ûã°ßŒß¾µ¦Ñ¥ÖkT\Pe7V]óJ¸ sfd #Ýj¿ÖN×Ú¡Ô~æ6 QWü™™ \zJJÀ¹š·öi'Œ§ï9‚0£V!ù£þ~?ç÷§õ¡%üü® Qý˜+ÂT^V Ky&ÓuÎ_&{~XØŽ¨…0ê }êàÏo^yùÛPòäÞ7»ƒK—Äú÷üök‚koݰdëºNodiMhzçë…׿|é Pþ“)¯-þÖcЦÄë;²¥Î–p¾É,ºŒ¨'ñFîzäe9!]ªÌƒ& XÖäÈsSnŽ-77ÇC´Šr ÿEáú0ý#å(Í5SžVR¥2žšz"÷Ý\z,îÈ’kÊ¥¹‰F)µÑLZJ¹f(Мœ|ß*Ç’´ ÓÖ¤ý2íOi|Zš#5·E mÔ–”ÈÛ¦]çI†cíǼ$È\·ú<_ä{¼°- !A5.§yþGŸ7!Åñ™ª±±éwŽO´fM®ÊÀªæZWð[“Ü#³™pËâÙk›:Œ¢0Í­!1œJ¾ „Áh°¹2ŸÝ¤㟌Œ0y*ž{ŸoBúz I:dÃJ œ&€Ž* U­Q7ñ Ï+²1ÑNÞ¨Ð!áõ:›^¯ËÇôià) Ìhvih¿Ú4P¯, PhàC oÒw)Fòp…lÚH©“‚ŠÂ~ýSzÚ®ŸÑÓ:=`æ¤Õãs©^O5ï¼Ãô°‰šLii'çvJ¶¢Å‘¡4ˆ¦AfZiZ}gJƒ4æ tb^¤? h4YÏr¤FžâánþO æ+d½†jÄÆ·¨ ˆÀ8ÉRJ´n¨“Œ…Í}›SxŒt 'CèãÿÞ…¶1ØD¾»›˜ËóJn33Uáýœ¬Ûa+„¿È|nWÝåñçQ| ”­¸ô¥tO‚éY…6ºaö«²ž×ÐîÙ{á+Õ«Ø<Ú.Ç6Uɽn+yVZUn† oÐjLrãÝÃ:ÚȸÄÓÂZ¨ÔB.rR»U-£5é%=õêA¯·ŸFë%éM…ÙÂ5qÀYû๠ûàÖÖƒVΪo1Í “N¡²Ú-Z¾E…Æ£c&æ"0“ªE2†åôJÞX-ée¡1°âLª‡ütëÝm Éäƒ9•HœÅ0oàçØÞx`·ŠGÖ¨!S½*þó¿¬U¿ÿó›—<À¶d?[w{‚AGüw³;‚ðÎþø#‰¸Ù‰¶þ´‡•$&m™ ƒT“!h ‚©B !#¸6H'  =€®†²á6ìwÀvT[WZéA3Ô˜¡Ç°A=(¾XŸ{±]Þ¬ÓÛõöJoZZTæêݪ¾röŠŒåVŸ›¤Czº[Af!ñ-oIÊlò–ä—7$Ïß9;ҹ䦤r~SòüÓ¥]>ÊÎY¹L•Ë/ uìè E‡×G‹–ntãà]õÅKyqjyóØ2±¸edl¤¥xéÕ/îºò•:˜~ÖðYó.ØX_•Ê+jÛ66_¼ou¡Ýïawfö²žÒ’5e‹¤Ö Mƒ_¯ çcnµãÊç1vö“Ř“kÇ‚(€Ë  v}‰3RÄô¬‹EEÕÛÃâLQ6mG‰ÓA»Êt :*› ‡Žêò¢^ÁTÖj51RêZ4ždö‘ŒÅdÁèe!uÉüÉË51†0žÛzTÛËaf³B¦˜baWä=P(x(8¬Ž×©·¿µN kÇÝònxVãÐå7¶ÚB¡|­%,°T®ÖäÙ=…U¢»vÙ²tª÷úR5qg(äT¼á ÞÚ¹úܦ8§T¬:E¨uãöÕ,õ´e†Å„¼ížû•ª¯!6Î#}-½ß"EDA] Ài´êôº%oS*ù\d€F QÑ`3 ÓSzIR3õ`’÷‚ 'rt+¥\=G³88@KUe„KŒPo„r#ˆF°ÓsÖê9%éU˜l&ª3™“ Þ>èsœà° ³Kj›Ú) S@“ã÷ '„…w¿A€a—@ûh@À&d ¥÷‘gØ`¬Z/´ IUŠÅ!!Ñ[)ëèZÁ—°æ›FU±¶'×DV'¿1'yMèEâ”.>qR ìYíGp攊‚$D…~á!&<'¨Œ‚W *hTJ“Ž(û )—sÐÂ|”ÊIžôÈ4£Ùý‡znéÑh% dØ_ÆV£žKQð5§ À[· Ð,@¹Jþý=¾'Àì`D€N\rÃ-rµQà·Ü&Àµl•q*Ùá(C;ñ€Œ¤˜GzSîIyÄ[Øò¦0$÷)—‰n;ùy &À}Ü0š ~½¢Ô$óhg‘|û`Mo/þŸó>}‰óíó/tUkÚ“ù’¡òÎ-óo5½èÚ0@G$³œ»B¨„áô% L°kÞ|Þݨ’k0ì?ßn>ÿ{!Ðé=/Ðíe§ ÅEH:A4ðè­ÌÔ«”/Ì>R{c|ÍÒ0È}CsÏup´ðkpïýاQüéoá®þìJ>…¸ÖÅÝöÙ0wË,Wú˜ÅG¨snr‡ä¸L½_M¿é„½ÎÛœ99§Ã¡ÔjSS™ ¬t-ŽØX­Ð£R]^u¹H¿M–5A0ÚB¶Z[ŸmÂvÐv·MuÚöŽmÎÆÙ¤ÌœˆÍ¦´ökóŒª> Ê>…eaç™uy_ŒKØNtx¶$á嘓c[Eò>Ùy¯pþŠBÎïã|a´çð‹ßÒÎ>¯=òúƒZ…XY§ôö¬isÄÿ2ÏTÕDjÐ:ÌþuþÈ$þìÏJf_?:Âömò‹Ü‹ÄF–’[¥‘æ%P¹†C[B´=¤ÐTHÓØó:t +¶è¡ YjçßR %Ü«„íʽJ:Ša]©üJŒ•&/ë/‡òrSÄ[½ªØWÅ]4Ûd'­n!i§åc&–Ù˜$&Ä`þ¼r^Vz³äìùÜáä—^8ó&Žá*àHFS}sÜ™•ï‰î¬°çKàµ5tÕ4dæ._wå¡–ŒÎžßƒÖæÉ;GÊG.j°[‚™™Ó­Ë×-ËÒŒºrKœt½R«ä.¾õ{c%µ™‹„%•¹Å]—-ï¾ÿжÎJgTV·ñ¶þƒÝ¥{÷JA y \¶á›çþÄß1Cì<"dŠ¥"]‡Y”FÜ[Ý׺¹Rw½›í}(•ÌìÉɘOrµ6ÑRd¡K(mµ6˜Û—ŸŸáì3Nؘ@q6›Q‘Ñdzdj> É'oò òþZbÏ!¡¼ „ZHª*¾TÉ[FP<»äÒ{WîûéMì<ÅǯúÙpxWstÃï>2R9rð¢òµÎÚo°Óñ=ÎaÅu/_ù ”¿9.¦ÆÏš?Hyé¦å|MÜ›xÿl%ÆëÏËçEd—dk AWöh6 d&Ë•Eó¶£}FÒ M³Fúr!— HÖrsKH‰©¤¨D*‰–ðEš.ÌZÄHT@`^×ÍÞÝñ‚×u šÀÞÜI¦'ìD£$q ´ùì¼~éµEÂTÍ¿/ØÜ…⯨¡`9§Ñéø¬U­­Ù‹‡—çg7_ys[Öª¶Ö¬GÒZKÚöVô^è­«[š.¿»Ód —…-ÙK‹<çBKMtœÎ5·½42y™=»$ù·Y¤É)´GKпe”‡ÃaNY$Ñ\7¬·]jÛgãºPæ(™H[vM’P©Jç˜zÁpñp_›˜U§·,®oJØÓ™»w×´áÐë—¦ŽN¤W/’Ïb3–5·•ôÝ4Tzì[ “-ùm7¾dÓš=¸Ÿ6¸ìú¼U—¶^¾S«nÕð…nX:>‘<¡Õ˜ôʼ—}õi·¶hÅPÍúÃË™N>=÷¾²u2 ¥«{CTf4gБô­éצsjà”#ÜVîZŽãøÞ¬-ä+„®%@ëH+¡ÅD*â` ^ÎŒ}—ÚOÕ¢68 Ôàà‰¥Ú8±_—0ª7\"¿7‘xß0y,Ћ&¾o^º0Ref^>Ûµ&ë ‡¹¼õ˜^Ñ?`ZuàÕï}¤Tü††:Àó‘©èä‘ IŸxñ³w.~ôÚÖgžpWÙ¸<ð–'ì¬^[ŸU=tuSÂ&]‹6÷}\¿ƒ4K£˜))6{Õh]ZªÕé¸Zú¥ÔHt“˜¡él\¿ž³Ó>’´¦ìý„°¼ÏA {_‹ÅEa4šó<–c°`'¦ýÙéðÒ5õEViÙ"Eá¢z­ÿú-ŧ_×¢­ü§T)^‡ªøK¯«tŸnð°8ެ„[h? ŽäJÎ"*ÑIL¢Ð“€)Ÿ(Sp´ ÇÓËŽi­e>ûJjƒ[n“ÑêÊ{|+z”bÒ@öK)CKf–ìZ‰":ê4f:|ìœÔéqÒåf0²CÓzÚN‡(‡Ny鸠÷û™åè Á/øMd|‹~š‚ã&I㈘xkîtIIzõ´G•>’8±—µ£ráÜ>ñ¶S,%NHÑäÉgÉl›GÎÙ³›àè×!‘äÓùøÈú…:÷^~SoiçÚêËž¹ê…§ß,u”O>Ha;… º(i JQlQóf3˜”JöÆF­||N¾ú’ÈñC%æÊÊÄ¡‘9œ Ì(Nè#Â*N~¿ |ô?*ŽTÌ~ÓF?8|øÂò•Z3{?¼¥ñAé%Ü­ŸM]Mïv׋YÙ³nYÞñc¼ø/¯ü~mŸ±úcâMüFëå'~5ÿ³$¶ƒŒû\«ši6Iè Qùâ 䢅_/ ¡DÒ÷I=ß9÷gZI¦¸¤VAÈrüÔ`}»siÄņSû=–ÛÞƒõzlü´à§9Ùo‰â7s7ðß—û7#žï#ܹßa{*ßÉÆŸëP¹±\Œ°*lwb}ò(Ù}?fcbÿÍ8~«Ã“„ÁV"Î,ŸVL“kéQV'ÆQ¼ È}ä…÷i½™¸'¹'’bŸâ?ø\þNÌê•?Rõ«~¤žTÿZs¡æEí”öŒ®K÷‘^­ÿeʨÁ`8lê1ý›ùj‹ÚRnyÀ*Y`›´}h_bïÔ¨L÷iD}LxuÖzP’^WÜ0Öš É_1²&É)ömÂ${)Éx²Ì‘ 2“,+ÐvÝ’,óDOM–•D §’e¹”œN–ÕÄÍɲ† 7YÖá6,ü’²v'Ë)d|3Y6-e3Vh°ö4ÂDˆÈy’eJô\U²Ì‘z®)YVBn²Ì'w*YV’îõdYE>âf“e5ÉUü9YÖ ß]ɲŽ,âË’e=¹˜ß”,§8ÿJ²l Êï/›˜Ü>5¶ntF|H,)*ª[‡‡ÄÈÀLØ<>X(.ݸQ”›§Å©áéá©­ÃC…âÊæº†Ö¥íÍ-ŠcÓâ€83504¼i`jƒ81òùþ+ÇÖO ÌŒMŒ‹mÃSc#Í3Ç—N O‰Añ‹bã€;‡§¦¬¸°¨üŠŒüRÇÿÍÔp=ëÆ¦g†§86.v¶ŠÑ™áñq`|Hl_èØ22268,‡§fybfg¿~ËÔØôÐØ {ÚtᢖMLMN$g63¼uX¼``ffxzb|tffrq(´mÛ¶Â$ò âNl ý³¶™í“ÃCÃÓcëÆqù…£3›6vL³õÌŒâÏ[ñÈN~zbdfÛÀÔ0[ÿô–µë‡gÄ™ Ä‘>ÃãØu`ÝÔðð&¶Ò-òŒ·Ž ŽŠÛ'¶ˆƒƒÃ“3H†þF.üg“ݸÐIž)YF&È$ÙN¦ÈYGFQ!Eò~J0T/"Xj%Ãh(E!ØZ€¥fTáARˆ¥¥d#þ‰çõž–kÃxÆûV¹/Ã\‰½êÐy·bŸv,· :&ãàg±w˜lÂûÙ€° 2òOŸ¿û¯•ŸÃZÆ[ÛdÈömFèÎo ±—⓱e\~ÆâåYýóÄÏñßÃî”áÓ xÅ8ã"RþwG97Fðÿà‰ÿwTKðg<ÊŒ‹S9 )7Yð¦ÿB¾³&ÏkÄLJÒÀS–z¿ý蜼Ñ>•™Ûx*šé­ý<‰ž=Æc}Ç&ŽÝ}L1ùØÎÇnxŒ+zLzìµÇÎ<öácÊGçi0“0‘0=î©Æ¤Â<®Ãx/„ŽÀÁ#0wúLÙy„{È2ç}›ó[:îÄžw´U{ï[^-?Ù~/³ñî{ŽÝCoÇYÞ¶|Î{ ¶< ià yÄ Î'Úò¼µßÆZ 8¤åô«7W{7︙jnŠÖxoÄÏ!¤Ä ×ÕxÈó^ ÆK@•ñ\‡Ù}užw×ÕÕÞÉ«w^M·n©ö~¸f¦ó½Ó2]ì’g2Ï;1ðŽ7å{Ý:O‡+ììP…¹%NöipƒMž‚뉾°W: ¶Ç3sqÎO Šå7ç¼þÆþ>ÉÛÇÆCÀí½îôÆ‹›Dïêž"oOSž×–+®š®Cå#õq;¸cœ²¿m²mg׺*ä]…œ‰~¥ÆoK¨…[Ñö.Gz47U{#M‹¼Æ&oS¨étÓ;M4)ïnBêx:R#ž!lïÀd¶Ã6v`’ÜaÒ2Î©ÑØgÜa䌤–Ðð˜=ßp¼½-XqR5׺"¦‰®ŽÁžXVû–VõÄ”{b¤£gu×q€ë»¿rà©K[+iëŠõ§u¯ˆ aAb…X0¥H]÷ôôL qÁôv#[0=-Cäzà܉ú â²Â–5Ó 8Þ§­ÓópÜ-r+Þ`šäòtr|&, Œ˜k¦×ÈÓ€ùÞrkeš]€çÿöÙ&— endstream endobj 34 0 obj 12378 endobj 35 0 obj <> endobj 36 0 obj <> stream xœ]“ËNÃ@ E÷ùŠYÂ%ã¼@ª"AK¥.xˆÂ¤‰["ÑI4Mý{æÚH,ZIlϱå¤ËÍjã†9}õc·åÙì×{>gß±Ùñap‰%ÓÝOòßÛ)ICîöršù¸qûq±HÒ·ðî4û‹¹ºïÇ_'é‹ïÙî`®>–ÛpÞž§é‹ìf“%MczÞ‡:OíôÜ9•¬›M^óå&¤ü¼_&6$g«*ÝØóij;ö­;p²È²Æ,Öë&a×ÿ{WTš²ÛwŸ­¡6„fYñØ&áêœ+çàBù\*¯Á•ò\+[ð­p-¹w”ïõ®øAc¼T.À+—šÂ¹Ä¬•QÇfʸ˪U‚£?êXõ/$&úã^ýѯþXýk©ýѯUÿZb¢?ú²Ñ³²ê_b&Výë¬þÕXýKä’ú×è…Ô¿D<©»(Î_ž«!¬þRÿRê¨?IŒúW˜©.÷ª‰IýKÌŠÔŸ0Rÿ½Sœ!‹7+…ÿYUÓ½k*†ì'6spüûíLã„,ù}¼nÖé endstream endobj 37 0 obj <> endobj 38 0 obj <> stream xœåXl×}ÿ¾;RT,[¤Y‘KÛ÷˜³k’HEnÛ“­ERRe[?(f¤íÄ<‘'‘®D²¼“ ¢  êÐQ“®Y[Ôm]¶8h|”Vβ6EÚ¢ØR¤EED²nEã­(â`hkqß÷x”eÙq±lÿí¨w÷ýùù~ß÷ûÞñQF~Vƒ0"(‰5×@àõ©Oœ4è““¯=€ô2€àŸÌMÍìÜõ³ÿ°ípا¦ç&…7^>PÓŽ>Ñ”¦&ŸÞTÕŠüÇ‘ßBÁå•Ï:ù©ã‘i¶!xP=M¨íÀ\kþoU3ê#¹²¿."ÿ;äiFÑŽÄþð(ÀFš9—ÕŸi Ádú\^ËýÃuç‘ÿ!€øQ”àé㌀TqþÿùeÍþmCG;7Ô Yƒ”¹SÎÉ΄ÂÎྡྷ՛XXSlªIsd4 ¸=žXGû Y+¸ ú8¤YÕg:8$M³Ôá,-¶¿RxjÉñ¶I9©‹š¢Š¾1X(|¬k3[å€Ùzú­&œ¹f¶Ë ÙÆP‡ÆVã ÝIL{³K¦…k€Ó‘¯¾}³Dµ$UÍ®kÀÈ–·PÉ4TˆÔ¥Òü„L]r¡¸qc!Ä ÃH½–J/u›¡§b¦+ž"û¬É†Æ†Ì»GFM¡9DS*Jð¯Göìq{êb›‘÷RË5õxØÄÏ.)0Œ9?-ó&Ü‹ øÚb¦gšW*šÍ¦™¯hVÝã2vs(-˜¶æÁ¤ÄŸUÍù \O'X+d—Yû®Û#êëè^_ŒÛRÌj0™¦¦½Ë‚^kp¥0—‚‹3µï–WÝ ¥®žî•†áå`Üú;™jBÚÑn´•[?5•Šjõ(Xìô¡‡Ç¥¼}¦OΙ ²µŸ,­`:å.–›ÙÐgBh[ÂÊž:7ßýl?Ë!71îh¾Ÿ EEa{9ŶmALäp´›[ãäQ÷i«†Èи¿£_fþ¢LÎŒr&|$zÏcôÌxtQ B_Ü+î@]ô Åï .˜” CÃÆ©æöî+ À<×Ú¸€ó‰%\V]‘H, e™«"Pf+Ë.cv©)…5Æ÷w&Yþ:–*ÄclC#VÿˆIäXù@‘UÍ ²æ7kd?“÷0yOY^Åä\¤‘t´Ÿ.¸‚òµ¦ö)@oI{OËð øº¶mW»ŠUöŸw/Š’P™ØÎÄ‹Žªíê^$L¾«ÎS×ì©óº²ƒ|n%eüáù€íP>¥ç±œ¹Ö}ÜÙ} ¤òyéûÓ——×~W[‘«1ò…~Ž+‡¡oÕ$¹î„'oC@ØËéòQ›ðyT0pŽàiâ!û3ˆÍ´[Ƀ«ç懡r†&àDŽX^5xæ*Ó"´À#m7|É¢íx¿bÑU°OgeÚ§á—] $jÑwA-™¶èÌáôêIÞK>cÑ›à1ò‹®…6¡el» ¹—…ŒEØ.þ…E Ð(X´Ň-ڻŠm‡&qÙ¢« Kü/‹vÀ;¶ N5ì´o·è»`«=dÑ5°Ç>aÑá˜ýó½ VìïZt-8|˜¦u<y5©Í¨ùÐìäÍþÓZ^5ÒÙ «ÝŸNÒACN'zõ„–IjyÚA×Ñ5VwP=¨åu&¿ßÛ¹û†³ê`V·øÿ™LqzSiÝÐò(LghÄöÒÕÐ2U3I:¾ê8<9™Nh\˜Ðò†ŠÆY#…91›OëÉt‚EÓ½«óëËæsY+9C;©ÑCªahz6“2ŒÜ>ŸïÔ©S^Õ2N ­7‘ñÝIgÌå´¤¦§§2Xoʘ™Žè›‘Â9®™ñd“׳“Æ)5¯±ùë³'´„A,Újë£eÐUÊkÚ ›é,ÏøT*Hѹì,U -g`E˜ù{!{ï”ìôªÏßYü1yHãé?PxGtâç¤Æ@Ãw…PQÛŽÔ d ^¤za?t·Î9 Ÿ>Or_fy½üD´^Gz÷Pôbö*­U´Õðm¡"ý”eaòŽñ¢ÿÃ4i´Ï 6Œ\qýÈOsÏAÔªH§Ñ«5 ôÉðXyÔvðìî„Dßëýy=È=ôUûûq&°û¶h¬ŽU¬?ÿWÓr÷¦8ŠÁ±Ë–iŽA‹0·ឬŽ–áVã·‰8Œ'y¾ÚËÇfs)#g‘NY9¿²ó<ƒ$÷«ÌMÇÈ·ö­à<®áìºÊ±ìNò˜‡¸Üà«’éRœËÁ>ü>óÁ)þñ¢ÍÍÈ ×Ë©´|¿~£Æû>…¶å5àå˜3ØÏß1•þ°Z”ûxûOò'«¼Î= ÌD彪ô_ÇN`%5^?†˜µp™Í´ÕŒUÅœ˜7ë[¥§³kj|Šç“À;ŹdQÇ|#Ç+›\ƒþ?ÍÙû¾+;}›H7j*”ÞÁqüdýÿ2Øõ-!<øøý"±)]ä‡×É·®×u’ý#QþH毸J”ß’_¿E¥}k¿ô/Ë›¥_-ï—ÞÜÿ‹È/÷‹‘_,‘{»%_o ¹A\x§8bér"mÙú¹X’¾ÿjIê|•|³¿^zi8)]‰—¤¥Ë’k‰¸7¡ß ®é‹Ê‹bürîòüeѹx|Q(ŠÝÒiPJ/tIæ…’ä»Ôsiø’øô%¢\ji I}{.ž¿hs^$ÊÅÚÆÐùÈ×Ðìù ’.Œ´H_:wŸôÅsÍÒpÀ¹ø9aþÜïÎ ç‡J’óYéYÁù¤ô¤ðégZ¤¿ùT‹ôôS-Ò'q8¤áøBvá±…Ò‚MY¸ûžó)òD—tþcäñ™’4s9‰à³8 :ß\ÏÜðœøÑ¬$åúR¶»ôÒÙ²«)âØ%FªÐåïfHë ™FJE‡øñ.é8>ý[¥-¿™èDýæPëÄÞ‰Á ñá~·ôÐÑ’tìè.é(n Jo}äî®úˆˆ[—ÉŠÄ)öˆÂgbäx˜(áí!%¼ý^¼ÝÝÝ)o•FplnbÃéaa‰4^´f_"›á ²YÙ/Œ&_>dzåx¨¿A:ˆ†p|¸_’Jƒd§1€,ô“Ʈ͑:⌸ºœ@„vŽÔ-º·àÃ¥ìǧ [(¸ÜÔÝéιm’³ÇyÜù˜ÓætúœÃάóiç›Î’ÓQ–þ§Ó–rÈ|#±“%òLq<ÜÖ6´ä( ™Ž‘£&9c6‡Ù]=bV1!räh´HÈ'cO,,€ÛÙŽšñm±!3‰„ˆy$\ÛŠàé†nÌêF[ù"Hm€”¡ëm±NtÐg¹žëô¶²M¸ …ºå¯·jg--àzƒÑœE0DÓ¹Âàh³£6§|¡‘Þ„{î¿°}: endstream endobj 39 0 obj 3384 endobj 40 0 obj <> endobj 41 0 obj <> stream xœ]AOÄ …ïüŠ9î6О›&fÍ&=è«?€Â´’ØLé¡ÿÞ)VM<@òxïƒ7èk÷ØQÈú…£ë1ÃÈ3.qe‡0àHU5øàò¡Êîf›”¶ß–ŒsGcl¥_Å[2opzðqÀ³ÒwöÈ&8½_{ÑýšÒ'ÎHŒj[ð8Ê=O6=Ûu¡.;äí"È_àmKuÑÕw=.É:dKªÆ˜šÛ­UHþŸwÃè>,K²’¤1µ)Ùãt§ö±~Ú€[™¥I™½TØ„¿ß“bÚ©²¾}Ymu endstream endobj 42 0 obj <> endobj 43 0 obj <> stream xœÝ{k`SÇ™è|sΑt$Ë’lëØÆÆ’,¿e[ò‘m0¶‘0¶lbÀobŒllƒÍÖmB Á„BÞIÓ$l›>’´A&$˜&7!Û$ÛÝ„@¶i²Ù´…mhÓ6¥a{“>–ï7G4íÞ»÷þº’Ï9ßÌ|óÍ7ß|¯™#Ç7’8²‹pÄÛ¿©/ðùÏ „¼I$ôo Z÷®>;ás„ÐÖÖmÊsð{BøBBÔºÛÖþå±Bt¥„ä ö tÄåRòu¤QÎ*v„oUcù,g m ÞäKyô–ÿŒåÐÆÑþ¾?¾úΫ„ÈßÅr÷¦¾›Ùêdž7^Ä:Ò·iðg7µ?ƒå,B4Mщà{¤`–Ê kŒŽÿtË÷Âýë¿ì‡ Š•)Ç *µFÔêâôñ£)!1É,%§¤ÎIKŸ›a±Ú2íYÙ9¹yùòÿåGxSx“ìv3٦ܯûð HÙJÈìïXéê=|ãÿ[.4ÊR!›|F>¾¦áòcò"§¯Å†\Èg« ä<ù”¼þ·¨"= ,UÀ³ämòyîoàQò$̃TÔóã±:ùzŸ§°n39—aØÈa0*­%H;ø/¡U ³ärw9GîƒZrN˜àR±áßèkäëÜnzмÀ-úÂtž †‡@ ÷"¿€r©<>~‚¬&GèOHùò]+˜àIB¼u]ím­-ÍMË—-m¼aIC½¯®vqÍ"¯gauU傊ùóÊËJ\Îâ¢Â¼Üœì,{¦Í’’d2âõ:­¨Q«ž£@ ëì¾^k(§7ÄçØŠXÙÞ‡}×Tô†¬Xå»'díUЬ×czsí0½LïL0Z«HUQ¡µÎn ªµ[§¡»¥áµö.kè‚/S`>G)è±`³ak]ÊP­5½ÖºoËÐd]o-Ò›ÒiÛj‹ É”V‡ ¡Pž=0y Ah^Ý‚)J4z6lˆË®ë5·tÖÕ¦Ùl]E…KBñöZ¥‰,VH†T‹Cj…¤u˜±Nö[§ ONÞ5m$kzqö¾U!®ûNru““{C&G(ß^Êß~>g>*´×Ö…Œjcë•q¯ !!Ûh·N~Fp:ö ¿»¾¦/Z£Ê6~FèCñNNúìVßdïdßôì®5v«Ñ>97¨C “æNì5=ûƒýi!ß]]!cï,ˆNÖ×ÚJlYÙ¢Ù>ëPÖàŸÇn›Ÿf3uÅpšÿV3AA 8P¦6›øþi/Yƒ…Ю–ÎHÙJÖ¤%^§£+D{YËÉX‹¹ƒµìе\éÞkÇÕllëœ ñÙKìu(ãý}¡]kPŸÖ³¥°CñL³Ù'LÖ g—‚kE®– [CBŠ{]Û5…u™4*…ø?FÒp€S‚µÂŽd:{]oôoËP °†‘¥oï ykðöEרnÊåÄ}½¸DõÊò…œö@(É^se=[uÃmJ—h·PÒâéíö 9ëjÙÈÖºÉÞÚ Œ–½¥óqÏž›*µ¦=ë&¥¤«–!K‹Q¯rê&;Ö†,½ihik­i¶· ¸ËÞ9ØÅ %”‡³)#†èâöÎÆ6{cKwçü(#‘FŽÏ®û{gZ„ ª\H“­±vÒ4® Xaõ!`¯©Â{H­ÁˈWj™ªÖTY;!İ‘P¾µn°6ŠÇÊט:-nˆQS±"ÒYÜfë²E>E…›­Ñ±‡† µ!ÖÄe£'À:Šd”*&˦óÖNû ½Ë>d y›;Ùܘx)G…¡È<ºVíו®Š‰Ø°9V` ùi× 7T¯”¯¾Ð¼$ÖlÔØÛ&q{” AΗ„Saï|SšbýÌží¾>4b´hÅž'§¼^fËCÌl'íK&ímU 6ziÛÙX ¤ÛkŠ Ñ™ÕLÙáΖ)/ÜÙÖÝy£¥õÎöΣèâÞš®©,l뺖?Å’ ’Œ9Ù)ÌÓÔÔå½E”×ñ0ºïàwðûùGx¡Rǧñ+ù üÓü ¼ªR©~ƒÿ€Ò§RiÙmó—x‘‡î7èôcÊ£ £t%}„òûé ô ÊQoAiO“(ÕÑC"ðbPÜ#rOˆÇÄOENœž=éýù4lwˆ´]„J†‘%Ò7/ŠðŽx^¤/ˆ°G¼O|Bä6ˆCHéâÇ" ‰'Eš%¶‹Hõ Q¸$Â9Öžá>VŠP+Bšè)/Â×.1RÇÄwDJEЉ€Ô½b3kÎKEî<òDOŠÀ¸£ûE°!DãjIœá ‰‡Y¯R±Gß# Ÿ²ÚDzLìd|®d¬±A>/‰ôŒ¯ŠðˆÂ‘NLé<ì²CÜ/>"z+_ß…Qb¥Ø(rsCJùŠêV'˜°hã?L8çeRxB3’Twà'yç¶]‡ÏîÕÔnî(ñÔ$ÝXÁMÍõ øn» ×#uö÷ôna>Ú{»×MóD-YçO‚¤$m¼Ss‚ÐÄù¹QŽã¼9… ‡9àôB“J«ÒhTS›ÆÛirQÁœ=ޱ(Œss;K\!3§Ìd/sÏs›Ýf»)Ib31Çw:j G)ÄQžçøÒG6ÞüàÕÜÅ-þù];–ÙÿahuîtJýWôòE®Á~Ãâ2}\™×+ÝØ6±¸{^Š¢½-,GÓÈâ™#Š,oA]…;í4ÜMM™5ž)ªñ„t§¦ÔOÈè†Pº3¤z7$ž ‰Æá§S)i²J8J0†’O…’ ˜ûîT²Á”Âõ˜Ò™H®©MS$­¢ÂU’­²gæâzÛLF·Œ;0fjɨ®NÈ)ã<þsze ¤žôx UþŒÊï}|ÓðKôÈÌ?„?\Ñ9Ik/OOÅ•Y½V ùg¾ùÕ‘<¯IaË­šžýµ¢¯›ADiPs­N¥ô·œ´èKÔpj^Ëè8HDñtl}uZ}ƒN§W å¦g/zç6qs”»¨‡szhÒ?®§ Ör<¯ œ²òwEª3Ù ¸ä)Ξùn· ¿¨(¡Â”ì6î=y’]TsRÃÍIt„üØt`Á–Æîœûr8ü¸+¼œý Jðyî ßDT~ ÖÌüyæ­ˆÎÓ0êü\²ú1Dglfœ§‹q f³åËãzÆN‹ßrÄÂÅÍõÁnQÉž^="’Ôæ¸£ÚÀ¦àñ¸/ÄdÏcž©ªûõä˜zš‹Áž©2›^£‡æ‚ŠæÊÔ}Ú¼œáÍ\•}y}…>®ÒWg®ìYdU«Ä§¾3sùDôíð]ôí,iò–ƒà£/«  ‚³*P‰J =,†Dz‹½"XDøD„]"‹ÏgD^0ðfÒ„9Db3¬±1™ÙU"XB*óÓüÌéÓ—8~Á¥×#òB=i"vS,јB|çìíÔž}(ûp6 (PöÅlá“lfñgô  L‹)úTU3³÷ˆ¹cÌtÿuLùÛÆÍ¬š[zM0¹*Âh¨ùÛf1X&Ã*´Ñ]WÊ0³Û÷|IЧ¸»‹Þl‘,SñÆ¥ˆ:²´\Öù2OæŸÉ§ùù™¾—}Ðä©^ ù攆ääªzÁ«76óZµÚtErJ·H%^’L-éÆ\O“ìTb çéì1E‹@Ž =cÆ7•è$£ä™Ç"’Êbn+;æÉ”ð„>³gæäÚã!QÏ™™£_HË`ßò+Š¿Þùs^eŽ}^^ªþ Î;ö­ÑÁÇF¨íé֌Լ¼¢ŒõƒZÕü#ÿr¨¨¥:«¾²¼³:3ÉѶ}yïm-ÙÀÏ«l’ÍñöÊ¢øúÍ+œrÿ!xKNU¾YõF"~hp0@ELí5Ë‹׸#yFTë¼.ð½L!@á,î¯Ð,ÐÃBH ·Ð+€E€OØ…'…3Èìå: Ä•wGtOxóóR%þï ßÈä[H)'='H&j]:jgÅ[výirqææCQºÉw&¢ºÆžÞÔµ„¼¦t«Q2˜Òä&­€+á¾p­‘ÊcÆŸ) çLWì(ÿ\;jSºœkuN(ÍQ´QY……”?X²öÑ .ÿŠ%sT€úþó?8Ê»n^÷@Ÿ3üA`´ mQ~Þ¢Ö‚òöŠ š¹ãÌIEKÊ…¼²ÊÔpÿÛ¶[³Ôy¥óÍVµßwjû‰gí7­?Ðnw¬üªGö"TãÞ@G–yÝÒ}ýª¾Yß«è¹]úCzêÒ{õ!NÕ\7á]¼}1O{ÕjQàx’ñ¨LÝVÇŒkŒÙ¸ÍtõËK3_ ¿¹t-^«fžvÏœ¦%ŸßÂÆo@± ½W’‡N´h6c4.\TвÏRàË·0qûR­ –|K¾ÖìSÖ¥ IT7Wƒ«:TM½Õà¬O+›gKÖ¿@ ,ÎZPe4¶FUGu:e‰”„ ï=øL¨¨À¬­'âÆXÑét//(seÑæepŠ%0WQÌŲºdu1Ç-®ZrÇ[Ý|lbÝ·w¶›ÿW°°­ÌÕVYÒ1±¨vÏ·*øôhçC7µÿ¢Î*ó ä7®«n¼{¬ª–ß|£3£n¤%»h~†V—V’]PbI6 F;–më(²Õ,Ÿ“ëÎй«² çš FGã–+¹ë½ècÑcfy“’êIvov ›Î©×&7Œ–&AIëXVŠjˆŠÍ&ƾ¢tª+:woÝä[ûöüË>ßâ;ßš¼ç­ÛªÃÿþ•›nÞc÷v—/ì«É¤7¿u[ë}§wn;õ@{ûý§¶¿òLèå¾»ºŽî»ØzÑ–†0Î3~N5dyBq§¦0+ÌJ’œ!ó»d*!NI;0ËH´JÍR2 “Ér€6|9áö–ý_\ZÖáãÎO·CþrK.x­µKÿ#M‚¹`­‹w²ù·³<}Có²Þ‹í;k›íÓ6/0œõ'ãÏÄSW<Ä[HASJ¢±¤I%Å/ ™â¦ºcL6Š©¢ââÊ¢ç»VDÉîÒœR%J’’!3ŒL^ÜR* ”Ù¾öþ>'ÚëÆ²5íµ)RU4üaÞ¢6Gy[ÅÜŸ´×äkòKËÍÃ+Û8µmÇ[÷·KÅ nm®\ž û/Y›vdRû†m¶‚î»úDzVÜÙ›àü„…Pž¹dë ’3û£gµdY&ÍÏ¢eXÙS«<ÿÍ»+ÒS°$±[ÒÊ—Éi$ïÊ÷æ÷æòwåÎWYó/æÓŒ•'µàÒ‚AkÑRmªßÀÛýBbTMÐ Ẍy,‡bTÅÛ3³rP" åYn™—„˜¢Š#|¸f:ü§ïL…ÿt´«ç9¿÷=Ÿ[þqÙÐÃë*-úÚàÆ¯ºè?}+ü‡“C›~q ´/ ®}1ü§'FŸÝUW·ëÙÑ‘ã{nh¼ý8zzæ—Ö£_JÀ 7 x%£=q¥_?ª§z½=½›X \h×Ê.-ÝÞbﶪ\*ª2fú)àOM9˜Â}’))öd¿š³ûU Ê14¢™_@{Çåvà>M©Á[Ô2Ð_±H˜¤Rg i°ry"›,ƒpžëWÜÿú茇޾ó‡wÔÝ~çÓÏ†ïØ¶5‚Öc­…ËÖU…'a÷‚%pφ‡ûŠ„ÝŽÎÛ»oúÖܤ#ðsø›7†ÅùK‹“ÂÍÌ^*q}U¸¾ÕdÓ ,üÚ[ñdYAw¾ÜÍ|E›ØM<ºn¶\MZ¿vT+he¡ÈŸ…n7ú·æÒq.Wñn‰ŠwSÜ[Ô·õ\P&Šn-æÕ®8µlæÔÊ#>M¸¢ñK#«›œ!*ß½¿|ì®wö›an\q˶<î]?ÙXuÓ¦Õu¹í÷¿µ}òµÛ—%„!íýÊòuÕsäî5·nYÛè€z TËkî^ít.¯°¬ì[pƒËjˆÏ(XÐ1¾lø!Ч+w媴âêÌÒÅEc¼¥ êÆ›"ºÏt` u ޤ’Î$eöœ’=§°ýB ÆfƒõÜ0eeHÁ¢~%I;”®4oMê&¨^§Q%âò'Å–_ S=OHbÊÖÎ7ªÕ0) =¶~úÒÝ3¯Cø Hx}$üÞ⛾3øöç]Žzè¡ðgÇV »[Ÿ ~lGh¬ürýÂÏGì5¼œïÆxæ&ulž_ê;ë'óGÎú@=¨|‚°°þdú™têJ‡ôCES–͸¨)1Ù,¶• ÕÝ#;®uNnç5þ "‹J$ªÜš]ç­p[ŠÏy¶x0³³‘Ä+fª$q´}Ã7'êôjèo–Û*m´djûºû×8åunÜø´ }™@AKùÚÛ^Ëó¶”·-È`IFM^x¹cÙ¯mÑ é‹GZÒ¤¤Ï^Ýr÷?oÛùÆ=M«Ö™ç¹óÔY·¶_þ-G‚ ¸Ÿ¯½³5³ óÎÕGŽÚÚ&QÏ5˜×¯Ç¸ "dŠÇýª jgHƒ!Aàc!lì‹ù¦ÃÀX8Þ ùr7^z{Ãì焨ªŠ#É”é¨E·{JgðL‹ÜHPï Å¿*Ÿsb¡þb«6Åñ!rrJÓôw''^ùíÅ-ÚºxJÔhBš“ñSk '9r”pšââbxž½ßWkÄâÈ'äC\%Èv}À6€ø=ö6ž>|>üÞ¿¿þ5hÈ,Wyù5®òÒëÜøå¯"‡˜Wbl:¨Ä¦RÌ®’b©dy $m‘ïL$ESÉ$%•Ä}^’’Jš‰ÉüwSÉ«y$;ÿ²-%8a$UY*YÍ${W4Ìa?] áO8R1FË$‡è+°qÌÑ^“—çmwlRÛŽÓ¶ãö§Ü\îÎÓØw·ñ? ¯±VÏÉH ô¶Ýÿæ¶é£™,¼«=s*’Cò¡ŠÄãÍá8A£ø# BÔ'ó~'å/iš£ œb¢ãÅ9)Ž¸Çø–©Â©7š•œÑ !úöŒ3ü>oà /=?sIØ}>væ¸eª#_õf¥˜p£v– ZÐj@ :˜ƒi¼<@x‘èŒÊ!%«ÓézPs¾3<ðQ±ó±}8¿ökbjUkÔjA |tïXá¬`‡ŽžØ’í¹ç»Ýø4U`àPÎUm V’]ål &|vÛÚ­PÞÃoÛÃë…7/?'ÃÝ3£¾Íˆ2‹Gß¶ü‰›ý³×Éú0WZoZ mWoIƒ$u·UãÒPÊïDuñÆÛ’’Ô‰~‘SQ†c1¯‹cJ$¢1 š±ú+ÿx{øá÷é¶½¯ßê ¿wÛmáÓPQã÷dp?\ùä­ËÂÈТ-O m ÏË\x£²?`ùå<Ì/­dž7Ãè3Ò|Mè·2¥z!±Ik4jÞ4HKK‰bÜQ‚f›l“ýW' ‘¸¢b¹“rJ[w¿²ËëÛóÊŽáonYþe\oçØÐÏš7êa޶~Û÷’šï9uóÞß½t~ßž¥ñmý?˜ Oè÷ {7;:ÜgPÒÈ'ˆwmsYxxy.ìš‹,ù `0¤‚ªpßîÂÝ¢¢ ÆÄšØ‡ZÔ”* ’Ê,0\ÞÙ3¦ˆ¥h/sÇ\«9ç‚Û ï‡ýØc ÛWºz²ŠsÝsã~ÂÝ|ywóKc ‡—jUÏñ‚ÑZbíx)¶ÏR)û¬Vo™CETs¢V9ªôê9A춪…ÆïF…ƒÂYaVp++"ïNô“kÒ—kl#ú˜²¡ef£\ûøöË?¦gŒÜ 4™ðcçÃÎãø,<ˆyF>égg¯V"iæ4ªÝ<¥­š3GX¥r¸½ÒeÜ9©Ñê7§¥™y«à¨Uè ç„‹~ˆ_—Yî±èz÷`XEýSVÜáÈF}cë{mÒ¨¬>óJ\ZÛ¶®ÊDËÀ§ï:þôHGÇ÷@}tòôƒýéáËñ ºnZºákýNçÀ£ï8—ö–ô-½'Pw5k\0|pE}Q›77–52ïÀ9Þ€2N%Þ²ÄîsI“hfÍhP³(jEã:œv2WwTð‰jVE#.*QÅéýDÉÝl2㮜)úËÜk$Ñ-‹¼t ?ÞóÓÇ» ·°5Xß°íFä®þîGIžÛÏâÆ.»kw[nþŠ=+¹æËÝýÎþÅ‘óïfx˜vҘݞ£„ü^$Lñœ‘±fª‡?ý[¡Í•*kUMZÈ 7Å`^8JJGÍY £¿eÔBͳE«œÚrœ ¤­a”å…TëÅ’6˰xsEÅ2çæ9êe›…Ô˜â\`™ z ã[Žˆâ°ä僦Šë·…óÊ®înÍ,À˜38øB¾Ÿø…2_ZuóK·ÝúòÎ*WóмÊÕ5öê-G&v<œo¯ñW-ܰ¼0übBA}y©¯ Á×PQµ$'ΖX²¢¦ª¥Älv·{<­®D8°òáÑ… Gê^¶u…[äõUíëæ·O®)›×?Ù6o]û‚8^[Ö±•Î/¨wÏë®/Èn¨ÈÊZÐ03íh®ÎÎ^ØRTÖéÉ´{»HìlB¨ö¦ô ÀôØ+„]Êéšé2”Cä’Ò†Ç ˜”rvRr×MˆÝWKÈIr†œ#üEÌJÏ!Aà˜A*éÙ8f”ŽÕÊ‹´ñÈÙÒ>Èv³ã äÁ2û;î#ä!™¸¼sŒFÁÐêJ ¤RU·`ô›8Ÿ%}‰Ñ¤/¶MW‚¡pMzÎ^Â)j·þÛÛ›’ [›å®Éí\þˆzn›J*ì}x“£²Å¼e„~øÑåê ßß¶HÉGÝèwîfï~Iý…·)‘bäoåôÜj^Hâ1ÛK¢‚V¢'«Õª$µJÏ«½‰RƒZ¬¦:µôOÒ{}H‚;$PKÉR®t£´U”¾#—>’xIy›û/§4üˆá~$qì­äEúÐ?a›Ç%ø¶Jp“õ¬ø‘Dï”’è:¤HçK,J‚‡þ(Á;Òy‰¾&ÁóÜ+}S¢{$”6K´]‚ÅdI¥5+ÈŸJðK†ÏI¯Iô îWÆ‚],µI´T‚l $ ¨ÿóK o–`­H¹î*e^‚¡_JŸI)#ö³ÃÜ#APÚ#Ñ54K K5ÍRh{ßCê%ø…¯JïHô¨ÈÊ! ne= UZ#ÑZ ÊÙ`Tx9/}*Ñw>|C:*Ñû$Ø"Ý!ц¥R­Ds$HR&9ÿ û%8¦HqCG¦šO¢ aÔêa)Äè¥û$®™Ñ`Ô¸=X<&½ŠóÔ²žŒì¤ ñ'ù3<Ç«ú“Rõ U×í%Tô`„Ä=LÏêÿ•7Íãþñ«Ÿ«o—¯y}ÍëhöñÿbÏ—#þÕ+íXC©0¾‹©b¯c¯ãUãÉ“FòÊ^!%Z(qÙ8ü²¬˜ËÇkã7íœùh'æ<VQ2sO\²I 5%Çí‡ûa(ü³Tî{}eårÔÛîì=E»1‘tð6åm*¹„•:b2L5™<º&Üqg2ÀšáÊ8œÁ%sÝVÞÅSÞHýþäÑä[’$óÉ^ÉÖœL$ÿ±Fó¶È)nGߓٻ(e·Žñg`ŠD ûáJÇ·„O„¿Ó9¹çµ=‹œmã‹agøî½;ÃwÀ­5þjŒ±3¿vWnz¤¯é¶µuÆ™÷öºp¯¥b9ú"Œ9êý8'ýö bÃM´Iyíª¼ts*÷„Ø;9;{UšÑ™+ïçØ1“U)“|›Um4¤¤¨ó9“ÓeT¹$Õ¹\r‚‰uÝf:a¢›Þ7Ñý&MKL4Ë´A† ¹K¦Reø ¯Ë°D–÷ÉOÊ|¥ ¢œ/ÓÓ¢ 7î—a› kY3`Ckƒ×/ÉðÏòodú‚ ¬ ÝÁè0ŠÃ2ÿ¾ü™¾!ÃÃò ™b÷2ÀJùÀCnN®J­Ø2Ø30N$—Ï[ÈÍKÄ)T–ð7^Í¢NÔé­N^ÂÎýàæèâ´†xU¼^/<7UÅÅÅ xéuéñOƒsf¯uÍ«¬œŸâ.)L¸ü¶°ûòEïÍ%¥7ø2¼ +’éïgR*=ž _ýü*÷ö ÎH®{o’ˆÞ!‹¤ˆ¦¢3­: f¯¹ÙÜkææ]fj5»Ì‡Í'ͼ–ZÅ#šÔ«"[:ûåC’bM›ÒÎ$rš˜çe/!Ї±+²}HÉ üürr‹•½?÷áÌמŸC e¨øêœμ˯»/ý  8w̹1+aÿˆUY¿|ý)¿¡ê3b‰üŽþG‹ý€{öóðrôo*ç64ö#uô ÃËÉâ«?[ÿÂï¾ éïH-ÿ!IÆë ?AšéS$U=—ìÅò-XŽç¦Øö!ÖM*ãEXYø'²ënƧŸíª ÒÎê…¤RiÇ:lÓ¨*f?Wh|¨ô9ˆíû¢ãÙŒ¸¬ß,3Z‹p ÖnÁ:7££>@({2ž¡^¤Và$n„û-ïãoå-Ü%¼(¼¨Z Ú®ž£^£þšF«¹Có¹x§ø íˆöí¯u«t÷èþwCÜCq¿×ëë/Æóñßÿ­¡ÀøºÉ•ðnbiT:…¤•ý_‰(Fâ$˜SòÛUÿÀH¬K‡Wd¸úŠ<°Ñ^<Ù…9b!cQ˜'IäPHy" ³L2…Õd;ùaÖ$¨ŽÂ"‰‡–(¬CüWþ»¥vDa=¹žŒÂñÄAóǼˆ¥i[’Á¢0n\¹¢(Ì‘…\YæI7…’Â=…U$‡{6 «É§ÜûQXCòøSQX$éügQXGæ ¦(GV K¢°ž„…oEáx²BõÍÅ£mãÃ놂Ö'­²Ë5ÏÚ:8`mè Z—Œô[mÜhUš'¬ãƒƒã[Š­K—ÔÔµ.j_Ò´Ü:‰—Œ{A™‡P+$øl }ØZˆÐ2BúI1B‹ÈFüZ¯é=¡”ñ9ˆÏ-J_†¹{Õ:¤¶ˆ´#ÜDر۰‚߇W±ûw]B°n”¬ý»ã/Åþk”qXË0â`k–Fn –7bÏE÷#ÖˆB}1Š~þ^_ë•Þÿ»x+œ‰+%ÈŸ‹”iÿXï¢ÿb”ÿ;éDÖaB%¨ÐŽ`+´;£MÁjVz2ù•ÑF¬ö/± G\‹ý™4¯bö+´ƒXŽPEx(*éõd³¢ˆÉúÅæ6#ÿõº0]Gmý‚´w[”1—)õAE¿XÛR ~œd«ò-Fœë)÷Gé+Ð&Äüïö ¢¥9*k½q#ë^¬ÐÜ„«Ù¡è~l}˜,"ëøåk¼Vy2ÉO(=‚ÈIŸ²V±õŸ@®AI*òcG£tÎÆèzŒDGíCžXo¶n±5Ý|Œ·*üôãÝŠsÅ6Ö§_¡P$;p õÿSž‹ÿÛ’Ýø%#]•i4Ï ³¹ä=ò%Ÿ—I3¨1h;•ûà½õpf^žã Œ^ï%ØõÙ¡ÏÆýçÅ2‹óâã©ÿp~âÿdô“Ç?9û‰ð«óVË/ÏW[~q.×òçª-g«Öñój®ãgÓq´Êâ\¤ƒ ö¼[ñòâÅÍž„ o^jºï§Ü¬…|ÿÎWYÞù×tËÿ5ÇÒûö¡·O¾Í±Gso³£·gßNëÃç±·µzŸa$¯^~)Çâ}!‘ÏûBf®ol^ûóÕ2 ÓǵrÈqëqïñÞãã{:~æøÅãÂ4X½úÄ{®÷9zø¹3Ï)çÿñÏéâ}†£þ£tŠ‹ðœJÃ]à½KgðöYöÑ;n7Xü·Cù­¾[édb3^A¼&ðÊ@Z¸|€w¿ Сt€mʃêèHƒeÄ'[æ@JGª;¥Cíæ:T¸:}Ø·×/[üø\ÝÝ`Yå˵¬ì¾ÉÒí+±$Ê p¼ÌuŒr`à<õ··-¯ÐçmËÈÄ[bНµ%ÏÒÒ”niÆ+µ)¿‰v5 7ÑiHðæû²-K|©–ŸÍR“þ³…’lî0¡Ã(:( ³–i0MñaôVãÓÈÞåÓØ±} ·<¿áo08 M†QÃAÃYìA©ýÄÀ–Íî’@€i84ÕÞæp4N«g[Cêæ•!¸3”ÝÆîÞ–îêÎéè^Ù9ðÕ®Û 5sCr[g¨wnWch/v!`œ;%‘š®‰àDpóD0º5…Db›Y-«Ší^A©ž˜ƒ$ÒeÂ1AŽàf¥ H&¢½':£ývÇòfGP!Å'‚ ÇÁ è`D©dd”Ž0‘‚¶þ¿/Í•ë endstream endobj 44 0 obj 11004 endobj 45 0 obj <> endobj 46 0 obj <> stream xœ]“Mo‚@†ïüŠ=Úƒ]¨‰!QÔÄC?RÛ€0Z’ºþûîÌKÛ¤ɳ»ï ›1®ö›½ëÆøÕ÷ÍFuê\ëéÚß|CêHçÎEÚ¨¶kÆi%ÏæRQj÷ëH—½;õËe¿…³ëèïj¶jû#=Dñ‹oÉwî¬fÕ!¬·aø¢ ¹Q%QYª–N¡ÏS=<׊¥j¾oÃq7Þç¡ä/ð~HYk¨4}KסnÈ×îLÑ2IJµÜíʈ\ûï̦(9žšÏÚ‡¨Ñ$ÉlØçÌ)8c¶Â…d2ìo˜sìK¦Kí#Ø0/_0¯À[æ5X3WàŠy#læ-úï„-÷× öSføç;føü.=ù³³†¿• ü-»iøgÒþVòðÏ fø93ü ÙŸü¥ü­dàŸ¯™áŸ±¿†Îï2ðÏ9oàoù ü­dàŸÉ>ü ß¹Ê÷`àoù{Íä¿b†¿å»5ðÏØÓÀ?c»•!™¦Ç…çùg Usó>Œ  ½ÌO]çè÷1ôWÉï|ÿÏÄ endstream endobj 47 0 obj <> endobj 48 0 obj <> stream xœÔ¼{|TÕ¹0¼žµöž™=×=·=™™Lf&³'—™„ ™\ ™lB.‚2@À2`@‘„›¢µDå" /´"ri%õp¬Uz H­Ö¶¦ç §þ¬•ž¢­G[i½½¥êÛWÌð­µgÂE=¿ïŸïŸo³×}¯õ¬çyÖs[³aÝÆåÈŒFAÊ-k–…/ý%„úBà¸eÓ†ÐåV…æá,BXZ1tëš²ä»"Dþž¿õöÍ+¾Úôø0B&Ú%óÞÊåKeÓ‡•­~Ô­¤f7ëºÝEóòÊ5îê+ø÷0Í7Ð1§ß¾ö–¥oüzýs­Ù@ëG×,½kèÝëBw°ö¡;–®Y.þPòÑ| B†ú¡µë7 "ù2B›ngõCë–ý½÷1Í?DçÇÆú}Ì4©cyL8^§7F“Ùbµ‰v‡Óå–<^Ÿ¿0P …‹#r´¤´¬<¯¨œ’¨šZ¬©­«ohœÖ4½9Õ¢ÌhÙÖÞѩΚÝ5ç†Ñÿÿ>üÃÈT¾ÙÐö}݇C^ö¼ü×뿳s.úÿå, ¹Çô$:‰Fï þ|EJ£Uh#-¹öóô ZÊ>iÔ‹žF»þ‡a¡h}®]íAÿ‡viôzýûuoI£5è:—ï¡w`*z¢ÊZô1Ð}è:êÇ´ì†/ [é× -¹âšÒwÑa¼ÍÆçhæ «Á ,¢Sè°„޼®óá++žþ…Aw {é÷´m¢iíÃ7öŸH¸üßtU÷¢Ùè~4Ý~MÂb¤û׎P˜þD+KLVêUr~ã‰GhækèVú)еã‡É ÔÆÛá$BJûâž…Ý æÏKϽñ†9]³g©ím3[g(-©æéMÓêëj§V%¦TV”•–DåHq8Xಋ6«Åd zÏ ¨¢=Ò‘ •dƸ’ˆªV²|d)-XzMAf,D‹:®o3ÊhÍB×·ThËŸk©äZ*WZ‚šŽ¦WV„Ú#¡±7Ú"¡ w^M?ÜY;¯¥oÐÒ\‰–±ÐL8L{„Ú V¶…Æ jëØ´rW{¦ŽwÜdœ™¹ÜXYŽM4i¢©±²ÈÐq(K–ÀeíÓŽcd°°×Ž‘hûÒÁ±ô¼žö68¼¸²bÖ˜5Ò¦U¡™Úcº™czmÈÐ*6u´;t¼b|×C/ˆhY&nŒ .½¹gŒ,¥}w‘ö]»vŒÙãcå‘¶±ò»ÏЕ/«ˆ´µÅÙ¨]󯼧ëê+aŒŠ‘Ю¿#ºœÈù¿^_²4_¢‹ŠG,ÙAÁ»kWG$Ô±+³ké —G–EBbd×q³y×P;…0J÷Ð^/\þÁnÿXÇC‹ÇÄÌJ˜–_lÇü®1ç¼¾ž1í­\JKè_K$ÜàÛO¶IÿOÕˆ‚‚ƒÂ4f ßý‚‚–ÑÌØÈ¼ž\>„–ùO %_<†3¬f|²Æ½ÕŒLÖ\鞉ÐÝìZгkŒ‹ÎŒ´Sï^:6²ŒâÓml+"â˜õ8²Ëa5&kmCtV³W…Æø ÚëÚSX—]¢–±~’{œ÷ӔءƆÓiÏäÿ6­, „*+ÆÔxnë»{Æ”6šP–æ÷¨ýxU‚öXš¡[´ªMÛ¾±DdhÌi½²ŸlZí«ôh]òÝÆ\3ÇPæ–|¯±D{{s¨}W¦-76Vd^Ï‹(yùìñšÿ¹$ªA‹ÛXci&Å«’ö]=ƒ+Æ‚ÿ ¥´¡xLYL7xq¤gùb†hBågéëÂÚÇðÌîž®‘®y½= ù‰ä*Øp\´ýsÃDzü¹a(Ê¢†Pö“Å´¡H B4iN¿ÇôQý/R€k¥ U[§‡zÀ&[ÓiŒ•‡Ú—·åÛ±üuƒò fª“£éX–Ž3Sõ‡‡sŸÊ L«CùÓTu²ŠD)' e˜£1X0œõD–GGV†Æ”t[å<04˜ç÷ªûºÜ5À¢`BaZ=™aÀëˆû¯îX§–¿’U?W=k²:´ËéZ°‹ ɈèÌg!†ÂJƒÝ¯Q?£çHÇRJÄ”¢5zÞu\Q-¯dd»+2kpWdAÏt­5å ÷úïfïr .èên­¬ Ì¬õxœw\ôö¼(R‘êÁîžðÌLëâã2­ëy1DÏ ­³RVÈ2!–a#ͧƒÖÞÿ¢‚ЈVËiZþ–ie†É2@·¼€seâd¦e\®LÑÊØ‡îRÁJ cÊ¿ÛCƒl¾²xå®Ìb†ãH¢¡0‘…N$u°Î/Rªª_Ë}%“ç=Õ-ç!Ñ?|¾zIÿ@ÿp}hIþ3ü…â/©¸®K\+_[y}ƒ/vÎ׊g–ôŸ·7&ÎWO­XÒGýqˆÓÖìîïú¤H²Zò°ï"ì¶{ÜáÚz{MI¤ØŠõÒçòù\im: o˜ß-·o „z—”Ôõ´D6g>ÔµÐ×ÞÞâ¶?œmݽpaasS­ãáì¢;ï'Ɉr‘³¦ÑQrµÞÖ5±ß[YéÅ= Œ®nÆÕ¼Î`â&¼,GBÞJÊgü—ÿJ~C5Î: ,ö(”žÍÂt› Ó Ø`ÓuÚL™°Ë  ŠÁñà٠׈‚¡`UP¡i^ f‚CÁ± Ò#´áXP‡:Ǽ°Ç{ċǽ§½ØûÂåqÅe0©^½/-l:2ßæ’Li«›nyKò|5û‰8ûùþa êañ= h d õ8äX(ƒ ƒQ©•DЧàZ R] îjø_¼3û¡!¾©Gfк ŠÀ¼`±MÄòV~vÈ[9G,.tµ®êÂ+¼•uP=íOtÝ~ø±R¼Éµß…yÿ6?¾Íw·ïð>ÐØ Æù6:ÁñÂå³ÏѤ> M-»,X0ƒAÈ×XMã´¬ a_~'ÒYy½Oïr"‹•7»Í~šrêhÚÚî„mNp¾pùÏÊC%åêlvñ@øšvÃ|Ú§Ëìv™ÍnæÓ.VÞeµòîÙ>ðù\t\ ˜ç a# ”¥ƒ*p°¨ ´[G§gç+—i!+:I Ï.Œ¬´&°!°O+Õ×Я3´‚ Ðz.VÙS©±yÔt€Rs@ `ç2+X­zdÍ!31˜n±¦u¿‹3zDâõÄœ&Fº¡á&žFúÌŸŽÃÕ”(ãqmg×ÅÅŸ-鯶;ÙZ™Ü!ÆwÄOí(Ð`4,îÏý7ŒòßS« N? )â쯿õG5²1ö€"…[ïÏ=€l¾#»èÞw²÷eÿe Ôf/¬…§ïýÞéû`þíÙ´º++=pCö8}Šp¾F‰ÂŸýDútgŸf2=ƒ±ƒŸƒœ”:¾¡,@³MÆÃÆgŒäã%#Þj£·Ó䊻p—«ÏuØuÉű\“ë×K®\:Ñ¥46«® tqãÅ ì Nkô1äöÒ2pWV©Ú³À¯=Ñ"ªüçKl.oÚÃh„rBÊ éÒû‡+ß‹3PNœ¡<æ<…ŠËŠ5š šqr••Üc/*“¤Ò"»½¨T’ÊŠìÆoe½£Û νm)muiÞ¶@飛ÊI¿¤òG-ú–"Ï®ÞU¿â~ȧI³¥»¥]ÇSé&š$Ó}s|_ñ=äã0<‚E-*ÌjTÝj4êì@õ¡z¨g˪* «së꟭'•…&S¡³’¥Ã5%m%¸¤$,Ši¾ÆÔf:j"!˜L<•±¨XEåªMº¢¨‰dB÷Æ7¬îl‡SÒ©±`…Y8;‡ŒQ< £*´WY¾*±9uØjßgÇ:;l5í3abƒ@(V­ÕJ5 ê‘jÜHéê¡ê½Õ§«/Tó¹™[ q‰+ìDa1 ŸsLÀHù§¤R¬t'ˆ(Ml“‡…›Æ¡(Œ¨ðθÓ:ŠZ \yQÀI)hòðÖp¬¤´ˆr¬r ÓÛ5Œ;° ØÝØ:»¤ç¡eÉš•ß\•NÒ}…£YåNŸV¨(MÒ·²­Tìpžt¿L^ÉÞa°8Œ­“üc£sjEÝÛ)=œ s7¡ zH™¾Õ¸Ïˆy#ì66`£vs‡9,p°ïÃX‡Á`V)Ð}Ãb¸*œŸ s,§„IS˜-Aš1[=†¡0(áLx$<æ2aЪ¬Ñ)ª¤êìiAô§‰¤‘$ÎÇ hœ2¿¯ù¸Ø^ÖÖÔQ¡×öÑJ7¸ˆò‹&'Þþý™_ÿú½·ÿó¤¯ypÖìLƒ$5dfÏlöÁ»]FÙ¿}øÙÿùï¥WÕׯ:¸tÙ¡Õ«1 žly‡ó£Ô‰ÃÊgs;,šºb*ž¢kS§öL]9õÁ©ÜT6c–à*U³µR¹K.£E¥¬ÈÂÃ"ƒ‰f5Ø@e‰Pm¡ï,®ž¢‹ph¡-V¼ÊféWq´8Z°ƒžÎµŠ/ ÖÖv©q/!Ðäõu÷Ò5}êƒ>önÇâŒ:Òú Ó'ûNõa­ØC·:Ú\´p}[ûŽö‘£´îLß¹>ŽÕ?7CUµgmsîOhOÅI9û•àP_U~¼)6ŸÚ˜š¨*€]¤:ÁÅHZ 4P>T*5jRM3Óé˜83í,Ô6²ùãúoT‹çížFÆËÎWÇû˜¼ÏIýLLŽ3™\üívR>߯õŒS¾G%ñ<;´¢‡ÑÁ¥cä«§Ô®–ÜטõSHm}ÉäÑé©÷Ð6„ÊäaÆs8Ú‘rMÈŠ}¾|Aa Që@:ðHíã­Þ¿$æ­LÉŽD¬ðñÇk–þ¯ÞÂiÉá7‘ÝÅáòŽÎì>wÄkõ4.›ÝûÀ¢òìskú܉9uõ7L•¤ª9ø'žtØ‹¶n˜qïÒ¦Hj~U¸©¾Æ§óÇê‹OÌ~gîæyå:½@ÖÆ÷–¬ÿìŠ#QSë•›b‘–E¸ñÞ--ýӋЦ÷·´ ´ÏÀ—ÿÊÿ˜Ê¶.ø"®ÆtOÊ+ÕÛÄ»ÅC"11‘Ócµ«]|—ñëøûyò‚ÿWžð/\>­l¤mæ¿ÉãÛø»yÜC¥Òxq9ž…cN²–X;¬‹¬œÎ(KŒDÒ—è1åÍ"Ì]6Ñe£R7Ì·Ú¬»Á˜Ï®Ëlq™-f˜oâM:³K§3óœÙj!lu6°1Œ7ÒYém6˜_¸ü‰ÂY졘ì³6˜a¡y¹ÓüSÊbˬ¤tfÉLåob³3щH’¥6©["¢œ'¥‹• MÚ m•öI\•²ÝÒ Í•8$Ac·t޶"ŠxD‚ÓH §Ë+TöTþ ú²CÒˆ4*‘ B˜NÔa…X‘I¤H; 6ÀœÙÆ3aÅ“Ô$ßD2I‘¶Z;n'¥µþIURüy5EêæD"¡¡q?eãýöd2÷—“€Ïì(¸FÎ?¨Œ—c]ý9 òï°D&EÞdNêåe_¹ñϘ}i-¼ü÷ÿ«û÷o‚ä¶ÏîÇ·Oì#w31wb^>ñ¾¦5›e^àFehìEd¡«SfSîjtáìÏÝiAc£1@116;ãGcb8¦©vñ*51é ÅFb{c„U<,Vµq§¤¢`çˆ Hå<.Ÿ–ÏÊ:ƒM—¡ [”ÓÎbwÏ{ç™ÕäkY¼ÌÂtèk´ç%ymÙÉr<.ÈéìL»‘ži¥hRjÐ?¨Ç˃l¼”Ngi9*‡”R>R>Z~ºüB9_®Iq±Ju üÙr¼(°"€êfãN#6¤]6±´xC M ¦¸sž(¨ˆÿóêÄÔ*´¤4q$zE\Ó¤Z{Nª½zlÕ“}êüű»¿{GÍÌ»þyÙ¼©úxtUãŒ[Ú#Es¸sf“§Ñpg޼¸qäÅ;œæì§Oº}‰ÁC«{¿¶¢Ìú¼¼!R\Ð¥Œï¤'‰ Á´>´݃#z¸õ¡—Ðëˆc¹gè™g:E¥u¶Õ-*{*… Mê^`J#¦´iÔ4f7éöÒÄ1åõ­¡™ê9T†EˆO“¼49’‰Ç©àš³PF½º‘k™î2 ×l •3ÒtO:èž©ä½A©Üé‚N09w;±ä/ñc¡À[P^p°€3”¨A“)X* 5R1Zq¡‚T°Íœ­²§â‰MQ£ >HyJG£ºPÚ+êæÙ¥üÉ”Ðv‡¢b~wÄ+"\Ù÷ä6Õ1” °- Ó³8w˺þ¢ÖÖ”Ï3ãÆžÊ߬øùË],kÌ>Ö0¯Ö _·ÇUxÇ1kû­Í¼Á¨k°ù%‹òÕlþäã²%ošßH,ºgΜ{%röaú ¿£kŽ ç_DÊ=c‚¨r0ؘ¤g;M¦LX2EF‹º¢§¢ç¢\ãQfô²µP’GÁ…ñ(UK£0‰î’hžlµF•Œl}#TaÅÌú¢‰ÓÁ³A!JGl>ÑL[n/B®œùîË wàzÒk ü¿Qm[á ÝÝ×Rljz-¥ØÛ†‡Áœ3x]gê¸J¬“:lov^IqÙƒÚ•‚R7T»Z]ø.Üe‚¾Ç¥<Õu4 †ªXzôñ X&9ÁZZ˜EêüÏâT"ž‹8{ ¦ÛëHfÓ÷<•³oïÚþÊŠòÀ+Û‡·¶;áÃîoyôáxwç·ß…Âýüÿùdg÷Œó S!î'|3òÀߔ˼Åm‰ZˆÑà3`Áæ…¬Í;×;àÝâÝã}Ùû¾÷²×p!gF{ÓK†¼`ói=y“V}ä%c^8â…/½ Ú‰ /ü|­÷YÚó#/—f­Þ/¹ì…Ó^xÙ £^h¡Ý·0ðÀ:èËtØË^>ã…¹^¨bà›i­Þµ´Ý³^Nd=ߤ^ör{½£^¼Å ֲŋϲñ&'ˇ´þ«é|ßÔ^µÇ Wgœ+¥ ³õpU^Å‹•A/Ði¿Ï–1æÅ,WåÅMtÎg'»0€ìñ’*–9ë½à%¹‘µ¶!Úš N× 1äñâ`nátà´yÄ‘´{zðl[¯  ¢R£Šœ§ ôÄ1ˆ†A1pƒ¡/¡Ó„FˆJNT†rÖ¦x&Ë—Öa‡Û…­ÐÚuûìJÁÙ¾uxÛ«ŒŒ_ÍÎéŽ?ðè‘owõ“ß¼8£»óÉÿÌþáG?Êž{÷ÛÚyƒ˜^Kç&Òóf"š"h¶¶Ê“pSè¦Q6‚T}ÈÂb¨B©T1RÅŠ½X©ÈÐÌÞŠ±Šñгú–¯à|¦Î÷c ÉKNª÷Ŭé¨ä3ùyÑž¦2šf¯`þ/UO,(K¥«‹39I y=%o—`À·k2Í]Q{Jq6Ú¾´© ¾®Ú»=¹ë+»wB‚JIPyßãoÔÜþÏÃU·dzKàŠ݋¢œ`6Lx †_sS *³cΩµµ‘ø_þzçËÛU“ÃkÓ`2›ÂäO&>*A|E©]Q¶© 0€`ØiÀßààaÌ(Òé¡:tÒÿJ|$>'¡xFKpqM|ˆOQýê\xOÚçv¦%Tš6Š„Bó4½>ÉSïû5ýžCóF.íµB$dÏ‹ISH3äÝ Ñuõv͈YñÀâì–ämO¬M®¯ÅàqhÛýG6mË4M¿-»#¹mKG¤~·ñ¥ÚÍ&=µl *?}Ñ[ o¬Ú»¸Ô#â?„·)®¶P<Í?Âð¬bt^]¹Ž(VLDØŠ–üã’º;5‘¶È`„lœ‰œ‹\ŒpCpÑ¢nZȱ¯ ‘“Z…ÎñGðÏ.Dà”Ö”h}Y=9:Ù7מ%yíƱçT­Û7´¬ùÀaõp6D¶F°V0uçÃê3`ݶFˆ?\.F॰q´¢xÓÂÕ¬Á¾Ñzí]¾RíšlûLä¥Þx¤µtE0+y=BXš-cC„Ÿv)'éñhä[ðm8¡ÔP¤*’ŽŒDöFÆ"g#"1¢ÙñW`±v’œÕn„Yí Ãé ùÒÄks¤…æ{åLÁ9ÛÆ‡ªÏW'! Lòã<÷_Ããš HÓ&›h% cœ‘ÚúÏŠ­Tjc~{‡ýî‰'âó6Ϊì(œZ)–F*|ÆO?}=Ëí&=SK[oûÖš“á{Œ¦àŒÁŽotöI¸²2œãÓ;.ÿlFo!'*U¼h?22ŠÆ F’Œ¶Ã¼åå¤uLÎýœ6;C•^_EÈé Uø¼•!çÍÞŠ°Ó®ð² ­dïñf;È?¨®_L|ù™/ʬúr^*³*”`LéŽcšý“]¥*ŠH’'PT”ÓØE®@ H‚ù…ÂNäòx¨ôk@Q€ rY°«€§H TË‘«H^,¯’7ˤK¯\.7ÊÄ$Ã?>/Éø üù5™ì–a‘ ´^Vþô¿ÕdxQ†gdØ,ï”qŸ¼ZÆÍò 2öËq¿-ÿQþD&ߑᰠËp lx,É@Gýé%γî¯Éø™\ÍNíÅ‚ ÿW:ò;2¼>9þ¦|߸Ü$wÉÄ+ÃÛtlmRøy·ŒV{€v|Wþ@ƯÉp’uÚ/?)“Y2ÔÉà’eëòýèœö+ÛdØ o•ñ"y…Œ± ËpF>'ãçåWe¼“UBZÎȸZn•ñd÷•Zÿò¿Êø¨ _ϱB†n:dpÈÅrµL8.²WýQÆ'åS2~RkºU†ùò2yLjä6‡Ë,ò Ý®ª¯ÊpT>)ãÉ!YK¬µ+a“úî†OØ A{ùVyŸ|T&ëd¸òîjº)lÚ B´LÕ^.3‚_XVÓ2hÒ©–É#ò^yŒ*ì¼Mž+cCÈ\eVÌÄl.Dž+ž´'ã!È#z°ài€)ŽªÀx£@(P ³4Wb±2SåÐXXØà@ *,"žtÈkç™u…Ìq¨YPìždÿ03ÚRZÍGuu¢2 >øRŸ}|ÒeÿyŸýõbÙe¹«åK®o³1æÿ?k¢Ns"Î<š×gvhžI y&È)Ì%1­°®uK’ýÙ¯…Zç­l÷•»á`}¼£©Jòeeàä¾ì'ÀrÛg}óºµ ó:þõŒ§¤}I£Jf¹Y‹Ѭ6š-"û lA¿¤2RóIázŒg ì*D3Ìó®û&ÿÔZa ~‰q9GœÄ‰¨˜Ûñ<ô'úOÅ'òœG_›b^ .¯¹Â–ð¬ úºŠ"E®¶Úš҂©Mk—Ì)¸!Ð]ïð:Þha]£»¼™Í£”ê4,¦Ý 7)âúGôx®mÀ†ç¢„/\þósBÞs~&,eV»Údî2÷™I“©ËÔg"~õôûôDѧõ˜Ó×èñ *ÃZ­›Î¦É¶yÙV¯áYÝ˺7uD§³¬5úª ÄÆDW¿âÏøñ¨?DÓiÿ¸ÿ¬_×,úÇüXôWÑ‚Œÿ´ÿ‚_‡hrÈ¿—–Ó½Ÿi½‹ûUíyÃÂܳ¶Q{*ÎøTÕ†ÄÎŒ‡ÜÄàÖD‡[uKi/2XDâNÛUϓӭƒœéŠÄ¬Ilš6\×|¸š\|ƒF ·“ŽF{2'A_ñsObu<Î\œÂ¤,s=]«&÷À}Ë`ÎÆìEèY‘ݲ(›½g0»åÎÝ0^#þÊJOöÉ=T+†Gwd?¾Î–5›Ê-˨.aB?VêQ¯‹—ù}üQžãø­4A,üeIJ×BÚ,Ý–A -! æ,pÖrÁ‚OZNYÎXˆEXš¹,Æ•›Úf© ë¤uÁUà,.‹l!Mœ¥ÆÒFÙ`Ùªu+s5á¶0WaczŠ„÷89Dv8ÌëwxÀ£ÃáMP aAÿy`whž!º¾œŽª TbúÅŽrÐT<ÍžÄTíI͇ӇɞÏ^_þËg†7m/jªK8"­sYôÞoâé ûŽâ,yô¾M#ûúïP°9G;¼ßÿöü‡î»wûc}“þL~>² 0Zú½ h²1qAi6ÛÔEînliêN3l6Â]:¸“€u-Š ˆÁJ$¥B/W°V1ì5ŒˆÁ¹Þ¤¬ç½hR›éÖ¼”9O}<L9ÓAÉjù‚›²öö'‡™£òÌ{/x›n™5{ Îí®˜=ë–&/>úTö³ã}0Ýp,ÍþSöÙ½¿?Tž"f_o •+åõU¹¶¹‚®„‹¸øˆÓ6àå&!ÚžYjs ò5¾ß'zzfË£Gä¬ôà ÑØŠc÷ßìÖXìVö\ûÅMO^|üà?ÝÛûô<øñÓ½xô¡ßÞtÓèïÚ}ö›ÝÝß<»ûà§Òé§²Ù'²—ž^°àiàAM—?å>¦06£jBçß! »‡øwûwùùÉݶ]¶C6RÊX†j0©\šü0LÑ£¾r…ž—ååI½«7 áP¹,}()&«’J2KêÌÉæªæ½Íxˆ~65Ÿnæ›Ùž©™A5ÑüQ3¶5C-jÑV™æñf~šØœnƈ¶¾ÐLš¢ÝN7ŸmÖMqdFõôàx3 Žð€.!¡"‡PúGwwø|#hå¼ñáœù¡Ÿ™Åi]ü¼£q sËäÂM4[Dì ÚÎ\RÒâsRrÜ:W¡/"ùðŠùq䯝ô¾ùnÿÝå•ûW]˜R_™\rÿ<ù³ÊÕ—?üÃuUå³VL¿éáeµM_}õ¡¡ß.!ÿhêi dùÒÎÁ‰S+f†'~†éÆn¸#ûï¹ >㬖•sk-BÍ‚u7}íÖFÃà”ž/RlBPžpå`Mžß‡&À>c¯oMÀö)àžR;ψWà*À†r'`2YÁÌÜ ØV,ÂbQ‘¹wj3j††³®¼§›qU³B¤¬×M‘Þ]åVÜi7gp¯(ƒî2x 6U@wÅ`ŽV€T;E˜-Þ-b³XQÆùôuuÎÈ€!ôqWÔß„æä¾"ÛjAv 19©ÅF1åõk~£<Þkò„3é)"yÜ×}™'IwÅ@´{FÄÙ¶àæŠ9C³äæ[¾ºí«·4OßðÛo9Ñ5#R1’ž½º£¸ù–-Û¶ÜÒܸþ»SwÝÖ†Uß+ˆ‡åê²iê’•Smé¿qËâ*Ÿ=û—£¡X¨¾+>cQsE¢©ïLÿþÕf—Ï’óß4]þŒßHiDD%è.¥{£6:Ádõ[W[I?YCp#™E0• üô|¥¬¨Ià‘ÊP´(e*ƒ¡²Ñ²³eDß›¦œGdŠ¿%$2`Îø3αÙuy®ÀÀtB9 ͪ¹" ÀŽA#žCWîèüƒ¿Þv²¨cv—¼å{ê'þñm°üøÖî§³Ï6î¸cé1Š OíûÕCm—îÁ˜@×£¿%åöý£Ù饙ÃEŠws`?ÎàåK)÷â«xhã7P™ë(’§‚hr|Á¢>u„Œxhç/ðXá‡øÍI?þ\“¢jL‘Ru Oyñ‹ðCÍ–²ÐìâϧV9é>»`ÿÁƒ¬[)Σ8_†ÐaeM}=±v–¡ •ÑdSø¥¸„o³Üm9d!Æ_I¬„Ô„×§‰ÓÒÓpzÚд±i¦qk‘˵ma!\55S7T3?(Y./\oJ ©E"’d÷–¯×ñÛùý<1ðzÔB>ÎÇx†øš¬&æŽ>f¤Õ•yÆ9+ãÊVŽEnÔN"o½Æ¤1h!}šIî\ëô¡ÑÁuO®©-íXºáöž=mÉZoÇæÞz¾õÐâ×Éû6‡[úšZ·¦ §/#Go=²º>ýílöø}¯ZÓ´Ûÿ¼W°šø¦­¿<­J.ß?xî;ó×u† ½¼ÿh—†«—_ÉÎÚ¹UåÊÏ=!fè½Úxª?wÁX(w®vÉNWÌóÇpöLì\ ¯ŽÝÛ#r ž‰½ÃÏÄ^Š]Š‘}10ÅàçñØêŽ)'¾§Æ”o?­²R,#@[áS±31ìu±úXÓšX[ ³ðVm€5Z³®XŸö’Ã1>¦ô ¨5¬îž{Õ»±bº¦Ñàsâ‡bU±±ØxìtL—ŽebC4Ãå|ù•Uª-‚M—ÙÂé€×¯)çìPÍÒÖå¸ÿMWÎéÈñœÍ-—cä5<ñ³3T+׌Ó_PÍGÓå4äº\,÷NŒêÉ¥zXpª1>wMk…MøÃÕMÓ†äbV?=wÛc™ £áß×~õ!2žíf²ÅºGåè}eÊfž‰c:»d/±wعh!ð~·?ê'Q xÛõªSñÔf‹ô"/¥/‰wÄWÄ7ŹOâðq€ñ8hêžmj:\\Ž×ÄÛâ\#‡\r_üdüTü\übÜ Æ)˜ãJ<ŠŸóÞ¾*ƒbÀæ\4”˶’`É‘RRRä0‰¢‰+ N-*11IšŸÅ=å"ž˜,/þ6žçí“ÒL4G µ" iP@N%â¹ Ÿî=rùd–ÂÂû?¸m¦¿ý'«7~ÿ«3oÜöìÒ©K¶:ÁGt”ÔÜôÌgÿ èfÏ>9µvÆ×Ï?óÝ?íšfq˜à^_]]^jBˆ?@ùµ*‰}×ë©n0_oÐ÷:.§Cï¼Y‹•1ëÜÈM¹³BnuŸu3sÀO•Fƒ¨: ÂÍi+XyƒÛ€©šGø¶Á:ð–pRmÇ9äÄóœ0Ó Qg­;õf‡¦âä¢`˜zÍ4~Í+_¿Â×'ªwhäLÉi2ö\£1sœ„׎-ýì•“ÙúcÇ(/>Á}È”ÄKîùK‡&•ÆÏ‚¹{X—¾A×D•øJ€Z¡]Àµ†vžf™mÁ†B0‘àÍœÎÅVæÂÐ’Sf—Ž%T-:±!$«!úÞ°+Œ/„éÊÂ#á±ðxøl˜w÷Zï¢k§KïôÝäþŒÁ“ }&Šq&^å5:ŠL¡‹_jóx_$£©\kÏçòb.·ƒ­vʆ×eÿžý?L/.]¸#³jÿÒÊܪ?{ÿæïnŸÿ·ß-÷îü™šš%ÛçQƾ…ÜŒ™~Àîཥ<ø¤ð¼€ «Ü%'ÀÛÂ…O(í(óºUI(êò^`ƒ+˜/„„*A'¸<í´'…Sö ¸F6DÔFÚ N ç„‹Ÿ@j„6œ@k‰3€[„ÓÖpP£QÁ†øƒˆ¹G œ^¤"UîQKõý¹ Þ÷âçû'ï ]±ïåî1ÞE?=’$ÂþcôJjv¶•~ñÍÙ ü.«ÅÑÈ~ˆ3üÃHw)A=‡]¸ wcÎŒMLœ`rA£TÿÖNã.ª¢ÀÖL´¼ãϨc¸€…­Â>ºFN`FWûoΪDÎÐãñ|çòÿx[ÍÐ-f d¼~R8CCè+Øx(ßúñïÿD=,<#¼$­4 ]~IxWø@ ûh 団ô pøa7+zIx]¸$º-q¡IÀÏІx+kvÛÖlÕEº„>6–_`-ú„Õ´ãaÚË`à²øˆð¬ð¾ð‘ÀÑí¡E´ŒÎ[N ø}¶MÀ]¦§ù;Fº¿#”Ð40¶Ã9 ê€vPhè9x.¿baÕ.N±‚|ÑKX(²“ꑼw€ŸfåÙuGåŸÚËlNRýu¯"#J)1Ñœ6ã´yÈL”ó•'þïR«=iË  ©¨Q†vl#`'®íh?å}k«§¯ ºà~××]X¬†=ÕGª±«ÚUm-Z»Ý°Ÿžåbgº§;‡:Ç:/trekwZZ1;Òì$²~ÆŒÊúõ¼×íz·yõò^}åzÁä5•›vššxƒÉ›†ûµ¸2v·¥?gb× Yôpÿðy*Ó ÓQÀ¥Ó”d].l½~ \‘¤>oú|~ç꾺ž–â·ÿöÛ#knZ3uÙ¾ÁÌ×o©Þýùàörwuw*5/át&æ¥RÝÕnòÉË\ÊMkgþxüG?ÙölyÙµsïë›ZÕ{ßDdÖðee7Ïš=<§¬lÎ0~»iYgYY粦é™6YnËhºÆ—ÿ !?Ñlô;e6‹«µ·9j®5¯»Ý½Ð‰Ûã 7n-ÜWH2…ÐVØ]ˆÏÂéBè¦Å' OrJ!È…5…x¬ µ•§ÚUT(† IGû-$ZySý4u¼X;ô¦uguX×"$¡Œ«Ê ^oÂ5àZë".—ΙÌfý€pMðÏù+j[î²{"'vQæÀw5ÄyI?3ôÛ“æ^¼&CžÂõIš„§ÞŸøÉ‘cäÃÖP¨oI·ç]ØlnâÞ‰O&E°ìËïp:¿Í>ÁbL²sÈ»äÊwcè¥×dÛm;l#ËË7–caú–IÙKùsX߉¯Fݤ+ s3ZÁ]¶Qï²?hÇsí`/Kƒ$šd98Ï*a}š÷çìòùLÞª"½‘¹#K–­\²å†PöÆ·&^?r >}øGëªk°‹Œ¥7tÉÛ*»ïÎ>“mDæÕ{Ì?°¡Cƒ‡‹ê#/P9À†è/Šª£[jµ‰6Á‹YóÍ¢¹W¯séõ:æÓŠ Ç»8Ž÷‹ †è‘-ŠÜ=h®Ç¤ˆÞårÁ9¸G©êQÙS±Å§¨§]Ttwv] dÂê¨T¯=}E¹6%¢SpAœÅšqØè4ì`»Dlu>èKøÞô]öq6šØã;â{Ö÷¾O7½Å·Ö÷²ï#÷2­Å>eÁ"uÔ[|òÁ€D¯”± æà±8y4@õ?Í[Ê ˆ¯æÃI4ŠçŒ÷Ú_üЦsÅ$ÉêZ8t sŠfÝxcxƪhk‘³#öP^»ù×¾]ƒ3ËDÑzq·Ç÷㜽±Œ gù6*7ZÑ}Šhnj­®]·PGé@Ç\:§âEZÄiqH/ˆœ…]œ™J §¸¼*g4à«a‡°‚Óxâ1 %`…Nçvºý ¨AiÀJC¦a´ál¬^kdK¯ ]Å@Æ ê†íÅP\\ê[Ÿ'¥'„M_ºþA6óÀœWý_bÒ‹³èDñçýÕƒ¯Fɣɋº  ž º"šFsÂ`ЬÓ%ìb%»hõoµ+ÞñÝ;[ì~~YסTkÄ‘H&=ÃóœúLw÷Ž%ÕÙeJOçÖ¡¶GgÛVÂèÊ'†¦-z]~úyÐ=vÙþ´Ó(šõm»N?\R•È|=Û[¸¥ûè×}{{pî¿0nä;¨T8çEšù³Rê©F Œ‹ÆððÿÆŸb€ h)âaz†jX¡ô/\û>ó‹QÍÓ™‹O°°ñ Ç=Tµ„_-sñ½“ç¾îw½}G Ae=¨ÀdW=Y,†«§·Ê¡8Òâðù͹u®†Í÷™¿f&3Í Ì·˜‰™!bÁ¤š1&7g #ãî·|݂͖B ¶è°½€V³‰‡‡Ø Øàf?¡çsîŠ>ûv4öçòD5óÙÄÅ÷˜-ÅAyO’ßñxøZÓ‰ŒúrY²$’uç-(pÞƒ‘cÇ&Ξäî»ôæ G>«õ]Z¦å§¢IÛ÷Š»q´Rq//w X î‚hÁòõ°¸$-`„Ž(¥4!Š%•J%øúª¨6‡yfCãÝFÄØ«‘+) 0àSù$Ï@4ÃYîöŒöý|±ÌäeG¬]ðs~ÞJÖIb%Dw4{ùøÍýÇ?Ù~÷ÚÁ„®µ¨ãÅ 3“µ~å¹u©á¥7ø¢3t±ÛïÞîºù_.=~ ßí¬cö݉'”¯ÿîß=:[,Œº~š}Åìqs¼æ¶ì+ð MŽ£aňörà€ë1í'1Š vÕ‹äÓ26È#•€*¡e¤ro%­«<]IhQ¥v1ÓåQ}ßô>µ6°%°'@™Ò¡Ò‘RRʶ±KùpžœŒ…D°¨{2ñ%? àü’HŸ/HµBxÖÆ}‹\Er‘«½¦°¶´€¨Ÿ“c?;ýåÁ@€–QÞ4F÷w:{n{=Ô0§æzºeñÝʇ`dOž€¡iZ×4<Í¿½j׆ükýxKhOWøC!Ük7¡í~)ªÛ³ß˜a?Ds6Åå JÂ4ÿúÂÂjÅÅ8ŽÇ£ë«õâzžiØFu#cLù_`‚«]S²Ø)›Ó°±è®Y7n¸±T4àƒ³ÝœAϳµ/"7ƒŸÙ]áéÏ®·_º¬žáÏñyò¯š{pD>þD]į Ë§¹çþú‘æ|îOäü‚ÆßÿAäaQ®ñ÷ß{_¥¢ï»,ýüÛÿ©æŠÇŸûÅ[¹Öæ×ßTµ QŹöÏŸÊóÒËêÞλ$|ïEõOòÏóx«x¨ã;xÌâÆ0ÿŸi;åðã*¯OPøøákJëj?ÌõÃBÌvAÚ5˜¥{Q‡—û!æ›æ›í#/yá¥˜í§ bÎiNlý"6Ùü6³N³bíW'Ž›-jÒ Q3¤MPkj7á´˜sà¢Ê”Ëìöë}Tª·˜Ý:½·ò. û7šÖ[a>­ÈðVÏ[õtÌî^ŸÞå£5«÷8±Óé²øuœÞ7à?‹)³; °-ИK…ügtï.³[J`(0àš”„#½‚Z6œœ èÍûôGõXÿÂåÙfW0iñ|¡Ùê$ú¿Îeáˆ(äö~ €8®ÿ…6;PþB·3ž¿ÒÉl.?c[É~©‚Ŧ_÷ãlÿò]=¯|Ðd„µ¿Ëö¹0vh~2;{ü4{Ïc¸üi þIøþ>Šo˜øÕÓÙQÍý†—`…ñì2ÐB¹³Æ‰­Üp%a7Û{´W)§:®Á Ì`0J½}v¨±·Ù±]ûU¼S’û<¢ýÌÝ´æÜÏÜM)©g€ÓPO8ÁÛ›Æg1ÆAÀ¢‰Êמx%;p±»ÂÕ9óû¾ò£ùß·£êóäÏö÷''CÒíáü3Gì:"±÷¾“ùãY¼úÄÎc“±êÃ6-AÓ›qíÄëWîþ^þÏdb+(Ê“&4n0n5’îævq”qÎ"x3ÙIðr3™}b¾Ùd¾еÄdæ{yŽâ$Çiåûm-²Âœ‰ýðZ·‰3™xó ›ñNŒ3˜Ê( Y¼Dl[ĹâñYñ#Q÷¾xY¤B”¢™Ç¸¦Ñÿ§´gnâȲ»gô±õµÇ–mŒ=cll$¬mÀ|…!¶üÁÒ‚íl Y¶¶¤•d8vëÖºº„lÂ9vewÉ&!!û¹-–@á•lðf“û…$\»•*â…Ûܧî _ŽKåƒå{Ý;ÀrIÝž¤ž~ÓïÓ¯_·zº{zÞÀqÊ*(Ö”uÜ* ë%ë5¶hÆv³ŸYîôòØšÇcÅ`öfGa̦ɇ᫅;…¨XÆBœµ•xU‡žSŽ× ÏhºÍ:Ad;d2>Vrkùþhë;ÐJ矧àÛÝ2íosgößÍ÷¹Cït‡•o1Ïœ8_wû6wÍÄ÷gÿÅ={õ)R:‰à‚•é¢ÏÿK½bU Wø¾½^Ò*<öÏÁÆWQ¨¨èÍ^½ßð}Ε°(á›þ­ô¯™äþ§W´ÙÄ\½˜,…¾BRÈ6t–™³µ$û”aq±A^d>e),Ì ±Þ`1X„üAÏ/)l•»–&sÜn(sagõ;oKN¶èÂÝzÝu€òç— å åÔ ¡Œ ’ ¸óË…* ˆ+é[­­éØn=9µösllhÆ´e åggkáÊ@kÓoc_ºÿœ…>ìîÇO¥Ã,ô§ßÆîLÿY‹?‹„VTŽx-†R¤7z­…z“—o0ŽÉãò„|Iž‘5Ügq–É+™Ž™ˆÉS‰‹N!dì1½±Tw*Zçïwí¾ÌŸ¢sÚù~k }Ò‡ÈW³Ë¹w‘ßt›Áèhó5Öo]RµvY6¤-^ô¹:|M÷×)ËÖ,-Ð÷ëKÖð't­(Y¿kÆï7T¬Ú¸ªBÙØ¹v÷ƒ3k‰sµš¨U7ù•gnoÎ39DÃî`)GFªÇªU Õ2ª†É¢¤zUQ}Él@€*¢_Püï_¡˜z>»åÝG¿CS¡–6RRLí”d1ôg”ÁßS|Ž‘ýÛÞIñ:ÚLÉ Š‹)6Qü!ˆ"oÓiJhê½+Þ!ú§ úÇßxw‰¦.¾ëý%}›’)~šb– ÙM‡(i‘© ¯{g¹çè[ô ž£§(9H£dÝGÉ6–®b”''¼ Ç³ôEzŽ £ãŠ9 M½ðS¯âcW=é :G…·(¾@ñ‹Œë?òre\Y ÅsL_ü.½Ê:GÉ1ŠS¥#,G¼‰ú@ ç»æ½ ¢HFÐaÊr ?(}·Zz™:)\£¼ô\õ„ÂÞ(ÅvºŽî¢‚…Ê”¨Å:ÍÌ·£Ãë£Ø@a¸:7_’©ø0Å1ŠÛh/%.Š+(FÔJÉFÊo ®Ùà½DñOPLB¡*"ˆü4F'è½Fuœ´ÊYJãqйjˆb½K¸äÈÏbCÎg+¥œÓs61³4>ûŽz‡6sCÖžydþÎ}4™©ãín°ï|¦VݘeW§7 8û ӮîêºÝ»¶ûécÓG¾¢è~•×¹ÉraõFáÏ£êªKwo½xÅ¿h¬býò¢5mY,/¯«m»|¹¬ùO¨Ýv÷ú5î­%ºò-ám}ž’Åù?KK…'×ô7çšf?ÿƒgè êÃÇEY8Î÷9Ö{\Z…¸`ü#¢…È„è‰!yP9•‚±“ö”¬Ãh{ŒV¯±gŸ®ÿn÷uÞÏ3ç(¼C£,ˆò­áÕ[ ÄÜׇŸa—0þ.,ly(ž>ÓmYÿßHμ‡éï'ÿiþ]Aso¦ëui~‚ØKšˆš|º²ôýè…÷ atçÇG>B[I-ÊÓÖ"»ˆP1„\Æiv¡µ £È ie䪂¸ƒš¿Eâ<Àµ]Ä~8wM§øª|ÀuåGMo™\D22–BZ“ŠïTå> |mš´NL Ç^4-^GjçÞä8Hƒ`¶¢Àýp“ª¦ÇO€oËÅ@—ú³2-c:éJ ½µ¼Òö€®ŒþþêÐB™>àxÆ»BÍ«ð½"š›2®¸RlCµïÿœ¿7j9J‘IÁ%ÖÁwJÓ©¹©}Xû{]§þù¬³Ù-Ùç 6øq‰qÐôfdn5¿gé°öXûdîo¥—óªóÞËoÊ9ÆÖ[PSðIa¤ðdQË¢ÒEïo-~}q]InIS©«ô÷rDÉVž(³•½¼Ä±ä‰òUå{Ë?ªè­x¿òñûÐ}ß]Ú³ìfÕ¡j›=Ïþøò¦åŸ®0®xÞQíxܵÊáµíCÐJ3«VädO]Ѝmc㎅6ѵÐ>0Pv©0QaÆÃ*,͸ k½ ÂZdA'UX‡¾Î©°åaªÂY0iTaèðÀÂÛÞ8¡Â&4‚¬Âfd' ÓXÌ‚³ó¤Y…1R d–ª°€V T…E Ù§ÂT(Va-*~ªÂ:tSø• 롽ü• g¡Åâ´ Ðñ36¢‡4+U؄ҚQ6£íá-ÑØÁx¸ ©üB©q¹V+ÛC½Š7\®4F‚eóà ÂÑ %J„âûC½¥¥±îþí›Û}­J8¡”d<Ð Ä÷)Ѿ;ù[Â{Bñ@2(;BñpßöPÿð` ¾9 EzCqe…r7ÅÝç¡x‚¬t¸V}‰»›ôkÔÝûÉd(‰áˆÒîØáPüd(’T‘^¥mÑ×׆xb0O€8šE÷ÇÉÞpå–p,è¿%EU•’¡ý!e[ ™ %¢‘d2¶Öéȱø™Õ¾¤ rÙI8ÏHŽ< Zt/X;Î5èå|óeK@ÎhÖöâÐú¢wY‰i·Ÿç¹§'y{b¸~Cká2ãDø×4wJªrÊ?–/ ÿŒ·cˆ×q?ÐfêÛÁeAÛjçm}¾~˜-2õxï:îã1³|‚s$A“¯«ùúO€ ÷€%CÜ~LbT•ËhÕúˆ¨¹@'ÆÍêm¾N‡o³ñ®OŽ ”% 8Æä2bܲ½·Iÿ¿êìø£-;xœ¾´©:ž@se àŸ—²z^ÃÌ3ì&~¼€EÏN|m¿;‹•Y<òöSLþs¦J>=sa†øntß8}CpÝÀ–X®[¯û¯÷\]áº6Ûò6¢qÎ×ÖÈWÝÓí¿s¿ßަñzÿtjzbZ`S›Îi½¡~ íï 6Ù:¥L¹¦bS©©KSצf¦ô©×Æ_#¿<ï”-çåóD>ã;3rFè9Ž-ÇåãÄÿlϳdü(¶•: Ï<íŸn(•Ÿ:²T¾vdæw„O˜r껈Gž{’Ä¥RŒ?BNï¿°Ÿ$üUr4b—# Õr‘»°]çÚµÂ_غ§rY}O·Gî¢].¹³¡J–ܹíPVB‹ ó;‚QaL¸ èôÛý¥ò7!\óÏø‰Å'ûœ>þ·@sjŠ5¥š„Æú*ÙÛ°F¶4È Î†w®6ÜhÐv7àcð«?]¡^ðÔW9ë=õ¥eõ‹½Åí6w~»Õmi'µc7jwZæØž†nˈ…9AÌ™µ¿‚Ç_jÛa·7¿¢›ÛÞ<¡÷ïšÀß›¨ÜÁŽžovNh¿7Ú;w=øÆOì|dtÕ•4OÔìxp¢§dgóD/¤°–¼dCu;‰$÷§Žív‡áˆìÃÔ•È$"û<Ù8‘@‰¶3!%ì,™¥0 œ] Ä kçT J$ »þ”gÒ endstream endobj 49 0 obj 21642 endobj 50 0 obj <> endobj 51 0 obj <> stream xœ]ÔÍŽ›0à=OÁrº×2R„”I&RýQ3}NiˆEÞ¾œ{n[©‹Dsm6àd{ØúnN~LCs s|îúv ·á>5!>…K×G™Äm×Ìv¥ÿ͵£dé{|Üæp=ôça½Ž’ŸË½Û<=â§M;œÂ—(ù>µaêúKüôk{\®÷qü ×ÐÏqUU܆ó2Î×züV_C¢½žír»›ÏK—ï1Ä¢×)ÍІÛX7aªûKˆÖiZÅëý¾ŠBßþw¯\±ËéÜ|ÔÓRš-¥iZújÉ¢¹Ø!;¶çÈžy…œk–¹`ý rɇ¼b{†üÂv­ß0 ò+köÈ[æyÇÍol×y÷Ì?K™·ÈôpfæC¦_Ð7£?Ç3ús˜3úE3ýl™ùµžþ¢@¦_t|úKíK¿¨‡þâ™~¯5ô—Gèw˜KèwØ¡ßaíB¿Çþý^ëÍ}ú=œB¿àÙ ýóŠù7Èô{Ÿ~µý9öPè‹þkúž©£?Ç\ÎüØgg~ÌåÌõ:{´¯ù±^g~m§ßk_óc]Ž~§ãØþcOœùÕ@¿Çž8úÌŽ~§íô{¬ÑÛþk¦_°'žþ\³ùñL½½?Øo~¬×›_3ý9œž~ÁZ<ý9ÌÞÞm§_`óô—xÞüZoþ~ÈöÅâ“Æ™ó稈›û4-Ç„Lz>àdèúð÷쇽ô÷J1"^ endstream endobj 52 0 obj <> endobj 53 0 obj <> endobj 54 0 obj <> endobj 1 0 obj <>/Contents 2 0 R>> endobj 4 0 obj <>/Contents 5 0 R>> endobj 7 0 obj <>/Contents 8 0 R>> endobj 10 0 obj <>/Contents 11 0 R>> endobj 13 0 obj <>/Contents 14 0 R>> endobj 16 0 obj <>/Contents 17 0 R>> endobj 19 0 obj <>/Contents 20 0 R>> endobj 55 0 obj <> endobj 56 0 obj < /Dest[1 0 R/XYZ 56.7 773.3 0]/Parent 55 0 R>> endobj 57 0 obj < /Dest[1 0 R/XYZ 56.7 422.9 0]/Parent 56 0 R/Next 58 0 R>> endobj 58 0 obj < /Dest[1 0 R/XYZ 301.2 621.6 0]/Parent 56 0 R/Prev 57 0 R/Next 59 0 R>> endobj 59 0 obj < /Dest[4 0 R/XYZ 56.7 785.3 0]/Parent 56 0 R/Prev 58 0 R/Next 60 0 R>> endobj 60 0 obj < /Dest[7 0 R/XYZ 56.7 109.6 0]/Parent 56 0 R/Prev 59 0 R/Next 61 0 R>> endobj 61 0 obj < /Dest[7 0 R/XYZ 301.2 204.1 0]/Parent 56 0 R/Prev 60 0 R/Next 62 0 R>> endobj 62 0 obj < /Dest[10 0 R/XYZ 56.7 682.8 0]/Parent 56 0 R/Prev 61 0 R/Next 63 0 R>> endobj 63 0 obj < /Dest[10 0 R/XYZ 56.7 224.5 0]/Parent 56 0 R/Prev 62 0 R/Next 69 0 R>> endobj 64 0 obj < /Dest[10 0 R/XYZ 56.7 143 0]/Parent 63 0 R/Next 65 0 R>> endobj 65 0 obj < /Dest[13 0 R/XYZ 56.7 725.9 0]/Parent 63 0 R/Prev 64 0 R/Next 66 0 R>> endobj 66 0 obj < /Dest[13 0 R/XYZ 301.2 609.5 0]/Parent 63 0 R/Prev 65 0 R/Next 67 0 R>> endobj 67 0 obj < /Dest[13 0 R/XYZ 301.2 100 0]/Parent 63 0 R/Prev 66 0 R/Next 68 0 R>> endobj 68 0 obj < /Dest[16 0 R/XYZ 56.7 194.9 0]/Parent 63 0 R/Prev 67 0 R>> endobj 69 0 obj < /Dest[16 0 R/XYZ 301.2 595.7 0]/Parent 56 0 R/Prev 63 0 R/Next 70 0 R>> endobj 70 0 obj < /Dest[19 0 R/XYZ 56.7 625.1 0]/Parent 56 0 R/Prev 69 0 R>> endobj 22 0 obj <> endobj 71 0 obj <> /Outlines 55 0 R /Lang(en-GB) >> endobj 72 0 obj < /Creator /Producer /CreationDate(D:20110212113044Z')>> endobj xref 0 73 0000000000 65535 f 0000106372 00000 n 0000000019 00000 n 0000005115 00000 n 0000106516 00000 n 0000005136 00000 n 0000011826 00000 n 0000106660 00000 n 0000011847 00000 n 0000018239 00000 n 0000106804 00000 n 0000018260 00000 n 0000024570 00000 n 0000106950 00000 n 0000024592 00000 n 0000030431 00000 n 0000107096 00000 n 0000030453 00000 n 0000036429 00000 n 0000107242 00000 n 0000036451 00000 n 0000038043 00000 n 0000109970 00000 n 0000038065 00000 n 0000039305 00000 n 0000039327 00000 n 0000039519 00000 n 0000039810 00000 n 0000039971 00000 n 0000052330 00000 n 0000052353 00000 n 0000052549 00000 n 0000053014 00000 n 0000053332 00000 n 0000065797 00000 n 0000065820 00000 n 0000066027 00000 n 0000066523 00000 n 0000066877 00000 n 0000070347 00000 n 0000070369 00000 n 0000070581 00000 n 0000070872 00000 n 0000071048 00000 n 0000082139 00000 n 0000082162 00000 n 0000082365 00000 n 0000082847 00000 n 0000083186 00000 n 0000104915 00000 n 0000104938 00000 n 0000105135 00000 n 0000105759 00000 n 0000106234 00000 n 0000106317 00000 n 0000107388 00000 n 0000107445 00000 n 0000107674 00000 n 0000107800 00000 n 0000107975 00000 n 0000108145 00000 n 0000108323 00000 n 0000108506 00000 n 0000108661 00000 n 0000108894 00000 n 0000109023 00000 n 0000109202 00000 n 0000109362 00000 n 0000109532 00000 n 0000109695 00000 n 0000109851 00000 n 0000110110 00000 n 0000110270 00000 n trailer < <57CA71FEF10AB74054D6C6503D04283E> ] /DocChecksum /87B0E98CEEA4C4EB32796D38A8155FEF >> startxref 110581 %%EOF tmate-2.4.0/presentations/tmux_linuxtag_2011.odp000066400000000000000000000355641356407164200216310ustar00rootroot00000000000000PKMk®>3&¬¨//mimetypeapplication/vnd.oasis.opendocument.presentationPKMk®>Configurations2/statusbar/PKMk®>'Configurations2/accelerator/current.xmlPKPKMk®>Configurations2/floater/PKMk®>Configurations2/popupmenu/PKMk®>Configurations2/progressbar/PKMk®>Configurations2/toolpanel/PKMk®>Configurations2/menubar/PKMk®>Configurations2/toolbar/PKMk®>Configurations2/images/Bitmaps/PKMk®> content.xmlí][wÜ6’~ß_élr”=}—Úºø’ã(qâ3vììLöiM¢»‘Õê<Í?˜‡}Øÿ·¿d« ›}Q_(Å‘%úøH"q+€úP…BñÙ7×qÄ®„6R%Ͻv·ÁD¨P&ãç^µNß¼ø·gj4’8 UÅ"±­@%~3(˜3—ú¼‘éäLq#ÍYÂcaÎlp¦R‘ä¥ÎʹϨ-÷ÆØY´sqÊ\.mŵݵ0æ](ˇ»·L™Ë¥Cͧ»ƼÀÔrñ‘Úµðµ‰Z#\SnåבL.Ÿ7&Ö¦gÎt:mOÛJ;½ÓÓÓ¥E¾4Óå ƒŽˆ6f:½v¯“ç…å»Ò‡yË$%Y<zgÖpËWF5ÕÂ@è.NÌÝ**—Y˜_Wãg×Õø6®wžg”yqª†»O•ð\6ævrÃøžtÞB"ýxûf>¯t¼k[˜wU–éÎÝt¹Ëå•R©XÀ-v"·ßíuÜs)÷tcö©–VèRö`cö€GAÁq¯cäëu GK\á”/2ÂÜP ßqÉEfÞXõ¯oß\óyf¹=sK&ÆòdÎËhçQ€¼7LZžÈ§æ]Y:'Ãt´H•¶Åvh¥_ðhbãèf†©yÖ±õYœÃˆ3&­+)¦_4ÐióÄ<]š˜$ê·¡LÅ¢“"ÊWx‘׳@\§BKì=p·b[¥g¥Ò‹Ø ãëݪÃɬÂÑrK ;0æÐ®ãÛ‡Ÿ;˜ÖBháï[*m `”ˆégVóÄ`½^(ûº`dM›F¼…¤R¥®ÀX:&² ¯¯ÃQ/òÝ„“¦S¼à™UXyÐ"80/ž9X ŸÌý,xÞÓ^ÿq˜þ3xå¶•ò±hä%Ë/[)pGh+…a H1äÁåX«, aâI;«3Ú›²©áo"°fcöPš4â3Àme 7d!êrÌñÈÜ”&·hYç-ë:%mâWÿîø•S3<ÜÒ³{Ñù±^™,cÍÓ‰ Š~ûçr—]¼°0ÔÏFþ¾G“éj“å>åi)׸›¦‡–+ùñ,‚wÙÐJ‰M$âhèˆPúG Ø÷ňþ5\ .§ÌØ)Œ”Ol> #uˤxÙ{Ò>>> â}z·2›öì]\5Ÿ kýö฿_×oÙµ­£¶Dãa{Ð}²‰G·$Qe´ÑÛƒÊÞaûdpº™ƒ?o’Üz¼_]¿\s¢¡ s‰e#…}QãÚOíîí}Xikß±©£#h*/ž¿n"óä¦DÜ.FâÚ'ïNæÊÂ߃Ìþ&2×'dö÷#seïDæÔO—¡ŠÂZ\BNêMɱ”awbW–ó^Ä&¸!‹6{s†‚`Ÿew’W–öN$ç™ÏU¦%ªu%’\Zk,ØÛ‚–+Ø[$ YRiP„GòZ„å¹Õß4·Ö&ÎçÖÚ•J¦¢H/›úÿEç„2S¢-Ä%~–:¼3rõ 9tt‡:y܇úí£É¡'wÆ¡ÃÉŸã»ãÏ•Ó'wÆ¡£*§OïCSN÷ºwÆ¢Áç%¨KÉ»l§ûüvzýs£t?½gLývá½öàArhý.üŸÿSeµQv|2ß×]øCÅ¿õ»ð*sèÞ÷;žCw· ò@w™w·?~ {¨»Û‡Ÿ>HþÜÝ.¼×} ‚ú·á½Þ}ÕU÷á}-|ÂP…³â¡|b÷â·áy½;yóç|ðÜóDz®ñ’‹½¹±Bûƒþò ßÒAùÁåŽøLeÖzù¦÷¡;÷!,Fn.ó¾ŒT™ÂÀ%¦i4k…ÂÈqÒÂS…<¹ã;4ÒPý"%å~ÐQ>e¤ñ,'½ÏS€Nô^päÂ`^Ïü”èÚ½S’<øråÈÞ^ã>û(‚‰AËp  âúU8,¦Â´Ñ­Â;G„~ÆøþÙCuíWLêÃÄNü/uéC¯ñÂÆÙµŸ9˜)ŸEén€.÷“ & (fo¹ÖRY{«ÚúÝ^o}¥þuæãùâÙ‡è(yÝí7JÓºe'YÍ…9¹;Loý4 —§o;ÝÇíþqo…îüÐ<§œ–‡îúbëˆt§÷K+<a Scî6´4G;KƒØYÅ<2aƒHê‘Ôdzã@V”¹‡Èza:æáñMƒ;£¬›pË$,øov\V»uõ¨ª`ÌÝLÖ÷õ¨}Ú;º©¯Þ«åv²ñƒÐë"\éß® %®Ö¸²W.Ï4º«?sa5©†@ø žÌ(•T`ëW2h¿%OjýŠ:ÿ_$°‘±™Å`SÏÍŠjuáSùPFÒb•B¬"9JÐP3gÒ[.ÞYPÍ ýžB„„ TãG%TXõ4 ÃZì5<'n-ƒNÁp‚æLÖÔÿàeåQ-+«ÊÊA-+·ÊÊoaO'¼—Å’®e¤Û ² ¿ea–Ñê`(a£ ñöF&”Ò0ÜÇG2 4¸ÿ–j™p´¡Üd±‚½ª»ÅÒÐ{|U¬¨Ë­Yž8ôÖ¨HðêCÅA$éšÐ°¯¦:ãâC PAá náX·xIÛ(íαù„ß¿îWb DF‘€t(,Èš"æAʵa´‰Å_¾Þ¿—,RvÙf\ë56VÅÆ'56nÅFJÎ(®"5žÕˆXÑÞÞx¬|Øxáä}ù`Í80¨ \ ‹–¬ŽÓý¥è…4Äp™˜ˆ1hM2 äÄŽú:¶VáüFÖÖE†€¿)¨SÎj·­.H›hÑY’”OhögÐ6 Î ”iðÇN@ «}tØuxV0깶ȸ‚˜Çñw Š×Ú™ÛÔ émF:ãþÍüÍ mî¶Žä×KcpÐm²^“¿¯©ïÛ*¡p1 ®› 5 ‰K3iBW­…Ÿ³ÿÆ”&íÕ±ã_/TUïh?ð¤ÞTÝ×ûÝ\4P€ÓÆ TµŠ|]œ0‚’BΠ΄÷š¬vÕ—Û«Äçe°™CKI©«*þA+IoÇí Øy®ÒÕâÙ¿ü·rÜd£H\cì*Àô8&'Q¡S/sÏKæ¶ Žs7Ã㣂‡ãªÂÃI [áaAN=h¸©·?Qœšhö5S‰÷3sMK«$kÍÊš,˜È(œ{’‘ SL­½øA©ðö¸A¾m^+|Ü–”»}$ý„'°0œ…²@¡iGNxû×ùC¤†ŸÔPùÒ™´ͼwþÜé =–!„d¿·ƒ‘õKT‹¼>o¤£©Ò—tÝ®U•<-,7—Ðk#2…ÇXÉeᯮæŽîÕóõÜïÁ¬ÌÐX—ýÍ«WýÒƒ@ÏDžñŽ¡b_íÓA·Ô)2K²‘˜B³‰ÔîwNk<ªŠGx}µ¤mv²’uåÁ@Ñ]¡ÌüðÜFC–ë¡\Ó36QZþŽ;îzÏÞVàa™—»ÊI=¦@ÿÆ;›»“#ت/µãÞ­ÁŠÀ@ø„°ÒñÊ{d¤ÍûèkìÀÊH„ÍåÆÑ9¥‚sÄ9G±,K©ºZL>Õyf–­ŒHêlƒ*%• fEˆWŸÒŒõ—~€gPØê(‹ÜÕ3؅ͼvdj¨¸<Û­1§2æ|¢ýŸ5æÜh‡¯Q§`:Ò†™¬K˜ân•ú/gHCUÀZE ©ÙêQºÂÕ+Icáüà.ÅŒ %!‰ùKÅ"ÝËZº•„n\x@®ð¬Éd)^…j2 yPÑਵ‰Pn¹w³Ù0\âÙ껆 Ç$Þ5|Í8pʘ[(2è@PЇEý@ÿ ² í‡0jñ r'!)ˆÔ1v‘uǯ÷ A¥žî•ùlîsNªÆI7?Í6Õî¶žç¥ÉG+Rɘ¹0C´Û$7•4¢(ÆS ÜÁ{s¹ô£ï‚ÁQ,…7hús‚ĉˆ¢ 0‚GCdÀsß×ÂNîwœ³ÑKlÐxuKôüûoʲ^÷ïOY˜û‡Wñ3ÈÇ×GÜ ðRÚj»ßÚ áïJ£–;e_ý#Sö©1ÐùŒýw鞟槅‘lÄH+¼É¡JÄú >&Öá#ªcb?b/Ld˦š{¢r•³è™HðÈæ 2—OYþ£ŠqËW™‚~–Ú3&ð,¥p»eD¡wÚ×븲¡)YqÇЄ:sçšîWÞ–oÞÁ¹©å±}/¿t¿]Ž àpç¾ „ù:öQ& –;ŠÝaܬ“üq¬º.Xµ@Ç%~Y ôÛeçQ!ce¢:2Öa&vGFËõX؇sÒu+gð²~•+p<‚çãþTjE|}Ä{¹ M9Ùðxj¼æ¼1L3÷_h.‡•5Ð)JóKt3BÆÖ€R»|uˆ'îâS±´¶|aÙÿþþšãéè~ô#Óhõ,S=ü;w|éŽ~(SP \x"_`Ù'o;&8 '±ÏZ†½ÄoH³–egíþcUwêpÕ…ú'‡ñÉ#Cß;|˜ßÇp;Ü{Ÿ“SÞ»$š1X$ko™TºÁ±þ&– ë~‹ 3èÆÎÈ[0墠—D~µv.ö]”‹„,…®è|Ä`ffÐìUÄÄëàUg|¤àxûWú“HE3ip9Ã?L yŠd»xEàX²a–5?Á­0.w@ìèb\Zú4W (ƒWu‹)#EÀ9òÆ3¦‚äËȨÜXËY€n‰TgÂHr2˜a¨uâCwl¦!ú`]hvůkWŒBë¨Õ!ô“†Í¨!”nÜŠ#À\Sš‚áJE÷äêíQûäää3BÓT¦Î³­°kµÈ·ô5 (÷oc˜±Š“yɪ–1 Í‡¢@F¨…aPV;áÉ¥q •£6qÆN‹*zO`ü]0aƒ*®õè#B {÷Q31ÏþUþ‚N(°ö`!JrEA/ n3 R•ÀC :!¬h¼ UÁq²?xÂðC3™ ¨ññëÖIÎÐ X €ß!;±DÍW%è SÉ¡ói2‘"K´È7òÀLðNƒS%ÊÕÅùë׌Ì "\nrz\@]‡³¨Ôu<‹ÝãYä;õÖâÆ —’Â%ð†•ÆöÐEåö7Žß“O8ª* ÕÖ!©¨xŸÌƒO £í¦‘Ó¹ÐêœÈñ*ºŠHmlt9’^æîWòŸÉC9°rú¶†&øeïn£ºä¯º$ ©A`¬ ÂpŠ%B ·¼ÿj:~ÌÆh| ôrH¨CXT‡„:†Ån‡¥R–>@ø¤ÊÖwr4bç¿\E ¡¦à¹‘<œ±ÞI·Û%­¡ÊåZçƒl‹I,Æ¯Šµ›õò—¢§Ï%±óž#:þá1(Ö^éèPß<ìTñ®Ìì*ƒj%Æý wÐ!^õ•É€sëì©VŒµÿÛÙ"‰AÀ4Å¢†_…ŠÏv䀊—â‘ E|¥)Ÿ9ïzŒ]b [rµ÷ÀÐÁ ñº]‘•òäþˆºÈèƒV`QmR¬šÕ1ª£Yc;šý²Â†J/ ²jjY¨æ_âKÉA¬ãÍÊ™ço‡*œÍŸBÐý¢?ü~ñÿPK¦Î,Эº²PKMk®> styles.xmlí]Kã6¾ï¯0$7½å¶Õ3=AØÅé Èôî–h[I$¹ÝÓþƒ=ìaÿßþ’åK2e=Z²åG{8‚6Y4«>U‹Å2õñû—(œ<Ã4 Pü ˜š¡L`ì!?ˆWÊ?žþªÎ•ï?ýå#Z.ÞûÈÛD0ÎÕ, a6Áƒãìžu>(›4¾G ²ûD0»Ï½{”À¸t/RßÓ©X ý²¾Ã)±8:‡/yßÁ„¶2,úÏL‰ÅÑ~ ¶}ZŒ©8|‰ú~ÉBu‰TE ȃ=.^ þò ¬ó<¹×õív«mm ¥+Ýt]W§½%Ã^I—lÒRùžCH&ËtS3õ‚6‚9èË¡YŠ7Ѧ½¡9¨=Õ$…&Áâ½ì÷E☊~=¯zk×óªfo ÒÞzF‰«ªbûýUÅöűÈ×-Ïw®?âNú¿Ç_vz•F}ç"´¨¼4Hz‹É¨Åñ¡’U2€;e×2 GgŸêm'ù6 r˜ ä^'¹B¯DEM a:SÇ*|&*_Êao©1m‹’€8è =¡­©jJÀo•pª§0Ai^²ìïtñ,Vé2Öy¶» Ò[®Rßo$ÅìØ:vØxÕçn¿Q*«A·"¸{Š@]ë[C(QiŽYfçM|=ý®“>•¸zìÌøj#¬p*Ô}ž‚8#À ÿ.Œ\¦QDUÂýRö-Ø?Z:öŹŽü¥•úK P>‹#[?}$®mú¦ú7y0ÊiжÊ{˜{‚×èåA1&ÆÄ2&¶ÁÚ±‹ˆLܦš¤mm*ú§Ì7úp 6!_y'¬m °*¾>(«$ëÀS Ú¤€¶©IŠeHó¯Õ¬‹À¢‚M޲5;ð!b¤ LÖ@ádÉ&öò FÝâî% ¢„ÀÏúñªÕE ^y²< ¼¼è!&‹:5B>þú0UóEѵD8rbý$QýÂ6– Ì`)~ÚXV”d½]¬’œÈU“v“A/»>Úªtr…¯KyºÁ’,ã¨@ñ; ìÃS€­hò+ÜN~GˆYcEF¯®` ±àJJè*I{Øe?ƒ4`:[L•bP,'Éi[âÕ¬pŒiƒ‡6qžb^þöcÓ”X)A\0ú3üüs3ùŒ•xò"€fГÝâ;²×,‡Që¢Ç»@AØ/˜\¸Eߟ뢇 TtüôkkÄ—†›ÃXc,»¸/I[ù/)Ú%(Ie({×Á¾e×ßUvjY±ÌBE3eöŸå öAê+o/ÿ,*3õ"ØÌÐò=( |æ/XÖpŸD†Uš¹²cÐÊDðK˜W ”5› ­÷{ØHIÇ,³B€-¸c8ém¼ °” l)9u]Ï[.©%$À'{{2ƒiMÉBÇå9Yâ›úB¸ÌIO­# Vk¡‡áº> Cß'f(4ªØ³g0W_ªBV;_; ‘æù?\ºõƒ¬êÁ÷TC$ q„2bu± C˜OX'iÇ ™Â>².•DœÊÿþûïR„/Ù©’Þæ/÷ág¬Ÿ_£ •!¾”98gú-µ.aˆj$*C—9uñ:Ƀº#ŠöDA¬†`Ç”zL:ß%Böháç&rFDh~“MGCÈÒœ›Dèn4„ì›Äg6>7ê§ç£!äܨŸvGDè6ý´iŒÑô}9j¡›ïµô¶-Sg&Ï÷)+"0Û(ð]o,6 ÕV¶©¶•{ÞLókÈÆ›†ñ-m¥È4ãÁH[1í›0mr2a¹Ãº(#ù:E›ÕZåÇ+1Š’?àMr8nzÜóôk+fO#2QG±ëb\ ¬o0P)K 5ñ½åèßÇ5æx¯©â­gLSI"Vך@a0‰TÙyoU>Aô‚ýtÊQbŽ›ii¶ìo·…„ê Œ’5`J'êžÂ0€ËCÏMúÕDÀ og„BÒçdZü½|äØ9³´ocΧȶ‚´éÚ¢-vè+¼nnƒåëp1Yu½B)«ag÷„#È…•öJÙq)2ɼ„L­'óÓ׫kM¨uÖׄþVÄVÙ¾Ãþ8¬Ó<ަðÄØÅTžÁréy®{]Ï ‘³YöwŽšÙ²w,;žï°ícõ¡Ûƒ“5> z/ïYÞ€ž£éXmpZfŒ†FÑE\Õå@ªîç‹ ø…þpèNäV®:§!äØÈ½c È6é9ìU\$‡n­ýM"Þ6Ž«l­Ê‚Py¢Ù{íMä|‡Ù÷AXC ýg^ѱޗUŠ6q-¶KMOR±ÖÆÔÖ’°evœ@ç(™8 .1Êá‘XŒ¹k`Ç^ýö ê zÖúÞ“É–!“É}ªñŒ¯(™Ü"ìÍ&“ gÅm´~Žè¯ºëÔjŒœ¼^­¹ˆ«³|½²SË ”¹™Úô&j.sû×)Qê,˜Ÿ ¡Ùµ–¹ÝjIs™Û!:t5Õ#ëÐxenw7ZÆ5^¡ÛìF‹”Æ+tsoŸñÊÜLãFõˆun¦y-®ú½ºiŽë6bÞ~&¶d&¢G&‚Áô•d"Z„ýZ2µÓœJ&¢ý¢!pf/6؇íe%kåŒb.®±s—½šrÁ¡²ÇAÜ:;âö±ˆ;]ˆ7vVæ{(âÎ8ˆÛgGÜ:ñZ–µ3ß\ÏЊøtij#n¾WÄïÆA|*ï‹ølÄï$â}ŸƒøL"ÞqwÄçñ7Ï6‹ÆºàOY?1 >“‘‹‚Å(ðý°ïùaÉ®¼ïBÞw!ï»÷]\ÏA ¼ïBÞwqþc@yßÅ×ZŽ ï»÷]\×1 ü˜Ÿ!õ@C(ûäqŸ<î»îã¾÷”[‰™X‰™X‰…ëEH&dbA&dbA&Þ}b¡![p㉧éN™ØO 8µš¼N ´{#‰q§¯&ä)!xÅæYÙÿð‹ñd‘z¤Ê[Ø’»É5 ±ÒO*ìŠe °'ØðžW÷¤þA3¦ôfIòù•lfŽ]|æÕ4´é´¼Ó²¸oW54Ëæ×Jǃ‰Ý¹i]˜‰} p$·ãéR@\‚‰} 0OsóÒ@Æ„ÞmOíÍ|2›[‘Éëol–­¹¦¹/•­MÙ!È •JmwnÏ{ñ`ÚÙSk=™ïX.W}ïÝwü#yÁy­ž§EÙŒùo†Ò@ÓuCWKñ^µ¹Ïy¯ÄÔ¦®Ø^`bÍ4—†ø ^4Qì^QJÞ˘‚@,ÞkÒÀVÌë`Þ(½Ù)zˆ½eævË^ÏÙ?úIí2þ¢\ªf%$bcílaw·Ð(õÉ©åÁ÷c¾j?mÄWÅBý ÃÖûª’;ðv7c7’,Êß ¡S/Øå[£FBäx“Ð=Ýõ¸J/wÃaã= äï!žEÅO{[*'Ç€¨7ÖíÒ‰i¶;«8±·¥¾Üå„oIÝz4ÆÌþäØ$uXÌ]¿€îZôÄqÜXû³ÇëÅb¸^ûƒÄscqiKú­nHEfMy+C(Þ)Ú’uiîܽ†cÐ/!«kzÙ>‰AûÃnL=tuÝ=HºawqŸQÀ§š®Ö.¦f]Õ¿œá2¹kJÁËš Y³!k6®!Y³!k6d͆¬Ù¸,B²fCÖlô¯Ù莧-OËxZÆÓ2ž–ñôU"$ãiOËxZÆÓ2ž¾–xZo-^áÈrúŠ3ÖJOŠBðJšÈ7î>³C$‰³ÂPs¿ðޱ4Å!Y+©‡qHQØAÁ_GŠ…(•¾/œ—À©Lèj‰A[=Z)-ÿöûIµOq–÷ZžÒ¢ Îô2ÅM $´2¡<Ç«(Ò“µ(M ‰¥O®fß¹µÊ'ÍvïŠÆ^!Ãë§èß¼d{µ!L Î)_ ôÂm0á–²Ç(ïÅ7ÔËÏØ¥R“qÅßIñw3ë; ÁÓOÓÒ,«¦eíÊX­Á°–ÓœYëRšjÝiSVmØ-+1:­º²9Î êéÕµX¡0kt­eÃw|ú.Ì?°?¿[å l‚C¡§ß‘¯ñ—Ä !jñ IÑ;͉€Š`jŽ=e1Áy'6 ͙ۙy®Mmól3›˜hz1Ä÷g?3ìµéìõ¦§ˆ{xG¸kº_¢+ª1›£³ÕT|‡HºwÛF@¬©fº5oF*É+ÞLs< mnÏÚ\ç ­‡Ý{NNo÷äoм{³á±R›¶6o—ÚÑ\Ói“º¬ ?½Ü¬ r”ð¯I–]CAsœy+ ¦«™ÔÆ‹½†Ç£Å^‚ly®Ígµ_öìƒìjÓÙ]o˜‡Çaö…â°õÒbËÐŒ¿û€¬"}Suç–ºy]¹Á.Ö½›×^ßg¢çÅŽ¸ï™Vgì\=× þêî|Ï4kVÿµ“…ÍÖµL3§®¨dv1¬‰sÎÅY–Œ‹í(o ÷q ®22 ¶̅\\™9r Öëkp¹vŠ»Ãƒ½3‚¢ÙGÞ&*“}ú?PK¿¾ÈÈí x±PKMk®>Vx£kkmeta.xml 2011-05-10T20:00:03PT05H47M17S1002011-05-14T14:26:27OpenOffice.org/3.2$Linux OpenOffice.org_project/320m19$Build-9505PKMk®>Thumbnails/thumbnail.pngíÕû;Ó ð¯JTn%%—veÕ¢$m£‹\™:Ê\NéädcmÙ)%;.%žp¹n.5C$r™a®1‹1 »°l§ó'œ_Ïsžçó¼ÏûãûÛ›xᨾSo'êÎNöî ô½ͪ[¾çM²™¨Üu¶?çEYâóÍìOÃ@%¹T¼‰ëˆB¨ôÅÌu‘*:r(8¹S²6OSwÛÔ0ç@?Ƽs€G¦2Ö4Õ’}Ó-Þí±–Š§J–¡ü#ÐÞºÂ3¨!’Ô1,½Ñ]YZ¦ñè9½ÄÖ¶ù¸j³Ò,ºÇìþlûÚU+sîø_¨¤+YI~nGĆ˜@jêž]]ŠÈ*&?g„Œ>Ö{ Ø_2õ ÙIΦ‡ó‡+i"ŽGQûŠíÏGÒ5Ô^Ã<¬–ï(Lj°7B¹÷â}Ÿd3§‚MU…áV¯1Á×—Ÿ‚á_7Ô‘I³Ð…ÞÁÀNf~¬BàM\ÊV W”MÏš .Ü"ríª+Àßçõµ ŸÌÑìÄ0½!&ÒT¹rŠJË*j—H0ñ–0Þ`JÀWˆ7ÙwKÙCD-ER”Wðû–vw7ÌÑÍúÓ8Er=H1CG%3N¶²Çh²f@]ƒ¿Íäñ7>ÑŠ—yÔ¯8Æ>ÔÜœ×Þ¸c^ÄÒãs‡Â#eé ˜ï›eùÙíf[°Ý7-G ð4d™!Žn›Om)Vòj2§»¹Ê[‹9îI}[¤Ï2¸¬êì|키ηZ¬É¸×ð@¿ŸrlÂ+‘lf×`nE9²ªùø¾ _k½woá¢øè7›ÖÍ/®d•5Ù¶„«KÖÿ6¤Ú\5#€Bhï5W‡W„·È?R7ì¾þŽ“áË'èìç>åãÑ wæú¥EIÌÈ™•.Ê%ÕØaÍjú*Ž0ÌÓ> ®©k@‘P¼¦XËLø¢•øDô­îT^¯U¼Œ ÿr0pñ•ö 0…¦3tÈÁ»âÂúÎ1—"V+zaTN›÷ß5¹s­Óö¶Æ‚(˨¢9¡!/–Ÿ_ûW2[sõ/ÒÒOÕ> ø3P=”/¼]R˃‚Ðô—næI€Á®³ÕƒvÐÖŽ„¬ˆIC¥Æ ;˜ŸÈÛyt΂ éF[ÂÖ‘9©£E UÆJUËAz $™ú"Ì(µ~?Îç~ËÈïßì—¬!£}ù«±•y^Gs÷Y†¡+k¨mµTri4É×£¾‰…>€¶…ä;p=Ñ/¦¾òã­E‹ wCÄu# Ýg¬ÝÓ`,qnFè'(³Ÿ“3«C Fò85ú›Ü']¼Ä*ÅkÙîzä-æœNu„7£»°"ÏMת®ßHÛ—z]ÂÓÙœ¨%ùÉS¿¤›ŽA¤ÛN˜ñÚú £cZ jáË'<»ÊÜ3%Ò†P˜¿Ÿ1òµåð9ë0UÖäù0 C‘µ×(%³‘]quh˜Z@Ûé¦NǺ [ù’`99ù#L7í6iÝ~F‡?biDÂGVܾÇd! EW)Ïÿ”-Çùpj7öÒîðY “µú§KCDd”êã?ø.Þ2æ˜J@§Q)/. Oúåp`[Íml½˜Y•ð¹ì ~ßøb×½»›Õ3OBd³ }VÒQcÀéÿU2ÌY,'Š þ¹eç‹ûòó¿ÞÿPKÆ÷dè¶PKMk®> settings.xmlÝZÝsâ6ï_‘ñô¡} |w LàHraŽKh ×^ß„½5²Ö#É!Ü__ÉÊ82îtš‡KΖ~¿Õîj¿àâãsÀNž@HмãÔOkÎ p=ÊýŽó0½®œ;»?]à|N]h{èFpU‘ ”^"Oôv.Ûë×'¼DRÙæ$ÙVnCà›míÝÕí˜lýä™QþØqJ…íju¹\ž.ߢð«õV«Ußn–†¤†"*8áîž]ZùœúyQÖ«w÷#âVh³a}°XðF­Ö¬®ÿ¿Y-Êòr™µƒPËr’<62vMÙ~¢°Üz“¶ïÇ=_©4 è S ÍKµ õKÊ•Ó=«Ÿ]TaÞ=‚¹Jî4›õÂà¿SO-ÒÐç­w­Âð7@ýEªôúYýöàgˆ wºsÂ$Á¿È÷u| ô¡œpNѰdÁ+D¾áS.Ë#1ÿŽ(‡LŠ#¨énö¸êZ臥³ŒQ_¢r3f‘¼!Üc {lIV%º¯q]ýÇ¥ þD_5–m{¸b4 œ(#[ÅjéªgèÒâRý}­f{šC®2L”ØcDV:ííã š}-„X9ÝjîŸóŽ,c"ù¯H3B÷¼×E©¾u‹=¥ãÿ¬ ÞâŠXÜÙï#¢Ì„c‚õgˆ÷Så…¾;–gL‡4ð x:pÃúJÆÎð½rÂWŸúI¨,þ#mÆ£îãžÕ•G3Ó|‘ „ûpëF¤„ãLõ@ŽAÜã2ÝÊM˯Õb,Uú Sã ÷£ŒÉÊÏ¿DZ€_‚ªLø©Ä™«.bÑ¥¤``æªf*8Õ”G-WãâÝúd®é%‘ kNÏ’æ$bJÓNTz-Qo¼Ï­ÄìÞ5Šõ<à[óŸèt5¥ô‰®€ÛÚ>2ü¨Ÿú–Õ ”ŒðÔÍj|ÊÍxËáŠ{/î/ç^ÏQÄ£ÌÚÅ};Í'¸¼AA¿#W„?ÞåÚßÝ…mBþ ‚÷¤ö«qÄ]•ÕÖ e<ê#>2(g ½KÐ'îc=ð.Ç‹óôÂ$:ß–3ŒX£ ²4U–ô×T½T)?@zK|L‚8Õ˜C”òyCÌ‘6/8âî"Å£õ ŒvnËú#f˜Rå:Ò”–"¿I“:[ZÇs[Z$Þ=9Û¯>Ž5×ÕÝç „.Æ’Ú¬[Žçƺ¦ó “(ÊÊB±Å‹£j_=Eæt1,ˆ¡.cÌ÷t2"«Ãè´±:ÃeEwÈ"sÌB”)·/5—`L@Eûeû[?™'øºþzÅ0”eÜ©íh8=ø'íÚ;ð‡ÐÓWçô` AÈ^Ⱦokóã6½zð]“jÖ·‘ºPKd…<&Ï$PKMk®>META-INF/manifest.xmlµ•KjÃ0@÷9…ÑÞR›U1q-ôéyìôC3 Éí+’¸m(M±V–Ìè½ñH­6GkªDÔÞµì™?± œòvCË>¶ïõ Û¬++ ª¼ÎáuÚ²]ã%jlœ´€ ©ÆpWÉ‚£æk|3šÖ‹êîµ:ÆSu“A§eM§-“!­$å<ÅÁuüìâS0?Ï1ìF™|ÝròºOÆÔAÒ¾e‚‰‡r¹Oyó®×CŠg?.æLîd,ƒ—J<õQ¨ãX\äâ®"‚ÞxIP|H!ŸTýO^¹&ïMLüX™¢¹ƒk+@ñªÉÊ€E²¿w+Lnü9yÒ\MËáA9Á‘ÄØ þ×+~ç" àìX $gëkÛ}²;'µAA—!n˜>oa(_Ç×Ò®ÄÛxý PK’ð§3&¬¨//mimetypePKMk®>UConfigurations2/statusbar/PKMk®>'Configurations2/accelerator/current.xmlPKMk®>äConfigurations2/floater/PKMk®>Configurations2/popupmenu/PKMk®>RConfigurations2/progressbar/PKMk®>ŒConfigurations2/toolpanel/PKMk®>ÄConfigurations2/menubar/PKMk®>úConfigurations2/toolbar/PKMk®>0Configurations2/images/Bitmaps/PKMk®>¦Î,Эº² mcontent.xmlPKMk®>¿¾ÈÈí x± Sstyles.xmlPKMk®>Vx£kkx%meta.xmlPKMk®>Æ÷dè¶ *Thumbnails/thumbnail.pngPKMk®>d…<&Ï$ 70settings.xmlPKMk®>’ð§ * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF 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" struct tmuxproc { const char *name; int exit; void (*signalcb)(int); }; struct tmuxpeer { struct tmuxproc *parent; struct imsgbuf ibuf; struct event event; int flags; #define PEER_BAD 0x1 void (*dispatchcb)(struct imsg *, void *); void *arg; }; static int peer_check_version(struct tmuxpeer *, struct imsg *); static void proc_update_event(struct tmuxpeer *); static void proc_event_cb(__unused int fd, short events, void *arg) { struct tmuxpeer *peer = arg; ssize_t n; struct imsg imsg; if (!(peer->flags & PEER_BAD) && (events & EV_READ)) { if (((n = imsg_read(&peer->ibuf)) == -1 && errno != EAGAIN) || n == 0) { peer->dispatchcb(NULL, peer->arg); return; } for (;;) { if ((n = imsg_get(&peer->ibuf, &imsg)) == -1) { peer->dispatchcb(NULL, peer->arg); return; } if (n == 0) break; log_debug("peer %p message %d", peer, imsg.hdr.type); if (peer_check_version(peer, &imsg) != 0) { if (imsg.fd != -1) close(imsg.fd); imsg_free(&imsg); break; } peer->dispatchcb(&imsg, peer->arg); imsg_free(&imsg); } } if (events & EV_WRITE) { if (msgbuf_write(&peer->ibuf.w) <= 0 && errno != EAGAIN) { peer->dispatchcb(NULL, peer->arg); return; } } if ((peer->flags & PEER_BAD) && peer->ibuf.w.queued == 0) { peer->dispatchcb(NULL, peer->arg); return; } proc_update_event(peer); } static void proc_signal_cb(int signo, __unused short events, void *arg) { struct tmuxproc *tp = arg; tp->signalcb(signo); } static int peer_check_version(struct tmuxpeer *peer, struct imsg *imsg) { int version; version = imsg->hdr.peerid & 0xff; if (imsg->hdr.type != MSG_VERSION && version != PROTOCOL_VERSION) { log_debug("peer %p bad version %d", peer, version); proc_send(peer, MSG_VERSION, -1, NULL, 0); peer->flags |= PEER_BAD; return (-1); } return (0); } static void proc_update_event(struct tmuxpeer *peer) { short events; event_del(&peer->event); events = EV_READ; if (peer->ibuf.w.queued > 0) events |= EV_WRITE; event_set(&peer->event, peer->ibuf.fd, events, proc_event_cb, peer); event_add(&peer->event, NULL); } int proc_send(struct tmuxpeer *peer, enum msgtype type, int fd, const void *buf, size_t len) { struct imsgbuf *ibuf = &peer->ibuf; void *vp = (void *)buf; int retval; if (peer->flags & PEER_BAD) return (-1); log_debug("sending message %d to peer %p (%zu bytes)", type, peer, len); retval = imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, fd, vp, len); if (retval != 1) return (-1); proc_update_event(peer); return (0); } int proc_send_s(struct tmuxpeer *peer, enum msgtype type, const char *s) { return (proc_send(peer, type, -1, s, strlen(s) + 1)); } struct tmuxproc * proc_start(const char *name, struct event_base *base, int forkflag, void (*signalcb)(int)) { struct tmuxproc *tp; struct utsname u; if (forkflag && !tmate_foreground) { switch (fork()) { case -1: fatal("fork failed"); case 0: break; default: return (NULL); } if (daemon(1, 0) != 0) fatal("daemon failed"); clear_signals(0); if (event_reinit(base) != 0) fatalx("event_reinit failed"); } if (tmate_foreground) { if (forkflag) clear_signals(0); log_open_fp(stdout); } else { log_open(name); } #ifdef HAVE_SETPROCTITLE setproctitle("%s (%s)", name, socket_path); #endif if (uname(&u) < 0) memset(&u, 0, sizeof u); log_debug("%s started (%ld): socket %s, protocol %d", name, (long)getpid(), socket_path, PROTOCOL_VERSION); log_debug("on %s %s %s; libevent %s (%s)", u.sysname, u.release, u.version, event_get_version(), event_get_method()); tp = xcalloc(1, sizeof *tp); tp->name = xstrdup(name); tp->signalcb = signalcb; set_signals(proc_signal_cb, tp); return (tp); } void proc_loop(struct tmuxproc *tp, int (*loopcb)(void)) { log_debug("%s loop enter", tp->name); do event_loop(EVLOOP_ONCE); while (!tp->exit && (loopcb == NULL || !loopcb ())); log_debug("%s loop exit", tp->name); } void proc_exit(struct tmuxproc *tp) { tp->exit = 1; } struct tmuxpeer * proc_add_peer(struct tmuxproc *tp, int fd, void (*dispatchcb)(struct imsg *, void *), void *arg) { struct tmuxpeer *peer; peer = xcalloc(1, sizeof *peer); peer->parent = tp; peer->dispatchcb = dispatchcb; peer->arg = arg; imsg_init(&peer->ibuf, fd); event_set(&peer->event, fd, EV_READ, proc_event_cb, peer); log_debug("add peer %p: %d (%p)", peer, fd, arg); proc_update_event(peer); return (peer); } void proc_remove_peer(struct tmuxpeer *peer) { log_debug("remove peer %p", peer); event_del(&peer->event); imsg_clear(&peer->ibuf); close(peer->ibuf.fd); free(peer); } void proc_kill_peer(struct tmuxpeer *peer) { peer->flags |= PEER_BAD; } tmate-2.4.0/resize.c000066400000000000000000000114311356407164200143040ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" #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 ssx, ssy, has, limit; int flag, has_status, is_zoomed, forced; #ifdef TMATE int tmate_sx = tmate_session.min_sx; int tmate_sy = tmate_session.min_sy; #endif RB_FOREACH(s, sessions, &sessions) { has_status = options_get_number(s->options, "status"); s->attached = 0; ssx = ssy = UINT_MAX; TAILQ_FOREACH(c, &clients, entry) { if (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; s->attached++; } } #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; } RB_FOREACH(w, windows, &windows) { if (w->active == 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); if (has) { if (s->sx < ssx) ssx = s->sx; if (s->sy < ssy) ssy = s->sy; } } if (ssx == UINT_MAX || ssy == UINT_MAX) continue; forced = 0; limit = options_get_number(w->options, "force-width"); if (limit >= PANE_MINIMUM && ssx > limit) { ssx = limit; forced |= WINDOW_FORCEWIDTH; } limit = options_get_number(w->options, "force-height"); if (limit >= PANE_MINIMUM && ssy > limit) { ssy = limit; forced |= WINDOW_FORCEHEIGHT; } if (w->sx == ssx && w->sy == ssy) continue; log_debug("window size %u,%u (was %u,%u)", ssx, ssy, w->sx, w->sy); w->flags &= ~(WINDOW_FORCEWIDTH|WINDOW_FORCEHEIGHT); w->flags |= forced; 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-2.4.0/screen-redraw.c000066400000000000000000000300651356407164200155500ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" 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_is(u_int, u_int, int, struct window *, struct window_pane *, struct window_pane *); void screen_redraw_draw_borders(struct client *, int, u_int); void screen_redraw_draw_panes(struct client *, u_int); void screen_redraw_draw_status(struct client *, u_int); void screen_redraw_draw_number(struct client *, struct window_pane *, u_int); #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 if the border of a particular pane. */ int screen_redraw_check_is(u_int px, u_int py, int type, struct window *w, struct window_pane *wantwp, struct window_pane *wp) { /* Is this off the active pane border? */ if (screen_redraw_cell_border1(wantwp, 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 == wantwp) return (px <= wp->sx / 2); return (px > wp->sx / 2); } return (0); } /* Check if the pane covers the whole height. */ if (wp->yoff == 0 && wp->sy == w->sy) { /* This can either be the left pane or the right pane. */ if (wp->xoff == 0) { /* left pane */ if (wp == wantwp) return (py <= wp->sy / 2); return (py > wp->sy / 2); } return (0); } return (type); } /* Redraw entire screen. */ void screen_redraw_screen(struct client *c, int draw_panes, int draw_status, int draw_borders) { struct options *oo = c->session->options; struct tty *tty = &c->tty; u_int top; int status, spos; /* 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 (!status) draw_status = 0; if (draw_borders) screen_redraw_draw_borders(c, status, top); if (draw_panes) screen_redraw_draw_panes(c, top); if (draw_status) screen_redraw_draw_status(c, top); 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_pane(&c->tty, wp, i, wp->xoff, yoff); tty_reset(&c->tty); } /* Draw the borders. */ void screen_redraw_draw_borders(struct client *c, int status, u_int top) { struct session *s = c->session; struct window *w = s->curw->window; struct options *oo = w->options; struct tty *tty = &c->tty; struct window_pane *wp = NULL; struct grid_cell m_active_gc, active_gc, m_other_gc, other_gc; struct grid_cell msg_gc; u_int i, j, type, msgx = 0, msgy = 0; int active, small, flags; char msg[256]; const char *tmp; size_t msglen = 0; small = (tty->sy - status + top > w->sy) || (tty->sx > w->sx); if (small) { flags = w->flags & (WINDOW_FORCEWIDTH|WINDOW_FORCEHEIGHT); if (flags == (WINDOW_FORCEWIDTH|WINDOW_FORCEHEIGHT)) tmp = "force-width, force-height"; else if (flags == WINDOW_FORCEWIDTH) tmp = "force-width"; else if (flags == WINDOW_FORCEHEIGHT) tmp = "force-height"; else tmp = "a smaller client"; xsnprintf(msg, sizeof msg, "(size %ux%u from %s)", w->sx, w->sy, tmp); msglen = strlen(msg); if (tty->sy - 1 - status + top > w->sy && tty->sx >= msglen) { msgx = tty->sx - msglen; msgy = tty->sy - 1 - status + top; } else if (tty->sx - w->sx > msglen) { msgx = tty->sx - msglen; msgy = tty->sy - 1 - status + top; } else small = 0; } style_apply(&other_gc, oo, "pane-border-style"); style_apply(&active_gc, oo, "pane-active-border-style"); active_gc.attr = other_gc.attr = GRID_ATTR_CHARSET; memcpy(&m_other_gc, &other_gc, sizeof m_other_gc); m_other_gc.attr ^= GRID_ATTR_REVERSE; memcpy(&m_active_gc, &active_gc, sizeof m_active_gc); m_active_gc.attr ^= GRID_ATTR_REVERSE; for (j = 0; j < tty->sy - status; j++) { for (i = 0; i < tty->sx; i++) { type = screen_redraw_check_cell(c, i, j, &wp); if (type == CELL_INSIDE) continue; if (type == CELL_OUTSIDE && small && i > msgx && j == msgy) continue; active = screen_redraw_check_is(i, j, type, w, w->active, wp); if (server_is_marked(s, s->curw, marked_pane.wp) && screen_redraw_check_is(i, j, type, w, marked_pane.wp, wp)) { if (active) tty_attributes(tty, &m_active_gc, NULL); else tty_attributes(tty, &m_other_gc, NULL); } else if (active) tty_attributes(tty, &active_gc, NULL); else tty_attributes(tty, &other_gc, NULL); tty_cursor(tty, i, top + j); tty_putc(tty, CELL_BORDERS[type]); } } if (small) { memcpy(&msg_gc, &grid_default_cell, sizeof msg_gc); tty_attributes(tty, &msg_gc, NULL); tty_cursor(tty, msgx, msgy); tty_puts(tty, msg); } } /* Draw the panes. */ void screen_redraw_draw_panes(struct client *c, u_int top) { struct window *w = c->session->curw->window; struct tty *tty = &c->tty; struct window_pane *wp; u_int i; TAILQ_FOREACH(wp, &w->panes, entry) { if (!window_pane_visible(wp)) continue; for (i = 0; i < wp->sy; i++) tty_draw_pane(tty, wp, i, wp->xoff, top + wp->yoff); if (c->flags & CLIENT_IDENTIFY) screen_redraw_draw_number(c, wp, top); } } /* Draw the status line. */ void screen_redraw_draw_status(struct client *c, u_int top) { struct tty *tty = &c->tty; if (top) tty_draw_line(tty, NULL, &c->status, 0, 0, 0); else tty_draw_line(tty, NULL, &c->status, 0, 0, tty->sy - 1); } /* Draw number on a pane. */ void screen_redraw_draw_number(struct client *c, struct window_pane *wp, u_int top) { 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 (top) 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_default_cell, sizeof gc); if (w->active == wp) colour_set_bg(&gc, active_colour); else colour_set_bg(&gc, colour); tty_attributes(tty, &gc, wp); for (ptr = buf; *ptr != '\0'; ptr++) { if (*ptr < '0' || *ptr > '9') continue; idx = *ptr - '0'; for (j = 0; j < 5; j++) { for (i = px; i < px + 5; i++) { tty_cursor(tty, xoff + i, yoff + py + j); if (window_clock_table[idx][j][i - px]) tty_putc(tty, ' '); } } px += 6; } len = xsnprintf(buf, sizeof buf, "%ux%u", wp->sx, wp->sy); if (wp->sx < len || wp->sy < 6) return; tty_cursor(tty, xoff + wp->sx - len, yoff); draw_text: memcpy(&gc, &grid_default_cell, sizeof gc); if (w->active == wp) colour_set_fg(&gc, active_colour); else colour_set_fg(&gc, colour); tty_attributes(tty, &gc, wp); tty_puts(tty, buf); tty_cursor(tty, 0, 0); } tmate-2.4.0/screen-write.c000066400000000000000000000573251356407164200154260ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "tmux.h" 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|MODE_FOCUSON); 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) { utf8_set(&gc->data, ch); screen_write_cell(ctx, gc); } /* Calculate string length, with embedded formatting. */ size_t screen_write_cstrlen(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("%s", msg2); free(msg); free(msg2); return (size); } /* Calculate string length. */ size_t screen_write_strlen(const char *fmt, ...) { va_list ap; char *msg; struct utf8_data ud; u_char *ptr; size_t left, size = 0; enum utf8_state more; va_start(ap, fmt); xvasprintf(&msg, fmt, ap); va_end(ap); ptr = msg; while (*ptr != '\0') { if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) { ptr++; left = strlen(ptr); if (left < (size_t)ud.size - 1) break; while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE) ptr++; ptr++; if (more == UTF8_DONE) size += ud.width; } else { if (*ptr > 0x1f && *ptr < 0x7f) size++; ptr++; } } free(msg); return (size); } /* Write simple string (no UTF-8 or maximum length). */ void screen_write_puts(struct screen_write_ctx *ctx, struct grid_cell *gc, const char *fmt, ...) { va_list ap; va_start(ap, fmt); screen_write_vnputs(ctx, -1, gc, fmt, ap); va_end(ap); } /* Write string with length limit (-1 for unlimited). */ void screen_write_nputs(struct screen_write_ctx *ctx, ssize_t maxlen, struct grid_cell *gc, const char *fmt, ...) { va_list ap; va_start(ap, fmt); screen_write_vnputs(ctx, maxlen, gc, fmt, ap); va_end(ap); } void screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, struct grid_cell *gc, const char *fmt, va_list ap) { char *msg; struct utf8_data ud; u_char *ptr; size_t left, size = 0; enum utf8_state more; xvasprintf(&msg, fmt, ap); ptr = msg; while (*ptr != '\0') { if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) { ptr++; left = strlen(ptr); if (left < (size_t)ud.size - 1) break; while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE) ptr++; ptr++; if (more == UTF8_DONE) { if (maxlen > 0 && size + ud.width > (size_t) maxlen) { while (size < (size_t) maxlen) { screen_write_putc(ctx, gc, ' '); size++; } break; } size += ud.width; utf8_copy(&gc->data, &ud); screen_write_cell(ctx, gc); } } else { if (maxlen > 0 && size + 1 > (size_t) maxlen) break; if (*ptr == '\001') gc->attr ^= GRID_ATTR_CHARSET; else if (*ptr > 0x1f && *ptr < 0x7f) { size++; screen_write_putc(ctx, gc, *ptr); } ptr++; } } free(msg); } /* Write string, similar to nputs, but with embedded formatting (#[]). */ void screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen, struct grid_cell *gc, const char *fmt, ...) { struct grid_cell lgc; struct utf8_data ud; va_list ap; char *msg; u_char *ptr, *last; size_t left, size = 0; enum utf8_state more; 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'; style_parse(gc, &lgc, ptr); ptr = last + 1; continue; } if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) { ptr++; left = strlen(ptr); if (left < (size_t)ud.size - 1) break; while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE) ptr++; ptr++; if (more == UTF8_DONE) { if (maxlen > 0 && size + ud.width > (size_t) maxlen) { while (size < (size_t) maxlen) { screen_write_putc(ctx, gc, ' '); size++; } break; } size += ud.width; utf8_copy(&lgc.data, &ud); screen_write_cell(ctx, &lgc); } } else { if (maxlen > 0 && size + 1 > (size_t) maxlen) break; if (*ptr > 0x1f && *ptr < 0x7f) { size++; screen_write_putc(ctx, &lgc, *ptr); } ptr++; } } free(msg); } /* 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; struct grid_cell gc; 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++) { grid_get_cell(gd, xx, yy, &gc); 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; 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. */ memcpy(&gc, &grid_default_cell, sizeof gc); for (xx = 1; xx <= screen_size_x(s); xx++) { grid_view_get_cell(gd, screen_size_x(s) - xx, s->cy, &gc); 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 (s->cx == screen_size_x(s)) s->cx--; 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 (s->cx == screen_size_x(s)) s->cx--; 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); utf8_set(&gc.data, 'E'); for (yy = 0; yy < screen_size_y(s); yy++) { for (xx = 0; xx < screen_size_x(s); xx++) grid_view_set_cell(s->grid, xx, yy, &gc); } 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; u_int sx = screen_size_x(s); u_int sy = screen_size_y(s); 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, sx, sy); 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; int insert; /* Ignore padding. */ if (gc->flags & GRID_FLAG_PADDING) return; width = gc->data.width; /* * If this is a wide character and there is no room on the screen, for * the entire character, don't print it. */ if (!(s->mode & MODE_WRAP) && (width > 1 && (width > screen_size_x(s) || (s->cx != screen_size_x(s) && s->cx > screen_size_x(s) - width)))) return; /* * If the width is zero, combine onto the previous character, if * there is space. */ if (width == 0) { if (screen_write_combine(ctx, &gc->data) == 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. */ memcpy(&tmp_gc, &grid_default_cell, sizeof tmp_gc); tmp_gc.flags |= GRID_FLAG_PADDING; tmp_gc.data.width = 0; for (xx = s->cx + 1; xx < s->cx + width; xx++) grid_view_set_cell(gd, xx, s->cy, &tmp_gc); /* 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); utf8_copy(&tmp_gc.data, &gc->data); tmp_gc.attr = tmp_gc.attr & ~GRID_ATTR_CHARSET; tmp_gc.attr |= gc->attr & GRID_ATTR_CHARSET; tmp_gc.flags = gc->flags; tmp_gc.flags &= ~(GRID_FLAG_FGRGB|GRID_FLAG_BGRGB); tmp_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; /* 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. */ grid_view_get_cell(gd, s->cx - 1, s->cy, &gc); /* Check there is enough space. */ if (gc.data.size + ud->size > sizeof gc.data.data) return (-1); /* Append the data. */ memcpy(gc.data.data + gc.data.size, ud->data, ud->size); gc.data.size += ud->size; /* Set the new cell. */ grid_view_set_cell(gd, s->cx - 1, s->cy, &gc); 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; struct grid_cell gc; u_int xx; grid_view_get_cell(gd, s->cx, s->cy, &gc); if (gc.flags & GRID_FLAG_PADDING) { /* * A padding cell, so clear any following and leading padding * cells back to the character. Don't overwrite the current * cell as that happens later anyway. */ xx = s->cx + 1; while (--xx > 0) { grid_view_get_cell(gd, xx, s->cy, &gc); 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)) { grid_view_get_cell(gd, xx, s->cy, &gc); 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-2.4.0/screen.c000066400000000000000000000203311356407164200142610ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" 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) { s->grid = grid_create(sx, sy, hlimit); 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) { free(s->ccolour); s->ccolour = xstrdup(colour); } /* Set screen title. */ void screen_set_title(struct screen *s, const char *title) { free(s->title); s->title = xstrdup(title); } /* 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. */ 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 = xreallocarray(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; sel->lineflag = LINE_SEL_NONE; } /* Check if cell in selection. */ int screen_check_selection(struct screen *s, u_int px, u_int py) { struct screen_sel *sel = &s->sel; u_int xx; if (!sel->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) return (0); if (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->ey && px < sel->ex) return (0); if (sel->modekeys == MODEKEY_EMACS) xx = sel->sx - 1; else xx = sel->sx; if (py == sel->sy && px > xx) return (0); } else { /* starting line == ending line. */ if (py != sel->sy) return (0); if (sel->ex < sel->sx) { /* cursor (ex) is on the left */ if (sel->modekeys == MODEKEY_EMACS) xx = sel->sx - 1; else xx = sel->sx; if (px > xx || px < sel->ex) return (0); } else { /* selection start (sx) is on the left */ if (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; u_int change; s->grid = grid_create(old->sx, old->sy, old->hlimit); change = grid_reflow(s->grid, old, new_x); if (change < s->cy) s->cy -= change; else s->cy = 0; } tmate-2.4.0/server-client.c000066400000000000000000001015301356407164200155650ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include "tmux.h" #include "tmate.h" void server_client_free(int, short, void *); void server_client_check_focus(struct window_pane *); void server_client_check_resize(struct window_pane *); key_code server_client_check_mouse(struct client *); 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 *); void server_client_dispatch(struct imsg *, void *); void server_client_dispatch_command(struct client *, struct imsg *); void server_client_dispatch_identify(struct client *, struct imsg *); void server_client_dispatch_shell(struct client *); /* Check if this client is inside this server. */ int server_client_check_nested(struct client *c) { struct environ_entry *envent; struct window_pane *wp; if (c->tty.path == NULL) return (0); envent = environ_find(c->environ, "TMUX"); if (envent == NULL || *envent->value == '\0') return (0); RB_FOREACH(wp, window_pane_tree, &all_window_panes) { if (strcmp(wp->tty, c->tty.path) == 0) return (1); } return (0); } /* Set client key table. */ void server_client_set_key_table(struct client *c, const char *name) { if (name == NULL) name = server_client_get_key_table(c); key_bindings_unref_table(c->keytable); c->keytable = key_bindings_get_table(name, 1); c->keytable->references++; } /* Get default key table. */ const char * server_client_get_key_table(struct client *c) { struct session *s = c->session; const char *name; if (s == NULL) return ("root"); name = options_get_string(s->options, "key-table"); if (*name == '\0') return ("root"); return (name); } /* Create a new client. */ void server_client_create(int fd) { struct client *c; setblocking(fd, 0); c = xcalloc(1, sizeof *c); c->references = 1; c->peer = proc_add_peer(server_proc, fd, server_client_dispatch, c); if (gettimeofday(&c->creation_time, NULL) != 0) fatal("gettimeofday failed"); memcpy(&c->activity_time, &c->creation_time, sizeof c->activity_time); c->environ = environ_create(); c->fd = -1; c->cwd = NULL; 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); c->message_string = NULL; TAILQ_INIT(&c->message_log); c->prompt_string = NULL; c->prompt_buffer = NULL; c->prompt_index = 0; c->flags |= CLIENT_FOCUSED; c->keytable = key_bindings_get_table("root", 1); c->keytable->references++; evtimer_set(&c->repeat_timer, server_client_repeat_timer, c); TAILQ_INSERT_TAIL(&clients, c, entry); log_debug("new client %p", c); } /* Open client terminal if needed. */ int server_client_open(struct client *c, char **cause) { if (c->flags & CLIENT_CONTROL) return (0); if (strcmp(c->ttyname, "/dev/tty") == 0) { *cause = xstrdup("can't use /dev/tty"); return (-1); } if (!(c->flags & CLIENT_TERMINAL)) { *cause = xstrdup("not a terminal"); return (-1); } if (tty_open(&c->tty, cause) != 0) return (-1); return (0); } /* Lost a client. */ void server_client_lost(struct client *c) { struct message_entry *msg, *msg1; c->flags |= CLIENT_DEAD; status_prompt_clear(c); status_message_clear(c); if (c->stdin_callback != NULL) c->stdin_callback(c, 1, c->stdin_callback_data); TAILQ_REMOVE(&clients, c, entry); log_debug("lost client %p", c); /* * If CLIENT_TERMINAL hasn't been set, then tty_init hasn't been called * and tty_free might close an unrelated fd. */ if (c->flags & CLIENT_TERMINAL) tty_free(&c->tty); free(c->ttyname); free(c->term); evbuffer_free(c->stdin_data); evbuffer_free(c->stdout_data); if (c->stderr_data != c->stdout_data) evbuffer_free(c->stderr_data); if (event_initialized(&c->status_timer)) evtimer_del(&c->status_timer); screen_free(&c->status); free(c->title); free((void *)c->cwd); evtimer_del(&c->repeat_timer); key_bindings_unref_table(c->keytable); 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); TAILQ_FOREACH_SAFE(msg, &c->message_log, entry, msg1) { free(msg->msg); TAILQ_REMOVE(&c->message_log, msg, entry); free(msg); } free(c->prompt_string); free(c->prompt_buffer); c->cmdq->flags |= CMD_Q_DEAD; cmdq_free(c->cmdq); c->cmdq = NULL; environ_free(c->environ); proc_remove_peer(c->peer); c->peer = NULL; server_client_unref(c); server_add_accept(0); /* may be more file descriptors now */ recalculate_sizes(); server_check_unattached(); server_update_socket(); } /* Remove reference from a client. */ void server_client_unref(struct client *c) { log_debug("unref client %p (%d references)", c, c->references); c->references--; if (c->references == 0) event_once(-1, EV_TIMEOUT, server_client_free, c, NULL); } /* Free dead client. */ void server_client_free(__unused int fd, __unused short events, void *arg) { struct client *c = arg; log_debug("free client %p (%d references)", c, c->references); if (c->references == 0) free(c); } /* Detach a client. */ void server_client_detach(struct client *c, enum msgtype msgtype) { struct session *s = c->session; if (s == NULL) return; hooks_run(c->session->hooks, c, NULL, "client-detached"); proc_send_s(c->peer, msgtype, s->name); } /* Check for mouse keys. */ key_code server_client_check_mouse(struct client *c) { struct session *s = c->session; struct mouse_event *m = &c->tty.mouse; struct window *w; struct window_pane *wp; enum { NOTYPE, DOWN, UP, DRAG, WHEEL } type = NOTYPE; enum { NOWHERE, PANE, STATUS, BORDER } where = NOWHERE; u_int x, y, b; key_code key; log_debug("mouse %02x at %u,%u (last %u,%u) (%d)", m->b, m->x, m->y, m->lx, m->ly, c->tty.mouse_drag_flag); /* What type of event is this? */ if (MOUSE_DRAG(m->b)) { type = DRAG; if (c->tty.mouse_drag_flag) { x = m->x, y = m->y, b = m->b; log_debug("drag update at %u,%u", x, y); } else { x = m->lx, y = m->ly, b = m->lb; log_debug("drag start at %u,%u", x, y); } } else if (MOUSE_WHEEL(m->b)) { type = WHEEL; x = m->x, y = m->y, b = m->b; log_debug("wheel at %u,%u", x, y); } else if (MOUSE_BUTTONS(m->b) == 3) { type = UP; x = m->x, y = m->y, b = m->lb; log_debug("up at %u,%u", x, y); } else { type = DOWN; x = m->x, y = m->y, b = m->b; log_debug("down at %u,%u", x, y); } if (type == NOTYPE) return (KEYC_UNKNOWN); /* Always save the session. */ m->s = s->id; /* Is this on the status line? */ m->statusat = status_at_line(c); if (m->statusat != -1 && y == (u_int)m->statusat) { w = status_get_window_at(c, x); if (w == NULL) return (KEYC_UNKNOWN); m->w = w->id; where = STATUS; } else m->w = -1; /* Not on status line. Adjust position and check for border or pane. */ if (where == NOWHERE) { if (m->statusat == 0 && y > 0) y--; else if (m->statusat > 0 && y >= (u_int)m->statusat) y = m->statusat - 1; TAILQ_FOREACH(wp, &s->curw->window->panes, entry) { if ((wp->xoff + wp->sx == x && wp->yoff <= 1 + y && wp->yoff + wp->sy >= y) || (wp->yoff + wp->sy == y && wp->xoff <= 1 + x && wp->xoff + wp->sx >= x)) break; } if (wp != NULL) where = BORDER; else { wp = window_get_active_at(s->curw->window, x, y); if (wp != NULL) { where = PANE; log_debug("mouse at %u,%u is on pane %%%u", x, y, wp->id); } } if (where == NOWHERE) return (KEYC_UNKNOWN); m->wp = wp->id; m->w = wp->window->id; } else m->wp = -1; /* Stop dragging if needed. */ if (type != DRAG && c->tty.mouse_drag_flag) { if (c->tty.mouse_drag_release != NULL) c->tty.mouse_drag_release(c, m); c->tty.mouse_drag_update = NULL; c->tty.mouse_drag_release = NULL; /* * End a mouse drag by passing a MouseDragEnd key corresponding * to the button that started the drag. */ switch (c->tty.mouse_drag_flag) { case 1: if (where == PANE) key = KEYC_MOUSEDRAGEND1_PANE; if (where == STATUS) key = KEYC_MOUSEDRAGEND1_STATUS; if (where == BORDER) key = KEYC_MOUSEDRAGEND1_BORDER; break; case 2: if (where == PANE) key = KEYC_MOUSEDRAGEND2_PANE; if (where == STATUS) key = KEYC_MOUSEDRAGEND2_STATUS; if (where == BORDER) key = KEYC_MOUSEDRAGEND2_BORDER; break; case 3: if (where == PANE) key = KEYC_MOUSEDRAGEND3_PANE; if (where == STATUS) key = KEYC_MOUSEDRAGEND3_STATUS; if (where == BORDER) key = KEYC_MOUSEDRAGEND3_BORDER; break; default: key = KEYC_MOUSE; break; } c->tty.mouse_drag_flag = 0; return (key); } /* Convert to a key binding. */ key = KEYC_UNKNOWN; switch (type) { case NOTYPE: break; case DRAG: if (c->tty.mouse_drag_update != NULL) c->tty.mouse_drag_update(c, m); else { switch (MOUSE_BUTTONS(b)) { case 0: if (where == PANE) key = KEYC_MOUSEDRAG1_PANE; if (where == STATUS) key = KEYC_MOUSEDRAG1_STATUS; if (where == BORDER) key = KEYC_MOUSEDRAG1_BORDER; break; case 1: if (where == PANE) key = KEYC_MOUSEDRAG2_PANE; if (where == STATUS) key = KEYC_MOUSEDRAG2_STATUS; if (where == BORDER) key = KEYC_MOUSEDRAG2_BORDER; break; case 2: if (where == PANE) key = KEYC_MOUSEDRAG3_PANE; if (where == STATUS) key = KEYC_MOUSEDRAG3_STATUS; if (where == BORDER) key = KEYC_MOUSEDRAG3_BORDER; break; } } /* * Begin a drag by setting the flag to a non-zero value that * corresponds to the mouse button in use. */ c->tty.mouse_drag_flag = MOUSE_BUTTONS(b) + 1; break; case WHEEL: if (MOUSE_BUTTONS(b) == MOUSE_WHEEL_UP) { if (where == PANE) key = KEYC_WHEELUP_PANE; if (where == STATUS) key = KEYC_WHEELUP_STATUS; if (where == BORDER) key = KEYC_WHEELUP_BORDER; } else { if (where == PANE) key = KEYC_WHEELDOWN_PANE; if (where == STATUS) key = KEYC_WHEELDOWN_STATUS; if (where == BORDER) key = KEYC_WHEELDOWN_BORDER; } break; case UP: switch (MOUSE_BUTTONS(b)) { case 0: if (where == PANE) key = KEYC_MOUSEUP1_PANE; if (where == STATUS) key = KEYC_MOUSEUP1_STATUS; if (where == BORDER) key = KEYC_MOUSEUP1_BORDER; break; case 1: if (where == PANE) key = KEYC_MOUSEUP2_PANE; if (where == STATUS) key = KEYC_MOUSEUP2_STATUS; if (where == BORDER) key = KEYC_MOUSEUP2_BORDER; break; case 2: if (where == PANE) key = KEYC_MOUSEUP3_PANE; if (where == STATUS) key = KEYC_MOUSEUP3_STATUS; if (where == BORDER) key = KEYC_MOUSEUP3_BORDER; break; } break; case DOWN: switch (MOUSE_BUTTONS(b)) { case 0: if (where == PANE) key = KEYC_MOUSEDOWN1_PANE; if (where == STATUS) key = KEYC_MOUSEDOWN1_STATUS; if (where == BORDER) key = KEYC_MOUSEDOWN1_BORDER; break; case 1: if (where == PANE) key = KEYC_MOUSEDOWN2_PANE; if (where == STATUS) key = KEYC_MOUSEDOWN2_STATUS; if (where == BORDER) key = KEYC_MOUSEDOWN2_BORDER; break; case 2: if (where == PANE) key = KEYC_MOUSEDOWN3_PANE; if (where == STATUS) key = KEYC_MOUSEDOWN3_STATUS; if (where == BORDER) key = KEYC_MOUSEDOWN3_BORDER; break; } break; } if (key == KEYC_UNKNOWN) return (KEYC_UNKNOWN); /* Apply modifiers if any. */ if (b & MOUSE_MASK_META) key |= KEYC_ESCAPE; if (b & MOUSE_MASK_CTRL) key |= KEYC_CTRL; if (b & MOUSE_MASK_SHIFT) key |= KEYC_SHIFT; return (key); } /* Is this fast enough to probably be a paste? */ int server_client_assume_paste(struct session *s) { struct timeval tv; int t; if ((t = options_get_number(s->options, "assume-paste-time")) == 0) return (0); timersub(&s->activity_time, &s->last_activity_time, &tv); if (tv.tv_sec == 0 && tv.tv_usec < t * 1000) { log_debug("session %s pasting (flag %d)", s->name, !!(s->flags & SESSION_PASTING)); if (s->flags & SESSION_PASTING) return (1); s->flags |= SESSION_PASTING; return (0); } log_debug("session %s not pasting", s->name); s->flags &= ~SESSION_PASTING; return (0); } /* Handle data key input from client. */ void server_client_handle_key(struct client *c, key_code key) { struct mouse_event *m = &c->tty.mouse; struct session *s = c->session; struct window *w; struct window_pane *wp; struct timeval tv; struct key_table *table; struct key_binding bd_find, *bd; int xtimeout; /* Check the client is good to accept input. */ if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0) return; w = s->curw->window; /* Update the activity timer. */ if (gettimeofday(&c->activity_time, NULL) != 0) fatal("gettimeofday failed"); session_update_activity(s, &c->activity_time); /* 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; key = server_client_check_mouse(c); if (key == KEYC_UNKNOWN) return; m->valid = 1; m->key = key; if (!options_get_number(s->options, "mouse")) goto forward; } else m->valid = 0; /* Treat everything as a regular key when pasting is detected. */ if (!KEYC_IS_MOUSE(key) && server_client_assume_paste(s)) goto forward; retry: /* Try to see if there is a key binding in the current table. */ bd_find.key = key; bd = RB_FIND(key_bindings, &c->keytable->key_bindings, &bd_find); if (bd != NULL) { /* * Key was matched in this table. If currently repeating but a * non-repeating binding was found, stop repeating and try * again in the root table. */ if ((c->flags & CLIENT_REPEAT) && !bd->can_repeat) { server_client_set_key_table(c, NULL); c->flags &= ~CLIENT_REPEAT; server_status_client(c); goto retry; } /* * Take a reference to this table to make sure the key binding * doesn't disappear. */ table = c->keytable; table->references++; /* * If this is a repeating key, start the timer. Otherwise reset * the client back to the root table. */ xtimeout = options_get_number(s->options, "repeat-time"); if (xtimeout != 0 && bd->can_repeat) { c->flags |= CLIENT_REPEAT; tv.tv_sec = xtimeout / 1000; tv.tv_usec = (xtimeout % 1000) * 1000L; evtimer_del(&c->repeat_timer); evtimer_add(&c->repeat_timer, &tv); } else { c->flags &= ~CLIENT_REPEAT; server_client_set_key_table(c, NULL); } server_status_client(c); /* Dispatch the key binding. */ key_bindings_dispatch(bd, c, m); key_bindings_unref_table(table); return; } /* * No match in this table. If repeating, switch the client back to the * root table and try again. */ if (c->flags & CLIENT_REPEAT) { server_client_set_key_table(c, NULL); c->flags &= ~CLIENT_REPEAT; server_status_client(c); goto retry; } /* If no match and we're not in the root table, that's it. */ if (strcmp(c->keytable->name, server_client_get_key_table(c)) != 0) { server_client_set_key_table(c, NULL); server_status_client(c); return; } /* * No match, but in the root table. Prefix switches to the prefix table * and everything else is passed through. */ if (key == (key_code)options_get_number(s->options, "prefix") || key == (key_code)options_get_number(s->options, "prefix2")) { server_client_set_key_table(c, "prefix"); server_status_client(c); return; } forward: if (c->flags & CLIENT_READONLY) return; if (KEYC_IS_MOUSE(key)) wp = cmd_mouse_pane(m, NULL, NULL); else wp = w->active; if (wp != NULL) window_pane_key(wp, c, s, key, m); } /* Client functions that need to happen every loop. */ void server_client_loop(void) { struct client *c; struct window *w; struct window_pane *wp; #ifdef TMATE int tmate_should_sync_layout = 0; #endif TAILQ_FOREACH(c, &clients, entry) { server_client_check_exit(c); if (c->session != NULL) { server_client_check_redraw(c); server_client_reset_state(c); } } /* * Any windows will have been redrawn as part of clients, so clear * their flags now. Also check pane focus and resize. */ RB_FOREACH(w, windows, &windows) { #ifdef TMATE if (w->flags & WINDOW_REDRAW) tmate_should_sync_layout = 1; if (w->tmate_last_sync_active_pane != w->active) tmate_should_sync_layout = 1; #endif w->flags &= ~WINDOW_REDRAW; TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->fd != -1) { server_client_check_focus(wp); server_client_check_resize(wp); } wp->flags &= ~PANE_REDRAW; } check_window_name(w); } #ifdef TMATE if (tmate_should_sync_layout) tmate_sync_layout(); #endif } /* Check if pane should be resized. */ void server_client_check_resize(struct window_pane *wp) { struct winsize ws; if (!(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 && errno != ENXIO) #endif fatal("ioctl failed"); } wp->flags &= ~PANE_RESIZE; } /* Check whether pane should be focused. */ void server_client_check_focus(struct window_pane *wp) { struct client *c; int push; /* Are focus events off? */ if (!options_get_number(global_options, "focus-events")) return; /* Do we need to push the focus state? */ push = wp->flags & PANE_FOCUSPUSH; wp->flags &= ~PANE_FOCUSPUSH; /* 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. */ TAILQ_FOREACH(c, &clients, entry) { if (c->session == NULL || !(c->flags & CLIENT_FOCUSED)) continue; if (c->session->flags & SESSION_UNATTACHED) continue; if (c->session->curw->window == wp->window) goto focused; } not_focused: if (push || (wp->flags & PANE_FOCUSED)) bufferevent_write(wp->event, "\033[O", 3); wp->flags &= ~PANE_FOCUSED; return; focused: if (push || !(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; 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); } /* * Set mouse mode if requested. To support dragging, always use button * mode. */ mode = s->mode; if (options_get_number(oo, "mouse")) mode = (mode & ~ALL_MOUSE_MODES) | MODE_MOUSE_BUTTON; /* 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) { server_client_set_key_table(c, NULL); c->flags &= ~CLIENT_REPEAT; server_status_client(c); } } /* Check if client should be exited. */ void server_client_check_exit(struct client *c) { 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; proc_send(c->peer, MSG_EXIT, -1, &c->retval, sizeof c->retval); c->flags &= ~CLIENT_EXIT; } /* Check for client redraws. */ void server_client_check_redraw(struct client *c) { struct session *s = c->session; struct tty *tty = &c->tty; struct window_pane *wp; int flags, redraw; if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED)) return; 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; } flags = tty->flags & (TTY_FREEZE|TTY_NOCURSOR); tty->flags = (tty->flags & ~TTY_FREEZE) | TTY_NOCURSOR; if (c->flags & CLIENT_REDRAW) { tty_update_mode(tty, tty->mode, NULL); screen_redraw_screen(c, 1, 1, 1); c->flags &= ~(CLIENT_STATUS|CLIENT_BORDERS); } else if (c->flags & CLIENT_REDRAWWINDOW) { tty_update_mode(tty, tty->mode, NULL); 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) { tty_update_mode(tty, tty->mode, NULL); screen_redraw_pane(c, wp); } } } if (c->flags & CLIENT_BORDERS) { tty_update_mode(tty, tty->mode, NULL); screen_redraw_screen(c, 0, 0, 1); } if (c->flags & CLIENT_STATUS) { tty_update_mode(tty, tty->mode, NULL); screen_redraw_screen(c, 0, 1, 0); } tty->flags = (tty->flags & ~(TTY_FREEZE|TTY_NOCURSOR)) | flags; tty_update_mode(tty, tty->mode, NULL); c->flags &= ~(CLIENT_REDRAW|CLIENT_BORDERS|CLIENT_STATUS| CLIENT_STATUSFORCE); } /* Set client title. */ void server_client_set_title(struct client *c) { struct session *s = c->session; const char *template; char *title; struct format_tree *ft; template = options_get_string(s->options, "set-titles-string"); ft = format_create(NULL, 0); format_defaults(ft, c, NULL, NULL, NULL); title = format_expand_time(ft, template, time(NULL)); if (c->title == NULL || strcmp(title, c->title) != 0) { free(c->title); c->title = xstrdup(title); tty_set_title(&c->tty, c->title); } free(title); format_free(ft); } /* Dispatch message from client. */ void server_client_dispatch(struct imsg *imsg, void *arg) { struct client *c = arg; struct msg_stdin_data stdindata; const char *data; ssize_t datalen; struct session *s; if (c->flags & CLIENT_DEAD) return; if (imsg == NULL) { server_client_lost(c); return; } data = imsg->data; datalen = imsg->hdr.len - IMSG_HEADER_SIZE; switch (imsg->hdr.type) { case MSG_IDENTIFY_FLAGS: case MSG_IDENTIFY_TERM: case MSG_IDENTIFY_TTYNAME: case MSG_IDENTIFY_CWD: case MSG_IDENTIFY_STDIN: case MSG_IDENTIFY_ENVIRON: case MSG_IDENTIFY_CLIENTPID: case MSG_IDENTIFY_DONE: server_client_dispatch_identify(c, imsg); break; case MSG_COMMAND: server_client_dispatch_command(c, imsg); break; case MSG_STDIN: if (datalen != sizeof stdindata) fatalx("bad MSG_STDIN size"); memcpy(&stdindata, data, sizeof stdindata); if (c->stdin_callback == NULL) break; if (stdindata.size <= 0) c->stdin_closed = 1; else { evbuffer_add(c->stdin_data, stdindata.data, stdindata.size); } c->stdin_callback(c, c->stdin_closed, c->stdin_callback_data); break; case MSG_RESIZE: if (datalen != 0) fatalx("bad MSG_RESIZE size"); if (c->flags & CLIENT_CONTROL) break; if (tty_resize(&c->tty)) { recalculate_sizes(); server_redraw_client(c); } if (c->session != NULL) hooks_run(c->session->hooks, c, NULL, "client-resized"); break; case MSG_EXITING: if (datalen != 0) fatalx("bad MSG_EXITING size"); c->session = NULL; tty_close(&c->tty); proc_send(c->peer, MSG_EXITED, -1, NULL, 0); break; case MSG_WAKEUP: case MSG_UNLOCK: if (datalen != 0) fatalx("bad MSG_WAKEUP size"); if (!(c->flags & CLIENT_SUSPENDED)) break; c->flags &= ~CLIENT_SUSPENDED; if (c->tty.fd == -1) /* exited in the meantime */ break; s = c->session; if (gettimeofday(&c->activity_time, NULL) != 0) fatal("gettimeofday failed"); if (s != NULL) session_update_activity(s, &c->activity_time); tty_start_tty(&c->tty); server_redraw_client(c); recalculate_sizes(); break; case MSG_SHELL: if (datalen != 0) fatalx("bad MSG_SHELL size"); server_client_dispatch_shell(c); break; } } /* Handle command message. */ void server_client_dispatch_command(struct client *c, struct imsg *imsg) { struct msg_command_data data; char *buf; size_t len; struct cmd_list *cmdlist = NULL; int argc; char **argv, *cause; if (imsg->hdr.len - IMSG_HEADER_SIZE < sizeof data) fatalx("bad MSG_COMMAND size"); memcpy(&data, imsg->data, sizeof data); buf = (char *)imsg->data + sizeof data; len = imsg->hdr.len - IMSG_HEADER_SIZE - sizeof data; if (len > 0 && buf[len - 1] != '\0') fatalx("bad MSG_COMMAND string"); argc = data.argc; if (cmd_unpack_argv(buf, len, argc, &argv) != 0) { 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); if (c != cfg_client || cfg_finished) cmdq_run(c->cmdq, cmdlist, NULL); else cmdq_append(c->cmdq, cmdlist, NULL); cmd_list_free(cmdlist); return; error: if (cmdlist != NULL) cmd_list_free(cmdlist); c->flags |= CLIENT_EXIT; } /* Handle identify message. */ void server_client_dispatch_identify(struct client *c, struct imsg *imsg) { const char *data, *home; size_t datalen; int flags; if (c->flags & CLIENT_IDENTIFIED) fatalx("out-of-order identify message"); data = imsg->data; datalen = imsg->hdr.len - IMSG_HEADER_SIZE; switch (imsg->hdr.type) { case MSG_IDENTIFY_FLAGS: if (datalen != sizeof flags) fatalx("bad MSG_IDENTIFY_FLAGS size"); memcpy(&flags, data, sizeof flags); c->flags |= flags; log_debug("client %p IDENTIFY_FLAGS %#x", c, flags); break; case MSG_IDENTIFY_TERM: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_TERM string"); c->term = xstrdup(data); log_debug("client %p IDENTIFY_TERM %s", c, data); break; case MSG_IDENTIFY_TTYNAME: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_TTYNAME string"); c->ttyname = xstrdup(data); log_debug("client %p IDENTIFY_TTYNAME %s", c, data); break; case MSG_IDENTIFY_CWD: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_CWD string"); if (access(data, X_OK) == 0) c->cwd = xstrdup(data); else if ((home = find_home()) != NULL) c->cwd = xstrdup(home); else c->cwd = xstrdup("/"); log_debug("client %p IDENTIFY_CWD %s", c, data); break; case MSG_IDENTIFY_STDIN: if (datalen != 0) fatalx("bad MSG_IDENTIFY_STDIN size"); c->fd = imsg->fd; log_debug("client %p IDENTIFY_STDIN %d", c, imsg->fd); break; case MSG_IDENTIFY_ENVIRON: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_ENVIRON string"); if (strchr(data, '=') != NULL) environ_put(c->environ, data); log_debug("client %p IDENTIFY_ENVIRON %s", c, data); break; case MSG_IDENTIFY_CLIENTPID: if (datalen != sizeof c->pid) fatalx("bad MSG_IDENTIFY_CLIENTPID size"); memcpy(&c->pid, data, sizeof c->pid); log_debug("client %p IDENTIFY_CLIENTPID %ld", c, (long)c->pid); break; default: break; } if (imsg->hdr.type != MSG_IDENTIFY_DONE) return; c->flags |= CLIENT_IDENTIFIED; #ifdef __CYGWIN__ c->fd = open(c->ttyname, O_RDWR|O_NOCTTY); #endif if (c->flags & CLIENT_CONTROL) { c->stdin_callback = control_callback; evbuffer_free(c->stderr_data); c->stderr_data = c->stdout_data; if (c->flags & CLIENT_CONTROLCONTROL) evbuffer_add_printf(c->stdout_data, "\033P1000p"); proc_send(c->peer, MSG_STDIN, -1, NULL, 0); c->tty.fd = -1; close(c->fd); c->fd = -1; return; } if (c->fd == -1) return; if (tty_init(&c->tty, c, c->fd, c->term) != 0) { close(c->fd); c->fd = -1; return; } if (c->flags & CLIENT_UTF8) c->tty.flags |= TTY_UTF8; if (c->flags & CLIENT_256COLOURS) c->tty.term_flags |= TERM_256COLOURS; tty_resize(&c->tty); if (!(c->flags & CLIENT_CONTROL)) c->flags |= CLIENT_TERMINAL; } /* Handle shell message. */ void server_client_dispatch_shell(struct client *c) { const char *shell; shell = options_get_string(global_s_options, "default-shell"); if (*shell == '\0' || areshell(shell)) shell = _PATH_BSHELL; proc_send_s(c->peer, MSG_SHELL, shell); proc_kill_peer(c->peer); } /* Event callback to push more stdout data if any left. */ static void server_client_stdout_cb(__unused int fd, __unused short events, void *arg) { struct client *c = arg; if (~c->flags & CLIENT_DEAD) server_client_push_stdout(c); server_client_unref(c); } /* Push stdout to client if possible. */ void server_client_push_stdout(struct client *c) { struct msg_stdout_data data; size_t sent, left; left = EVBUFFER_LENGTH(c->stdout_data); while (left != 0) { sent = left; if (sent > sizeof data.data) sent = sizeof data.data; memcpy(data.data, EVBUFFER_DATA(c->stdout_data), sent); data.size = sent; if (proc_send(c->peer, MSG_STDOUT, -1, &data, sizeof data) != 0) break; evbuffer_drain(c->stdout_data, sent); left = EVBUFFER_LENGTH(c->stdout_data); log_debug("%s: client %p, sent %zu, left %zu", __func__, c, sent, left); } if (left != 0) { c->references++; event_once(-1, EV_TIMEOUT, server_client_stdout_cb, c, NULL); log_debug("%s: client %p, queued", __func__, c); } } /* Event callback to push more stderr data if any left. */ static void server_client_stderr_cb(__unused int fd, __unused short events, void *arg) { struct client *c = arg; if (~c->flags & CLIENT_DEAD) server_client_push_stderr(c); server_client_unref(c); } /* Push stderr to client if possible. */ void server_client_push_stderr(struct client *c) { struct msg_stderr_data data; size_t sent, left; if (c->stderr_data == c->stdout_data) { server_client_push_stdout(c); return; } left = EVBUFFER_LENGTH(c->stderr_data); while (left != 0) { sent = left; if (sent > sizeof data.data) sent = sizeof data.data; memcpy(data.data, EVBUFFER_DATA(c->stderr_data), sent); data.size = sent; if (proc_send(c->peer, MSG_STDERR, -1, &data, sizeof data) != 0) break; evbuffer_drain(c->stderr_data, sent); left = EVBUFFER_LENGTH(c->stderr_data); log_debug("%s: client %p, sent %zu, left %zu", __func__, c, sent, left); } if (left != 0) { c->references++; event_once(-1, EV_TIMEOUT, server_client_stderr_cb, c, NULL); log_debug("%s: client %p, queued", __func__, c); } } tmate-2.4.0/server-fn.c000066400000000000000000000251161356407164200147170ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "tmux.h" struct session *server_next_session(struct session *); void server_callback_identify(int, short, void *); void server_fill_environ(struct session *s, struct environ *env) { char *term; u_int idx; long pid; if (s != NULL) { term = options_get_string(global_options, "default-terminal"); environ_set(env, "TERM", "%s", term); idx = s->id; } else idx = (u_int)-1; pid = getpid(); environ_set(env, "TMUX", "%s,%ld,%u", socket_path, pid, idx); } 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; TAILQ_FOREACH(c, &clients, entry) { if (c->session == s) server_redraw_client(c); } } void server_redraw_session_group(struct session *s) { struct session_group *sg; if ((sg = session_group_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; TAILQ_FOREACH(c, &clients, entry) { if (c->session == s) server_status_client(c); } } void server_status_session_group(struct session *s) { struct session_group *sg; if ((sg = session_group_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; TAILQ_FOREACH(c, &clients, entry) { if (c->session != NULL && c->session->curw->window == w) server_redraw_client(c); } w->flags |= WINDOW_REDRAW; } void server_redraw_window_borders(struct window *w) { struct client *c; TAILQ_FOREACH(c, &clients, entry) { if (c->session != NULL && c->session->curw->window == w) c->flags |= CLIENT_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)) server_status_session(s); } } void server_lock(void) { struct client *c; TAILQ_FOREACH(c, &clients, entry) { if (c->session != NULL) server_lock_client(c); } } void server_lock_session(struct session *s) { struct client *c; TAILQ_FOREACH(c, &clients, entry) { if (c->session == s) server_lock_client(c); } } void server_lock_client(struct client *c) { const char *cmd; if (c->flags & CLIENT_CONTROL) return; if (c->flags & CLIENT_SUSPENDED) return; cmd = options_get_string(c->session->options, "lock-command"); if (strlen(cmd) + 1 > MAX_IMSGSIZE - IMSG_HEADER_SIZE) return; tty_stop_tty(&c->tty); tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_SMCUP)); tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_CLEAR)); tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_E3)); c->flags |= CLIENT_SUSPENDED; proc_send_s(c->peer, MSG_LOCK, cmd); } void server_kill_window(struct window *w) { struct session *s, *next_s, *target_s; struct session_group *sg; struct winlink *wl; next_s = RB_MIN(sessions, &sessions); while (next_s != NULL) { s = next_s; next_s = RB_NEXT(sessions, &sessions, s); if (!session_has(s, w)) continue; server_unzoom_window(w); while ((wl = winlink_find_by_window(&s->windows, w)) != NULL) { if (session_detach(s, wl)) { server_destroy_session_group(s); break; } else server_redraw_session_group(s); } if (options_get_number(s->options, "renumber-windows")) { if ((sg = session_group_find(s)) != NULL) { TAILQ_FOREACH(target_s, &sg->sessions, gentry) session_renumber_windows(target_s); } else session_renumber_windows(s); } } recalculate_sizes(); } int server_link_window(struct session *src, struct winlink *srcwl, struct session *dst, int dstidx, int killflag, int selectflag, char **cause) { struct winlink *dstwl; struct session_group *srcsg, *dstsg; srcsg = session_group_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, int hooks) { struct window *w = wp->window; int old_fd; struct screen_write_ctx ctx; struct grid_cell gc; struct cmd_find_state fs; old_fd = wp->fd; if (wp->fd != -1) { #ifdef HAVE_UTEMPTER utempter_remove_record(wp->fd); #endif 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; if (hooks && cmd_find_from_pane(&fs, wp) == 0) hooks_run(hooks_get(fs.s), NULL, &fs, "pane-died"); return; } server_unzoom_window(w); layout_close_pane(wp); window_remove_pane(w, wp); if (hooks && cmd_find_from_window(&fs, w) == 0) hooks_run(hooks_get(fs.s), NULL, &fs, "pane-exited"); 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; struct session *s1; if ((sg = session_group_find(s)) == NULL) server_destroy_session(s); else { TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) { server_destroy_session(s); session_destroy(s); } } } struct session * server_next_session(struct session *s) { struct session *s_loop, *s_out; s_out = NULL; RB_FOREACH(s_loop, sessions, &sessions) { if (s_loop == s) continue; if (s_out == NULL || timercmp(&s_loop->activity_time, &s_out->activity_time, <)) s_out = s_loop; } return (s_out); } void server_destroy_session(struct session *s) { struct client *c; struct session *s_new; if (!options_get_number(s->options, "detach-on-destroy")) s_new = server_next_session(s); else s_new = NULL; TAILQ_FOREACH(c, &clients, entry) { if (c->session != s) continue; if (s_new == NULL) { c->session = NULL; c->flags |= CLIENT_EXIT; } else { c->last_session = NULL; c->session = s_new; server_client_set_key_table(c, NULL); status_timer_start(c); notify_attached_session_changed(c); session_update_activity(s_new, NULL); gettimeofday(&s_new->last_attached_time, NULL); server_redraw_client(c); alerts_check_session(s_new); } } recalculate_sizes(); } void server_check_unattached(void) { struct session *s; /* * If any sessions are no longer attached and have destroy-unattached * set, collect them. */ RB_FOREACH(s, sessions, &sessions) { if (!(s->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); } /* 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); proc_send(c->peer, MSG_STDIN, -1, NULL, 0); return (0); } void server_unzoom_window(struct window *w) { if (window_unzoom(w) == 0) { server_redraw_window(w); server_status_window(w); } } tmate-2.4.0/server.c000066400000000000000000000226131356407164200143150ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tmux.h" #include "tmate.h" /* * Main server functions. */ struct clients clients; struct tmuxproc *server_proc; int server_fd; int server_exit; struct event server_ev_accept; struct cmd_find_state marked_pane; int server_create_socket(void); int server_loop(void); int server_should_exit(void); void server_send_exit(void); void server_accept(int, short, void *); void server_signal(int); void server_child_signal(void); void server_child_exited(pid_t, int); void server_child_stopped(pid_t, int); /* Set marked pane. */ void server_set_marked(struct session *s, struct winlink *wl, struct window_pane *wp) { cmd_find_clear_state(&marked_pane, NULL, 0); marked_pane.s = s; marked_pane.wl = wl; marked_pane.w = wl->window; marked_pane.wp = wp; } /* Clear marked pane. */ void server_clear_marked(void) { cmd_find_clear_state(&marked_pane, NULL, 0); } /* Is this the marked pane? */ int server_is_marked(struct session *s, struct winlink *wl, struct window_pane *wp) { if (s == NULL || wl == NULL || wp == NULL) return (0); if (marked_pane.s != s || marked_pane.wl != wl) return (0); if (marked_pane.wp != wp) return (0); return (server_check_marked()); } /* Check if the marked pane is still valid. */ int server_check_marked(void) { return (cmd_find_valid_state(&marked_pane)); } /* Create server socket. */ 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; return (-1); } unlink(sa.sun_path); if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) return (-1); mask = umask(S_IXUSR|S_IXGRP|S_IRWXO); if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) == -1) return (-1); umask(mask); if (listen(fd, 16) == -1) return (-1); setblocking(fd, 0); return (fd); } #ifdef TMATE static void tmate_set_editor_mode(void) { switch (options_get_number(global_s_options, "status-keys")) { case MODEKEY_EMACS: tmate_exec_cmd_args(4, (const char *[]){"set-option", "-g", "status-keys", "emacs"}); break; case MODEKEY_VI: tmate_exec_cmd_args(4, (const char *[]){"set-option", "-g", "status-keys", "vi"}); break; } switch (options_get_number(global_w_options, "mode-keys")) { case MODEKEY_EMACS: tmate_exec_cmd_args(4, (const char *[]){"set-window-option", "-g", "status-keys", "emacs"}); break; case MODEKEY_VI: tmate_exec_cmd_args(4, (const char *[]){"set-window-option", "-g", "status-keys", "vi"}); break; } } #endif /* Fork new server. */ int server_start(struct event_base *base, int lockfd, char *lockfile) { int pair[2]; if (!tmate_foreground) if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) fatal("socketpair failed"); server_proc = proc_start("server", base, 1, server_signal); if (server_proc == NULL) { close(pair[1]); return (pair[0]); } close(pair[0]); if (log_get_level() > 3) tty_create_log(); #ifdef __OpenBSD__ if (pledge("stdio rpath wpath cpath fattr unix getpw recvfd proc exec " "tty ps", NULL) != 0) fatal("pledge failed"); #endif RB_INIT(&windows); RB_INIT(&all_window_panes); TAILQ_INIT(&clients); RB_INIT(&sessions); TAILQ_INIT(&session_groups); mode_key_init_trees(); #ifdef TMATE tmate_session_init(base); #endif key_bindings_init(); gettimeofday(&start_time, NULL); server_fd = server_create_socket(); if (server_fd == -1) fatal("couldn't create socket"); server_update_socket(); if (!tmate_foreground) server_client_create(pair[1]); if (lockfd >= 0) { unlink(lockfile); free(lockfile); close(lockfd); } #ifdef TMATE tmate_set_editor_mode(); #endif start_cfg(); status_prompt_load_history(); server_add_accept(0); if (tmate_foreground) run_initial_client_cmd(); proc_loop(server_proc, server_loop); status_prompt_save_history(); #ifdef TMATE unlink(socket_path); #endif exit(0); } /* Server loop callback. */ int server_loop(void) { struct client *c; server_client_loop(); if (!options_get_number(global_options, "exit-unattached")) { if (!RB_EMPTY(&sessions)) return (0); } TAILQ_FOREACH(c, &clients, entry) { if (c->session != NULL) return (0); } /* * No attached clients therefore want to exit - flush any waiting * clients but don't actually exit until they've gone. */ cmd_wait_for_flush(); if (!TAILQ_EMPTY(&clients)) return (0); return (1); } /* Exit the server by killing all clients and windows. */ void server_send_exit(void) { struct client *c, *c1; struct session *s, *s1; cmd_wait_for_flush(); TAILQ_FOREACH_SAFE(c, &clients, entry, c1) { if (c->flags & CLIENT_SUSPENDED) server_client_lost(c); else proc_send(c->peer, MSG_SHUTDOWN, -1, NULL, 0); c->session = NULL; } RB_FOREACH_SAFE(s, sessions, &sessions, s1) session_destroy(s); } /* 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(int fd, short events, __unused void *data) { struct sockaddr_storage sa; socklen_t slen = sizeof sa; int newfd; server_add_accept(0); if (!(events & EV_READ)) return; newfd = accept(fd, (struct sockaddr *) &sa, &slen); if (newfd == -1) { if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED) return; if (errno == ENFILE || errno == EMFILE) { /* Delete and don't try again for 1 second. */ server_add_accept(1); return; } fatal("accept failed"); } if (server_exit) { close(newfd); return; } server_client_create(newfd); } /* * Add accept event. If timeout is nonzero, add as a timeout instead of a read * event - used to backoff when running out of file descriptors. */ void server_add_accept(int timeout) { struct timeval tv = { timeout, 0 }; if (event_initialized(&server_ev_accept)) event_del(&server_ev_accept); if (timeout == 0) { event_set(&server_ev_accept, server_fd, EV_READ, server_accept, NULL); event_add(&server_ev_accept, NULL); } else { event_set(&server_ev_accept, server_fd, EV_TIMEOUT, server_accept, NULL); event_add(&server_ev_accept, &tv); } } /* Signal handler. */ void server_signal(int sig) { int fd; switch (sig) { case SIGINT: case SIGTERM: server_exit = 1; server_send_exit(); break; case SIGCHLD: server_child_signal(); break; case SIGUSR1: event_del(&server_ev_accept); fd = server_create_socket(); if (fd != -1) { close(server_fd); server_fd = fd; server_update_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, *w1; struct window_pane *wp; struct job *job; RB_FOREACH_SAFE(w, windows, &windows, w1) { TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->pid == pid) { wp->status = status; server_destroy_pane(wp, 1); 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; if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU) return; RB_FOREACH(w, windows, &windows) { TAILQ_FOREACH(wp, &w->panes, entry) { if (wp->pid == pid) { if (killpg(pid, SIGCONT) != 0) kill(pid, SIGCONT); } } } } tmate-2.4.0/session.c000066400000000000000000000436211356407164200144740ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "tmux.h" #include "tmate.h" struct sessions sessions; u_int next_session_id; struct session_groups session_groups; void session_free(int, short, void *); void session_lock_timer(int, short, void *); 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 parsed from a string. */ struct session * session_find_by_id_str(const char *s) { const char *errstr; u_int id; if (*s != '$') return (NULL); id = strtonum(s + 1, 0, UINT_MAX, &errstr); if (errstr != NULL) return (NULL); return (session_find_by_id(id)); } /* Find session by id. */ struct session * session_find_by_id(u_int id) { struct session *s; RB_FOREACH(s, sessions, &sessions) { if (s->id == id) return (s); } return (NULL); } /* Create a new session. */ struct session * session_create(const char *name, int argc, char **argv, const char *path, const char *cwd, struct environ *env, struct termios *tio, int idx, u_int sx, u_int sy, char **cause) { struct session *s; struct winlink *wl; #ifdef TMATE if (next_session_id != 0) { xasprintf(cause, "multi sessions is not supported with tmate"); return NULL; } #endif s = xcalloc(1, sizeof *s); s->references = 1; s->flags = 0; s->cwd = xstrdup(cwd); s->curw = NULL; TAILQ_INIT(&s->lastw); RB_INIT(&s->windows); s->environ = environ_create(); if (env != NULL) environ_copy(env, s->environ); s->options = options_create(global_s_options); s->hooks = hooks_create(global_hooks); 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); log_debug("new session %s $%u", s->name, s->id); if (gettimeofday(&s->creation_time, NULL) != 0) fatal("gettimeofday failed"); session_update_activity(s, &s->creation_time); if (argc >= 0) { wl = session_new(s, NULL, argc, argv, path, cwd, idx, cause); if (wl == 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); } /* Remove a reference from a session. */ void session_unref(struct session *s) { log_debug("session %s has %d references", s->name, s->references); s->references--; if (s->references == 0) event_once(-1, EV_TIMEOUT, session_free, s, NULL); } /* Free session. */ void session_free(__unused int fd, __unused short events, void *arg) { struct session *s = arg; log_debug("session %s freed (%d references)", s->name, s->references); if (s->references == 0) { environ_free(s->environ); options_free(s->options); hooks_free(s->hooks); free(s->name); free(s); } } static void maybe_restart_session(void) { int fg_restart = options_get_number(global_options, "tmate-foreground-restart"); if (!fg_restart) return; /* * throttle restarts. This is a blocking sleep. It's * simpler than using a timer, but fairly harmless * from a blocking perspective. */ usleep(500*1000); next_session_id = 0; run_initial_client_cmd(); tmate_info("Session shell restarted"); struct session *s; s = RB_MIN(sessions, &sessions); if (!s) return; struct window_pane *wp; wp = s->curw->window->active; window_pane_set_mode(wp, &window_copy_mode); window_copy_init_for_output(wp); window_copy_add(wp, "%s", "Session shell restarted"); window_copy_add(wp, "%s", "Note: press the following sequence to disconnect from SSH: ~."); } /* 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); if (event_initialized(&s->lock_timer)) event_del(&s->lock_timer); session_group_remove(s); while (!TAILQ_EMPTY(&s->lastw)) winlink_stack_remove(&s->lastw, TAILQ_FIRST(&s->lastw)); while (!RB_EMPTY(&s->windows)) { wl = RB_ROOT(&s->windows); notify_window_unlinked(s, wl->window); winlink_remove(&s->windows, wl); } free((void *)s->cwd); session_unref(s); #ifdef TMATE if (tmate_foreground && !server_exit) { maybe_restart_session(); } else { tmate_info("Session closed"); tmate_write_fin(); } #endif } /* Check a session name is valid: not empty and no colons or periods. */ int session_check_name(const char *name) { return (*name != '\0' && name[strcspn(name, ":.")] == '\0'); } /* Lock session if it has timed out. */ void session_lock_timer(__unused int fd, __unused short events, void *arg) { struct session *s = arg; if (s->flags & SESSION_UNATTACHED) return; log_debug("session %s locked, activity time %lld", s->name, (long long)s->activity_time.tv_sec); server_lock_session(s); recalculate_sizes(); } /* Update activity time. */ void session_update_activity(struct session *s, struct timeval *from) { struct timeval *last = &s->last_activity_time; struct timeval tv; memcpy(last, &s->activity_time, sizeof *last); if (from == NULL) gettimeofday(&s->activity_time, NULL); else memcpy(&s->activity_time, from, sizeof s->activity_time); log_debug("session %s activity %lld.%06d (last %lld.%06d)", s->name, (long long)s->activity_time.tv_sec, (int)s->activity_time.tv_usec, (long long)last->tv_sec, (int)last->tv_usec); if (evtimer_initialized(&s->lock_timer)) evtimer_del(&s->lock_timer); else evtimer_set(&s->lock_timer, session_lock_timer, s); if (~s->flags & SESSION_UNATTACHED) { timerclear(&tv); tv.tv_sec = options_get_number(s->options, "lock-after-time"); if (tv.tv_sec != 0) evtimer_add(&s->lock_timer, &tv); } } /* Find the next usable session. */ struct session * session_next_session(struct session *s) { struct session *s2; if (RB_EMPTY(&sessions) || !session_alive(s)) return (NULL); s2 = RB_NEXT(sessions, &sessions, s); if (s2 == NULL) s2 = RB_MIN(sessions, &sessions); if (s2 == s) return (NULL); return (s2); } /* Find the previous usable session. */ struct session * session_previous_session(struct session *s) { struct session *s2; if (RB_EMPTY(&sessions) || !session_alive(s)) return (NULL); s2 = RB_PREV(sessions, &sessions, s); if (s2 == NULL) s2 = RB_MAX(sessions, &sessions); if (s2 == s) return (NULL); return (s2); } /* Create a new window on a session. */ struct winlink * session_new(struct session *s, const char *name, int argc, char **argv, const char *path, 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); } env = environ_create(); 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, argc, argv, path, 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. */ int session_has(struct session *s, struct window *w) { struct winlink *wl; RB_FOREACH(wl, winlinks, &s->windows) { if (wl->window == w) return (1); } return (0); } /* * Return 1 if a window is linked outside this session (not including session * groups). The window must be in this session! */ int session_is_linked(struct session *s, struct window *w) { struct session_group *sg; if ((sg = session_group_find(s)) != NULL) return (w->references != session_group_count(sg)); return (w->references != 1); } 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 window_update_activity(wl->window); 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"); for(;;); } /* * 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); } } /* Count number of sessions in session group. */ u_int session_group_count(struct session_group *sg) { struct session *s; u_int n; n = 0; TAILQ_FOREACH(s, &sg->sessions, gentry) n++; return (n); } /* Synchronize a session to its session group. */ void session_group_synchronize_to(struct session *s) { struct session_group *sg; struct session *target; if ((sg = session_group_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); wl2 = winlink_find_by_window_id(&s->windows, wl->window->id); if (wl2 == 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_window(&s->windows, wl->window); if (wl_new != NULL) TAILQ_INSERT_TAIL(&s->lastw, wl_new, sentry); } /* Set the current window. */ s->curw = winlink_find_by_index(&s->windows, new_curw_idx); /* Free the old winlinks (reducing window references too). */ RB_FOREACH_SAFE(wl, winlinks, &old_wins, wl1) winlink_remove(&old_wins, wl); } tmate-2.4.0/signal.c000066400000000000000000000070131356407164200142610ustar00rootroot00000000000000/* $OpenBSD$ */ /* * 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 #include "tmux.h" struct event ev_sigint; 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, void *), void *arg) { struct sigaction sigact; memset(&sigact, 0, sizeof sigact); sigemptyset(&sigact.sa_mask); sigact.sa_flags = SA_RESTART; sigact.sa_handler = SIG_IGN; #ifndef TMATE if (sigaction(SIGINT, &sigact, NULL) != 0) fatal("sigaction failed"); #endif if (sigaction(SIGPIPE, &sigact, NULL) != 0) fatal("sigaction failed"); if (sigaction(SIGUSR2, &sigact, NULL) != 0) fatal("sigaction failed"); #ifndef TMATE if (sigaction(SIGTSTP, &sigact, NULL) != 0) fatal("sigaction failed"); #endif #ifdef TMATE signal_set(&ev_sigint, SIGINT, handler, arg); signal_add(&ev_sigint, NULL); #endif signal_set(&ev_sighup, SIGHUP, handler, arg); signal_add(&ev_sighup, NULL); signal_set(&ev_sigchld, SIGCHLD, handler, arg); signal_add(&ev_sigchld, NULL); signal_set(&ev_sigcont, SIGCONT, handler, arg); signal_add(&ev_sigcont, NULL); signal_set(&ev_sigterm, SIGTERM, handler, arg); signal_add(&ev_sigterm, NULL); signal_set(&ev_sigusr1, SIGUSR1, handler, arg); signal_add(&ev_sigusr1, NULL); signal_set(&ev_sigwinch, SIGWINCH, handler, arg); 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; #ifndef TMATE if (sigaction(SIGINT, &sigact, NULL) != 0) fatal("sigaction failed"); #endif if (sigaction(SIGPIPE, &sigact, NULL) != 0) fatal("sigaction failed"); if (sigaction(SIGUSR2, &sigact, NULL) != 0) fatal("sigaction failed"); #ifndef TMATE if (sigaction(SIGTSTP, &sigact, NULL) != 0) fatal("sigaction failed"); #endif if (after_fork) { #ifdef TMATE if (sigaction(SIGINT, &sigact, NULL) != 0) fatal("sigaction failed"); #endif 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_sigint); 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-2.4.0/status.c000066400000000000000000001025621356407164200143340ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include "tmux.h" #include "tmate.h" char *status_redraw_get_left(struct client *, time_t, struct grid_cell *, size_t *); char *status_redraw_get_right(struct client *, time_t, struct grid_cell *, size_t *); char *status_print(struct client *, struct winlink *, time_t, struct grid_cell *); char *status_replace(struct client *, struct winlink *, const char *, time_t); void status_message_callback(int, short, void *); void status_timer_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 *); const char **status_prompt_complete_list(u_int *, const char *); char *status_prompt_complete_prefix(const char **, u_int); char *status_prompt_complete(struct session *, const char *); char *status_prompt_find_history_file(void); /* Status prompt history. */ #define PROMPT_HISTORY 100 char **status_prompt_hlist; u_int status_prompt_hsize; /* Find the history file to load/save from/to. */ char * status_prompt_find_history_file(void) { const char *home, *history_file; char *path; history_file = options_get_string(global_options, "history-file"); if (*history_file == '\0') return (NULL); if (*history_file == '/') return (xstrdup(history_file)); if (history_file[0] != '~' || history_file[1] != '/') return (NULL); if ((home = find_home()) == NULL) return (NULL); xasprintf(&path, "%s%s", home, history_file + 1); return (path); } /* Load status prompt history from file. */ void status_prompt_load_history(void) { FILE *f; char *history_file, *line, *tmp; size_t length; if ((history_file = status_prompt_find_history_file()) == NULL) return; log_debug("loading history from %s", history_file); f = fopen(history_file, "r"); if (f == NULL) { log_debug("%s: %s", history_file, strerror(errno)); free(history_file); return; } free(history_file); for (;;) { if ((line = fgetln(f, &length)) == NULL) break; if (length > 0) { if (line[length - 1] == '\n') { line[length - 1] = '\0'; status_prompt_add_history(line); } else { tmp = xmalloc(length + 1); memcpy(tmp, line, length); tmp[length] = '\0'; status_prompt_add_history(tmp); free(tmp); } } } fclose(f); } /* Save status prompt history to file. */ void status_prompt_save_history(void) { FILE *f; u_int i; char *history_file; if ((history_file = status_prompt_find_history_file()) == NULL) return; log_debug("saving history to %s", history_file); f = fopen(history_file, "w"); if (f == NULL) { log_debug("%s: %s", history_file, strerror(errno)); free(history_file); return; } free(history_file); for (i = 0; i < status_prompt_hsize; i++) { fputs(status_prompt_hlist[i], f); fputc('\n', f); } fclose(f); } /* Status timer callback. */ void status_timer_callback(__unused int fd, __unused short events, void *arg) { struct client *c = arg; struct session *s = c->session; struct timeval tv; evtimer_del(&c->status_timer); if (s == NULL) return; if (c->message_string == NULL && c->prompt_string == NULL) c->flags |= CLIENT_STATUS; timerclear(&tv); tv.tv_sec = options_get_number(s->options, "status-interval"); if (tv.tv_sec != 0) evtimer_add(&c->status_timer, &tv); log_debug("client %p, status interval %d", c, (int)tv.tv_sec); } /* Start status timer for client. */ void status_timer_start(struct client *c) { struct session *s = c->session; if (event_initialized(&c->status_timer)) evtimer_del(&c->status_timer); else evtimer_set(&c->status_timer, status_timer_callback, c); if (s != NULL && options_get_number(s->options, "status")) status_timer_callback(-1, 0, c); } /* Start status timer for all clients. */ void status_timer_start_all(void) { struct client *c; TAILQ_FOREACH(c, &clients, entry) status_timer_start(c); } /* 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, struct grid_cell *gc, size_t *size) { struct session *s = c->session; const char *template; char *left; size_t leftlen; style_apply_update(gc, s->options, "status-left-style"); template = options_get_string(s->options, "status-left"); left = status_replace(c, NULL, template, t); *size = options_get_number(s->options, "status-left-length"); leftlen = screen_write_cstrlen("%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, struct grid_cell *gc, size_t *size) { struct session *s = c->session; const char *template; char *right; size_t rightlen; style_apply_update(gc, s->options, "status-right-style"); template = options_get_string(s->options, "status-right"); right = status_replace(c, NULL, template, t); *size = options_get_number(s->options, "status-right-length"); rightlen = screen_write_cstrlen("%s", right); if (rightlen < *size) *size = rightlen; return (right); } /* Get window at window list position. */ struct window * status_get_window_at(struct client *c, u_int x) { struct session *s = c->session; struct winlink *wl; struct options *oo; size_t len; x += c->wlmouse; RB_FOREACH(wl, winlinks, &s->windows) { oo = wl->window->options; len = strlen(options_get_string(oo, "window-status-separator")); if (x < wl->status_width) return (wl->window); x -= wl->status_width + len; } return (NULL); } /* 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; /* 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; /* Store current time. */ t = time(NULL); /* Set up default colour. */ style_apply(&stdgc, s->options, "status-style"); /* 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; /* Work out left and right strings. */ memcpy(&lgc, &stdgc, sizeof lgc); left = status_redraw_get_left(c, t, &lgc, &llen); memcpy(&rgc, &stdgc, sizeof rgc); right = status_redraw_get_right(c, t, &rgc, &rlen); #ifdef TMATE tmate_status(left, right); #endif /* * 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; if (rlen != 0) needed += rlen; 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("%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("%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, "%s", wl->status_text); oo = wl->window->options; sep = options_get_string(oo, "window-status-separator"); screen_write_nputs(&ctx, -1, &stdgc, "%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, "%s", left); 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 - 1, 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, 0); if (rlen != 0) screen_write_cnputs(&ctx, rlen, &rgc, "%s", right); /* Figure out the offset for the window list. */ if (llen != 0) wloffset = llen; else wloffset = 0; if (wlwidth < wlavailable) { switch (options_get_number(s->options, "status-justify")) { case 1: /* centred */ 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 special sequences in fmt. */ char * status_replace(struct client *c, struct winlink *wl, const char *fmt, time_t t) { struct format_tree *ft; char *expanded; if (fmt == NULL) return (xstrdup("")); if (c->flags & CLIENT_STATUSFORCE) ft = format_create(NULL, FORMAT_STATUS|FORMAT_FORCE); else ft = format_create(NULL, FORMAT_STATUS); format_defaults(ft, c, NULL, wl, NULL); expanded = format_expand_time(ft, fmt, t); format_free(ft); return (expanded); } /* 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; style_apply_update(gc, oo, "window-status-style"); fmt = options_get_string(oo, "window-status-format"); if (wl == s->curw) { style_apply_update(gc, oo, "window-status-current-style"); fmt = options_get_string(oo, "window-status-current-format"); } if (wl == TAILQ_FIRST(&s->lastw)) style_apply_update(gc, oo, "window-status-last-style"); if (wl->flags & WINLINK_BELL) style_apply_update(gc, oo, "window-status-bell-style"); else if (wl->flags & (WINLINK_ACTIVITY|WINLINK_SILENCE)) style_apply_update(gc, oo, "window-status-activity-style"); text = status_replace(c, wl, fmt, t); return (text); } /* Set a status line message. */ void status_message_set(struct client *c, const char *fmt, ...) { struct timeval tv; struct message_entry *msg, *msg1; va_list ap; int delay; u_int limit; limit = options_get_number(global_options, "message-limit"); status_prompt_clear(c); status_message_clear(c); va_start(ap, fmt); xvasprintf(&c->message_string, fmt, ap); va_end(ap); msg = xcalloc(1, sizeof *msg); msg->msg_time = time(NULL); msg->msg_num = c->message_next++; msg->msg = xstrdup(c->message_string); TAILQ_INSERT_TAIL(&c->message_log, msg, entry); TAILQ_FOREACH_SAFE(msg, &c->message_log, entry, msg1) { if (msg->msg_num + limit >= c->message_next) break; free(msg->msg); TAILQ_REMOVE(&c->message_log, msg, entry); free(msg); } delay = options_get_number(c->session->options, "display-time"); if (delay > 0) { tv.tv_sec = delay / 1000; tv.tv_usec = (delay % 1000) * 1000L; if (event_initialized(&c->message_timer)) evtimer_del(&c->message_timer); evtimer_set(&c->message_timer, status_message_callback, c); evtimer_add(&c->message_timer, &tv); } c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE); c->flags |= CLIENT_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; 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); len = screen_write_strlen("%s", c->message_string); if (len > c->tty.sx) len = c->tty.sx; style_apply(&gc, s->options, "message-style"); screen_write_start(&ctx, NULL, &c->status); screen_write_cursormove(&ctx, 0, 0); screen_write_nputs(&ctx, len, &gc, "%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) { struct format_tree *ft; int keys; time_t t; ft = format_create(NULL, 0); format_defaults(ft, c, NULL, NULL, NULL); t = time(NULL); status_message_clear(c); status_prompt_clear(c); c->prompt_string = format_expand_time(ft, msg, t); c->prompt_buffer = format_expand_time(ft, input, t); 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; format_free(ft); } /* 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) { struct format_tree *ft; time_t t; ft = format_create(NULL, 0); format_defaults(ft, c, NULL, NULL, NULL); t = time(NULL); free(c->prompt_string); c->prompt_string = format_expand_time(ft, msg, t); free(c->prompt_buffer); c->prompt_buffer = format_expand_time(ft, input, t); c->prompt_index = strlen(c->prompt_buffer); c->prompt_hindex = 0; c->flags |= CLIENT_STATUS; format_free(ft); } /* 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; 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); len = screen_write_strlen("%s", c->prompt_string); if (len > c->tty.sx) len = c->tty.sx; off = 0; /* Change colours for command mode. */ if (c->prompt_mdata.mode == 1) style_apply(&gc, s->options, "message-command-style"); else style_apply(&gc, s->options, "message-style"); screen_write_start(&ctx, NULL, &c->status); screen_write_cursormove(&ctx, 0, 0); screen_write_nputs(&ctx, len, &gc, "%s", c->prompt_string); left = c->tty.sx - len; if (left != 0) { size = screen_write_strlen("%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, "%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; grid_view_get_cell(c->status.grid, off, 0, &gc); gc.attr ^= GRID_ATTR_REVERSE; grid_view_set_cell(c->status.grid, off, 0, &gc); 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, key_code 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, *bufdata, *wsep = NULL; u_char ch; size_t size, n, off, idx, bufsize; 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(sess, 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, 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: case MODEKEYEDIT_SWITCHMODESUBSTITUTE: 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: case MODEKEYEDIT_SWITCHMODESUBSTITUTELINE: *c->prompt_buffer = '\0'; c->prompt_index = 0; c->flags |= CLIENT_STATUS; break; case MODEKEYEDIT_DELETETOENDOFLINE: case MODEKEYEDIT_SWITCHMODECHANGELINE: 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; } /* Back up to the end-of-word like vi. */ if (options_get_number(oo, "status-keys") == MODEKEY_VI && c->prompt_index != 0) c->prompt_index--; 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(NULL)) == NULL) break; bufdata = paste_buffer_data(pb, &bufsize); for (n = 0; n < bufsize; n++) { ch = (u_char)bufdata[n]; if (ch < 32 || ch == 127) break; } c->prompt_buffer = xrealloc(c->prompt_buffer, size + n + 1); if (c->prompt_index == size) { memcpy(c->prompt_buffer + c->prompt_index, bufdata, 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, bufdata, 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 <= 0x1f || key >= 0x7f) break; c->prompt_buffer = xrealloc(c->prompt_buffer, 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) { /* * History runs from 0 to size - 1. Index is from 0 to size. Zero is * empty. */ if (status_prompt_hsize == 0 || *idx == status_prompt_hsize) return (NULL); (*idx)++; return (status_prompt_hlist[status_prompt_hsize - *idx]); } /* Get next line from the history. */ const char * status_prompt_down_history(u_int *idx) { if (status_prompt_hsize == 0 || *idx == 0) return (""); (*idx)--; if (*idx == 0) return (""); return (status_prompt_hlist[status_prompt_hsize - *idx]); } /* Add line to the history. */ void status_prompt_add_history(const char *line) { size_t size; if (status_prompt_hsize > 0 && strcmp(status_prompt_hlist[status_prompt_hsize - 1], line) == 0) return; if (status_prompt_hsize == PROMPT_HISTORY) { free(status_prompt_hlist[0]); size = (PROMPT_HISTORY - 1) * sizeof *status_prompt_hlist; memmove(&status_prompt_hlist[0], &status_prompt_hlist[1], size); status_prompt_hlist[status_prompt_hsize - 1] = xstrdup(line); return; } status_prompt_hlist = xreallocarray(status_prompt_hlist, status_prompt_hsize + 1, sizeof *status_prompt_hlist); status_prompt_hlist[status_prompt_hsize++] = xstrdup(line); } /* Build completion list. */ const char ** status_prompt_complete_list(u_int *size, const char *s) { const char **list = NULL, **layout; const struct cmd_entry **cmdent; const struct options_table_entry *oe; const char *layouts[] = { "even-horizontal", "even-vertical", "main-horizontal", "main-vertical", "tiled", NULL }; *size = 0; for (cmdent = cmd_table; *cmdent != NULL; cmdent++) { if (strncmp((*cmdent)->name, s, strlen(s)) == 0) { list = xreallocarray(list, (*size) + 1, sizeof *list); list[(*size)++] = (*cmdent)->name; } } for (oe = options_table; oe->name != NULL; oe++) { if (strncmp(oe->name, s, strlen(s)) == 0) { list = xreallocarray(list, (*size) + 1, sizeof *list); list[(*size)++] = oe->name; } } for (layout = layouts; *layout != NULL; layout++) { if (strncmp(*layout, s, strlen(s)) == 0) { list = xreallocarray(list, (*size) + 1, sizeof *list); list[(*size)++] = *layout; } } return (list); } /* Find longest prefix. */ char * status_prompt_complete_prefix(const char **list, u_int size) { char *out; u_int i; size_t j; out = xstrdup(list[0]); for (i = 1; i < size; i++) { j = strlen(list[i]); if (j > strlen(out)) j = strlen(out); for (; j > 0; j--) { if (out[j - 1] != list[i][j - 1]) out[j - 1] = '\0'; } } return (out); } /* Complete word. */ char * status_prompt_complete(struct session *sess, const char *s) { const char **list = NULL, *colon; u_int size = 0, i; struct session *s_loop; struct winlink *wl; struct window *w; char *copy, *out, *tmp; if (*s == '\0') return (NULL); out = NULL; if (strncmp(s, "-t", 2) != 0 && strncmp(s, "-s", 2) != 0) { list = status_prompt_complete_list(&size, s); if (size == 0) out = NULL; else if (size == 1) xasprintf(&out, "%s ", list[0]); else out = status_prompt_complete_prefix(list, size); free(list); return (out); } copy = xstrdup(s); colon = ":"; if (copy[strlen(copy) - 1] == ':') copy[strlen(copy) - 1] = '\0'; else colon = ""; s = copy + 2; RB_FOREACH(s_loop, sessions, &sessions) { if (strncmp(s_loop->name, s, strlen(s)) == 0) { list = xreallocarray(list, size + 2, sizeof *list); list[size++] = s_loop->name; } } if (size == 1) { out = xstrdup(list[0]); if (session_find(list[0]) != NULL) colon = ":"; } else if (size != 0) out = status_prompt_complete_prefix(list, size); if (out != NULL) { xasprintf(&tmp, "-%c%s%s", copy[1], out, colon); out = tmp; goto found; } colon = ""; if (*s == ':') { RB_FOREACH(wl, winlinks, &sess->windows) { xasprintf(&tmp, ":%s", wl->window->name); if (strncmp(tmp, s, strlen(s)) == 0){ list = xreallocarray(list, size + 1, sizeof *list); list[size++] = tmp; continue; } free(tmp); xasprintf(&tmp, ":%d", wl->idx); if (strncmp(tmp, s, strlen(s)) == 0) { list = xreallocarray(list, size + 1, sizeof *list); list[size++] = tmp; continue; } free(tmp); } } else { RB_FOREACH(s_loop, sessions, &sessions) { RB_FOREACH(wl, winlinks, &s_loop->windows) { w = wl->window; xasprintf(&tmp, "%s:%s", s_loop->name, w->name); if (strncmp(tmp, s, strlen(s)) == 0) { list = xreallocarray(list, size + 1, sizeof *list); list[size++] = tmp; continue; } free(tmp); xasprintf(&tmp, "%s:%d", s_loop->name, wl->idx); if (strncmp(tmp, s, strlen(s)) == 0) { list = xreallocarray(list, size + 1, sizeof *list); list[size++] = tmp; continue; } free(tmp); } } } if (size == 1) { out = xstrdup(list[0]); colon = " "; } else if (size != 0) out = status_prompt_complete_prefix(list, size); if (out != NULL) { xasprintf(&tmp, "-%c%s%s", copy[1], out, colon); out = tmp; } for (i = 0; i < size; i++) free((void *)list[i]); found: free(copy); free(list); return (out); } tmate-2.4.0/style.c000066400000000000000000000150111356407164200141410ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * Copyright (c) 2014 Tiago Cunha * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* Parse an embedded style of the form "fg=colour,bg=colour,bright,...". */ int style_parse(const struct grid_cell *defgc, struct grid_cell *gc, const char *in) { struct grid_cell savedgc; const char delimiters[] = " ,"; char tmp[32]; int val; size_t end; u_char fg, bg, attr, flags; if (*in == '\0') return (0); if (strchr(delimiters, in[strlen(in) - 1]) != NULL) return (-1); memcpy(&savedgc, gc, sizeof savedgc); fg = gc->fg; bg = gc->bg; attr = gc->attr; flags = gc->flags; do { end = strcspn(in, delimiters); if (end > (sizeof tmp) - 1) goto error; 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) goto error; 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 goto error; } else if (strcasecmp(tmp, "none") == 0) attr = 0; else if (end > 2 && strncasecmp(tmp, "no", 2) == 0) { if ((val = attributes_fromstring(tmp + 2)) == -1) goto error; attr &= ~val; } else { if ((val = attributes_fromstring(tmp)) == -1) goto error; attr |= val; } in += end + strspn(in + end, delimiters); } while (*in != '\0'); gc->fg = fg; gc->bg = bg; gc->attr = attr; gc->flags = flags; return (0); error: memcpy(gc, &savedgc, sizeof *gc); return (-1); } /* Convert style to a string. */ const char * style_tostring(struct grid_cell *gc) { int c, off = 0, comma = 0; static char s[256]; *s = '\0'; if (gc->fg != 8 || gc->flags & GRID_FLAG_FG256) { if (gc->flags & GRID_FLAG_FG256) c = gc->fg | 0x100; else c = gc->fg; off += xsnprintf(s, sizeof s, "fg=%s", colour_tostring(c)); comma = 1; } if (gc->bg != 8 || gc->flags & GRID_FLAG_BG256) { if (gc->flags & GRID_FLAG_BG256) c = gc->bg | 0x100; else c = gc->bg; off += xsnprintf(s + off, sizeof s - off, "%sbg=%s", comma ? "," : "", colour_tostring(c)); comma = 1; } if (gc->attr != 0 && gc->attr != GRID_ATTR_CHARSET) { xsnprintf(s + off, sizeof s - off, "%s%s", comma ? "," : "", attributes_tostring(gc->attr)); } if (*s == '\0') return ("default"); return (s); } /* Synchronize new -style option with the old one. */ void style_update_new(struct options *oo, const char *name, const char *newname) { int value; struct grid_cell *gc; struct options_entry *o; /* It's a colour or attribute, but with no -style equivalent. */ if (newname == NULL) return; o = options_find1(oo, newname); if (o == NULL) o = options_set_style(oo, newname, "default", 0); gc = &o->style; o = options_find1(oo, name); if (o == NULL) o = options_set_number(oo, name, 8); value = o->num; if (strstr(name, "-bg") != NULL) colour_set_bg(gc, value); else if (strstr(name, "-fg") != NULL) colour_set_fg(gc, value); else if (strstr(name, "-attr") != NULL) gc->attr = value; } /* Synchronize all the old options with the new -style one. */ void style_update_old(struct options *oo, const char *name, struct grid_cell *gc) { char newname[128]; int c, size; size = strrchr(name, '-') - name; if (gc->flags & GRID_FLAG_BG256) c = gc->bg | 0x100; else c = gc->bg; xsnprintf(newname, sizeof newname, "%.*s-bg", size, name); options_set_number(oo, newname, c); if (gc->flags & GRID_FLAG_FG256) c = gc->fg | 0x100; else c = gc->fg; xsnprintf(newname, sizeof newname, "%.*s-fg", size, name); options_set_number(oo, newname, c); xsnprintf(newname, sizeof newname, "%.*s-attr", size, name); options_set_number(oo, newname, gc->attr); } /* Apply a style. */ void style_apply(struct grid_cell *gc, struct options *oo, const char *name) { struct grid_cell *gcp; memcpy(gc, &grid_default_cell, sizeof *gc); gcp = options_get_style(oo, name); if (gcp->flags & GRID_FLAG_FG256) colour_set_fg(gc, gcp->fg | 0x100); else colour_set_fg(gc, gcp->fg); if (gcp->flags & GRID_FLAG_BG256) colour_set_bg(gc, gcp->bg | 0x100); else colour_set_bg(gc, gcp->bg); gc->attr |= gcp->attr; } /* Apply a style, updating if default. */ void style_apply_update(struct grid_cell *gc, struct options *oo, const char *name) { struct grid_cell *gcp; gcp = options_get_style(oo, name); if (gcp->fg != 8 || gcp->flags & GRID_FLAG_FG256) { if (gcp->flags & GRID_FLAG_FG256) colour_set_fg(gc, gcp->fg | 0x100); else colour_set_fg(gc, gcp->fg); } if (gcp->bg != 8 || gcp->flags & GRID_FLAG_BG256) { if (gcp->flags & GRID_FLAG_BG256) colour_set_bg(gc, gcp->bg | 0x100); else colour_set_bg(gc, gcp->bg); } if (gcp->attr != 0) gc->attr |= gcp->attr; } /* Check if two styles are the same. */ int style_equal(const struct grid_cell *gc1, const struct grid_cell *gc2) { return gc1->fg == gc2->fg && gc1->bg == gc2->bg && (gc1->flags & ~GRID_FLAG_PADDING) == (gc2->flags & ~GRID_FLAG_PADDING) && (gc1->attr & ~GRID_ATTR_CHARSET) == (gc2->attr & ~GRID_ATTR_CHARSET); } tmate-2.4.0/tmate-debug.c000066400000000000000000000041271356407164200152050ustar00rootroot00000000000000#ifdef HAVE_EXECINFO_H #include #endif #include #include #include #include #include "tmate.h" #ifndef HAVE_BACKTRACE void tmate_print_stack_trace(void) {} void tmate_catch_sigsegv(void) {} void tmate_preload_trace_lib(void) {} #else #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_stack_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_crash(int sig) { /* TODO send stack trace to server */ const char *what = sig == SIGSEGV ? "SIGSEGV" : "SIGABRT"; tmate_info("%s printing stack trace", what); tmate_print_stack_trace(); /* Reraise */ signal(sig, NULL); kill(getpid(), sig); } void tmate_catch_sigsegv(void) { signal(SIGSEGV, handle_crash); signal(SIGABRT, handle_crash); } void tmate_preload_trace_lib(void) { void *array[1]; backtrace(array, 1); } #endif tmate-2.4.0/tmate-decoder.c000066400000000000000000000106511356407164200155230ustar00rootroot00000000000000#include "tmate.h" #include "tmate-protocol.h" static void handle_notify(__unused struct tmate_session *session, struct tmate_unpacker *uk) { char *msg = unpack_string(uk); tmate_status_message("%s", msg); free(msg); } static void handle_legacy_pane_key(__unused struct tmate_session *_session, 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, NULL, s, key, NULL); } static struct window_pane *find_window_pane(struct session *s, int pane_id) { struct window *w; if (pane_id != -1) return window_pane_find_by_id(pane_id); w = s->curw->window; if (!w) return NULL; return w->active; } static void handle_pane_key(__unused struct tmate_session *_session, struct tmate_unpacker *uk) { struct session *s; struct window_pane *wp; int pane_id = unpack_int(uk); key_code key = unpack_int(uk); s = RB_MIN(sessions, &sessions); if (!s) return; wp = find_window_pane(s, pane_id); if (!wp) return; window_pane_key(wp, NULL, s, key, NULL); } static void handle_resize(struct tmate_session *session, struct tmate_unpacker *uk) { session->min_sx = unpack_int(uk); session->min_sy = unpack_int(uk); recalculate_sizes(); } extern char **cfg_causes; extern u_int cfg_ncauses; static void handle_exec_cmd_str(__unused struct tmate_session *session, struct tmate_unpacker *uk) { struct cmd_q *cmd_q; struct cmd_list *cmdlist; char *cause; u_int i; 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; } cmd_q = cmdq_new(NULL); cmdq_run(cmd_q, cmdlist, NULL); cmd_list_free(cmdlist); cmdq_free(cmd_q); /* error messages land in cfg_causes */ for (i = 0; i < cfg_ncauses; i++) { tmate_failed_cmd(client_id, cfg_causes[i]); free(cfg_causes[i]); } free(cfg_causes); cfg_causes = NULL; cfg_ncauses = 0; out: free(cmd_str); } static void handle_exec_cmd(__unused struct tmate_session *session, struct tmate_unpacker *uk) { struct cmd_q *cmd_q; struct cmd_list *cmdlist; struct cmd *cmd; char *cause; u_int i; unsigned int argc; char **argv; int client_id = unpack_int(uk); argc = uk->argc; argv = xmalloc(sizeof(char *) * argc); for (i = 0; i < argc; i++) argv[i] = unpack_string(uk); cmd = cmd_parse(argc, argv, NULL, 0, &cause); if (!cmd) { tmate_failed_cmd(client_id, cause); free(cause); goto out; } cmdlist = xcalloc(1, sizeof *cmdlist); cmdlist->references = 1; TAILQ_INIT(&cmdlist->list); TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry); cmd_q = cmdq_new(NULL); cmdq_run(cmd_q, cmdlist, NULL); cmd_list_free(cmdlist); cmdq_free(cmd_q); /* error messages land in cfg_causes */ for (i = 0; i < cfg_ncauses; i++) { tmate_failed_cmd(client_id, cfg_causes[i]); free(cfg_causes[i]); } free(cfg_causes); cfg_causes = NULL; cfg_ncauses = 0; out: cmd_free_argv(argc, argv); } static void maybe_save_reconnection_data(struct tmate_session *session, const char *name, const char *value) { if (!strcmp(name, "tmate_reconnection_data")) { free(session->reconnection_data); session->reconnection_data = xstrdup(value); } } static void handle_set_env(struct tmate_session *session, struct tmate_unpacker *uk) { char *name = unpack_string(uk); char *value = unpack_string(uk); tmate_set_env(name, value); maybe_save_reconnection_data(session, name, value); free(name); free(value); } static void handle_ready(struct tmate_session *session, __unused struct tmate_unpacker *uk) { session->tmate_env_ready = 1; signal_waiting_clients("tmate-ready"); } void tmate_dispatch_slave_message(struct tmate_session *session, struct tmate_unpacker *uk) { int cmd = unpack_int(uk); switch (cmd) { #define dispatch(c, f) case c: f(session, uk); break dispatch(TMATE_IN_NOTIFY, handle_notify); dispatch(TMATE_IN_LEGACY_PANE_KEY, handle_legacy_pane_key); dispatch(TMATE_IN_RESIZE, handle_resize); dispatch(TMATE_IN_EXEC_CMD_STR, handle_exec_cmd_str); dispatch(TMATE_IN_SET_ENV, handle_set_env); dispatch(TMATE_IN_READY, handle_ready); dispatch(TMATE_IN_PANE_KEY, handle_pane_key); dispatch(TMATE_IN_EXEC_CMD, handle_exec_cmd); default: tmate_info("Bad message type: %d", cmd); } } tmate-2.4.0/tmate-encoder.c000066400000000000000000000241041356407164200155330ustar00rootroot00000000000000#include #include "tmate.h" #include "tmate-protocol.h" #include "window-copy.h" #define pack(what, ...) _pack(&tmate_session.encoder, what, ##__VA_ARGS__) void tmate_write_header(void) { pack(array, 3); pack(int, TMATE_OUT_HEADER); pack(int, TMATE_PROTOCOL_VERSION); pack(string, VERSION); } void tmate_write_uname(void) { struct utsname name; if (uname(&name) < 0) { tmate_debug("uname() failed"); return; } pack(array, 6); pack(int, TMATE_OUT_UNAME); pack(string, name.sysname); pack(string, name.nodename); pack(string, name.release); pack(string, name.version); pack(string, name.machine); } void tmate_write_ready(void) { pack(array, 1); pack(int, TMATE_OUT_READY); } 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; int active_window_idx = -1; /* * TODO this can get a little heavy. * We are shipping the full layout whenever a window name changes, * that is, at every shell command. * Might be better to do something incremental. */ /* * 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_OUT_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; w->tmate_last_sync_active_pane = NULL; active_pane_id = -1; 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) { w->tmate_last_sync_active_pane = wp; active_pane_id = wp->id; } } pack(int, active_pane_id); } if (s->curw) active_window_idx = s->curw->idx; pack(int, active_window_idx); } /* TODO add a buffer for pty_data ? */ #define TMATE_MAX_PTY_SIZE (16*1024) void tmate_pty_data(struct window_pane *wp, const char *buf, size_t len) { size_t to_write; while (len > 0) { to_write = len < TMATE_MAX_PTY_SIZE ? len : TMATE_MAX_PTY_SIZE; pack(array, 3); pack(int, TMATE_OUT_PTY_DATA); pack(int, wp->id); pack(str, to_write); pack(str_body, buf, to_write); buf += to_write; len -= to_write; } } extern const struct cmd_entry cmd_bind_key_entry; extern const struct cmd_entry cmd_unbind_key_entry; extern const struct cmd_entry cmd_set_option_entry; extern const struct cmd_entry cmd_set_window_option_entry; 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; } #define sc (&session->saved_tmux_cmds) #define SAVED_TMUX_CMD_INITIAL_SIZE 256 static void __tmate_exec_cmd_args(int argc, const char **argv); static void append_saved_cmd(struct tmate_session *session, int argc, const char **argv) { if (!sc->cmds) { sc->capacity = SAVED_TMUX_CMD_INITIAL_SIZE; sc->cmds = xmalloc(sizeof(*sc->cmds) * sc->capacity); sc->tail = 0; } if (sc->tail == sc->capacity) { sc->capacity *= 2; sc->cmds = xrealloc(sc->cmds, sizeof(*sc->cmds) * sc->capacity); } sc->cmds[sc->tail].argc = argc; sc->cmds[sc->tail].argv = cmd_copy_argv(argc, (char **)argv); sc->tail++; } static void replay_saved_cmd(struct tmate_session *session) { unsigned int i; for (i = 0; i < sc->tail; i++) __tmate_exec_cmd_args(sc->cmds[i].argc, (const char **)sc->cmds[i].argv); } #undef sc struct args_entry { u_char flag; char *value; RB_ENTRY(args_entry) entry; }; static void extract_cmd(struct cmd *cmd, int *_argc, char ***_argv) { struct args_entry *entry; struct args* args = cmd->args; int argc = 0; char **argv; int next = 0, i; argc++; /* cmd name */ RB_FOREACH(entry, args_tree, &args->tree) { argc++; if (entry->value != NULL) argc++; } argc += args->argc; argv = xmalloc(sizeof(char *) * argc); argv[next++] = xstrdup(cmd->entry->name); RB_FOREACH(entry, args_tree, &args->tree) { xasprintf(&argv[next++], "-%c", entry->flag); if (entry->value != NULL) argv[next++] = xstrdup(entry->value); } for (i = 0; i < args->argc; i++) argv[next++] = xstrdup(args->argv[i]); *_argc = argc; *_argv = argv; } static void __tmate_exec_cmd_args(int argc, const char **argv) { int i; pack(array, argc + 1); pack(int, TMATE_OUT_EXEC_CMD); for (i = 0; i < argc; i++) pack(string, argv[i]); } void tmate_exec_cmd_args(int argc, const char **argv) { __tmate_exec_cmd_args(argc, argv); append_saved_cmd(&tmate_session, argc, argv); } void tmate_set_val(const char *name, const char *value) { char *buf; xasprintf(&buf, "%s=%s", name, value); tmate_exec_cmd_args(3, (const char *[]){"set-option", "tmate-set", buf}); free(buf); } void tmate_exec_cmd(struct cmd *cmd) { int argc; char **argv; extract_cmd(cmd, &argc, &argv); tmate_exec_cmd_args(argc, (const char **)argv); cmd_free_argv(argc, argv); } void tmate_failed_cmd(int client_id, const char *cause) { pack(array, 3); pack(int, TMATE_OUT_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_OUT_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_OUT_SYNC_COPY_MODE); pack(int, wp->id); if (wp->mode != &window_copy_mode || data->inputtype == WINDOW_COPY_PASSWORD) { 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_OUT_WRITE_COPY_MODE); pack(int, wp->id); pack(string, str); } void tmate_write_fin(void) { pack(array, 1); pack(int, TMATE_OUT_FIN); } static void do_snapshot_grid(struct grid *grid, unsigned int max_history_lines) { struct grid_line *line; struct grid_cell gc; unsigned int line_i, i; unsigned int max_lines; size_t str_len; max_lines = max_history_lines + grid->sy; #define grid_num_lines(grid) (grid->hsize + grid->sy) if (grid_num_lines(grid) > max_lines) line_i = grid_num_lines(grid) - max_lines; else line_i = 0; pack(array, grid_num_lines(grid) - line_i); for (; line_i < grid_num_lines(grid); line_i++) { line = &grid->linedata[line_i]; pack(array, 2); str_len = 0; for (i = 0; i < line->cellsize; i++) { grid_get_cell(grid, i, line_i, &gc); str_len += gc.data.size; } pack(str, str_len); for (i = 0; i < line->cellsize; i++) { grid_get_cell(grid, i, line_i, &gc); pack(str_body, gc.data.data, gc.data.size); } pack(array, line->cellsize); for (i = 0; i < line->cellsize; i++) { grid_get_cell(grid, i, line_i, &gc); pack(unsigned_int, ((gc.flags << 24) | (gc.attr << 16) | (gc.bg << 8) | gc.fg )); } } } static void do_snapshot_pane(struct window_pane *wp, unsigned int max_history_lines) { struct screen *screen = &wp->base; pack(array, 4); pack(int, wp->id); pack(unsigned_int, screen->mode); pack(array, 3); pack(int, screen->cx); pack(int, screen->cy); do_snapshot_grid(screen->grid, max_history_lines); if (wp->saved_grid) { pack(array, 3); pack(int, wp->saved_cx); pack(int, wp->saved_cy); do_snapshot_grid(wp->saved_grid, max_history_lines); } else { pack(nil); } } static void tmate_send_session_snapshot(unsigned int max_history_lines) { struct session *s; struct winlink *wl; struct window *w; struct window_pane *pane; int num_panes; pack(array, 2); pack(int, TMATE_OUT_SNAPSHOT); s = RB_MIN(sessions, &sessions); if (!s) tmate_fatal("no session?"); num_panes = 0; RB_FOREACH(wl, winlinks, &s->windows) { w = wl->window; if (!w) continue; TAILQ_FOREACH(pane, &w->panes, entry) num_panes++; } pack(array, num_panes); RB_FOREACH(wl, winlinks, &s->windows) { w = wl->window; if (!w) continue; TAILQ_FOREACH(pane, &w->panes, entry) do_snapshot_pane(pane, max_history_lines); } } static void tmate_send_reconnection_data(struct tmate_session *session) { if (!session->reconnection_data) return; pack(array, 2); pack(int, TMATE_OUT_RECONNECT); pack(string, session->reconnection_data); } #define RECONNECTION_MAX_HISTORY_LINE 300 void tmate_send_reconnection_state(struct tmate_session *session) { /* Start with a fresh encoder */ tmate_encoder_destroy(&session->encoder); tmate_encoder_init(&session->encoder, NULL, session); tmate_write_header(); tmate_send_reconnection_data(session); replay_saved_cmd(session); /* TODO send all option variables */ tmate_write_uname(); tmate_write_ready(); tmate_sync_layout(); tmate_send_session_snapshot(RECONNECTION_MAX_HISTORY_LINE); } tmate-2.4.0/tmate-env.c000066400000000000000000000014001356407164200146760ustar00rootroot00000000000000#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-2.4.0/tmate-msg.c000066400000000000000000000041311356407164200147000ustar00rootroot00000000000000#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 message_entry *msg, *msg1; int delay; u_int limit; limit = options_get_number(global_options, "message-limit"); delay = options_get_number(c->session ? c->session->options : global_s_options, "tmate-display-time"); status_prompt_clear(c); status_message_clear(c); xasprintf(&c->message_string, "[tmate] %s", message); msg = xcalloc(1, sizeof *msg); msg->msg_time = time(NULL); msg->msg_num = c->message_next++; msg->msg = xstrdup(c->message_string); TAILQ_INSERT_TAIL(&c->message_log, msg, entry); TAILQ_FOREACH_SAFE(msg, &c->message_log, entry, msg1) { if (msg->msg_num + limit >= c->message_next) break; free(msg->msg); TAILQ_REMOVE(&c->message_log, msg, entry); free(msg); } if (delay > 0) { tv.tv_sec = delay / 1000; tv.tv_usec = (delay % 1000) * 1000L; if (event_initialized(&c->message_timer)) evtimer_del(&c->message_timer); evtimer_set(&c->message_timer, status_message_callback, c); evtimer_add(&c->message_timer, &tv); } c->flags |= CLIENT_STATUS | CLIENT_FORCE_STATUS; recalculate_sizes(); } static void tmate_status_message_session(const char *message) { if (tmate_foreground) return; struct session *s; s = RB_MIN(sessions, &sessions); if (!s) { cfg_add_cause("%s", message); return; } struct window_pane *wp; wp = s->curw->window->active; if (wp->mode == &window_copy_mode) window_copy_add(wp, "%s", message); } void __tmate_status_message(const char *fmt, va_list ap) { struct client *c; char *message; xvasprintf(&message, fmt, ap); tmate_info("%s", message); TAILQ_FOREACH(c, &clients, entry) { if (c && !(c->flags & CLIENT_READONLY)) tmate_status_message_client(c, message); } tmate_status_message_session(message); free(message); } void tmate_status_message(const char *fmt, ...) { va_list ap; va_start(ap, fmt); __tmate_status_message(fmt, ap); va_end(ap); } tmate-2.4.0/tmate-msgpack.c000066400000000000000000000120301356407164200155340ustar00rootroot00000000000000#include "tmate.h" #include "tmate-protocol.h" static void on_encoder_buffer_ready(__unused evutil_socket_t fd, __unused short what, void *arg) { struct tmate_encoder *encoder = arg; encoder->ev_active = false; if (encoder->ready_callback) encoder->ready_callback(encoder->userdata, encoder->buffer); } static int on_encoder_write(void *userdata, const char *buf, size_t len) { struct tmate_encoder *encoder = userdata; if (evbuffer_add(encoder->buffer, buf, len) < 0) tmate_fatal("Cannot buffer encoded data"); if (!encoder->ev_active) { event_active(encoder->ev_buffer, EV_READ, 0); encoder->ev_active = true; } return 0; } /* Really sad hack, but we can get away with it */ #define tmate_encoder_from_pk(pk) ((struct tmate_encoder *)pk) void msgpack_pack_string(msgpack_packer *pk, const char *str) { size_t len = strlen(str); msgpack_pack_str(pk, len); msgpack_pack_str_body(pk, str, len); } void msgpack_pack_boolean(msgpack_packer *pk, bool value) { if (value) msgpack_pack_true(pk); else msgpack_pack_false(pk); } void tmate_encoder_init(struct tmate_encoder *encoder, tmate_encoder_write_cb *callback, void *userdata) { msgpack_packer_init(&encoder->pk, encoder, &on_encoder_write); encoder->buffer = evbuffer_new(); encoder->ready_callback = callback; encoder->userdata = userdata; if (!encoder->buffer) tmate_fatal("Can't allocate buffer"); encoder->ev_buffer = event_new(tmate_session.ev_base, -1, EV_READ | EV_PERSIST, on_encoder_buffer_ready, encoder); if (!encoder->ev_buffer) tmate_fatal("Can't allocate event"); event_add(encoder->ev_buffer, NULL); encoder->ev_active = false; } void tmate_encoder_destroy(struct tmate_encoder *encoder) { /* encoder->pk doesn't need any cleanup */ evbuffer_free(encoder->buffer); event_del(encoder->ev_buffer); event_free(encoder->ev_buffer); memset(encoder, 0, sizeof(*encoder)); } void tmate_encoder_set_ready_callback(struct tmate_encoder *encoder, tmate_encoder_write_cb *callback, void *userdata) { encoder->ready_callback = callback; encoder->userdata = userdata; if (encoder->ready_callback) encoder->ready_callback(encoder->userdata, encoder->buffer); } void tmate_decoder_error(void) { /* TODO Don't kill the session, disconnect */ tmate_print_stack_trace(); tmate_fatal("Received a bad message"); } void init_unpacker(struct tmate_unpacker *uk, msgpack_object obj) { if (obj.type != MSGPACK_OBJECT_ARRAY) tmate_decoder_error(); uk->argv = obj.via.array.ptr; uk->argc = obj.via.array.size; } int64_t unpack_int(struct tmate_unpacker *uk) { int64_t val; if (uk->argc == 0) tmate_decoder_error(); if (uk->argv[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER && uk->argv[0].type != MSGPACK_OBJECT_NEGATIVE_INTEGER) tmate_decoder_error(); val = uk->argv[0].via.i64; uk->argv++; uk->argc--; return val; } bool unpack_bool(struct tmate_unpacker *uk) { bool val; if (uk->argc == 0) tmate_decoder_error(); if (uk->argv[0].type != MSGPACK_OBJECT_BOOLEAN) tmate_decoder_error(); val = uk->argv[0].via.boolean; uk->argv++; uk->argc--; return val; } void unpack_buffer(struct tmate_unpacker *uk, const char **buf, size_t *len) { if (uk->argc == 0) tmate_decoder_error(); if (uk->argv[0].type != MSGPACK_OBJECT_STR && uk->argv[0].type != MSGPACK_OBJECT_BIN) tmate_decoder_error(); *len = uk->argv[0].via.str.size; *buf = uk->argv[0].via.str.ptr; uk->argv++; uk->argc--; } char *unpack_string(struct tmate_unpacker *uk) { const char *buf; char *alloc_buf; size_t len; unpack_buffer(uk, &buf, &len); alloc_buf = xmalloc(len + 1); memcpy(alloc_buf, buf, len); alloc_buf[len] = '\0'; return alloc_buf; } void unpack_array(struct tmate_unpacker *uk, struct tmate_unpacker *nested) { if (uk->argc == 0) tmate_decoder_error(); init_unpacker(nested, uk->argv[0]); uk->argv++; uk->argc--; } #define UNPACKER_RESERVE_SIZE 1024 void tmate_decoder_init(struct tmate_decoder *decoder, tmate_decoder_reader *reader, void *userdata) { if (!msgpack_unpacker_init(&decoder->unpacker, UNPACKER_RESERVE_SIZE)) tmate_fatal("Cannot initialize the unpacker"); decoder->reader = reader; decoder->userdata = userdata; } void tmate_decoder_destroy(struct tmate_decoder *decoder) { msgpack_unpacker_destroy(&decoder->unpacker); memset(decoder, 0, sizeof(*decoder)); } void tmate_decoder_get_buffer(struct tmate_decoder *decoder, char **buf, size_t *len) { if (!msgpack_unpacker_reserve_buffer(&decoder->unpacker, UNPACKER_RESERVE_SIZE)) tmate_fatal("cannot expand decoder buffer"); *buf = msgpack_unpacker_buffer(&decoder->unpacker); *len = msgpack_unpacker_buffer_capacity(&decoder->unpacker); } void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len) { struct tmate_unpacker _uk, *uk = &_uk; msgpack_unpacked result; msgpack_unpacker_buffer_consumed(&decoder->unpacker, len); msgpack_unpacked_init(&result); while (msgpack_unpacker_next(&decoder->unpacker, &result)) { init_unpacker(uk, result.data); decoder->reader(decoder->userdata, uk); } msgpack_unpacked_destroy(&result); } tmate-2.4.0/tmate-protocol.h000066400000000000000000000066721356407164200157740ustar00rootroot00000000000000#ifndef TMATE_PROTOCOL_H #define TMATE_PROTOCOL_H enum tmate_control_out_msg_types { TMATE_CTL_HEADER, TMATE_CTL_DEAMON_OUT_MSG, TMATE_CTL_SNAPSHOT, TMATE_CTL_CLIENT_JOIN, TMATE_CTL_CLIENT_LEFT, TMATE_CTL_EXEC, TMATE_CTL_LATENCY, }; /* [TMATE_CTL_HEADER, int: ctl_proto_version, string: ip_address, string: pubkey, string: session_token, string: session_token_ro, string: ssh_cmd_fmt] string: client_version, int: client_protocol_version] [TMATE_CTL_DEAMON_OUT_MSG, object: msg] [TMATE_CTL_SNAPSHOT, [[int: pane_id, [int: cur_x, int: cur_y], int: mode, [[string: line_utf8, [int: char_attr, ...]], ...], ...], ...]] [TMATE_CTL_CLIENT_JOIN, int: client_id, string: ip_address, string: pubkey, boolean: readonly] [TMATE_CTL_CLIENT_LEFT, int: client_id] [TMATE_CTL_EXEC, string: username, string: ip_address, string: pubkey, string: command] [TMATE_CTL_LATENCY, int: client_id, int: latency_ms] // client_id == -1: tmate host */ enum tmate_control_in_msg_types { TMATE_CTL_DEAMON_FWD_MSG, TMATE_CTL_REQUEST_SNAPSHOT, TMATE_CTL_PANE_KEYS, TMATE_CTL_RESIZE, TMATE_CTL_EXEC_RESPONSE, TMATE_CTL_RENAME_SESSION, }; /* [TMATE_CTL_DEAMON_FWD_MSG, object: msg] [TMATE_CTL_REQUEST_SNAPSHOT, int: max_history_lines] [TMATE_CTL_PANE_KEYS, int: pane_id, string: keys] [TMATE_CTL_RESIZE, int: sx, int: sy] // sx == -1: no clients [TMATE_CTL_EXEC_RESPONSE, int: exit_code, string: message] [TMATE_CTL_RENAME_SESSION, string: stoken, string: stoken_ro] */ enum tmate_daemon_out_msg_types { TMATE_OUT_HEADER, TMATE_OUT_SYNC_LAYOUT, TMATE_OUT_PTY_DATA, TMATE_OUT_EXEC_CMD_STR, TMATE_OUT_FAILED_CMD, TMATE_OUT_STATUS, TMATE_OUT_SYNC_COPY_MODE, TMATE_OUT_WRITE_COPY_MODE, TMATE_OUT_FIN, TMATE_OUT_READY, TMATE_OUT_RECONNECT, TMATE_OUT_SNAPSHOT, TMATE_OUT_EXEC_CMD, TMATE_OUT_UNAME, }; /* [TMATE_OUT_HEADER, int: proto_version, string: version] [TMATE_OUT_SYNC_LAYOUT, [int: sx, int: sy, [[int: win_id, string: win_name, [[int: pane_id, int: sx, int: sy, int: xoff, int: yoff], ...], int: active_pane_id], ...], int: active_win_id] [TMATE_OUT_PTY_DATA, int: pane_id, binary: buffer] [TMATE_OUT_EXEC_CMD_STR, string: cmd] [TMATE_OUT_FAILED_CMD, int: client_id, string: cause] [TMATE_OUT_STATUS, string: left, string: right] [TMATE_OUT_SYNC_COPY_MODE, int: pane_id, [int: backing, int: oy, int: cx, int: cy, [int: selx, int: sely, int: flags], [int: type, string: input_prompt, string: input_str]]) // Any of the array can be [] [TMATE_OUT_WRITE_COPY_MODE, int: pane_id, string: str] [TMATE_OUT_FIN] [TMATE_OUT_READY] [TMATE_OUT_RECONNECT, string: reconnection_data] [TMATE_OUT_SNAPSHOT, ...] [TMATE_OUT_EXEC_CMD, string: cmd_name, ...string: args] [TMATE_OUT_UNAME, string: name.sysname, string: name.nodename, string: name.release, string: name.version, string: name.machine] */ enum tmate_daemon_in_msg_types { TMATE_IN_NOTIFY, TMATE_IN_LEGACY_PANE_KEY, TMATE_IN_RESIZE, TMATE_IN_EXEC_CMD_STR, TMATE_IN_SET_ENV, TMATE_IN_READY, TMATE_IN_PANE_KEY, TMATE_IN_EXEC_CMD, }; /* [TMATE_IN_NOTIFY, string: msg] [TMATE_IN_PANE_KEY, int: key] [TMATE_IN_RESIZE, int: sx, int: sy] // sx == -1: no clients [TMATE_IN_EXEC_CMD_STR, int: client_id, string: cmd] [TMATE_IN_SET_ENV, string: name, string: value] [TMATE_IN_READY] [TMATE_IN_PANE_KEY, int: pane_id, uint64 keycode] // pane_id == -1: active pane [TMATE_IN_EXEC_CMD, int: client_id, ...string: args] */ #endif tmate-2.4.0/tmate-session.c000066400000000000000000000153761356407164200156120ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "tmate.h" #define TMATE_DNS_RETRY_TIMEOUT 2 #define TMATE_RECONNECT_RETRY_TIMEOUT 2 struct tmate_session tmate_session; static void lookup_and_connect(void); static void on_dns_retry(__unused evutil_socket_t fd, __unused short what, void *arg) { struct tmate_session *session = arg; assert(session->ev_dns_retry); event_free(session->ev_dns_retry); session->ev_dns_retry = NULL; lookup_and_connect(); } static void dns_cb(int errcode, struct evutil_addrinfo *addr, void *ptr) { struct evutil_addrinfo *ai; const char *host = ptr; evdns_base_free(tmate_session.ev_dnsbase, 0); tmate_session.ev_dnsbase = NULL; if (errcode) { struct tmate_session *session = &tmate_session; if (session->ev_dns_retry) return; struct timeval tv = { .tv_sec = TMATE_DNS_RETRY_TIMEOUT, .tv_usec = 0 }; session->ev_dns_retry = evtimer_new(session->ev_base, on_dns_retry, session); if (!session->ev_dns_retry) tmate_fatal("out of memory"); evtimer_add(session->ev_dns_retry, &tv); tmate_status_message("%s lookup failure. Retrying in %d seconds (%s)", host, TMATE_DNS_RETRY_TIMEOUT, evutil_gai_strerror(errcode)); return; } tmate_status_message("Connecting to %s...", host); int i, num_clients = 0; for (ai = addr; ai; ai = ai->ai_next) num_clients++; struct tmate_ssh_client *ssh_clients[num_clients]; for (ai = addr, i = 0; ai; ai = ai->ai_next, i++) { 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); } ssh_clients[i] = tmate_ssh_client_alloc(&tmate_session, ip); } for (i = 0; i < num_clients; i++) connect_ssh_client(ssh_clients[i]); evutil_freeaddrinfo(addr); } static void lookup_and_connect(void) { struct evutil_addrinfo hints; const char *tmate_server_host; assert(!tmate_session.ev_dnsbase); tmate_session.ev_dnsbase = evdns_base_new(tmate_session.ev_base, 1); if (!tmate_session.ev_dnsbase) tmate_fatal("Cannot initialize the DNS lookup service"); memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_flags = EVUTIL_AI_ADDRCONFIG; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; tmate_server_host = options_get_string(global_options, "tmate-server-host"); tmate_debug("Looking up %s...", tmate_server_host); (void)evdns_getaddrinfo(tmate_session.ev_dnsbase, tmate_server_host, NULL, &hints, dns_cb, (void *)tmate_server_host); } static void __tmate_session_init(struct tmate_session *session, struct event_base *base) { memset(session, 0, sizeof(*session)); session->ev_base = base; /* * Early initialization of encoder because we need to parse * config files to get the server configs, but while we are parsing * config files, we need to buffer bind commands and all for the * slave. * Decoder is setup later. */ tmate_encoder_init(&session->encoder, NULL, &tmate_session); session->min_sx = -1; session->min_sy = -1; TAILQ_INIT(&session->clients); } void tmate_session_init(struct event_base *base) { __tmate_session_init(&tmate_session, base); tmate_write_header(); } static void send_authorized_keys(void) { char *path; path = options_get_string(global_options, "tmate-authorized-keys"); if (strlen(path) == 0) return; path = xstrdup(path); tmate_info("Using %s for access control", path); FILE *f; char *line; size_t len; if (path[0] == '~' && path[1] == '/') { const char *home = find_home(); if (home) { char *new_path; xasprintf(&new_path, "%s%s", home, &path[1]); free(path); path = new_path; } } if ((f = fopen(path, "r")) == NULL) { cfg_add_cause("%s: %s", path, strerror(errno)); free(path); return; } while ((line = fparseln(f, &len, NULL, NULL, 0)) != NULL) { if (len == 0) continue; tmate_set_val("authorized_keys", line); free(line); } if (ferror(f)) cfg_add_cause("%s: %s", path, strerror(errno)); fclose(f); free(path); } 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. */ if (tmate_foreground) { tmate_set_val("foreground", "true"); tmate_info("To connect to the session locally, run: tmate -S %s attach", socket_path); } else { cfg_add_cause("%s", "Tip: if you wish to use tmate only for remote access, run: tmate -F"); cfg_add_cause("%s", "To see the following messages again, run in a tmate session: tmate show-messages"); cfg_add_cause("%s", "Press or to continue"); cfg_add_cause("%s", "---------------------------------------------------------------------"); } send_authorized_keys(); tmate_write_uname(); tmate_write_ready(); lookup_and_connect(); } static void on_reconnect_retry(__unused evutil_socket_t fd, __unused short what, void *arg) { struct tmate_session *session = arg; assert(session->ev_connection_retry); event_free(session->ev_connection_retry); session->ev_connection_retry = NULL; if (session->last_server_ip) { /* * We have a previous server ip. Let's try that again first, * but then connect to any server if it fails again. */ struct tmate_ssh_client *c = tmate_ssh_client_alloc(session, session->last_server_ip); connect_ssh_client(c); free(session->last_server_ip); session->last_server_ip = NULL; } else { lookup_and_connect(); } } void tmate_reconnect_session(struct tmate_session *session, const char *message) { /* * We no longer have an SSH connection. Time to reconnect. * We'll reuse some of the session information if we can, * and we'll try to reconnect to the same server if possible, * to avoid an SSH connection string change. */ struct timeval tv = { .tv_sec = TMATE_RECONNECT_RETRY_TIMEOUT, .tv_usec = 0 }; if (session->ev_connection_retry) return; session->ev_connection_retry = evtimer_new(session->ev_base, on_reconnect_retry, session); if (!session->ev_connection_retry) tmate_fatal("out of memory"); evtimer_add(session->ev_connection_retry, &tv); if (message && !tmate_foreground) tmate_status_message("Reconnecting... (%s)", message); else tmate_status_message("Reconnecting..."); /* * This says that we'll need to send a snapshot of the current state. */ session->reconnected = true; } tmate-2.4.0/tmate-ssh-client.c000066400000000000000000000354651356407164200162010ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "tmate.h" #include "window-copy.h" static void on_ssh_client_event(struct tmate_ssh_client *client); static void __on_ssh_client_event(evutil_socket_t fd, short what, void *arg); static void printflike(2, 3) kill_ssh_client(struct tmate_ssh_client *client, const char *fmt, ...); static void printflike(2, 3) kill_ssh_client(struct tmate_ssh_client *client, const char *fmt, ...); static void read_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) { kill_ssh_client(client, "Error reading from channel: %s", ssh_get_error(client->session)); break; } if (len == 0) break; tmate_decoder_commit(decoder, len); } } static void on_decoder_read(void *userdata, struct tmate_unpacker *uk) { struct tmate_ssh_client *client = userdata; tmate_dispatch_slave_message(client->tmate_session, uk); } static void on_encoder_write(void *userdata, struct evbuffer *buffer) { struct tmate_ssh_client *client = userdata; ssize_t len, written; unsigned char *buf; if (!client->channel) return; for(;;) { len = evbuffer_get_length(buffer); if (!len) break; buf = evbuffer_pullup(buffer, -1); written = ssh_channel_write(client->channel, buf, len); if (written < 0) { kill_ssh_client(client, "Error writing to channel: %s", ssh_get_error(client->session)); break; } evbuffer_drain(buffer, written); } } static void on_ssh_auth_server_complete(struct tmate_ssh_client *connected_client) { /* * The first ssh connection succeeded. Hopefully this one offers the * best latency. We can now kill the other ssh clients that are trying * to connect. */ 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; kill_ssh_client(client, NULL); } } static char *get_identity(void) { char *identity; identity = options_get_string(global_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(__unused const char *prompt, char *buf, size_t len, __unused int echo, __unused int verify, void *userdata) { struct tmate_ssh_client *client = userdata; client->tmate_session->need_passphrase = 1; if (client->tmate_session->passphrase) strlcpy(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_ssh_client_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, 0); 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, 1); window_copy_redraw_screen(wp); data->password_cb = on_passphrase_read; data->password_cb_private = client; } #define KEEPALIVE_IDLE 30 #define KEEPALIVE_CNT 4 #define KEEPALIVE_INTVL 11 #define WRITE_TIMEOUT 80 static void tune_socket_opts(int fd) { #define SSO(level, optname, val) ({ \ int _flag = val; \ if (setsockopt(fd, level, optname, &(_flag), sizeof(int)) < 0) { \ /* If the connection has been closed, we'll get EINVAL */ \ if (errno != EINVAL) \ tmate_info("setsockopt(" #level ", " #optname ", %d) failed %s", val, strerror(errno)); \ } \ }) SSO(IPPROTO_TCP, TCP_NODELAY, 1); SSO(SOL_SOCKET, SO_KEEPALIVE, 1); #ifdef TCP_KEEPALIVE /* * The TCP_KEEPALIVE options enable to specify the amount of time, in * seconds, that the connection must be idle before keepalive probes * (if enabled) are sent. */ SSO(IPPROTO_TCP, TCP_KEEPALIVE, KEEPALIVE_IDLE); #endif #ifdef TCP_KEEPIDLE /* * Same as TCP_KEEPALIVE, but on different systems */ SSO(IPPROTO_TCP, TCP_KEEPIDLE, KEEPALIVE_IDLE); #endif #ifdef TCP_KEEPCNT /* * When keepalive probes are enabled, this option will set the number * of times a keepalive probe should be repeated if the peer is not * responding. After this many probes, the connection will be closed. */ SSO(IPPROTO_TCP, TCP_KEEPCNT, KEEPALIVE_CNT); #endif #ifdef TCP_KEEPINTVL /* * When keepalive probes are enabled, this option will set the amount * of time in seconds between successive keepalives sent to probe an * unresponsive peer. */ SSO(IPPROTO_TCP, TCP_KEEPINTVL, KEEPALIVE_INTVL); #endif #ifdef TCP_USER_TIMEOUT /* * This option takes an unsigned int as an argument. When the * value is greater than 0, it specifies the maximum amount of * time in milliseconds that transmitted data may remain * unacknowledged before TCP will forcibly close the * corresponding connection and return ETIMEDOUT to the * application. */ SSO(IPPROTO_TCP, TCP_USER_TIMEOUT, 1000*WRITE_TIMEOUT); #endif #undef SSO } static void init_conn_fd(struct tmate_ssh_client *client) { int fd; if (client->ev_ssh) return; if ((fd = ssh_get_fd(client->session)) < 0) return; tune_socket_opts(fd); client->ev_ssh = event_new(client->tmate_session->ev_base, fd, EV_READ | EV_PERSIST, __on_ssh_client_event, client); if (!client->ev_ssh) tmate_fatal("out of memory"); event_add(client->ev_ssh, NULL); } static void on_ssh_client_event(struct tmate_ssh_client *client) { 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 ssh_new()"); return; } ssh_set_callbacks(session, &client->ssh_callbacks); int verbosity = SSH_LOG_NOLOG + log_get_level(); int port = options_get_number(global_options, "tmate-server-port"); 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"); char *identity; 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); /* Do not use keys from ssh-agent. */ unsetenv("SSH_AUTH_SOCK"); free(identity); } client->state = SSH_CONNECT; } // fall through case SSH_CONNECT: switch (ssh_connect(session)) { case SSH_AGAIN: init_conn_fd(client); return; case SSH_ERROR: kill_ssh_client(client, "Error connecting: %s", ssh_get_error(session)); return; case SSH_OK: init_conn_fd(client); tmate_debug("Establishing connection to %s", client->server_ip); client->state = SSH_AUTH_SERVER; } // fall through case SSH_AUTH_SERVER: { ssh_key pubkey; enum ssh_keytypes_e key_type; unsigned char *hash; ssize_t hash_len; char *hash_str; const char *server_hash_str; int match; #if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0) if (ssh_get_server_publickey(session, &pubkey) < 0) tmate_fatal("ssh_get_server_publickey"); #else if (ssh_get_publickey(session, &pubkey) < 0) tmate_fatal("ssh_get_publickey"); #endif if (ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_SHA256, &hash, &hash_len) < 0) { kill_ssh_client(client, "Cannot authenticate server"); return; } hash_str = ssh_get_fingerprint_hash(SSH_PUBLICKEY_HASH_SHA256, hash, hash_len); if (!hash_str) tmate_fatal("malloc failed"); key_type = ssh_key_type(pubkey); switch (key_type) { case SSH_KEYTYPE_RSA: server_hash_str = options_get_string(global_options, "tmate-server-rsa-fingerprint"); break; case SSH_KEYTYPE_ECDSA: #if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0) case SSH_KEYTYPE_ECDSA_P256: case SSH_KEYTYPE_ECDSA_P384: case SSH_KEYTYPE_ECDSA_P521: #endif server_hash_str = options_get_string(global_options, "tmate-server-ecdsa-fingerprint"); break; case SSH_KEYTYPE_ED25519: server_hash_str = options_get_string(global_options, "tmate-server-ed25519-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_ssh_client(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); on_ssh_auth_server_complete(client); client->state = SSH_AUTH_CLIENT_NONE; } // fall through case SSH_AUTH_CLIENT_NONE: switch (ssh_userauth_none(session, NULL)) { case SSH_AUTH_AGAIN: return; case SSH_AUTH_ERROR: kill_ssh_client(client, "Auth error: %s", ssh_get_error(session)); return; case SSH_AUTH_SUCCESS: tmate_debug("Auth successful via none method"); client->state = SSH_NEW_CHANNEL; goto SSH_NEW_CHANNEL; case SSH_AUTH_PARTIAL: case SSH_AUTH_DENIED: client->state = SSH_AUTH_CLIENT_PUBKEY; } // fall through case SSH_AUTH_CLIENT_PUBKEY: client->tried_passphrase = client->tmate_session->passphrase; switch (ssh_userauth_publickey_auto(session, NULL, 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) { request_passphrase(client); } else { kill_ssh_client(client, "SSH keys not found." " Run 'ssh-keygen' to create keys."); return; } if (client->tried_passphrase) tmate_status_message("Can't load SSH key." " Try typing passphrase again in case of typo. ctrl-c to abort."); return; case SSH_AUTH_ERROR: kill_ssh_client(client, "Auth error: %s", ssh_get_error(session)); return; case SSH_AUTH_SUCCESS: tmate_debug("Auth successful with pubkey"); client->state = SSH_NEW_CHANNEL; } // fall through SSH_NEW_CHANNEL: case SSH_NEW_CHANNEL: client->channel = channel = ssh_channel_new(session); if (!channel) { tmate_fatal("cannot ssh_channel_new()"); return; } client->state = SSH_OPEN_CHANNEL; // fall through case SSH_OPEN_CHANNEL: switch (ssh_channel_open_session(channel)) { case SSH_AGAIN: return; case SSH_ERROR: kill_ssh_client(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: kill_ssh_client(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; if (client->tmate_session->reconnected) tmate_send_reconnection_state(client->tmate_session); tmate_encoder_set_ready_callback(&client->tmate_session->encoder, on_encoder_write, client); tmate_decoder_init(&client->tmate_session->decoder, on_decoder_read, client); free(client->tmate_session->last_server_ip); client->tmate_session->last_server_ip = xstrdup(client->server_ip); } // fall through case SSH_READY: read_channel(client); } } static void __on_ssh_client_event(__unused evutil_socket_t fd, __unused short what, void *arg) { on_ssh_client_event(arg); } static void kill_ssh_client(struct tmate_ssh_client *client, const char *fmt, ...) { bool last_client; va_list ap; char *message = NULL; TAILQ_REMOVE(&client->tmate_session->clients, client, node); last_client = TAILQ_EMPTY(&client->tmate_session->clients); if (fmt && last_client) { va_start(ap, fmt); xvasprintf(&message, fmt, ap); va_end(ap); tmate_status_message("%s", message); } tmate_debug("SSH client killed (%s)", client->server_ip); if (client->ev_ssh) { event_del(client->ev_ssh); event_free(client->ev_ssh); client->ev_ssh = NULL; } if (client->state == SSH_READY) { tmate_encoder_set_ready_callback(&client->tmate_session->encoder, NULL, NULL); tmate_decoder_destroy(&client->tmate_session->decoder); client->tmate_session->min_sx = -1; client->tmate_session->min_sy = -1; recalculate_sizes(); } if (client->session) { /* ssh_free() also frees the associated channels. */ ssh_free(client->session); client->session = NULL; client->channel = NULL; } if (last_client) tmate_reconnect_session(client->tmate_session, message); free(client->server_ip); free(client); } void connect_ssh_client(struct tmate_ssh_client *client) { assert(!client->session); client->state = SSH_INIT; on_ssh_client_event(client); } static void ssh_log_function(int priority, const char *function, const char *buffer, __unused void *userdata) { tmate_debug("[%d] [%s] %s", priority, function, buffer); } 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, 0, sizeof(*client)); ssh_set_log_callback(ssh_log_function); 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; return client; } tmate-2.4.0/tmate.1000077700000000000000000000000001356407164200151052tmux.1ustar00rootroot00000000000000tmate-2.4.0/tmate.h000066400000000000000000000146101356407164200141240ustar00rootroot00000000000000#ifndef TMATE_H #define TMATE_H #include #include #include #include #include #include "tmux.h" #define tmate_debug(...) log_emit(LOG_DEBUG, __VA_ARGS__) #define tmate_info(...) log_emit(LOG_INFO, __VA_ARGS__) #define tmate_fatal(...) fatalx( __VA_ARGS__) /* tmate-msgpack.c */ typedef void tmate_encoder_write_cb(void *userdata, struct evbuffer *buffer); struct tmate_encoder { msgpack_packer pk; tmate_encoder_write_cb *ready_callback; void *userdata; struct evbuffer *buffer; struct event *ev_buffer; bool ev_active; }; extern void tmate_encoder_init(struct tmate_encoder *encoder, tmate_encoder_write_cb *callback, void *userdata); extern void tmate_encoder_destroy(struct tmate_encoder *encoder); extern void tmate_encoder_set_ready_callback(struct tmate_encoder *encoder, tmate_encoder_write_cb *callback, void *userdata); extern void msgpack_pack_string(msgpack_packer *pk, const char *str); extern void msgpack_pack_boolean(msgpack_packer *pk, bool value); #define _pack(enc, what, ...) msgpack_pack_##what(&(enc)->pk, ##__VA_ARGS__) struct tmate_unpacker; struct tmate_decoder; typedef void tmate_decoder_reader(void *userdata, struct tmate_unpacker *uk); struct tmate_decoder { struct msgpack_unpacker unpacker; tmate_decoder_reader *reader; void *userdata; }; extern void tmate_decoder_init(struct tmate_decoder *decoder, tmate_decoder_reader *reader, void *userdata); extern void tmate_decoder_destroy(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); struct tmate_unpacker { int argc; msgpack_object *argv; }; extern void init_unpacker(struct tmate_unpacker *uk, msgpack_object obj); extern void tmate_decoder_error(void); extern int64_t unpack_int(struct tmate_unpacker *uk); extern bool unpack_bool(struct tmate_unpacker *uk); extern void unpack_buffer(struct tmate_unpacker *uk, const char **buf, size_t *len); extern char *unpack_string(struct tmate_unpacker *uk); extern void unpack_array(struct tmate_unpacker *uk, struct tmate_unpacker *nested); #define unpack_each(nested_uk, tmp_uk, uk) \ for (unpack_array(uk, tmp_uk); \ (tmp_uk)->argc > 0 && (init_unpacker(nested_uk, (tmp_uk)->argv[0]), 1); \ (tmp_uk)->argv++, (tmp_uk)->argc--) /* tmate-encoder.c */ #define TMATE_PROTOCOL_VERSION 6 struct tmate_session; extern void tmate_write_header(void); extern void tmate_write_uname(void); extern void tmate_write_ready(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_set_val(const char *name, const char *value); extern void tmate_exec_cmd_args(int argc, const char **argv); extern void tmate_exec_cmd(struct cmd *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); extern void tmate_write_fin(void); extern void tmate_send_reconnection_state(struct tmate_session *session); /* tmate-decoder.c */ struct tmate_session; extern void tmate_dispatch_slave_message(struct tmate_session *session, struct tmate_unpacker *uk); /* tmate-ssh-client.c */ enum tmate_ssh_client_state_types { SSH_NONE, SSH_INIT, SSH_CONNECT, SSH_AUTH_SERVER, SSH_AUTH_CLIENT_NONE, SSH_AUTH_CLIENT_PUBKEY, SSH_NEW_CHANNEL, 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 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; }; TAILQ_HEAD(tmate_ssh_clients, tmate_ssh_client); extern void connect_ssh_client(struct tmate_ssh_client *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 event_base *ev_base; struct evdns_base *ev_dnsbase; struct event *ev_dns_retry; struct tmate_encoder encoder; struct tmate_decoder decoder; /* True when the slave has sent all the environment variables */ int tmate_env_ready; int min_sx; int min_sy; /* * 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; bool reconnected; struct event *ev_connection_retry; char *last_server_ip; char *reconnection_data; /* * When we reconnect, instead of serializing the key bindings and * options, we replay all the tmux commands we replicated. * It may be a little innacurate to replicate the state, but * it's much easier. */ struct { unsigned int capacity; unsigned int tail; struct { int argc; char **argv; } *cmds; } saved_tmux_cmds; }; extern struct tmate_session tmate_session; extern void tmate_session_init(struct event_base *base); extern void tmate_session_start(void); extern void tmate_reconnect_session(struct tmate_session *session, const char *message); /* tmate-debug.c */ extern void tmate_print_stack_trace(void); extern void tmate_catch_sigsegv(void); extern void tmate_preload_trace_lib(void); /* tmate-msg.c */ extern void __tmate_status_message(const char *fmt, va_list ap); extern void printflike(1, 2) 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-2.4.0/tmux.1000066400000000000000000003074051356407164200137270ustar00rootroot00000000000000.\" $OpenBSD$ .\" .\" Copyright (c) 2007 Nicholas Marriott .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER .\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING .\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .Dd $Mdocdate: March 25 2013 $ .Dt TMATE 1 .Os .Sh NAME .Nm tmate .Nd terminal multiplexer .Sh SYNOPSIS .Nm tmate .Bk -words .Op Fl 2CluvV .Op Fl c Ar shell-command .Op Fl f Ar file .Op Fl L Ar socket-name .Op Fl S Ar socket-path .Op Ar command Op Ar flags .Ek .Sh DESCRIPTION .Nm is a terminal multiplexer: it enables a number of terminals to be created, accessed, and controlled from a single screen. .Nm may be detached from a screen and continue running in the background, then later reattached. .Pp When .Nm is started it creates a new .Em session with a single .Em window and displays it on screen. A status line at the bottom of the screen shows information on the current session and is used to enter interactive commands. .Pp A session is a single collection of .Em pseudo terminals under the management of .Nm . Each session has one or more windows linked to it. A window occupies the entire screen and may be split into rectangular panes, each of which is a separate pseudo terminal (the .Xr pty 4 manual page documents the technical details of pseudo terminals). Any number of .Nm instances may connect to the same session, and any number of windows may be present in the same session. Once all sessions are killed, .Nm exits. .Pp Each session is persistent and will survive accidental disconnection (such as .Xr ssh 1 connection timeout) or intentional detaching (with the .Ql C-b d key strokes). .Nm may be reattached using: .Pp .Dl $ tmate attach .Pp In .Nm , a session is displayed on screen by a .Em client and all sessions are managed by a single .Em server . The server and each client are separate processes which communicate through a socket in .Pa /tmp . .Pp The options are as follows: .Bl -tag -width "XXXXXXXXXXXX" .It Fl 2 Force .Nm to assume the terminal supports 256 colours. .It Fl C Start in control mode (see the .Sx CONTROL MODE section). Given twice .Xo ( Fl CC ) Xc disables echo. .It Fl c Ar shell-command Execute .Ar shell-command using the default shell. If necessary, the .Nm server will be started to retrieve the .Ic default-shell option. This option is for compatibility with .Xr sh 1 when .Nm is used as a login shell. .It Fl f Ar file Specify an alternative configuration file. By default, .Nm loads the system configuration file from .Pa @SYSCONFDIR@/tmux.conf , if present, then looks for a user configuration file at .Pa ~/.tmux.conf and .Pa ~/.tmate.conf . .Pp The configuration file is a set of .Nm commands which are executed in sequence when the server is first started. .Nm loads configuration files once when the server process has started. The .Ic source-file command may be used to load a file later. .Pp .Nm shows any error messages from commands in configuration files in the first session created, and continues to process the rest of the configuration file. .It Fl L Ar socket-name .Nm stores the server socket in a directory under .Ev TMUX_TMPDIR or .Pa /tmp if it is unset. The default socket is named .Em default . This option allows a different socket name to be specified, allowing several independent .Nm servers to be run. Unlike .Fl S a full path is not necessary: the sockets are all created in the same directory. .Pp If the socket is accidentally removed, the .Dv SIGUSR1 signal may be sent to the .Nm server process to recreate it (note that this will fail if any parent directories are missing). .It Fl l Behave as a login shell. This flag currently has no effect and is for compatibility with other shells when using .Nm as a login shell. .It Fl S Ar socket-path Specify a full alternative path to the server socket. If .Fl S is specified, the default socket directory is not used and any .Fl L flag is ignored. .It Fl u .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 Note that .Nm itself always accepts UTF-8; this controls whether it will send UTF-8 characters to the terminal it is running (if not, they are replaced by .Ql _ ) . .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 \&( Switch the attached client to the previous session. .It \&) Switch the attached client to the next session. .It , Rename the current window. .It - Delete the most recently copied buffer of text. .It . Prompt for an index to move the current window. .It 0 to 9 Select windows 0 to 9. .It : Enter the .Nm command prompt. .It ; Move to the previously active pane. .It = Choose which buffer to paste interactively from a list. .It \&? List all key bindings. .It D Choose a client to detach. .It L Switch the attached client back to the last session. .It \&[ Enter copy mode to copy text or view the history. .It \&] Paste the most recently copied buffer of text. .It c Create a new window. .It d Detach the current client. .It f Prompt to search for text in open windows. .It i Display some information about the current window. .It l Move to the previously selected window. .It n Change to the next window. .It o Select the next pane in the current window. .It p Change to the previous window. .It q Briefly display pane indexes. .It r Force redraw of the attached client. .It m Mark the current pane (see .Ic select-pane .Fl m ) . .It M Clear the marked pane. .It s Select a new session for the attached client interactively. .It t Show the time. .It w Choose the current window interactively. .It x Kill the current pane. .It z Toggle zoom state of the current pane. .It { Swap the current pane with the previous pane. .It } Swap the current pane with the next pane. .It ~ Show previous messages from .Nm , if any. .It Page Up Enter copy mode and scroll one page up. .It Up, Down .It Left, Right Change to the pane above, below, to the left, or to the right of the current pane. .It M-1 to M-5 Arrange panes in one of the five preset layouts: even-horizontal, even-vertical, main-horizontal, main-vertical, or tiled. .It Space Arrange the current window in the next preset layout. .It M-n Move to the next window with a bell or activity marker. .It M-o Rotate the panes in the current window backwards. .It M-p Move to the previous window with a bell or activity marker. .It C-Up, C-Down .It C-Left, C-Right Resize the current pane in steps of one cell. .It M-Up, M-Down .It M-Left, M-Right Resize the current pane in steps of five cells. .El .Pp Key bindings may be changed with the .Ic bind-key and .Ic unbind-key commands. .Sh COMMANDS This section contains a list of the commands supported by .Nm . Most commands accept the optional .Fl t (and sometimes .Fl s ) argument with one of .Ar target-client , .Ar target-session .Ar target-window , or .Ar target-pane . These specify the client, session, window or pane which a command should affect. .Pp .Ar target-client 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, .Nm attempts to work out the client currently in use; if that fails, an error is reported. Clients may be listed with the .Ic list-clients command. .Pp .Ar target-session is tried as, in order: .Bl -enum -offset Ds .It A session ID prefixed with a $. .It An exact name of a session (as listed by the .Ic list-sessions command). .It The start of a session name, for example .Ql mysess would match a session named .Ql mysession . .It An .Xr fnmatch 3 pattern which is matched against the session name. .El .Pp If the session name is prefixed with an .Ql = , only an exact match is accepted (so .Ql =mysess will only match exactly .Ql mysess , not .Ql mysession ) . .Pp If a single session is found, it is used as the target session; multiple matches produce an error. If a session is omitted, the current session is used if available; if no current session is available, the most recently used is chosen. .Pp .Ar target-window (or .Ar src-window or .Ar dst-window ) specifies a window in the form .Em session Ns \&: Ns Em window . .Em session follows the same rules as for .Ar target-session , and .Em window is looked for in order as: .Bl -enum -offset Ds .It A special token, listed below. .It A window index, for example .Ql mysession:1 is window 1 in session .Ql mysession . .It A window ID, such as @1. .It An exact window name, such as .Ql mysession:mywindow . .It The start of a window name, such as .Ql mysession:mywin . .It As an .Xr fnmatch 3 pattern matched against the window name. .El .Pp Like sessions, a .Ql = prefix will do an exact match only. An empty window name specifies the next unused index if appropriate (for example the .Ic new-window and .Ic link-window commands) otherwise the current window in .Em session is chosen. .Pp The following special tokens are available to indicate particular windows. Each has a single-character alternative form. .Bl -column "XXXXXXXXXX" "X" .It Sy "Token" Ta Sy "" Ta Sy "Meaning" .It Li "{start}" Ta "^" Ta "The lowest-numbered window" .It Li "{end}" Ta "$" Ta "The highest-numbered window" .It Li "{last}" Ta "!" Ta "The last (previously current) window" .It Li "{next}" Ta "+" Ta "The next window by number" .It Li "{previous}" Ta "-" Ta "The previous window by number" .El .Pp .Ar target-pane (or .Ar src-pane or .Ar dst-pane ) may be a pane ID or takes a similar form to .Ar target-window but with the optional addition of a period followed by a pane index or pane ID, for example: .Ql mysession:mywindow.1 . If the pane index is omitted, the currently active pane in the specified window is used. The following special tokens are available for the pane index: .Bl -column "XXXXXXXXXXXXXX" "X" .It Sy "Token" Ta Sy "" Ta Sy "Meaning" .It Li "{last}" Ta "!" Ta "The last (previously active) pane" .It Li "{next}" Ta "+" Ta "The next pane by number" .It Li "{previous}" Ta "-" Ta "The previous pane by number" .It Li "{top}" Ta "" Ta "The top pane" .It Li "{bottom}" Ta "" Ta "The bottom pane" .It Li "{left}" Ta "" Ta "The leftmost pane" .It Li "{right}" Ta "" Ta "The rightmost pane" .It Li "{top-left}" Ta "" Ta "The top-left pane" .It Li "{top-right}" Ta "" Ta "The top-right pane" .It Li "{bottom-left}" Ta "" Ta "The bottom-left pane" .It Li "{bottom-right}" Ta "" Ta "The bottom-right pane" .It Li "{up-of}" Ta "" Ta "The pane above the active pane" .It Li "{down-of}" Ta "" Ta "The pane below the active pane" .It Li "{left-of}" Ta "" Ta "The pane to the left of the active pane" .It Li "{right-of}" Ta "" Ta "The pane to the right of the active pane" .El .Pp The tokens .Ql + and .Ql - may be followed by an offset, for example: .Bd -literal -offset indent select-window -t:+2 .Ed .Pp In addition, .Em target-session , .Em target-window or .Em target-pane may consist entirely of the token .Ql {mouse} (alternative form .Ql = ) to specify the most recent mouse event (see the .Sx MOUSE SUPPORT section) or .Ql {marked} (alternative form .Ql ~ ) to specify the marked pane (see .Ic select-pane .Fl m ) . .Pp Sessions, window and panes are each numbered with a unique ID; session IDs are prefixed with a .Ql $ , windows with a .Ql @ , and panes with a .Ql % . These are unique and are unchanged for the life of the session, window or pane in the .Nm server. The pane ID is passed to the child process of the pane in the .Ev TMUX_PANE environment variable. IDs may be displayed using the .Ql session_id , .Ql window_id , or .Ql pane_id formats (see the .Sx FORMATS section) and the .Ic display-message , .Ic list-sessions , .Ic list-windows or .Ic list-panes commands. .Pp .Ar shell-command arguments are .Xr sh 1 commands. This may be a single argument passed to the shell, for example: .Bd -literal -offset indent new-window 'vi /etc/passwd' .Ed .Pp Will run: .Bd -literal -offset indent /bin/sh -c 'vi /etc/passwd' .Ed .Pp Additionally, the .Ic new-window , .Ic new-session , .Ic split-window , .Ic respawn-window and .Ic respawn-pane commands allow .Ar shell-command to be given as multiple arguments and executed directly (without .Ql sh -c ) . This can avoid issues with shell quoting. For example: .Bd -literal -offset indent $ tmate new-window vi /etc/passwd .Ed .Pp Will run .Xr vi 1 directly without invoking the shell. .Pp .Ar command .Op Ar arguments refers to a .Nm command, 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 $ tmate 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 $ tmate kill-window -t :1 $ tmate new-window \e; split-window -d $ tmate 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 dEr .Op Fl c Ar working-directory .Op Fl t Ar target-session .Xc .D1 (alias: Ic attach ) If run from outside .Nm , create a new client in the current terminal and attach it to .Ar target-session . If used from inside, switch the current client. If .Fl d is specified, any other clients attached to the session are detached. .Fl r signifies the client is read-only (only keys bound to the .Ic detach-client or .Ic switch-client commands have any effect) .Pp If no server is started, .Ic attach-session will attempt to start it; this will fail unless sessions are created in the configuration file. .Pp The .Ar target-session rules for .Ic attach-session are slightly adjusted: if .Nm needs to select the most recently used session, it will prefer the most recently used .Em unattached session. .Pp .Fl c will set the session working directory (used for new windows) to .Ar working-directory . .Pp If .Fl E is used, the .Ic update-environment option will not be applied. .It Xo Ic detach-client .Op Fl aP .Op Fl 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 Xo Ic kill-session .Op Fl aC .Op Fl t Ar target-session .Xc Destroy the given session, closing any windows linked to it and no other sessions, and detaching all clients attached to it. If .Fl a is given, all sessions but the specified one is killed. The .Fl C flag clears alerts (bell, activity, or silence) in all windows linked to the session. .It Xo Ic list-clients .Op Fl F Ar format .Op Fl t Ar target-session .Xc .D1 (alias: Ic lsc ) List all clients attached to the server. For the meaning of the .Fl F flag, see the .Sx FORMATS section. If .Ar target-session is specified, list only clients connected to that session. .It 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 AdDEP .Op Fl c Ar start-directory .Op Fl F Ar format .Op Fl n Ar window-name .Op Fl s Ar session-name .Op Fl t Ar 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 this 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, any new windows are linked to both sessions and any windows closed removed from both sessions. The current and previous window and any session options remain independent and either session may be killed without affecting the other. .Fl n and .Ar shell-command are invalid if .Fl t is used. .Pp The .Fl P option prints information about the new session after it has been created. By default, it uses the format .Ql #{session_name}: but a different format may be specified with .Fl F . .Pp If .Fl E is used, the .Ic update-environment option will not be applied. .It Xo Ic refresh-client .Op Fl 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 JT .Op Fl t Ar target-client .Xc .D1 (alias: Ic showmsgs ) Show client messages or server information. Any messages displayed on the status line are saved in a per-client message log, up to a maximum of the limit set by the .Ar message-limit server option. With .Fl t , display the log for .Ar target-client . .Fl J and .Fl T show debugging information about jobs and terminals. .It 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 Elnpr .Op Fl c Ar target-client .Op Fl t Ar target-session .Op Fl T Ar key-table .Xc .D1 (alias: Ic switchc ) Switch the current session for client .Ar target-client to .Ar target-session . If .Fl l , .Fl n or .Fl p is used, the client is moved to the last, next or previous session respectively. .Fl r toggles whether a client is read-only (see the .Ic attach-session command). .Pp If .Fl E is used, .Ic update-environment option will not be applied. .Pp .Fl T sets the client's key table; the next key from the client will be interpreted from .Ar key-table . This may be used to configure multiple prefix keys, or to bind commands to sequences of keys. For example, to make typing .Ql abc run the .Ic list-keys command: .Bd -literal -offset indent bind-key -Ttable2 c list-keys bind-key -Ttable1 b switch-client -Ttable2 bind-key -Troot a switch-client -Ttable1 .Ed .El .Sh WINDOWS AND PANES A .Nm window may be in one of 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 "Append selection" Ta "A" Ta "" .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 "Copy to named buffer" Ta \&" Ta "" .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 again" Ta ";" Ta ";" .It Li "Jump again in reverse" Ta "," Ta "," .It Li "Jump backward" Ta "F" Ta "F" .It Li "Jump forward" Ta "f" Ta "f" .It Li "Jump to backward" Ta "T" Ta "" .It Li "Jump to forward" Ta "t" 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 "Other end of selection" Ta "o" Ta "" .It Li "Paste buffer" Ta "p" Ta "C-y" .It Li "Previous page" Ta "C-b" Ta "Page up" .It Li "Previous space" Ta "B" Ta "" .It Li "Previous word" Ta "b" Ta "M-b" .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 "Select line" Ta "V" Ta "" .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 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 . If .Ic append-selection , .Ic copy-selection , or .Ic start-named-buffer are given the .Fl x flag, .Nm will not exit copy mode after copying. .Ic copy-pipe copies the selection and pipes it to a command. For example the following will bind .Ql C-w not to exit after copying and .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-w copy-selection -x 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 Meu .Op Fl t Ar target-pane .Xc Enter copy mode. The .Fl u option scrolls one page up. .Fl M begins a mouse drag (only valid if bound to a mouse key binding, see .Sx MOUSE SUPPORT ) . .Fl e specifies that scrolling to the bottom of the history (to the visible screen) should exit copy mode. While in copy mode, pressing a key other than those used for scrolling will disable this behaviour. This is intended to allow fast scrolling through a pane's history, for example with: .Bd -literal -offset indent bind PageUp copy-mode -eu .Ed .El .Pp Each window displayed by .Nm may be split into one or more .Em panes ; each pane takes up a certain area of the display and is a separate terminal. A window may be split into panes using the .Ic split-window command. Windows may be split horizontally (with the .Fl h flag) or vertically. Panes may be resized with the .Ic resize-pane command (bound to .Ql C-up , .Ql C-down .Ql C-left and .Ql C-right by default), the current pane may be changed with the .Ic select-pane command and the .Ic rotate-window and .Ic swap-pane commands may be used to swap panes without changing their position. Panes are numbered beginning from zero in the order they are created. .Pp A number of preset .Em layouts are available. These may be selected with the .Ic select-layout command or cycled with .Ic next-layout (bound to .Ql Space by default); once a layout is chosen, panes within it may be moved and resized as normal. .Pp The following layouts are supported: .Bl -tag -width Ds .It Ic even-horizontal Panes are spread out evenly from left to right across the window. .It Ic even-vertical Panes are spread evenly from top to bottom. .It Ic main-horizontal A large (main) pane is shown at the top of the window and the remaining panes are spread from left to right in the leftover space at the bottom. Use the .Em main-pane-height window option to specify the height of the top pane. .It Ic main-vertical Similar to .Ic main-horizontal but the large pane is placed on the left and the others spread from top to bottom along the right. See the .Em main-pane-width window option. .It Ic tiled Panes are spread out as evenly as possible over the window in both rows and columns. .El .Pp In addition, .Ic select-layout may be used to apply a previously used layout - the .Ic list-windows command displays the layout of each window in a form suitable for use with .Ic select-layout . For example: .Bd -literal -offset indent $ tmate list-windows 0: ksh [159x48] layout: bb62,159x48,0,0{79x48,0,0,79x48,80,0} $ tmate 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 s Ar src-pane .Op Fl t Ar dst-window .Xc .D1 (alias: Ic breakp ) Break .Ar src-pane off from its containing window to make it the only pane in .Ar dst-window . If .Fl d is given, the new window does not become the current window. The .Fl P option prints information about the new window after it has been created. By default, it uses the format .Ql #{session_name}:#{window_index} but a different format may be specified with .Fl F . .It Xo Ic capture-pane .Op Fl aepPq .Op Fl b Ar buffer-name .Op Fl E Ar end-line .Op Fl S Ar start-line .Op Fl t Ar target-pane .Xc .D1 (alias: Ic capturep ) Capture the contents of a pane. If .Fl p is given, the output goes to stdout, otherwise to the buffer specified with .Fl b or a new buffer if omitted. If .Fl a is given, the alternate screen is used, and the history is not accessible. If no alternate screen exists, an error will be returned unless .Fl q is given. If .Fl e is given, the output includes escape sequences for text and background attributes. .Fl C also escapes non-printable characters as octal \exxx. .Fl J joins wrapped lines and preserves trailing spaces at each line's end. .Fl P captures only any output that the pane has received that is the beginning of an as-yet incomplete escape sequence. .Pp .Fl S and .Fl E specify the starting and ending line numbers, zero is the first line of the visible pane and negative numbers are lines in the history. .Ql - to .Fl S is the start of the history and to .Fl E the end of the visible pane. The default is to capture only the visible contents of the pane. .It Xo .Ic choose-client .Op Fl 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-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 . .Pp If .Fl s is omitted and a marked pane is present (see .Ic select-pane .Fl m ) , the marked pane is used rather than the current pane. .It Xo Ic kill-pane .Op Fl a .Op Fl t Ar target-pane .Xc .D1 (alias: Ic killp ) Destroy the given pane. If no panes remain in the containing window, it is also destroyed. The .Fl a option kills all but the pane given with .Fl t . .It Xo Ic kill-window .Op Fl a .Op Fl t Ar target-window .Xc .D1 (alias: Ic killw ) Kill the current window or the window at .Ar target-window , removing it from any sessions to which it is linked. The .Fl a option kills all but the window given with .Fl t . .It Xo Ic last-pane .Op Fl de .Op Fl t Ar target-window .Xc .D1 (alias: Ic lastp ) Select the last (previously selected) pane. .Fl e enables or .Fl d disables input to the pane. .It Ic last-window Op Fl t Ar target-session .D1 (alias: Ic last ) Select the last (previously selected) window. If no .Ar target-session is specified, select the last window of the current session. .It Xo Ic link-window .Op Fl adk .Op Fl s Ar src-window .Op Fl t Ar dst-window .Xc .D1 (alias: Ic linkw ) Link the window at .Ar src-window to the specified .Ar dst-window . If .Ar dst-window is specified and no such window exists, the .Ar src-window is linked there. With .Fl a , the window is moved to the next index up (following windows are moved if necessary). If .Fl k is given and .Ar dst-window exists, it is killed, otherwise an error is generated. If .Fl d is given, the newly linked window is not selected. .It Xo Ic list-panes .Op Fl as .Op Fl F Ar format .Op Fl t Ar target .Xc .D1 (alias: Ic lsp ) If .Fl a is given, .Ar target is ignored and all panes on the server are listed. If .Fl s is given, .Ar target is a session (or the current session). If neither is given, .Ar target is a window (or the current window). For the meaning of the .Fl F flag, see the .Sx FORMATS section. .It Xo Ic list-windows .Op Fl a .Op Fl F Ar format .Op Fl t Ar target-session .Xc .D1 (alias: Ic lsw ) If .Fl a is given, list all windows on the server. Otherwise, list windows in the current session or in .Ar target-session . For the meaning of the .Fl F flag, see the .Sx FORMATS section. .It Xo Ic move-pane .Op Fl bdhv .Oo Fl l .Ar size | .Fl p Ar percentage Oc .Op Fl s Ar src-pane .Op Fl t Ar dst-pane .Xc .D1 (alias: Ic movep ) Like .Ic join-pane , but .Ar src-pane and .Ar dst-pane may belong to the same window. .It Xo Ic move-window .Op Fl ardk .Op Fl s Ar src-window .Op Fl t Ar dst-window .Xc .D1 (alias: Ic movew ) This is similar to .Ic link-window , except the window at .Ar src-window is moved to .Ar dst-window . With .Fl r , all windows in the session are renumbered in sequential order, respecting the .Ic base-index option. .It Xo Ic new-window .Op Fl adkP .Op Fl c Ar start-directory .Op Fl F Ar format .Op Fl n Ar window-name .Op Fl t Ar target-window .Op Ar shell-command .Xc .D1 (alias: Ic neww ) Create a new window. With .Fl a , the new window is inserted at the next index up from the specified .Ar target-window , moving windows up if necessary, otherwise .Ar target-window is the new window location. .Pp If .Fl d is given, the session does not make the new window the current window. .Ar target-window represents the window to be created; if the target already exists an error is shown, unless the .Fl k flag is used, in which case it is destroyed. .Ar shell-command is the command to execute. If .Ar shell-command is not specified, the value of the .Ic default-command option is used. .Fl c specifies the working directory in which the new window is created. .Pp When the shell command completes, the window closes. See the .Ic remain-on-exit option to change this behaviour. .Pp 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 DLMRUZ .Op Fl t Ar target-pane .Op Fl x Ar width .Op Fl y Ar height .Op Ar adjustment .Xc .D1 (alias: Ic resizep ) Resize a pane, up, down, left or right by .Ar adjustment with .Fl U , .Fl D , .Fl L or .Fl R , or to an absolute size with .Fl x or .Fl y . The .Ar adjustment is given in lines or cells (the default is 1). .Pp With .Fl Z , the active pane is toggled between zoomed (occupying the whole of the window) and unzoomed (its normal position in the layout). .Pp .Fl M begins mouse resizing (only valid if bound to a mouse key binding, see .Sx MOUSE SUPPORT ) . .It Xo Ic 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 nop .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. .Fl o applies the last set layout if possible (undoes the most recent layout change). .It Xo Ic select-pane .Op Fl DdegLlMmRU .Op Fl P Ar style .Op Fl t Ar target-pane .Xc .D1 (alias: Ic selectp ) Make pane .Ar target-pane the active pane in window .Ar target-window , or set its style (with .Fl P ) . If one of .Fl D , .Fl L , .Fl R , or .Fl U is used, respectively the pane below, to the left, to the right, or above the target pane is used. .Fl l is the same as using the .Ic last-pane command. .Fl e enables or .Fl d disables input to the pane. .Pp .Fl m and .Fl M are used to set and clear the .Em marked pane . There is one marked pane at a time, setting a new marked pane clears the last. The marked pane is the default target for .Fl s to .Ic join-pane , .Ic swap-pane and .Ic swap-window . .Pp Each pane has a style: by default the .Ic window-style and .Ic window-active-style options are used, .Ic select-pane .Fl P sets the style for a single pane. For example, to set the pane 1 background to red: .Bd -literal -offset indent select-pane -t:.1 -P 'bg=red' .Ed .Pp .Fl g shows the current pane style. .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 bdhvP .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. The .Fl b option causes the new pane to be created to the left of or above .Ar target-pane . All other options have the same meaning as for the .Ic new-window command. .It Xo Ic swap-pane .Op Fl dDU .Op Fl s Ar src-pane .Op Fl t Ar dst-pane .Xc .D1 (alias: Ic swapp ) Swap two panes. If .Fl U is used and no source pane is specified with .Fl s , .Ar dst-pane is swapped with the previous pane (before it numerically); .Fl D swaps with the next pane (after it numerically). .Fl d instructs .Nm not to change the active pane. .Pp If .Fl s is omitted and a marked pane is present (see .Ic select-pane .Fl m ) , the marked pane is used rather than the current pane. .It Xo Ic swap-window .Op Fl d .Op Fl s Ar src-window .Op Fl t Ar dst-window .Xc .D1 (alias: Ic swapw ) This is similar to .Ic link-window , except the source and destination windows are swapped. It is an error if no window exists at .Ar src-window . .Pp Like .Ic swap-pane , if .Fl s is omitted and a marked pane is present (see .Ic select-pane .Fl m ) , the window containing the marked pane is used rather than the current window. .It Xo Ic unlink-window .Op Fl k .Op Fl t Ar target-window .Xc .D1 (alias: Ic unlinkw ) Unlink .Ar target-window . Unless .Fl k is given, a window may be unlinked only if it is linked to multiple sessions - windows may not be linked to no sessions; if .Fl k is specified and the window is linked to only one session, it is unlinked and destroyed. .El .Sh KEY BINDINGS .Nm allows a command to be bound to most keys, with or without a prefix key. When specifying keys, most represent themselves (for example .Ql A to .Ql Z ) . Ctrl keys may be prefixed with .Ql C- or .Ql ^ , and Alt (meta) with .Ql M- . In addition, the following special key names are accepted: .Em Up , .Em Down , .Em Left , .Em Right , .Em BSpace , .Em BTab , .Em DC (Delete), .Em End , .Em Enter , .Em Escape , .Em F1 to .Em F12 , .Em Home , .Em IC (Insert), .Em NPage/PageDown/PgDn , .Em PPage/PageUp/PgUp , .Em Space , and .Em Tab . Note that to bind the .Ql \&" or .Ql ' keys, quotation marks are necessary, for example: .Bd -literal -offset indent bind-key '"' split-window bind-key "'" new-window .Ed .Pp Commands related to key bindings are as follows: .Bl -tag -width Ds .It Xo Ic bind-key .Op Fl cnr .Op Fl t Ar mode-table .Op Fl T Ar key-table .Ar key Ar command Op Ar arguments .Xc .D1 (alias: Ic bind ) Bind key .Ar key to .Ar command . Keys are bound in a key table. By default (without -T), the key is bound in the .Em prefix key table. This table is used for keys pressed after the prefix key (for example, by default .Ql c is bound to .Ic new-window in the .Em prefix table, so .Ql C-b c creates a new window). The .Em root table is used for keys pressed without the prefix key: binding .Ql c to .Ic new-window in the .Em root table (not recommended) means a plain .Ql c will create a new window. .Fl n is an alias for .Fl T Ar root . Keys may also be bound in custom key tables and the .Ic switch-client .Fl T command used to switch to them from a key binding. The .Fl r flag indicates this key may repeat, see the .Ic repeat-time option. .Pp If .Fl t is present, .Ar key is bound in .Ar mode-table : the binding for command mode with .Fl c or for normal mode without. See the .Sx WINDOWS AND PANES section and the .Ic list-keys command for information on mode key bindings. .Pp To view the default bindings and possible commands, see the .Ic list-keys command. .It Xo Ic list-keys .Op Fl t Ar mode-table .Op Fl T Ar key-table .Xc .D1 (alias: Ic lsk ) List all key bindings. Without .Fl T all key tables are printed. With .Fl T only .Ar key-table . .Pp With .Fl t , the key bindings in .Ar mode-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 lMR .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. .Pp .Fl M passes through a mouse event (only valid if bound to a mouse key binding, see .Sx MOUSE SUPPORT ) . .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 mode-table .Op Fl T Ar key-table .Ar key .Xc .D1 (alias: Ic unbind ) Unbind the command bound to .Ar key . .Fl c , .Fl n , .Fl T and .Fl t are the same as for .Ic bind-key . If .Fl a is present, all key bindings are removed. .El .Sh OPTIONS The appearance and behaviour of .Nm may be modified by changing the value of various options. There are 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 $ tmate setw -q @foo "abc123" $ tmate 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. If .Fl g is given, the global session or window option is set. The .Fl u flag unsets an option, so a session inherits the option from the global options (or with .Fl g , restores a global option to the default). .Pp The .Fl o flag prevents setting an option that is already set and the .Fl q flag suppresses errors about unknown or ambiguous options. .Pp With .Fl a , and if the option expects a string or a style, .Ar value is appended to the existing setting. For example: .Bd -literal -offset indent set -g status-left "foo" set -ag status-left "bar" .Ed .Pp Will result in .Ql foobar . And: .Bd -literal -offset indent set -g status-style "bg=red" set -ag status-style "fg=blue" .Ed .Pp Will result in a red background .Em and blue foreground. Without .Fl a , the result would be the default background and a blue foreground. .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 default-terminal Ar terminal Set the default terminal for new windows created in this session - the default value of the .Ev TERM environment variable. For .Nm to work correctly, this .Em must be set to .Ql screen , .Ql tmux , .Ql tmate or a derivative of them. .It Ic escape-time Ar time Set the time in milliseconds for which .Nm waits after an escape is input to determine if it is part of a function or meta key sequences. The default is 500 milliseconds. .It Xo Ic exit-unattached .Op Ic on | off .Xc If enabled, the server will exit when there are no attached clients. .It Xo Ic focus-events .Op Ic on | off .Xc When enabled, focus events are requested from the terminal if supported and passed through to applications running in .Nm . Attached clients should be detached and attached again after changing this option. .It Ic history-file Ar path If not empty, a file to which .Nm will write command prompt history on exit and load it from on start. .It Ic message-limit Ar number Set the number of error or information messages to save in the message log for each client. The default is 100. .It Xo Ic set-clipboard .Op Ic on | off .Xc Attempt to set the terminal clipboard content using the \ee]52;...\e007 .Xr xterm 1 escape sequences. This option is on by default if there is an .Em \&Ms entry in the .Xr terminfo 5 description for the client terminal. Note that this feature needs to be enabled in .Xr xterm 1 by setting the resource: .Bd -literal -offset indent disallowedWindowOps: 20,21,SetXprop .Ed .Pp Or changing this property from the .Xr xterm 1 interactive menu when required. .It Ic 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 256 colours: .Bd -literal -offset indent "*256col*:colors=256,xterm*:XT" .Ed .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 | other .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, .Ic current means only bells in windows other than the current window are ignored and .Ic other means bells in the current window are ignored but not those in other windows. .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-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 Xo Ic destroy-unattached .Op Ic on | off .Xc If enabled and the session is no longer attached to any clients, it is destroyed. .It Xo Ic detach-on-destroy .Op Ic on | off .Xc If on (the default), the client is detached when the session it is attached to is destroyed. If off, the client is switched to the most recently active of the remaining sessions. .It Ic display-panes-active-colour Ar colour Set the colour used by the .Ic display-panes command to show the indicator for the active pane. .It Ic display-panes-colour Ar colour Set the colour used by the .Ic display-panes command to show the indicators for inactive panes. .It Ic display-panes-time Ar time Set the time in milliseconds for which the indicators shown by the .Ic display-panes command appear. .It Ic display-time Ar time Set the amount of time for which status line messages and other on-screen indicators are displayed. If set to 0, messages and indicators are displayed until a key is pressed. .Ar time is in milliseconds. .It Ic history-limit Ar lines Set the maximum number of lines held in window history. This setting applies only to new windows - existing window histories are not resized and retain the limit at the point they were created. .It Ic key-table Ar key-table Set the default key table to .Ar key-table instead of .Em root . .It Ic lock-after-time Ar number Lock the session (like the .Ic lock-session command) after .Ar number seconds of inactivity. The default is not to lock (set to 0). .It Ic lock-command Ar shell-command Command to run when locking each client. The default is to run .Xr lock 1 with .Fl np . .It Ic message-command-style Ar style Set status line message command style, where .Ar style is a comma-separated list of characteristics to be specified. .Pp These may be .Ql bg=colour to set the background colour, .Ql fg=colour to set the foreground colour, and a list of attributes as specified below. .Pp The 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. .Pp The 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 , to turn an attribute on, or an attribute prefixed with .Ql no to turn one off. .Pp Examples are: .Bd -literal -offset indent fg=yellow,bold,underscore,blink bg=black,fg=default,noreverse .Ed .Pp With the .Fl a flag to the .Ic set-option command the new style is added otherwise the existing style is replaced. .It Ic message-style Ar style Set status line message style. For how to specify .Ar style , see the .Ic message-command-style option. .It Xo Ic mouse .Op Ic on | off .Xc If on, .Nm captures the mouse and allows mouse events to be bound as key bindings. See the .Sx MOUSE SUPPORT section for details. .It Ic prefix Ar key Set the key accepted as a prefix key. In addition to the standard keys described under .Sx KEY BINDINGS , .Ic prefix can be set to the special key .Ql None to set no prefix. .It Ic prefix2 Ar key Set a secondary key accepted as a prefix key. Like .Ic prefix , .Ic prefix2 can be set to .Ql None . .It Xo Ic renumber-windows .Op Ic on | off .Xc If on, when a window is closed in a session, automatically renumber the other windows in numerical order. This respects the .Ic base-index option if it has been set. If off, do not renumber the windows. .It Ic repeat-time Ar time Allow multiple commands to be entered without pressing the prefix-key again in the specified .Ar time milliseconds (the default is 500). Whether a key repeats may be set when it is bound using the .Fl r flag to .Ic bind-key . Repeat is enabled for the default keys bound to the .Ic resize-pane command. .It Xo Ic set-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]0;...\e007 sequence if the terminal appears to be .Xr xterm 1 . This option is off by default. .It Ic set-titles-string Ar string String used to set the window title if .Ic set-titles is on. Formats are expanded, see the .Sx FORMATS section. .It Xo Ic status .Op Ic on | off .Xc Show or hide the status line. .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 (by default the session name) to the left of the status bar. .Ar string will be passed through .Xr strftime 3 and formats (see .Sx FORMATS ) will be expanded. It may also 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 "#[attributes]" Ta "Colour or attribute change" .It Li "##" Ta "A literal" Ql # .El .Pp For details on how the names and titles can be set see the .Sx "NAMES AND TITLES" section. For a list of allowed attributes see the .Ic message-command-style option. .Pp Examples are: .Bd -literal -offset indent #(sysctl vm.loadavg) #[fg=yellow,bold]#(apm -l)%%#[default] [#S] .Ed .Pp The default is .Ql "[#S] " . .It Ic status-left-length Ar length Set the maximum .Ar length of the left component of the status bar. The default is 10. .It Ic status-left-style Ar style Set the style of the left part of the status line. For how to specify .Ar style , see the .Ic message-command-style option. .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 and character pairs are replaced. .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 Ic status-right-style Ar style Set the style of the right part of the status line. For how to specify .Ar style , see the .Ic message-command-style option. .It Ic status-style Ar style Set status line style. For how to specify .Ar style , see the .Ic message-command-style option. .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-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 agoqu .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 o , .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 (\eek...\ee\e\e). 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 rename the window automatically using the format specified by .Ic automatic-rename-format . This flag is automatically disabled for an individual window when a name is specified at creation with .Ic new-window or .Ic new-session , or later with .Ic rename-window , or with a terminal escape sequence. It may be switched off globally with: .Bd -literal -offset indent set-window-option -g automatic-rename off .Ed .Pp .It Ic automatic-rename-format Ar format The format (see .Sx FORMATS ) used when the .Ic automatic-rename option is enabled. .Pp .It Ic clock-mode-colour Ar colour Set clock colour. .Pp .It Xo Ic clock-mode-style .Op Ic 12 | 24 .Xc Set clock hour format. .Pp .It Ic 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 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 Ic mode-style Ar style Set window modes style. For how to specify .Ar style , see the .Ic message-command-style option. .Pp .It Xo Ic monitor-activity .Op Ic on | off .Xc Monitor for activity in the window. Windows with activity are highlighted in the status line. .Pp .It Xo Ic monitor-silence .Op Ic interval .Xc Monitor for silence (no activity) in the window within .Ic interval seconds. Windows that have been silent for the interval are highlighted in the status line. An interval of zero disables the monitoring. .Pp .It Ic other-pane-height Ar height Set the height of the other panes (not the main pane) in the .Ic main-horizontal layout. If this option is set to 0 (the default), it will have no effect. If both the .Ic main-pane-height and .Ic other-pane-height options are set, the main pane will grow taller to make the other panes the specified height, but will never shrink to do so. .Pp .It Ic other-pane-width Ar width Like .Ic other-pane-height , but set the width of other panes in the .Ic main-vertical layout. .Pp .It Ic pane-active-border-style Ar style Set the pane border style for the currently active pane. For how to specify .Ar style , see the .Ic message-command-style option. Attributes are ignored. .Pp .It Ic pane-base-index Ar index Like .Ic base-index , but set the starting index for pane numbers. .Pp .It Ic pane-border-style Ar style Set the pane border style for panes aside from the active pane. For how to specify .Ar style , see the .Ic message-command-style option. Attributes are ignored. .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 Ic window-active-style Ar style Set the style for the window's active pane. For how to specify .Ar style , see the .Ic message-command-style option. .Pp .It Ic window-status-activity-style Ar style Set status line style for windows with an activity alert. For how to specify .Ar style , see the .Ic message-command-style option. .Pp .It Ic window-status-bell-style Ar style Set status line style for windows with a bell alert. For how to specify .Ar style , see the .Ic message-command-style option. .Pp .It Ic window-status-current-format Ar string Like .Ar window-status-format , but is the format used when the window is the current window. .Pp .It Ic window-status-current-style Ar style Set status line style for the currently active window. For how to specify .Ar style , see the .Ic message-command-style option. .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-last-style Ar style Set status line style for the last active window. For how to specify .Ar style , see the .Ic message-command-style option. .Pp .It Ic window-status-separator Ar string Sets the separator drawn between windows in the status line. The default is a single space character. .Pp .It Ic window-status-style Ar style Set status line style for a single window. For how to specify .Ar style , see the .Ic message-command-style option. .Pp .It Ic window-style Ar style Set the default window style. For how to specify .Ar style , see the .Ic message-command-style option. .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 HOOKS .Nm allows commands to run on various triggers, called .Em hooks . Each hook has a .Em name . The following hooks are available: .Bl -tag -width "XXXXXXXXXXXXXXXX" .It alert-activity Run when a window has activity. See .Ic monitor-activity . .It alert-bell Run when a window has received a bell. .It alert-silence Run when a window has been silent. See .Ic monitor-silence . .It client-attached Run when a client is attached. .It client-detached Run when a client is detached .It client-resized Run when a client is resized. .It pane-died Run when the program running in a pane exits, but .Ic remain-on-exit is on so the pane has not closed. .It pane-exited Run when the program running in a pane exits. .El .Pp Hooks are managed with these commands: .Bl -tag -width Ds .It Xo Ic set-hook .Op Fl g .Op Fl t Ar target-session .Ar hook-name .Ar command .Xc Sets hook .Ar hook-name to .Ar command . If .Fl g is given, .Em hook-name is added to the global list of hooks, otherwise it is added to the session hooks (for .Ar target-session with .Fl t ) . Like options, session hooks inherit from the global ones. .It Xo Ic show-hooks .Op Fl g .Op Fl t Ar target-session .Xc Shows the global list of hooks with .Fl g , otherwise the session hooks. .El .Sh MOUSE SUPPORT If the .Ic mouse option is on (the default is off), .Nm allows mouse events to be bound as keys. The name of each key is made up of a mouse event (such as .Ql MouseUp1 ) and a location suffix (one of .Ql Pane for the contents of a pane, .Ql Border for a pane border or .Ql Status for the status line). The following mouse events are available: .Bl -column "MouseDown1" "MouseDrag1" "WheelDown" -offset indent .It Li "MouseDown1" Ta "MouseUp1" Ta "MouseDrag1" Ta "MouseDragEnd1" .It Li "MouseDown2" Ta "MouseUp2" Ta "MouseDrag2" Ta "MouseDragEnd2" .It Li "MouseDown3" Ta "MouseUp3" Ta "MouseDrag3" Ta "MouseDragEnd3" .It Li "WheelUp" Ta "WheelDown" Ta "" Ta "" .El .Pp Each should be suffixed with a location, for example .Ql MouseDown1Status . .Pp The special token .Ql {mouse} or .Ql = may be used as .Ar target-window or .Ar target-pane in commands bound to mouse key bindings. It resolves to the window or pane over which the mouse event took place (for example, the window in the status line over which button 1 was released for a .Ql MouseUp1Status binding, or the pane over which the wheel was scrolled for a .Ql WheelDownPane binding). .Pp The .Ic send-keys .Fl M flag may be used to forward a mouse event to a pane. .Pp The default key bindings allow the mouse to be used to select and resize panes, to copy text and to change window using the status line. These take effect if the .Ic mouse option is turned on. .Sh FORMATS Certain commands accept the .Fl F flag with a .Ar format argument. This is a string which controls the output format of the command. Replacement variables are enclosed in .Ql #{ and .Ql } , for example .Ql #{session_name} . The possible variables are listed in the table below, or the name of a .Nm option may be used for an option's value. Some variables have a shorter alias such as .Ql #S , and .Ql ## is replaced by a single .Ql # . .Pp Conditionals are available by prefixing with .Ql \&? and separating two alternatives with a comma; if the specified variable exists and is not zero, the first alternative is chosen, otherwise the second is used. For example .Ql #{?session_attached,attached,not attached} will include the string .Ql attached if the session is attached and the string .Ql not attached if it is unattached, or .Ql #{?automatic-rename,yes,no} will include .Ql yes if .Ic automatic-rename is enabled, or .Ql no if not. .Pp A limit may be placed on the length of the resultant string by prefixing it by an .Ql = , a number and a colon. Positive numbers count from the start of the string and negative from the end, so .Ql #{=5:pane_title} will include at most the first 5 characters of the pane title, or .Ql #{=-5:pane_title} the last 5 characters. Prefixing a time variable with .Ql t: will convert it to a string, so if .Ql #{window_activity} gives .Ql 1445765102 , .Ql #{t:window_activity} gives .Ql Sun Oct 25 09:25:02 2015 . The .Ql b: and .Ql d: prefixes are .Xr basename 3 and .Xr dirname 3 of the variable respectively. A prefix of the form .Ql s/foo/bar/: will substitute .Ql foo with .Ql bar throughout. .Pp In addition, the first line of a shell command's output may be inserted using .Ql #() . For example, .Ql #(uptime) will insert the system's uptime. When constructing formats, .Nm does not wait for .Ql #() commands to finish; instead, the previous result from running the same command is used, or a placeholder if the command has not been run before. Commands are executed with the .Nm global environment set (see the .Sx ENVIRONMENT section). .Pp The following variables are available, where appropriate: .Bl -column "XXXXXXXXXXXXXXXXXXX" "XXXXX" .It Sy "Variable name" Ta Sy "Alias" Ta Sy "Replaced with" .It Li "alternate_on" Ta "" Ta "If pane is in alternate screen" .It Li "alternate_saved_x" Ta "" Ta "Saved cursor X in alternate screen" .It Li "alternate_saved_y" Ta "" Ta "Saved cursor Y in alternate screen" .It Li "buffer_sample" Ta "" Ta "Sample of start of buffer" .It Li "buffer_size" Ta "" Ta "Size of the specified buffer in bytes" .It Li "client_activity" Ta "" Ta "Integer time client last had activity" .It Li "client_created" Ta "" Ta "Integer time client created" .It Li "client_control_mode" Ta "" Ta "1 if client is in control mode" .It Li "client_height" Ta "" Ta "Height of client" .It Li "client_key_table" Ta "" Ta "Current key table" .It Li "client_last_session" Ta "" Ta "Name of the client's last session" .It Li "client_pid" Ta "" Ta "PID of client process" .It Li "client_prefix" Ta "" Ta "1 if prefix key has been pressed" .It Li "client_readonly" Ta "" Ta "1 if client is readonly" .It Li "client_session" Ta "" Ta "Name of the client's session" .It Li "client_termname" Ta "" Ta "Terminal name of client" .It Li "client_tty" Ta "" Ta "Pseudo terminal of client" .It Li "client_utf8" Ta "" Ta "1 if client supports utf8" .It Li "client_width" Ta "" Ta "Width of client" .It Li "command_name" Ta "" Ta "Name of command in use, if any" .It Li "cursor_flag" Ta "" Ta "Pane cursor flag" .It Li "cursor_x" Ta "" Ta "Cursor X position in pane" .It Li "cursor_y" Ta "" Ta "Cursor Y position in pane" .It Li "history_bytes" Ta "" Ta "Number of bytes in window history" .It Li "history_limit" Ta "" Ta "Maximum window history lines" .It Li "history_size" Ta "" Ta "Size of history in bytes" .It Li "host" Ta "#H" Ta "Hostname of local host" .It Li "host_short" Ta "#h" Ta "Hostname of local host (no domain name)" .It Li "insert_flag" Ta "" Ta "Pane insert flag" .It Li "keypad_cursor_flag" Ta "" Ta "Pane keypad cursor flag" .It Li "keypad_flag" Ta "" Ta "Pane keypad flag" .It Li "line" Ta "" Ta "Line number in the list" .It Li "mouse_any_flag" Ta "" Ta "Pane mouse any flag" .It Li "mouse_button_flag" Ta "" Ta "Pane mouse button flag" .It Li "mouse_standard_flag" Ta "" Ta "Pane mouse standard flag" .It Li "pane_active" Ta "" Ta "1 if active pane" .It Li "pane_bottom" Ta "" Ta "Bottom of pane" .It Li "pane_current_command" Ta "" Ta "Current command if available" .It Li "pane_current_path" Ta "" Ta "Current path if available" .It Li "pane_dead" Ta "" Ta "1 if pane is dead" .It Li "pane_dead_status" Ta "" Ta "Exit status of process in dead pane" .It Li "pane_height" Ta "" Ta "Height of pane" .It Li "pane_id" Ta "#D" Ta "Unique pane ID" .It Li "pane_in_mode" Ta "" Ta "If pane is in a mode" .It Li "pane_input_off" Ta "" Ta "If input to pane is disabled" .It Li "pane_index" Ta "#P" Ta "Index of pane" .It Li "pane_left" Ta "" Ta "Left of pane" .It Li "pane_pid" Ta "" Ta "PID of first process in pane" .It Li "pane_right" Ta "" Ta "Right of pane" .It Li "pane_start_command" Ta "" Ta "Command pane started with" .It Li "pane_synchronized" Ta "" Ta "If pane is synchronized" .It Li "pane_tabs" Ta "" Ta "Pane tab positions" .It Li "pane_title" Ta "#T" Ta "Title of pane" .It Li "pane_top" Ta "" Ta "Top of pane" .It Li "pane_tty" Ta "" Ta "Pseudo terminal of pane" .It Li "pane_width" Ta "" Ta "Width of pane" .It Li "pid" Ta "" Ta "Server PID" .It Li "scroll_region_lower" Ta "" Ta "Bottom of scroll region in pane" .It Li "scroll_region_upper" Ta "" Ta "Top of scroll region in pane" .It Li "scroll_position" Ta "" Ta "Scroll position in copy mode" .It Li "session_alerts" Ta "" Ta "List of window indexes with alerts" .It Li "session_attached" Ta "" Ta "Number of clients session is attached to" .It Li "session_activity" Ta "" Ta "Integer time of session last activity" .It Li "session_created" Ta "" Ta "Integer time session created" .It Li "session_last_attached" Ta "" Ta "Integer time session last attached" .It Li "session_group" Ta "" Ta "Number of session group" .It Li "session_grouped" Ta "" Ta "1 if session in a group" .It Li "session_height" Ta "" Ta "Height of session" .It Li "session_id" Ta "" Ta "Unique session ID" .It Li "session_many_attached" Ta "" Ta "1 if multiple clients attached" .It Li "session_name" Ta "#S" Ta "Name of session" .It Li "session_width" Ta "" Ta "Width of session" .It Li "session_windows" Ta "" Ta "Number of windows in session" .It Li "socket_path" Ta "" "Server socket path" .It Li "start_time" Ta "" Ta "Server start time" .It Li "window_activity" Ta "" Ta "Integer time of window last activity" .It Li "window_active" Ta "" Ta "1 if window active" .It Li "window_bell_flag" Ta "" Ta "1 if window has bell" .It Li "window_find_matches" Ta "" Ta "Matched data from the find-window" .It Li "window_flags" Ta "#F" Ta "Window flags" .It Li "window_height" Ta "" Ta "Height of window" .It Li "window_id" Ta "" Ta "Unique window ID" .It Li "window_index" Ta "#I" Ta "Index of window" .It Li "window_last_flag" Ta "" Ta "1 if window is the last used" .It Li "window_layout" Ta "" Ta "Window layout description, ignoring zoomed window panes" .It Li "window_linked" Ta "" Ta "1 if window is linked across sessions" .It Li "window_name" Ta "#W" Ta "Name of window" .It Li "window_panes" Ta "" Ta "Number of panes in window" .It Li "window_silence_flag" Ta "" Ta "1 if window has silence alert" .It Li "window_visible_layout" Ta "" Ta "Window layout description, respecting zoomed window panes" .It Li "window_width" Ta "" Ta "Width of window" .It Li "window_zoomed_flag" Ta "" Ta "1 if window is zoomed" .It Li "wrap_flag" Ta "" Ta "Pane wrap flag" .El .Sh 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 gs .Op Fl t Ar target-session .Op Ar variable .Xc .D1 (alias: Ic showenv ) Display the environment for .Ar target-session or the global environment with .Fl g . If .Ar variable is omitted, all variables are shown. Variables removed from the environment are prefixed with .Ql - . If .Fl s is used, the output is formatted as a set of Bourne shell commands. .El .Sh STATUS LINE .Nm includes an optional status line which is displayed in the bottom line of each terminal. 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 "The window has been silent for the monitor-silence interval." .It Li "M" Ta "The window contains the marked pane." .It Li "Z" Ta "The window's active pane is zoomed." .El .Pp The # symbol relates to the .Ic monitor-activity window option. The window name is printed in inverted colours if an alert (bell, activity or silence) is present. .Pp The colour and attributes of the status line may be configured, the entire status line using the .Ic status-style session option and individual windows using the .Ic window-status-style window option. .Pp The status line is automatically refreshed at interval if it has changed, the interval may be controlled with the .Ic status-interval session option. .Pp Commands related to the status line are as follows: .Bl -tag -width Ds .It Xo Ic command-prompt .Op Fl 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 set of named .Em paste buffers . Each buffer may be either explicitly or automatically named. Explicitly named buffers are named when created with the .Ic set-buffer or .Ic load-buffer commands, or by renaming an automatically named buffer with .Ic set-buffer .Fl n . Automatically named buffers are given a name such as .Ql buffer0001 , .Ql buffer0002 and so on. When the .Ic buffer-limit option is reached, the oldest automatically named buffer is deleted. Explicitly named are not subject to .Ic buffer-limit and may be deleted with .Ic delete-buffer command. .Pp Buffers may be added using .Ic copy-mode or the .Ic set-buffer and .Ic load-buffer commands, and pasted into a window using the .Ic paste-buffer command. If a buffer command is used and no buffer is specified, the most recently added automatically named buffer is assumed. .Pp A configurable history buffer is also maintained for each window. By default, up to 2000 lines are kept; this can be altered with the .Ic history-limit option (see the .Ic set-option command above). .Pp The buffer commands are as follows: .Bl -tag -width Ds .It Xo .Ic choose-buffer .Op Fl 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 name 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-name .D1 (alias: Ic deleteb ) Delete the buffer named .Ar buffer-name , or the most recently added automatically named buffer if not specified. .It Xo Ic list-buffers .Op Fl F Ar format .Xc .D1 (alias: Ic lsb ) List the global buffers. For the meaning of the .Fl F flag, see the .Sx FORMATS section. .It Xo Ic load-buffer .Op Fl b Ar buffer-name .Ar path .Xc .D1 (alias: Ic loadb ) Load the contents of the specified paste buffer from .Ar path . .It Xo Ic paste-buffer .Op Fl dpr .Op Fl b Ar buffer-name .Op Fl s Ar separator .Op Fl t Ar target-pane .Xc .D1 (alias: Ic pasteb ) Insert the contents of a paste buffer into the specified pane. If not specified, paste into the current one. With .Fl d , also delete the paste buffer. When output, any linefeed (LF) characters in the paste buffer are replaced with a separator, by default carriage return (CR). A custom separator may be specified using the .Fl s flag. The .Fl r flag means to do no replacement (equivalent to a separator of LF). If .Fl p is specified, paste bracket control codes are inserted around the buffer if the application has requested bracketed paste mode. .It Xo Ic save-buffer .Op Fl a .Op Fl b Ar buffer-name .Ar path .Xc .D1 (alias: Ic saveb ) Save the contents of the specified paste buffer to .Ar path . The .Fl a option appends to rather than overwriting the file. .It Xo Ic set-buffer .Op Fl a .Op Fl b Ar buffer-name .Op Fl n Ar new-buffer-name .Ar data .Xc .D1 (alias: Ic setb ) Set the contents of the specified buffer to .Ar data . The .Fl a option appends to rather than overwriting the buffer. The .Fl n option renames the buffer to .Ar new-buffer-name . .It Xo Ic show-buffer .Op Fl b Ar buffer-name .Xc .D1 (alias: Ic showb ) Display the contents of the specified buffer. .El .Sh MISCELLANEOUS Miscellaneous commands are as follows: .Bl -tag -width Ds .It Ic clock-mode Op Fl t Ar target-pane Display a large clock. .It Xo Ic if-shell .Op Fl bF .Op Fl t Ar target-pane .Ar shell-command command .Op Ar command .Xc .D1 (alias: Ic if ) Execute the first .Ar command if .Ar shell-command returns success or the second .Ar command otherwise. Before being executed, .Ar shell-command is expanded using the rules specified in the .Sx FORMATS section, including those relevant to .Ar target-pane . With .Fl b , .Ar shell-command is run in the background. .Pp If .Fl F is given, .Ar shell-command is not executed but considered success if neither empty nor zero (after formats are expanded). .It Ic lock-server .D1 (alias: Ic lock ) Lock each client individually by running the command specified by the .Ic lock-command option. .It Xo Ic run-shell .Op Fl b .Op Fl t Ar target-pane .Ar shell-command .Xc .D1 (alias: Ic run ) Execute .Ar shell-command in the background without creating a window. Before being executed, shell-command is expanded using the rules specified in the .Sx FORMATS section. With .Fl b , the command is run in the background. After it finishes, any output to stdout is displayed in copy mode (in the pane specified by .Fl t or the current pane if omitted). If the command doesn't return success, the exit status is also displayed. .It Xo Ic wait-for .Op Fl L | S | U .Ar channel .Xc .D1 (alias: Ic wait ) When used without options, prevents the client from exiting until woken using .Ic wait-for .Fl S with the same channel. When .Fl L is used, the channel is locked and any clients that try to lock the same channel are made to wait until the channel is unlocked with .Ic wait-for .Fl U . This command only works from outside .Nm . .El .Sh TERMINFO EXTENSIONS .Nm understands some unofficial extensions to .Xr terminfo 5 : .Bl -tag -width Ds .It Em Cs , Cr Set the cursor colour. The first takes a single string argument and is used to set the colour; the second takes no arguments and restores the default cursor colour. If set, a sequence such as this may be used to change the cursor colour from inside .Nm : .Bd -literal -offset indent $ printf '\e033]12;red\e033\e\e' .Ed .It Em \&Ss , Se Set or reset the cursor style. If set, a sequence such as this may be used to change the cursor to an underline: .Bd -literal -offset indent $ printf '\e033[4 q' .Ed .Pp If .Em Se is not set, \&Ss with argument 0 will be used to reset the cursor style instead. .It Em \&Tc Indicate that the terminal supports the .Ql direct colour RGB escape sequence (for example, \ee[38;2;255;255;255m). .It Em \&Ms Store the current buffer in the host terminal's selection (clipboard). See the .Em set-clipboard option above and the .Xr xterm 1 man page. .El .Sh CONTROL MODE .Nm offers a textual interface called .Em control mode . This allows applications to communicate with .Nm using a simple text-only protocol. .Pp In control mode, a client sends .Nm commands or command sequences terminated by newlines on standard input. Each command will produce one block of output on standard output. An output block consists of a .Em %begin line followed by the output (which may be empty). The output block ends with a .Em %end or .Em %error . .Em %begin and matching .Em %end or .Em %error have two arguments: an integer time (as seconds from epoch) and command number. For example: .Bd -literal -offset indent %begin 1363006971 2 0: ksh* (1 panes) [80x24] [layout b25f,80x24,0,0,2] @2 (active) %end 1363006971 2 .Ed .Pp 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 Ar window-visible-layout Ar window-flags The layout of a window with ID .Ar window-id changed. The new layout is .Ar window-layout . The window's visible layout is .Ar window-visible-layout and the window flags are .Ar window-flags . .It Ic %output Ar pane-id Ar value A window pane produced output. .Ar value escapes non-printable characters and backslash as octal \\xxx. .It Ic %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 "@SYSCONFDIR@/tmux.confXXX" -compact .It Pa ~/.tmux.conf Default .Nm configuration file. .It Pa @SYSCONFDIR@/tmux.conf System-wide configuration file. .El .Sh EXAMPLES To create a new .Nm session running .Xr vi 1 : .Pp .Dl $ tmate new-session vi .Pp Most commands have a shorter form, known as an alias. For new-session, this is .Ic new : .Pp .Dl $ tmate 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 $ tmate 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 $ tmate attach-session .Pp Typing .Ql C-b \&? lists the current key bindings in the current window; up and down may be used to navigate the list or .Ql q to exit from it. .Pp Commands to be run when the .Nm server is started may be placed in the .Pa ~/.tmux.conf configuration file. Common examples include: .Pp Changing the default prefix key: .Bd -literal -offset indent set-option -g prefix C-a unbind-key C-b bind-key C-a send-prefix .Ed .Pp Turning the status line off, or changing its colour: .Bd -literal -offset indent set-option -g status off set-option -g status-style bg=blue .Ed .Pp Setting other options, such as the default command, or locking after 30 minutes of inactivity: .Bd -literal -offset indent set-option -g default-command "exec /bin/ksh" set-option -g lock-after-time 1800 .Ed .Pp Creating new key bindings: .Bd -literal -offset indent bind-key b set-option status bind-key / command-prompt "split-window 'exec man %%'" bind-key S command-prompt "new-window -n %1 'ssh %1'" .Ed .Sh SEE ALSO .Xr pty 4 .Sh AUTHORS .An Nicholas Marriott Aq Mt nicholas.marriott@gmail.com tmate-2.4.0/tmux.c000066400000000000000000000223531356407164200140050ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tmux.h" #include "tmate.h" struct options *global_options; /* server options */ struct options *global_s_options; /* session options */ struct options *global_w_options; /* window options */ struct environ *global_environ; struct hooks *global_hooks; struct timeval start_time; const char *socket_path; __dead void usage(void); static char *make_label(const char *); #ifndef HAVE___PROGNAME char *__progname = (char *) "tmate"; #endif #ifdef TMATE int tmate_foreground; #endif __dead void usage(void) { fprintf(stderr, "Usage: %s [options] [tmux-command [flags]]\n" "\n" "Basic options:\n" " -n specify the session token instead of getting a random one\n" " -r same, but for the read-only token\n" " -k specify an api-key, necessary for using named sessions on tmate.io\n" " -F set the foreground mode, useful for setting remote access\n" " -f set the config file path\n" " -S set the socket path, useful to issue commands to a running tmate instance\n" " -v set verbosity (can be repeated)\n" " -V print version\n" ,__progname); exit(1); } 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); } static char * make_label(const char *label) { char *base, resolved[PATH_MAX], *path, *s; struct stat sb; u_int uid; int saved_errno; #ifdef TMATE int do_random_label = label == NULL; #endif if (label == NULL) label = "default"; uid = getuid(); if ((s = getenv("TMUX_TMPDIR")) != NULL && *s != '\0') xasprintf(&base, "%s/tmate-%u", s, uid); else xasprintf(&base, "%s/tmate-%u", _PATH_TMP, uid); if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST) goto fail; if (lstat(base, &sb) != 0) goto fail; if (!S_ISDIR(sb.st_mode)) { errno = ENOTDIR; goto fail; } if (sb.st_uid != uid || (sb.st_mode & S_IRWXO) != 0) { errno = EACCES; goto fail; } #ifdef TMATE if (do_random_label) label = "XXXXXX"; #endif if (realpath(base, resolved) == NULL) strlcpy(resolved, base, sizeof resolved); xasprintf(&path, "%s/%s", resolved, label); #ifdef TMATE if (do_random_label) mktemp(path); #endif return (path); fail: saved_errno = errno; free(base); errno = saved_errno; return (NULL); } void setblocking(int fd, int state) { int mode; if ((mode = fcntl(fd, F_GETFL)) != -1) { if (!state) mode |= O_NONBLOCK; else mode &= ~O_NONBLOCK; fcntl(fd, F_SETFL, mode); } } const char * find_home(void) { struct passwd *pw; static const char *home; if (home != NULL) return (home); home = getenv("HOME"); if (home == NULL || *home == '\0') { pw = getpwuid(getuid()); if (pw != NULL) home = pw->pw_dir; else home = NULL; } return (home); } #ifdef TMATE static char *api_key; static char *session_name; static char *session_name_ro; static char *authorized_keys; void tmate_load_cli_options(void) { #define SET_OPT(name, val) ({\ if (val) { \ run_headless_command(3, (const char *[]){"set-option", name, val}, DEFER_ERRORS_CFG, NULL); \ free(val); \ val = NULL; \ } \ }) SET_OPT("tmate-api-key", api_key); SET_OPT("tmate-session-name", session_name); SET_OPT("tmate-session-name-ro", session_name_ro); SET_OPT("tmate-authorized-keys", authorized_keys); #undef SET_OPT } #endif int main(int argc, char **argv) { char *path, *label, **var, tmp[PATH_MAX], *shellcmd = NULL; const char *s; int opt, flags, keys; if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL && setlocale(LC_CTYPE, "C.UTF-8") == NULL) { if (setlocale(LC_CTYPE, "") == NULL) errx(1, "invalid LC_ALL, LC_CTYPE or LANG"); s = nl_langinfo(CODESET); if (strcasecmp(s, "UTF-8") != 0 && strcasecmp(s, "UTF8") != 0) errx(1, "need UTF-8 locale (LC_CTYPE) but have %s", s); } setlocale(LC_TIME, ""); tzset(); if (**argv == '-') flags = CLIENT_LOGIN; else flags = 0; #ifdef TMATE tmate_catch_sigsegv(); flags |= CLIENT_256COLOURS | CLIENT_UTF8; #endif label = path = NULL; while ((opt = getopt(argc, argv, "h2c:CdFf:lL:qS:uUVvk:n:r:a:")) != -1) { switch (opt) { case '2': flags |= CLIENT_256COLOURS; break; case 'c': free(shellcmd); shellcmd = xstrdup(optarg); break; case 'C': if (flags & CLIENT_CONTROL) flags |= CLIENT_CONTROLCONTROL; else flags |= CLIENT_CONTROL; break; case 'V': printf("%s %s\n", __progname, VERSION); exit(0); case 'f': set_cfg_file(optarg); break; case 'l': flags |= CLIENT_LOGIN; break; case 'L': free(label); label = xstrdup(optarg); break; case 'q': break; case 'S': free(path); path = xstrdup(optarg); break; case 'u': flags |= CLIENT_UTF8; break; case 'v': log_add_level(); break; case 'F': tmate_foreground = 1; log_add_level(); unsetenv("TMUX"); break; case 'k': api_key = xstrdup(optarg); break; case 'n': session_name = xstrdup(optarg); break; case 'r': session_name_ro = xstrdup(optarg); break; case 'a': authorized_keys = xstrdup(optarg); break; case 'h': default: usage(); } } argc -= optind; argv += optind; if (shellcmd != NULL && argc != 0) usage(); #ifdef __OpenBSD__ if (pledge("stdio rpath wpath cpath flock fattr unix getpw sendfd " "recvfd proc exec tty ps", NULL) != 0) err(1, "pledge"); #endif /* * tmux is a UTF-8 terminal, so if TMUX is set, assume UTF-8. * Otherwise, if the user has set LC_ALL, LC_CTYPE or LANG to contain * UTF-8, it is a safe assumption that either they are using a UTF-8 * terminal, or if not they know that output from UTF-8-capable * programs may be wrong. */ if (getenv("TMUX") != NULL) flags |= CLIENT_UTF8; else { s = getenv("LC_ALL"); if (s == NULL || *s == '\0') s = getenv("LC_CTYPE"); if (s == NULL || *s == '\0') s = getenv("LANG"); if (s == NULL || *s == '\0') s = ""; if (strcasestr(s, "UTF-8") != NULL || strcasestr(s, "UTF8") != NULL) flags |= CLIENT_UTF8; } global_hooks = hooks_create(NULL); global_environ = environ_create(); for (var = environ; *var != NULL; var++) environ_put(global_environ, *var); if (getcwd(tmp, sizeof tmp) != NULL) environ_set(global_environ, "PWD", "%s", tmp); global_options = options_create(NULL); options_table_populate_tree(OPTIONS_TABLE_SERVER, global_options); global_s_options = options_create(NULL); options_table_populate_tree(OPTIONS_TABLE_SESSION, global_s_options); options_set_string(global_s_options, "default-shell", "%s", getshell()); global_w_options = options_create(NULL); options_table_populate_tree(OPTIONS_TABLE_WINDOW, global_w_options); /* Override keys to vi if VISUAL or EDITOR are set. */ if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) { if (strrchr(s, '/') != NULL) s = strrchr(s, '/') + 1; if (strstr(s, "vi") != NULL) keys = MODEKEY_VI; else keys = MODEKEY_EMACS; options_set_number(global_s_options, "status-keys", keys); options_set_number(global_w_options, "mode-keys", keys); } /* * If socket is specified on the command-line with -S or -L, it is * used. Otherwise, $TMUX is checked and if that fails "default" is * used. */ if (path == NULL && label == NULL) { s = getenv("TMUX"); if (s != NULL && *s != '\0' && *s != ',') { path = xstrdup(s); path[strcspn (path, ",")] = '\0'; } } if (path == NULL && (path = make_label(label)) == NULL) { fprintf(stderr, "can't create socket: %s\n", strerror(errno)); exit(1); } socket_path = path; free(label); /* Pass control to the client. */ exit(client_main(event_init(), argc, argv, flags, shellcmd)); } tmate-2.4.0/tmux.h000066400000000000000000002055651356407164200140220ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef TMUX_H #define TMUX_H #define TMATE #define PROTOCOL_VERSION 8 #include #include #include #include #include #include #include #include #ifdef HAVE_UTEMPTER #include #endif #include "compat.h" #include "xmalloc.h" extern char *__progname; extern char **environ; struct client; struct environ; struct input_ctx; struct mouse_event; struct options; struct session; struct tmuxpeer; struct tmuxproc; /* Default global configuration file. */ #define TMUX_CONF "/etc/tmux.conf" /* * 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 microseconds. Must be < 1 second. */ #define NAME_INTERVAL 500000 /* * READ_SIZE is the maximum size of data to hold from a pty (the event high * watermark). READ_BACKOFF is the amount of data waiting to be output to a tty * before pty reads will be backed off. READ_TIME is how long to back off * before the next read (in microseconds) if a tty is above READ_BACKOFF. */ #define READ_SIZE 1024 #define READ_BACKOFF 512 #define READ_TIME 100 /* Attribute to make gcc check printf-like arguments. */ #define printflike(a, b) __attribute__ ((format (printf, a, b))) /* Number of items in array. */ #ifndef nitems #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) #endif /* Bell option values. */ #define BELL_NONE 0 #define BELL_ANY 1 #define BELL_CURRENT 2 #define BELL_OTHER 3 /* Special key codes. */ #define KEYC_NONE 0xffff00000000ULL #define KEYC_UNKNOWN 0xfffe00000000ULL #define KEYC_BASE 0x100000000000ULL /* Key modifier bits. */ #define KEYC_ESCAPE 0x200000000000ULL #define KEYC_CTRL 0x400000000000ULL #define KEYC_SHIFT 0x800000000000ULL /* Mask to obtain key w/o modifiers. */ #define KEYC_MASK_MOD (KEYC_ESCAPE|KEYC_CTRL|KEYC_SHIFT) #define KEYC_MASK_KEY (~KEYC_MASK_MOD) /* Is this a mouse key? */ #define KEYC_IS_MOUSE(key) (((key) & KEYC_MASK_KEY) >= KEYC_MOUSE && \ ((key) & KEYC_MASK_KEY) < KEYC_BSPACE) /* Mouse key codes. */ #define KEYC_MOUSE_KEY(name) \ KEYC_ ## name ## _PANE, \ KEYC_ ## name ## _STATUS, \ KEYC_ ## name ## _BORDER #define KEYC_MOUSE_STRING(name, s) \ { #s "Pane", KEYC_ ## name ## _PANE }, \ { #s "Status", KEYC_ ## name ## _STATUS }, \ { #s "Border", KEYC_ ## name ## _BORDER } /* * A single key. This can be ASCII or Unicode or one of the keys starting at * KEYC_BASE. */ typedef unsigned long long key_code; /* Special key codes. */ enum { /* Focus events. */ KEYC_FOCUS_IN = KEYC_BASE, KEYC_FOCUS_OUT, /* Mouse keys. */ KEYC_MOUSE, /* unclassified mouse event */ KEYC_MOUSE_KEY(MOUSEDOWN1), KEYC_MOUSE_KEY(MOUSEDOWN2), KEYC_MOUSE_KEY(MOUSEDOWN3), KEYC_MOUSE_KEY(MOUSEUP1), KEYC_MOUSE_KEY(MOUSEUP2), KEYC_MOUSE_KEY(MOUSEUP3), KEYC_MOUSE_KEY(MOUSEDRAG1), KEYC_MOUSE_KEY(MOUSEDRAG2), KEYC_MOUSE_KEY(MOUSEDRAG3), KEYC_MOUSE_KEY(MOUSEDRAGEND1), KEYC_MOUSE_KEY(MOUSEDRAGEND2), KEYC_MOUSE_KEY(MOUSEDRAGEND3), KEYC_MOUSE_KEY(WHEELUP), KEYC_MOUSE_KEY(WHEELDOWN), /* Backspace key. */ KEYC_BSPACE, /* Function keys. */ KEYC_F1, KEYC_F2, KEYC_F3, KEYC_F4, KEYC_F5, KEYC_F6, KEYC_F7, KEYC_F8, KEYC_F9, KEYC_F10, KEYC_F11, KEYC_F12, KEYC_IC, KEYC_DC, KEYC_HOME, KEYC_END, KEYC_NPAGE, KEYC_PPAGE, KEYC_BTAB, /* Arrow keys. */ KEYC_UP, KEYC_DOWN, KEYC_LEFT, KEYC_RIGHT, /* Numeric keypad. */ KEYC_KP_SLASH, KEYC_KP_STAR, KEYC_KP_MINUS, KEYC_KP_SEVEN, KEYC_KP_EIGHT, KEYC_KP_NINE, KEYC_KP_PLUS, KEYC_KP_FOUR, KEYC_KP_FIVE, KEYC_KP_SIX, KEYC_KP_ONE, KEYC_KP_TWO, KEYC_KP_THREE, KEYC_KP_ENTER, KEYC_KP_ZERO, KEYC_KP_PERIOD, }; /* Termcap codes. */ enum tty_code_code { TTYC_AX = 0, TTYC_ACSC, /* acs_chars, ac */ TTYC_BCE, /* back_color_erase, ut */ TTYC_BEL, /* bell, bl */ TTYC_BLINK, /* enter_blink_mode, mb */ TTYC_BOLD, /* enter_bold_mode, md */ 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_CS, /* set cursor colour, Cs */ TTYC_CSR, /* change_scroll_region, cs */ 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_CVVIS, /* cursor_visible, vs */ 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, TTYC_KF10, TTYC_KF11, TTYC_KF12, TTYC_KF13, TTYC_KF14, TTYC_KF15, TTYC_KF16, TTYC_KF17, TTYC_KF18, TTYC_KF19, TTYC_KF2, TTYC_KF20, TTYC_KF21, TTYC_KF22, TTYC_KF23, TTYC_KF24, TTYC_KF25, TTYC_KF26, TTYC_KF27, TTYC_KF28, TTYC_KF29, TTYC_KF3, TTYC_KF30, TTYC_KF31, TTYC_KF32, TTYC_KF33, TTYC_KF34, TTYC_KF35, TTYC_KF36, TTYC_KF37, TTYC_KF38, TTYC_KF39, TTYC_KF4, TTYC_KF40, TTYC_KF41, TTYC_KF42, TTYC_KF43, TTYC_KF44, TTYC_KF45, TTYC_KF46, TTYC_KF47, TTYC_KF48, TTYC_KF49, TTYC_KF5, TTYC_KF50, TTYC_KF51, TTYC_KF52, TTYC_KF53, TTYC_KF54, TTYC_KF55, TTYC_KF56, TTYC_KF57, TTYC_KF58, TTYC_KF59, TTYC_KF6, TTYC_KF60, TTYC_KF61, TTYC_KF62, TTYC_KF63, TTYC_KF7, TTYC_KF8, TTYC_KF9, TTYC_KHOM2, TTYC_KHOM3, TTYC_KHOM4, TTYC_KHOM5, TTYC_KHOM6, TTYC_KHOM7, TTYC_KHOME, /* 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_SE, /* reset cursor style, Se */ 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_SS, /* set cursor style, Ss */ TTYC_TC, /* 24-bit "true" colour, Tc */ TTYC_TSL, /* to_status_line, tsl */ TTYC_VPA, /* row_address, cv */ TTYC_XENL, /* eat_newline_glitch, xn */ TTYC_XT, /* xterm(1)-compatible title, XT */ }; /* Message codes. */ enum msgtype { MSG_VERSION = 12, MSG_IDENTIFY_FLAGS = 100, MSG_IDENTIFY_TERM, MSG_IDENTIFY_TTYNAME, MSG_IDENTIFY_OLDCWD, /* unused */ MSG_IDENTIFY_STDIN, MSG_IDENTIFY_ENVIRON, MSG_IDENTIFY_DONE, MSG_IDENTIFY_CLIENTPID, MSG_IDENTIFY_CWD, MSG_COMMAND = 200, MSG_DETACH, MSG_DETACHKILL, MSG_EXIT, MSG_EXITED, MSG_EXITING, MSG_LOCK, MSG_READY, MSG_RESIZE, MSG_SHELL, MSG_SHUTDOWN, MSG_STDERR, MSG_STDIN, MSG_STDOUT, MSG_SUSPEND, MSG_UNLOCK, MSG_WAKEUP, }; /* * Message data. * * Don't forget to bump PROTOCOL_VERSION if any of these change! */ struct msg_command_data { int argc; }; /* followed by packed argv */ struct msg_stdin_data { ssize_t size; char data[BUFSIZ]; }; struct msg_stdout_data { ssize_t size; char data[BUFSIZ]; }; struct msg_stderr_data { ssize_t size; char data[BUFSIZ]; }; /* Mode 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_SWITCHMODECHANGELINE, MODEKEYEDIT_SWITCHMODESUBSTITUTE, MODEKEYEDIT_SWITCHMODESUBSTITUTELINE, MODEKEYEDIT_TRANSPOSECHARS, /* Menu (choice) keys. */ MODEKEYCHOICE_BACKSPACE, MODEKEYCHOICE_BOTTOMLINE, MODEKEYCHOICE_CANCEL, MODEKEYCHOICE_CHOOSE, MODEKEYCHOICE_DOWN, MODEKEYCHOICE_ENDOFLIST, MODEKEYCHOICE_PAGEDOWN, MODEKEYCHOICE_PAGEUP, MODEKEYCHOICE_SCROLLDOWN, MODEKEYCHOICE_SCROLLUP, MODEKEYCHOICE_STARTNUMBERPREFIX, MODEKEYCHOICE_STARTOFLIST, MODEKEYCHOICE_TOPLINE, MODEKEYCHOICE_TREE_COLLAPSE, MODEKEYCHOICE_TREE_COLLAPSE_ALL, MODEKEYCHOICE_TREE_EXPAND, MODEKEYCHOICE_TREE_EXPAND_ALL, MODEKEYCHOICE_TREE_TOGGLE, MODEKEYCHOICE_UP, /* Copy keys. */ MODEKEYCOPY_APPENDSELECTION, 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_OTHEREND, 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_STARTNAMEDBUFFER, MODEKEYCOPY_STARTNUMBERPREFIX, MODEKEYCOPY_STARTOFLINE, MODEKEYCOPY_STARTSELECTION, MODEKEYCOPY_TOPLINE, MODEKEYCOPY_UP, }; /* 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 { key_code 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_entry; 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_BLINKING 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) /* * A single UTF-8 character. UTF8_SIZE must be big enough to hold at least one * combining character as well. */ #define UTF8_SIZE 9 struct utf8_data { u_char data[UTF8_SIZE]; u_char have; u_char size; u_char width; /* 0xff if invalid */ } __packed; enum utf8_state { UTF8_MORE, UTF8_DONE, UTF8_ERROR }; /* Grid attributes. */ #define GRID_ATTR_BRIGHT 0x1 #define GRID_ATTR_DIM 0x2 #define GRID_ATTR_UNDERSCORE 0x4 #define GRID_ATTR_BLINK 0x8 #define GRID_ATTR_REVERSE 0x10 #define GRID_ATTR_HIDDEN 0x20 #define GRID_ATTR_ITALICS 0x40 #define GRID_ATTR_CHARSET 0x80 /* alternative character set */ /* Grid flags. */ #define GRID_FLAG_FG256 0x1 #define GRID_FLAG_BG256 0x2 #define GRID_FLAG_PADDING 0x4 #define GRID_FLAG_EXTENDED 0x8 #define GRID_FLAG_FGRGB 0x10 #define GRID_FLAG_BGRGB 0x20 /* Grid line flags. */ #define GRID_LINE_WRAPPED 0x1 /* Grid cell RGB colours. */ struct grid_cell_rgb { u_char r; u_char g; u_char b; }; /* Grid cell data. */ struct grid_cell { u_char flags; u_char attr; union { u_char fg; struct grid_cell_rgb fg_rgb; }; union { u_char bg; struct grid_cell_rgb bg_rgb; }; struct utf8_data data; }; struct grid_cell_entry { u_char flags; union { u_int offset; struct { u_char attr; u_char fg; u_char bg; u_char data; } data; }; } __packed; /* Grid line. */ struct grid_line { u_int cellsize; struct grid_cell_entry *celldata; u_int extdsize; struct grid_cell *extddata; int flags; } __packed; /* Entire grid of cells. */ struct grid { int flags; #define GRID_HISTORY 0x1 /* scroll lines into history */ u_int sx; u_int sy; u_int hsize; u_int hlimit; struct grid_line *linedata; }; /* Hook data structures. */ struct hook { const char *name; struct cmd_q *cmdq; struct cmd_list *cmdlist; RB_ENTRY(hook) entry; }; /* Option data structures. */ struct options_entry { const char *name; enum { OPTIONS_STRING, OPTIONS_NUMBER, OPTIONS_STYLE } type; char *str; long long num; struct grid_cell style; RB_ENTRY(options_entry) entry; }; /* Scheduled job. */ struct job { enum { JOB_RUNNING, JOB_DEAD, JOB_CLOSED } state; 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; enum { LINE_SEL_NONE, LINE_SEL_LEFT_RIGHT, LINE_SEL_RIGHT_LEFT, } lineflag; int modekeys; 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) /* * Window mode. Windows can be in several modes and this is used to call the * right function to handle input and output. */ struct window_mode { 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 client *, struct session *, key_code, struct mouse_event *); }; /* 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; }; /* Child window structure. */ struct window_pane { u_int id; u_int active_point; 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 #define PANE_FOCUSPUSH 0x10 #define PANE_INPUTOFF 0x20 #define PANE_CHANGED 0x40 int argc; char **argv; char *shell; const char *cwd; pid_t pid; char tty[TTY_NAME_MAX]; int status; int fd; struct bufferevent *event; struct event timer; struct input_ctx *ictx; struct grid_cell colgc; int pipe_fd; struct bufferevent *pipe_event; size_t pipe_off; #ifdef TMATE size_t tmate_off; #endif 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_event; struct timeval name_time; struct event alerts_timer; struct timeval activity_time; #ifdef TMATE struct window_pane *tmate_last_sync_active_pane; #endif struct window_pane *active; struct window_pane *last; struct window_panes panes; int lastlayout; struct layout_cell *layout_root; struct layout_cell *saved_layout_root; char *old_layout; u_int sx; u_int sy; int flags; #define WINDOW_BELL 0x1 #define WINDOW_ACTIVITY 0x2 #define WINDOW_REDRAW 0x4 #define WINDOW_SILENCE 0x8 #define WINDOW_ZOOMED 0x1000 #define WINDOW_FORCEWIDTH 0x2000 #define WINDOW_FORCEHEIGHT 0x4000 #define WINDOW_ALERTFLAGS (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_SILENCE) struct options *options; u_int references; RB_ENTRY(window) entry; }; RB_HEAD(windows, 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_SILENCE 0x4 #define WINLINK_ALERTFLAGS (WINLINK_BELL|WINLINK_ACTIVITY|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; }; /* Environment variable. */ struct environ_entry { char *name; char *value; RB_ENTRY(environ_entry) 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; const char *cwd; struct timeval creation_time; struct timeval last_attached_time; struct timeval activity_time; struct timeval last_activity_time; struct event lock_timer; u_int sx; u_int sy; struct winlink *curw; struct winlink_stack lastw; struct winlinks windows; struct hooks *hooks; struct options *options; #define SESSION_UNATTACHED 0x1 /* not attached to any clients */ #define SESSION_PASTING 0x2 int flags; u_int attached; struct termios *tio; struct environ *environ; int references; TAILQ_ENTRY(session) gentry; RB_ENTRY(session) entry; }; RB_HEAD(sessions, session); /* Mouse button masks. */ #define MOUSE_MASK_BUTTONS 3 #define MOUSE_MASK_SHIFT 4 #define MOUSE_MASK_META 8 #define MOUSE_MASK_CTRL 16 #define MOUSE_MASK_DRAG 32 #define MOUSE_MASK_WHEEL 64 /* Mouse wheel states. */ #define MOUSE_WHEEL_UP 0 #define MOUSE_WHEEL_DOWN 64 /* Mouse helpers. */ #define MOUSE_BUTTONS(b) ((b) & MOUSE_MASK_BUTTONS) #define MOUSE_WHEEL(b) ((b) & MOUSE_MASK_WHEEL) #define MOUSE_DRAG(b) ((b) & MOUSE_MASK_DRAG) #define MOUSE_RELEASE(b) (((b) & MOUSE_MASK_BUTTONS) == 3) /* Mouse input. */ struct mouse_event { int valid; key_code key; int statusat; u_int x; u_int y; u_int b; u_int lx; u_int ly; u_int lb; int s; int w; int wp; u_int sgr_type; u_int sgr_b; }; /* TTY information. */ struct tty_key { char ch; key_code key; struct tty_key *left; struct tty_key *right; struct tty_key *next; }; struct tty_code; struct tty_term { char *name; u_int references; char acs[UCHAR_MAX + 1][2]; struct tty_code *codes; #define TERM_256COLOURS 0x1 #define TERM_EARLYWRAP 0x2 int flags; LIST_ENTRY(tty_term) entry; }; LIST_HEAD(tty_terms, tty_term); struct tty { struct client *client; char *path; u_int sx; u_int sy; u_int cx; u_int cy; u_int cstyle; char *ccolour; int mode; u_int rlower; u_int rupper; char *termname; struct tty_term *term; int fd; struct bufferevent *event; 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 #define TTY_FOCUS 0x40 int flags; int term_flags; struct mouse_event mouse; int mouse_drag_flag; void (*mouse_drag_update)(struct client *, struct mouse_event *); void (*mouse_drag_release)(struct client *, struct mouse_event *); struct event key_timer; struct tty_key *key_tree; }; /* TTY command context. */ 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; u_int msg_num; time_t msg_time; TAILQ_ENTRY(message_entry) entry; }; /* Client connection. */ struct client { struct tmuxpeer *peer; pid_t pid; int fd; struct event event; int retval; struct timeval creation_time; struct timeval activity_time; struct environ *environ; char *title; const char *cwd; char *term; char *ttyname; 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 event status_timer; struct screen status; #define CLIENT_TERMINAL 0x1 #define CLIENT_LOGIN 0x2 #define CLIENT_EXIT 0x4 #define CLIENT_REDRAW 0x8 #define CLIENT_STATUS 0x10 #define CLIENT_REPEAT 0x20 #define CLIENT_SUSPENDED 0x40 /* 0x80 unused */ #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_CONTROLCONTROL 0x4000 #define CLIENT_FOCUSED 0x8000 #define CLIENT_UTF8 0x10000 #define CLIENT_256COLOURS 0x20000 #define CLIENT_IDENTIFIED 0x40000 #define CLIENT_STATUSFORCE 0x80000 #ifdef TMATE /* TODO investigate if we can merge with CLIENT_STATUSFORCE */ #define CLIENT_FORCE_STATUS 0x800000 #endif int flags; struct key_table *keytable; struct event identify_timer; char *message_string; struct event message_timer; u_int message_next; TAILQ_HEAD(, 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; TAILQ_ENTRY(client) entry; }; TAILQ_HEAD(clients, client); /* Parsed arguments structures. */ struct args_entry; RB_HEAD(args_tree, args_entry); struct args { struct args_tree tree; int argc; char **argv; }; /* Command find structures. */ enum cmd_find_type { CMD_FIND_PANE, CMD_FIND_WINDOW, CMD_FIND_SESSION, }; struct cmd_find_state { struct cmd_q *cmdq; int flags; struct cmd_find_state *current; struct session *s; struct winlink *wl; struct window *w; struct window_pane *wp; int idx; }; /* Command find flags. */ #define CMD_FIND_PREFER_UNATTACHED 0x1 #define CMD_FIND_QUIET 0x2 #define CMD_FIND_WINDOW_INDEX 0x4 #define CMD_FIND_DEFAULT_MARKED 0x8 #define CMD_FIND_EXACT_SESSION 0x10 #define CMD_FIND_EXACT_WINDOW 0x20 /* Context for command being executed. */ struct cmd_state { struct client *c; struct cmd_find_state tflag; struct cmd_find_state sflag; }; /* Command and list of commands. */ struct cmd { const struct cmd_entry *entry; struct args *args; char *file; u_int line; #define CMD_CONTROL 0x1 int flags; 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; struct mouse_event mouse; TAILQ_ENTRY(cmd_q_item) qentry; }; TAILQ_HEAD(cmd_q_items, cmd_q_item); /* Command queue. */ struct cmd_q { int references; int flags; #define CMD_Q_DEAD 0x1 #define CMD_Q_REENTRY 0x2 #define CMD_Q_NOHOOKS 0x4 struct client *client; int client_exit; struct cmd_q_items queue; struct cmd_q_item *item; struct cmd *cmd; struct cmd_q *parent; struct cmd_find_state current; struct cmd_state state; time_t time; u_int number; void (*emptyfn)(struct cmd_q *); void *data; TAILQ_ENTRY(cmd_q) waitentry; }; /* Command -c, -t or -s flags. */ enum cmd_entry_flag { CMD_NONE, CMD_CLIENT, CMD_CLIENT_CANFAIL, CMD_SESSION, CMD_SESSION_CANFAIL, CMD_SESSION_PREFERUNATTACHED, CMD_SESSION_WITHPANE, CMD_WINDOW, CMD_WINDOW_CANFAIL, CMD_WINDOW_MARKED, CMD_WINDOW_INDEX, CMD_PANE, CMD_PANE_CANFAIL, CMD_PANE_MARKED, CMD_MOVEW_R, }; /* Command definition. */ struct cmd_entry { const char *name; const char *alias; struct { const char *template; int lower; int upper; } args; const char *usage; enum cmd_entry_flag tflag; enum cmd_entry_flag sflag; enum cmd_entry_flag cflag; #define CMD_STARTSERVER 0x1 #define CMD_READONLY 0x2 int flags; enum cmd_retval (*exec)(struct cmd *, struct cmd_q *); }; /* Key binding and key table. */ struct key_binding { key_code key; struct cmd_list *cmdlist; int can_repeat; RB_ENTRY(key_binding) entry; }; RB_HEAD(key_bindings, key_binding); struct key_table { const char *name; struct key_bindings key_bindings; u_int references; RB_ENTRY(key_table) entry; }; RB_HEAD(key_tables, key_table); /* * Option 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, OPTIONS_TABLE_STYLE }; enum options_table_scope { OPTIONS_TABLE_NONE, OPTIONS_TABLE_SERVER, OPTIONS_TABLE_SESSION, OPTIONS_TABLE_WINDOW, }; struct options_table_entry { const char *name; enum options_table_type type; enum options_table_scope scope; u_int minimum; u_int maximum; const char **choices; const char *default_str; long long default_num; const char *style; }; /* Common command usages. */ #define CMD_TARGET_PANE_USAGE "[-t target-pane]" #define CMD_TARGET_WINDOW_USAGE "[-t target-window]" #define CMD_TARGET_SESSION_USAGE "[-t target-session]" #define CMD_TARGET_CLIENT_USAGE "[-t target-client]" #define CMD_SRCDST_PANE_USAGE "[-s src-pane] [-t dst-pane]" #define CMD_SRCDST_WINDOW_USAGE "[-s src-window] [-t dst-window]" #define CMD_SRCDST_SESSION_USAGE "[-s src-session] [-t dst-session]" #define CMD_SRCDST_CLIENT_USAGE "[-s src-client] [-t dst-client]" #define CMD_BUFFER_USAGE "[-b buffer-name]" /* tmux.c */ extern struct hooks *global_hooks; extern struct options *global_options; extern struct options *global_s_options; extern struct options *global_w_options; extern struct environ *global_environ; extern struct timeval start_time; extern const char *socket_path; #ifdef TMATE extern int tmate_foreground; void tmate_load_cli_options(void); #endif const char *getshell(void); int checkshell(const char *); int areshell(const char *); void setblocking(int, int); const char *find_home(void); /* proc.c */ struct imsg; int proc_send(struct tmuxpeer *, enum msgtype, int, const void *, size_t); int proc_send_s(struct tmuxpeer *, enum msgtype, const char *); struct tmuxproc *proc_start(const char *, struct event_base *, int, void (*)(int)); void proc_loop(struct tmuxproc *, int (*)(void)); void proc_exit(struct tmuxproc *); struct tmuxpeer *proc_add_peer(struct tmuxproc *, int, void (*)(struct imsg *, void *), void *); void proc_remove_peer(struct tmuxpeer *); void proc_kill_peer(struct tmuxpeer *); /* cfg.c */ extern int cfg_finished; extern int cfg_references; extern struct client *cfg_client; extern char **cfg_causes; extern u_int cfg_ncauses; void start_cfg(void); int load_cfg(const char *, struct cmd_q *, char **); void set_cfg_file(const char *); void printflike(1, 2) cfg_add_cause(const char *, ...); void cfg_print_causes(struct cmd_q *); void cfg_show_causes(struct session *); /* paste.c */ struct paste_buffer; const char *paste_buffer_name(struct paste_buffer *); const char *paste_buffer_data(struct paste_buffer *, size_t *); struct paste_buffer *paste_walk(struct paste_buffer *); struct paste_buffer *paste_get_top(const char **); struct paste_buffer *paste_get_name(const char *); void paste_free(struct paste_buffer *); void paste_add(char *, size_t); int paste_rename(const char *, const char *, char **); int paste_set(char *, size_t, const char *, char **); char *paste_make_sample(struct paste_buffer *); /* format.c */ #define FORMAT_STATUS 0x1 #define FORMAT_FORCE 0x2 struct format_tree; struct format_tree *format_create(struct cmd_q *, int); void format_free(struct format_tree *); void printflike(3, 4) format_add(struct format_tree *, const char *, const char *, ...); char *format_expand_time(struct format_tree *, const char *, time_t); char *format_expand(struct format_tree *, const char *); void format_defaults(struct format_tree *, struct client *, struct session *, struct winlink *, struct window_pane *); void format_defaults_window(struct format_tree *, struct window *); void format_defaults_pane(struct format_tree *, struct window_pane *); void format_defaults_paste_buffer(struct format_tree *, struct paste_buffer *); /* hooks.c */ struct hook; struct hooks *hooks_get(struct session *); struct hooks *hooks_create(struct hooks *); void hooks_free(struct hooks *); struct hook *hooks_first(struct hooks *); struct hook *hooks_next(struct hook *); void hooks_add(struct hooks *, const char *, struct cmd_list *); void hooks_copy(struct hooks *, struct hooks *); void hooks_remove(struct hooks *, const char *); struct hook *hooks_find(struct hooks *, const char *); int printflike(4, 5) hooks_run(struct hooks *, struct client *, struct cmd_find_state *, const char *, ...); int printflike(4, 5) hooks_wait(struct hooks *, struct cmd_q *, struct cmd_find_state *, const char *, ...); /* 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 *, key_code, 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 */ struct options *options_create(struct options *); void options_free(struct options *); struct options_entry *options_first(struct options *); struct options_entry *options_next(struct options_entry *); 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 *printflike(3, 4) options_set_string(struct options *, const char *, const char *, ...); char *options_get_string(struct options *, const char *); struct options_entry *options_set_number(struct options *, const char *, long long); long long options_get_number(struct options *, const char *); struct options_entry *options_set_style(struct options *, const char *, const char *, int); struct grid_cell *options_get_style(struct options *, const char *); /* options-table.c */ extern const struct options_table_entry options_table[]; void options_table_populate_tree(enum options_table_scope, 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 **); /* job.c */ extern struct joblist all_jobs; struct job *job_run(const char *, struct session *, const char *, void (*)(struct job *), void (*)(void *), void *); void job_free(struct job *); void job_died(struct job *, int); /* environ.c */ struct environ *environ_create(void); void environ_free(struct environ *); struct environ_entry *environ_first(struct environ *); struct environ_entry *environ_next(struct environ_entry *); void environ_copy(struct environ *, struct environ *); struct environ_entry *environ_find(struct environ *, const char *); void printflike(3, 4) environ_set(struct environ *, const char *, const char *, ...); void environ_clear(struct environ *, const char *); void environ_put(struct environ *, const char *); void environ_unset(struct environ *, const char *); void environ_update(const char *, struct environ *, struct environ *); void environ_push(struct environ *); /* tty.c */ void tty_create_log(void); 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 *, const struct window_pane *); 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); int 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_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_pane(struct tty *, const struct window_pane *, u_int, u_int, u_int); void tty_draw_line(struct tty *, const struct window_pane *, struct screen *, u_int, u_int, u_int); int tty_open(struct tty *, char **); void tty_close(struct tty *); void tty_free(struct tty *); void tty_write(void (*)(struct tty *, const struct tty_ctx *), struct tty_ctx *); int tty_client_ready(struct client *, struct window_pane *wp); 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 *); /* tty-term.c */ extern struct tty_terms tty_terms; u_int tty_term_ncodes(void); struct tty_term *tty_term_find(char *, int, char **); void tty_term_free(struct tty_term *); int tty_term_has(struct tty_term *, enum tty_code_code); const char *tty_term_string(struct tty_term *, enum tty_code_code); const char *tty_term_string1(struct tty_term *, enum tty_code_code, int); const char *tty_term_string2(struct tty_term *, enum tty_code_code, int, int); const char *tty_term_ptr1(struct tty_term *, enum tty_code_code, const void *); const char *tty_term_ptr2(struct tty_term *, enum tty_code_code, const void *, const void *); int tty_term_number(struct tty_term *, enum tty_code_code); int tty_term_flag(struct tty_term *, enum tty_code_code); const char *tty_term_describe(struct tty_term *, enum tty_code_code); /* tty-acs.c */ const char *tty_acs_get(struct tty *, u_char); /* tty-keys.c */ void tty_keys_build(struct tty *); void tty_keys_free(struct tty *); key_code tty_keys_next(struct tty *); /* arguments.c */ int args_cmp(struct args_entry *, struct args_entry *); RB_PROTOTYPE(args_tree, args_entry, entry, args_cmp); struct args *args_create(int, ...); struct args *args_parse(const char *, int, char **); void args_free(struct args *); char *args_print(struct args *); 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-find.c */ int cmd_find_current(struct cmd_find_state *, struct cmd_q *, int); int cmd_find_target(struct cmd_find_state *, struct cmd_find_state *, struct cmd_q *, const char *, enum cmd_find_type, int); struct client *cmd_find_client(struct cmd_q *, const char *, int); void cmd_find_clear_state(struct cmd_find_state *, struct cmd_q *, int); int cmd_find_valid_state(struct cmd_find_state *); void cmd_find_copy_state(struct cmd_find_state *, struct cmd_find_state *); void cmd_find_log_state(const char *, struct cmd_find_state *); int cmd_find_from_session(struct cmd_find_state *, struct session *); int cmd_find_from_winlink(struct cmd_find_state *, struct session *, struct winlink *); int cmd_find_from_window(struct cmd_find_state *, struct window *); int cmd_find_from_pane(struct cmd_find_state *, struct window_pane *); /* 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 **); void cmd_free_argv(int, char **); char *cmd_stringify_argv(int, char **); struct cmd *cmd_parse(int, char **, const char *, u_int, char **); int cmd_prepare_state(struct cmd *, struct cmd_q *, struct cmd_q *); char *cmd_print(struct cmd *); int cmd_mouse_at(struct window_pane *, struct mouse_event *, u_int *, u_int *, int); struct winlink *cmd_mouse_window(struct mouse_event *, struct session **); struct window_pane *cmd_mouse_pane(struct mouse_event *, struct session **, struct winlink **); char *cmd_template_replace(const char *, const char *, int); extern const struct cmd_entry *cmd_table[]; /* cmd-attach-session.c */ enum cmd_retval cmd_attach_session(struct cmd_q *, int, int, const char *, int); /* cmd-list.c */ struct cmd_list *cmd_list_parse(int, char **, const char *, u_int, char **); void cmd_list_free(struct cmd_list *); char *cmd_list_print(struct cmd_list *); /* cmd-queue.c */ struct cmd_q *cmdq_new(struct client *); int cmdq_free(struct cmd_q *); void printflike(2, 3) cmdq_print(struct cmd_q *, const char *, ...); void printflike(2, 3) cmdq_error(struct cmd_q *, const char *, ...); void cmdq_guard(struct cmd_q *, const char *, int); void cmdq_run(struct cmd_q *, struct cmd_list *, struct mouse_event *); void cmdq_append(struct cmd_q *, struct cmd_list *, struct mouse_event *); 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 **); /* cmd-wait-for.c */ #ifdef TMATE void signal_waiting_clients(const char *name); #endif void cmd_wait_for_flush(void); /* client.c */ #define DEFER_ERRORS_CFG 1 int run_headless_command(int argc, const char **argv, int flags, void (*err_callback)(const char *)); void run_initial_client_cmd(void); int client_main(struct event_base *, int, char **, int, const char *); /* key-bindings.c */ RB_PROTOTYPE(key_bindings, key_binding, entry, key_bindings_cmp); RB_PROTOTYPE(key_tables, key_table, entry, key_table_cmp); extern struct key_tables key_tables; int key_table_cmp(struct key_table *, struct key_table *); int key_bindings_cmp(struct key_binding *, struct key_binding *); struct key_table *key_bindings_get_table(const char *, int); void key_bindings_unref_table(struct key_table *); void key_bindings_add(const char *, key_code, int, struct cmd_list *); void key_bindings_remove(const char *, key_code); void key_bindings_remove_table(const char *); void key_bindings_init(void); void key_bindings_dispatch(struct key_binding *, struct client *, struct mouse_event *); /* key-string.c */ key_code key_string_lookup_string(const char *); const char *key_string_lookup_key(key_code); /* alerts.c */ void alerts_reset_all(void); void alerts_queue(struct window *, int); void alerts_check_session(struct session *); /* server.c */ extern int server_exit; extern struct tmuxproc *server_proc; extern struct clients clients; extern struct cmd_find_state marked_pane; void server_set_marked(struct session *, struct winlink *, struct window_pane *); void server_clear_marked(void); int server_is_marked(struct session *, struct winlink *, struct window_pane *); int server_check_marked(void); int server_start(struct event_base *, int, char *); void server_update_socket(void); void server_add_accept(int); /* server-client.c */ void server_client_set_key_table(struct client *, const char *); const char *server_client_get_key_table(struct client *); int server_client_check_nested(struct client *); void server_client_handle_key(struct client *, key_code); void server_client_create(int); int server_client_open(struct client *, char **); void server_client_unref(struct client *); void server_client_lost(struct client *); void server_client_detach(struct client *, enum msgtype); void server_client_loop(void); void server_client_push_stdout(struct client *); void server_client_push_stderr(struct client *); /* server-fn.c */ void server_fill_environ(struct session *, struct environ *); void server_redraw_client(struct client *); void server_status_client(struct client *); void server_redraw_session(struct session *); void server_redraw_session_group(struct session *); void server_status_session(struct session *); void server_status_session_group(struct session *); void server_redraw_window(struct window *); void server_redraw_window_borders(struct window *); void server_status_window(struct window *); void server_lock(void); void server_lock_session(struct session *); void server_lock_client(struct client *); void server_kill_window(struct window *); int server_link_window(struct session *, struct winlink *, struct session *, int, int, int, char **); void server_unlink_window(struct session *, struct winlink *); void server_destroy_pane(struct window_pane *, int); void server_destroy_session_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 *); int server_set_stdin_callback(struct client *, void (*)(struct client *, int, void *), void *, char **); void server_unzoom_window(struct window *); /* status.c */ void status_timer_start(struct client *); void status_timer_start_all(void); int status_at_line(struct client *); struct window *status_get_window_at(struct client *, u_int); int status_redraw(struct client *); void printflike(2, 3) status_message_set(struct client *, const char *, ...); void status_message_clear(struct client *); int status_message_redraw(struct client *); void status_prompt_set(struct client *, const char *, const char *, 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 *, key_code); void status_prompt_update(struct client *, const char *, const char *); void status_prompt_load_history(void); void status_prompt_save_history(void); /* resize.c */ void recalculate_sizes(void); /* input.c */ void input_init(struct window_pane *); void input_free(struct window_pane *); void input_reset(struct window_pane *, int); struct evbuffer *input_pending(struct window_pane *); void input_parse(struct window_pane *); /* input-key.c */ void input_key(struct window_pane *, key_code, struct mouse_event *); /* xterm-keys.c */ char *xterm_keys_lookup(key_code); int xterm_keys_find(const char *, size_t, size_t *, key_code *); /* colour.c */ int colour_find_rgb(u_char, u_char, u_char); 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); /* attributes.c */ const char *attributes_tostring(u_char); int attributes_fromstring(const char *); /* grid.c */ extern const struct grid_cell grid_default_cell; struct grid *grid_create(u_int, u_int, u_int); void grid_destroy(struct grid *); int grid_compare(struct grid *, struct grid *); void grid_collect_history(struct grid *); void grid_scroll_history(struct grid *); void grid_scroll_history_region(struct grid *, u_int, u_int); void grid_clear_history(struct grid *); void grid_expand_line(struct grid *, u_int, u_int); const struct grid_line *grid_peek_line(struct grid *, u_int); void grid_get_cell(struct grid *, u_int, u_int, struct grid_cell *); void grid_set_cell(struct grid *, u_int, u_int, const struct grid_cell *); void grid_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-view.c */ void grid_view_get_cell(struct grid *, u_int, u_int, struct grid_cell *); void grid_view_set_cell(struct grid *, u_int, u_int, const struct grid_cell *); void grid_view_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 printflike(1, 2) screen_write_cstrlen(const char *, ...); void printflike(4, 5) screen_write_cnputs(struct screen_write_ctx *, ssize_t, struct grid_cell *, const char *, ...); size_t printflike(1, 2) screen_write_strlen(const char *, ...); void printflike(3, 4) screen_write_puts(struct screen_write_ctx *, struct grid_cell *, const char *, ...); void printflike(4, 5) screen_write_nputs(struct screen_write_ctx *, ssize_t, struct grid_cell *, const char *, ...); void screen_write_vnputs(struct screen_write_ctx *, ssize_t, struct grid_cell *, const char *, va_list); 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_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, 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 window_cmp(struct window *, struct window *); RB_PROTOTYPE(windows, window, entry, window_cmp); int winlink_cmp(struct winlink *, struct winlink *); RB_PROTOTYPE(winlinks, winlink, entry, winlink_cmp); int window_pane_cmp(struct window_pane *, struct window_pane *); RB_PROTOTYPE(window_pane_tree, window_pane, tree_entry, window_pane_cmp); struct winlink *winlink_find_by_index(struct winlinks *, int); struct winlink *winlink_find_by_window(struct winlinks *, struct window *); struct winlink *winlink_find_by_window_id(struct winlinks *, u_int); 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 *); struct window *window_find_by_id_str(const char *); struct window *window_find_by_id(u_int); void window_update_activity(struct window *); struct window *window_create1(u_int, u_int); struct window *window_create(const char *, int, 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); struct window_pane *window_find_string(struct window *, const char *); int window_has_pane(struct window *, struct window_pane *); int window_set_active_pane(struct window *, struct window_pane *); void window_redraw_active_switch(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_lost_pane(struct window *, struct window_pane *); void window_remove_pane(struct window *, struct window_pane *); struct window_pane *window_pane_at_index(struct window *, u_int); struct window_pane *window_pane_next_by_number(struct window *, struct window_pane *, u_int); struct window_pane *window_pane_previous_by_number(struct window *, struct window_pane *, u_int); int window_pane_index(struct window_pane *, u_int *); u_int window_count_panes(struct window *); void window_destroy_panes(struct window *); struct window_pane *window_pane_find_by_id_str(const char *); struct window_pane *window_pane_find_by_id(u_int); struct window_pane *window_pane_create(struct window *, u_int, u_int, u_int); void window_pane_destroy(struct window_pane *); int window_pane_spawn(struct window_pane *, int, char **, 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 client *, struct session *, key_code, 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 *); int winlink_shuffle_up(struct session *, struct winlink *); /* 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_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 layout_cell *); int layout_parse(struct window *, const char *); /* layout-set.c */ int layout_set_lookup(const char *); u_int layout_set_select(struct window *, u_int); u_int layout_set_next(struct window *); u_int layout_set_previous(struct window *); /* window-clock.c */ extern const struct window_mode window_clock_mode; extern const char window_clock_table[14][5][5]; /* window-copy.c */ extern const struct window_mode window_copy_mode; void window_copy_init_from_pane(struct window_pane *, int); void window_copy_init_for_output(struct window_pane *); void printflike(2, 3) window_copy_add(struct window_pane *, const char *, ...); void window_copy_vadd(struct window_pane *, const char *, va_list); void window_copy_pageup(struct window_pane *); void window_copy_start_drag(struct client *, struct mouse_event *); int window_copy_scroll_position(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); void window_choose_expand_all(struct window_pane *); void window_choose_collapse_all(struct window_pane *); void window_choose_set_current(struct window_pane *, u_int); /* names.c */ void check_window_name(struct window *); char *default_window_name(struct window *); char *format_window_name(struct window *); char *parse_window_name(const char *); /* signal.c */ void set_signals(void(*)(int, short, void *), void *); void clear_signals(int); /* control.c */ void control_callback(struct client *, int, void *); void printflike(2, 3) control_write(struct client *, const char *, ...); void control_write_buffer(struct client *, struct evbuffer *); /* control-notify.c */ void control_notify_input(struct client *, struct window_pane *, 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 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_str(const char *); struct session *session_find_by_id(u_int); struct session *session_create(const char *, int, char **, const char *, const char *, struct environ *, struct termios *, int, u_int, u_int, char **); void session_destroy(struct session *); void session_unref(struct session *); int session_check_name(const char *); void session_update_activity(struct session *, struct timeval *); struct session *session_next_session(struct session *); struct session *session_previous_session(struct session *); struct winlink *session_new(struct session *, const char *, int, char **, const char *, const char *, int, char **); struct winlink *session_attach(struct session *, struct window *, int, char **); int session_detach(struct session *, struct winlink *); int session_has(struct session *, struct window *); int session_is_linked(struct session *, struct window *); int session_next(struct session *, int); int session_previous(struct session *, int); int session_select(struct session *, int); int session_last(struct session *); int session_set_current(struct session *, struct winlink *); struct session_group *session_group_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 *); u_int session_group_count(struct session_group *); 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_set(struct utf8_data *, u_char); void utf8_copy(struct utf8_data *, const struct utf8_data *); enum utf8_state utf8_open(struct utf8_data *, u_char); enum utf8_state utf8_append(struct utf8_data *, u_char); enum utf8_state utf8_combine(const struct utf8_data *, wchar_t *); enum utf8_state utf8_split(wchar_t, struct utf8_data *); int utf8_strvis(char *, const char *, size_t, int); char *utf8_sanitize(const char *); struct utf8_data *utf8_fromcstr(const char *); char *utf8_tocstr(struct utf8_data *); u_int utf8_cstrwidth(const char *); char *utf8_rtrimcstr(const char *, u_int); char *utf8_trimcstr(const char *, u_int); char *utf8_padcstr(const char *, u_int); /* osdep-*.c */ char *osdep_get_name(int, char *); char *osdep_get_cwd(int); struct event_base *osdep_event_init(void); /* log.c */ void log_add_level(void); int log_get_level(void); void log_open_fp(FILE *f); void log_open(const char *); void log_close(void); #define LOG_ERROR 0 #define LOG_INFO 1 #define LOG_DEBUG 2 #define log_debug(...) log_emit(LOG_DEBUG+1, __VA_ARGS__) void printflike(2, 3) log_emit(int level, const char *, ...); __dead void printflike(1, 2) fatal(const char *, ...); __dead void printflike(1, 2) fatalx(const char *, ...); /* style.c */ int style_parse(const struct grid_cell *, struct grid_cell *, const char *); const char *style_tostring(struct grid_cell *); void style_update_new(struct options *, const char *, const char *); void style_update_old(struct options *, const char *, struct grid_cell *); void style_apply(struct grid_cell *, struct options *, const char *); void style_apply_update(struct grid_cell *, struct options *, const char *); int style_equal(const struct grid_cell *, const struct grid_cell *); #endif /* TMUX_H */ tmate-2.4.0/tools/000077500000000000000000000000001356407164200137775ustar00rootroot00000000000000tmate-2.4.0/tools/256colors.pl000066400000000000000000000031741356407164200160770ustar00rootroot00000000000000#!/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-2.4.0/tools/UTF-8-demo.txt000066400000000000000000000333441356407164200162740ustar00rootroot00000000000000 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 £©µÀÆÖÞßéöÿ –—‘“â€â€žâ€ â€¢â€¦â€°â„¢Å“ŠŸž€ ΑΒΓΔΩαβγδω ÐБВГДабвгд ∀∂∈â„∧∪≡∞ ↑↗↨↻⇣ â”┼╔╘░►☺♀ ï¬ï¿½â‘€â‚‚ἠḂӥẄÉËâŽ×ԱრGreetings in various languages: Hello world, ΚαλημέÏα κόσμε, コンニãƒãƒ Box drawing alignment tests: â–ˆ â–‰ â•”â•â•╦â•â•â•— ┌──┬──┠╭──┬──╮ ╭──┬──╮ â”â”â”┳â”â”┓ ┎┒â”┑ â•· â•» â”┯┓ ┌┰┠▊ ╱╲╱╲╳╳╳ ║┌─╨─â”â•‘ │╔â•â•§â•╗│ │╒â•╪â•╕│ │╓─â•─╖│ ┃┌─╂─â”┃ ┗╃╄┙ ╶┼╴╺╋╸┠┼┨ â”╋┥ â–‹ ╲╱╲╱╳╳╳ ║│╲ ╱│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ â•¿ │┃ â”╅╆┓ ╵ ╹ â”—â”·â”› └┸┘ â–Œ ╱╲╱╲╳╳╳ â• â•¡ ╳ ╞╣ ├╢ ╟┤ ├┼─┼─┼┤ ├╫─╂─╫┤ ┣┿╾┼╼┿┫ ┕┛┖┚ ┌┄┄┠╎ â”┅┅┓ ┋ ■╲╱╲╱╳╳╳ ║│╱ ╲│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╽ │┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╠┇ ┋ â–Ž ║└─╥─┘║ │╚â•╤â•â•│ │╘â•╪â•╛│ │╙─╀─╜│ ┃└─╂─┘┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╠┇ ┋ ■╚â•â•â•©â•â•╠└──┴──┘ ╰──┴──╯ ╰──┴──╯ â”—â”â”â”»â”â”â”› ▗▄▖▛▀▜ └╌╌┘ ╎ â”—â•â•â”› ┋ â–▂▃▄▅▆▇█ â–▀▘▙▄▟ tmate-2.4.0/tools/ansicode.txt000066400000000000000000001210751356407164200163330ustar00rootroot00000000000000Summary 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-2.4.0/tools/cmp-cvs.sh000066400000000000000000000003241356407164200157020ustar00rootroot00000000000000#!/bin/ksh 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-2.4.0/tty-acs.c000066400000000000000000000064101356407164200143700ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2010 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" 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" }, /* arrow pointing right */ { ',', "\342\206\220" }, /* arrow pointing left */ { '-', "\342\206\221" }, /* arrow pointing up */ { '.', "\342\206\223" }, /* arrow pointing down */ { '0', "\342\226\256" }, /* solid square block */ { '`', "\342\227\206" }, /* diamond */ { 'a', "\342\226\222" }, /* checker board (stipple) */ { 'f', "\302\260" }, /* degree symbol */ { 'g', "\302\261" }, /* plus/minus */ { 'h', "\342\226\222" }, /* board of squares */ { 'i', "\342\230\203" }, /* lantern symbol */ { 'j', "\342\224\230" }, /* lower right corner */ { 'k', "\342\224\220" }, /* upper right corner */ { 'l', "\342\224\214" }, /* upper left corner */ { 'm', "\342\224\224" }, /* lower left corner */ { 'n', "\342\224\274" }, /* large plus or crossover */ { 'o', "\342\216\272" }, /* scan line 1 */ { 'p', "\342\216\273" }, /* scan line 3 */ { 'q', "\342\224\200" }, /* horizontal line */ { 'r', "\342\216\274" }, /* scan line 7 */ { 's', "\342\216\275" }, /* scan line 9 */ { 't', "\342\224\234" }, /* tee pointing right */ { 'u', "\342\224\244" }, /* tee pointing left */ { 'v', "\342\224\264" }, /* tee pointing up */ { 'w', "\342\224\254" }, /* tee pointing down */ { 'x', "\342\224\202" }, /* vertical line */ { 'y', "\342\211\244" }, /* less-than-or-equal-to */ { 'z', "\342\211\245" }, /* greater-than-or-equal-to */ { '{', "\317\200" }, /* greek pi */ { '|', "\342\211\240" }, /* not-equal */ { '}', "\302\243" }, /* UK pound sign */ { '~', "\302\267" } /* bullet */ }; 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 != NULL && !(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-2.4.0/tty-keys.c000066400000000000000000000515731356407164200146070ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include "tmux.h" /* * 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 *, key_code); void tty_keys_add(struct tty *, const char *, key_code); 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 *); /* Default raw keys. */ struct tty_default_key_raw { const char *string; key_code 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[2^", KEYC_IC|KEYC_CTRL }, { "\033[3^", KEYC_DC|KEYC_CTRL }, { "\033[7^", KEYC_HOME|KEYC_CTRL }, { "\033[8^", KEYC_END|KEYC_CTRL }, { "\033[6^", KEYC_NPAGE|KEYC_CTRL }, { "\033[5^", KEYC_PPAGE|KEYC_CTRL }, { "\033[11$", KEYC_F1|KEYC_SHIFT }, { "\033[12$", KEYC_F2|KEYC_SHIFT }, { "\033[13$", KEYC_F3|KEYC_SHIFT }, { "\033[14$", KEYC_F4|KEYC_SHIFT }, { "\033[15$", KEYC_F5|KEYC_SHIFT }, { "\033[17$", KEYC_F6|KEYC_SHIFT }, { "\033[18$", KEYC_F7|KEYC_SHIFT }, { "\033[19$", KEYC_F8|KEYC_SHIFT }, { "\033[20$", KEYC_F9|KEYC_SHIFT }, { "\033[21$", KEYC_F10|KEYC_SHIFT }, { "\033[23$", KEYC_F11|KEYC_SHIFT }, { "\033[24$", KEYC_F12|KEYC_SHIFT }, { "\033[2$", KEYC_IC|KEYC_SHIFT }, { "\033[3$", KEYC_DC|KEYC_SHIFT }, { "\033[7$", KEYC_HOME|KEYC_SHIFT }, { "\033[8$", KEYC_END|KEYC_SHIFT }, { "\033[6$", KEYC_NPAGE|KEYC_SHIFT }, { "\033[5$", KEYC_PPAGE|KEYC_SHIFT }, { "\033[11@", KEYC_F1|KEYC_CTRL|KEYC_SHIFT }, { "\033[12@", KEYC_F2|KEYC_CTRL|KEYC_SHIFT }, { "\033[13@", KEYC_F3|KEYC_CTRL|KEYC_SHIFT }, { "\033[14@", KEYC_F4|KEYC_CTRL|KEYC_SHIFT }, { "\033[15@", KEYC_F5|KEYC_CTRL|KEYC_SHIFT }, { "\033[17@", KEYC_F6|KEYC_CTRL|KEYC_SHIFT }, { "\033[18@", KEYC_F7|KEYC_CTRL|KEYC_SHIFT }, { "\033[19@", KEYC_F8|KEYC_CTRL|KEYC_SHIFT }, { "\033[20@", KEYC_F9|KEYC_CTRL|KEYC_SHIFT }, { "\033[21@", KEYC_F10|KEYC_CTRL|KEYC_SHIFT }, { "\033[23@", KEYC_F11|KEYC_CTRL|KEYC_SHIFT }, { "\033[24@", KEYC_F12|KEYC_CTRL|KEYC_SHIFT }, { "\033[2@", KEYC_IC|KEYC_CTRL|KEYC_SHIFT }, { "\033[3@", KEYC_DC|KEYC_CTRL|KEYC_SHIFT }, { "\033[7@", KEYC_HOME|KEYC_CTRL|KEYC_SHIFT }, { "\033[8@", KEYC_END|KEYC_CTRL|KEYC_SHIFT }, { "\033[6@", KEYC_NPAGE|KEYC_CTRL|KEYC_SHIFT }, { "\033[5@", KEYC_PPAGE|KEYC_CTRL|KEYC_SHIFT }, /* Focus tracking. */ { "\033[I", KEYC_FOCUS_IN }, { "\033[O", KEYC_FOCUS_OUT }, }; /* Default terminfo(5) keys. */ struct tty_default_key_code { enum tty_code_code code; key_code 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_F1|KEYC_SHIFT }, { TTYC_KF14, KEYC_F2|KEYC_SHIFT }, { TTYC_KF15, KEYC_F3|KEYC_SHIFT }, { TTYC_KF16, KEYC_F4|KEYC_SHIFT }, { TTYC_KF17, KEYC_F5|KEYC_SHIFT }, { TTYC_KF18, KEYC_F6|KEYC_SHIFT }, { TTYC_KF19, KEYC_F7|KEYC_SHIFT }, { TTYC_KF20, KEYC_F8|KEYC_SHIFT }, { TTYC_KF21, KEYC_F9|KEYC_SHIFT }, { TTYC_KF22, KEYC_F10|KEYC_SHIFT }, { TTYC_KF23, KEYC_F11|KEYC_SHIFT }, { TTYC_KF24, KEYC_F12|KEYC_SHIFT }, { TTYC_KF25, KEYC_F1|KEYC_CTRL }, { TTYC_KF26, KEYC_F2|KEYC_CTRL }, { TTYC_KF27, KEYC_F3|KEYC_CTRL }, { TTYC_KF28, KEYC_F4|KEYC_CTRL }, { TTYC_KF29, KEYC_F5|KEYC_CTRL }, { TTYC_KF30, KEYC_F6|KEYC_CTRL }, { TTYC_KF31, KEYC_F7|KEYC_CTRL }, { TTYC_KF32, KEYC_F8|KEYC_CTRL }, { TTYC_KF33, KEYC_F9|KEYC_CTRL }, { TTYC_KF34, KEYC_F10|KEYC_CTRL }, { TTYC_KF35, KEYC_F11|KEYC_CTRL }, { TTYC_KF36, KEYC_F12|KEYC_CTRL }, { TTYC_KF37, KEYC_F1|KEYC_SHIFT|KEYC_CTRL }, { TTYC_KF38, KEYC_F2|KEYC_SHIFT|KEYC_CTRL }, { TTYC_KF39, KEYC_F3|KEYC_SHIFT|KEYC_CTRL }, { TTYC_KF40, KEYC_F4|KEYC_SHIFT|KEYC_CTRL }, { TTYC_KF41, KEYC_F5|KEYC_SHIFT|KEYC_CTRL }, { TTYC_KF42, KEYC_F6|KEYC_SHIFT|KEYC_CTRL }, { TTYC_KF43, KEYC_F7|KEYC_SHIFT|KEYC_CTRL }, { TTYC_KF44, KEYC_F8|KEYC_SHIFT|KEYC_CTRL }, { TTYC_KF45, KEYC_F9|KEYC_SHIFT|KEYC_CTRL }, { TTYC_KF46, KEYC_F10|KEYC_SHIFT|KEYC_CTRL }, { TTYC_KF47, KEYC_F11|KEYC_SHIFT|KEYC_CTRL }, { TTYC_KF48, KEYC_F12|KEYC_SHIFT|KEYC_CTRL }, { TTYC_KF49, KEYC_F1|KEYC_ESCAPE }, { TTYC_KF50, KEYC_F2|KEYC_ESCAPE }, { TTYC_KF51, KEYC_F3|KEYC_ESCAPE }, { TTYC_KF52, KEYC_F4|KEYC_ESCAPE }, { TTYC_KF53, KEYC_F5|KEYC_ESCAPE }, { TTYC_KF54, KEYC_F6|KEYC_ESCAPE }, { TTYC_KF55, KEYC_F7|KEYC_ESCAPE }, { TTYC_KF56, KEYC_F8|KEYC_ESCAPE }, { TTYC_KF57, KEYC_F9|KEYC_ESCAPE }, { TTYC_KF58, KEYC_F10|KEYC_ESCAPE }, { TTYC_KF59, KEYC_F11|KEYC_ESCAPE }, { TTYC_KF60, KEYC_F12|KEYC_ESCAPE }, { TTYC_KF61, KEYC_F1|KEYC_ESCAPE|KEYC_SHIFT }, { TTYC_KF62, KEYC_F2|KEYC_ESCAPE|KEYC_SHIFT }, { TTYC_KF63, KEYC_F3|KEYC_ESCAPE|KEYC_SHIFT }, { 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, key_code key) { struct tty_key *tk; size_t size; const char *keystr; keystr = key_string_lookup_key(key); if ((tk = tty_keys_find(tty, s, strlen(s), &size)) == NULL) { log_debug("new key %s: 0x%llx (%s)", s, key, keystr); tty_keys_add1(&tty->key_tree, s, key); } else { log_debug("replacing key %s: 0x%llx (%s)", s, key, keystr); tk->key = key; } } /* Add next node to the tree. */ void tty_keys_add1(struct tty_key **tkp, const char *s, key_code key) { struct tty_key *tk; /* Allocate a tree entry if there isn't one already. */ tk = *tkp; if (tk == NULL) { tk = *tkp = xcalloc(1, sizeof *tk); tk->ch = *s; tk->key = KEYC_UNKNOWN; } /* Find the next entry. */ if (*s == tk->ch) { /* Move forward in string. */ s++; /* If this is the end of the string, no more is necessary. */ if (*s == '\0') { tk->key = key; return; } /* Use the child tree for the next character. */ tkp = &tk->next; } else { if (*s < tk->ch) tkp = &tk->left; else if (*s > tk->ch) tkp = &tk->right; } /* And recurse to add it. */ tty_keys_add1(tkp, s, key); } /* Initialise a key tree from the table. */ void tty_keys_build(struct tty *tty) { const struct tty_default_key_raw *tdkr; const struct tty_default_key_code *tdkc; u_int i; const char *s; 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_UNKNOWN)) return (tk); /* Move into the next tree for the following character. */ tk = tk->next; } else { if (*buf < tk->ch) tk = tk->left; else if (*buf > tk->ch) tk = tk->right; } /* Move to the next in the tree. */ return (tty_keys_find1(tk, buf, len, size)); } /* * 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. */ key_code tty_keys_next(struct tty *tty) { struct tty_key *tk; struct timeval tv; const char *buf; size_t len, size; cc_t bspace; int delay, expired = 0; key_code key; struct utf8_data ud; enum utf8_state more; u_int i; wchar_t wc; /* 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 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 -2: /* yes, but we don't care. */ key = KEYC_MOUSE; goto discard_key; case 1: /* partial */ 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; } /* 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; } 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_UNKNOWN) key |= KEYC_ESCAPE; goto complete_key; } } /* Is this valid UTF-8? */ if ((more = utf8_open(&ud, (u_char)*buf) == UTF8_MORE)) { size = ud.size; if (len < size) { if (expired) goto discard_key; goto partial_key; } for (i = 1; i < size; i++) more = utf8_append(&ud, (u_char)buf[i]); if (more != UTF8_DONE) goto discard_key; if (utf8_combine(&ud, &wc) != UTF8_DONE) goto discard_key; key = wc; log_debug("UTF-8 key %.*s %#llx", (int)size, buf, key); 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 %#llx", (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_UNKNOWN) server_client_handle_key(tty->client, key); return (1); discard_key: log_debug("discard key %.*s %#llx", (int)size, buf, key); /* Remove data from buffer. */ evbuffer_drain(tty->event->input, size); 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; u_int i, x, y, b, sgr_b; u_char sgr_type, 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_b = 0; sgr_type = ' '; /* First two bytes are always \033[. */ if (buf[0] != '\033') return (-1); if (len == 1) return (1); if (buf[1] != '[') return (-1); if (len == 2) return (1); /* * Third byte is M in old standard (and UTF-8 extension which we do not * support), < in SGR extension. */ if (buf[2] == 'M') { /* Read the three inputs. */ *size = 3; for (i = 0; i < 3; i++) { if (len <= *size) return (1); c = (u_char)buf[(*size)++]; if (i == 0) b = c; else if (i == 1) x = c; else y = c; } log_debug("mouse input: %.*s", (int)*size, buf); /* Check and return the mouse input. */ if (b < 32) return (-1); b -= 32; if (x >= 33) x -= 33; else x = 256 - x; if (y >= 33) y -= 33; else y = 256 - y; } else if (buf[2] == '<') { /* Read the three inputs. */ *size = 3; while (1) { if (len <= *size) return (1); 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--; b = sgr_b; /* Type is M for press, m for release. */ sgr_type = c; if (sgr_type == 'm') b |= 3; /* * Some terminals (like PuTTY 0.63) mistakenly send * button-release events for scroll-wheel button-press event. * Discard it before it reaches any program running inside * tmux. */ if (sgr_type == 'm' && (sgr_b & 64)) return (-2); } else return (-1); /* Fill mouse event. */ m->lx = m->x; m->x = x; m->ly = m->y; m->y = y; m->lb = m->b; m->b = b; m->sgr_type = sgr_type; m->sgr_b = sgr_b; return (0); } tmate-2.4.0/tty-term.c000066400000000000000000000455351356407164200146040ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #if defined(HAVE_CURSES_H) #include #elif defined(HAVE_NCURSES_H) #include #endif #include #include #include #include #include "tmux.h" 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); enum tty_code_type { TTYCODE_NONE = 0, TTYCODE_STRING, TTYCODE_NUMBER, TTYCODE_FLAG, }; struct tty_code { enum tty_code_type type; union { char *string; int number; int flag; } value; }; struct tty_term_code_entry { enum tty_code_type type; const char *name; }; const struct tty_term_code_entry tty_term_codes[] = { [TTYC_ACSC] = { TTYCODE_STRING, "acsc" }, [TTYC_AX] = { TTYCODE_FLAG, "AX" }, [TTYC_BCE] = { TTYCODE_FLAG, "bce" }, [TTYC_BEL] = { TTYCODE_STRING, "bel" }, [TTYC_BLINK] = { TTYCODE_STRING, "blink" }, [TTYC_BOLD] = { TTYCODE_STRING, "bold" }, [TTYC_CIVIS] = { TTYCODE_STRING, "civis" }, [TTYC_CLEAR] = { TTYCODE_STRING, "clear" }, [TTYC_CNORM] = { TTYCODE_STRING, "cnorm" }, [TTYC_COLORS] = { TTYCODE_NUMBER, "colors" }, [TTYC_CR] = { TTYCODE_STRING, "Cr" }, [TTYC_CS] = { TTYCODE_STRING, "Cs" }, [TTYC_CSR] = { 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_CVVIS] = { TTYCODE_STRING, "cvvis" }, [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_KF21] = { TTYCODE_STRING, "kf21" }, [TTYC_KF22] = { TTYCODE_STRING, "kf22" }, [TTYC_KF23] = { TTYCODE_STRING, "kf23" }, [TTYC_KF24] = { TTYCODE_STRING, "kf24" }, [TTYC_KF25] = { TTYCODE_STRING, "kf25" }, [TTYC_KF26] = { TTYCODE_STRING, "kf26" }, [TTYC_KF27] = { TTYCODE_STRING, "kf27" }, [TTYC_KF28] = { TTYCODE_STRING, "kf28" }, [TTYC_KF29] = { TTYCODE_STRING, "kf29" }, [TTYC_KF3] = { TTYCODE_STRING, "kf3" }, [TTYC_KF30] = { TTYCODE_STRING, "kf30" }, [TTYC_KF31] = { TTYCODE_STRING, "kf31" }, [TTYC_KF32] = { TTYCODE_STRING, "kf32" }, [TTYC_KF33] = { TTYCODE_STRING, "kf33" }, [TTYC_KF34] = { TTYCODE_STRING, "kf34" }, [TTYC_KF35] = { TTYCODE_STRING, "kf35" }, [TTYC_KF36] = { TTYCODE_STRING, "kf36" }, [TTYC_KF37] = { TTYCODE_STRING, "kf37" }, [TTYC_KF38] = { TTYCODE_STRING, "kf38" }, [TTYC_KF39] = { TTYCODE_STRING, "kf39" }, [TTYC_KF4] = { TTYCODE_STRING, "kf4" }, [TTYC_KF40] = { TTYCODE_STRING, "kf40" }, [TTYC_KF41] = { TTYCODE_STRING, "kf41" }, [TTYC_KF42] = { TTYCODE_STRING, "kf42" }, [TTYC_KF43] = { TTYCODE_STRING, "kf43" }, [TTYC_KF44] = { TTYCODE_STRING, "kf44" }, [TTYC_KF45] = { TTYCODE_STRING, "kf45" }, [TTYC_KF46] = { TTYCODE_STRING, "kf46" }, [TTYC_KF47] = { TTYCODE_STRING, "kf47" }, [TTYC_KF48] = { TTYCODE_STRING, "kf48" }, [TTYC_KF49] = { TTYCODE_STRING, "kf49" }, [TTYC_KF5] = { TTYCODE_STRING, "kf5" }, [TTYC_KF50] = { TTYCODE_STRING, "kf50" }, [TTYC_KF51] = { TTYCODE_STRING, "kf51" }, [TTYC_KF52] = { TTYCODE_STRING, "kf52" }, [TTYC_KF53] = { TTYCODE_STRING, "kf53" }, [TTYC_KF54] = { TTYCODE_STRING, "kf54" }, [TTYC_KF55] = { TTYCODE_STRING, "kf55" }, [TTYC_KF56] = { TTYCODE_STRING, "kf56" }, [TTYC_KF57] = { TTYCODE_STRING, "kf57" }, [TTYC_KF58] = { TTYCODE_STRING, "kf58" }, [TTYC_KF59] = { TTYCODE_STRING, "kf59" }, [TTYC_KF6] = { TTYCODE_STRING, "kf6" }, [TTYC_KF60] = { TTYCODE_STRING, "kf60" }, [TTYC_KF61] = { TTYCODE_STRING, "kf61" }, [TTYC_KF62] = { TTYCODE_STRING, "kf62" }, [TTYC_KF63] = { TTYCODE_STRING, "kf63" }, [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_SE] = { TTYCODE_STRING, "Se" }, [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_SS] = { TTYCODE_STRING, "Ss" }, [TTYC_TC] = { TTYCODE_FLAG, "Tc" }, [TTYC_TSL] = { TTYCODE_STRING, "tsl" }, [TTYC_VPA] = { TTYCODE_STRING, "vpa" }, [TTYC_XENL] = { TTYCODE_FLAG, "xenl" }, [TTYC_XT] = { TTYCODE_FLAG, "XT" }, }; u_int tty_term_ncodes(void) { return (nitems(tty_term_codes)); } 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 < tty_term_ncodes(); i++) { ent = &tty_term_codes[i]; if (strcmp(entstr, ent->name) != 0) continue; code = &term->codes[i]; 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, 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; term->codes = xcalloc (tty_term_ncodes(), sizeof *term->codes); LIST_INSERT_HEAD(&tty_terms, term, entry); /* Set up curses terminal. */ if (setupterm(name, fd, &error) != OK) { switch (error) { case 1: xasprintf(cause, "can't use hardcopy terminal: %s", name); break; case 0: xasprintf(cause, "missing or unsuitable terminal: %s", name); break; case -1: xasprintf(cause, "can't find terminfo database"); break; default: xasprintf(cause, "unknown error"); break; } goto error; } /* Fill in codes. */ for (i = 0; i < tty_term_ncodes(); i++) { ent = &tty_term_codes[i]; code = &term->codes[i]; code->type = TTYCODE_NONE; switch (ent->type) { case TTYCODE_NONE: break; case TTYCODE_STRING: s = tigetstr((char *) ent->name); if (s == NULL || s == (char *) -1) break; code->type = TTYCODE_STRING; code->value.string = tty_term_strip(s); break; case TTYCODE_NUMBER: n = tigetnum((char *) ent->name); if (n == -1 || n == -2) break; code->type = TTYCODE_NUMBER; code->value.number = n; break; case TTYCODE_FLAG: n = tigetflag((char *) ent->name); if (n == -1) break; code->type = TTYCODE_FLAG; code->value.flag = n; break; } } /* Apply terminal overrides. */ s = options_get_string(global_options, "terminal-overrides"); tty_term_override(term, s); /* 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. */ if (tty_term_number(term, TTYC_COLORS) == 256) term->flags |= TERM_256COLOURS; /* * Terminals without xenl (eat newline glitch) wrap at at $COLUMNS - 1 * rather than $COLUMNS (the cursor can never be beyond $COLUMNS - 1). * * This is irritating, most notably because it is impossible to write * to the very bottom-right of the screen without scrolling. * * Flag the terminal here and apply some workarounds in other places to * do the best possible. */ if (!tty_term_flag(term, TTYC_XENL)) term->flags |= TERM_EARLYWRAP; /* Generate ACS table. If none is present, use nearest ASCII. */ memset(term->acs, 0, sizeof term->acs); if (tty_term_has(term, TTYC_ACSC)) acs = tty_term_string(term, TTYC_ACSC); else acs = "a#j+k+l+m+n+o-p-q-r-s-t+u+v+w+x|y~."; for (; acs[0] != '\0' && acs[1] != '\0'; acs += 2) term->acs[(u_char) acs[0]][0] = acs[1]; /* On terminals with xterm titles (XT), fill in tsl and fsl. */ if (tty_term_flag(term, TTYC_XT) && !tty_term_has(term, TTYC_TSL) && !tty_term_has(term, TTYC_FSL)) { code = &term->codes[TTYC_TSL]; code->value.string = xstrdup("\033]0;"); code->type = TTYCODE_STRING; code = &term->codes[TTYC_FSL]; code->value.string = xstrdup("\007"); code->type = TTYCODE_STRING; } return (term); error: tty_term_free(term); return (NULL); } void tty_term_free(struct tty_term *term) { u_int i; if (--term->references != 0) return; LIST_REMOVE(term, entry); for (i = 0; i < tty_term_ncodes(); i++) { if (term->codes[i].type == TTYCODE_STRING) free(term->codes[i].value.string); } free(term->codes); free(term->name); free(term); } int tty_term_has(struct tty_term *term, enum tty_code_code code) { return (term->codes[code].type != TTYCODE_NONE); } const char * tty_term_string(struct tty_term *term, enum tty_code_code code) { if (!tty_term_has(term, code)) return (""); if (term->codes[code].type != TTYCODE_STRING) fatalx("not a string: %d", code); return (term->codes[code].value.string); } const char * tty_term_string1(struct tty_term *term, enum tty_code_code code, int a) { return (tparm((char *) tty_term_string(term, code), a, 0, 0, 0, 0, 0, 0, 0, 0)); } const char * tty_term_string2(struct tty_term *term, enum tty_code_code code, int a, int b) { return (tparm((char *) tty_term_string(term, code), a, b, 0, 0, 0, 0, 0, 0, 0)); } const char * tty_term_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) fatalx("not a number: %d", code); return (term->codes[code].value.number); } int tty_term_flag(struct tty_term *term, enum tty_code_code code) { if (!tty_term_has(term, code)) return (0); if (term->codes[code].type != TTYCODE_FLAG) fatalx("not a flag: %d", code); return (term->codes[code].value.flag); } const char * tty_term_describe(struct tty_term *term, enum tty_code_code code) { static char s[256]; char out[128]; switch (term->codes[code].type) { case TTYCODE_NONE: xsnprintf(s, sizeof s, "%4u: %s: [missing]", code, tty_term_codes[code].name); break; case TTYCODE_STRING: strnvis(out, term->codes[code].value.string, sizeof out, VIS_OCTAL|VIS_TAB|VIS_NL); xsnprintf(s, sizeof s, "%4u: %s: (string) %s", code, tty_term_codes[code].name, out); break; case TTYCODE_NUMBER: xsnprintf(s, sizeof s, "%4u: %s: (number) %d", code, tty_term_codes[code].name, term->codes[code].value.number); break; case TTYCODE_FLAG: xsnprintf(s, sizeof s, "%4u: %s: (flag) %s", code, tty_term_codes[code].name, term->codes[code].value.flag ? "true" : "false"); break; } return (s); } tmate-2.4.0/tty.c000066400000000000000000001261521356407164200136320ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include "tmux.h" static int tty_log_fd = -1; void tty_read_callback(struct bufferevent *, void *); void tty_error_callback(struct bufferevent *, short, void *); static int tty_same_fg(const struct grid_cell *, const struct grid_cell *); static int tty_same_bg(const struct grid_cell *, const struct grid_cell *); static int tty_same_colours(const struct grid_cell *, const struct grid_cell *); static int tty_is_fg(const struct grid_cell *, int); static int tty_is_bg(const struct grid_cell *, int); void tty_set_italics(struct tty *); int tty_try_256(struct tty *, u_char, const char *); int tty_try_rgb(struct tty *, const struct grid_cell_rgb *, 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 *); int tty_fake_bce(const struct tty *, const struct window_pane *); 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 *, const struct window_pane *); void tty_default_colours(struct grid_cell *, const struct window_pane *); #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) static int tty_same_fg(const struct grid_cell *gc1, const struct grid_cell *gc2) { int flags1, flags2; flags1 = (gc1->flags & (GRID_FLAG_FG256|GRID_FLAG_FGRGB)); flags2 = (gc2->flags & (GRID_FLAG_FG256|GRID_FLAG_FGRGB)); if (flags1 != flags2) return (0); if (flags1 & GRID_FLAG_FGRGB) { if (gc1->fg_rgb.r != gc2->fg_rgb.r) return (0); if (gc1->fg_rgb.g != gc2->fg_rgb.g) return (0); if (gc1->fg_rgb.b != gc2->fg_rgb.b) return (0); return (1); } return (gc1->fg == gc2->fg); } static int tty_same_bg(const struct grid_cell *gc1, const struct grid_cell *gc2) { int flags1, flags2; flags1 = (gc1->flags & (GRID_FLAG_BG256|GRID_FLAG_BGRGB)); flags2 = (gc2->flags & (GRID_FLAG_BG256|GRID_FLAG_BGRGB)); if (flags1 != flags2) return (0); if (flags1 & GRID_FLAG_BGRGB) { if (gc1->bg_rgb.r != gc2->bg_rgb.r) return (0); if (gc1->bg_rgb.g != gc2->bg_rgb.g) return (0); if (gc1->bg_rgb.b != gc2->bg_rgb.b) return (0); return (1); } return (gc1->bg == gc2->bg); } static int tty_same_colours(const struct grid_cell *gc1, const struct grid_cell *gc2) { return (tty_same_fg(gc1, gc2) && tty_same_bg(gc1, gc2)); } static int tty_is_fg(const struct grid_cell *gc, int c) { if (gc->flags & (GRID_FLAG_FG256|GRID_FLAG_FGRGB)) return (0); return (gc->fg == c); } static int tty_is_bg(const struct grid_cell *gc, int c) { if (gc->flags & (GRID_FLAG_BG256|GRID_FLAG_BGRGB)) return (0); return (gc->bg == c); } void tty_create_log(void) { char name[64]; xsnprintf(name, sizeof name, "tmate-out-%ld.log", (long)getpid()); tty_log_fd = open(name, O_WRONLY|O_CREAT|O_TRUNC, 0644); if (tty_log_fd != -1 && fcntl(tty_log_fd, F_SETFD, FD_CLOEXEC) == -1) fatal("fcntl failed"); } int tty_init(struct tty *tty, struct client *c, int fd, char *term) { char *path; if (!isatty(fd)) return (-1); memset(tty, 0, sizeof *tty); 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) return (-1); tty->path = xstrdup(path); tty->cstyle = 0; tty->ccolour = xstrdup(""); tty->flags = 0; tty->term_flags = 0; return (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, char **cause) { tty->term = tty_term_find(tty->termname, tty->fd, 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|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[?1002l\033[?1006l\033[?1005l"); if (tty_term_flag(tty->term, TTYC_XT)) { if (options_get_number(global_options, "focus-events")) { tty->flags |= TTY_FOCUS; tty_puts(tty, "\033[?1004h"); } } tty->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, ""); tty->mouse_drag_flag = 0; tty->mouse_drag_update = NULL; tty->mouse_drag_release = NULL; } void tty_stop_tty(struct tty *tty) { struct winsize ws; if (!(tty->flags & TTY_STARTED)) return; tty->flags &= ~TTY_STARTED; 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_SS) && tty->cstyle != 0) { if (tty_term_has(tty->term, TTYC_SE)) tty_raw(tty, tty_term_string(tty->term, TTYC_SE)); else tty_raw(tty, tty_term_string1(tty->term, TTYC_SS, 0)); } if (tty->mode & MODE_BRACKETPASTE) tty_raw(tty, "\033[?2004l"); tty_raw(tty, tty_term_string(tty->term, TTYC_CR)); tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM)); if (tty_term_has(tty->term, TTYC_KMOUS)) tty_raw(tty, "\033[?1000l\033[?1002l\033[?1006l\033[?1005l"); if (tty_term_flag(tty->term, TTYC_XT)) { if (tty->flags & TTY_FOCUS) { tty->flags &= ~TTY_FOCUS; tty_raw(tty, "\033[?1004l"); } } tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP)); setblocking(tty->fd, 1); } void tty_close(struct tty *tty) { if (event_initialized(&tty->key_timer)) evtimer_del(&tty->key_timer); tty_stop_tty(tty); if (tty->flags & TTY_OPENED) { 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); free(tty->path); 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_italics(struct tty *tty) { const char *s; if (tty_term_has(tty->term, TTYC_SITM)) { s = options_get_string(global_options, "default-terminal"); if (strcmp(s, "screen") != 0 && strncmp(s, "screen-", 7) != 0) { tty_putcode(tty, TTYC_SITM); return; } } tty_putcode(tty, TTYC_SMSO); } void tty_set_title(struct tty *tty, const char *title) { if (!tty_term_has(tty->term, TTYC_TSL) || !tty_term_has(tty->term, TTYC_FSL)) return; tty_putcode(tty, TTYC_TSL); tty_puts(tty, title); tty_putcode(tty, TTYC_FSL); } void tty_force_cursor_colour(struct tty *tty, const char *ccolour) { if (*ccolour == '\0') tty_putcode(tty, TTYC_CR); else tty_putcode_ptr1(tty, TTYC_CS, ccolour); free(tty->ccolour); tty->ccolour = xstrdup(ccolour); } void tty_update_mode(struct tty *tty, int mode, struct screen *s) { int changed; if (s != NULL && strcmp(s->ccolour, tty->ccolour) != 0) tty_force_cursor_colour(tty, s->ccolour); if (tty->flags & TTY_NOCURSOR) mode &= ~MODE_CURSOR; changed = mode ^ tty->mode; if (changed & MODE_BLINKING) { if (tty_term_has(tty->term, TTYC_CVVIS)) tty_putcode(tty, TTYC_CVVIS); else tty_putcode(tty, TTYC_CNORM); changed |= MODE_CURSOR; } if (changed & MODE_CURSOR) { if (mode & MODE_CURSOR) tty_putcode(tty, TTYC_CNORM); else tty_putcode(tty, TTYC_CIVIS); } if (s != NULL && tty->cstyle != s->cstyle) { if (tty_term_has(tty->term, TTYC_SS)) { if (s->cstyle == 0 && tty_term_has(tty->term, TTYC_SE)) tty_putcode(tty, TTYC_SE); else tty_putcode1(tty, TTYC_SS, s->cstyle); } tty->cstyle = s->cstyle; } if (changed & ALL_MOUSE_MODES) { if (mode & ALL_MOUSE_MODES) { /* * Enable the SGR (1006) extension unconditionally, as * 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. */ tty_puts(tty, "\033[?1006h"); 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_BUTTON) tty_puts(tty, "\033[?1002l"); else if (tty->mode & MODE_MOUSE_STANDARD) tty_puts(tty, "\033[?1000l"); tty_puts(tty, "\033[?1006l"); } } if (changed & MODE_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); } /* * Return if BCE is needed but the terminal doesn't have it - it'll need to be * emulated. */ int tty_fake_bce(const struct tty *tty, const struct window_pane *wp) { struct grid_cell gc; memcpy(&gc, &grid_default_cell, sizeof gc); tty_default_colours(&gc, wp); if (gc.bg == 8 && !(gc.flags & GRID_FLAG_BG256)) return (0); return (!tty_term_flag(tty->term, TTYC_BCE)); } /* * 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_pane(tty, wp, i, ctx->xoff, ctx->yoff); } else { for (i = ctx->orupper; i <= ctx->orlower; i++) tty_draw_pane(tty, wp, i, ctx->xoff, ctx->yoff); } } void tty_draw_pane(struct tty *tty, const struct window_pane *wp, u_int py, u_int ox, u_int oy) { tty_draw_line(tty, wp, wp->screen, py, ox, oy); } void tty_draw_line(struct tty *tty, const struct window_pane *wp, struct screen *s, u_int py, u_int ox, u_int oy) { struct grid_cell gc; struct grid_line *gl; u_int i, sx; int flags; flags = tty->flags & TTY_NOCURSOR; tty->flags |= TTY_NOCURSOR; tty_update_mode(tty, tty->mode, 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 position 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++) { grid_view_get_cell(s->grid, i, py, &gc); if (screen_check_selection(s, i, py)) { gc.flags &= ~(GRID_FLAG_FG256|GRID_FLAG_BG256); gc.flags |= s->sel.cell.flags & (GRID_FLAG_FG256|GRID_FLAG_BG256); } tty_cell(tty, &gc, wp); } if (sx < tty->sx) { tty_attributes(tty, &grid_default_cell, wp); 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_fake_bce(tty, wp)) tty_putcode(tty, TTYC_EL); else tty_repeat_space(tty, screen_size_x(s) - sx); } tty->flags = (tty->flags & ~TTY_NOCURSOR) | flags; tty_update_mode(tty, tty->mode, s); } int tty_client_ready(struct client *c, struct window_pane *wp) { if (c->session == NULL || c->tty.term == NULL) return (0); if (c->flags & CLIENT_SUSPENDED) return (0); if (c->tty.flags & TTY_FREEZE) return (0); if (c->session->curw->window != wp->window) return (0); return (1); } void tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *), struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; struct client *c; /* 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; TAILQ_FOREACH(c, &clients, entry) { if (!tty_client_ready(c, wp)) 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_pane(tty, wp, ctx->ocy, ctx->xoff, ctx->yoff); return; } tty_attributes(tty, &grid_default_cell, wp); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); if (!tty_fake_bce(tty, wp) && (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_pane(tty, wp, 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_fake_bce(tty, wp) || (!tty_term_has(tty->term, TTYC_DCH) && !tty_term_has(tty->term, TTYC_DCH1))) { tty_draw_pane(tty, wp, ctx->ocy, ctx->xoff, ctx->yoff); return; } tty_attributes(tty, &grid_default_cell, wp); 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_attributes(tty, &grid_default_cell, ctx->wp); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); if (tty_term_has(tty->term, TTYC_ECH) && !tty_fake_bce(tty, ctx->wp)) 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_fake_bce(tty, ctx->wp) || !tty_term_has(tty->term, TTYC_CSR) || !tty_term_has(tty->term, TTYC_IL1)) { tty_redraw_region(tty, ctx); return; } tty_attributes(tty, &grid_default_cell, ctx->wp); 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_fake_bce(tty, ctx->wp) || !tty_term_has(tty->term, TTYC_CSR) || !tty_term_has(tty->term, TTYC_DL1)) { tty_redraw_region(tty, ctx); return; } tty_attributes(tty, &grid_default_cell, ctx->wp); 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_attributes(tty, &grid_default_cell, wp); tty_cursor_pane(tty, ctx, 0, ctx->ocy); if (tty_pane_full_width(tty, ctx) && !tty_fake_bce(tty, wp) && 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_attributes(tty, &grid_default_cell, wp); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); if (tty_pane_full_width(tty, ctx) && tty_term_has(tty->term, TTYC_EL) && !tty_fake_bce(tty, wp)) 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_attributes(tty, &grid_default_cell, ctx->wp); if (ctx->xoff == 0 && tty_term_has(tty->term, TTYC_EL1) && !tty_fake_bce(tty, ctx->wp)) { 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_fake_bce(tty, ctx->wp) || !tty_term_has(tty->term, TTYC_CSR) || !tty_term_has(tty->term, TTYC_RI)) { tty_redraw_region(tty, ctx); return; } tty_attributes(tty, &grid_default_cell, ctx->wp); 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_fake_bce(tty, wp) || !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_attributes(tty, &grid_default_cell, wp); 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_attributes(tty, &grid_default_cell, wp); 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_fake_bce(tty, wp)) { 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_attributes(tty, &grid_default_cell, wp); 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) && !tty_fake_bce(tty, wp)) { 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_attributes(tty, &grid_default_cell, wp); 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) && !tty_fake_bce(tty, wp)) { 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_attributes(tty, &grid_default_cell, wp); 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 = ctx->cell->data.width; 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) - ctx->last_cell.data.width; tty_cursor_pane(tty, ctx, cx, ctx->ocy); tty_cell(tty, &ctx->last_cell, wp); } } else tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); tty_cell(tty, ctx->cell, wp); } 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_pane(tty, wp, 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_attributes(tty, &grid_default_cell, ctx->wp); tty_cursor(tty, 0, 0); } void tty_cell(struct tty *tty, const struct grid_cell *gc, const struct window_pane *wp) { 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, wp); /* Get the cell and if ASCII write with putc to do ACS translation. */ if (gc->data.size == 1) { if (*gc->data.data < 0x20 || *gc->data.data == 0x7f) return; tty_putc(tty, *gc->data.data); return; } /* If not UTF-8, write _. */ if (!(tty->flags & TTY_UTF8)) { for (i = 0; i < gc->data.width; i++) tty_putc(tty, '_'); return; } /* Write the data. */ tty_putn(tty, gc->data.data, gc->data.size, gc->data.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, const struct window_pane *wp) { struct grid_cell *tc = &tty->cell, gc2; u_char changed; memcpy(&gc2, gc, sizeof gc2); tty_default_colours(&gc2, wp); /* * If no setab, try to use the reverse attribute as a best-effort for a * non-default background. This is a bit of a hack but it doesn't do * any serious harm and makes a couple of applications happier. */ if (!tty_term_has(tty->term, TTYC_SETAB)) { if (gc2.attr & GRID_ATTR_REVERSE) { if (gc2.fg != 7 && 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) tty_set_italics(tty); 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; int have_ax, fg_default, bg_default; /* No changes? Nothing is necessary. */ if (tty_same_colours(gc, tc)) 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 = tty_is_fg(gc, 8); bg_default = tty_is_bg(gc, 8); 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_flag(tty->term, TTYC_AX); if (!have_ax && tty_term_has(tty->term, TTYC_OP)) tty_reset(tty); else { if (fg_default && !tty_is_fg(tc, 8)) { if (have_ax) tty_puts(tty, "\033[39m"); else if (!tty_is_fg(tc, 7)) tty_putcode1(tty, TTYC_SETAF, 7); tc->fg = 8; tc->flags &= ~(GRID_FLAG_FG256|GRID_FLAG_FGRGB); } if (bg_default && !tty_is_bg(tc, 8)) { if (have_ax) tty_puts(tty, "\033[49m"); else if (!tty_is_bg(tc, 0)) tty_putcode1(tty, TTYC_SETAB, 0); tc->bg = 8; tc->flags &= ~(GRID_FLAG_BG256|GRID_FLAG_BGRGB); } } } /* Set the foreground colour. */ if (!fg_default && !tty_same_fg(gc, tc)) 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 && !tty_same_bg(gc, tc)) tty_colours_bg(tty, gc); } void tty_check_fg(struct tty *tty, struct grid_cell *gc) { struct grid_cell_rgb *rgb = &gc->fg_rgb; u_int colours; /* Is this a 24-bit colour? */ if (gc->flags & GRID_FLAG_FGRGB) { /* Not a 24-bit terminal? Translate to 256-colour palette. */ if (!tty_term_flag(tty->term, TTYC_TC)) { gc->flags &= ~GRID_FLAG_FGRGB; gc->flags |= GRID_FLAG_FG256; gc->fg = colour_find_rgb(rgb->r, rgb->g, rgb->b); } else return; } colours = tty_term_number(tty->term, TTYC_COLORS); /* Is this a 256-colour colour? */ if (gc->flags & GRID_FLAG_FG256) { /* And not a 256 colour mode? */ if (!(tty->term->flags & TERM_256COLOURS) && !(tty->term_flags & TERM_256COLOURS)) { gc->fg = colour_256to16(gc->fg); if (gc->fg & 8) { gc->fg &= 7; if (colours >= 16) gc->fg += 90; else gc->attr |= GRID_ATTR_BRIGHT; } else gc->attr &= ~GRID_ATTR_BRIGHT; gc->flags &= ~GRID_FLAG_FG256; } return; } /* Is this an aixterm colour? */ 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) { struct grid_cell_rgb *rgb = &gc->bg_rgb; u_int colours; /* Is this a 24-bit colour? */ if (gc->flags & GRID_FLAG_BGRGB) { /* Not a 24-bit terminal? Translate to 256-colour palette. */ if (!tty_term_flag(tty->term, TTYC_TC)) { gc->flags &= ~GRID_FLAG_BGRGB; gc->flags |= GRID_FLAG_BG256; gc->bg = colour_find_rgb(rgb->r, rgb->g, rgb->b); } else return; } colours = tty_term_number(tty->term, TTYC_COLORS); /* 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_256COLOURS) && !(tty->term_flags & TERM_256COLOURS)) { gc->bg = colour_256to16(gc->bg); if (gc->bg & 8) { gc->bg &= 7; if (colours >= 16) gc->fg += 90; } gc->flags &= ~GRID_FLAG_BG256; } return; } /* Is this an aixterm colour? */ if (gc->bg >= 90 && gc->bg <= 97 && colours < 16) gc->bg -= 90; } 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]; tc->flags &= ~(GRID_FLAG_FG256|GRID_FLAG_FGRGB); /* Is this a 24-bit colour? */ if (gc->flags & GRID_FLAG_FGRGB) { if (tty_try_rgb(tty, &gc->fg_rgb, "38") == 0) goto save_fg; /* Should not get here, already converted in tty_check_fg. */ return; } /* Is this a 256-colour colour? */ if (gc->flags & GRID_FLAG_FG256) { if (tty_try_256(tty, fg, "38") == 0) goto save_fg; /* Should not get here, already converted in 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. */ if (gc->flags & GRID_FLAG_FGRGB) memcpy(&tc->fg_rgb, &gc->fg_rgb, sizeof tc->fg_rgb); else tc->fg = fg; tc->flags &= ~(GRID_FLAG_FGRGB|GRID_FLAG_FG256); tc->flags |= (gc->flags & (GRID_FLAG_FG256|GRID_FLAG_FGRGB)); } 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 24-bit colour? */ if (gc->flags & GRID_FLAG_BGRGB) { if (tty_try_rgb(tty, &gc->bg_rgb, "48") == 0) goto save_bg; /* Should not get here, already converted in tty_check_bg. */ return; } /* Is this a 256-colour colour? */ if (gc->flags & GRID_FLAG_BG256) { if (tty_try_256(tty, bg, "48") == 0) goto save_bg; /* Should not get here, already converted in tty_check_bg. */ return; } /* Is this an aixterm bright colour? */ if (bg >= 90 && bg <= 97) { xsnprintf(s, sizeof s, "\033[%dm", bg + 10); tty_puts(tty, s); goto save_bg; } /* Otherwise set the background colour. */ tty_putcode1(tty, TTYC_SETAB, bg); save_bg: /* Save the new values in the terminal current cell. */ if (gc->flags & GRID_FLAG_BGRGB) memcpy(&tc->bg_rgb, &gc->bg_rgb, sizeof tc->bg_rgb); else tc->bg = bg; tc->flags &= ~(GRID_FLAG_BGRGB|GRID_FLAG_BG256); tc->flags |= (gc->flags & (GRID_FLAG_BG256|GRID_FLAG_BGRGB)); } int tty_try_256(struct tty *tty, u_char colour, const char *type) { char s[32]; /* * If the user has specified -2 to the client, setaf and setab may not * work (or they may not want to use them), so send the usual sequence. */ if (tty->term_flags & TERM_256COLOURS) goto fallback; /* * If the terminfo entry has 256 colours and setaf and setab exist, * assume that they work correctly. */ if (tty->term->flags & TERM_256COLOURS) { if (*type == '3') { if (!tty_term_has(tty->term, TTYC_SETAF)) goto fallback; tty_putcode1(tty, TTYC_SETAF, colour); } else { if (!tty_term_has(tty->term, TTYC_SETAB)) goto fallback; tty_putcode1(tty, TTYC_SETAB, colour); } return (0); } return (-1); fallback: xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour); tty_puts(tty, s); return (0); } int tty_try_rgb(struct tty *tty, const struct grid_cell_rgb *rgb, const char *type) { char s[32]; if (!tty_term_flag(tty->term, TTYC_TC)) return (-1); xsnprintf(s, sizeof s, "\033[%s;2;%hhu;%hhu;%hhum", type, rgb->r, rgb->g, rgb->b); tty_puts(tty, s); return (0); } void tty_default_colours(struct grid_cell *gc, const struct window_pane *wp) { const struct grid_cell *agc, *pgc, *wgc; if (wp == NULL) return; pgc = &wp->colgc; agc = options_get_style(wp->window->options, "window-active-style"); wgc = options_get_style(wp->window->options, "window-style"); if (gc->fg == 8 && !(gc->flags & GRID_FLAG_FG256)) { if (pgc->fg != 8 || (pgc->flags & GRID_FLAG_FG256)) { gc->fg = pgc->fg; gc->flags |= (pgc->flags & GRID_FLAG_FG256); } else if (wp == wp->window->active && (agc->fg != 8 || (agc->flags & GRID_FLAG_FG256))) { gc->fg = agc->fg; gc->flags |= (agc->flags & GRID_FLAG_FG256); } else { gc->fg = wgc->fg; gc->flags |= (wgc->flags & GRID_FLAG_FG256); } } if (gc->bg == 8 && !(gc->flags & GRID_FLAG_BG256)) { if (pgc->bg != 8 || (pgc->flags & GRID_FLAG_BG256)) { gc->bg = pgc->bg; gc->flags |= (pgc->flags & GRID_FLAG_BG256); } else if (wp == wp->window->active && (agc->bg != 8 || (agc->flags & GRID_FLAG_BG256))) { gc->bg = agc->bg; gc->flags |= (agc->flags & GRID_FLAG_BG256); } else { gc->bg = wgc->bg; gc->flags |= (wgc->flags & GRID_FLAG_BG256); } } } tmate-2.4.0/utf8.c000066400000000000000000000173771356407164200137100ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2008 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" static int utf8_width(wchar_t); /* Set a single character. */ void utf8_set(struct utf8_data *ud, u_char ch) { u_int i; *ud->data = ch; ud->have = 1; ud->size = 1; ud->width = 1; for (i = ud->size; i < sizeof ud->data; i++) ud->data[i] = '\0'; } /* Copy UTF-8 character. */ void utf8_copy(struct utf8_data *to, const struct utf8_data *from) { u_int i; memcpy(to, from, sizeof *to); for (i = to->size; i < sizeof to->data; i++) to->data[i] = '\0'; } /* * Open UTF-8 sequence. * * 11000010-11011111 C2-DF start of 2-byte sequence * 11100000-11101111 E0-EF start of 3-byte sequence * 11110000-11110100 F0-F4 start of 4-byte sequence */ enum utf8_state utf8_open(struct utf8_data *ud, u_char ch) { memset(ud, 0, sizeof *ud); if (ch >= 0xc2 && ch <= 0xdf) ud->size = 2; else if (ch >= 0xe0 && ch <= 0xef) ud->size = 3; else if (ch >= 0xf0 && ch <= 0xf4) ud->size = 4; else return (UTF8_ERROR); utf8_append(ud, ch); return (UTF8_MORE); } /* Append character to UTF-8, closing if finished. */ enum utf8_state utf8_append(struct utf8_data *ud, u_char ch) { wchar_t wc; int width; if (ud->have >= ud->size) fatalx("UTF-8 character overflow"); if (ud->size > sizeof ud->data) fatalx("UTF-8 character size too large"); if (ud->have != 0 && (ch & 0xc0) != 0x80) ud->width = 0xff; ud->data[ud->have++] = ch; if (ud->have != ud->size) return (UTF8_MORE); if (ud->width == 0xff) return (UTF8_ERROR); if (utf8_combine(ud, &wc) != UTF8_DONE) return (UTF8_ERROR); if ((width = utf8_width(wc)) < 0) return (UTF8_ERROR); ud->width = width; return (UTF8_DONE); } /* Get width of Unicode character. */ static int utf8_width(wchar_t wc) { int width; width = wcwidth(wc); if (width < 0 || width > 0xff) return (-1); return (width); } /* Combine UTF-8 into Unicode. */ enum utf8_state utf8_combine(const struct utf8_data *ud, wchar_t *wc) { switch (mbtowc(wc, ud->data, ud->size)) { case -1: mbtowc(NULL, NULL, MB_CUR_MAX); return (UTF8_ERROR); case 0: return (UTF8_ERROR); default: return (UTF8_DONE); } } /* Split Unicode into UTF-8. */ enum utf8_state utf8_split(wchar_t wc, struct utf8_data *ud) { char s[MB_LEN_MAX]; int slen; slen = wctomb(s, wc); if (slen <= 0 || slen > (int)sizeof ud->data) return (UTF8_ERROR); memcpy(ud->data, s, slen); ud->size = slen; ud->width = utf8_width(wc); return (UTF8_DONE); } /* * Encode len characters from src into dst, which is guaranteed to have four * bytes available for each character from src (for \abc or UTF-8) plus space * for \0. */ int utf8_strvis(char *dst, const char *src, size_t len, int flag) { struct utf8_data ud; const char *start, *end; enum utf8_state more; size_t i; start = dst; end = src + len; while (src < end) { if ((more = utf8_open(&ud, *src)) == UTF8_MORE) { while (++src < end && more == UTF8_MORE) more = utf8_append(&ud, *src); if (more == UTF8_DONE) { /* UTF-8 character finished. */ for (i = 0; i < ud.size; i++) *dst++ = ud.data[i]; continue; } /* Not a complete, valid UTF-8 character. */ src -= ud.have; } if (src < end - 1) dst = vis(dst, src[0], flag, src[1]); else if (src < end) dst = vis(dst, src[0], flag, '\0'); src++; } *dst = '\0'; return (dst - start); } /* * Sanitize a string, changing any UTF-8 characters to '_'. Caller should free * the returned string. Anything not valid printable ASCII or UTF-8 is * stripped. */ char * utf8_sanitize(const char *src) { char *dst; size_t n; enum utf8_state more; struct utf8_data ud; u_int i; dst = NULL; n = 0; while (*src != '\0') { dst = xreallocarray(dst, n + 1, sizeof *dst); if ((more = utf8_open(&ud, *src)) == UTF8_MORE) { while (*++src != '\0' && more == UTF8_MORE) more = utf8_append(&ud, *src); if (more == UTF8_DONE) { dst = xreallocarray(dst, n + ud.width, sizeof *dst); for (i = 0; i < ud.width; i++) dst[n++] = '_'; continue; } src -= ud.have; } if (*src > 0x1f && *src < 0x7f) dst[n++] = *src; else dst[n++] = '_'; src++; } dst = xreallocarray(dst, n + 1, sizeof *dst); dst[n] = '\0'; return (dst); } /* * Convert a string into a buffer of UTF-8 characters. Terminated by size == 0. * Caller frees. */ struct utf8_data * utf8_fromcstr(const char *src) { struct utf8_data *dst; size_t n; enum utf8_state more; dst = NULL; n = 0; while (*src != '\0') { dst = xreallocarray(dst, n + 1, sizeof *dst); if ((more = utf8_open(&dst[n], *src)) == UTF8_MORE) { while (*++src != '\0' && more == UTF8_MORE) more = utf8_append(&dst[n], *src); if (more == UTF8_DONE) { n++; continue; } src -= dst[n].have; } utf8_set(&dst[n], *src); n++; src++; } dst = xreallocarray(dst, n + 1, sizeof *dst); dst[n].size = 0; return (dst); } /* Convert from a buffer of UTF-8 characters into a string. Caller frees. */ char * utf8_tocstr(struct utf8_data *src) { char *dst; size_t n; dst = NULL; n = 0; for(; src->size != 0; src++) { dst = xreallocarray(dst, n + src->size, 1); memcpy(dst + n, src->data, src->size); n += src->size; } dst = xreallocarray(dst, n + 1, 1); dst[n] = '\0'; return (dst); } /* Get width of UTF-8 string. */ u_int utf8_cstrwidth(const char *s) { struct utf8_data tmp; u_int width; enum utf8_state more; width = 0; while (*s != '\0') { if ((more = utf8_open(&tmp, *s)) == UTF8_MORE) { while (*++s != '\0' && more == UTF8_MORE) more = utf8_append(&tmp, *s); if (more == UTF8_DONE) { width += tmp.width; continue; } s -= tmp.have; } if (*s > 0x1f && *s != 0x7f) width++; s++; } return (width); } /* Trim UTF-8 string to width. Caller frees. */ char * utf8_trimcstr(const char *s, u_int width) { struct utf8_data *tmp, *next; char *out; u_int at; tmp = utf8_fromcstr(s); at = 0; for (next = tmp; next->size != 0; next++) { if (at + next->width > width) { next->size = 0; break; } at += next->width; } out = utf8_tocstr(tmp); free(tmp); return (out); } /* Trim UTF-8 string to width. Caller frees. */ char * utf8_rtrimcstr(const char *s, u_int width) { struct utf8_data *tmp, *next, *end; char *out; u_int at; tmp = utf8_fromcstr(s); for (end = tmp; end->size != 0; end++) /* nothing */; if (end == tmp) { free(tmp); return (xstrdup("")); } next = end - 1; at = 0; for (;;) { if (at + next->width > width) { next++; break; } at += next->width; if (next == tmp) break; next--; } out = utf8_tocstr(next); free(tmp); return (out); } /* Pad UTF-8 string to width. Caller frees. */ char * utf8_padcstr(const char *s, u_int width) { size_t slen; char *out; u_int n, i; n = utf8_cstrwidth(s); if (n >= width) return (xstrdup(s)); slen = strlen(s); out = xmalloc(slen + 1 + (width - n)); memcpy(out, s, slen); for (i = n; i < width; i++) out[slen++] = ' '; out[slen] = '\0'; return (out); } tmate-2.4.0/window-choose.c000066400000000000000000000601411356407164200155720ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "array.h" #include "tmux.h" struct screen *window_choose_init(struct window_pane *); void window_choose_free(struct window_pane *); void window_choose_resize(struct window_pane *, u_int, u_int); void window_choose_key(struct window_pane *, struct client *, struct session *, key_code, struct mouse_event *); void window_choose_default_callback(struct window_choose_data *); struct window_choose_mode_item *window_choose_get_item(struct window_pane *, key_code, struct mouse_event *); 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 *, u_int); void window_choose_expand(struct window_pane *, struct session *, u_int); 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, }; struct window_choose_mode_item { struct window_choose_data *wcd; char *name; int pos; int state; #define TREE_EXPANDED 0x1 }; 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 *, key_code); void window_choose_prompt_input(enum window_choose_input_type, const char *, struct window_pane *, key_code); void window_choose_reset_top(struct window_pane *, u_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 , "%d", item->pos); } void window_choose_set_current(struct window_pane *wp, u_int cur) { struct window_choose_mode_data *data = wp->modedata; struct screen *s = &data->screen; data->selected = cur; window_choose_reset_top(wp, screen_size_y(s)); } void window_choose_reset_top(struct window_pane *wp, u_int sy) { struct window_choose_mode_data *data = wp->modedata; data->top = 0; if (data->selected > sy - 1) data->top = data->selected - (sy - 1); window_choose_redraw_screen(wp); } 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; data->callbackfn = callbackfn; if (data->callbackfn == NULL) data->callbackfn = window_choose_default_callback; ARRAY_CONCAT(&data->old_list, &data->list); window_choose_set_current(wp, cur); 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; 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(NULL, 0); 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) { server_client_unref(wcd->start_client); session_unref(wcd->start_session); if (wcd->tree_session != NULL) session_unref(wcd->tree_session); 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, NULL); 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; window_choose_reset_top(wp, sy); 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, key_code 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, 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, 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; ARRAY_DECL(, struct window_choose_mode_item) list_copy; ARRAY_INIT(&list_copy); 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, *item); /* * 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 screen *scr = &data->screen; 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, data->selected); /* 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_reset_top(wp, screen_size_y(scr)); } void window_choose_expand_all(struct window_pane *wp) { struct window_choose_mode_data *data = wp->modedata; struct window_choose_mode_item *item; struct screen *scr = &data->screen; 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_reset_top(wp, screen_size_y(scr)); } 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)); } } } } } struct window_choose_mode_item * window_choose_get_item(struct window_pane *wp, key_code key, struct mouse_event *m) { struct window_choose_mode_data *data = wp->modedata; u_int x, y, idx; if (!KEYC_IS_MOUSE(key)) return (&ARRAY_ITEM(&data->list, data->selected)); if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) return (NULL); idx = data->top + y; if (idx >= ARRAY_LENGTH(&data->list)) return (NULL); return (&ARRAY_ITEM(&data->list, idx)); } void window_choose_key(struct window_pane *wp, __unused struct client *c, __unused struct session *sess, key_code key, struct mouse_event *m) { 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: if ((item = window_choose_get_item(wp, key, m)) == NULL) break; window_choose_fire_callback(wp, item->wcd); break; case MODEKEYCHOICE_TREE_TOGGLE: if ((item = window_choose_get_item(wp, key, m)) == NULL) break; if (item->state & TREE_EXPANDED) { window_choose_collapse(wp, item->wcd->tree_session, data->selected); } else { window_choose_expand(wp, item->wcd->tree_session, data->selected); } window_choose_redraw_screen(wp); break; case MODEKEYCHOICE_TREE_COLLAPSE: if ((item = window_choose_get_item(wp, key, m)) == NULL) break; if (item->state & TREE_EXPANDED) { window_choose_collapse(wp, item->wcd->tree_session, data->selected); window_choose_redraw_screen(wp); } break; case MODEKEYCHOICE_TREE_COLLAPSE_ALL: window_choose_collapse_all(wp); break; case MODEKEYCHOICE_TREE_EXPAND: if ((item = window_choose_get_item(wp, key, m)) == NULL) break; 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; case MODEKEYCHOICE_STARTOFLIST: data->selected = 0; data->top = 0; window_choose_redraw_screen(wp); break; case MODEKEYCHOICE_TOPLINE: data->selected = data->top; window_choose_redraw_screen(wp); break; case MODEKEYCHOICE_BOTTOMLINE: data->selected = data->top + screen_size_y(s) - 1; if (data->selected > items - 1) data->selected = items - 1; window_choose_redraw_screen(wp); break; case MODEKEYCHOICE_ENDOFLIST: data->selected = items - 1; if (screen_size_y(s) < items) data->top = items - screen_size_y(s); else data->top = 0; window_choose_redraw_screen(wp); break; default: idx = window_choose_index_key(data, key); if (idx < 0 || (u_int) idx >= ARRAY_LENGTH(&data->list)) break; data->selected = idx; item = &ARRAY_ITEM(&data->list, data->selected); window_choose_fire_callback(wp, item->wcd); break; } } 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 key; if (data->callbackfn == NULL) fatalx("called before callback assigned"); last = screen_size_y(s) - 1; memcpy(&gc, &grid_default_cell, sizeof gc); if (data->selected == data->top + py) style_apply(&gc, oo, "mode-style"); 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, "%*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) { style_apply(&gc, oo, "mode-style"); 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, key_code 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 == (key_code)*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_defaults(wcd->ft, NULL, s, NULL, NULL); wcd->command = cmd_template_replace(action, s->name, 1); 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_defaults(wcd->ft, NULL, s, wl, NULL); 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-2.4.0/window-clock.c000066400000000000000000000145451356407164200154140ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" 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 client *, struct session *, key_code, struct mouse_event *); void window_clock_timer_callback(int, short, void *); 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, }; struct window_clock_mode_data { struct screen screen; time_t tim; struct event timer; }; const char window_clock_table[14][5][5] = { { { 1,1,1,1,1 }, /* 0 */ { 1,0,0,0,1 }, { 1,0,0,0,1 }, { 1,0,0,0,1 }, { 1,1,1,1,1 } }, { { 0,0,0,0,1 }, /* 1 */ { 0,0,0,0,1 }, { 0,0,0,0,1 }, { 0,0,0,0,1 }, { 0,0,0,0,1 } }, { { 1,1,1,1,1 }, /* 2 */ { 0,0,0,0,1 }, { 1,1,1,1,1 }, { 1,0,0,0,0 }, { 1,1,1,1,1 } }, { { 1,1,1,1,1 }, /* 3 */ { 0,0,0,0,1 }, { 1,1,1,1,1 }, { 0,0,0,0,1 }, { 1,1,1,1,1 } }, { { 1,0,0,0,1 }, /* 4 */ { 1,0,0,0,1 }, { 1,1,1,1,1 }, { 0,0,0,0,1 }, { 0,0,0,0,1 } }, { { 1,1,1,1,1 }, /* 5 */ { 1,0,0,0,0 }, { 1,1,1,1,1 }, { 0,0,0,0,1 }, { 1,1,1,1,1 } }, { { 1,1,1,1,1 }, /* 6 */ { 1,0,0,0,0 }, { 1,1,1,1,1 }, { 1,0,0,0,1 }, { 1,1,1,1,1 } }, { { 1,1,1,1,1 }, /* 7 */ { 0,0,0,0,1 }, { 0,0,0,0,1 }, { 0,0,0,0,1 }, { 0,0,0,0,1 } }, { { 1,1,1,1,1 }, /* 8 */ { 1,0,0,0,1 }, { 1,1,1,1,1 }, { 1,0,0,0,1 }, { 1,1,1,1,1 } }, { { 1,1,1,1,1 }, /* 9 */ { 1,0,0,0,1 }, { 1,1,1,1,1 }, { 0,0,0,0,1 }, { 1,1,1,1,1 } }, { { 0,0,0,0,0 }, /* : */ { 0,0,1,0,0 }, { 0,0,0,0,0 }, { 0,0,1,0,0 }, { 0,0,0,0,0 } }, { { 1,1,1,1,1 }, /* A */ { 1,0,0,0,1 }, { 1,1,1,1,1 }, { 1,0,0,0,1 }, { 1,0,0,0,1 } }, { { 1,1,1,1,1 }, /* P */ { 1,0,0,0,1 }, { 1,1,1,1,1 }, { 1,0,0,0,0 }, { 1,0,0,0,0 } }, { { 1,0,0,0,1 }, /* M */ { 1,1,0,1,1 }, { 1,0,1,0,1 }, { 1,0,0,0,1 }, { 1,0,0,0,1 } }, }; void window_clock_timer_callback(__unused int fd, __unused short events, void *arg) { struct window_pane *wp = arg; struct window_clock_mode_data *data = wp->modedata; struct tm now, then; time_t t; struct timeval tv = { .tv_sec = 1 }; evtimer_del(&data->timer); evtimer_add(&data->timer, &tv); 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); } struct screen * window_clock_init(struct window_pane *wp) { struct window_clock_mode_data *data; struct screen *s; struct timeval tv = { .tv_sec = 1 }; wp->modedata = data = xmalloc(sizeof *data); data->tim = time(NULL); evtimer_set(&data->timer, window_clock_timer_callback, wp); evtimer_add(&data->timer, &tv); s = &data->screen; screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); s->mode &= ~MODE_CURSOR; window_clock_draw_screen(wp); return (s); } void window_clock_free(struct window_pane *wp) { struct window_clock_mode_data *data = wp->modedata; evtimer_del(&data->timer); 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 client *c, __unused struct session *sess, __unused key_code key, __unused struct mouse_event *m) { window_pane_reset_mode(wp); } 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; struct screen *s = &data->screen; struct grid_cell gc; char tim[64], *ptr; time_t t; struct tm *tm; u_int i, j, x, y, idx; colour = options_get_number(wp->window->options, "clock-mode-colour"); style = options_get_number(wp->window->options, "clock-mode-style"); screen_write_start(&ctx, NULL, s); t = time(NULL); tm = localtime(&t); if (style == 0) { strftime(tim, sizeof tim, "%l:%M ", localtime(&t)); if (tm->tm_hour >= 12) strlcat(tim, "PM", sizeof tim); else strlcat(tim, "AM", sizeof tim); } else strftime(tim, sizeof tim, "%H:%M", tm); screen_write_clearscreen(&ctx); 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); } screen_write_stop(&ctx); return; } x = (screen_size_x(s) / 2) - 3 * strlen(tim); y = (screen_size_y(s) / 2) - 3; memcpy(&gc, &grid_default_cell, sizeof gc); 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 (window_clock_table[idx][j][i]) screen_write_putc(&ctx, &gc, ' '); } } x += 6; } screen_write_stop(&ctx); } tmate-2.4.0/window-copy.c000066400000000000000000001656221356407164200152760ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "tmux.h" #include "tmate.h" struct screen *window_copy_init(struct window_pane *); void window_copy_free(struct window_pane *); void window_copy_pagedown(struct window_pane *); void window_copy_resize(struct window_pane *, u_int, u_int); void window_copy_key(struct window_pane *, struct client *, struct session *, key_code, struct mouse_event *); int window_copy_key_input(struct window_pane *, key_code); int window_copy_key_numeric_prefix(struct window_pane *, key_code); void window_copy_redraw_selection(struct window_pane *, u_int); void window_copy_redraw_lines(struct window_pane *, u_int, u_int); void window_copy_redraw_screen(struct window_pane *); void window_copy_write_line(struct window_pane *, struct screen_write_ctx *, u_int); void window_copy_write_lines(struct window_pane *, struct screen_write_ctx *, u_int, u_int); void window_copy_scroll_to(struct window_pane *, u_int, u_int); int window_copy_search_compare(struct grid *, u_int, u_int, struct grid *, u_int, int); int window_copy_search_lr(struct grid *, struct grid *, u_int *, u_int, u_int, u_int, int); int window_copy_search_rl(struct grid *, struct grid *, u_int *, u_int, u_int, u_int, int); void window_copy_search_up(struct window_pane *, const char *); void window_copy_search_down(struct window_pane *, const char *); void window_copy_goto_line(struct window_pane *, const char *); void window_copy_update_cursor(struct window_pane *, u_int, u_int); void window_copy_start_selection(struct window_pane *); int window_copy_update_selection(struct window_pane *, int); void *window_copy_get_selection(struct window_pane *, size_t *); void window_copy_copy_buffer(struct window_pane *, const char *, void *, size_t); void window_copy_copy_pipe(struct window_pane *, struct session *, const char *, const char *); void window_copy_copy_selection(struct window_pane *, const char *); void window_copy_append_selection(struct window_pane *, const char *); 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_other_end(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 *, int); void window_copy_cursor_jump_to_back(struct window_pane *, int); 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 *); void window_copy_drag_update(struct client *, struct mouse_event *); void window_copy_drag_release(struct client *, struct mouse_event *); const struct window_mode window_copy_mode = { window_copy_init, window_copy_free, window_copy_resize, window_copy_key, }; #include "window-copy.h" 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->scroll_exit = 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); 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); s->sel.modekeys = keys; data->backing = NULL; #ifdef TMATE data->password_cb = NULL; #endif return (s); } void window_copy_init_from_pane(struct window_pane *wp, int scroll_exit) { 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; data->scroll_exit = scroll_exit; 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); #ifdef TMATE tmate_sync_copy_mode(wp); #endif } 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); } 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; u_int old_hsize, old_cy; #ifdef TMATE char *msg; #endif if (backing == &wp->base) return; memcpy(&gc, &grid_default_cell, sizeof gc); old_hsize = screen_hsize(data->backing); screen_write_start(&back_ctx, NULL, backing); if (data->backing_written) { /* * On the second or later line, do a CRLF before writing * (so it's on a new line). */ screen_write_carriagereturn(&back_ctx); screen_write_linefeed(&back_ctx, 0); } else data->backing_written = 1; old_cy = backing->cy; #ifdef TMATE xvasprintf(&msg, fmt, ap); screen_write_nputs(&back_ctx, 0, &gc, "%s", msg); tmate_write_copy_mode(wp, msg); free(msg); #else screen_write_vnputs(&back_ctx, 0, &gc, 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 new lines. */ window_copy_redraw_lines(wp, old_cy, backing->cy - old_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, ox, oy; oy = screen_hsize(data->backing) + data->cy - data->oy; ox = window_copy_find_length(wp, oy); if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely) window_copy_other_end(wp); if (data->cx != ox) { data->lastcx = data->cx; data->lastsx = ox; } data->cx = data->lastcx; 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; if (!data->screen.sel.flag || !data->rectflag) { u_int py = screen_hsize(data->backing) + data->cy - data->oy; u_int 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); } window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); } void window_copy_pagedown(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; u_int n, ox, oy; oy = screen_hsize(data->backing) + data->cy - data->oy; ox = window_copy_find_length(wp, oy); if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT && oy == data->sely) window_copy_other_end(wp); if (data->cx != ox) { data->lastcx = data->cx; data->lastsx = ox; } data->cx = data->lastcx; n = 1; if (screen_size_y(s) > 2) n = screen_size_y(s) - 2; if (data->oy < n) data->oy = 0; else data->oy -= n; if (!data->screen.sel.flag || !data->rectflag) { u_int py = screen_hsize(data->backing) + data->cy - data->oy; u_int 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); } if (data->scroll_exit && data->oy == 0) { window_pane_reset_mode(wp); return; } window_copy_update_selection(wp, 1); 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, 1); if (data->backing != &wp->base) screen_resize(data->backing, sx, sy, 1); if (data->cy > sy - 1) data->cy = sy - 1; if (data->cx > sx) data->cx = sx; if (data->oy > screen_hsize(data->backing)) data->oy = screen_hsize(data->backing); 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 client *c, struct session *sess, key_code key, struct mouse_event *m) { const char *word_separators; struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; u_int n, np; int keys; enum mode_key_cmd cmd; const char *arg, *ss; np = 1; if (data->numprefix > 0) np = data->numprefix; 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); } if (data->inputtype == WINDOW_COPY_JUMPBACK) { for (; np != 0; np--) window_copy_cursor_jump_back(wp); } if (data->inputtype == WINDOW_COPY_JUMPTOFORWARD) { for (; np != 0; np--) window_copy_cursor_jump_to(wp, 0); } if (data->inputtype == WINDOW_COPY_JUMPTOBACK) { for (; np != 0; np--) window_copy_cursor_jump_to_back(wp, 0); } } 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); if (cmd != MODEKEYCOPY_PREVIOUSPAGE && cmd != MODEKEYCOPY_NEXTPAGE && cmd != MODEKEYCOPY_SCROLLUP && cmd != MODEKEYCOPY_SCROLLDOWN && cmd != MODEKEYCOPY_HALFPAGEUP && cmd != MODEKEYCOPY_HALFPAGEDOWN) data->scroll_exit = 0; switch (cmd) { case MODEKEYCOPY_APPENDSELECTION: if (sess != NULL) { window_copy_append_selection(wp, NULL); if (arg == NULL) { window_pane_reset_mode(wp); return; } window_copy_clear_selection(wp); window_copy_redraw_screen(wp); } break; case MODEKEYCOPY_CANCEL: window_pane_reset_mode(wp); return; case MODEKEYCOPY_OTHEREND: if (np % 2) window_copy_other_end(wp); break; 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); if (data->scroll_exit && data->oy == 0) { window_pane_reset_mode(wp); return; } break; case MODEKEYCOPY_PREVIOUSPAGE: for (; np != 0; np--) window_copy_pageup(wp); break; case MODEKEYCOPY_NEXTPAGE: for (; np != 0; np--) window_copy_pagedown(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, 1); 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; } if (data->scroll_exit && data->oy == 0) { window_pane_reset_mode(wp); return; } window_copy_update_selection(wp, 1); window_copy_redraw_screen(wp); break; case MODEKEYCOPY_TOPLINE: data->cx = 0; data->cy = 0; window_copy_update_selection(wp, 1); 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, 1); window_copy_redraw_screen(wp); break; case MODEKEYCOPY_BOTTOMLINE: data->cx = 0; data->cy = screen_size_y(s) - 1; window_copy_update_selection(wp, 1); 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, 1); 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, 1); window_copy_redraw_screen(wp); break; case MODEKEYCOPY_STARTSELECTION: if (KEYC_IS_MOUSE(key)) { if (c != NULL) window_copy_start_drag(c, m); } else { s->sel.lineflag = LINE_SEL_NONE; window_copy_start_selection(wp); window_copy_redraw_screen(wp); } break; case MODEKEYCOPY_SELECTLINE: s->sel.lineflag = LINE_SEL_LEFT_RIGHT; data->rectflag = 0; /* FALLTHROUGH */ case MODEKEYCOPY_COPYLINE: 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, NULL); 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, NULL, arg); window_pane_reset_mode(wp); return; } break; case MODEKEYCOPY_COPYSELECTION: if (sess != NULL) { window_copy_copy_selection(wp, NULL); if (arg == NULL) { window_pane_reset_mode(wp); return; } window_copy_clear_selection(wp); window_copy_redraw_screen(wp); } 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, 1); } else if (data->jumptype == WINDOW_COPY_JUMPTOBACK) { for (; np != 0; np--) window_copy_cursor_jump_to_back(wp, 1); } 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, 1); } else if (data->jumptype == WINDOW_COPY_JUMPTOBACK) { for (; np != 0; np--) window_copy_cursor_jump_to(wp, 1); } 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) { #ifdef TMATE case WINDOW_COPY_PASSWORD: break; #endif 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_NAMEDBUFFER: case WINDOW_COPY_NUMERICPREFIX: case WINDOW_COPY_SEARCHUP: ss = data->searchstr; if (cmd == MODEKEYCOPY_SEARCHAGAIN) { for (; np != 0; np--) window_copy_search_up(wp, ss); } else { for (; np != 0; np--) window_copy_search_down(wp, ss); } break; case WINDOW_COPY_SEARCHDOWN: ss = data->searchstr; if (cmd == MODEKEYCOPY_SEARCHAGAIN) { for (; np != 0; np--) window_copy_search_down(wp, ss); } else { for (; np != 0; np--) window_copy_search_up(wp, ss); } break; } break; case MODEKEYCOPY_GOTOLINE: data->inputtype = WINDOW_COPY_GOTOLINE; data->inputprompt = "Goto Line"; *data->inputstr = '\0'; goto input_on; case MODEKEYCOPY_STARTNAMEDBUFFER: data->inputtype = WINDOW_COPY_NAMEDBUFFER; data->inputexit = (arg == NULL); data->inputprompt = "Buffer"; *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: s->sel.lineflag = LINE_SEL_NONE; 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 client *c, struct session *sess, key_code key, struct mouse_event *m) { __window_copy_key(wp, c, sess, key, m); #ifdef TMATE tmate_sync_copy_mode(wp); #endif } int window_copy_key_input(struct window_pane *wp, key_code key) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; const char *bufdata; size_t inputlen, n, bufsize; int np; struct paste_buffer *pb; u_char ch; 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_PASTE: if ((pb = paste_get_top(NULL)) == NULL) break; bufdata = paste_buffer_data(pb, &bufsize); for (n = 0; n < bufsize; n++) { ch = (u_char)bufdata[n]; if (ch < 32 || ch == 127) break; } inputlen = strlen(data->inputstr); data->inputstr = xrealloc(data->inputstr, inputlen + n + 1); memcpy(data->inputstr + inputlen, bufdata, n); data->inputstr[inputlen + n] = '\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_NAMEDBUFFER: window_copy_copy_selection(wp, data->inputstr); *data->inputstr = '\0'; if (data->inputexit) { window_pane_reset_mode(wp); return (0); } window_copy_clear_selection(wp); window_copy_redraw_screen(wp); 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); } window_pane_reset_mode(wp); return 0; #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, 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, key_code 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); } 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, 1); 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, int cis) { struct grid_cell gc, sgc; const struct utf8_data *ud, *sud; grid_get_cell(gd, px, py, &gc); ud = &gc.data; grid_get_cell(sgd, spx, 0, &sgc); sud = &sgc.data; if (ud->size != sud->size || ud->width != sud->width) return (0); if (cis && ud->size == 1) return (tolower(ud->data[0]) == sud->data[0]); return (memcmp(ud->data, sud->data, ud->size) == 0); } int window_copy_search_lr(struct grid *gd, struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis) { u_int ax, bx, px; int matched; for (ax = first; ax < last; ax++) { if (ax + sgd->sx >= gd->sx) break; for (bx = 0; bx < sgd->sx; bx++) { px = ax + bx; matched = window_copy_search_compare(gd, px, py, sgd, bx, cis); if (!matched) break; } if (bx == sgd->sx) { *ppx = ax; return (1); } } return (0); } int window_copy_search_rl(struct grid *gd, struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis) { u_int ax, bx, px; int matched; for (ax = last + 1; ax > first; ax--) { if (gd->sx - (ax - 1) < sgd->sx) continue; for (bx = 0; bx < sgd->sx; bx++) { px = ax - 1 + bx; matched = window_copy_search_compare(gd, px, py, sgd, bx, cis); if (!matched) break; } if (bx == sgd->sx) { *ppx = ax - 1; return (1); } } return (0); } 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 n, wrapped, wrapflag, cis; const char *ptr; #ifdef TMATE if (!searchstr) return; #endif if (*searchstr == '\0') return; wrapflag = options_get_number(wp->window->options, "wrap-search"); searchlen = screen_write_strlen("%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, "%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; cis = 1; for (ptr = searchstr; *ptr != '\0'; ptr++) { if (*ptr != tolower((u_char)*ptr)) { cis = 0; break; } } 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, cis); 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 n, wrapped, wrapflag, cis; const char *ptr; #ifdef TMATE if (!searchstr) return; #endif if (*searchstr == '\0') return; wrapflag = options_get_number(wp->window->options, "wrap-search"); searchlen = screen_write_strlen("%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, "%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; cis = 1; for (ptr = searchstr; *ptr != '\0'; ptr++) { if (*ptr != tolower((u_char)*ptr)) { cis = 0; break; } } 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, cis); 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, 1); window_copy_redraw_screen(wp); } void window_copy_write_line(struct window_pane *wp, struct screen_write_ctx *ctx, u_int py) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; struct options *oo = wp->window->options; struct grid_cell gc; char hdr[512]; size_t last, xoff = 0, size = 0, limit; style_apply(&gc, oo, "mode-style"); 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) { limit = sizeof hdr; if (limit > screen_size_x(s) + 1) limit = screen_size_x(s) + 1; if (data->inputtype == WINDOW_COPY_NUMERICPREFIX) { xoff = size = xsnprintf(hdr, limit, "Repeat: %d", 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, limit, "%s: %s", data->inputprompt, data->inputstr); } screen_write_cursormove(ctx, 0, last); screen_write_puts(ctx, &gc, "%s", hdr); } else size = 0; if (size < screen_size_x(s)) { 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_selection(struct window_pane *wp, u_int old_y) { struct window_copy_mode_data *data = wp->modedata; u_int new_y, start, end; new_y = data->cy; if (old_y <= new_y) { start = old_y; end = new_y; } else { start = new_y; end = old_y; } window_copy_redraw_lines(wp, start, end - start + 1); } 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, 1); } int window_copy_update_selection(struct window_pane *wp, int may_redraw) { 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 && s->sel.lineflag == LINE_SEL_NONE) return (0); /* Set colours. */ style_apply(&gc, oo, "mode-style"); /* 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 && may_redraw) { /* * Can't rely on the caller to redraw the right lines for * rectangle selection - find the highest line and the number * of lines, and redraw just past that in both directions */ cy = data->cy; if (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, ey_last; u_int firstsx, lastex, restex, restsx; int keys; if (!s->sel.flag && s->sel.lineflag == LINE_SEL_NONE) return (NULL); buf = xmalloc(1); off = 0; *buf = '\0'; /* * The selection extends from selx,sely to (adjusted) cx,cy on * the base screen. */ /* Find start and end. */ xx = data->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. */ ey_last = window_copy_find_length(wp, ey); if (ex > ey_last) ex = ey_last; /* * Deal with rectangle-copy if necessary; four situations: start of * first line (firstsx), end of last line (lastex), start (restsx) and * end (restex) of all other lines. */ xx = screen_size_x(s); /* * Behave according to mode-keys. If it is emacs, copy like emacs, * keeping the top-left-most character, and dropping the * bottom-right-most, regardless of copy direction. If it is vi, also * keep bottom-right-most character. */ keys = options_get_number(wp->window->options, "mode-keys"); if (data->rectflag) { /* * Need to ignore the column with the cursor in it, which for * rectangular copy means knowing which side the cursor is on. */ if (data->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. */ for (i = sy; i <= ey; i++) { window_copy_copy_line(wp, &buf, &off, i, (i == sy ? firstsx : restsx), (i == ey ? lastex : restex)); } /* Don't bother if no data. */ if (off == 0) { free(buf); return (NULL); } if (keys == MODEKEY_EMACS || lastex <= ey_last) off -= 1; /* remove final \n (unless at end in vi mode) */ *len = off; return (buf); } void window_copy_copy_buffer(struct window_pane *wp, const char *bufname, void *buf, size_t len) { 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 (paste_set(buf, len, bufname, NULL) != 0) free(buf); } void window_copy_copy_pipe(struct window_pane *wp, struct session *sess, const char *bufname, const char *arg) { void *buf; size_t len; struct job *job; struct format_tree *ft; char *expanded; buf = window_copy_get_selection(wp, &len); if (buf == NULL) return; ft = format_create(NULL, 0); format_defaults(ft, NULL, sess, NULL, wp); expanded = format_expand(ft, arg); job = job_run(expanded, sess, NULL, NULL, NULL, NULL); bufferevent_write(job->event, buf, len); free(expanded); format_free(ft); window_copy_copy_buffer(wp, bufname, buf, len); } void window_copy_copy_selection(struct window_pane *wp, const char *bufname) { void *buf; size_t len; buf = window_copy_get_selection(wp, &len); if (buf == NULL) return; window_copy_copy_buffer(wp, bufname, buf, len); } void window_copy_append_selection(struct window_pane *wp, const char *bufname) { char *buf; struct paste_buffer *pb; const char *bufdata; size_t len, bufsize; struct screen_write_ctx ctx; buf = window_copy_get_selection(wp, &len); if (buf == NULL) return; 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 (bufname == NULL || *bufname == '\0') pb = paste_get_top(&bufname); else pb = paste_get_name(bufname); if (pb != NULL) { bufdata = paste_buffer_data(pb, &bufsize); buf = xrealloc(buf, len + bufsize); memmove(buf + bufsize, buf, len); memcpy(buf, bufdata, bufsize); len += bufsize; } if (paste_set(buf, len, bufname, NULL) != 0) free(buf); } 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; struct grid_cell gc; struct grid_line *gl; struct utf8_data ud; u_int i, xx, wrapped = 0; const char *s; if (sx > ex) return; /* * Work out if the line was wrapped at the screen edge and all of it is * on screen. */ gl = &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++) { grid_get_cell(gd, i, sy, &gc); if (gc.flags & GRID_FLAG_PADDING) continue; utf8_copy(&ud, &gc.data); if (ud.size == 1 && (gc.attr & GRID_ATTR_CHARSET)) { s = tty_acs_get(NULL, ud.data[0]); if (s != NULL && strlen(s) <= sizeof ud.data) { ud.size = strlen(s); memcpy(ud.data, s, ud.size); } } *buf = xrealloc(*buf, (*off) + ud.size); memcpy(*buf + *off, ud.data, ud.size); *off += ud.size; } } /* Only add a newline if the line wasn't wrapped. */ if (!wrapped || ex != xx) { *buf = xrealloc(*buf, (*off) + 1); (*buf)[(*off)++] = '\n'; } } 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; struct grid_cell gc; const struct utf8_data *ud; grid_get_cell(data->backing->grid, px, py, &gc); ud = &gc.data; 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; struct grid_cell gc; u_int px; /* * If the pane has been resized, its grid can contain old overlong * lines. grid_peek_cell does not allow accessing cells beyond the * width of the grid, and screen_write_copy treats them as spaces, so * ignore them here too. */ px = s->grid->linedata[py].cellsize; if (px > screen_size_x(s)) px = screen_size_x(s); while (px > 0) { grid_get_cell(s->grid, px - 1, py, &gc); if (gc.data.size != 1 || *gc.data.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 screen *s = &data->screen; struct grid *gd = back_s->grid; u_int py; if (data->cx == 0 && s->sel.lineflag == LINE_SEL_NONE) { 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, 1)) 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; struct grid_cell gc; px = 0; py = screen_hsize(data->backing) + data->cy - data->oy; xx = window_copy_find_length(wp, py); while (px < xx) { grid_get_cell(data->backing->grid, px, py, &gc); if (gc.data.size != 1 || *gc.data.data != ' ') break; px++; } window_copy_update_cursor(wp, px, data->cy); if (window_copy_update_selection(wp, 1)) 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 screen *s = &data->screen; 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 && s->sel.lineflag == LINE_SEL_NONE) { 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, 1)) window_copy_redraw_lines(wp, data->cy, 1); } void window_copy_other_end(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; u_int selx, sely, cx, cy, yy, hsize; if (!s->sel.flag && s->sel.lineflag == LINE_SEL_NONE) return; if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT) s->sel.lineflag = LINE_SEL_RIGHT_LEFT; else if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT) s->sel.lineflag = LINE_SEL_LEFT_RIGHT; selx = data->selx; sely = data->sely; cx = data->cx; cy = data->cy; yy = screen_hsize(data->backing) + data->cy - data->oy; data->selx = cx; data->sely = yy; data->cx = selx; hsize = screen_hsize(data->backing); if (sely < hsize - data->oy) { data->oy = hsize - sely; data->cy = 0; } else if (sely > hsize - data->oy + screen_size_y(s)) { data->oy = hsize - sely + screen_size_y(s) - 1; data->cy = screen_size_y(s) - 1; } else data->cy = cy + sely - yy; window_copy_redraw_screen(wp); } void window_copy_cursor_left(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; u_int py; py = screen_hsize(data->backing) + data->cy - data->oy; if (data->cx == 0 && py > 0) { window_copy_cursor_up(wp, 0); window_copy_cursor_end_of_line(wp); } else if (data->cx > 0) { window_copy_update_cursor(wp, data->cx - 1, data->cy); if (window_copy_update_selection(wp, 1)) 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, yy; py = screen_hsize(data->backing) + data->cy - data->oy; yy = screen_hsize(data->backing) + screen_size_y(data->backing) - 1; if (data->screen.sel.flag && data->rectflag) px = screen_size_x(&data->screen); else { px = window_copy_find_length(wp, py); } if (data->cx >= px && py < yy) { window_copy_cursor_start_of_line(wp); window_copy_cursor_down(wp, 0); } else if (data->cx < px) { window_copy_update_cursor(wp, data->cx + 1, data->cy); if (window_copy_update_selection(wp, 1)) 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; } if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely) window_copy_other_end(wp); 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, 1)) { 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); } if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT) window_copy_cursor_end_of_line(wp); else if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT) window_copy_cursor_start_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; } if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT && oy == data->sely) window_copy_other_end(wp); 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, 1)) 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); } if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT) window_copy_cursor_end_of_line(wp); else if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT) window_copy_cursor_start_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; struct grid_cell gc; u_int px, py, xx; px = data->cx + 1; py = screen_hsize(back_s) + data->cy - data->oy; xx = window_copy_find_length(wp, py); while (px < xx) { grid_get_cell(back_s->grid, px, py, &gc); if (!(gc.flags & GRID_FLAG_PADDING) && gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wp, px, data->cy); if (window_copy_update_selection(wp, 1)) 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; struct grid_cell gc; u_int px, py; px = data->cx; py = screen_hsize(back_s) + data->cy - data->oy; if (px > 0) px--; for (;;) { grid_get_cell(back_s->grid, px, py, &gc); if (!(gc.flags & GRID_FLAG_PADDING) && gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wp, px, data->cy); if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); return; } if (px == 0) break; px--; } } void window_copy_cursor_jump_to(struct window_pane *wp, int jump_again) { struct window_copy_mode_data *data = wp->modedata; struct screen *back_s = data->backing; struct grid_cell gc; u_int px, py, xx; px = data->cx + 1 + jump_again; py = screen_hsize(back_s) + data->cy - data->oy; xx = window_copy_find_length(wp, py); while (px < xx) { grid_get_cell(back_s->grid, px, py, &gc); if (!(gc.flags & GRID_FLAG_PADDING) && gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wp, px - 1, data->cy); if (window_copy_update_selection(wp, 1)) window_copy_redraw_lines(wp, data->cy, 1); return; } px++; } } void window_copy_cursor_jump_to_back(struct window_pane *wp, int jump_again) { struct window_copy_mode_data *data = wp->modedata; struct screen *back_s = data->backing; struct grid_cell gc; u_int px, py; px = data->cx; py = screen_hsize(back_s) + data->cy - data->oy; if (px > 0) px--; if (jump_again && px > 0) px--; for (;;) { grid_get_cell(back_s->grid, px, py, &gc); if (!(gc.flags & GRID_FLAG_PADDING) && gc.data.size == 1 && *gc.data.data == data->jumpchar) { window_copy_update_cursor(wp, px + 1, data->cy); if (window_copy_update_selection(wp, 1)) 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, 1)) 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 options *oo = wp->window->options; struct screen *back_s = data->backing; u_int px, py, xx, yy; int keys, expected = 1; px = data->cx; py = screen_hsize(back_s) + data->cy - data->oy; xx = window_copy_find_length(wp, py); yy = screen_hsize(back_s) + screen_size_y(back_s) - 1; keys = options_get_number(oo, "mode-keys"); if (keys == MODEKEY_VI && !window_copy_in_set(wp, px, py, separators)) px++; /* * First skip past any word characters, then any nonword characters. * * expected is initially set to 1 for the former and then 0 for the * latter. */ do { while (px > xx || window_copy_in_set(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); if (keys == MODEKEY_VI && px != 0) px--; window_copy_update_cursor(wp, px, data->cy); if (window_copy_update_selection(wp, 1)) 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, 1)) 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; window_copy_update_selection(wp, 0); 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_write_line(wp, &ctx, screen_size_y(s) - ny - 1); screen_write_cursormove(&ctx, data->cx, data->cy); 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; window_copy_update_selection(wp, 0); 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_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); screen_write_stop(&ctx); } int window_copy_scroll_position(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; if (wp->mode != &window_copy_mode) return (-1); return (data->oy); } 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, 1); window_copy_redraw_screen(wp); } void window_copy_start_drag(struct client *c, struct mouse_event *m) { struct window_pane *wp; u_int x, y; wp = cmd_mouse_pane(m, NULL, NULL); if (wp == NULL || wp->mode != &window_copy_mode) return; if (cmd_mouse_at(wp, m, &x, &y, 1) != 0) return; c->tty.mouse_drag_update = window_copy_drag_update; c->tty.mouse_drag_release = NULL; /* will fire MouseUp key */ window_copy_update_cursor(wp, x, y); window_copy_start_selection(wp); window_copy_redraw_screen(wp); } void window_copy_drag_update(__unused struct client *c, struct mouse_event *m) { struct window_pane *wp; struct window_copy_mode_data *data; u_int x, y, old_cy; wp = cmd_mouse_pane(m, NULL, NULL); if (wp == NULL || wp->mode != &window_copy_mode) return; data = wp->modedata; if (cmd_mouse_at(wp, m, &x, &y, 0) != 0) return; old_cy = data->cy; window_copy_update_cursor(wp, x, y); if (window_copy_update_selection(wp, 1)) window_copy_redraw_selection(wp, old_cy); } tmate-2.4.0/window-copy.h000066400000000000000000000043061356407164200152720ustar00rootroot00000000000000#ifndef WINDOW_COPY_H #define WINDOW_COPY_H #include "tmux.h" enum window_copy_input_type { WINDOW_COPY_OFF, WINDOW_COPY_NAMEDBUFFER, 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 started */ struct mode_key_data mdata; u_int oy; u_int selx; u_int sely; int rectflag; /* in rectangle copy mode? */ int scroll_exit; /* exit on scroll to end? */ u_int cx; u_int cy; u_int lastcx; /* position in last line w/ content */ u_int lastsx; /* size of last line w/ content */ enum window_copy_input_type inputtype; const char *inputprompt; char *inputstr; int inputexit; 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 int window_copy_update_selection(struct window_pane *, int); extern void window_copy_redraw_screen(struct window_pane *); #endif tmate-2.4.0/window.c000066400000000000000000000744131356407164200143230ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2007 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include "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; u_int next_active_point; 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 *); struct window_pane *window_pane_choose_best(struct window_pane **, u_int); RB_GENERATE(windows, window, entry, window_cmp); int window_cmp(struct window *w1, struct window *w2) { return (w1->id - w2->id); } 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; } } } struct window * window_find_by_id_str(const char *s) { const char *errstr; u_int id; if (*s != '@') return (NULL); id = strtonum(s + 1, 0, UINT_MAX, &errstr); if (errstr != NULL) return (NULL); return (window_find_by_id(id)); } struct window * window_find_by_id(u_int id) { struct window w; w.id = id; return (RB_FIND(windows, &windows, &w)); } void window_update_activity(struct window *w) { gettimeofday(&w->activity_time, NULL); alerts_queue(w, WINDOW_ACTIVITY); } struct window * window_create1(u_int sx, u_int sy) { struct window *w; w = xcalloc(1, sizeof *w); w->name = NULL; w->flags = 0; TAILQ_INIT(&w->panes); w->active = NULL; #ifdef TMATE w->tmate_last_sync_active_pane = NULL; #endif w->lastlayout = -1; w->layout_root = NULL; w->sx = sx; w->sy = sy; w->options = options_create(global_w_options); w->references = 0; w->id = next_window_id++; RB_INSERT(windows, &windows, w); window_update_activity(w); return (w); } struct window * window_create(const char *name, int argc, char **argv, const char *path, 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, argc, argv, path, 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) { RB_REMOVE(windows, &windows, w); if (w->layout_root != NULL) layout_free_cell(w->layout_root); if (w->saved_layout_root != NULL) layout_free_cell(w->saved_layout_root); free(w->old_layout); if (event_initialized(&w->name_event)) evtimer_del(&w->name_event); if (event_initialized(&w->alerts_timer)) evtimer_del(&w->alerts_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); #ifdef TMATE tmate_sync_layout(); #endif } void window_resize(struct window *w, u_int sx, u_int sy) { w->sx = sx; w->sy = sy; } int window_has_pane(struct window *w, struct window_pane *wp) { struct window_pane *wp1; TAILQ_FOREACH(wp1, &w->panes, entry) { if (wp1 == wp) return (1); } return (0); } int window_set_active_pane(struct window *w, struct window_pane *wp) { if (wp == w->active) return (0); 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 (1); } w->active->active_point = next_active_point++; w->active->flags |= PANE_CHANGED; return (1); } void window_redraw_active_switch(struct window *w, struct window_pane *wp) { const struct grid_cell *agc, *wgc; if (wp == w->active) return; /* * If window-style and window-active-style are the same, we don't need * to redraw panes when switching active panes. Otherwise, if the * active or inactive pane do not have a custom style, they will need * to be redrawn. */ agc = options_get_style(w->options, "window-active-style"); wgc = options_get_style(w->options, "window-style"); if (style_equal(agc, wgc)) return; if (style_equal(&grid_default_cell, &w->active->colgc)) w->active->flags |= PANE_REDRAW; if (style_equal(&grid_default_cell, &wp->colgc)) wp->flags |= PANE_REDRAW; } struct window_pane * window_get_active_at(struct window *w, u_int x, u_int y) { struct window_pane *wp; TAILQ_FOREACH(wp, &w->panes, entry) { if (!window_pane_visible(wp)) continue; if (x < wp->xoff || x > wp->xoff + wp->sx) continue; if (y < wp->yoff || y > wp->yoff + wp->sy) continue; return (wp); } return (NULL); } struct window_pane * window_find_string(struct window *w, const char *s) { u_int x, y; x = w->sx / 2; y = w->sy / 2; if (strcasecmp(s, "top") == 0) y = 0; else if (strcasecmp(s, "bottom") == 0) y = w->sy - 1; else if (strcasecmp(s, "left") == 0) x = 0; else if (strcasecmp(s, "right") == 0) x = w->sx - 1; else if (strcasecmp(s, "top-left") == 0) { x = 0; y = 0; } else if (strcasecmp(s, "top-right") == 0) { x = w->sx - 1; y = 0; } else if (strcasecmp(s, "bottom-left") == 0) { x = 0; y = w->sy - 1; } else if (strcasecmp(s, "bottom-right") == 0) { x = w->sx - 1; y = w->sy - 1; } else return (NULL); return (window_get_active_at(w, x, y)); } int window_zoom(struct window_pane *wp) { struct window *w = wp->window; struct window_pane *wp1; if (w->flags & WINDOW_ZOOMED) return (-1); if (!window_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; notify_window_layout_changed(w); return (0); } int window_unzoom(struct window *w) { struct window_pane *wp; if (!(w->flags & WINDOW_ZOOMED)) return (-1); w->flags &= ~WINDOW_ZOOMED; layout_free(w); w->layout_root = w->saved_layout_root; w->saved_layout_root = NULL; TAILQ_FOREACH(wp, &w->panes, entry) { wp->layout_cell = wp->saved_layout_cell; wp->saved_layout_cell = NULL; } layout_fix_panes(w, w->sx, w->sy); notify_window_layout_changed(w); 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_lost_pane(struct window *w, struct window_pane *wp) { if (wp == marked_pane.wp) server_clear_marked(); if (wp == w->active) { w->active = w->last; w->last = NULL; if (w->active == NULL) { w->active = TAILQ_PREV(wp, window_panes, entry); if (w->active == NULL) w->active = TAILQ_NEXT(wp, entry); } if (w->active != NULL) w->active->flags |= PANE_CHANGED; } else if (wp == w->last) w->last = NULL; } void window_remove_pane(struct window *w, struct window_pane *wp) { window_lost_pane(w, wp); TAILQ_REMOVE(&w->panes, wp, entry); window_pane_destroy(wp); } struct window_pane * window_pane_at_index(struct window *w, u_int idx) { struct window_pane *wp; u_int n; n = options_get_number(w->options, "pane-base-index"); TAILQ_FOREACH(wp, &w->panes, entry) { if (n == idx) return (wp); n++; } return (NULL); } struct window_pane * window_pane_next_by_number(struct window *w, struct window_pane *wp, u_int n) { for (; n > 0; n--) { if ((wp = TAILQ_NEXT(wp, entry)) == NULL) wp = TAILQ_FIRST(&w->panes); } return (wp); } struct window_pane * window_pane_previous_by_number(struct window *w, struct window_pane *wp, u_int n) { for (; n > 0; n--) { if ((wp = TAILQ_PREV(wp, window_panes, entry)) == NULL) wp = TAILQ_LAST(&w->panes, window_panes); } return (wp); } int window_pane_index(struct window_pane *wp, u_int *i) { struct window_pane *wq; struct window *w = wp->window; *i = options_get_number(w->options, "pane-base-index"); TAILQ_FOREACH(wq, &w->panes, entry) { if (wp == wq) { return (0); } (*i)++; } return (-1); } u_int window_count_panes(struct window *w) { struct window_pane *wp; u_int n; n = 0; TAILQ_FOREACH(wp, &w->panes, entry) n++; return (n); } void window_destroy_panes(struct window *w) { struct window_pane *wp; while (!TAILQ_EMPTY(&w->panes)) { wp = TAILQ_FIRST(&w->panes); TAILQ_REMOVE(&w->panes, wp, entry); window_pane_destroy(wp); } } /* Retuns the printable flags on a window, empty string if no flags set. */ char * window_printable_flags(struct session *s, struct winlink *wl) { char flags[32]; int pos; pos = 0; if (wl->flags & WINLINK_ACTIVITY) flags[pos++] = '#'; if (wl->flags & WINLINK_BELL) flags[pos++] = '!'; if (wl->flags & WINLINK_SILENCE) flags[pos++] = '~'; if (wl == s->curw) flags[pos++] = '*'; if (wl == TAILQ_FIRST(&s->lastw)) flags[pos++] = '-'; if (server_check_marked() && wl == marked_pane.wl) flags[pos++] = 'M'; if (wl->window->flags & WINDOW_ZOOMED) flags[pos++] = 'Z'; flags[pos] = '\0'; return (xstrdup(flags)); } struct window_pane * window_pane_find_by_id_str(const char *s) { const char *errstr; u_int id; if (*s != '%') return (NULL); id = strtonum(s + 1, 0, UINT_MAX, &errstr); if (errstr != NULL) return (NULL); return (window_pane_find_by_id(id)); } struct window_pane * window_pane_find_by_id(u_int id) { struct window_pane wp; wp.id = id; return (RB_FIND(window_pane_tree, &all_window_panes, &wp)); } struct window_pane * window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) { struct window_pane *wp; char host[HOST_NAME_MAX + 1]; wp = xcalloc(1, sizeof *wp); wp->window = w; wp->id = next_window_pane_id++; RB_INSERT(window_pane_tree, &all_window_panes, wp); wp->argc = 0; wp->argv = NULL; wp->shell = NULL; wp->cwd = NULL; wp->fd = -1; wp->event = NULL; 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; #ifdef TMATE wp->tmate_off = 0; #endif wp->saved_grid = NULL; memcpy(&wp->colgc, &grid_default_cell, sizeof wp->colgc); screen_init(&wp->base, sx, sy, hlimit); wp->screen = &wp->base; if (gethostname(host, sizeof host) == 0) screen_set_title(&wp->base, host); input_init(wp); return (wp); } void window_pane_destroy(struct window_pane *wp) { window_pane_reset_mode(wp); if (event_initialized(&wp->timer)) evtimer_del(&wp->timer); if (wp->fd != -1) { #ifdef HAVE_UTEMPTER utempter_remove_record(wp->fd); #endif bufferevent_free(wp->event); close(wp->fd); } input_free(wp); screen_free(&wp->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((void *)wp->cwd); free(wp->shell); cmd_free_argv(wp->argc, wp->argv); free(wp); } int window_pane_spawn(struct window_pane *wp, int argc, char **argv, const char *path, const char *shell, const char *cwd, struct environ *env, struct termios *tio, char **cause) { struct winsize ws; char *argv0, *cmd, **argvp; const char *ptr, *first, *home; struct termios tio2; #ifdef HAVE_UTEMPTER char s[32]; #endif int i; if (wp->fd != -1) { bufferevent_free(wp->event); close(wp->fd); } if (argc > 0) { cmd_free_argv(wp->argc, wp->argv); wp->argc = argc; wp->argv = cmd_copy_argv(argc, argv); } if (shell != NULL) { free(wp->shell); wp->shell = xstrdup(shell); } if (cwd != NULL) { free((void *)wp->cwd); wp->cwd = xstrdup(cwd); } cmd = cmd_stringify_argv(wp->argc, wp->argv); log_debug("spawn: %s -- %s", wp->shell, cmd); for (i = 0; i < wp->argc; i++) log_debug("spawn: argv[%d] = %s", i, wp->argv[i]); 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)); free(cmd); return (-1); case 0: if (chdir(wp->cwd) != 0) { if ((home = find_home()) == NULL || chdir(home) != 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 tio2.c_iflag |= IUTF8; #endif if (tcsetattr(STDIN_FILENO, TCSANOW, &tio2) != 0) fatal("tcgetattr failed"); closefrom(STDERR_FILENO + 1); if (path != NULL) environ_set(env, "PATH", "%s", path); environ_set(env, "TMUX_PANE", "%%%u", wp->id); environ_push(env); clear_signals(1); log_close(); setenv("SHELL", wp->shell, 1); ptr = strrchr(wp->shell, '/'); /* * If given one argument, assume it should be passed to sh -c; * with more than one argument, use execvp(). If there is no * arguments, create a login shell. */ if (wp->argc > 0) { if (wp->argc != 1) { /* Copy to ensure argv ends in NULL. */ argvp = cmd_copy_argv(wp->argc, wp->argv); execvp(argvp[0], argvp); fatal("execvp failed"); } first = wp->argv[0]; if (ptr != NULL && *(ptr + 1) != '\0') xasprintf(&argv0, "%s", ptr + 1); else xasprintf(&argv0, "%s", wp->shell); execl(wp->shell, argv0, "-c", first, (char *)NULL); fatal("execl failed"); } 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"); } #ifdef HAVE_UTEMPTER xsnprintf(s, sizeof s, "tmux(%lu).%%%u", (long) getpid(), wp->id); utempter_add_record(wp->fd, s); kill(getpid(), SIGCHLD); #endif setblocking(wp->fd, 0); wp->event = bufferevent_new(wp->fd, window_pane_read_callback, NULL, window_pane_error_callback, wp); bufferevent_setwatermark(wp->event, EV_READ, 0, READ_SIZE); bufferevent_enable(wp->event, EV_READ|EV_WRITE); free(cmd); return (0); } void window_pane_timer_callback(__unused int fd, __unused short events, void *data) { window_pane_read_callback(NULL, data); } void window_pane_read_callback(__unused struct bufferevent *bufev, void *data) { struct window_pane *wp = data; struct evbuffer *evb = wp->event->input; char *new_data; size_t new_size, available; struct client *c; struct timeval tv; if (event_initialized(&wp->timer)) evtimer_del(&wp->timer); log_debug("%%%u has %zu bytes", wp->id, EVBUFFER_LENGTH(evb)); TAILQ_FOREACH(c, &clients, entry) { if (!tty_client_ready(c, wp)) continue; available = EVBUFFER_LENGTH(c->tty.event->output); if (available > READ_BACKOFF) goto start_timer; } new_size = EVBUFFER_LENGTH(evb) - wp->pipe_off; if (wp->pipe_fd != -1 && new_size > 0) { new_data = EVBUFFER_DATA(evb) + wp->pipe_off; bufferevent_write(wp->pipe_event, new_data, new_size); } #ifdef TMATE 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); #endif input_parse(wp); wp->pipe_off = EVBUFFER_LENGTH(evb); #ifdef TMATE wp->tmate_off = EVBUFFER_LENGTH(evb); #endif return; start_timer: log_debug("%%%u backing off (%s %zu > %d)", wp->id, c->ttyname, available, READ_BACKOFF); tv.tv_sec = 0; tv.tv_usec = READ_TIME; evtimer_set(&wp->timer, window_pane_timer_callback, wp); evtimer_add(&wp->timer, &tv); } void window_pane_error_callback(__unused struct bufferevent *bufev, __unused short what, void *data) { struct window_pane *wp = data; server_destroy_pane(wp, 1); } 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|PANE_CHANGED); server_status_window(wp->window); 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|PANE_CHANGED); server_status_window(wp->window); #ifdef TMATE tmate_sync_copy_mode(wp); #endif } void window_pane_key(struct window_pane *wp, struct client *c, struct session *s, key_code key, struct mouse_event *m) { struct window_pane *wp2; if (KEYC_IS_MOUSE(key) && m == NULL) return; if (wp->mode != NULL) { if (wp->mode->key != NULL) wp->mode->key(wp, c, s, key, m); return; } if (wp->fd == -1 || wp->flags & PANE_INPUTOFF) return; input_key(wp, key, m); if (KEYC_IS_MOUSE(key)) return; if (options_get_number(wp->window->options, "synchronize-panes")) { TAILQ_FOREACH(wp2, &wp->window->panes, entry) { if (wp2 == wp || wp2->mode != NULL) continue; if (wp2->fd == -1 || wp2->flags & PANE_INPUTOFF) continue; if (window_pane_visible(wp2)) input_key(wp2, key, NULL); } } } 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); } /* Get MRU pane from a list. */ struct window_pane * window_pane_choose_best(struct window_pane **list, u_int size) { struct window_pane *next, *best; u_int i; if (size == 0) return (NULL); best = list[0]; for (i = 1; i < size; i++) { next = list[i]; if (next->active_point > best->active_point) best = next; } return (best); } /* * Find the pane directly above another. We build a list of those adjacent to * top edge and then choose the best. */ struct window_pane * window_pane_find_up(struct window_pane *wp) { struct window_pane *next, *best, **list; u_int edge, left, right, end, size; int found; if (wp == NULL || !window_pane_visible(wp)) return (NULL); list = NULL; size = 0; edge = wp->yoff; if (edge == 0) edge = wp->window->sy + 1; left = wp->xoff; right = wp->xoff + wp->sx; TAILQ_FOREACH(next, &wp->window->panes, entry) { if (next == wp || !window_pane_visible(next)) continue; if (next->yoff + next->sy + 1 != edge) continue; end = next->xoff + next->sx - 1; found = 0; if (next->xoff < left && end > right) found = 1; else if (next->xoff >= left && next->xoff <= right) found = 1; else if (end >= left && end <= right) found = 1; if (!found) continue; list = xreallocarray(list, size + 1, sizeof *list); list[size++] = next; } best = window_pane_choose_best(list, size); free(list); return (best); } /* Find the pane directly below another. */ struct window_pane * window_pane_find_down(struct window_pane *wp) { struct window_pane *next, *best, **list; u_int edge, left, right, end, size; int found; if (wp == NULL || !window_pane_visible(wp)) return (NULL); list = NULL; size = 0; edge = wp->yoff + wp->sy + 1; if (edge >= wp->window->sy) edge = 0; left = wp->xoff; right = wp->xoff + wp->sx; TAILQ_FOREACH(next, &wp->window->panes, entry) { if (next == wp || !window_pane_visible(next)) continue; if (next->yoff != edge) continue; end = next->xoff + next->sx - 1; found = 0; if (next->xoff < left && end > right) found = 1; else if (next->xoff >= left && next->xoff <= right) found = 1; else if (end >= left && end <= right) found = 1; if (!found) continue; list = xreallocarray(list, size + 1, sizeof *list); list[size++] = next; } best = window_pane_choose_best(list, size); free(list); return (best); } /* Find the pane directly to the left of another. */ struct window_pane * window_pane_find_left(struct window_pane *wp) { struct window_pane *next, *best, **list; u_int edge, top, bottom, end, size; int found; if (wp == NULL || !window_pane_visible(wp)) return (NULL); list = NULL; size = 0; edge = wp->xoff; if (edge == 0) edge = wp->window->sx + 1; top = wp->yoff; bottom = wp->yoff + wp->sy; TAILQ_FOREACH(next, &wp->window->panes, entry) { if (next == wp || !window_pane_visible(next)) continue; if (next->xoff + next->sx + 1 != edge) continue; end = next->yoff + next->sy - 1; found = 0; if (next->yoff < top && end > bottom) found = 1; else if (next->yoff >= top && next->yoff <= bottom) found = 1; else if (end >= top && end <= bottom) found = 1; if (!found) continue; list = xreallocarray(list, size + 1, sizeof *list); list[size++] = next; } best = window_pane_choose_best(list, size); free(list); return (best); } /* Find the pane directly to the right of another. */ struct window_pane * window_pane_find_right(struct window_pane *wp) { struct window_pane *next, *best, **list; u_int edge, top, bottom, end, size; int found; if (wp == NULL || !window_pane_visible(wp)) return (NULL); list = NULL; size = 0; edge = wp->xoff + wp->sx + 1; if (edge >= wp->window->sx) edge = 0; top = wp->yoff; bottom = wp->yoff + wp->sy; TAILQ_FOREACH(next, &wp->window->panes, entry) { if (next == wp || !window_pane_visible(next)) continue; if (next->xoff != edge) continue; end = next->yoff + next->sy - 1; found = 0; if (next->yoff < top && end > bottom) found = 1; else if (next->yoff >= top && next->yoff <= bottom) found = 1; else if (end >= top && end <= bottom) found = 1; if (!found) continue; list = xreallocarray(list, size + 1, sizeof *list); list[size++] = next; } best = window_pane_choose_best(list, size); free(list); return (best); } /* Clear alert flags for a winlink */ void winlink_clear_flags(struct winlink *wl) { struct session *s; struct winlink *wl_loop; RB_FOREACH(s, sessions, &sessions) { RB_FOREACH(wl_loop, winlinks, &s->windows) { if (wl_loop->window != wl->window) continue; if ((wl_loop->flags & WINLINK_ALERTFLAGS) == 0) continue; wl_loop->flags &= ~WINLINK_ALERTFLAGS; wl_loop->window->flags &= ~WINDOW_ALERTFLAGS; server_status_session(s); } } } int winlink_shuffle_up(struct session *s, struct winlink *wl) { int idx, last; idx = wl->idx + 1; /* Find the next free index. */ for (last = idx; last < INT_MAX; last++) { if (winlink_find_by_index(&s->windows, last) == NULL) break; } if (last == INT_MAX) return (-1); /* Move everything from last - 1 to idx up a bit. */ for (; last > idx; last--) { wl = winlink_find_by_index(&s->windows, last - 1); server_link_window(s, wl, s, last, 0, 0, NULL); server_unlink_window(s, wl); } return (idx); } tmate-2.4.0/xmalloc.c000066400000000000000000000051421356407164200144440ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Versions of malloc and friends that check their results, and never return * failure (they call fatal if they encounter an error). * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #include #include #include #include #include #include #include "tmux.h" void * xmalloc(size_t size) { void *ptr; if (size == 0) fatal("xmalloc: zero size"); ptr = malloc(size); if (ptr == NULL) fatal("xmalloc: allocating %zu bytes: %s", size, strerror(errno)); return ptr; } void * xcalloc(size_t nmemb, size_t size) { void *ptr; if (size == 0 || nmemb == 0) fatal("xcalloc: zero size"); ptr = calloc(nmemb, size); if (ptr == NULL) fatal("xcalloc: allocating %zu * %zu bytes: %s", nmemb, size, strerror(errno)); return ptr; } void * xrealloc(void *ptr, size_t size) { return xreallocarray(ptr, 1, size); } void * xreallocarray(void *ptr, size_t nmemb, size_t size) { void *new_ptr; if (nmemb == 0 || size == 0) fatal("xreallocarray: zero size"); new_ptr = reallocarray(ptr, nmemb, size); if (new_ptr == NULL) fatal("xreallocarray: allocating %zu * %zu bytes: %s", nmemb, size, strerror(errno)); return new_ptr; } char * xstrdup(const char *str) { char *cp; if ((cp = strdup(str)) == NULL) fatal("xstrdup: %s", strerror(errno)); return cp; } int xasprintf(char **ret, const char *fmt, ...) { va_list ap; int i; va_start(ap, fmt); i = xvasprintf(ret, fmt, ap); va_end(ap); return i; } __attribute__((__format__(__printf__, 2, 0))) int xvasprintf(char **ret, const char *fmt, va_list ap) { int i; i = vasprintf(ret, fmt, ap); if (i < 0 || *ret == NULL) fatal("xasprintf: %s", strerror(errno)); return i; } int xsnprintf(char *str, size_t len, const char *fmt, ...) { va_list ap; int i; va_start(ap, fmt); i = xvsnprintf(str, len, fmt, ap); va_end(ap); return i; } __attribute__((__format__(__printf__, 3, 0))) int xvsnprintf(char *str, size_t len, const char *fmt, va_list ap) { int i; if (len > INT_MAX) fatal("xsnprintf: len > INT_MAX"); i = vsnprintf(str, len, fmt, ap); if (i < 0 || i >= (int)len) fatal("xsnprintf: overflow"); return i; } tmate-2.4.0/xmalloc.h000066400000000000000000000027451356407164200144570ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Created: Mon Mar 20 22:09:17 1995 ylo * * Versions of malloc and friends that check their results, and never return * failure (they call fatal if they encounter an error). * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #ifndef XMALLOC_H #define XMALLOC_H #if !defined(__bounded__) # define __bounded__(x, y, z) #endif void *xmalloc(size_t); void *xcalloc(size_t, size_t); void *xrealloc(void *, size_t); void *xreallocarray(void *, size_t, size_t); char *xstrdup(const char *); int xasprintf(char **, const char *, ...) __attribute__((__format__ (printf, 2, 3))) __attribute__((__nonnull__ (2))); int xvasprintf(char **, const char *, va_list) __attribute__((__nonnull__ (2))); int xsnprintf(char *, size_t, const char *, ...) __attribute__((__format__ (printf, 3, 4))) __attribute__((__nonnull__ (3))) __attribute__((__bounded__ (__string__, 1, 2))); int xvsnprintf(char *, size_t, const char *, va_list) __attribute__((__nonnull__ (3))) __attribute__((__bounded__ (__string__, 1, 2))); #endif /* XMALLOC_H */ tmate-2.4.0/xterm-keys.c000066400000000000000000000134671356407164200151260ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2009 Nicholas Marriott * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "tmux.h" /* * xterm-style function keys append one of the following values before the last * character: * * 2 Shift * 3 Alt * 4 Shift + Alt * 5 Ctrl * 6 Shift + Ctrl * 7 Alt + Ctrl * 8 Shift + Alt + Ctrl * * Rather than parsing them, just match against a table. * * There are three forms for F1-F4 (\\033O_P and \\033O1;_P and \\033[1;_P). * We accept any but always output the latter (it comes first in the table). */ int xterm_keys_match(const char *, const char *, size_t, size_t *, key_code *); int xterm_keys_modifiers(const char *, size_t, size_t *, key_code *); struct xterm_keys_entry { key_code 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_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 *size, key_code *modifiers) { size_t pos; int retval; *modifiers = 0; if (len == 0) return (0); pos = 0; do { if (*template == '_') { retval = xterm_keys_modifiers(buf, len, &pos, modifiers); if (retval != 0) return (retval); continue; } if (buf[pos] != *template) return (-1); pos++; } while (*++template != '\0' && pos != len); if (*template != '\0') /* partial */ return (1); *size = pos; return (0); } /* Find modifiers from buffer. */ int xterm_keys_modifiers(const char *buf, size_t len, size_t *pos, key_code *modifiers) { u_int flags; if (len - *pos < 2) return (1); if (buf[*pos] < '0' || buf[*pos] > '9') return (-1); flags = buf[(*pos)++] - '0'; if (buf[*pos] >= '0' && buf[*pos] <= '9') flags = (flags * 10) + (buf[(*pos)++] - '0'); flags -= 1; *modifiers = 0; if (flags & 1) *modifiers |= KEYC_SHIFT; if (flags & 2) *modifiers |= KEYC_ESCAPE; if (flags & 4) *modifiers |= KEYC_CTRL; if (flags & 8) *modifiers |= KEYC_ESCAPE; return (0); } /* * Lookup key from a buffer against the table. Returns 0 for found (and the * key), -1 for not found, 1 for partial match. */ int xterm_keys_find(const char *buf, size_t len, size_t *size, key_code *key) { const struct xterm_keys_entry *entry; u_int i; int matched; key_code modifiers; for (i = 0; i < nitems(xterm_keys_table); i++) { entry = &xterm_keys_table[i]; matched = xterm_keys_match(entry->template, buf, len, size, &modifiers); if (matched == -1) continue; if (matched == 0) *key = entry->key | modifiers; return (matched); } return (-1); } /* Lookup a key number from the table. */ char * xterm_keys_lookup(key_code key) { const struct xterm_keys_entry *entry; u_int i; key_code modifiers; char *out; modifiers = 1; if (key & KEYC_SHIFT) modifiers += 1; if (key & KEYC_ESCAPE) modifiers += 2; if (key & KEYC_CTRL) modifiers += 4; /* * If the key has no modifiers, return NULL and let it fall through to * the normal lookup. */ if (modifiers == 1) return (NULL); /* 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); }