Merge branch 'master' into sixel

This commit is contained in:
Nicholas Marriott
2021-10-07 13:19:48 +01:00
189 changed files with 30660 additions and 10187 deletions

View File

@@ -37,7 +37,8 @@ Also include:
- Your terminal, and `$TERM` inside and outside of tmux.
- Logs from tmux (see below).
- Logs from tmux (see below). Please attach logs to the issue directly rather
than using a download site or pastebin. Put in a zip file if necessary.
- At most one or two screenshots, if helpful.

14
.github/README.md vendored
View File

@@ -14,8 +14,17 @@ page](https://github.com/libevent/libevent/releases/latest).
It also depends on [ncurses](https://www.gnu.org/software/ncurses/), available
from [this page](https://invisible-mirror.net/archives/ncurses/).
To build tmux, a C compiler (for example gcc or clang), make, pkg-config and a
suitable yacc (yacc or bison) are needed.
## Installation
### Binary packages
Some platforms provide binary packages for tmux, although these are sometimes
out of date. Examples are listed on
[this page](https://github.com/tmux/tmux/wiki/Installing).
### From release tarball
To build and install tmux from a release tarball, use:
@@ -28,6 +37,9 @@ sudo make install
tmux can use the utempter library to update utmp(5), if it is installed - run
configure with `--enable-utempter` to enable this.
For more detailed instructions on building and installing tmux, see
[this page](https://github.com/tmux/tmux/wiki/Installing).
### From version control
To get and build the latest from version control - note that this requires
@@ -69,7 +81,7 @@ And a bash(1) completion file at:
https://github.com/imomaliev/tmux-bash-completion
For debugging, run tmux with `-v` or `-vv` to generate server and client log
For debugging, run tmux with `-v` or `-vv` to generate server and client log
files in the current directory.
## Support

10
.github/lock.yml vendored
View File

@@ -1,10 +0,0 @@
daysUntilLock: 180
skipCreatedBefore: false
exemptLabels: []
lockLabel: false
lockComment: >
This thread has been automatically locked since there has not been
any recent activity after it was closed. Please open a new issue for
related bugs.
setLockReason: false
#only: issues

24
.github/travis/before-install.sh vendored Normal file
View File

@@ -0,0 +1,24 @@
#!/bin/sh
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
sudo apt-get update -qq
sudo apt-get -y install bison \
autotools-dev \
libncurses5-dev \
libevent-dev \
pkg-config \
libutempter-dev \
build-essential
if [ "$BUILD" = "musl" -o "$BUILD" = "musl-static" ]; then
sudo apt-get -y install musl-dev \
musl-tools
fi
fi
if [ "$TRAVIS_OS_NAME" = "freebsd" ]; then
sudo pkg install -y \
automake \
libevent \
pkgconf
fi

38
.github/travis/build-all.sh vendored Normal file
View File

@@ -0,0 +1,38 @@
#!/bin/sh
BUILD=$PWD/build
LIBEVENT=https://github.com/libevent/libevent/releases/download/release-2.1.11-stable/libevent-2.1.11-stab\
le.tar.gz
NCURSES=https://ftp.gnu.org/gnu/ncurses/ncurses-6.2.tar.gz
wget -4q $LIBEVENT || exit 1
tar -zxf libevent-*.tar.gz || exit 1
(cd libevent-*/ &&
./configure --prefix=$BUILD \
--enable-shared \
--disable-libevent-regress \
--disable-samples &&
make && make install) || exit 1
wget -4q $NCURSES || exit 1
tar -zxf ncurses-*.tar.gz || exit 1
(cd ncurses-*/ &&
CPPFLAGS=-P ./configure --prefix=$BUILD \
--with-shared \
--with-termlib \
--without-ada \
--without-cxx \
--without-manpages \
--without-progs \
--without-tests \
--without-tack \
--disable-database \
--enable-termcap \
--enable-pc-files \
--with-pkg-config-libdir=$BUILD/lib/pkgconfig &&
make && make install) || exit 1
sh autogen.sh || exit 1
PKG_CONFIG_PATH=$BUILD/lib/pkgconfig ./configure --prefix=$BUILD "$@"
make && make install || (cat config.log; exit 1)

25
.github/travis/build.sh vendored Normal file
View File

@@ -0,0 +1,25 @@
#!/bin/sh
sh autogen.sh || exit 1
case "$BUILD" in
static)
./configure --enable-static || exit 1
exec make
;;
all)
sh $(dirname $0)/build-all.sh
exec make
;;
musl)
CC=musl-gcc sh $(dirname $0)/build-all.sh
exec make
;;
musl-static)
CC=musl-gcc sh $(dirname $0)/build-all.sh --enable-static
exec make
;;
*)
./configure || exit 1
exec make
;;
esac

23
.github/workflows/lock.yml vendored Normal file
View File

@@ -0,0 +1,23 @@
name: 'Lock Threads'
on:
schedule:
- cron: '0 0 * * *'
jobs:
lock:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v2
with:
github-token: ${{ github.token }}
issue-lock-inactive-days: '30'
pr-lock-inactive-days: '60'
issue-lock-comment: >
This issue has been automatically locked since there
has not been any recent activity after it was closed.
Please open a new issue for related bugs.
pr-lock-comment: >
This pull request has been automatically locked since there
has not been any recent activity after it was closed.
Please open a new issue for related bugs.

2
.gitignore vendored
View File

@@ -19,3 +19,5 @@ configure
tmux.1.*
*.dSYM
cmd-parse.c
fuzz/*-fuzzer
.dirstamp

View File

@@ -1,16 +1,88 @@
language: c
os:
- linux
- osx
- linux
- freebsd
- osx
compiler:
- gcc
- clang
- gcc
- clang
arch:
- amd64
- arm64
env:
- BUILD=
- BUILD=static
- BUILD=all
- BUILD=musl
- BUILD=musl-static
jobs:
exclude:
# Static builds are broken on OS X (by Apple)
- os: osx
compiler: gcc
env: BUILD=static
- os: osx
compiler: clang
env: BUILD=static
# No musl on FreeBSD
- os: freebsd
compiler: gcc
env: BUILD=musl
- os: freebsd
compiler: clang
env: BUILD=musl
- os: freebsd
compiler: gcc
env: BUILD=musl-static
- os: freebsd
compiler: clang
env: BUILD=musl-static
# No musl on OS X
- os: osx
compiler: gcc
env: BUILD=musl
- os: osx
compiler: clang
env: BUILD=musl
- os: osx
compiler: gcc
env: BUILD=musl-static
- os: osx
compiler: clang
env: BUILD=musl-static
# arm64 doesn't link ncurses
- os: linux
compiler: gcc
arch: arm64
env: BUILD=all
- os: linux
compiler: clang
arch: arm64
env: BUILD=all
- os: linux
compiler: gcc
arch: arm64
env: BUILD=musl
- os: linux
compiler: clang
arch: arm64
env: BUILD=musl
- os: linux
compiler: gcc
arch: arm64
env: BUILD=musl-static
- os: linux
compiler: clang
arch: arm64
env: BUILD=musl-static
before_install:
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get update -qq; fi
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get -y install bison autotools-dev libncurses5-dev libevent-dev pkg-config libutempter-dev build-essential; fi
- sh .github/travis/before-install.sh
script:
- ./autogen.sh && ./configure && make
- sh .github/travis/build.sh

506
CHANGES
View File

@@ -1,8 +1,508 @@
CHANGES FROM 3.1 TO 3.2
CHANGES FROM 3.2a TO 3.3
XXX
* Change so that {} is converted to tmux commands immediately when parsed. This
means it must contain valid tmux commands. For commands which expand %% and
%%%, this now only happens within string arguments. Use of nested aliases
inside {} is now forbidden. Processing of commands given in quotes remains
the same.
CHANGES FROM 3.0a to 3.1
* Disable evports on SunOS since they are broken.
* Do not expand the file given with tmux -f so it can contain :s.
* Bump FORMAT_LOOP_LIMIT and add a log message when hit.
* Add a terminal feature for the mouse (since FreeBSD termcap does not have kmous).
* Forbid empty session names.
* Improve error reporting when the tmux /tmp directory cannot be created or
used.
* Give #() commands a one second grace period where the output is empty before
telling the user they aren't doing anything ("not ready").
* When building, pick default-terminal from the first of tmux-256color, tmux,
screen-256color, screen that is available on the build system (--with-TERM
can override).
* Do not close popups on resize, instead adjust them to fit.
* Add a client-active hook.
* Make window-linked and window-unlinked window options.
* Do not configure on macOS without the user making a choice about utf8proc
(either --enable-utf8proc or --disable-utf8proc).
* Do not freeze output in panes when a popup is open, let them continue to
redraw.
* Add pipe variants of the line copy commands.
* Change copy-line and copy-end-of-line not to cancel and add -and-cancel
variants, like the other copy commands.
* Support the OSC palette-setting sequences in popups.
* Add a pane-colours array option to specify the defaults palette.
* Add support for Unicode zero-width joiner.
* Make newline a style delimiter as well so they can cross multiple lines for
readability in configuration files.
* Change focus to be driven by events rather than scanning panes so the
ordering of in and out is consistent.
* Add display-popup -B to open a popup without a border.
* Add a menu for popups that can be opened with button three outside the popup
or on the left or top border. Resizing now only works on the right and bottom
borders or when using Meta. The menu allows a popup to be closed, expanded to
the full size of the client, centered in the client or changed into a pane.
* Make command-prompt and confirm-before block by default (like run-shell). A
new -b flags runs them in the background as before. Also set return code for
confirm-before.
* Change cursor style handling so tmux understands which sequences contain
blinking and sets the flag appropriately, means that it works whether cnorm
disables blinking or not. This now matches xterm's behaviour.
* More accurate vi(1) word navigation in copy mode and on the status line. This
changes the meaning of the word-separators option: setting it to the empty
string is equivalent to the previous behavior.
* Add -F for command-prompt and use it to fix "Rename" on the window menu.
* Add different command histories for different types of prompts ("command",
"search" etc).
CHANGES FROM 3.2 TO 3.2a
* Add an "always" value for the "extended-keys" option; if set then tmux will
forward extended keys to applications even if they do not request them.
* Add a "mouse" terminal feature so tmux can enable the mouse on terminals
where it is known to be supported even if terminfo(5) says otherwise.
* Do not expand the filename given to -f so it can contain colons.
* Fixes for problems with extended keys and modifiers, scroll region,
source-file, crosscompiling, format modifiers and other minor issues.
CHANGES FROM 3.1c TO 3.2
* Add a flag to disable keys to close a message.
* Permit shortcut keys in buffer, client, tree modes to be configured with a
format (-K flag to choose-buffer, choose-client, choose-tree).
* Add a current_file format for the config file being parsed.
* When display-message used in config file, show the message after the config
file finishes.
* Add client-detached notification in control mode.
* Improve performance of format evaluation.
* Make jump command support UTF-8 in copy mode.
* Support X11 colour names and other colour formats for OSC 10 and 11.
* Add "pipe" variants of "copy-pipe" commands which do not copy.
* Include "focused" in client flags.
* Send Unicode directional isolate characters around horizontal pane borders if
the terminal supports UTF-8 and an extension terminfo(5) capability "Bidi" is
present.
* Add a -S flag to new-window to make it select the existing window if one
with the given name already exists rather than failing with an error.
* Add a format modifier to check if a window or session name exists (N/w or
N/s).
* Add compat clock_gettime for older macOS.
* Add a no-detached choice to detach-on-destroy which detaches only if there
are no other detached sessions to switch to.
* Add rectangle-on and rectangle-off copy mode commands.
* Change so that window_flags escapes # automatically. A new format
window_raw_flags contains the old unescaped version.
* Add -N flag to never start server even if command would normally do so.
* With incremental search, start empty and only repeat the previous search if
the user tries to search again with an empty prompt.
* Add a value for remain-on-exit that only keeps the pane if the program
failed.
* Add a -C flag to run-shell to use a tmux command rather than a shell command.
* Do not list user options with show-hooks.
* Remove current match indicator in copy mode which can't work anymore since we
only search the visible region.
* Make synchronize-panes a pane option and add -U flag to set-option to unset
an option on all panes.
* Make replacement of ##s consistent when drawing formats, whether followed by
[ or not. Add a flag (e) to the q: format modifier to double up #s.
* Add -N flag to display-panes to ignore keys.
* Change how escaping is processed for formats so that ## and # can be used in
styles.
* Add a 'w' format modifier for string width.
* Add support for Haiku.
* Expand menu and popup -x and -y as formats.
* Add numeric comparisons for formats.
* Fire focus events even when the pane is in a mode.
* Add -O flag to display-menu to not automatically close when all mouse buttons
are released.
* Allow fnmatch(3) wildcards in update-environment.
* Disable nested job expansion so that the result of #() is not expanded again.
* Use the setal capability as well as (tmux's) Setulc.
* Add -q flag to unbind-key to hide errors.
* Allow -N without a command to change or add a note to an existing key.
* Add a -w flag to set- and load-buffer to send to clipboard using OSC 52.
* Add -F to set-environment and source-file.
* Allow colour to be spelt as color in various places.
* Add n: modifier to get length of a format.
* Respond to OSC colour requests if a colour is available.
* Add a -d option to display-message to set delay.
* Add a way for control mode clients to subscribe to a format and be notified
of changes rather than having to poll.
* Add some formats for search in copy mode (search_present, search_match).
* Do not wait on shutdown for commands started with run -b.
* Add -b flags to insert a window before (like the existing -a for after) to
break-pane, move-window, new-window.
* Make paste -p the default for ].
* Add support for pausing a pane when the output buffered for a control mode
client gets too far behind. The pause-after flag with a time is set on the
pane with refresh-client -f and a paused pane may be resumed with
refresh-client -A.
* Allow strings in configuration files to span multiple lines - newlines and
any leading whitespace are removed, as well as any following comments that
couldn't be part of a format. This allows long formats or other strings to be
annotated and indented.
* Instead of using a custom parse function to process {} in configuration
files, treat as a set of statements the same as outside {} and convert back
to a string as the last step. This means the rules are consistent inside and
outside {}, %if and friends work at the right time, and the final result
isn't littered with unnecessary newlines.
* Add support for extended keys - both xterm(1)'s CSI 27 ~ sequence and the
libtickit CSI u sequence are accepted; only the latter is output. tmux will
only attempt to use these if the extended-keys option is on and it can detect
that the terminal outside supports them (or is told it does with the
"extkeys" terminal feature).
* Add an option to set the pane border lines style from a choice of single
lines (ACS or UTF-8), double or heavy (UTF-8), simple (plain ASCII) or number
(the pane numbers). Lines that won't work on a non-UTF-8 terminal are
translated back into ACS when they are output.
* Make focus events update the latest client (like a key press).
* Store UTF-8 characters differently to reduce memory use.
* Fix break-pane -n when only one pane in the window.
* Instead of sending all data to control mode clients as fast as possible, add
a limit of how much data will be sent to the client and try to use it for
panes with some degree of fairness.
* Add an active-pane client flag (set with attach-session -f, new-session -f
or refresh-client -f). This allows a client to have an independent active
pane for interactive use (the window client pane is still used for many
things however).
* Add a mark to copy mode, this is set with the set-mark command (bound to X)
and appears with the entire line shown using copy-mode-mark-style and the
marked character in reverse. The jump-to-mark command (bound to M-x) swaps
the mark and the cursor positions.
* Add a -D flag to make the tmux server run in the foreground and not as a
daemon.
* Do not loop forever in copy mode when search finds an empty match.
* Fix the next-matching-bracket logic when using vi(1) keys.
* Add a customize mode where options may be browsed and changed, includes
adding a brief description of each option. Bound to C-b C by default.
* Change message log (C-b ~) so there is one for the server rather than one per
client and it remains after detach, and make it useful by logging every
command.
* Add M-+ and M-- to tree mode to expand and collapse all.
* Change the existing client flags for control mode to apply for any client,
use the same mechanism for the read-only flag and add an ignore-size flag.
refresh-client -F has become -f (-F stays for backwards compatibility) and
attach-session and switch-client now have -f flags also. A new format
client_flags lists the flags and is shown by list-clients by default.
This separates the read-only flag from "ignore size" behaviour (new
ignore-size) flag - both behaviours are useful in different circumstances.
attach -r and switchc -r remain and set or toggle both flags together.
* Store and restore cursor position when copy mode is resized.
* Export TERM_PROGRAM and TERM_PROGRAM_VERSION like various other terminals.
* Add formats for after hook command arguments: hook_arguments with all the
arguments together; hook_argument_0, hook_argument_1 and so on with
individual arguments; hook_flag_X if flag -X is present; hook_flag_X_0,
hook_flag_X_1 and so on if -X appears multiple times.
* Try to search the entire history first for up to 200 ms so a search count can
be shown. If it takes too long, search the visible text only.
* Use VIS_CSTYLE for paste buffers also (show \012 as \n).
* Change default formats for tree mode, client mode and buffer mode to be more
compact and remove some clutter.
* Add a key (e) in buffer mode to open the buffer in an editor. The buffer
contents is updated when the editor exits.
* Add -e flag for new-session to set environment variables, like the same flag
for new-window.
* Improve search match marking in copy mode. Two new options
copy-mode-match-style and copy-mode-current-match-style to set the style for
matches and for the current match respectively. Also a change so that if a
copy key is pressed with no selection, the current match (if any) is copied.
* Sanitize session names like window names instead of forbidding invalid ones.
* Check if the clear terminfo(5) capability starts with CSI and if so then
assume the terminal is VT100-like, rather than relying on the XT capability.
* Improve command prompt tab completion and add menus both for strings and -t
and -s (when used without a trailing space). command-prompt has additional
flags for only completing a window (-W) and a target (-T), allowing C-b ' to
only show windows and C-b . only targets.
* Change all the style options to string options so they can support formats.
Change pane-active-border-style to use this to change the border colour when
in a mode or with synchronize-panes on. This also implies a few minor changes
to existing behaviour:
- set-option -a with a style option automatically inserts a comma between the
old value and appended text.
- OSC 10 and 11 no longer set the window-style option, instead they store the
colour internally in the pane data and it is used as the default when the
option is evaluated.
- status-fg and -bg now override status-style instead of the option values
being changed.
* Add extension terminfo(5) capabilities for margins and focus reporting.
* Try $XDG_CONFIG_HOME/tmux/tmux.conf as well as ~/.config/tmux/tmux.conf for
configuration file (the search paths are in TMUX_CONF in Makefile.am).
* Remove the DSR 1337 iTerm2 extension and replace by the extended device
attributes sequence (CSI > q) supported by more terminals.
* Add a -s flag to copy-mode to specify a different pane for the source
content. This means it is possible to view two places in a pane's history at
the same time in different panes, or view the history while still using the
pane. Pressing r refreshes the content from the source pane.
* Add an argument to list-commands to show only a single command.
* Change copy mode to make copy of the pane history so it does not need to
freeze the pane.
* Restore pane_current_path format from portable tmux on OpenBSD.
* Wait until the initial command sequence is done before sending a device
attributes request and other bits that prompt a reply from the terminal. This
means that stray replies are not left on the terminal if the command has
attached and then immediately detached and tmux will not be around to receive
them.
* Add a -f filter argument to the list commands like choose-tree.
* Move specific hooks for panes to pane options and windows for window options
rather than all hooks being session options. These hooks are now window options:
window-layout-changed
window-linked
window-pane-changed
window-renamed
window-unlinked
And these are now pane options:
pane-died
pane-exited
pane-focus-in
pane-focus-out
pane-mode-changed
pane-set-clipboard
Any existing configurations using these hooks on a session rather than
globally (that is, set-hook or set-option without -g) may need to be changed.
* Show signal names when a process exits with remain-on-exit on platforms which
have a way to get them.
* Start menu with top item selected if no mouse and use mode-style for the
selected item.
* Add a copy-command option and change copy-pipe and friends to pipe to it if
used without arguments, allows all the default copy key bindings to be
changed to pipe with one option rather than needing to change each key
binding individually.
* Tidy up the terminal detection and feature code and add named sets of
terminal features, each of which are defined in one place and map to a
builtin set of terminfo(5) capabilities. Features can be specified based on
TERM with a new terminal-features option or with the -T flag when running
tmux. tmux will also detect a few common terminals from the DA and DSR
responses.
This is intended to make it easier to configure tmux's use of terminfo(5)
even in the presence of outdated ncurses(3) or terminfo(5) databases or for
features which do not yet have a terminfo(5) entry. Instead of having to grok
terminfo(5) capability names and what they should be set to in the
terminal-overrides option, the user can hopefully just give tmux a feature
name and let it do the right thing.
The terminal-overrides option remains both for backwards compatibility and to
allow tweaks of individual capabilities.
* Support mintty's application escape sequence (means tmux doesn't have to
delay to wait for Escape, so no need to reduce escape-time when using
mintty).
* Change so main-pane-width and height can be given as a percentage.
* Support for the iTerm2 synchronized updates feature (allows the terminal to
avoid unnecessary drawing while output is still in progress).
* Make the mouse_word and mouse_line formats work in copy mode and enable the
default pane menu in copy mode.
* Add a -T flag to resize-pane to trim lines below the cursor, moving lines out
of the history.
* Add a way to mark environment variables as "hidden" so they can be used by
tmux (for example in formats) but are not set in the environment for new
panes. set-environment and show-environment have a new -h flag and there is a
new %hidden statement for the configuration file.
* Change default position for display-menu -x and -y to centre rather than top
left.
* Add support for per-client transient popups, similar to menus but which are
connected to an external command (like a pane). These are created with new
command display-popup.
* Change double and triple click bindings so that only one is fired (previously
double click was fired on the way to triple click). Also add default double
and triple click bindings to copy the word or line under the cursor and
change the existing bindings in copy mode to do the same.
* Add a default binding for button 2 to paste.
* Add -d flag to run-shell to delay before running the command and allow it to
be used without a command so it just delays.
* Add C-g to cancel command prompt with vi keys as well as emacs, and q in
command mode.
* When the server socket is given with -S, create it with umask 177 instead of
117 (because it may not be in a safe directory like the default directory in
/tmp).
* Add a copy-mode -H flag to hide the position marker in the top right.
* Add number operators for formats (+, -, *, / and m),
CHANGED FROM 3.1b TO 3.1c
* Do not write after the end of the array and overwrite the stack when
colon-separated SGR sequences contain empty arguments.
CHANGES FROM 3.1a TO 3.1b
* Fix build on systems without sys/queue.h.
* Fix crash when allow-rename is on and an empty name is set.
CHANGES FROM 3.1 TO 3.1a
* Do not close stdout prematurely in control mode since it is needed to print
exit messages. Prevents hanging when detaching with iTerm2.
CHANGES FROM 3.0a TO 3.1
* Only search the visible part of the history when marking (highlighting)
search terms. This is much faster than searching the whole history and solves
problems with large histories. The count of matches shown is now the visible
matches rather than all matches.
* Search using regular expressions in copy mode. search-forward and
search-backward use regular expressions by default; the incremental versions
do not.
* Turn off mouse mode 1003 as well as the rest when exiting.
* Add selection_active format for when the selection is present but not moving
with the cursor.
* Fix dragging with modifier keys, so binding keys such as C-MouseDrag1Pane and
C-MouseDragEnd1Pane now work.
* Add -a to list-keys to also list keys without notes with -N.
* Do not jump to next word end if already on a word end when selecting a word;
fixes select-word with single character words and vi(1) keys.
* Fix top and bottom pane calculation with pane border status enabled.
* Add support for adding a note to a key binding (with bind-key -N) and use
this to add descriptions to the default key bindings. A new -N flag to

View File

@@ -12,8 +12,9 @@ dist_EXTRA_tmux_SOURCES = compat/*.[ch]
# Preprocessor flags.
AM_CPPFLAGS += @XOPEN_DEFINES@ \
-DTMUX_VERSION="\"@VERSION@\"" \
-DTMUX_CONF="\"$(sysconfdir)/tmux.conf:~/.tmux.conf:~/.config/tmux/tmux.conf\""
-DTMUX_VERSION='"@VERSION@"' \
-DTMUX_CONF='"$(sysconfdir)/tmux.conf:~/.tmux.conf:$$XDG_CONFIG_HOME/tmux/tmux.conf:~/.config/tmux/tmux.conf"' \
-DTMUX_TERM='"@DEFAULT_TERM@"'
# Additional object files.
LDADD = $(LIBOBJS)
@@ -28,7 +29,10 @@ AM_CFLAGS += -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations
AM_CFLAGS += -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare
AM_CFLAGS += -Wundef -Wbad-function-cast -Winline -Wcast-align
AM_CFLAGS += -Wdeclaration-after-statement -Wno-pointer-sign -Wno-attributes
AM_CFLAGS += -Wno-unused-result
AM_CFLAGS += -Wno-unused-result -Wno-format-y2k
if IS_DARWIN
AM_CFLAGS += -Wno-deprecated-declarations -Wno-cast-align
endif
AM_CPPFLAGS += -DDEBUG
endif
AM_CPPFLAGS += -iquote.
@@ -58,6 +62,11 @@ if IS_NETBSD
AM_CPPFLAGS += -D_OPENBSD_SOURCE
endif
# Set flags for Haiku.
if IS_HAIKU
AM_CPPFLAGS += -D_BSD_SOURCE
endif
# List of sources.
dist_tmux_SOURCES = \
alerts.c \
@@ -120,6 +129,7 @@ dist_tmux_SOURCES = \
cmd-show-environment.c \
cmd-show-messages.c \
cmd-show-options.c \
cmd-show-prompt-history.c \
cmd-source-file.c \
cmd-split-window.c \
cmd-swap-pane.c \
@@ -136,6 +146,7 @@ dist_tmux_SOURCES = \
file.c \
format.c \
format-draw.c \
grid-reader.c \
grid-view.c \
grid.c \
input-keys.c \
@@ -155,6 +166,7 @@ dist_tmux_SOURCES = \
options-table.c \
options.c \
paste.c \
popup.c \
proc.c \
regsub.c \
resize.c \
@@ -171,7 +183,9 @@ dist_tmux_SOURCES = \
style.c \
tmux.c \
tmux.h \
tmux-protocol.h \
tty-acs.c \
tty-features.c \
tty-keys.c \
tty-term.c \
tty.c \
@@ -180,11 +194,11 @@ dist_tmux_SOURCES = \
window-client.c \
window-clock.c \
window-copy.c \
window-customize.c \
window-tree.c \
window.c \
xmalloc.c \
xmalloc.h \
xterm-keys.c
xmalloc.h
nodist_tmux_SOURCES = osdep-@PLATFORM@.c
# Add compat file for forkpty.
@@ -197,6 +211,12 @@ if HAVE_UTF8PROC
nodist_tmux_SOURCES += compat/utf8proc.c
endif
if NEED_FUZZING
check_PROGRAMS = fuzz/input-fuzzer
fuzz_input_fuzzer_LDFLAGS = $(FUZZING_LIBS)
fuzz_input_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS)
endif
# Install tmux.1 in the right format.
install-exec-hook:
if test x@MANFORMAT@ = xmdoc; then \

11
README
View File

@@ -16,6 +16,9 @@ It also depends on ncurses, available from:
https://invisible-mirror.net/archives/ncurses/
To build tmux, a C compiler (for example gcc or clang), make, pkg-config and a
suitable yacc (yacc or bison) are needed.
* Installation
To build and install tmux from a release tarball, use:
@@ -52,6 +55,14 @@ source tree with:
A small example configuration is in example_tmux.conf.
Other documentation is available in the wiki:
https://github.com/tmux/tmux/wiki
Also see the tmux FAQ at:
https://github.com/tmux/tmux/wiki/FAQ
A bash(1) completion file is at:
https://github.com/imomaliev/tmux-bash-completion

View File

@@ -89,7 +89,7 @@ of the remote name "obsd-tmux", we can now create the master branch from
% 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

View File

@@ -18,7 +18,6 @@
#include <sys/types.h>
#include <event.h>
#include <stdlib.h>
#include "tmux.h"
@@ -200,7 +199,7 @@ alerts_check_bell(struct window *w)
* not check WINLINK_BELL).
*/
s = wl->session;
if (s->curw != wl) {
if (s->curw != wl || s->attached == 0) {
wl->flags |= WINLINK_BELL;
server_status_session(s);
}
@@ -236,7 +235,7 @@ alerts_check_activity(struct window *w)
if (wl->flags & WINLINK_ACTIVITY)
continue;
s = wl->session;
if (s->curw != wl) {
if (s->curw != wl || s->attached == 0) {
wl->flags |= WINLINK_ACTIVITY;
server_status_session(s);
}
@@ -272,7 +271,7 @@ alerts_check_silence(struct window *w)
if (wl->flags & WINLINK_SILENCE)
continue;
s = wl->session;
if (s->curw != wl) {
if (s->curw != wl || s->attached == 0) {
wl->flags |= WINLINK_SILENCE;
server_status_session(s);
}
@@ -315,9 +314,12 @@ alerts_set_message(struct winlink *wl, const char *type, const char *option)
tty_putcode(&c->tty, TTYC_BEL);
if (visual == VISUAL_OFF)
continue;
if (c->session->curw == wl)
status_message_set(c, "%s in current window", type);
else
status_message_set(c, "%s in window %d", type, wl->idx);
if (c->session->curw == wl) {
status_message_set(c, -1, 1, 0, "%s in current window",
type);
} else {
status_message_set(c, -1, 1, 0, "%s in window %d", type,
wl->idx);
}
}
}

View File

@@ -18,6 +18,7 @@
#include <sys/types.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@@ -28,12 +29,10 @@
* Manipulate command arguments.
*/
struct args_value {
char *value;
TAILQ_ENTRY(args_value) entry;
};
/* List of argument values. */
TAILQ_HEAD(args_values, args_value);
/* Single arguments flag. */
struct args_entry {
u_char flag;
struct args_values values;
@@ -41,6 +40,20 @@ struct args_entry {
RB_ENTRY(args_entry) entry;
};
/* Parsed argument flags and values. */
struct args {
struct args_tree tree;
u_int count;
struct args_value *values;
};
/* Prepared command state. */
struct args_command_state {
struct cmd_list *cmdlist;
char *cmd;
struct cmd_parse_input pi;
};
static struct args_entry *args_find(struct args *, u_char);
static int args_cmp(struct args_entry *, struct args_entry *);
@@ -55,44 +68,297 @@ args_cmp(struct args_entry *a1, struct args_entry *a2)
/* Find a flag in the arguments tree. */
static struct args_entry *
args_find(struct args *args, u_char ch)
args_find(struct args *args, u_char flag)
{
struct args_entry entry;
entry.flag = ch;
entry.flag = flag;
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)
/* Copy value. */
static void
args_copy_value(struct args_value *to, struct args_value *from)
{
struct args *args;
int opt;
to->type = from->type;
switch (from->type) {
case ARGS_NONE:
break;
case ARGS_COMMANDS:
to->cmdlist = from->cmdlist;
to->cmdlist->references++;
break;
case ARGS_STRING:
to->string = xstrdup(from->string);
break;
}
}
/* Get value as string. */
static const char *
args_value_as_string(struct args_value *value)
{
switch (value->type) {
case ARGS_NONE:
return ("");
case ARGS_COMMANDS:
if (value->cached == NULL)
value->cached = cmd_list_print(value->cmdlist, 0);
return (value->cached);
case ARGS_STRING:
return (value->string);
}
}
/* Create an empty arguments set. */
struct args *
args_create(void)
{
struct args *args;
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);
RB_INIT(&args->tree);
return (args);
}
/* Parse arguments into a new argument set. */
struct args *
args_parse(const struct args_parse *parse, struct args_value *values,
u_int count, char **cause)
{
struct args *args;
u_int i;
enum args_parse_type type;
struct args_value *value, *new;
u_char flag, argument;
const char *found, *string, *s;
if (count == 0)
return (args_create());
args = args_create();
for (i = 1; i < count; /* nothing */) {
value = &values[i];
if (value->type != ARGS_STRING)
break;
string = value->string;
if (*string++ != '-' || *string == '\0')
break;
i++;
if (string[0] == '-' && string[1] == '\0')
break;
for (;;) {
flag = *string++;
if (flag == '\0')
break;
if (flag == '?') {
args_free(args);
return (NULL);
}
if (!isalnum(flag)) {
xasprintf(cause, "invalid flag -%c", flag);
args_free(args);
return (NULL);
}
found = strchr(parse->template, flag);
if (found == NULL) {
xasprintf(cause, "unknown flag -%c", flag);
args_free(args);
return (NULL);
}
argument = *++found;
if (argument != ':') {
log_debug("%s: -%c", __func__, flag);
args_set(args, flag, NULL);
continue;
}
new = xcalloc(1, sizeof *new);
if (*string != '\0') {
new->type = ARGS_STRING;
new->string = xstrdup(string);
} else {
if (i == count) {
xasprintf(cause,
"-%c expects an argument",
flag);
args_free(args);
return (NULL);
}
if (values[i].type != ARGS_STRING) {
xasprintf(cause,
"-%c argument must be a string",
flag);
args_free(args);
return (NULL);
}
args_copy_value(new, &values[i++]);
}
s = args_value_as_string(new);
log_debug("%s: -%c = %s", __func__, flag, s);
args_set(args, flag, new);
break;
}
}
log_debug("%s: flags end at %u of %u", __func__, i, count);
if (i != count) {
for (/* nothing */; i < count; i++) {
value = &values[i];
s = args_value_as_string(value);
log_debug("%s: %u = %s (type %d)", __func__, i, s,
value->type);
if (parse->cb != NULL) {
type = parse->cb(args, args->count, cause);
if (type == ARGS_PARSE_INVALID) {
args_free(args);
return (NULL);
}
} else
type = ARGS_PARSE_STRING;
args->values = xrecallocarray(args->values,
args->count, args->count + 1, sizeof *args->values);
new = &args->values[args->count++];
switch (type) {
case ARGS_PARSE_INVALID:
fatalx("unexpected argument type");
case ARGS_PARSE_STRING:
if (value->type != ARGS_STRING) {
xasprintf(cause,
"argument %u must be \"string\"",
args->count);
args_free(args);
return (NULL);
}
args_copy_value(new, value);
break;
case ARGS_PARSE_COMMANDS_OR_STRING:
args_copy_value(new, value);
break;
case ARGS_PARSE_COMMANDS:
if (value->type != ARGS_COMMANDS) {
xasprintf(cause,
"argument %u must be { commands }",
args->count);
args_free(args);
return (NULL);
}
args_copy_value(new, value);
break;
}
}
}
if (parse->lower != -1 && args->count < (u_int)parse->lower) {
xasprintf(cause,
"too few arguments (need at least %u)",
parse->lower);
args_free(args);
return (NULL);
}
if (parse->upper != -1 && args->count > (u_int)parse->upper) {
xasprintf(cause,
"too many arguments (need at most %u)",
parse->upper);
args_free(args);
return (NULL);
}
return (args);
}
/* Copy and expand a value. */
static void
args_copy_copy_value(struct args_value *to, struct args_value *from, int argc,
char **argv)
{
char *s, *expanded;
int i;
to->type = from->type;
switch (from->type) {
case ARGS_NONE:
break;
case ARGS_STRING:
expanded = xstrdup(from->string);
for (i = 0; i < argc; i++) {
s = cmd_template_replace(expanded, argv[i], i + 1);
free(expanded);
expanded = s;
}
to->string = expanded;
break;
case ARGS_COMMANDS:
to->cmdlist = cmd_list_copy(from->cmdlist, argc, argv);
break;
}
}
/* Copy an arguments set. */
struct args *
args_copy(struct args *args, int argc, char **argv)
{
struct args *new_args;
struct args_entry *entry;
struct args_value *value, *new_value;
u_int i;
cmd_log_argv(argc, argv, "%s", __func__);
new_args = args_create();
RB_FOREACH(entry, args_tree, &args->tree) {
if (TAILQ_EMPTY(&entry->values)) {
for (i = 0; i < entry->count; i++)
args_set(new_args, entry->flag, NULL);
continue;
}
TAILQ_FOREACH(value, &entry->values, entry) {
new_value = xcalloc(1, sizeof *new_value);
args_copy_copy_value(new_value, value, argc, argv);
args_set(new_args, entry->flag, new_value);
}
}
if (args->count == 0)
return (new_args);
new_args->count = args->count;
new_args->values = xcalloc(args->count, sizeof *new_args->values);
for (i = 0; i < args->count; i++) {
new_value = &new_args->values[i];
args_copy_copy_value(new_value, &args->values[i], argc, argv);
}
return (new_args);
}
/* Free a value. */
void
args_free_value(struct args_value *value)
{
switch (value->type) {
case ARGS_NONE:
break;
case ARGS_STRING:
free(value->string);
break;
case ARGS_COMMANDS:
cmd_list_free(value->cmdlist);
break;
}
free(value->cached);
}
/* Free values. */
void
args_free_values(struct args_value *values, u_int count)
{
u_int i;
for (i = 0; i < count; i++)
args_free_value(&values[i]);
}
/* Free an arguments set. */
void
args_free(struct args *args)
@@ -102,13 +368,14 @@ args_free(struct args *args)
struct args_value *value;
struct args_value *value1;
cmd_free_argv(args->argc, args->argv);
args_free_values(args->values, args->count);
free(args->values);
RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) {
RB_REMOVE(args_tree, &args->tree, entry);
TAILQ_FOREACH_SAFE(value, &entry->values, entry, value1) {
TAILQ_REMOVE(&entry->values, value, entry);
free(value->value);
args_free_value(value);
free(value);
}
free(entry);
@@ -117,11 +384,52 @@ args_free(struct args *args)
free(args);
}
/* Convert arguments to vector. */
void
args_to_vector(struct args *args, int *argc, char ***argv)
{
char *s;
u_int i;
*argc = 0;
*argv = NULL;
for (i = 0; i < args->count; i++) {
switch (args->values[i].type) {
case ARGS_NONE:
break;
case ARGS_STRING:
cmd_append_argv(argc, argv, args->values[i].string);
break;
case ARGS_COMMANDS:
s = cmd_list_print(args->values[i].cmdlist, 0);
cmd_append_argv(argc, argv, s);
free(s);
break;
}
}
}
/* Convert arguments from vector. */
struct args_value *
args_from_vector(int argc, char **argv)
{
struct args_value *values;
int i;
values = xcalloc(argc, sizeof *values);
for (i = 0; i < argc; i++) {
values[i].type = ARGS_STRING;
values[i].string = xstrdup(argv[i]);
}
return (values);
}
/* Add to string. */
static void printflike(3, 4)
args_print_add(char **buf, size_t *len, const char *fmt, ...)
{
va_list ap;
va_list ap;
char *s;
size_t slen;
@@ -138,43 +446,35 @@ args_print_add(char **buf, size_t *len, const char *fmt, ...)
/* Add value to string. */
static void
args_print_add_value(char **buf, size_t *len, struct args_entry *entry,
struct args_value *value)
args_print_add_value(char **buf, size_t *len, struct args_value *value)
{
char *escaped;
if (**buf != '\0')
args_print_add(buf, len, " -%c ", entry->flag);
else
args_print_add(buf, len, "-%c ", entry->flag);
escaped = args_escape(value->value);
args_print_add(buf, len, "%s", escaped);
free(escaped);
}
/* Add argument to string. */
static void
args_print_add_argument(char **buf, size_t *len, const char *argument)
{
char *escaped;
char *expanded = NULL;
if (**buf != '\0')
args_print_add(buf, len, " ");
escaped = args_escape(argument);
args_print_add(buf, len, "%s", escaped);
free(escaped);
switch (value->type) {
case ARGS_NONE:
break;
case ARGS_COMMANDS:
expanded = cmd_list_print(value->cmdlist, 0);
args_print_add(buf, len, "{ %s }", expanded);
break;
case ARGS_STRING:
expanded = args_escape(value->string);
args_print_add(buf, len, "%s", expanded);
break;
}
free(expanded);
}
/* Print a set of arguments. */
char *
args_print(struct args *args)
{
size_t len;
size_t len;
char *buf;
int i;
u_int j;
u_int i, j;
struct args_entry *entry;
struct args_value *value;
@@ -194,13 +494,18 @@ args_print(struct args *args)
/* Then the flags with arguments. */
RB_FOREACH(entry, args_tree, &args->tree) {
TAILQ_FOREACH(value, &entry->values, entry)
args_print_add_value(&buf, &len, entry, value);
TAILQ_FOREACH(value, &entry->values, entry) {
if (*buf != '\0')
args_print_add(&buf, &len, " -%c", entry->flag);
else
args_print_add(&buf, &len, "-%c", entry->flag);
args_print_add_value(&buf, &len, value);
}
}
/* And finally the argument vector. */
for (i = 0; i < args->argc; i++)
args_print_add_argument(&buf, &len, args->argv[i]);
for (i = 0; i < args->count; i++)
args_print_add_value(&buf, &len, &args->values[i]);
return (buf);
}
@@ -209,25 +514,35 @@ args_print(struct args *args)
char *
args_escape(const char *s)
{
static const char quoted[] = " #\"';${}";
static const char dquoted[] = " #';${}%";
static const char squoted[] = " \"";
char *escaped, *result;
int flags;
int flags, quotes = 0;
if (*s == '\0') {
xasprintf(&result, "''");
return (result);
}
if (s[strcspn(s, dquoted)] != '\0')
quotes = '"';
else if (s[strcspn(s, squoted)] != '\0')
quotes = '\'';
if (*s == '\0')
return (xstrdup(s));
if (s[0] != ' ' &&
(strchr(quoted, s[0]) != NULL || s[0] == '~') &&
s[1] == '\0') {
s[1] == '\0' &&
(quotes != 0 || s[0] == '~')) {
xasprintf(&escaped, "\\%c", s[0]);
return (escaped);
}
flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL;
if (s[strcspn(s, quoted)] != '\0')
if (quotes == '"')
flags |= VIS_DQ;
utf8_stravis(&escaped, s, flags);
if (flags & VIS_DQ) {
if (quotes == '\'')
xasprintf(&result, "'%s'", escaped);
else if (quotes == '"') {
if (*escaped == '~')
xasprintf(&result, "\"\\%s\"", escaped);
else
@@ -244,11 +559,11 @@ args_escape(const char *s)
/* Return if an argument is present. */
int
args_has(struct args *args, u_char ch)
args_has(struct args *args, u_char flag)
{
struct args_entry *entry;
entry = args_find(args, ch);
entry = args_find(args, flag);
if (entry == NULL)
return (0);
return (entry->count);
@@ -256,83 +571,256 @@ args_has(struct args *args, u_char ch)
/* Set argument value in the arguments tree. */
void
args_set(struct args *args, u_char ch, const char *s)
args_set(struct args *args, u_char flag, struct args_value *value)
{
struct args_entry *entry;
struct args_value *value;
entry = args_find(args, ch);
entry = args_find(args, flag);
if (entry == NULL) {
entry = xcalloc(1, sizeof *entry);
entry->flag = ch;
entry->flag = flag;
entry->count = 1;
TAILQ_INIT(&entry->values);
RB_INSERT(args_tree, &args->tree, entry);
} else
entry->count++;
if (s != NULL) {
value = xcalloc(1, sizeof *value);
value->value = xstrdup(s);
if (value != NULL && value->type != ARGS_NONE)
TAILQ_INSERT_TAIL(&entry->values, value, entry);
}
}
/* Get argument value. Will be NULL if it isn't present. */
const char *
args_get(struct args *args, u_char ch)
args_get(struct args *args, u_char flag)
{
struct args_entry *entry;
if ((entry = args_find(args, ch)) == NULL)
if ((entry = args_find(args, flag)) == NULL)
return (NULL);
return (TAILQ_LAST(&entry->values, args_values)->value);
if (TAILQ_EMPTY(&entry->values))
return (NULL);
return (TAILQ_LAST(&entry->values, args_values)->string);
}
/* Get first argument. */
u_char
args_first(struct args *args, struct args_entry **entry)
{
*entry = RB_MIN(args_tree, &args->tree);
if (*entry == NULL)
return (0);
return ((*entry)->flag);
}
/* Get next argument. */
u_char
args_next(struct args_entry **entry)
{
*entry = RB_NEXT(args_tree, &args->tree, *entry);
if (*entry == NULL)
return (0);
return ((*entry)->flag);
}
/* Get argument count. */
u_int
args_count(struct args *args)
{
return (args->count);
}
/* Get argument values. */
struct args_value *
args_values(struct args *args)
{
return (args->values);
}
/* Get argument value. */
struct args_value *
args_value(struct args *args, u_int idx)
{
if (idx >= args->count)
return (NULL);
return (&args->values[idx]);
}
/* Return argument as string. */
const char *
args_string(struct args *args, u_int idx)
{
if (idx >= args->count)
return (NULL);
return (args_value_as_string(&args->values[idx]));
}
/* Make a command now. */
struct cmd_list *
args_make_commands_now(struct cmd *self, struct cmdq_item *item, u_int idx,
int expand)
{
struct args_command_state *state;
char *error;
struct cmd_list *cmdlist;
state = args_make_commands_prepare(self, item, idx, NULL, 0, expand);
cmdlist = args_make_commands(state, 0, NULL, &error);
if (cmdlist == NULL) {
cmdq_error(item, "%s", error);
free(error);
}
else
cmdlist->references++;
args_make_commands_free(state);
return (cmdlist);
}
/* Save bits to make a command later. */
struct args_command_state *
args_make_commands_prepare(struct cmd *self, struct cmdq_item *item, u_int idx,
const char *default_command, int wait, int expand)
{
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct client *tc = cmdq_get_target_client(item);
struct args_value *value;
struct args_command_state *state;
const char *cmd;
state = xcalloc(1, sizeof *state);
if (idx < args->count) {
value = &args->values[idx];
if (value->type == ARGS_COMMANDS) {
state->cmdlist = value->cmdlist;
state->cmdlist->references++;
return (state);
}
cmd = value->string;
} else {
if (default_command == NULL)
fatalx("argument out of range");
cmd = default_command;
}
if (expand)
state->cmd = format_single_from_target(item, cmd);
else
state->cmd = xstrdup(cmd);
log_debug("%s: %s", __func__, state->cmd);
if (wait)
state->pi.item = item;
cmd_get_source(self, &state->pi.file, &state->pi.line);
state->pi.c = tc;
if (state->pi.c != NULL)
state->pi.c->references++;
cmd_find_copy_state(&state->pi.fs, target);
return (state);
}
/* Return argument as command. */
struct cmd_list *
args_make_commands(struct args_command_state *state, int argc, char **argv,
char **error)
{
struct cmd_parse_result *pr;
char *cmd, *new_cmd;
int i;
if (state->cmdlist != NULL) {
if (argc == 0)
return (state->cmdlist);
return (cmd_list_copy(state->cmdlist, argc, argv));
}
cmd = xstrdup(state->cmd);
for (i = 0; i < argc; i++) {
new_cmd = cmd_template_replace(cmd, argv[i], i + 1);
log_debug("%s: %%%u %s: %s", __func__, i + 1, argv[i], new_cmd);
free(cmd);
cmd = new_cmd;
}
log_debug("%s: %s", __func__, cmd);
pr = cmd_parse_from_string(cmd, &state->pi);
free(cmd);
switch (pr->status) {
case CMD_PARSE_ERROR:
*error = pr->error;
return (NULL);
case CMD_PARSE_SUCCESS:
return (pr->cmdlist);
}
}
/* Free commands state. */
void
args_make_commands_free(struct args_command_state *state)
{
if (state->cmdlist != NULL)
cmd_list_free(state->cmdlist);
if (state->pi.c != NULL)
server_client_unref(state->pi.c);
free(state->cmd);
free(state);
}
/* Get prepared command. */
char *
args_make_commands_get_command(struct args_command_state *state)
{
struct cmd *first;
int n;
char *s;
if (state->cmdlist != NULL) {
first = cmd_list_first(state->cmdlist);
if (first == NULL)
return (xstrdup(""));
return (xstrdup(cmd_get_entry(first)->name));
}
n = strcspn(state->cmd, " ,");
xasprintf(&s, "%.*s", n, state->cmd);
return (s);
}
/* Get first value in argument. */
const char *
args_first_value(struct args *args, u_char ch, struct args_value **value)
struct args_value *
args_first_value(struct args *args, u_char flag)
{
struct args_entry *entry;
if ((entry = args_find(args, ch)) == NULL)
if ((entry = args_find(args, flag)) == NULL)
return (NULL);
*value = TAILQ_FIRST(&entry->values);
if (*value == NULL)
return (NULL);
return ((*value)->value);
return (TAILQ_FIRST(&entry->values));
}
/* Get next value in argument. */
const char *
args_next_value(struct args_value **value)
struct args_value *
args_next_value(struct args_value *value)
{
if (*value == NULL)
return (NULL);
*value = TAILQ_NEXT(*value, entry);
if (*value == NULL)
return (NULL);
return ((*value)->value);
return (TAILQ_NEXT(value, entry));
}
/* 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)
args_strtonum(struct args *args, u_char flag, long long minval,
long long maxval, char **cause)
{
const char *errstr;
long long ll;
long long ll;
struct args_entry *entry;
struct args_value *value;
if ((entry = args_find(args, ch)) == NULL) {
if ((entry = args_find(args, flag)) == NULL) {
*cause = xstrdup("missing");
return (0);
}
value = TAILQ_LAST(&entry->values, args_values);
ll = strtonum(value->value, minval, maxval, &errstr);
ll = strtonum(value->string, minval, maxval, &errstr);
if (errstr != NULL) {
*cause = xstrdup(errstr);
return (0);
@@ -341,3 +829,60 @@ args_strtonum(struct args *args, u_char ch, long long minval, long long maxval,
*cause = NULL;
return (ll);
}
/* Convert an argument to a number which may be a percentage. */
long long
args_percentage(struct args *args, u_char flag, long long minval,
long long maxval, long long curval, char **cause)
{
const char *value;
struct args_entry *entry;
if ((entry = args_find(args, flag)) == NULL) {
*cause = xstrdup("missing");
return (0);
}
value = TAILQ_LAST(&entry->values, args_values)->string;
return (args_string_percentage(value, minval, maxval, curval, cause));
}
/* Convert a string to a number which may be a percentage. */
long long
args_string_percentage(const char *value, long long minval, long long maxval,
long long curval, char **cause)
{
const char *errstr;
long long ll;
size_t valuelen = strlen(value);
char *copy;
if (value[valuelen - 1] == '%') {
copy = xstrdup(value);
copy[valuelen - 1] = '\0';
ll = strtonum(copy, 0, 100, &errstr);
free(copy);
if (errstr != NULL) {
*cause = xstrdup(errstr);
return (0);
}
ll = (curval * ll) / 100;
if (ll < minval) {
*cause = xstrdup("too small");
return (0);
}
if (ll > maxval) {
*cause = xstrdup("too large");
return (0);
}
} else {
ll = strtonum(value, minval, maxval, &errstr);
if (errstr != NULL) {
*cause = xstrdup(errstr);
return (0);
}
}
*cause = NULL;
return (ll);
}

View File

@@ -31,7 +31,8 @@ attributes_tostring(int attr)
if (attr == 0)
return ("none");
len = xsnprintf(buf, sizeof buf, "%s%s%s%s%s%s%s%s%s%s%s%s%s",
len = xsnprintf(buf, sizeof buf, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
(attr & GRID_ATTR_CHARSET) ? "acs," : "",
(attr & GRID_ATTR_BRIGHT) ? "bright," : "",
(attr & GRID_ATTR_DIM) ? "dim," : "",
(attr & GRID_ATTR_UNDERSCORE) ? "underscore," : "",
@@ -62,6 +63,7 @@ attributes_fromstring(const char *str)
const char *name;
int attr;
} table[] = {
{ "acs", GRID_ATTR_CHARSET },
{ "bright", GRID_ATTR_BRIGHT },
{ "bold", GRID_ATTR_BRIGHT },
{ "dim", GRID_ATTR_DIM },

97
cfg.c
View File

@@ -27,12 +27,15 @@
#include "tmux.h"
struct client *cfg_client;
static char *cfg_file;
int cfg_finished;
static char **cfg_causes;
static u_int cfg_ncauses;
static struct cmdq_item *cfg_item;
int cfg_quiet = 1;
char **cfg_files;
u_int cfg_nfiles;
static enum cmd_retval
cfg_client_done(__unused struct cmdq_item *item, __unused void *data)
{
@@ -59,52 +62,11 @@ cfg_done(__unused struct cmdq_item *item, __unused void *data)
return (CMD_RETURN_NORMAL);
}
void
set_cfg_file(const char *path)
{
free(cfg_file);
cfg_file = xstrdup(path);
}
static char *
expand_cfg_file(const char *path, const char *home)
{
char *expanded, *name;
const char *end;
struct environ_entry *value;
if (strncmp(path, "~/", 2) == 0) {
if (home == NULL)
return (NULL);
xasprintf(&expanded, "%s%s", home, path + 1);
return (expanded);
}
if (*path == '$') {
end = strchr(path, '/');
if (end == NULL)
name = xstrdup(path + 1);
else
name = xstrndup(path + 1, end - path - 1);
value = environ_find(global_environ, name);
free(name);
if (value == NULL)
return (NULL);
if (end == NULL)
end = "";
xasprintf(&expanded, "%s%s", value->value, end);
return (expanded);
}
return (xstrdup(path));
}
void
start_cfg(void)
{
const char *home = find_home();
struct client *c;
char *path, *copy, *next, *expanded;
struct client *c;
u_int i;
/*
* Configuration files are loaded without a client, so commands are run
@@ -122,21 +84,12 @@ start_cfg(void)
cmdq_append(c, cfg_item);
}
if (cfg_file == NULL) {
path = copy = xstrdup(TMUX_CONF);
while ((next = strsep(&path, ":")) != NULL) {
expanded = expand_cfg_file(next, home);
if (expanded == NULL) {
log_debug("couldn't expand %s", next);
continue;
}
log_debug("expanded %s to %s", next, expanded);
load_cfg(expanded, c, NULL, CMD_PARSE_QUIET, NULL);
free(expanded);
}
free(copy);
} else
load_cfg(cfg_file, c, NULL, 0, NULL);
for (i = 0; i < cfg_nfiles; i++) {
if (cfg_quiet)
load_cfg(cfg_files[i], c, NULL, CMD_PARSE_QUIET, NULL);
else
load_cfg(cfg_files[i], c, NULL, 0, NULL);
}
cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL));
}
@@ -149,6 +102,7 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags,
struct cmd_parse_input pi;
struct cmd_parse_result *pr;
struct cmdq_item *new_item0;
struct cmdq_state *state;
if (new_item != NULL)
*new_item = NULL;
@@ -170,8 +124,6 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags,
pr = cmd_parse_from_file(f, &pi);
fclose(f);
if (pr->status == CMD_PARSE_EMPTY)
return (0);
if (pr->status == CMD_PARSE_ERROR) {
cfg_add_cause("%s", pr->error);
free(pr->error);
@@ -182,12 +134,19 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags,
return (0);
}
new_item0 = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
if (item != NULL)
state = cmdq_copy_state(cmdq_get_state(item));
else
state = cmdq_new_state(NULL, NULL, 0);
cmdq_add_format(state, "current_file", "%s", pi.file);
new_item0 = cmdq_get_command(pr->cmdlist, state);
if (item != NULL)
new_item0 = cmdq_insert_after(item, new_item0);
else
new_item0 = cmdq_append(NULL, new_item0);
cmd_list_free(pr->cmdlist);
cmdq_free_state(state);
if (new_item != NULL)
*new_item = new_item0;
@@ -202,6 +161,7 @@ load_cfg_from_buffer(const void *buf, size_t len, const char *path,
struct cmd_parse_input pi;
struct cmd_parse_result *pr;
struct cmdq_item *new_item0;
struct cmdq_state *state;
if (new_item != NULL)
*new_item = NULL;
@@ -216,8 +176,6 @@ load_cfg_from_buffer(const void *buf, size_t len, const char *path,
pi.c = c;
pr = cmd_parse_from_buffer(buf, len, &pi);
if (pr->status == CMD_PARSE_EMPTY)
return (0);
if (pr->status == CMD_PARSE_ERROR) {
cfg_add_cause("%s", pr->error);
free(pr->error);
@@ -228,12 +186,19 @@ load_cfg_from_buffer(const void *buf, size_t len, const char *path,
return (0);
}
new_item0 = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
if (item != NULL)
state = cmdq_copy_state(cmdq_get_state(item));
else
state = cmdq_new_state(NULL, NULL, 0);
cmdq_add_format(state, "current_file", "%s", pi.file);
new_item0 = cmdq_get_command(pr->cmdlist, state);
if (item != NULL)
new_item0 = cmdq_insert_after(item, new_item0);
else
new_item0 = cmdq_append(NULL, new_item0);
cmd_list_free(pr->cmdlist);
cmdq_free_state(state);
if (new_item != NULL)
*new_item = new_item0;
@@ -283,7 +248,7 @@ cfg_show_causes(struct session *s)
wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode != &window_view_mode)
window_pane_set_mode(wp, &window_view_mode, NULL, NULL);
window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL);
for (i = 0; i < cfg_ncauses; i++) {
window_copy_add(wp, "%s", cfg_causes[i]);
free(cfg_causes[i]);

493
client.c
View File

@@ -18,12 +18,12 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <sys/file.h>
#include <errno.h>
#include <event.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
@@ -34,7 +34,8 @@
static struct tmuxproc *client_proc;
static struct tmuxpeer *client_peer;
static int client_flags;
static uint64_t client_flags;
static int client_suspended;
static enum {
CLIENT_EXIT_NONE,
CLIENT_EXIT_DETACHED,
@@ -44,11 +45,13 @@ static enum {
CLIENT_EXIT_LOST_SERVER,
CLIENT_EXIT_EXITED,
CLIENT_EXIT_SERVER_EXITED,
CLIENT_EXIT_MESSAGE_PROVIDED
} client_exitreason = CLIENT_EXIT_NONE;
static int client_exitflag;
static int client_exitval;
static enum msgtype client_exittype;
static const char *client_exitsession;
static char *client_exitmessage;
static const char *client_execshell;
static const char *client_execcmd;
static int client_attached;
@@ -56,8 +59,10 @@ static struct client_files client_files = RB_INITIALIZER(&client_files);
static __dead void client_exec(const char *,const char *);
static int client_get_lock(char *);
static int client_connect(struct event_base *, const char *, int);
static void client_send_identify(const char *, const char *);
static int client_connect(struct event_base *, const char *,
uint64_t);
static void client_send_identify(const char *, const char *,
char **, u_int, const char *, int);
static void client_signal(int);
static void client_dispatch(struct imsg *, void *);
static void client_dispatch_attached(struct imsg *);
@@ -97,7 +102,7 @@ client_get_lock(char *lockfile)
/* Connect client to server. */
static int
client_connect(struct event_base *base, const char *path, int start_server)
client_connect(struct event_base *base, const char *path, uint64_t flags)
{
struct sockaddr_un sa;
size_t size;
@@ -122,7 +127,9 @@ retry:
log_debug("connect failed: %s", strerror(errno));
if (errno != ECONNREFUSED && errno != ENOENT)
goto failed;
if (!start_server)
if (flags & CLIENT_NOSTARTSERVER)
goto failed;
if (~flags & CLIENT_STARTSERVER)
goto failed;
close(fd);
@@ -154,7 +161,7 @@ retry:
close(lockfd);
return (-1);
}
fd = server_start(client_proc, base, lockfd, lockfile);
fd = server_start(client_proc, flags, base, lockfd, lockfile);
}
if (locked && lockfd >= 0) {
@@ -206,6 +213,8 @@ client_exit_message(void)
return ("exited");
case CLIENT_EXIT_SERVER_EXITED:
return ("server exited");
case CLIENT_EXIT_MESSAGE_PROVIDED:
return (client_exitmessage);
}
return ("unknown reason");
}
@@ -214,76 +223,68 @@ client_exit_message(void)
static void
client_exit(void)
{
struct client_file *cf;
size_t left;
int waiting = 0;
RB_FOREACH (cf, client_files, &client_files) {
if (cf->event == NULL)
continue;
left = EVBUFFER_LENGTH(cf->event->output);
if (left != 0) {
waiting++;
log_debug("file %u %zu bytes left", cf->stream, left);
}
}
if (waiting == 0)
if (!file_write_left(&client_files))
proc_exit(client_proc);
}
/* Client main loop. */
int
client_main(struct event_base *base, int argc, char **argv, int flags)
client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
int feat)
{
struct cmd_parse_result *pr;
struct cmd *cmd;
struct msg_command *data;
int cmdflags, fd, i;
const char *ttynam, *cwd;
int fd, i;
const char *ttynam, *termname, *cwd;
pid_t ppid;
enum msgtype msg;
struct termios tio, saved_tio;
size_t size;
size_t size, linesize = 0;
ssize_t linelen;
char *line = NULL, **caps = NULL, *cause;
u_int ncaps = 0;
struct args_value *values;
/* Ignore SIGCHLD now or daemon() in the server will leave a zombie. */
signal(SIGCHLD, SIG_IGN);
/* Save the flags. */
client_flags = flags;
/* Set up the initial command. */
cmdflags = 0;
if (shell_command != NULL) {
msg = MSG_SHELL;
cmdflags = CMD_STARTSERVER;
flags |= CLIENT_STARTSERVER;
} else if (argc == 0) {
msg = MSG_COMMAND;
cmdflags = CMD_STARTSERVER;
flags |= CLIENT_STARTSERVER;
} else {
msg = MSG_COMMAND;
/*
* It sucks parsing the command string twice (in client and
* later in server) but it is necessary to get the start server
* flag.
* It's annoying parsing the command string twice (in client
* and later in server) but it is necessary to get the start
* server flag.
*/
pr = cmd_parse_from_arguments(argc, argv, NULL);
values = args_from_vector(argc, argv);
pr = cmd_parse_from_arguments(values, argc, NULL);
if (pr->status == CMD_PARSE_SUCCESS) {
TAILQ_FOREACH(cmd, &pr->cmdlist->list, qentry) {
if (cmd->entry->flags & CMD_STARTSERVER)
cmdflags |= CMD_STARTSERVER;
}
if (cmd_list_any_have(pr->cmdlist, CMD_STARTSERVER))
flags |= CLIENT_STARTSERVER;
cmd_list_free(pr->cmdlist);
} else
free(pr->error);
args_free_values(values, argc);
free(values);
}
/* Create client process structure (starts logging). */
client_proc = proc_start("client");
proc_set_signals(client_proc, client_signal);
/* Save the flags. */
client_flags = flags;
log_debug("flags are %#llx", (unsigned long long)client_flags);
/* Initialize the client socket and start the server. */
fd = client_connect(base, socket_path, cmdflags & CMD_STARTSERVER);
fd = client_connect(base, socket_path, client_flags);
if (fd == -1) {
if (errno == ECONNREFUSED) {
fprintf(stderr, "no server running on %s\n",
@@ -301,6 +302,8 @@ client_main(struct event_base *base, int argc, char **argv, int flags)
cwd = "/";
if ((ttynam = ttyname(STDIN_FILENO)) == NULL)
ttynam = "";
if ((termname = getenv("TERM")) == NULL)
termname = "";
/*
* Drop privileges for client. "proc exec" is needed for -c and for
@@ -316,6 +319,16 @@ client_main(struct event_base *base, int argc, char **argv, int flags)
NULL) != 0)
fatal("pledge failed");
/* Load terminfo entry if any. */
if (isatty(STDIN_FILENO) &&
*termname != '\0' &&
tty_term_read_list(termname, STDIN_FILENO, &caps, &ncaps,
&cause) != 0) {
fprintf(stderr, "%s\n", cause);
free(cause);
return (1);
}
/* Free stuff that is not used in the client. */
if (ptm_fd != -1)
close(ptm_fd);
@@ -346,7 +359,8 @@ client_main(struct event_base *base, int argc, char **argv, int flags)
}
/* Send identify messages. */
client_send_identify(ttynam, cwd);
client_send_identify(ttynam, termname, caps, ncaps, cwd, feat);
tty_term_free_list(caps, ncaps);
/* Send first command. */
if (msg == MSG_COMMAND) {
@@ -389,6 +403,11 @@ client_main(struct event_base *base, int argc, char **argv, int flags)
client_exec(client_execshell, client_execcmd);
}
/* Restore streams to blocking. */
setblocking(STDIN_FILENO, 1);
setblocking(STDOUT_FILENO, 1);
setblocking(STDERR_FILENO, 1);
/* Print the exit message, if any, and exit. */
if (client_attached) {
if (client_exitreason != CLIENT_EXIT_NONE)
@@ -397,42 +416,65 @@ client_main(struct event_base *base, int argc, char **argv, int flags)
ppid = getppid();
if (client_exittype == MSG_DETACHKILL && ppid > 1)
kill(ppid, SIGHUP);
} else if (client_flags & CLIENT_CONTROLCONTROL) {
} else if (client_flags & CLIENT_CONTROL) {
if (client_exitreason != CLIENT_EXIT_NONE)
printf("%%exit %s\n", client_exit_message());
else
printf("%%exit\n");
printf("\033\\");
tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio);
fflush(stdout);
if (client_flags & CLIENT_CONTROL_WAITEXIT) {
setvbuf(stdin, NULL, _IOLBF, 0);
for (;;) {
linelen = getline(&line, &linesize, stdin);
if (linelen <= 1)
break;
}
free(line);
}
if (client_flags & CLIENT_CONTROLCONTROL) {
printf("\033\\");
fflush(stdout);
tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio);
}
} else if (client_exitreason != CLIENT_EXIT_NONE)
fprintf(stderr, "%s\n", client_exit_message());
setblocking(STDIN_FILENO, 1);
return (client_exitval);
}
/* Send identify messages to server. */
static void
client_send_identify(const char *ttynam, const char *cwd)
client_send_identify(const char *ttynam, const char *termname, char **caps,
u_int ncaps, const char *cwd, int feat)
{
const char *s;
char **ss;
size_t sslen;
int fd, flags = client_flags;
pid_t pid;
char **ss;
size_t sslen;
int fd, flags = client_flags;
pid_t pid;
u_int i;
proc_send(client_peer, MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags);
proc_send(client_peer, MSG_IDENTIFY_LONGFLAGS, -1, &client_flags,
sizeof client_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_TERM, -1, termname,
strlen(termname) + 1);
proc_send(client_peer, MSG_IDENTIFY_FEATURES, -1, &feat, sizeof feat);
proc_send(client_peer, MSG_IDENTIFY_TTYNAME, -1, ttynam,
strlen(ttynam) + 1);
proc_send(client_peer, MSG_IDENTIFY_CWD, -1, cwd, strlen(cwd) + 1);
for (i = 0; i < ncaps; i++) {
proc_send(client_peer, MSG_IDENTIFY_TERMINFO, -1,
caps[i], strlen(caps[i]) + 1);
}
if ((fd = dup(STDIN_FILENO)) == -1)
fatal("dup failed");
proc_send(client_peer, MSG_IDENTIFY_STDIN, fd, NULL, 0);
if ((fd = dup(STDOUT_FILENO)) == -1)
fatal("dup failed");
proc_send(client_peer, MSG_IDENTIFY_STDOUT, fd, NULL, 0);
pid = getpid();
proc_send(client_peer, MSG_IDENTIFY_CLIENTPID, -1, &pid, sizeof pid);
@@ -447,256 +489,6 @@ client_send_identify(const char *ttynam, const char *cwd)
proc_send(client_peer, MSG_IDENTIFY_DONE, -1, NULL, 0);
}
/* File write error callback. */
static void
client_write_error_callback(__unused struct bufferevent *bev,
__unused short what, void *arg)
{
struct client_file *cf = arg;
log_debug("write error file %d", cf->stream);
bufferevent_free(cf->event);
cf->event = NULL;
close(cf->fd);
cf->fd = -1;
if (client_exitflag)
client_exit();
}
/* File write callback. */
static void
client_write_callback(__unused struct bufferevent *bev, void *arg)
{
struct client_file *cf = arg;
if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) {
bufferevent_free(cf->event);
close(cf->fd);
RB_REMOVE(client_files, &client_files, cf);
file_free(cf);
}
if (client_exitflag)
client_exit();
}
/* Open write file. */
static void
client_write_open(void *data, size_t datalen)
{
struct msg_write_open *msg = data;
const char *path;
struct msg_write_ready reply;
struct client_file find, *cf;
const int flags = O_NONBLOCK|O_WRONLY|O_CREAT;
int error = 0;
if (datalen < sizeof *msg)
fatalx("bad MSG_WRITE_OPEN size");
if (datalen == sizeof *msg)
path = "-";
else
path = (const char *)(msg + 1);
log_debug("open write file %d %s", msg->stream, path);
find.stream = msg->stream;
if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) {
cf = file_create(NULL, msg->stream, NULL, NULL);
RB_INSERT(client_files, &client_files, cf);
} else {
error = EBADF;
goto reply;
}
if (cf->closed) {
error = EBADF;
goto reply;
}
cf->fd = -1;
if (msg->fd == -1)
cf->fd = open(path, msg->flags|flags, 0644);
else {
if (msg->fd != STDOUT_FILENO && msg->fd != STDERR_FILENO)
errno = EBADF;
else {
cf->fd = dup(msg->fd);
if (client_flags & CLIENT_CONTROL)
close(msg->fd); /* can only be used once */
}
}
if (cf->fd == -1) {
error = errno;
goto reply;
}
cf->event = bufferevent_new(cf->fd, NULL, client_write_callback,
client_write_error_callback, cf);
bufferevent_enable(cf->event, EV_WRITE);
goto reply;
reply:
reply.stream = msg->stream;
reply.error = error;
proc_send(client_peer, MSG_WRITE_READY, -1, &reply, sizeof reply);
}
/* Write to client file. */
static void
client_write_data(void *data, size_t datalen)
{
struct msg_write_data *msg = data;
struct client_file find, *cf;
size_t size = datalen - sizeof *msg;
if (datalen < sizeof *msg)
fatalx("bad MSG_WRITE size");
find.stream = msg->stream;
if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL)
fatalx("unknown stream number");
log_debug("write %zu to file %d", size, cf->stream);
if (cf->event != NULL)
bufferevent_write(cf->event, msg + 1, size);
}
/* Close client file. */
static void
client_write_close(void *data, size_t datalen)
{
struct msg_write_close *msg = data;
struct client_file find, *cf;
if (datalen != sizeof *msg)
fatalx("bad MSG_WRITE_CLOSE size");
find.stream = msg->stream;
if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL)
fatalx("unknown stream number");
log_debug("close file %d", cf->stream);
if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) {
if (cf->event != NULL)
bufferevent_free(cf->event);
if (cf->fd != -1)
close(cf->fd);
RB_REMOVE(client_files, &client_files, cf);
file_free(cf);
}
}
/* File read callback. */
static void
client_read_callback(__unused struct bufferevent *bev, void *arg)
{
struct client_file *cf = arg;
void *bdata;
size_t bsize;
struct msg_read_data *msg;
size_t msglen;
msg = xmalloc(sizeof *msg);
for (;;) {
bdata = EVBUFFER_DATA(cf->event->input);
bsize = EVBUFFER_LENGTH(cf->event->input);
if (bsize == 0)
break;
if (bsize > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg)
bsize = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg;
log_debug("read %zu from file %d", bsize, cf->stream);
msglen = (sizeof *msg) + bsize;
msg = xrealloc(msg, msglen);
msg->stream = cf->stream;
memcpy(msg + 1, bdata, bsize);
proc_send(client_peer, MSG_READ, -1, msg, msglen);
evbuffer_drain(cf->event->input, bsize);
}
free(msg);
}
/* File read error callback. */
static void
client_read_error_callback(__unused struct bufferevent *bev,
__unused short what, void *arg)
{
struct client_file *cf = arg;
struct msg_read_done msg;
log_debug("read error file %d", cf->stream);
msg.stream = cf->stream;
msg.error = 0;
proc_send(client_peer, MSG_READ_DONE, -1, &msg, sizeof msg);
bufferevent_free(cf->event);
close(cf->fd);
RB_REMOVE(client_files, &client_files, cf);
file_free(cf);
}
/* Open read file. */
static void
client_read_open(void *data, size_t datalen)
{
struct msg_read_open *msg = data;
const char *path;
struct msg_read_done reply;
struct client_file find, *cf;
const int flags = O_NONBLOCK|O_RDONLY;
int error = 0;
if (datalen < sizeof *msg)
fatalx("bad MSG_READ_OPEN size");
if (datalen == sizeof *msg)
path = "-";
else
path = (const char *)(msg + 1);
log_debug("open read file %d %s", msg->stream, path);
find.stream = msg->stream;
if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) {
cf = file_create(NULL, msg->stream, NULL, NULL);
RB_INSERT(client_files, &client_files, cf);
} else {
error = EBADF;
goto reply;
}
if (cf->closed) {
error = EBADF;
goto reply;
}
cf->fd = -1;
if (msg->fd == -1)
cf->fd = open(path, flags);
else {
if (msg->fd != STDIN_FILENO)
errno = EBADF;
else {
cf->fd = dup(msg->fd);
close(msg->fd); /* can only be used once */
}
}
if (cf->fd == -1) {
error = errno;
goto reply;
}
cf->event = bufferevent_new(cf->fd, client_read_callback, NULL,
client_read_error_callback, cf);
bufferevent_enable(cf->event, EV_READ);
return;
reply:
reply.stream = msg->stream;
reply.error = error;
proc_send(client_peer, MSG_READ_DONE, -1, &reply, sizeof reply);
}
/* Run command in shell; used for -c. */
static __dead void
client_exec(const char *shell, const char *shellcmd)
@@ -735,6 +527,7 @@ client_signal(int sig)
struct sigaction sigact;
int status;
log_debug("%s: %s", __func__, strsignal(sig));
if (sig == SIGCHLD)
waitpid(WAIT_ANY, &status, WNOHANG);
else if (!client_attached) {
@@ -748,7 +541,8 @@ client_signal(int sig)
proc_send(client_peer, MSG_EXITING, -1, NULL, 0);
break;
case SIGTERM:
client_exitreason = CLIENT_EXIT_TERMINATED;
if (!client_suspended)
client_exitreason = CLIENT_EXIT_TERMINATED;
client_exitval = 1;
proc_send(client_peer, MSG_EXITING, -1, NULL, 0);
break;
@@ -763,18 +557,31 @@ client_signal(int sig)
if (sigaction(SIGTSTP, &sigact, NULL) != 0)
fatal("sigaction failed");
proc_send(client_peer, MSG_WAKEUP, -1, NULL, 0);
client_suspended = 0;
break;
}
}
}
/* Callback for file write error or close. */
static void
client_file_check_cb(__unused struct client *c, __unused const char *path,
__unused int error, __unused int closed, __unused struct evbuffer *buffer,
__unused void *data)
{
if (client_exitflag)
client_exit();
}
/* Callback for client read events. */
static void
client_dispatch(struct imsg *imsg, __unused void *arg)
{
if (imsg == NULL) {
client_exitreason = CLIENT_EXIT_LOST_SERVER;
client_exitval = 1;
if (!client_exitflag) {
client_exitreason = CLIENT_EXIT_LOST_SERVER;
client_exitval = 1;
}
proc_exit(client_proc);
return;
}
@@ -785,13 +592,38 @@ client_dispatch(struct imsg *imsg, __unused void *arg)
client_dispatch_wait(imsg);
}
/* Process an exit message. */
static void
client_dispatch_exit_message(char *data, size_t datalen)
{
int retval;
if (datalen < sizeof retval && datalen != 0)
fatalx("bad MSG_EXIT size");
if (datalen >= sizeof retval) {
memcpy(&retval, data, sizeof retval);
client_exitval = retval;
}
if (datalen > sizeof retval) {
datalen -= sizeof retval;
data += sizeof retval;
client_exitmessage = xmalloc(datalen);
memcpy(client_exitmessage, data, datalen);
client_exitmessage[datalen - 1] = '\0';
client_exitreason = CLIENT_EXIT_MESSAGE_PROVIDED;
}
}
/* Dispatch imsgs when in wait state (before MSG_READY). */
static void
client_dispatch_wait(struct imsg *imsg)
{
char *data;
ssize_t datalen;
int retval;
static int pledge_applied;
/*
@@ -814,12 +646,7 @@ client_dispatch_wait(struct imsg *imsg)
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;
}
client_dispatch_exit_message(data, datalen);
client_exitflag = 1;
client_exit();
break;
@@ -840,6 +667,14 @@ client_dispatch_wait(struct imsg *imsg)
client_exitval = 1;
proc_exit(client_proc);
break;
case MSG_FLAGS:
if (datalen != sizeof client_flags)
fatalx("bad MSG_FLAGS string");
memcpy(&client_flags, data, sizeof client_flags);
log_debug("new flags are %#llx",
(unsigned long long)client_flags);
break;
case MSG_SHELL:
if (datalen == 0 || data[datalen - 1] != '\0')
fatalx("bad MSG_SHELL string");
@@ -854,16 +689,20 @@ client_dispatch_wait(struct imsg *imsg)
proc_exit(client_proc);
break;
case MSG_READ_OPEN:
client_read_open(data, datalen);
file_read_open(&client_files, client_peer, imsg, 1,
!(client_flags & CLIENT_CONTROL), client_file_check_cb,
NULL);
break;
case MSG_WRITE_OPEN:
client_write_open(data, datalen);
file_write_open(&client_files, client_peer, imsg, 1,
!(client_flags & CLIENT_CONTROL), client_file_check_cb,
NULL);
break;
case MSG_WRITE:
client_write_data(data, datalen);
file_write_data(&client_files, imsg);
break;
case MSG_WRITE_CLOSE:
client_write_close(data, datalen);
file_write_close(&client_files, imsg);
break;
case MSG_OLDSTDERR:
case MSG_OLDSTDIN:
@@ -886,6 +725,14 @@ client_dispatch_attached(struct imsg *imsg)
datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
switch (imsg->hdr.type) {
case MSG_FLAGS:
if (datalen != sizeof client_flags)
fatalx("bad MSG_FLAGS string");
memcpy(&client_flags, data, sizeof client_flags);
log_debug("new flags are %#llx",
(unsigned long long)client_flags);
break;
case MSG_DETACH:
case MSG_DETACHKILL:
if (datalen == 0 || data[datalen - 1] != '\0')
@@ -910,11 +757,10 @@ client_dispatch_attached(struct imsg *imsg)
proc_send(client_peer, MSG_EXITING, -1, NULL, 0);
break;
case MSG_EXIT:
if (datalen != 0 && datalen != sizeof (int))
fatalx("bad MSG_EXIT size");
client_dispatch_exit_message(data, datalen);
if (client_exitreason == CLIENT_EXIT_NONE)
client_exitreason = CLIENT_EXIT_EXITED;
proc_send(client_peer, MSG_EXITING, -1, NULL, 0);
client_exitreason = CLIENT_EXIT_EXITED;
break;
case MSG_EXITED:
if (datalen != 0)
@@ -940,6 +786,7 @@ client_dispatch_attached(struct imsg *imsg)
sigact.sa_handler = SIG_DFL;
if (sigaction(SIGTSTP, &sigact, NULL) != 0)
fatal("sigaction failed");
client_suspended = 1;
kill(getpid(), SIGTSTP);
break;
case MSG_LOCK:

View File

@@ -37,8 +37,9 @@ const struct cmd_entry cmd_attach_session_entry = {
.name = "attach-session",
.alias = "attach",
.args = { "c:dErt:x", 0, 0 },
.usage = "[-dErx] [-c working-directory] " CMD_TARGET_SESSION_USAGE,
.args = { "c:dEf:rt:x", 0, 0, NULL },
.usage = "[-dErx] [-c working-directory] [-f flags] "
CMD_TARGET_SESSION_USAGE,
/* -t is special */
@@ -48,16 +49,17 @@ const struct cmd_entry cmd_attach_session_entry = {
enum cmd_retval
cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
int xflag, int rflag, const char *cflag, int Eflag)
int xflag, int rflag, const char *cflag, int Eflag, const char *fflag)
{
struct cmd_find_state *current = &item->shared->current;
struct cmd_find_state *current = cmdq_get_current(item);
struct cmd_find_state target;
enum cmd_find_type type;
int flags;
struct client *c = item->client, *c_loop;
struct client *c = cmdq_get_client(item), *c_loop;
struct session *s;
struct winlink *wl;
struct window_pane *wp;
char *cause;
char *cwd, *cause;
enum msgtype msgtype;
if (RB_EMPTY(&sessions)) {
@@ -80,11 +82,11 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
type = CMD_FIND_SESSION;
flags = CMD_FIND_PREFER_UNATTACHED;
}
if (cmd_find_target(&item->target, item, tflag, type, flags) != 0)
if (cmd_find_target(&target, item, tflag, type, flags) != 0)
return (CMD_RETURN_ERROR);
s = item->target.s;
wl = item->target.wl;
wp = item->target.wp;
s = target.s;
wl = target.wl;
wp = target.wp;
if (wl != NULL) {
if (wp != NULL)
@@ -97,9 +99,14 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
}
if (cflag != NULL) {
cwd = format_single(item, cflag, c, s, wl, wp);
free((void *)s->cwd);
s->cwd = format_single(item, cflag, c, s, wl, wp);
s->cwd = cwd;
}
if (fflag)
server_client_set_flags(c, fflag);
if (rflag)
c->flags |= (CLIENT_READONLY|CLIENT_IGNORESIZE);
c->last_session = c->session;
if (c->session != NULL) {
@@ -117,25 +124,15 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
if (!Eflag)
environ_update(s->options, c->environ, s->environ);
c->session = s;
if (~item->shared->flags & CMDQ_SHARED_REPEAT)
server_client_set_session(c, s);
if (~cmdq_get_flags(item) & CMDQ_STATE_REPEAT)
server_client_set_key_table(c, NULL);
tty_update_client_offset(c);
status_timer_start(c);
notify_client("client-session-changed", c);
session_update_activity(s, NULL);
gettimeofday(&s->last_attached_time, NULL);
server_redraw_client(c);
s->curw->flags &= ~WINLINK_ALERTFLAGS;
s->curw->window->latest = c;
} else {
if (server_client_open(c, &cause) != 0) {
cmdq_error(item, "open terminal failed: %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
if (rflag)
c->flags |= CLIENT_READONLY;
if (dflag || xflag) {
if (xflag)
@@ -151,25 +148,14 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
if (!Eflag)
environ_update(s->options, c->environ, s->environ);
c->session = s;
server_client_set_session(c, s);
server_client_set_key_table(c, NULL);
tty_update_client_offset(c);
status_timer_start(c);
notify_client("client-session-changed", c);
session_update_activity(s, NULL);
gettimeofday(&s->last_attached_time, NULL);
server_redraw_client(c);
s->curw->flags &= ~WINLINK_ALERTFLAGS;
s->curw->window->latest = c;
if (~c->flags & CLIENT_CONTROL)
proc_send(c->peer, MSG_READY, -1, NULL, 0);
notify_client("client-attached", c);
c->flags |= CLIENT_ATTACHED;
}
recalculate_sizes();
alerts_check_session(s);
server_update_socket();
return (CMD_RETURN_NORMAL);
}
@@ -177,9 +163,9 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
static enum cmd_retval
cmd_attach_session_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct args *args = cmd_get_args(self);
return (cmd_attach_session(item, args_get(args, 't'),
args_has(args, 'd'), args_has(args, 'x'), args_has(args, 'r'),
args_get(args, 'c'), args_has(args, 'E')));
args_get(args, 'c'), args_has(args, 'E'), args_get(args, 'f')));
}

View File

@@ -27,33 +27,44 @@
* Bind a key to a command.
*/
static enum cmd_retval cmd_bind_key_exec(struct cmd *, struct cmdq_item *);
static enum args_parse_type cmd_bind_key_args_parse(struct args *, u_int,
char **);
static enum cmd_retval cmd_bind_key_exec(struct cmd *,
struct cmdq_item *);
const struct cmd_entry cmd_bind_key_entry = {
.name = "bind-key",
.alias = "bind",
.args = { "nrN:T:", 2, -1 },
.args = { "nrN:T:", 1, -1, cmd_bind_key_args_parse },
.usage = "[-nr] [-T key-table] [-N note] key "
"command [arguments]",
"[command [arguments]]",
.flags = CMD_AFTERHOOK,
.exec = cmd_bind_key_exec
};
static enum args_parse_type
cmd_bind_key_args_parse(__unused struct args *args, __unused u_int idx,
__unused char **cause)
{
return (ARGS_PARSE_COMMANDS_OR_STRING);
}
static enum cmd_retval
cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct args *args = cmd_get_args(self);
key_code key;
const char *tablename, *note;
const char *tablename, *note = args_get(args, 'N');
struct cmd_parse_result *pr;
char **argv = args->argv;
int argc = args->argc, repeat;
int repeat;
struct args_value *value;
u_int count = args_count(args);
key = key_string_lookup_string(argv[0]);
key = key_string_lookup_string(args_string(args, 0));
if (key == KEYC_NONE || key == KEYC_UNKNOWN) {
cmdq_error(item, "unknown key: %s", argv[0]);
cmdq_error(item, "unknown key: %s", args_string(args, 0));
return (CMD_RETURN_ERROR);
}
@@ -65,14 +76,25 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item)
tablename = "prefix";
repeat = args_has(args, 'r');
if (argc == 2)
pr = cmd_parse_from_string(argv[1], NULL);
else
pr = cmd_parse_from_arguments(argc - 1, argv + 1, NULL);
if (count == 1) {
key_bindings_add(tablename, key, note, repeat, NULL);
return (CMD_RETURN_NORMAL);
}
value = args_value(args, 1);
if (count == 2 && value->type == ARGS_COMMANDS) {
key_bindings_add(tablename, key, note, repeat, value->cmdlist);
value->cmdlist->references++;
return (CMD_RETURN_NORMAL);
}
if (count == 2)
pr = cmd_parse_from_string(args_string(args, 1), NULL);
else {
pr = cmd_parse_from_arguments(args_values(args) + 1, count - 1,
NULL);
}
switch (pr->status) {
case CMD_PARSE_EMPTY:
cmdq_error(item, "empty command");
return (CMD_RETURN_ERROR);
case CMD_PARSE_ERROR:
cmdq_error(item, "%s", pr->error);
free(pr->error);
@@ -80,7 +102,6 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item)
case CMD_PARSE_SUCCESS:
break;
}
note = args_get(args, 'N');
key_bindings_add(tablename, key, note, repeat, pr->cmdlist);
return (CMD_RETURN_NORMAL);
}

View File

@@ -34,8 +34,8 @@ const struct cmd_entry cmd_break_pane_entry = {
.name = "break-pane",
.alias = "breakp",
.args = { "dPF:n:s:t:", 0, 0 },
.usage = "[-dP] [-F format] [-n window-name] [-s src-pane] "
.args = { "abdPF:n:s:t:", 0, 0, NULL },
.usage = "[-abdP] [-F format] [-n window-name] [-s src-pane] "
"[-t dst-window]",
.source = { 's', CMD_FIND_PANE, 0 },
@@ -48,31 +48,52 @@ const struct cmd_entry cmd_break_pane_entry = {
static enum cmd_retval
cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct cmd_find_state *current = &item->shared->current;
struct client *c = cmd_find_client(item, NULL, 1);
struct winlink *wl = item->source.wl;
struct session *src_s = item->source.s;
struct session *dst_s = item->target.s;
struct window_pane *wp = item->source.wp;
struct args *args = cmd_get_args(self);
struct cmd_find_state *current = cmdq_get_current(item);
struct cmd_find_state *target = cmdq_get_target(item);
struct cmd_find_state *source = cmdq_get_source(item);
struct client *tc = cmdq_get_target_client(item);
struct winlink *wl = source->wl;
struct session *src_s = source->s;
struct session *dst_s = target->s;
struct window_pane *wp = source->wp;
struct window *w = wl->window;
char *name, *cause;
int idx = item->target.idx;
char *name, *cause, *cp;
int idx = target->idx, before;
const char *template;
char *cp;
if (idx != -1 && winlink_find_by_index(&dst_s->windows, idx) != NULL) {
cmdq_error(item, "index %d already in use", idx);
return (CMD_RETURN_ERROR);
}
if (window_count_panes(w) == 1) {
cmdq_error(item, "can't break with only one pane");
return (CMD_RETURN_ERROR);
before = args_has(args, 'b');
if (args_has(args, 'a') || before) {
if (target->wl != NULL)
idx = winlink_shuffle_up(dst_s, target->wl, before);
else
idx = winlink_shuffle_up(dst_s, dst_s->curw, before);
if (idx == -1)
return (CMD_RETURN_ERROR);
}
server_unzoom_window(w);
if (window_count_panes(w) == 1) {
if (server_link_window(src_s, wl, dst_s, idx, 0,
!args_has(args, 'd'), &cause) != 0) {
cmdq_error(item, "%s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'n')) {
window_set_name(w, args_get(args, 'n'));
options_set_number(w->options, "automatic-rename", 0);
}
server_unlink_window(src_s, wl);
return (CMD_RETURN_NORMAL);
}
if (idx != -1 && winlink_find_by_index(&dst_s->windows, idx) != NULL) {
cmdq_error(item, "index in use: %d", idx);
return (CMD_RETURN_ERROR);
}
TAILQ_REMOVE(&w->panes, wp, entry);
server_client_remove_pane(wp);
window_lost_pane(w, wp);
layout_close_pane(wp);
@@ -81,7 +102,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
wp->flags |= PANE_STYLECHANGED;
TAILQ_INSERT_HEAD(&w->panes, wp, entry);
w->active = wp;
w->latest = c;
w->latest = tc;
if (!args_has(args, 'n')) {
name = default_window_name(w);
@@ -98,7 +119,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
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')) {
if (!args_has(args, 'd')) {
session_select(dst_s, wl->idx);
cmd_find_from_session(current, dst_s, 0);
}
@@ -113,7 +134,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'P')) {
if ((template = args_get(args, 'F')) == NULL)
template = BREAK_PANE_TEMPLATE;
cp = format_single(item, template, c, dst_s, wl, wp);
cp = format_single(item, template, tc, dst_s, wl, wp);
cmdq_print(item, "%s", cp);
free(cp);
}

View File

@@ -39,7 +39,7 @@ const struct cmd_entry cmd_capture_pane_entry = {
.name = "capture-pane",
.alias = "capturep",
.args = { "ab:CeE:JNpPqS:t:", 0, 0 },
.args = { "ab:CeE:JNpPqS:t:", 0, 0, NULL },
.usage = "[-aCeJNpPq] " CMD_BUFFER_USAGE " [-E end-line] "
"[-S start-line] " CMD_TARGET_PANE_USAGE,
@@ -53,7 +53,7 @@ const struct cmd_entry cmd_clear_history_entry = {
.name = "clear-history",
.alias = "clearhist",
.args = { "t:", 0, 0 },
.args = { "t:", 0, 0, NULL },
.usage = CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 },
@@ -80,7 +80,7 @@ cmd_capture_pane_pending(struct args *args, struct window_pane *wp,
size_t linelen;
u_int i;
pending = input_pending(wp);
pending = input_pending(wp->ictx);
if (pending == NULL)
return (xstrdup(""));
@@ -118,7 +118,7 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
sx = screen_size_x(&wp->base);
if (args_has(args, 'a')) {
gd = wp->saved_grid;
gd = wp->base.saved_grid;
if (gd == NULL) {
if (!args_has(args, 'q')) {
cmdq_error(item, "no alternate screen");
@@ -192,14 +192,14 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
static enum cmd_retval
cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c = item->client;
struct window_pane *wp = item->target.wp;
struct args *args = cmd_get_args(self);
struct client *c = cmdq_get_client(item);
struct window_pane *wp = cmdq_get_target(item)->wp;
char *buf, *cause;
const char *bufname;
size_t len;
if (self->entry == &cmd_clear_history_entry) {
if (cmd_get_entry(self) == &cmd_clear_history_entry) {
window_pane_reset_mode_all(wp);
grid_clear_history(wp->base.grid);
return (CMD_RETURN_NORMAL);
@@ -214,15 +214,20 @@ cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR);
if (args_has(args, 'p')) {
if (!file_can_print(c)) {
cmdq_error(item, "can't write output to client");
free(buf);
return (CMD_RETURN_ERROR);
}
file_print_buffer(c, buf, len);
if (args_has(args, 'P') && len > 0)
if (len > 0 && buf[len - 1] == '\n')
len--;
if (c->flags & CLIENT_CONTROL)
control_write(c, "%.*s", (int)len, buf);
else {
if (!file_can_print(c)) {
cmdq_error(item, "can't write to client");
free(buf);
return (CMD_RETURN_ERROR);
}
file_print_buffer(c, buf, len);
file_print(c, "\n");
free(buf);
free(buf);
}
} else {
bufname = NULL;
if (args_has(args, 'b'))

View File

@@ -24,15 +24,18 @@
* Enter a mode.
*/
static enum cmd_retval cmd_choose_tree_exec(struct cmd *, struct cmdq_item *);
static enum args_parse_type cmd_choose_tree_args_parse(struct args *args,
u_int idx, char **cause);
static enum cmd_retval cmd_choose_tree_exec(struct cmd *,
struct cmdq_item *);
const struct cmd_entry cmd_choose_tree_entry = {
.name = "choose-tree",
.alias = NULL,
.args = { "F:Gf:NO:rst:wZ", 0, 1 },
.usage = "[-GNrswZ] [-F format] [-f filter] [-O sort-order] "
CMD_TARGET_PANE_USAGE " [template]",
.args = { "F:f:GK:NO:rst:wZ", 0, 1, cmd_choose_tree_args_parse },
.usage = "[-GNrswZ] [-F format] [-f filter] [-K key-format] "
"[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -44,9 +47,9 @@ const struct cmd_entry cmd_choose_client_entry = {
.name = "choose-client",
.alias = NULL,
.args = { "F:f:NO:rt:Z", 0, 1 },
.usage = "[-NrZ] [-F format] [-f filter] [-O sort-order] "
CMD_TARGET_PANE_USAGE " [template]",
.args = { "F:f:K:NO:rt:Z", 0, 1, cmd_choose_tree_args_parse },
.usage = "[-NrZ] [-F format] [-f filter] [-K key-format] "
"[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -58,9 +61,9 @@ const struct cmd_entry cmd_choose_buffer_entry = {
.name = "choose-buffer",
.alias = NULL,
.args = { "F:f:NO:rt:Z", 0, 1 },
.usage = "[-NrZ] [-F format] [-f filter] [-O sort-order] "
CMD_TARGET_PANE_USAGE " [template]",
.args = { "F:f:K:NO:rt:Z", 0, 1, cmd_choose_tree_args_parse },
.usage = "[-NrZ] [-F format] [-f filter] [-K key-format] "
"[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -68,24 +71,47 @@ const struct cmd_entry cmd_choose_buffer_entry = {
.exec = cmd_choose_tree_exec
};
const struct cmd_entry cmd_customize_mode_entry = {
.name = "customize-mode",
.alias = NULL,
.args = { "F:f:Nt:Z", 0, 0, NULL },
.usage = "[-NZ] [-F format] [-f filter] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 },
.flags = 0,
.exec = cmd_choose_tree_exec
};
static enum args_parse_type
cmd_choose_tree_args_parse(__unused struct args *args, __unused u_int idx,
__unused char **cause)
{
return (ARGS_PARSE_COMMANDS_OR_STRING);
}
static enum cmd_retval
cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct window_pane *wp = item->target.wp;
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct window_pane *wp = target->wp;
const struct window_mode *mode;
if (self->entry == &cmd_choose_buffer_entry) {
if (cmd_get_entry(self) == &cmd_choose_buffer_entry) {
if (paste_get_top(NULL) == NULL)
return (CMD_RETURN_NORMAL);
mode = &window_buffer_mode;
} else if (self->entry == &cmd_choose_client_entry) {
} else if (cmd_get_entry(self) == &cmd_choose_client_entry) {
if (server_client_how_many() == 0)
return (CMD_RETURN_NORMAL);
mode = &window_client_mode;
} else
} else if (cmd_get_entry(self) == &cmd_customize_mode_entry)
mode = &window_customize_mode;
else
mode = &window_tree_mode;
window_pane_set_mode(wp, mode, &item->target, args);
window_pane_set_mode(wp, NULL, mode, target, args);
return (CMD_RETURN_NORMAL);
}

View File

@@ -29,8 +29,10 @@
* Prompt for command in client.
*/
static enum cmd_retval cmd_command_prompt_exec(struct cmd *,
struct cmdq_item *);
static enum args_parse_type cmd_command_prompt_args_parse(struct args *,
u_int, char **);
static enum cmd_retval cmd_command_prompt_exec(struct cmd *,
struct cmdq_item *);
static int cmd_command_prompt_callback(struct client *, void *,
const char *, int);
@@ -40,81 +42,111 @@ const struct cmd_entry cmd_command_prompt_entry = {
.name = "command-prompt",
.alias = NULL,
.args = { "1kiI:Np:t:", 0, 1 },
.usage = "[-1kiN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " "
"[template]",
.args = { "1bFkiI:Np:t:T:", 0, 1, cmd_command_prompt_args_parse },
.usage = "[-1bFkiN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE
" [-T type] [template]",
.flags = 0,
.flags = CMD_CLIENT_TFLAG,
.exec = cmd_command_prompt_exec
};
struct cmd_command_prompt_cdata {
int flags;
char *inputs;
char *next_input;
char *prompts;
char *next_prompt;
char *template;
int idx;
struct cmd_command_prompt_prompt {
char *input;
char *prompt;
};
struct cmd_command_prompt_cdata {
struct cmdq_item *item;
struct args_command_state *state;
int flags;
enum prompt_type prompt_type;
struct cmd_command_prompt_prompt *prompts;
u_int count;
u_int current;
int argc;
char **argv;
};
static enum args_parse_type
cmd_command_prompt_args_parse(__unused struct args *args, __unused u_int idx,
__unused char **cause)
{
return (ARGS_PARSE_COMMANDS_OR_STRING);
}
static enum cmd_retval
cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
const char *inputs, *prompts;
struct args *args = cmd_get_args(self);
struct client *tc = cmdq_get_target_client(item);
struct cmd_find_state *target = cmdq_get_target(item);
const char *type, *s, *input;
struct cmd_command_prompt_cdata *cdata;
struct client *c;
char *prompt, *ptr, *input = NULL;
size_t n;
char *tmp, *prompts, *prompt, *next_prompt;
char *inputs = NULL, *next_input;
u_int count = args_count(args);
int wait = !args_has(args, 'b'), space = 1;
if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL)
return (CMD_RETURN_ERROR);
if (c->prompt_string != NULL)
if (tc->prompt_string != NULL)
return (CMD_RETURN_NORMAL);
if (args_has(args, 'i'))
wait = 0;
cdata = xcalloc(1, sizeof *cdata);
if (wait)
cdata->item = item;
cdata->state = args_make_commands_prepare(self, item, 0, "%1", wait,
args_has(args, 'F'));
cdata->inputs = NULL;
cdata->next_input = NULL;
cdata->prompts = NULL;
cdata->next_prompt = NULL;
cdata->template = NULL;
cdata->idx = 1;
if (args->argc != 0)
cdata->template = xstrdup(args->argv[0]);
else
cdata->template = xstrdup("%1");
if ((prompts = args_get(args, 'p')) != NULL)
cdata->prompts = xstrdup(prompts);
else if (args->argc != 0) {
n = strcspn(cdata->template, " ,");
xasprintf(&cdata->prompts, "(%.*s) ", (int) n, cdata->template);
if ((s = args_get(args, 'p')) == NULL) {
if (count != 0) {
tmp = args_make_commands_get_command(cdata->state);
xasprintf(&prompts, "(%s)", tmp);
free(tmp);
} else {
prompts = xstrdup(":");
space = 0;
}
next_prompt = prompts;
} else
cdata->prompts = xstrdup(":");
/* Get first prompt. */
cdata->next_prompt = cdata->prompts;
ptr = strsep(&cdata->next_prompt, ",");
if (prompts == NULL)
prompt = xstrdup(ptr);
next_prompt = prompts = xstrdup (s);
if ((s = args_get(args, 'I')) != NULL)
next_input = inputs = xstrdup(s);
else
xasprintf(&prompt, "%s ", ptr);
next_input = NULL;
while ((prompt = strsep(&next_prompt, ",")) != NULL) {
cdata->prompts = xreallocarray(cdata->prompts, cdata->count + 1,
sizeof *cdata->prompts);
if (!space)
tmp = xstrdup(prompt);
else
xasprintf(&tmp, "%s ", prompt);
cdata->prompts[cdata->count].prompt = tmp;
/* Get initial prompt input. */
if ((inputs = args_get(args, 'I')) != NULL) {
cdata->inputs = xstrdup(inputs);
cdata->next_input = cdata->inputs;
input = strsep(&cdata->next_input, ",");
if (next_input != NULL) {
input = strsep(&next_input, ",");
if (input == NULL)
input = "";
} else
input = "";
cdata->prompts[cdata->count].input = xstrdup(input);
cdata->count++;
}
free(inputs);
free(prompts);
if ((type = args_get(args, 'T')) != NULL) {
cdata->prompt_type = status_prompt_type(type);
if (cdata->prompt_type == PROMPT_TYPE_INVALID) {
cmdq_error(item, "unknown type: %s", type);
return (CMD_RETURN_ERROR);
}
} else
cdata->prompt_type = PROMPT_TYPE_COMMAND;
if (args_has(args, '1'))
cdata->flags |= PROMPT_SINGLE;
@@ -124,69 +156,68 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
cdata->flags |= PROMPT_INCREMENTAL;
else if (args_has(args, 'k'))
cdata->flags |= PROMPT_KEY;
status_prompt_set(c, prompt, input, cmd_command_prompt_callback,
cmd_command_prompt_free, cdata, cdata->flags);
free(prompt);
status_prompt_set(tc, target, cdata->prompts[0].prompt,
cdata->prompts[0].input, cmd_command_prompt_callback,
cmd_command_prompt_free, cdata, cdata->flags, cdata->prompt_type);
return (CMD_RETURN_NORMAL);
if (!wait)
return (CMD_RETURN_NORMAL);
return (CMD_RETURN_WAIT);
}
static int
cmd_command_prompt_callback(struct client *c, void *data, const char *s,
int done)
{
struct cmd_command_prompt_cdata *cdata = data;
struct cmdq_item *new_item;
char *new_template, *prompt, *ptr;
char *input = NULL;
struct cmd_parse_result *pr;
struct cmd_command_prompt_cdata *cdata = data;
char *error;
struct cmdq_item *item = cdata->item, *new_item;
struct cmd_list *cmdlist;
struct cmd_command_prompt_prompt *prompt;
int argc = 0;
char **argv = NULL;
if (s == NULL)
return (0);
if (done && (cdata->flags & PROMPT_INCREMENTAL))
return (0);
new_template = cmd_template_replace(cdata->template, s, cdata->idx);
goto out;
if (done) {
free(cdata->template);
cdata->template = new_template;
if (cdata->flags & PROMPT_INCREMENTAL)
goto out;
cmd_append_argv(&cdata->argc, &cdata->argv, s);
if (++cdata->current != cdata->count) {
prompt = &cdata->prompts[cdata->current];
status_prompt_update(c, prompt->prompt, prompt->input);
return (1);
}
}
/*
* Check if there are more prompts; if so, get its respective input
* and update the prompt data.
*/
if (done && (ptr = strsep(&cdata->next_prompt, ",")) != NULL) {
xasprintf(&prompt, "%s ", ptr);
input = strsep(&cdata->next_input, ",");
status_prompt_update(c, prompt, input);
free(prompt);
cdata->idx++;
return (1);
argc = cdata->argc;
argv = cmd_copy_argv(cdata->argc, cdata->argv);
cmd_append_argv(&argc, &argv, s);
if (done) {
cdata->argc = argc;
cdata->argv = cmd_copy_argv(argc, argv);
}
pr = cmd_parse_from_string(new_template, NULL);
switch (pr->status) {
case CMD_PARSE_EMPTY:
new_item = NULL;
break;
case CMD_PARSE_ERROR:
new_item = cmdq_get_error(pr->error);
free(pr->error);
cmdlist = args_make_commands(cdata->state, argc, argv, &error);
if (cmdlist == NULL) {
cmdq_append(c, cmdq_get_error(error));
free(error);
} else if (item == NULL) {
new_item = cmdq_get_command(cmdlist, NULL);
cmdq_append(c, new_item);
break;
case CMD_PARSE_SUCCESS:
new_item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
cmd_list_free(pr->cmdlist);
cmdq_append(c, new_item);
break;
} else {
new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
cmdq_insert_after(item, new_item);
}
cmd_free_argv(argc, argv);
if (!done)
free(new_template);
if (c->prompt_inputcb != cmd_command_prompt_callback)
return (1);
out:
if (item != NULL)
cmdq_continue(item);
return (0);
}
@@ -194,9 +225,14 @@ static void
cmd_command_prompt_free(void *data)
{
struct cmd_command_prompt_cdata *cdata = data;
u_int i;
free(cdata->inputs);
for (i = 0; i < cdata->count; i++) {
free(cdata->prompts[i].prompt);
free(cdata->prompts[i].input);
}
free(cdata->prompts);
free(cdata->template);
cmd_free_argv(cdata->argc, cdata->argv);
args_make_commands_free(cdata->state);
free(cdata);
}

View File

@@ -28,8 +28,10 @@
* Asks for confirmation before executing a command.
*/
static enum cmd_retval cmd_confirm_before_exec(struct cmd *,
struct cmdq_item *);
static enum args_parse_type cmd_confirm_before_args_parse(struct args *,
u_int, char **);
static enum cmd_retval cmd_confirm_before_exec(struct cmd *,
struct cmdq_item *);
static int cmd_confirm_before_callback(struct client *, void *,
const char *, int);
@@ -39,47 +41,59 @@ 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",
.args = { "bp:t:", 1, 1, cmd_confirm_before_args_parse },
.usage = "[-b] [-p prompt] " CMD_TARGET_CLIENT_USAGE " command",
.flags = 0,
.flags = CMD_CLIENT_TFLAG,
.exec = cmd_confirm_before_exec
};
struct cmd_confirm_before_data {
char *cmd;
struct cmdq_item *item;
struct cmd_list *cmdlist;
};
static enum args_parse_type
cmd_confirm_before_args_parse(__unused struct args *args, __unused u_int idx,
__unused char **cause)
{
return (ARGS_PARSE_COMMANDS_OR_STRING);
}
static enum cmd_retval
cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct args *args = cmd_get_args(self);
struct cmd_confirm_before_data *cdata;
struct client *c;
char *cmd, *copy, *new_prompt, *ptr;
const char *prompt;
struct client *tc = cmdq_get_target_client(item);
struct cmd_find_state *target = cmdq_get_target(item);
char *new_prompt;
const char *prompt, *cmd;
int wait = !args_has(args, 'b');
if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL)
cdata = xcalloc(1, sizeof *cdata);
cdata->cmdlist = args_make_commands_now(self, item, 0, 0);
if (cdata->cmdlist == NULL)
return (CMD_RETURN_ERROR);
if (wait)
cdata->item = item;
if ((prompt = args_get(args, 'p')) != NULL)
xasprintf(&new_prompt, "%s ", prompt);
else {
ptr = copy = xstrdup(args->argv[0]);
cmd = strsep(&ptr, " \t");
cmd = cmd_get_entry(cmd_list_first(cdata->cmdlist))->name;
xasprintf(&new_prompt, "Confirm '%s'? (y/n) ", cmd);
free(copy);
}
cdata = xmalloc(sizeof *cdata);
cdata->cmd = xstrdup(args->argv[0]);
status_prompt_set(c, new_prompt, NULL,
status_prompt_set(tc, target, new_prompt, NULL,
cmd_confirm_before_callback, cmd_confirm_before_free, cdata,
PROMPT_SINGLE);
PROMPT_SINGLE, PROMPT_TYPE_COMMAND);
free(new_prompt);
return (CMD_RETURN_NORMAL);
if (!wait)
return (CMD_RETURN_NORMAL);
return (CMD_RETURN_WAIT);
}
static int
@@ -87,34 +101,34 @@ cmd_confirm_before_callback(struct client *c, void *data, const char *s,
__unused int done)
{
struct cmd_confirm_before_data *cdata = data;
struct cmdq_item *new_item;
struct cmd_parse_result *pr;
struct cmdq_item *item = cdata->item, *new_item;
int retcode = 1;
if (c->flags & CLIENT_DEAD)
return (0);
goto out;
if (s == NULL || *s == '\0')
return (0);
goto out;
if (tolower((u_char)s[0]) != 'y' || s[1] != '\0')
return (0);
goto out;
retcode = 0;
pr = cmd_parse_from_string(cdata->cmd, NULL);
switch (pr->status) {
case CMD_PARSE_EMPTY:
new_item = NULL;
break;
case CMD_PARSE_ERROR:
new_item = cmdq_get_error(pr->error);
free(pr->error);
if (item == NULL) {
new_item = cmdq_get_command(cdata->cmdlist, NULL);
cmdq_append(c, new_item);
break;
case CMD_PARSE_SUCCESS:
new_item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
cmd_list_free(pr->cmdlist);
cmdq_append(c, new_item);
break;
} else {
new_item = cmdq_get_command(cdata->cmdlist,
cmdq_get_state(item));
cmdq_insert_after(item, new_item);
}
out:
if (item != NULL) {
if (cmdq_get_client(item) != NULL &&
cmdq_get_client(item)->session == NULL)
cmdq_get_client(item)->retval = retcode;
cmdq_continue(item);
}
return (0);
}
@@ -123,6 +137,6 @@ cmd_confirm_before_free(void *data)
{
struct cmd_confirm_before_data *cdata = data;
free(cdata->cmd);
cmd_list_free(cdata->cmdlist);
free(cdata);
}

View File

@@ -30,9 +30,10 @@ const struct cmd_entry cmd_copy_mode_entry = {
.name = "copy-mode",
.alias = NULL,
.args = { "Met:u", 0, 0 },
.usage = "[-Mu] " CMD_TARGET_PANE_USAGE,
.args = { "eHMs:t:uq", 0, 0, NULL },
.usage = "[-eHMuq] [-s src-pane] " CMD_TARGET_PANE_USAGE,
.source = { 's', CMD_FIND_PANE, 0 },
.target = { 't', CMD_FIND_PANE, 0 },
.flags = CMD_AFTERHOOK,
@@ -43,7 +44,7 @@ const struct cmd_entry cmd_clock_mode_entry = {
.name = "clock-mode",
.alias = NULL,
.args = { "t:", 0, 0 },
.args = { "t:", 0, 0, NULL },
.usage = CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 },
@@ -55,29 +56,40 @@ const struct cmd_entry cmd_clock_mode_entry = {
static enum cmd_retval
cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct cmdq_shared *shared = item->shared;
struct client *c = item->client;
struct args *args = cmd_get_args(self);
struct key_event *event = cmdq_get_event(item);
struct cmd_find_state *source = cmdq_get_source(item);
struct cmd_find_state *target = cmdq_get_target(item);
struct client *c = cmdq_get_client(item);
struct session *s;
struct window_pane *wp = item->target.wp;
struct window_pane *wp = target->wp, *swp;
if (args_has(args, 'q')) {
window_pane_reset_mode_all(wp);
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'M')) {
if ((wp = cmd_mouse_pane(&shared->mouse, &s, NULL)) == NULL)
if ((wp = cmd_mouse_pane(&event->m, &s, NULL)) == NULL)
return (CMD_RETURN_NORMAL);
if (c == NULL || c->session != s)
return (CMD_RETURN_NORMAL);
}
if (self->entry == &cmd_clock_mode_entry) {
window_pane_set_mode(wp, &window_clock_mode, NULL, NULL);
if (cmd_get_entry(self) == &cmd_clock_mode_entry) {
window_pane_set_mode(wp, NULL, &window_clock_mode, NULL, NULL);
return (CMD_RETURN_NORMAL);
}
if (!window_pane_set_mode(wp, &window_copy_mode, NULL, args)) {
if (args_has(args, 's'))
swp = source->wp;
else
swp = wp;
if (!window_pane_set_mode(wp, swp, &window_copy_mode, NULL, args)) {
if (args_has(args, 'M'))
window_copy_start_drag(c, &shared->mouse);
window_copy_start_drag(c, &event->m);
}
if (args_has(self->args, 'u'))
if (args_has(args, 'u'))
window_copy_pageup(wp, 0);
return (CMD_RETURN_NORMAL);

View File

@@ -33,13 +33,13 @@ const struct cmd_entry cmd_detach_client_entry = {
.name = "detach-client",
.alias = "detach",
.args = { "aE:s:t:P", 0, 0 },
.args = { "aE:s:t:P", 0, 0, NULL },
.usage = "[-aP] [-E shell-command] "
"[-s target-session] " CMD_TARGET_CLIENT_USAGE,
.source = { 's', CMD_FIND_SESSION, CMD_FIND_CANFAIL },
.flags = CMD_READONLY,
.flags = CMD_READONLY|CMD_CLIENT_TFLAG,
.exec = cmd_detach_client_exec
};
@@ -47,27 +47,25 @@ const struct cmd_entry cmd_suspend_client_entry = {
.name = "suspend-client",
.alias = "suspendc",
.args = { "t:", 0, 0 },
.args = { "t:", 0, 0, NULL },
.usage = CMD_TARGET_CLIENT_USAGE,
.flags = 0,
.flags = CMD_CLIENT_TFLAG,
.exec = cmd_detach_client_exec
};
static enum cmd_retval
cmd_detach_client_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c, *cloop;
struct session *s;
enum msgtype msgtype;
const char *cmd = args_get(args, 'E');
struct args *args = cmd_get_args(self);
struct cmd_find_state *source = cmdq_get_source(item);
struct client *tc = cmdq_get_target_client(item), *loop;
struct session *s;
enum msgtype msgtype;
const char *cmd = args_get(args, 'E');
if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL)
return (CMD_RETURN_ERROR);
if (self->entry == &cmd_suspend_client_entry) {
server_client_suspend(c);
if (cmd_get_entry(self) == &cmd_suspend_client_entry) {
server_client_suspend(tc);
return (CMD_RETURN_NORMAL);
}
@@ -77,35 +75,35 @@ cmd_detach_client_exec(struct cmd *self, struct cmdq_item *item)
msgtype = MSG_DETACH;
if (args_has(args, 's')) {
s = item->source.s;
s = source->s;
if (s == NULL)
return (CMD_RETURN_NORMAL);
TAILQ_FOREACH(cloop, &clients, entry) {
if (cloop->session == s) {
TAILQ_FOREACH(loop, &clients, entry) {
if (loop->session == s) {
if (cmd != NULL)
server_client_exec(cloop, cmd);
server_client_exec(loop, cmd);
else
server_client_detach(cloop, msgtype);
server_client_detach(loop, msgtype);
}
}
return (CMD_RETURN_STOP);
}
if (args_has(args, 'a')) {
TAILQ_FOREACH(cloop, &clients, entry) {
if (cloop->session != NULL && cloop != c) {
TAILQ_FOREACH(loop, &clients, entry) {
if (loop->session != NULL && loop != tc) {
if (cmd != NULL)
server_client_exec(cloop, cmd);
server_client_exec(loop, cmd);
else
server_client_detach(cloop, msgtype);
server_client_detach(loop, msgtype);
}
}
return (CMD_RETURN_NORMAL);
}
if (cmd != NULL)
server_client_exec(c, cmd);
server_client_exec(tc, cmd);
else
server_client_detach(c, msgtype);
server_client_detach(tc, msgtype);
return (CMD_RETURN_STOP);
}

View File

@@ -27,73 +27,297 @@
* Display a menu on a client.
*/
static enum cmd_retval cmd_display_menu_exec(struct cmd *,
struct cmdq_item *);
static enum args_parse_type cmd_display_menu_args_parse(struct args *,
u_int, char **);
static enum cmd_retval cmd_display_menu_exec(struct cmd *,
struct cmdq_item *);
static enum cmd_retval cmd_display_popup_exec(struct cmd *,
struct cmdq_item *);
const struct cmd_entry cmd_display_menu_entry = {
.name = "display-menu",
.alias = "menu",
.args = { "c:t:T:x:y:", 1, -1 },
.usage = "[-c target-client] " CMD_TARGET_PANE_USAGE " [-T title] "
.args = { "c:t:OT:x:y:", 1, -1, cmd_display_menu_args_parse },
.usage = "[-O] [-c target-client] " CMD_TARGET_PANE_USAGE " [-T title] "
"[-x position] [-y position] name key command ...",
.target = { 't', CMD_FIND_PANE, 0 },
.flags = CMD_AFTERHOOK,
.flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG,
.exec = cmd_display_menu_exec
};
const struct cmd_entry cmd_display_popup_entry = {
.name = "display-popup",
.alias = "popup",
.args = { "BCc:d:Eh:t:w:x:y:", 0, -1, NULL },
.usage = "[-BCE] [-c target-client] [-d start-directory] [-h height] "
CMD_TARGET_PANE_USAGE " [-w width] "
"[-x position] [-y position] [shell-command]",
.target = { 't', CMD_FIND_PANE, 0 },
.flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG,
.exec = cmd_display_popup_exec
};
static enum args_parse_type
cmd_display_menu_args_parse(struct args *args, u_int idx, __unused char **cause)
{
u_int i = 0;
enum args_parse_type type = ARGS_PARSE_STRING;
for (;;) {
type = ARGS_PARSE_STRING;
if (i == idx)
break;
if (*args_string(args, i++) == '\0')
continue;
type = ARGS_PARSE_STRING;
if (i++ == idx)
break;
type = ARGS_PARSE_COMMANDS_OR_STRING;
if (i++ == idx)
break;
}
return (type);
}
static int
cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item,
struct args *args, u_int *px, u_int *py, u_int w, u_int h)
{
struct tty *tty = &tc->tty;
struct cmd_find_state *target = cmdq_get_target(item);
struct key_event *event = cmdq_get_event(item);
struct session *s = tc->session;
struct winlink *wl = target->wl;
struct window_pane *wp = target->wp;
struct style_ranges *ranges = NULL;
struct style_range *sr = NULL;
const char *xp, *yp;
char *p;
int top;
u_int line, ox, oy, sx, sy, lines, position;
long n;
struct format_tree *ft;
/*
* Work out the position from the -x and -y arguments. This is the
* bottom-left position.
*/
/* If the popup is too big, stop now. */
if (w > tty->sx || h > tty->sy)
return (0);
/* Create format with mouse position if any. */
ft = format_create_from_target(item);
if (event->m.valid) {
format_add(ft, "popup_mouse_x", "%u", event->m.x);
format_add(ft, "popup_mouse_y", "%u", event->m.y);
}
/*
* If there are any status lines, add this window position and the
* status line position.
*/
top = status_at_line(tc);
if (top != -1) {
lines = status_line_size(tc);
if (top == 0)
top = lines;
else
top = 0;
position = options_get_number(s->options, "status-position");
for (line = 0; line < lines; line++) {
ranges = &tc->status.entries[line].ranges;
TAILQ_FOREACH(sr, ranges, entry) {
if (sr->type != STYLE_RANGE_WINDOW)
continue;
if (sr->argument == (u_int)wl->idx)
break;
}
if (sr != NULL)
break;
}
if (sr != NULL) {
format_add(ft, "popup_window_status_line_x", "%u",
sr->start);
if (position == 0) {
format_add(ft, "popup_window_status_line_y",
"%u", line + 1 + h);
} else {
format_add(ft, "popup_window_status_line_y",
"%u", tty->sy - lines + line);
}
}
if (position == 0)
format_add(ft, "popup_status_line_y", "%u", lines + h);
else {
format_add(ft, "popup_status_line_y", "%u",
tty->sy - lines);
}
} else
top = 0;
/* Popup width and height. */
format_add(ft, "popup_width", "%u", w);
format_add(ft, "popup_height", "%u", h);
/* Position so popup is in the centre. */
n = (long)(tty->sx - 1) / 2 - w / 2;
if (n < 0)
format_add(ft, "popup_centre_x", "%u", 0);
else
format_add(ft, "popup_centre_x", "%ld", n);
n = (tty->sy - 1) / 2 + h / 2;
if (n >= tty->sy)
format_add(ft, "popup_centre_y", "%u", tty->sy - h);
else
format_add(ft, "popup_centre_y", "%ld", n);
/* Position of popup relative to mouse. */
if (event->m.valid) {
n = (long)event->m.x - w / 2;
if (n < 0)
format_add(ft, "popup_mouse_centre_x", "%u", 0);
else
format_add(ft, "popup_mouse_centre_x", "%ld", n);
n = event->m.y - h / 2;
if (n + h >= tty->sy) {
format_add(ft, "popup_mouse_centre_y", "%u",
tty->sy - h);
} else
format_add(ft, "popup_mouse_centre_y", "%ld", n);
n = (long)event->m.y + h;
if (n >= tty->sy)
format_add(ft, "popup_mouse_top", "%u", tty->sy - 1);
else
format_add(ft, "popup_mouse_top", "%ld", n);
n = event->m.y - h;
if (n < 0)
format_add(ft, "popup_mouse_bottom", "%u", 0);
else
format_add(ft, "popup_mouse_bottom", "%ld", n);
}
/* Position in pane. */
tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy);
n = top + wp->yoff - oy + h;
if (n >= tty->sy)
format_add(ft, "popup_pane_top", "%u", tty->sy - h);
else
format_add(ft, "popup_pane_top", "%ld", n);
format_add(ft, "popup_pane_bottom", "%u", top + wp->yoff + wp->sy - oy);
format_add(ft, "popup_pane_left", "%u", wp->xoff - ox);
n = (long)wp->xoff + wp->sx - ox - w;
if (n < 0)
format_add(ft, "popup_pane_right", "%u", 0);
else
format_add(ft, "popup_pane_right", "%ld", n);
/* Expand horizontal position. */
xp = args_get(args, 'x');
if (xp == NULL || strcmp(xp, "C") == 0)
xp = "#{popup_centre_x}";
else if (strcmp(xp, "R") == 0)
xp = "#{popup_pane_right}";
else if (strcmp(xp, "P") == 0)
xp = "#{popup_pane_left}";
else if (strcmp(xp, "M") == 0)
xp = "#{popup_mouse_centre_x}";
else if (strcmp(xp, "W") == 0)
xp = "#{popup_window_status_line_x}";
p = format_expand(ft, xp);
n = strtol(p, NULL, 10);
if (n + w >= tty->sx)
n = tty->sx - w;
else if (n < 0)
n = 0;
*px = n;
log_debug("%s: -x: %s = %s = %u (-w %u)", __func__, xp, p, *px, w);
free(p);
/* Expand vertical position */
yp = args_get(args, 'y');
if (yp == NULL || strcmp(yp, "C") == 0)
yp = "#{popup_centre_y}";
else if (strcmp(yp, "P") == 0)
yp = "#{popup_pane_bottom}";
else if (strcmp(yp, "M") == 0)
yp = "#{popup_mouse_top}";
else if (strcmp(yp, "S") == 0)
yp = "#{popup_status_line_y}";
else if (strcmp(yp, "W") == 0)
yp = "#{popup_window_status_line_y}";
p = format_expand(ft, yp);
n = strtol(p, NULL, 10);
if (n < h)
n = 0;
else
n -= h;
if (n + h >= tty->sy)
n = tty->sy - h;
else if (n < 0)
n = 0;
*py = n;
log_debug("%s: -y: %s = %s = %u (-h %u)", __func__, yp, p, *py, h);
free(p);
return (1);
}
static enum cmd_retval
cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c;
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct window_pane *wp = item->target.wp;
struct cmd_find_state *fs = &item->target;
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct key_event *event = cmdq_get_event(item);
struct client *tc = cmdq_get_target_client(item);
struct menu *menu = NULL;
struct style_range *sr;
struct menu_item menu_item;
const char *xp, *yp, *key;
char *title, *name;
int at, flags, i;
u_int px, py, ox, oy, sx, sy;
const char *key, *name;
char *title;
int flags = 0;
u_int px, py, i, count = args_count(args);
if ((c = cmd_find_client(item, args_get(args, 'c'), 0)) == NULL)
return (CMD_RETURN_ERROR);
if (c->overlay_draw != NULL)
if (tc->overlay_draw != NULL)
return (CMD_RETURN_NORMAL);
at = status_at_line(c);
if (args_has(args, 'T'))
title = format_single(NULL, args_get(args, 'T'), c, s, wl, wp);
title = format_single_from_target(item, args_get(args, 'T'));
else
title = xstrdup("");
menu = menu_create(title);
for (i = 0; i != args->argc; /* nothing */) {
name = args->argv[i++];
for (i = 0; i != count; /* nothing */) {
name = args_string(args, i++);
if (*name == '\0') {
menu_add_item(menu, NULL, item, c, fs);
menu_add_item(menu, NULL, item, tc, target);
continue;
}
if (args->argc - i < 2) {
if (count - i < 2) {
cmdq_error(item, "not enough arguments");
free(title);
menu_free(menu);
return (CMD_RETURN_ERROR);
}
key = args->argv[i++];
key = args_string(args, i++);
menu_item.name = name;
menu_item.key = key_string_lookup_string(key);
menu_item.command = args->argv[i++];
menu_item.command = args_string(args, i++);
menu_add_item(menu, &menu_item, item, c, fs);
menu_add_item(menu, &menu_item, item, tc, target);
}
free(title);
if (menu == NULL) {
@@ -104,75 +328,98 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
menu_free(menu);
return (CMD_RETURN_NORMAL);
}
xp = args_get(args, 'x');
if (xp == NULL)
px = 0;
else if (strcmp(xp, "R") == 0)
px = c->tty.sx - 1;
else if (strcmp(xp, "P") == 0) {
tty_window_offset(&c->tty, &ox, &oy, &sx, &sy);
if (wp->xoff >= ox)
px = wp->xoff - ox;
else
px = 0;
} else if (strcmp(xp, "M") == 0 && item->shared->mouse.valid) {
if (item->shared->mouse.x > (menu->width + 4) / 2)
px = item->shared->mouse.x - (menu->width + 4) / 2;
else
px = 0;
if (!cmd_display_menu_get_position(tc, item, args, &px, &py,
menu->width + 4, menu->count + 2)) {
menu_free(menu);
return (CMD_RETURN_NORMAL);
}
else if (strcmp(xp, "W") == 0) {
if (at == -1)
px = 0;
else {
TAILQ_FOREACH(sr, &c->status.entries[0].ranges, entry) {
if (sr->type != STYLE_RANGE_WINDOW)
continue;
if (sr->argument == (u_int)wl->idx)
break;
}
if (sr != NULL)
px = sr->start;
else
px = 0;
}
} else
px = strtoul(xp, NULL, 10);
if (px + menu->width + 4 >= c->tty.sx)
px = c->tty.sx - menu->width - 4;
yp = args_get(args, 'y');
if (yp == NULL)
py = 0;
else if (strcmp(yp, "P") == 0) {
tty_window_offset(&c->tty, &ox, &oy, &sx, &sy);
if (wp->yoff + wp->sy >= oy)
py = wp->yoff + wp->sy - oy;
else
py = 0;
} else if (strcmp(yp, "M") == 0 && item->shared->mouse.valid)
py = item->shared->mouse.y + menu->count + 2;
else if (strcmp(yp, "S") == 0) {
if (at == -1)
py = c->tty.sy;
else if (at == 0)
py = status_line_size(c) + menu->count + 2;
else
py = at;
} else
py = strtoul(yp, NULL, 10);
if (py < menu->count + 2)
py = 0;
else
py -= menu->count + 2;
if (py + menu->count + 2 >= c->tty.sy)
py = c->tty.sy - menu->count - 2;
flags = 0;
if (!item->shared->mouse.valid)
if (args_has(args, 'O'))
flags |= MENU_STAYOPEN;
if (!event->m.valid)
flags |= MENU_NOMOUSE;
if (menu_display(menu, flags, item, px, py, c, fs, NULL, NULL) != 0)
if (menu_display(menu, flags, item, px, py, tc, target, NULL,
NULL) != 0)
return (CMD_RETURN_NORMAL);
return (CMD_RETURN_WAIT);
}
static enum cmd_retval
cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct session *s = target->s;
struct client *tc = cmdq_get_target_client(item);
struct tty *tty = &tc->tty;
const char *value, *shell, *shellcmd = NULL;
char *cwd, *cause, **argv = NULL;
int flags = 0, argc = 0;
u_int px, py, w, h, count = args_count(args);
if (args_has(args, 'C')) {
server_client_clear_overlay(tc);
return (CMD_RETURN_NORMAL);
}
if (tc->overlay_draw != NULL)
return (CMD_RETURN_NORMAL);
h = tty->sy / 2;
if (args_has(args, 'h')) {
h = args_percentage(args, 'h', 1, tty->sy, tty->sy, &cause);
if (cause != NULL) {
cmdq_error(item, "height %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
}
w = tty->sx / 2;
if (args_has(args, 'w')) {
w = args_percentage(args, 'w', 1, tty->sx, tty->sx, &cause);
if (cause != NULL) {
cmdq_error(item, "width %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
}
if (w > tty->sx)
w = tty->sx;
if (h > tty->sy)
h = tty->sy;
if (!cmd_display_menu_get_position(tc, item, args, &px, &py, w, h))
return (CMD_RETURN_NORMAL);
value = args_get(args, 'd');
if (value != NULL)
cwd = format_single_from_target(item, value);
else
cwd = xstrdup(server_client_get_cwd(tc, s));
if (count == 0)
shellcmd = options_get_string(s->options, "default-command");
else if (count == 1)
shellcmd = args_string(args, 0);
if (count <= 1 && (shellcmd == NULL || *shellcmd == '\0')) {
shellcmd = NULL;
shell = options_get_string(s->options, "default-shell");
if (!checkshell(shell))
shell = _PATH_BSHELL;
cmd_append_argv(&argc, &argv, shell);
} else
args_to_vector(args, &argc, &argv);
if (args_has(args, 'E') > 1)
flags |= POPUP_CLOSEEXITZERO;
else if (args_has(args, 'E'))
flags |= POPUP_CLOSEEXIT;
if (args_has(args, 'B'))
flags |= POPUP_NOBORDER;
if (popup_display(flags, item, px, py, w, h, shellcmd, argc, argv, cwd,
tc, s, NULL, NULL) != 0) {
cmd_free_argv(argc, argv);
return (CMD_RETURN_NORMAL);
}
cmd_free_argv(argc, argv);
return (CMD_RETURN_WAIT);
}

View File

@@ -39,13 +39,13 @@ const struct cmd_entry cmd_display_message_entry = {
.name = "display-message",
.alias = "display",
.args = { "ac:Ipt:F:v", 0, 1 },
.usage = "[-aIpv] [-c target-client] [-F format] "
.args = { "ac:d:INpt:F:v", 0, 1, NULL },
.usage = "[-aINpv] [-c target-client] [-d delay] [-F format] "
CMD_TARGET_PANE_USAGE " [message]",
.target = { 't', CMD_FIND_PANE, 0 },
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
.flags = CMD_AFTERHOOK,
.flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG|CMD_CLIENT_CANFAIL,
.exec = cmd_display_message_exec
};
@@ -60,33 +60,51 @@ cmd_display_message_each(const char *key, const char *value, void *arg)
static enum cmd_retval
cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c, *target_c;
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct window_pane *wp = item->target.wp;
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct client *tc = cmdq_get_target_client(item), *c;
struct session *s = target->s;
struct winlink *wl = target->wl;
struct window_pane *wp = target->wp;
const char *template;
char *msg, *cause;
int delay = -1, flags;
struct format_tree *ft;
int flags;
u_int count = args_count(args);
if (args_has(args, 'I')) {
if (window_pane_start_input(wp, item, &cause) != 0) {
if (wp == NULL)
return (CMD_RETURN_NORMAL);
switch (window_pane_start_input(wp, item, &cause)) {
case -1:
cmdq_error(item, "%s", cause);
free(cause);
return (CMD_RETURN_ERROR);
case 1:
return (CMD_RETURN_NORMAL);
case 0:
return (CMD_RETURN_WAIT);
}
return (CMD_RETURN_WAIT);
}
if (args_has(args, 'F') && args->argc != 0) {
if (args_has(args, 'F') && count != 0) {
cmdq_error(item, "only one of -F or argument must be given");
return (CMD_RETURN_ERROR);
}
template = args_get(args, 'F');
if (args->argc != 0)
template = args->argv[0];
if (args_has(args, 'd')) {
delay = args_strtonum(args, 'd', 0, UINT_MAX, &cause);
if (cause != NULL) {
cmdq_error(item, "delay %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
}
if (count != 0)
template = args_string(args, 0);
else
template = args_get(args, 'F');
if (template == NULL)
template = DISPLAY_MESSAGE_TEMPLATE;
@@ -96,17 +114,18 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
* formats too, assuming it matches the session. If it doesn't, use the
* best client for the session.
*/
c = cmd_find_client(item, args_get(args, 'c'), 1);
if (c != NULL && c->session == s)
target_c = c;
if (tc != NULL && tc->session == s)
c = tc;
else if (s != NULL)
c = cmd_find_best_client(s);
else
target_c = cmd_find_best_client(s);
if (args_has(self->args, 'v'))
c = NULL;
if (args_has(args, 'v'))
flags = FORMAT_VERBOSE;
else
flags = 0;
ft = format_create(item->client, item, FORMAT_NONE, flags);
format_defaults(ft, target_c, s, wl, wp);
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, flags);
format_defaults(ft, c, s, wl, wp);
if (args_has(args, 'a')) {
format_each(ft, cmd_display_message_each, item);
@@ -114,10 +133,14 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
}
msg = format_expand_time(ft, template);
if (args_has(self->args, 'p'))
if (cmdq_get_client(item) == NULL)
cmdq_error(item, "%s", msg);
else if (args_has(args, 'p'))
cmdq_print(item, "%s", msg);
else if (c != NULL)
status_message_set(c, "%s", msg);
else if (tc != NULL) {
status_message_set(tc, delay, 0, args_has(args, 'N'), "%s",
msg);
}
free(msg);
format_free(ft);

View File

@@ -27,25 +27,34 @@
* Display panes on a client.
*/
static enum cmd_retval cmd_display_panes_exec(struct cmd *,
struct cmdq_item *);
static enum args_parse_type cmd_display_panes_args_parse(struct args *,
u_int, char **);
static enum cmd_retval cmd_display_panes_exec(struct cmd *,
struct cmdq_item *);
const struct cmd_entry cmd_display_panes_entry = {
.name = "display-panes",
.alias = "displayp",
.args = { "bd:t:", 0, 1 },
.usage = "[-b] [-d duration] " CMD_TARGET_CLIENT_USAGE " [template]",
.args = { "bd:Nt:", 0, 1, cmd_display_panes_args_parse },
.usage = "[-bN] [-d duration] " CMD_TARGET_CLIENT_USAGE " [template]",
.flags = CMD_AFTERHOOK,
.flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG,
.exec = cmd_display_panes_exec
};
struct cmd_display_panes_data {
struct cmdq_item *item;
char *command;
struct cmdq_item *item;
struct args_command_state *state;
};
static enum args_parse_type
cmd_display_panes_args_parse(__unused struct args *args, __unused u_int idx,
__unused char **cause)
{
return (ARGS_PARSE_COMMANDS_OR_STRING);
}
static void
cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
struct window_pane *wp)
@@ -55,11 +64,11 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
struct session *s = c->session;
struct options *oo = s->options;
struct window *w = wp->window;
struct grid_cell gc;
u_int idx, px, py, i, j, xoff, yoff, sx, sy;
struct grid_cell fgc, bgc;
u_int pane, idx, px, py, i, j, xoff, yoff, sx, sy;
int colour, active_colour;
char buf[16], *ptr;
size_t len;
char buf[16], lbuf[16], rbuf[16], *ptr;
size_t len, llen, rlen;
if (wp->xoff + wp->sx <= ctx->ox ||
wp->xoff >= ctx->ox + ctx->sx ||
@@ -109,31 +118,50 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
px = sx / 2;
py = sy / 2;
if (window_pane_index(wp, &idx) != 0)
if (window_pane_index(wp, &pane) != 0)
fatalx("index not found");
len = xsnprintf(buf, sizeof buf, "%u", idx);
len = xsnprintf(buf, sizeof buf, "%u", pane);
if (sx < len)
return;
colour = options_get_number(oo, "display-panes-colour");
active_colour = options_get_number(oo, "display-panes-active-colour");
memcpy(&fgc, &grid_default_cell, sizeof fgc);
memcpy(&bgc, &grid_default_cell, sizeof bgc);
if (w->active == wp) {
fgc.fg = active_colour;
bgc.bg = active_colour;
} else {
fgc.fg = colour;
bgc.bg = colour;
}
rlen = xsnprintf(rbuf, sizeof rbuf, "%ux%u", wp->sx, wp->sy);
if (pane > 9 && pane < 35)
llen = xsnprintf(lbuf, sizeof lbuf, "%c", 'a' + (pane - 10));
else
llen = 0;
if (sx < len * 6 || sy < 5) {
tty_cursor(tty, xoff + px - len / 2, yoff + py);
goto draw_text;
tty_attributes(tty, &fgc, &grid_default_cell, NULL);
if (sx >= len + llen + 1) {
len += llen + 1;
tty_cursor(tty, xoff + px - len / 2, yoff + py);
tty_putn(tty, buf, len, len);
tty_putn(tty, " ", 1, 1);
tty_putn(tty, lbuf, llen, llen);
} else {
tty_cursor(tty, xoff + px - len / 2, yoff + py);
tty_putn(tty, buf, len, len);
}
goto out;
}
px -= len * 3;
py -= 2;
memcpy(&gc, &grid_default_cell, sizeof gc);
if (w->active == wp)
gc.bg = active_colour;
else
gc.bg = colour;
gc.flags |= GRID_FLAG_NOPALETTE;
tty_attributes(tty, &gc, wp);
tty_attributes(tty, &bgc, &grid_default_cell, NULL);
for (ptr = buf; *ptr != '\0'; ptr++) {
if (*ptr < '0' || *ptr > '9')
continue;
@@ -149,27 +177,26 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
px += 6;
}
len = xsnprintf(buf, sizeof buf, "%ux%u", wp->sx, wp->sy);
if (sx < len || sy < 6)
return;
tty_cursor(tty, xoff + sx - len, yoff);
draw_text:
memcpy(&gc, &grid_default_cell, sizeof gc);
if (w->active == wp)
gc.fg = active_colour;
else
gc.fg = colour;
gc.flags |= GRID_FLAG_NOPALETTE;
tty_attributes(tty, &gc, wp);
tty_puts(tty, buf);
if (sy <= 6)
goto out;
tty_attributes(tty, &fgc, &grid_default_cell, NULL);
if (rlen != 0 && sx >= rlen) {
tty_cursor(tty, xoff + sx - rlen, yoff);
tty_putn(tty, rbuf, rlen, rlen);
}
if (llen != 0) {
tty_cursor(tty, xoff + sx / 2 + len * 3 - llen - 1,
yoff + py + 5);
tty_putn(tty, lbuf, llen, llen);
}
out:
tty_cursor(tty, 0, 0);
}
static void
cmd_display_panes_draw(struct client *c, struct screen_redraw_ctx *ctx)
cmd_display_panes_draw(struct client *c, __unused void *data,
struct screen_redraw_ctx *ctx)
{
struct window *w = c->session->curw->window;
struct window_pane *wp;
@@ -183,55 +210,58 @@ cmd_display_panes_draw(struct client *c, struct screen_redraw_ctx *ctx)
}
static void
cmd_display_panes_free(struct client *c)
cmd_display_panes_free(__unused struct client *c, void *data)
{
struct cmd_display_panes_data *cdata = c->overlay_data;
struct cmd_display_panes_data *cdata = data;
if (cdata->item != NULL)
cmdq_continue(cdata->item);
free(cdata->command);
args_make_commands_free(cdata->state);
free(cdata);
}
static int
cmd_display_panes_key(struct client *c, struct key_event *event)
cmd_display_panes_key(struct client *c, void *data, struct key_event *event)
{
struct cmd_display_panes_data *cdata = c->overlay_data;
struct cmdq_item *new_item;
char *cmd, *expanded;
struct cmd_display_panes_data *cdata = data;
char *expanded, *error;
struct cmdq_item *item = cdata->item, *new_item;
struct cmd_list *cmdlist;
struct window *w = c->session->curw->window;
struct window_pane *wp;
struct cmd_parse_result *pr;
u_int index;
key_code key;
if (event->key < '0' || event->key > '9')
if (event->key >= '0' && event->key <= '9')
index = event->key - '0';
else if ((event->key & KEYC_MASK_MODIFIERS) == 0) {
key = (event->key & KEYC_MASK_KEY);
if (key >= 'a' && key <= 'z')
index = 10 + (key - 'a');
else
return (-1);
} else
return (-1);
wp = window_pane_at_index(w, event->key - '0');
wp = window_pane_at_index(w, index);
if (wp == NULL)
return (1);
window_unzoom(w);
xasprintf(&expanded, "%%%u", wp->id);
cmd = cmd_template_replace(cdata->command, expanded, 1);
pr = cmd_parse_from_string(cmd, NULL);
switch (pr->status) {
case CMD_PARSE_EMPTY:
new_item = NULL;
break;
case CMD_PARSE_ERROR:
new_item = cmdq_get_error(pr->error);
free(pr->error);
cmdlist = args_make_commands(cdata->state, 1, &expanded, &error);
if (cmdlist == NULL) {
cmdq_append(c, cmdq_get_error(error));
free(error);
} else if (item == NULL) {
new_item = cmdq_get_command(cmdlist, NULL);
cmdq_append(c, new_item);
break;
case CMD_PARSE_SUCCESS:
new_item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
cmd_list_free(pr->cmdlist);
cmdq_append(c, new_item);
break;
} else {
new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
cmdq_insert_after(item, new_item);
}
free(cmd);
free(expanded);
return (1);
}
@@ -239,18 +269,15 @@ cmd_display_panes_key(struct client *c, struct key_event *event)
static enum cmd_retval
cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c;
struct session *s;
u_int delay;
struct args *args = cmd_get_args(self);
struct client *tc = cmdq_get_target_client(item);
struct session *s = tc->session;
u_int delay;
char *cause;
struct cmd_display_panes_data *cdata;
int wait = !args_has(args, 'b');
if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL)
return (CMD_RETURN_ERROR);
s = c->session;
if (c->overlay_draw != NULL)
if (tc->overlay_draw != NULL)
return (CMD_RETURN_NORMAL);
if (args_has(args, 'd')) {
@@ -263,20 +290,23 @@ cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item)
} else
delay = options_get_number(s->options, "display-panes-time");
cdata = xmalloc(sizeof *cdata);
if (args->argc != 0)
cdata->command = xstrdup(args->argv[0]);
else
cdata->command = xstrdup("select-pane -t '%%'");
if (args_has(args, 'b'))
cdata->item = NULL;
else
cdata = xcalloc(1, sizeof *cdata);
if (wait)
cdata->item = item;
cdata->state = args_make_commands_prepare(self, item, 0,
"select-pane -t \"%%%\"", wait, 0);
server_client_set_overlay(c, delay, cmd_display_panes_draw,
cmd_display_panes_key, cmd_display_panes_free, cdata);
if (args_has(args, 'N')) {
server_client_set_overlay(tc, delay, NULL, NULL,
cmd_display_panes_draw, NULL, cmd_display_panes_free, NULL,
cdata);
} else {
server_client_set_overlay(tc, delay, NULL, NULL,
cmd_display_panes_draw, cmd_display_panes_key,
cmd_display_panes_free, NULL, cdata);
}
if (args_has(args, 'b'))
if (!wait)
return (CMD_RETURN_NORMAL);
return (CMD_RETURN_WAIT);
}

View File

@@ -32,8 +32,8 @@ const struct cmd_entry cmd_find_window_entry = {
.name = "find-window",
.alias = "findw",
.args = { "CNrt:TZ", 1, 1 },
.usage = "[-CNrTZ] " CMD_TARGET_PANE_USAGE " match-string",
.args = { "CiNrt:TZ", 1, 1, NULL },
.usage = "[-CiNrTZ] " CMD_TARGET_PANE_USAGE " match-string",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -44,82 +44,70 @@ const struct cmd_entry cmd_find_window_entry = {
static enum cmd_retval
cmd_find_window_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args, *new_args;
struct window_pane *wp = item->target.wp;
const char *s = args->argv[0];
char *filter, *argv = { NULL };
struct args *args = cmd_get_args(self), *new_args;
struct cmd_find_state *target = cmdq_get_target(item);
struct window_pane *wp = target->wp;
const char *s = args_string(args, 0), *suffix = "";
struct args_value *filter;
int C, N, T;
C = args_has(args, 'C');
N = args_has(args, 'N');
T = args_has(args, 'T');
if (args_has(args, 'r') && args_has(args, 'i'))
suffix = "/ri";
else if (args_has(args, 'r'))
suffix = "/r";
else if (args_has(args, 'i'))
suffix = "/i";
if (!C && !N && !T)
C = N = T = 1;
if (!args_has(args, 'r')) {
if (C && N && T) {
xasprintf(&filter,
"#{||:"
"#{C:%s},#{||:#{m:*%s*,#{window_name}},"
"#{m:*%s*,#{pane_title}}}}",
s, s, s);
} else if (C && N) {
xasprintf(&filter,
"#{||:#{C:%s},#{m:*%s*,#{window_name}}}",
s, s);
} else if (C && T) {
xasprintf(&filter,
"#{||:#{C:%s},#{m:*%s*,#{pane_title}}}",
s, s);
} else if (N && T) {
xasprintf(&filter,
"#{||:#{m:*%s*,#{window_name}},"
"#{m:*%s*,#{pane_title}}}",
s, s);
} else if (C)
xasprintf(&filter, "#{C:%s}", s);
else if (N)
xasprintf(&filter, "#{m:*%s*,#{window_name}}", s);
else
xasprintf(&filter, "#{m:*%s*,#{pane_title}}", s);
filter = xcalloc(1, sizeof *filter);
filter->type = ARGS_STRING;
if (C && N && T) {
xasprintf(&filter->string,
"#{||:"
"#{C%s:%s},#{||:#{m%s:*%s*,#{window_name}},"
"#{m%s:*%s*,#{pane_title}}}}",
suffix, s, suffix, s, suffix, s);
} else if (C && N) {
xasprintf(&filter->string,
"#{||:#{C%s:%s},#{m%s:*%s*,#{window_name}}}",
suffix, s, suffix, s);
} else if (C && T) {
xasprintf(&filter->string,
"#{||:#{C%s:%s},#{m%s:*%s*,#{pane_title}}}",
suffix, s, suffix, s);
} else if (N && T) {
xasprintf(&filter->string,
"#{||:#{m%s:*%s*,#{window_name}},"
"#{m%s:*%s*,#{pane_title}}}",
suffix, s, suffix, s);
} else if (C) {
xasprintf(&filter->string,
"#{C%s:%s}",
suffix, s);
} else if (N) {
xasprintf(&filter->string,
"#{m%s:*%s*,#{window_name}}",
suffix, s);
} else {
if (C && N && T) {
xasprintf(&filter,
"#{||:"
"#{C/r:%s},#{||:#{m/r:%s,#{window_name}},"
"#{m/r:%s,#{pane_title}}}}",
s, s, s);
} else if (C && N) {
xasprintf(&filter,
"#{||:#{C/r:%s},#{m/r:%s,#{window_name}}}",
s, s);
} else if (C && T) {
xasprintf(&filter,
"#{||:#{C/r:%s},#{m/r:%s,#{pane_title}}}",
s, s);
} else if (N && T) {
xasprintf(&filter,
"#{||:#{m/r:%s,#{window_name}},"
"#{m/r:%s,#{pane_title}}}",
s, s);
} else if (C)
xasprintf(&filter, "#{C/r:%s}", s);
else if (N)
xasprintf(&filter, "#{m/r:%s,#{window_name}}", s);
else
xasprintf(&filter, "#{m/r:%s,#{pane_title}}", s);
xasprintf(&filter->string,
"#{m%s:*%s*,#{pane_title}}",
suffix, s);
}
new_args = args_parse("", 1, &argv);
new_args = args_create();
if (args_has(args, 'Z'))
args_set(new_args, 'Z', NULL);
args_set(new_args, 'f', filter);
window_pane_set_mode(wp, &window_tree_mode, &item->target, new_args);
window_pane_set_mode(wp, NULL, &window_tree_mode, target, new_args);
args_free(new_args);
free(filter);
return (CMD_RETURN_NORMAL);
}

View File

@@ -587,22 +587,22 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane)
return (-1);
return (0);
} else if (strcmp(pane, "{up-of}") == 0) {
fs->wp = window_pane_find_up(fs->w->active);
fs->wp = window_pane_find_up(fs->current->wp);
if (fs->wp == NULL)
return (-1);
return (0);
} else if (strcmp(pane, "{down-of}") == 0) {
fs->wp = window_pane_find_down(fs->w->active);
fs->wp = window_pane_find_down(fs->current->wp);
if (fs->wp == NULL)
return (-1);
return (0);
} else if (strcmp(pane, "{left-of}") == 0) {
fs->wp = window_pane_find_left(fs->w->active);
fs->wp = window_pane_find_left(fs->current->wp);
if (fs->wp == NULL)
return (-1);
return (0);
} else if (strcmp(pane, "{right-of}") == 0) {
fs->wp = window_pane_find_right(fs->w->active);
fs->wp = window_pane_find_right(fs->current->wp);
if (fs->wp == NULL)
return (-1);
return (0);
@@ -614,7 +614,7 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane)
n = strtonum(pane + 1, 1, INT_MAX, NULL);
else
n = 1;
wp = fs->w->active;
wp = fs->current->wp;
if (pane[0] == '+')
fs->wp = window_pane_next_by_number(fs->w, wp, n);
else
@@ -866,7 +866,18 @@ cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags)
/* If this is an attached client, all done. */
if (c->session != NULL) {
cmd_find_from_session(fs, c->session, flags);
cmd_find_clear_state(fs, flags);
fs->wp = server_client_get_pane(c);
if (fs->wp == NULL) {
cmd_find_from_session(fs, c->session, flags);
return (0);
}
fs->s = c->session;
fs->wl = fs->s->curw;
fs->w = fs->wl->window;
cmd_find_log_state(__func__, fs);
return (0);
}
cmd_find_clear_state(fs, flags);
@@ -960,10 +971,11 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
if (server_check_marked() && (flags & CMD_FIND_DEFAULT_MARKED)) {
fs->current = &marked_pane;
log_debug("%s: current is marked pane", __func__);
} else if (cmd_find_valid_state(&item->shared->current)) {
fs->current = &item->shared->current;
} else if (cmd_find_valid_state(cmdq_get_current(item))) {
fs->current = cmdq_get_current(item);
log_debug("%s: current is from queue", __func__);
} else if (cmd_find_from_client(&current, item->client, flags) == 0) {
} else if (cmd_find_from_client(&current, cmdq_get_client(item),
flags) == 0) {
fs->current = &current;
log_debug("%s: current is from client", __func__);
} else {
@@ -980,7 +992,7 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
/* Mouse target is a plain = or {mouse}. */
if (strcmp(target, "=") == 0 || strcmp(target, "{mouse}") == 0) {
m = &item->shared->mouse;
m = &cmdq_get_event(item)->m;
switch (type) {
case CMD_FIND_PANE:
fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl);
@@ -1230,29 +1242,31 @@ no_pane:
static struct client *
cmd_find_current_client(struct cmdq_item *item, int quiet)
{
struct client *c;
struct client *c = NULL, *found;
struct session *s;
struct window_pane *wp;
struct cmd_find_state fs;
if (item->client != NULL && item->client->session != NULL)
return (item->client);
if (item != NULL)
c = cmdq_get_client(item);
if (c != NULL && c->session != NULL)
return (c);
c = NULL;
if ((wp = cmd_find_inside_pane(item->client)) != NULL) {
found = NULL;
if (c != NULL && (wp = cmd_find_inside_pane(c)) != NULL) {
cmd_find_clear_state(&fs, CMD_FIND_QUIET);
fs.w = wp->window;
if (cmd_find_best_session_with_window(&fs) == 0)
c = cmd_find_best_client(fs.s);
found = cmd_find_best_client(fs.s);
} else {
s = cmd_find_best_session(NULL, 0, CMD_FIND_QUIET);
if (s != NULL)
c = cmd_find_best_client(s);
found = cmd_find_best_client(s);
}
if (c == NULL && !quiet)
if (found == NULL && item != NULL && !quiet)
cmdq_error(item, "no current client");
log_debug("%s: no target, return %p", __func__, c);
return (c);
log_debug("%s: no target, return %p", __func__, found);
return (found);
}
/* Find the target client or report an error and return NULL. */

View File

@@ -20,6 +20,7 @@
#include <sys/types.h>
#include <sys/wait.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
@@ -29,16 +30,19 @@
* Executes a tmux command if a shell command returns true or false.
*/
static enum cmd_retval cmd_if_shell_exec(struct cmd *, struct cmdq_item *);
static enum args_parse_type cmd_if_shell_args_parse(struct args *, u_int,
char **);
static enum cmd_retval cmd_if_shell_exec(struct cmd *,
struct cmdq_item *);
static void cmd_if_shell_callback(struct job *);
static void cmd_if_shell_free(void *);
static void cmd_if_shell_callback(struct job *);
static void cmd_if_shell_free(void *);
const struct cmd_entry cmd_if_shell_entry = {
.name = "if-shell",
.alias = "if",
.args = { "bFt:", 2, 3 },
.args = { "bFt:", 2, 3, cmd_if_shell_args_parse },
.usage = "[-bF] " CMD_TARGET_PANE_USAGE " shell-command command "
"[command]",
@@ -49,102 +53,75 @@ const struct cmd_entry cmd_if_shell_entry = {
};
struct cmd_if_shell_data {
struct cmd_parse_input input;
struct args_command_state *cmd_if;
struct args_command_state *cmd_else;
char *cmd_if;
char *cmd_else;
struct client *client;
struct cmdq_item *item;
struct mouse_event mouse;
struct client *client;
struct cmdq_item *item;
};
static enum args_parse_type
cmd_if_shell_args_parse(__unused struct args *args, u_int idx,
__unused char **cause)
{
if (idx == 1 || idx == 2)
return (ARGS_PARSE_COMMANDS_OR_STRING);
return (ARGS_PARSE_STRING);
}
static enum cmd_retval
cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct mouse_event *m = &item->shared->mouse;
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct cmd_if_shell_data *cdata;
char *shellcmd, *cmd;
struct cmdq_item *new_item;
struct cmd_find_state *fs = &item->target;
struct client *c = cmd_find_client(item, NULL, 1);
struct session *s = fs->s;
struct winlink *wl = fs->wl;
struct window_pane *wp = fs->wp;
struct cmd_parse_input pi;
struct cmd_parse_result *pr;
char *shellcmd;
struct client *tc = cmdq_get_target_client(item);
struct session *s = target->s;
struct cmd_list *cmdlist;
u_int count = args_count(args);
int wait = !args_has(args, 'b');
shellcmd = format_single(item, args->argv[0], c, s, wl, wp);
shellcmd = format_single_from_target(item, args_string(args, 0));
if (args_has(args, 'F')) {
if (*shellcmd != '0' && *shellcmd != '\0')
cmd = args->argv[1];
else if (args->argc == 3)
cmd = args->argv[2];
else
cmd = NULL;
free(shellcmd);
if (cmd == NULL)
cmdlist = args_make_commands_now(self, item, 1, 0);
else if (count == 3)
cmdlist = args_make_commands_now(self, item, 2, 0);
else {
free(shellcmd);
return (CMD_RETURN_NORMAL);
memset(&pi, 0, sizeof pi);
if (self->file != NULL)
pi.file = self->file;
pi.line = self->line;
pi.item = item;
pi.c = c;
cmd_find_copy_state(&pi.fs, fs);
pr = cmd_parse_from_string(cmd, &pi);
switch (pr->status) {
case CMD_PARSE_EMPTY:
break;
case CMD_PARSE_ERROR:
cmdq_error(item, "%s", pr->error);
free(pr->error);
return (CMD_RETURN_ERROR);
case CMD_PARSE_SUCCESS:
new_item = cmdq_get_command(pr->cmdlist, fs, m, 0);
cmdq_insert_after(item, new_item);
cmd_list_free(pr->cmdlist);
break;
}
free(shellcmd);
if (cmdlist == NULL)
return (CMD_RETURN_ERROR);
new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
cmdq_insert_after(item, new_item);
return (CMD_RETURN_NORMAL);
}
cdata = xcalloc(1, sizeof *cdata);
cdata->cmd_if = xstrdup(args->argv[1]);
if (args->argc == 3)
cdata->cmd_else = xstrdup(args->argv[2]);
else
cdata->cmd_else = NULL;
memcpy(&cdata->mouse, m, sizeof cdata->mouse);
cdata->cmd_if = args_make_commands_prepare(self, item, 1, NULL, wait,
0);
if (count == 3) {
cdata->cmd_else = args_make_commands_prepare(self, item, 2,
NULL, wait, 0);
}
if (!args_has(args, 'b'))
cdata->client = item->client;
else
cdata->client = c;
if (wait) {
cdata->client = cmdq_get_client(item);
cdata->item = item;
} else
cdata->client = tc;
if (cdata->client != NULL)
cdata->client->references++;
if (!args_has(args, 'b'))
cdata->item = item;
else
cdata->item = NULL;
memset(&cdata->input, 0, sizeof cdata->input);
if (self->file != NULL)
cdata->input.file = xstrdup(self->file);
cdata->input.line = self->line;
cdata->input.item = cdata->item;
cdata->input.c = c;
if (cdata->input.c != NULL)
cdata->input.c->references++;
cmd_find_copy_state(&cdata->input.fs, fs);
if (job_run(shellcmd, s, server_client_get_cwd(item->client, s), NULL,
cmd_if_shell_callback, cmd_if_shell_free, cdata, 0) == NULL) {
if (job_run(shellcmd, 0, NULL, s,
server_client_get_cwd(cmdq_get_client(item), s), NULL,
cmd_if_shell_callback, cmd_if_shell_free, cdata, 0, -1,
-1) == NULL) {
cmdq_error(item, "failed to run command: %s", shellcmd);
free(shellcmd);
free(cdata);
@@ -152,7 +129,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
}
free(shellcmd);
if (args_has(args, 'b'))
if (!wait)
return (CMD_RETURN_NORMAL);
return (CMD_RETURN_WAIT);
}
@@ -162,39 +139,34 @@ cmd_if_shell_callback(struct job *job)
{
struct cmd_if_shell_data *cdata = job_get_data(job);
struct client *c = cdata->client;
struct mouse_event *m = &cdata->mouse;
struct cmdq_item *new_item = NULL;
char *cmd;
struct cmdq_item *item = cdata->item, *new_item;
struct args_command_state *state;
struct cmd_list *cmdlist;
char *error;
int status;
struct cmd_parse_result *pr;
status = job_get_status(job);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
cmd = cdata->cmd_else;
state = cdata->cmd_else;
else
cmd = cdata->cmd_if;
if (cmd == NULL)
state = cdata->cmd_if;
if (state == NULL)
goto out;
pr = cmd_parse_from_string(cmd, &cdata->input);
switch (pr->status) {
case CMD_PARSE_EMPTY:
break;
case CMD_PARSE_ERROR:
if (cdata->item != NULL)
cmdq_error(cdata->item, "%s", pr->error);
free(pr->error);
break;
case CMD_PARSE_SUCCESS:
new_item = cmdq_get_command(pr->cmdlist, NULL, m, 0);
cmd_list_free(pr->cmdlist);
break;
}
if (new_item != NULL) {
if (cdata->item == NULL)
cmdq_append(c, new_item);
else
cmdq_insert_after(cdata->item, new_item);
cmdlist = args_make_commands(state, 0, NULL, &error);
if (cmdlist == NULL) {
if (cdata->item == NULL) {
*error = toupper((u_char)*error);
status_message_set(c, -1, 1, 0, "%s", error);
} else
cmdq_error(cdata->item, "%s", error);
free(error);
} else if (item == NULL) {
new_item = cmdq_get_command(cmdlist, NULL);
cmdq_append(c, new_item);
} else {
new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
cmdq_insert_after(item, new_item);
}
out:
@@ -210,12 +182,9 @@ cmd_if_shell_free(void *data)
if (cdata->client != NULL)
server_client_unref(cdata->client);
free(cdata->cmd_else);
free(cdata->cmd_if);
if (cdata->input.c != NULL)
server_client_unref(cdata->input.c);
free((void *)cdata->input.file);
if (cdata->cmd_else != NULL)
args_make_commands_free(cdata->cmd_else);
args_make_commands_free(cdata->cmd_if);
free(cdata);
}

View File

@@ -35,7 +35,7 @@ const struct cmd_entry cmd_join_pane_entry = {
.name = "join-pane",
.alias = "joinp",
.args = { "bdfhvp:l:s:t:", 0, 0 },
.args = { "bdfhvp:l:s:t:", 0, 0, NULL },
.usage = "[-bdfhv] [-l size] " CMD_SRCDST_PANE_USAGE,
.source = { 's', CMD_FIND_PANE, CMD_FIND_DEFAULT_MARKED },
@@ -49,8 +49,8 @@ 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,
.args = { "bdfhvp:l:s:t:", 0, 0, NULL },
.usage = "[-bdfhv] [-l size] " CMD_SRCDST_PANE_USAGE,
.source = { 's', CMD_FIND_PANE, CMD_FIND_DEFAULT_MARKED },
.target = { 't', CMD_FIND_PANE, 0 },
@@ -62,42 +62,33 @@ const struct cmd_entry cmd_move_pane_entry = {
static enum cmd_retval
cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct cmd_find_state *current = &item->shared->current;
struct args *args = cmd_get_args(self);
struct cmd_find_state *current = cmdq_get_current(item);
struct cmd_find_state *target = cmdq_get_target(item);
struct cmd_find_state *source = cmdq_get_source(item);
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, *copy;
const char *errstr, *p;
size_t plen;
int size, percentage, dst_idx, not_same_window;
char *cause = NULL;
int size, percentage, dst_idx;
int flags;
enum layout_type type;
struct layout_cell *lc;
if (self->entry == &cmd_join_pane_entry)
not_same_window = 1;
else
not_same_window = 0;
dst_s = item->target.s;
dst_wl = item->target.wl;
dst_wp = item->target.wp;
dst_s = target->s;
dst_wl = target->wl;
dst_wp = target->wp;
dst_w = dst_wl->window;
dst_idx = dst_wl->idx;
server_unzoom_window(dst_w);
src_wl = item->source.wl;
src_wp = item->source.wp;
src_wl = source->wl;
src_wp = source->wp;
src_w = src_wl->window;
server_unzoom_window(src_w);
if (not_same_window && src_w == dst_w) {
cmdq_error(item, "can't join a pane to its own window");
return (CMD_RETURN_ERROR);
}
if (!not_same_window && src_wp == dst_wp) {
if (src_wp == dst_wp) {
cmdq_error(item, "source and target panes must be different");
return (CMD_RETURN_ERROR);
}
@@ -107,40 +98,27 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
type = LAYOUT_LEFTRIGHT;
size = -1;
if ((p = args_get(args, 'l')) != NULL) {
plen = strlen(p);
if (p[plen - 1] == '%') {
copy = xstrdup(p);
copy[plen - 1] = '\0';
percentage = strtonum(copy, 0, INT_MAX, &errstr);
free(copy);
if (errstr != NULL) {
cmdq_error(item, "percentage %s", errstr);
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'l')) {
if (type == LAYOUT_TOPBOTTOM) {
size = args_percentage(args, 'l', 0, INT_MAX,
dst_wp->sy, &cause);
} else {
size = args_percentage(args, 'l', 0, INT_MAX,
dst_wp->sx, &cause);
}
} else if (args_has(args, 'p')) {
percentage = args_strtonum(args, 'p', 0, 100, &cause);
if (cause == NULL) {
if (type == LAYOUT_TOPBOTTOM)
size = (dst_wp->sy * percentage) / 100;
else
size = (dst_wp->sx * percentage) / 100;
} else {
size = args_strtonum(args, 'l', 0, INT_MAX, &cause);
if (cause != NULL) {
cmdq_error(item, "size %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
}
} else if (args_has(args, 'p')) {
percentage = args_strtonum(args, 'p', 0, 100, &cause);
if (cause != NULL) {
cmdq_error(item, "percentage %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
if (type == LAYOUT_TOPBOTTOM)
size = (dst_wp->sy * percentage) / 100;
else
size = (dst_wp->sx * percentage) / 100;
}
if (cause != NULL) {
cmdq_error(item, "size %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
flags = 0;
@@ -157,14 +135,18 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
layout_close_pane(src_wp);
server_client_remove_pane(src_wp);
window_lost_pane(src_w, src_wp);
TAILQ_REMOVE(&src_w->panes, src_wp, entry);
src_wp->window = dst_w;
options_set_parent(src_wp->options, dst_w->options);
src_wp->flags |= PANE_STYLECHANGED;
TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry);
layout_assign_pane(lc, src_wp);
if (flags & SPAWN_BEFORE)
TAILQ_INSERT_BEFORE(dst_wp, src_wp, entry);
else
TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry);
layout_assign_pane(lc, src_wp, 0);
recalculate_sizes();
@@ -180,7 +162,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
server_status_session(dst_s);
if (window_count_panes(src_w) == 0)
server_kill_window(src_w);
server_kill_window(src_w, 1);
else
notify_window("window-layout-changed", src_w);
notify_window("window-layout-changed", dst_w);

View File

@@ -32,7 +32,7 @@ const struct cmd_entry cmd_kill_pane_entry = {
.name = "kill-pane",
.alias = "killp",
.args = { "at:", 0, 0 },
.args = { "at:", 0, 0, NULL },
.usage = "[-a] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 },
@@ -44,14 +44,17 @@ const struct cmd_entry cmd_kill_pane_entry = {
static enum cmd_retval
cmd_kill_pane_exec(struct cmd *self, struct cmdq_item *item)
{
struct winlink *wl = item->target.wl;
struct window_pane *loopwp, *tmpwp, *wp = item->target.wp;
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct winlink *wl = target->wl;
struct window_pane *loopwp, *tmpwp, *wp = target->wp;
if (args_has(self->args, 'a')) {
if (args_has(args, 'a')) {
server_unzoom_window(wl->window);
TAILQ_FOREACH_SAFE(loopwp, &wl->window->panes, entry, tmpwp) {
if (loopwp == wp)
continue;
server_client_remove_pane(loopwp);
layout_close_pane(loopwp);
window_remove_pane(wl->window, loopwp);
}

View File

@@ -33,7 +33,7 @@ const struct cmd_entry cmd_kill_server_entry = {
.name = "kill-server",
.alias = NULL,
.args = { "", 0, 0 },
.args = { "", 0, 0, NULL },
.usage = "",
.flags = 0,
@@ -44,7 +44,7 @@ const struct cmd_entry cmd_start_server_entry = {
.name = "start-server",
.alias = "start",
.args = { "", 0, 0 },
.args = { "", 0, 0, NULL },
.usage = "",
.flags = CMD_STARTSERVER,
@@ -54,7 +54,7 @@ const struct cmd_entry cmd_start_server_entry = {
static enum cmd_retval
cmd_kill_server_exec(struct cmd *self, __unused struct cmdq_item *item)
{
if (self->entry == &cmd_kill_server_entry)
if (cmd_get_entry(self) == &cmd_kill_server_entry)
kill(getpid(), SIGTERM);
return (CMD_RETURN_NORMAL);

View File

@@ -33,7 +33,7 @@ const struct cmd_entry cmd_kill_session_entry = {
.name = "kill-session",
.alias = NULL,
.args = { "aCt:", 0, 0 },
.args = { "aCt:", 0, 0, NULL },
.usage = "[-aC] " CMD_TARGET_SESSION_USAGE,
.target = { 't', CMD_FIND_SESSION, 0 },
@@ -45,11 +45,10 @@ const struct cmd_entry cmd_kill_session_entry = {
static enum cmd_retval
cmd_kill_session_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct session *s, *sloop, *stmp;
struct winlink *wl;
s = item->target.s;
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct session *s = target->s, *sloop, *stmp;
struct winlink *wl;
if (args_has(args, 'C')) {
RB_FOREACH(wl, winlinks, &s->windows) {

View File

@@ -30,7 +30,7 @@ const struct cmd_entry cmd_kill_window_entry = {
.name = "kill-window",
.alias = "killw",
.args = { "at:", 0, 0 },
.args = { "at:", 0, 0, NULL },
.usage = "[-a] " CMD_TARGET_WINDOW_USAGE,
.target = { 't', CMD_FIND_WINDOW, 0 },
@@ -43,7 +43,7 @@ const struct cmd_entry cmd_unlink_window_entry = {
.name = "unlink-window",
.alias = "unlinkw",
.args = { "kt:", 0, 0 },
.args = { "kt:", 0, 0, NULL },
.usage = "[-k] " CMD_TARGET_WINDOW_USAGE,
.target = { 't', CMD_FIND_WINDOW, 0 },
@@ -55,27 +55,56 @@ const struct cmd_entry cmd_unlink_window_entry = {
static enum cmd_retval
cmd_kill_window_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct winlink *wl = item->target.wl, *wl2, *wl3;
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct winlink *wl = target->wl, *loop;
struct window *w = wl->window;
struct session *s = item->target.s;
struct session *s = target->s;
u_int found;
if (self->entry == &cmd_unlink_window_entry) {
if (!args_has(self->args, 'k') && !session_is_linked(s, w)) {
if (cmd_get_entry(self) == &cmd_unlink_window_entry) {
if (!args_has(args, 'k') && !session_is_linked(s, w)) {
cmdq_error(item, "window only linked to one session");
return (CMD_RETURN_ERROR);
}
server_unlink_window(s, wl);
} else {
if (args_has(args, 'a')) {
RB_FOREACH_SAFE(wl2, winlinks, &s->windows, wl3) {
if (wl != wl2)
server_kill_window(wl2->window);
}
} else
server_kill_window(wl->window);
recalculate_sizes();
return (CMD_RETURN_NORMAL);
}
recalculate_sizes();
if (args_has(args, 'a')) {
if (RB_PREV(winlinks, &s->windows, wl) == NULL &&
RB_NEXT(winlinks, &s->windows, wl) == NULL)
return (CMD_RETURN_NORMAL);
/* Kill all windows except the current one. */
do {
found = 0;
RB_FOREACH(loop, winlinks, &s->windows) {
if (loop->window != wl->window) {
server_kill_window(loop->window, 0);
found++;
break;
}
}
} while (found != 0);
/*
* If the current window appears in the session more than once,
* kill it as well.
*/
found = 0;
RB_FOREACH(loop, winlinks, &s->windows) {
if (loop->window == wl->window)
found++;
}
if (found > 1)
server_kill_window(wl->window, 0);
server_renumber_all();
return (CMD_RETURN_NORMAL);
}
server_kill_window(wl->window, 1);
return (CMD_RETURN_NORMAL);
}

View File

@@ -36,8 +36,8 @@ const struct cmd_entry cmd_list_buffers_entry = {
.name = "list-buffers",
.alias = "lsb",
.args = { "F:", 0, 0 },
.usage = "[-F format]",
.args = { "F:f:", 0, 0, NULL },
.usage = "[-F format] [-f filter]",
.flags = CMD_AFTERHOOK,
.exec = cmd_list_buffers_exec
@@ -46,23 +46,33 @@ const struct cmd_entry cmd_list_buffers_entry = {
static enum cmd_retval
cmd_list_buffers_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct args *args = cmd_get_args(self);
struct paste_buffer *pb;
struct format_tree *ft;
char *line;
const char *template;
const char *template, *filter;
char *line, *expanded;
int flag;
if ((template = args_get(args, 'F')) == NULL)
template = LIST_BUFFERS_TEMPLATE;
filter = args_get(args, 'f');
pb = NULL;
while ((pb = paste_walk(pb)) != NULL) {
ft = format_create(item->client, item, FORMAT_NONE, 0);
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_defaults_paste_buffer(ft, pb);
line = format_expand(ft, template);
cmdq_print(item, "%s", line);
free(line);
if (filter != NULL) {
expanded = format_expand(ft, filter);
flag = format_true(expanded);
free(expanded);
} else
flag = 1;
if (flag) {
line = format_expand(ft, template);
cmdq_print(item, "%s", line);
free(line);
}
format_free(ft);
}

View File

@@ -28,10 +28,10 @@
* List all clients.
*/
#define LIST_CLIENTS_TEMPLATE \
"#{client_name}: #{session_name} " \
"[#{client_width}x#{client_height} #{client_termname}]" \
"#{?client_utf8, (utf8),} #{?client_readonly, (ro),}"
#define LIST_CLIENTS_TEMPLATE \
"#{client_name}: #{session_name} " \
"[#{client_width}x#{client_height} #{client_termname}] " \
"#{?client_flags,(,}#{client_flags}#{?client_flags,),}"
static enum cmd_retval cmd_list_clients_exec(struct cmd *, struct cmdq_item *);
@@ -39,7 +39,7 @@ const struct cmd_entry cmd_list_clients_entry = {
.name = "list-clients",
.alias = "lsc",
.args = { "F:t:", 0, 0 },
.args = { "F:t:", 0, 0, NULL },
.usage = "[-F format] " CMD_TARGET_SESSION_USAGE,
.target = { 't', CMD_FIND_SESSION, 0 },
@@ -51,7 +51,8 @@ const struct cmd_entry cmd_list_clients_entry = {
static enum cmd_retval
cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct client *c;
struct session *s;
struct format_tree *ft;
@@ -60,7 +61,7 @@ cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item)
char *line;
if (args_has(args, 't'))
s = item->target.s;
s = target->s;
else
s = NULL;
@@ -72,7 +73,7 @@ cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item)
if (c->session == NULL || (s != NULL && s != c->session))
continue;
ft = format_create(item->client, item, FORMAT_NONE, 0);
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_add(ft, "line", "%u", idx);
format_defaults(ft, c, NULL, NULL, NULL);

View File

@@ -36,7 +36,7 @@ const struct cmd_entry cmd_list_keys_entry = {
.name = "list-keys",
.alias = "lsk",
.args = { "1aNP:T:", 0, 1 },
.args = { "1aNP:T:", 0, 1, NULL },
.usage = "[-1aN] [-P prefix-string] [-T key-table] [key]",
.flags = CMD_STARTSERVER|CMD_AFTERHOOK,
@@ -47,8 +47,8 @@ const struct cmd_entry cmd_list_commands_entry = {
.name = "list-commands",
.alias = "lscm",
.args = { "F:", 0, 0 },
.usage = "[-F format]",
.args = { "F:", 0, 1, NULL },
.usage = "[-F format] [command]",
.flags = CMD_STARTSERVER|CMD_AFTERHOOK,
.exec = cmd_list_keys_exec
@@ -68,11 +68,12 @@ cmd_list_keys_get_width(const char *tablename, key_code only)
while (bd != NULL) {
if ((only != KEYC_UNKNOWN && bd->key != only) ||
KEYC_IS_MOUSE(bd->key) ||
bd->note == NULL) {
bd->note == NULL ||
*bd->note == '\0') {
bd = key_bindings_next(table, bd);
continue;
}
width = utf8_cstrwidth(key_string_lookup_key(bd->key));
width = utf8_cstrwidth(key_string_lookup_key(bd->key, 0));
if (width > keywidth)
keywidth = width;
@@ -85,7 +86,7 @@ static int
cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args,
const char *tablename, u_int keywidth, key_code only, const char *prefix)
{
struct client *c = cmd_find_client(item, NULL, 1);
struct client *tc = cmdq_get_target_client(item);
struct key_table *table;
struct key_binding *bd;
const char *key;
@@ -99,21 +100,23 @@ cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args,
while (bd != NULL) {
if ((only != KEYC_UNKNOWN && bd->key != only) ||
KEYC_IS_MOUSE(bd->key) ||
(bd->note == NULL && !args_has(args, 'a'))) {
((bd->note == NULL || *bd->note == '\0') &&
!args_has(args, 'a'))) {
bd = key_bindings_next(table, bd);
continue;
}
found = 1;
key = key_string_lookup_key(bd->key);
key = key_string_lookup_key(bd->key, 0);
if (bd->note == NULL)
if (bd->note == NULL || *bd->note == '\0')
note = cmd_list_print(bd->cmdlist, 1);
else
note = xstrdup(bd->note);
tmp = utf8_padcstr(key, keywidth + 1);
if (args_has(args, '1') && c != NULL)
status_message_set(c, "%s%s%s", prefix, tmp, note);
else
if (args_has(args, '1') && tc != NULL) {
status_message_set(tc, -1, 1, 0, "%s%s%s", prefix, tmp,
note);
} else
cmdq_print(item, "%s%s%s", prefix, tmp, note);
free(tmp);
free(note);
@@ -133,7 +136,7 @@ cmd_list_keys_get_prefix(struct args *args, key_code *prefix)
*prefix = options_get_number(global_s_options, "prefix");
if (!args_has(args, 'P')) {
if (*prefix != KEYC_NONE)
xasprintf(&s, "%s ", key_string_lookup_key(*prefix));
xasprintf(&s, "%s ", key_string_lookup_key(*prefix, 0));
else
s = xstrdup("");
} else
@@ -144,24 +147,25 @@ cmd_list_keys_get_prefix(struct args *args, key_code *prefix)
static enum cmd_retval
cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct args *args = cmd_get_args(self);
struct key_table *table;
struct key_binding *bd;
const char *tablename, *r;
const char *tablename, *r, *keystr;
char *key, *cp, *tmp, *start, *empty;
key_code prefix, only = KEYC_UNKNOWN;
int repeat, width, tablewidth, keywidth, found = 0;
size_t tmpsize, tmpused, cplen;
if (self->entry == &cmd_list_commands_entry)
if (cmd_get_entry(self) == &cmd_list_commands_entry)
return (cmd_list_keys_commands(self, item));
if (args->argc != 0) {
only = key_string_lookup_string(args->argv[0]);
if ((keystr = args_string(args, 0)) != NULL) {
only = key_string_lookup_string(keystr);
if (only == KEYC_UNKNOWN) {
cmdq_error(item, "invalid key: %s", args->argv[0]);
cmdq_error(item, "invalid key: %s", keystr);
return (CMD_RETURN_ERROR);
}
only &= (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS);
}
tablename = args_get(args, 'T');
@@ -207,7 +211,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
repeat = 0;
tablewidth = keywidth = 0;
table = key_bindings_first_table ();
table = key_bindings_first_table();
while (table != NULL) {
if (tablename != NULL && strcmp(table->name, tablename) != 0) {
table = key_bindings_next_table(table);
@@ -219,7 +223,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
bd = key_bindings_next(table, bd);
continue;
}
key = args_escape(key_string_lookup_key(bd->key));
key = args_escape(key_string_lookup_key(bd->key, 0));
if (bd->flags & KEY_BINDING_REPEAT)
repeat = 1;
@@ -240,7 +244,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
tmpsize = 256;
tmp = xmalloc(tmpsize);
table = key_bindings_first_table ();
table = key_bindings_first_table();
while (table != NULL) {
if (tablename != NULL && strcmp(table->name, tablename) != 0) {
table = key_bindings_next_table(table);
@@ -253,7 +257,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
continue;
}
found = 1;
key = args_escape(key_string_lookup_key(bd->key));
key = args_escape(key_string_lookup_key(bd->key, 0));
if (!repeat)
r = "";
@@ -269,7 +273,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
tmpsize *= 2;
tmp = xrealloc(tmp, tmpsize);
}
tmpused = strlcat(tmp, cp, tmpsize);
strlcat(tmp, cp, tmpsize);
tmpused = strlcat(tmp, " ", tmpsize);
free(cp);
@@ -279,7 +283,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
tmpsize *= 2;
tmp = xrealloc(tmp, tmpsize);
}
tmpused = strlcat(tmp, cp, tmpsize);
strlcat(tmp, cp, tmpsize);
tmpused = strlcat(tmp, " ", tmpsize);
free(cp);
@@ -304,7 +308,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
out:
if (only != KEYC_UNKNOWN && !found) {
cmdq_error(item, "unknown key: %s", args->argv[0]);
cmdq_error(item, "unknown key: %s", args_string(args, 0));
return (CMD_RETURN_ERROR);
}
return (CMD_RETURN_NORMAL);
@@ -313,11 +317,11 @@ out:
static enum cmd_retval
cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct args *args = cmd_get_args(self);
const struct cmd_entry **entryp;
const struct cmd_entry *entry;
struct format_tree *ft;
const char *template, *s;
const char *template, *s, *command;
char *line;
if ((template = args_get(args, 'F')) == NULL) {
@@ -326,11 +330,17 @@ cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
"#{command_list_usage}";
}
ft = format_create(item->client, item, FORMAT_NONE, 0);
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_defaults(ft, NULL, NULL, NULL, NULL);
command = args_string(args, 0);
for (entryp = cmd_table; *entryp != NULL; entryp++) {
entry = *entryp;
if (command != NULL &&
(strcmp(entry->name, command) != 0 &&
(entry->alias == NULL ||
strcmp(entry->alias, command) != 0)))
continue;
format_add(ft, "command_list_name", "%s", entry->name);
if (entry->alias != NULL)

View File

@@ -38,8 +38,8 @@ 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,
.args = { "asF:f:t:", 0, 0, NULL },
.usage = "[-as] [-F format] [-f filter] " CMD_TARGET_WINDOW_USAGE,
.target = { 't', CMD_FIND_WINDOW, 0 },
@@ -50,9 +50,10 @@ const struct cmd_entry cmd_list_panes_entry = {
static enum cmd_retval
cmd_list_panes_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct session *s = target->s;
struct winlink *wl = target->wl;
if (args_has(args, 'a'))
cmd_list_panes_server(self, item);
@@ -87,12 +88,13 @@ static void
cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl,
struct cmdq_item *item, int type)
{
struct args *args = self->args;
struct args *args = cmd_get_args(self);
struct window_pane *wp;
u_int n;
struct format_tree *ft;
const char *template;
char *line;
const char *template, *filter;
char *line, *expanded;
int flag;
template = args_get(args, 'F');
if (template == NULL) {
@@ -120,16 +122,25 @@ cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl,
break;
}
}
filter = args_get(args, 'f');
n = 0;
TAILQ_FOREACH(wp, &wl->window->panes, entry) {
ft = format_create(item->client, item, FORMAT_NONE, 0);
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_add(ft, "line", "%u", n);
format_defaults(ft, NULL, s, wl, wp);
line = format_expand(ft, template);
cmdq_print(item, "%s", line);
free(line);
if (filter != NULL) {
expanded = format_expand(ft, filter);
flag = format_true(expanded);
free(expanded);
} else
flag = 1;
if (flag) {
line = format_expand(ft, template);
cmdq_print(item, "%s", line);
free(line);
}
format_free(ft);
n++;

View File

@@ -42,8 +42,8 @@ const struct cmd_entry cmd_list_sessions_entry = {
.name = "list-sessions",
.alias = "ls",
.args = { "F:", 0, 0 },
.usage = "[-F format]",
.args = { "F:f:", 0, 0, NULL },
.usage = "[-F format] [-f filter]",
.flags = CMD_AFTERHOOK,
.exec = cmd_list_sessions_exec
@@ -52,25 +52,35 @@ const struct cmd_entry cmd_list_sessions_entry = {
static enum cmd_retval
cmd_list_sessions_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct args *args = cmd_get_args(self);
struct session *s;
u_int n;
struct format_tree *ft;
const char *template;
char *line;
const char *template, *filter;
char *line, *expanded;
int flag;
if ((template = args_get(args, 'F')) == NULL)
template = LIST_SESSIONS_TEMPLATE;
filter = args_get(args, 'f');
n = 0;
RB_FOREACH(s, sessions, &sessions) {
ft = format_create(item->client, item, FORMAT_NONE, 0);
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_add(ft, "line", "%u", n);
format_defaults(ft, NULL, s, NULL, NULL);
line = format_expand(ft, template);
cmdq_print(item, "%s", line);
free(line);
if (filter != NULL) {
expanded = format_expand(ft, filter);
flag = format_true(expanded);
free(expanded);
} else
flag = 1;
if (flag) {
line = format_expand(ft, template);
cmdq_print(item, "%s", line);
free(line);
}
format_free(ft);
n++;

View File

@@ -28,14 +28,14 @@
*/
#define LIST_WINDOWS_TEMPLATE \
"#{window_index}: #{window_name}#{window_flags} " \
"#{window_index}: #{window_name}#{window_raw_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_index}: #{window_name}#{window_raw_flags} " \
"(#{window_panes} panes) " \
"[#{window_width}x#{window_height}] "
@@ -49,8 +49,8 @@ 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,
.args = { "F:f:at:", 0, 0, NULL },
.usage = "[-a] [-F format] [-f filter] " CMD_TARGET_SESSION_USAGE,
.target = { 't', CMD_FIND_SESSION, 0 },
@@ -61,12 +61,13 @@ const struct cmd_entry cmd_list_windows_entry = {
static enum cmd_retval
cmd_list_windows_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
if (args_has(args, 'a'))
cmd_list_windows_server(self, item);
else
cmd_list_windows_session(self, item->target.s, item, 0);
cmd_list_windows_session(self, target->s, item, 0);
return (CMD_RETURN_NORMAL);
}
@@ -84,12 +85,13 @@ static void
cmd_list_windows_session(struct cmd *self, struct session *s,
struct cmdq_item *item, int type)
{
struct args *args = self->args;
struct args *args = cmd_get_args(self);
struct winlink *wl;
u_int n;
struct format_tree *ft;
const char *template;
char *line;
const char *template, *filter;
char *line, *expanded;
int flag;
template = args_get(args, 'F');
if (template == NULL) {
@@ -102,16 +104,25 @@ cmd_list_windows_session(struct cmd *self, struct session *s,
break;
}
}
filter = args_get(args, 'f');
n = 0;
RB_FOREACH(wl, winlinks, &s->windows) {
ft = format_create(item->client, item, FORMAT_NONE, 0);
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_add(ft, "line", "%u", n);
format_defaults(ft, NULL, s, wl, NULL);
line = format_expand(ft, template);
cmdq_print(item, "%s", line);
free(line);
if (filter != NULL) {
expanded = format_expand(ft, filter);
flag = format_true(expanded);
free(expanded);
} else
flag = 1;
if (flag) {
line = format_expand(ft, template);
cmdq_print(item, "%s", line);
free(line);
}
format_free(ft);
n++;

View File

@@ -37,14 +37,15 @@ const struct cmd_entry cmd_load_buffer_entry = {
.name = "load-buffer",
.alias = "loadb",
.args = { "b:", 1, 1 },
.usage = CMD_BUFFER_USAGE " path",
.args = { "b:t:w", 1, 1, NULL },
.usage = CMD_BUFFER_USAGE " " CMD_TARGET_CLIENT_USAGE " path",
.flags = CMD_AFTERHOOK,
.flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG|CMD_CLIENT_CANFAIL,
.exec = cmd_load_buffer_exec
};
struct cmd_load_buffer_data {
struct client *client;
struct cmdq_item *item;
char *name;
};
@@ -54,6 +55,7 @@ cmd_load_buffer_done(__unused struct client *c, const char *path, int error,
int closed, struct evbuffer *buffer, void *data)
{
struct cmd_load_buffer_data *cdata = data;
struct client *tc = cdata->client;
struct cmdq_item *item = cdata->item;
void *bdata = EVBUFFER_DATA(buffer);
size_t bsize = EVBUFFER_LENGTH(buffer);
@@ -72,7 +74,12 @@ cmd_load_buffer_done(__unused struct client *c, const char *path, int error,
cmdq_error(item, "%s", cause);
free(cause);
free(copy);
}
} else if (tc != NULL &&
tc->session != NULL &&
(~tc->flags & CLIENT_DEAD))
tty_set_selection(&tc->tty, copy, bsize);
if (tc != NULL)
server_client_unref(tc);
}
cmdq_continue(item);
@@ -83,24 +90,23 @@ cmd_load_buffer_done(__unused struct client *c, const char *path, int error,
static enum cmd_retval
cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct args *args = cmd_get_args(self);
struct client *tc = cmdq_get_target_client(item);
struct cmd_load_buffer_data *cdata;
struct client *c = cmd_find_client(item, NULL, 1);
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct window_pane *wp = item->target.wp;
const char *bufname = args_get(args, 'b');
char *path;
cdata = xmalloc(sizeof *cdata);
cdata = xcalloc(1, sizeof *cdata);
cdata->item = item;
if (bufname != NULL)
cdata->name = xstrdup(bufname);
else
cdata->name = NULL;
if (args_has(args, 'w') && tc != NULL) {
cdata->client = tc;
cdata->client->references++;
}
path = format_single(item, args->argv[0], c, s, wl, wp);
file_read(item->client, path, cmd_load_buffer_done, cdata);
path = format_single_from_target(item, args_string(args, 0));
file_read(cmdq_get_client(item), path, cmd_load_buffer_done, cdata);
free(path);
return (CMD_RETURN_WAIT);

View File

@@ -30,7 +30,7 @@ const struct cmd_entry cmd_lock_server_entry = {
.name = "lock-server",
.alias = "lock",
.args = { "", 0, 0 },
.args = { "", 0, 0, NULL },
.usage = "",
.flags = CMD_AFTERHOOK,
@@ -41,7 +41,7 @@ const struct cmd_entry cmd_lock_session_entry = {
.name = "lock-session",
.alias = "locks",
.args = { "t:", 0, 0 },
.args = { "t:", 0, 0, NULL },
.usage = CMD_TARGET_SESSION_USAGE,
.target = { 't', CMD_FIND_SESSION, 0 },
@@ -54,28 +54,25 @@ const struct cmd_entry cmd_lock_client_entry = {
.name = "lock-client",
.alias = "lockc",
.args = { "t:", 0, 0 },
.args = { "t:", 0, 0, NULL },
.usage = CMD_TARGET_CLIENT_USAGE,
.flags = CMD_AFTERHOOK,
.flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG,
.exec = cmd_lock_server_exec
};
static enum cmd_retval
cmd_lock_server_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c;
struct cmd_find_state *target = cmdq_get_target(item);
struct client *tc = cmdq_get_target_client(item);
if (self->entry == &cmd_lock_server_entry)
if (cmd_get_entry(self) == &cmd_lock_server_entry)
server_lock();
else if (self->entry == &cmd_lock_session_entry)
server_lock_session(item->target.s);
else {
if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL)
return (CMD_RETURN_ERROR);
server_lock_client(c);
}
else if (cmd_get_entry(self) == &cmd_lock_session_entry)
server_lock_session(target->s);
else
server_lock_client(tc);
recalculate_sizes();
return (CMD_RETURN_NORMAL);

View File

@@ -32,8 +32,8 @@ const struct cmd_entry cmd_move_window_entry = {
.name = "move-window",
.alias = "movew",
.args = { "adkrs:t:", 0, 0 },
.usage = "[-dkr] " CMD_SRCDST_WINDOW_USAGE,
.args = { "abdkrs:t:", 0, 0, NULL },
.usage = "[-abdkr] " CMD_SRCDST_WINDOW_USAGE,
.source = { 's', CMD_FIND_WINDOW, 0 },
/* -t is special */
@@ -46,8 +46,8 @@ const struct cmd_entry cmd_link_window_entry = {
.name = "link-window",
.alias = "linkw",
.args = { "adks:t:", 0, 0 },
.usage = "[-dk] " CMD_SRCDST_WINDOW_USAGE,
.args = { "abdks:t:", 0, 0, NULL },
.usage = "[-abdk] " CMD_SRCDST_WINDOW_USAGE,
.source = { 's', CMD_FIND_WINDOW, 0 },
/* -t is special */
@@ -59,49 +59,53 @@ const struct cmd_entry cmd_link_window_entry = {
static enum cmd_retval
cmd_move_window_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
const char *tflag = args_get(args, 't');
struct session *src;
struct session *dst;
struct winlink *wl;
char *cause;
int idx, kflag, dflag, sflag;
struct args *args = cmd_get_args(self);
struct cmd_find_state *source = cmdq_get_source(item);
struct cmd_find_state target;
const char *tflag = args_get(args, 't');
struct session *src = source->s;
struct session *dst;
struct winlink *wl = source->wl;
char *cause;
int idx, kflag, dflag, sflag, before;
if (args_has(args, 'r')) {
if (cmd_find_target(&item->target, item, tflag,
CMD_FIND_SESSION, CMD_FIND_QUIET) != 0)
if (cmd_find_target(&target, item, tflag, CMD_FIND_SESSION,
CMD_FIND_QUIET) != 0)
return (CMD_RETURN_ERROR);
session_renumber_windows(item->target.s);
session_renumber_windows(target.s);
recalculate_sizes();
server_status_session(item->target.s);
server_status_session(target.s);
return (CMD_RETURN_NORMAL);
}
if (cmd_find_target(&item->target, item, tflag, CMD_FIND_WINDOW,
if (cmd_find_target(&target, item, tflag, CMD_FIND_WINDOW,
CMD_FIND_WINDOW_INDEX) != 0)
return (CMD_RETURN_ERROR);
src = item->source.s;
dst = item->target.s;
wl = item->source.wl;
idx = item->target.idx;
dst = target.s;
idx = target.idx;
kflag = args_has(self->args, 'k');
dflag = args_has(self->args, 'd');
sflag = args_has(self->args, 's');
kflag = args_has(args, 'k');
dflag = args_has(args, 'd');
sflag = args_has(args, 's');
if (args_has(self->args, 'a')) {
if ((idx = winlink_shuffle_up(dst, dst->curw)) == -1)
before = args_has(args, 'b');
if (args_has(args, 'a') || before) {
if (target.wl != NULL)
idx = winlink_shuffle_up(dst, target.wl, before);
else
idx = winlink_shuffle_up(dst, dst->curw, before);
if (idx == -1)
return (CMD_RETURN_ERROR);
}
if (server_link_window(src, wl, dst, idx, kflag, !dflag,
&cause) != 0) {
cmdq_error(item, "can't link window: %s", cause);
if (server_link_window(src, wl, dst, idx, kflag, !dflag, &cause) != 0) {
cmdq_error(item, "%s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
if (self->entry == &cmd_move_window_entry)
if (cmd_get_entry(self) == &cmd_move_window_entry)
server_unlink_window(src, wl);
/*

View File

@@ -39,10 +39,11 @@ const struct cmd_entry cmd_new_session_entry = {
.name = "new-session",
.alias = "new",
.args = { "Ac:dDEF:n:Ps:t:x:Xy:", 0, -1 },
.usage = "[-AdDEPX] [-c start-directory] [-F format] [-n window-name] "
"[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] "
"[-y height] [command]",
.args = { "Ac:dDe:EF:f:n:Ps:t:x:Xy:", 0, -1, NULL },
.usage = "[-AdDEPX] [-c start-directory] [-e environment] [-F format] "
"[-f flags] [-n window-name] [-s session-name] "
CMD_TARGET_SESSION_USAGE " [-x width] [-y height] "
"[shell-command]",
.target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL },
@@ -54,7 +55,7 @@ const struct cmd_entry cmd_has_session_entry = {
.name = "has-session",
.alias = "has",
.args = { "t:", 0, 0 },
.args = { "t:", 0, 0, NULL },
.usage = CMD_TARGET_SESSION_USAGE,
.target = { 't', CMD_FIND_SESSION, 0 },
@@ -66,22 +67,26 @@ const struct cmd_entry cmd_has_session_entry = {
static enum cmd_retval
cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c = item->client;
struct session *s, *as, *groupwith;
struct args *args = cmd_get_args(self);
struct cmd_find_state *current = cmdq_get_current(item);
struct cmd_find_state *target = cmdq_get_target(item);
struct client *c = cmdq_get_client(item);
struct session *s, *as, *groupwith = NULL;
struct environ *env;
struct options *oo;
struct termios tio, *tiop;
struct session_group *sg;
const char *errstr, *template, *group, *prefix, *tmp;
struct session_group *sg = NULL;
const char *errstr, *template, *group, *tmp;
char *cause, *cwd = NULL, *cp, *newname = NULL;
char *name, *prefix = NULL;
int detached, already_attached, is_control = 0;
u_int sx, sy, dsx, dsy;
struct spawn_context sc;
u_int sx, sy, dsx, dsy, count = args_count(args);
struct spawn_context sc = { 0 };
enum cmd_retval retval;
struct cmd_find_state fs;
struct args_value *av;
if (self->entry == &cmd_has_session_entry) {
if (cmd_get_entry(self) == &cmd_has_session_entry) {
/*
* cmd_find_target() will fail if the session cannot be found,
* so always return success here.
@@ -89,28 +94,31 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 't') && (args->argc != 0 || args_has(args, 'n'))) {
if (args_has(args, 't') && (count != 0 || args_has(args, 'n'))) {
cmdq_error(item, "command or window name given with target");
return (CMD_RETURN_ERROR);
}
tmp = args_get(args, 's');
if (tmp != NULL) {
newname = format_single(item, tmp, c, NULL, NULL, NULL);
if (!session_check_name(newname)) {
cmdq_error(item, "bad session name: %s", newname);
goto fail;
name = format_single(item, tmp, c, NULL, NULL, NULL);
newname = session_check_name(name);
if (newname == NULL) {
cmdq_error(item, "invalid session: %s", name);
free(name);
return (CMD_RETURN_ERROR);
}
free(name);
}
if (args_has(args, 'A')) {
if (newname != NULL)
as = session_find(newname);
else
as = item->target.s;
as = target->s;
if (as != NULL) {
retval = cmd_attach_session(item, as->name,
args_has(args, 'D'), args_has(args, 'X'), 0, NULL,
args_has(args, 'E'));
args_has(args, 'E'), args_get(args, 'f'));
free(newname);
return (retval);
}
@@ -123,25 +131,23 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
/* Is this going to be part of a session group? */
group = args_get(args, 't');
if (group != NULL) {
groupwith = item->target.s;
if (groupwith == NULL) {
if (!session_check_name(group)) {
cmdq_error(item, "bad group name: %s", group);
goto fail;
}
groupwith = target->s;
if (groupwith == NULL)
sg = session_group_find(group);
} else
else
sg = session_group_contains(groupwith);
if (sg != NULL)
prefix = sg->name;
prefix = xstrdup(sg->name);
else if (groupwith != NULL)
prefix = groupwith->name;
else
prefix = group;
} else {
groupwith = NULL;
sg = NULL;
prefix = NULL;
prefix = xstrdup(groupwith->name);
else {
prefix = session_check_name(group);
if (prefix == NULL) {
cmdq_error(item, "invalid session group: %s",
group);
goto fail;
}
}
}
/* Set -d if no client. */
@@ -171,13 +177,16 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
* the terminal as that calls tcsetattr() to prepare for tmux taking
* over.
*/
if (!detached && !already_attached && c->tty.fd != -1) {
if (server_client_check_nested(item->client)) {
if (!detached &&
!already_attached &&
c->fd != -1 &&
(~c->flags & CLIENT_CONTROL)) {
if (server_client_check_nested(cmdq_get_client(item))) {
cmdq_error(item, "sessions should be nested with care, "
"unset $TMUX to force");
goto fail;
}
if (tcgetattr(c->tty.fd, &tio) != 0)
if (tcgetattr(c->fd, &tio) != 0)
fatal("tcgetattr failed");
tiop = &tio;
} else
@@ -207,7 +216,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
goto fail;
}
}
}
} else
dsx = 80;
if (args_has(args, 'y')) {
tmp = args_get(args, 'y');
if (strcmp(tmp, "-") == 0) {
@@ -222,7 +232,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
goto fail;
}
}
}
} else
dsy = 24;
/* Find new session size. */
if (!detached && !is_control) {
@@ -233,13 +244,14 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
} else {
tmp = options_get_string(global_s_options, "default-size");
if (sscanf(tmp, "%ux%u", &sx, &sy) != 2) {
sx = 80;
sy = 24;
}
if (args_has(args, 'x'))
sx = dsx;
if (args_has(args, 'y'))
sy = dsy;
} else {
if (args_has(args, 'x'))
sx = dsx;
if (args_has(args, 'y'))
sy = dsy;
}
}
if (sx == 0)
sx = 1;
@@ -258,17 +270,21 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
env = environ_create();
if (c != NULL && !args_has(args, 'E'))
environ_update(global_s_options, c->environ, env);
av = args_first_value(args, 'e');
while (av != NULL) {
environ_put(env, av->string, 0);
av = args_next_value(av);
}
s = session_create(prefix, newname, cwd, env, oo, tiop);
/* Spawn the initial window. */
memset(&sc, 0, sizeof sc);
sc.item = item;
sc.s = s;
sc.c = c;
if (!detached)
sc.tc = c;
sc.name = args_get(args, 'n');
sc.argc = args->argc;
sc.argv = args->argv;
args_to_vector(args, &sc.argc, &sc.argv);
sc.idx = -1;
sc.cwd = args_get(args, 'c');
@@ -305,23 +321,17 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
* taking this session and needs to get MSG_READY and stay around.
*/
if (!detached) {
if (args_has(args, 'f'))
server_client_set_flags(c, args_get(args, 'f'));
if (!already_attached) {
if (~c->flags & CLIENT_CONTROL)
proc_send(c->peer, MSG_READY, -1, NULL, 0);
} else if (c->session != NULL)
c->last_session = c->session;
c->session = s;
if (~item->shared->flags & CMDQ_SHARED_REPEAT)
server_client_set_session(c, s);
if (~cmdq_get_flags(item) & CMDQ_STATE_REPEAT)
server_client_set_key_table(c, NULL);
tty_update_client_offset(c);
status_timer_start(c);
notify_client("client-session-changed", c);
session_update_activity(s, NULL);
gettimeofday(&s->last_attached_time, NULL);
server_redraw_client(c);
}
recalculate_sizes();
server_update_socket();
/*
* If there are still configuration file errors to display, put the new
@@ -334,25 +344,31 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'P')) {
if ((template = args_get(args, 'F')) == NULL)
template = NEW_SESSION_TEMPLATE;
cp = format_single(item, template, c, s, NULL, NULL);
cp = format_single(item, template, c, s, s->curw, NULL);
cmdq_print(item, "%s", cp);
free(cp);
}
if (!detached) {
if (!detached)
c->flags |= CLIENT_ATTACHED;
cmd_find_from_session(&item->shared->current, s, 0);
}
if (!args_has(args, 'd'))
cmd_find_from_session(current, s, 0);
cmd_find_from_session(&fs, s, 0);
cmdq_insert_hook(s, item, &fs, "after-new-session");
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
free(cwd);
free(newname);
free(prefix);
return (CMD_RETURN_NORMAL);
fail:
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
free(cwd);
free(newname);
free(prefix);
return (CMD_RETURN_ERROR);
}

View File

@@ -38,9 +38,9 @@ const struct cmd_entry cmd_new_window_entry = {
.name = "new-window",
.alias = "neww",
.args = { "ac:de:F:kn:Pt:", 0, -1 },
.usage = "[-adkP] [-c start-directory] [-e environment] [-F format] "
"[-n window-name] " CMD_TARGET_WINDOW_USAGE " [command]",
.args = { "abc:de:F:kn:PSt:", 0, -1, NULL },
.usage = "[-abdkPS] [-c start-directory] [-e environment] [-F format] "
"[-n window-name] " CMD_TARGET_WINDOW_USAGE " [shell-command]",
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX },
@@ -51,38 +51,67 @@ const struct cmd_entry cmd_new_window_entry = {
static enum cmd_retval
cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct cmd_find_state *current = &item->shared->current;
struct spawn_context sc;
struct client *c = cmd_find_client(item, NULL, 1);
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
int idx = item->target.idx;
struct winlink *new_wl;
struct args *args = cmd_get_args(self);
struct client *c = cmdq_get_client(item);
struct cmd_find_state *current = cmdq_get_current(item);
struct cmd_find_state *target = cmdq_get_target(item);
struct spawn_context sc = { 0 };
struct client *tc = cmdq_get_target_client(item);
struct session *s = target->s;
struct winlink *wl = target->wl, *new_wl = NULL;
int idx = target->idx, before;
char *cause = NULL, *cp;
const char *template, *add;
const char *template, *name;
struct cmd_find_state fs;
struct args_value *value;
struct args_value *av;
if (args_has(args, 'a') && (idx = winlink_shuffle_up(s, wl)) == -1) {
cmdq_error(item, "couldn't get a window index");
return (CMD_RETURN_ERROR);
/*
* If -S and -n are given and -t is not and a single window with this
* name already exists, select it.
*/
name = args_get(args, 'n');
if (args_has(args, 'S') && name != NULL && target->idx == -1) {
RB_FOREACH(wl, winlinks, &s->windows) {
if (strcmp(wl->window->name, name) != 0)
continue;
if (new_wl == NULL) {
new_wl = wl;
continue;
}
cmdq_error(item, "multiple windows named %s", name);
return (CMD_RETURN_ERROR);
}
if (new_wl != NULL) {
if (args_has(args, 'd'))
return (CMD_RETURN_NORMAL);
if (session_set_current(s, new_wl) == 0)
server_redraw_session(s);
if (c != NULL && c->session != NULL)
s->curw->window->latest = c;
recalculate_sizes();
return (CMD_RETURN_NORMAL);
}
}
before = args_has(args, 'b');
if (args_has(args, 'a') || before) {
idx = winlink_shuffle_up(s, wl, before);
if (idx == -1)
idx = target->idx;
}
memset(&sc, 0, sizeof sc);
sc.item = item;
sc.s = s;
sc.c = c;
sc.tc = tc;
sc.name = args_get(args, 'n');
sc.argc = args->argc;
sc.argv = args->argv;
args_to_vector(args, &sc.argc, &sc.argv);
sc.environ = environ_create();
add = args_first_value(args, 'e', &value);
while (add != NULL) {
environ_put(sc.environ, add);
add = args_next_value(&value);
av = args_first_value(args, 'e');
while (av != NULL) {
environ_put(sc.environ, av->string, 0);
av = args_next_value(av);
}
sc.idx = idx;
@@ -97,6 +126,9 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
if ((new_wl = spawn_window(&sc, &cause)) == NULL) {
cmdq_error(item, "create window failed: %s", cause);
free(cause);
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);
return (CMD_RETURN_ERROR);
}
if (!args_has(args, 'd') || new_wl == s->curw) {
@@ -108,7 +140,8 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'P')) {
if ((template = args_get(args, 'F')) == NULL)
template = NEW_WINDOW_TEMPLATE;
cp = format_single(item, template, c, s, new_wl, NULL);
cp = format_single(item, template, tc, s, new_wl,
new_wl->window->active);
cmdq_print(item, "%s", cp);
free(cp);
}
@@ -116,6 +149,8 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
cmd_find_from_winlink(&fs, new_wl, 0);
cmdq_insert_hook(s, item, &fs, "after-new-window");
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);
return (CMD_RETURN_NORMAL);
}

File diff suppressed because it is too large Load Diff

View File

@@ -33,7 +33,7 @@ const struct cmd_entry cmd_paste_buffer_entry = {
.name = "paste-buffer",
.alias = "pasteb",
.args = { "db:prs:t:", 0, 0 },
.args = { "db:prs:t:", 0, 0, NULL },
.usage = "[-dpr] [-s separator] " CMD_BUFFER_USAGE " "
CMD_TARGET_PANE_USAGE,
@@ -46,8 +46,9 @@ const struct cmd_entry cmd_paste_buffer_entry = {
static enum cmd_retval
cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct window_pane *wp = item->target.wp;
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct window_pane *wp = target->wp;
struct paste_buffer *pb;
const char *sepstr, *bufname, *bufdata, *bufend, *line;
size_t seplen, bufsize;

View File

@@ -43,8 +43,8 @@ const struct cmd_entry cmd_pipe_pane_entry = {
.name = "pipe-pane",
.alias = "pipep",
.args = { "IOot:", 0, 1 },
.usage = "[-IOo] " CMD_TARGET_PANE_USAGE " [command]",
.args = { "IOot:", 0, 1, NULL },
.usage = "[-IOo] " CMD_TARGET_PANE_USAGE " [shell-command]",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -55,15 +55,17 @@ const struct cmd_entry cmd_pipe_pane_entry = {
static enum cmd_retval
cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c = cmd_find_client(item, NULL, 1);
struct window_pane *wp = item->target.wp;
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
char *cmd;
int old_fd, pipe_fd[2], null_fd, in, out;
struct format_tree *ft;
sigset_t set, oldset;
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct client *tc = cmdq_get_target_client(item);
struct window_pane *wp = target->wp;
struct session *s = target->s;
struct winlink *wl = target->wl;
struct window_pane_offset *wpo = &wp->pipe_offset;
char *cmd;
int old_fd, pipe_fd[2], null_fd, in, out;
struct format_tree *ft;
sigset_t set, oldset;
/* Destroy the old pipe. */
old_fd = wp->pipe_fd;
@@ -79,7 +81,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
}
/* If no pipe command, that is enough. */
if (args->argc == 0 || *args->argv[0] == '\0')
if (args_count(args) == 0 || *args_string(args, 0) == '\0')
return (CMD_RETURN_NORMAL);
/*
@@ -88,13 +90,13 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
*
* bind ^p pipep -o 'cat >>~/output'
*/
if (args_has(self->args, 'o') && old_fd != -1)
if (args_has(args, 'o') && old_fd != -1)
return (CMD_RETURN_NORMAL);
/* What do we want to do? Neither -I or -O is -O. */
if (args_has(self->args, 'I')) {
if (args_has(args, 'I')) {
in = 1;
out = args_has(self->args, 'O');
out = args_has(args, 'O');
} else {
in = 0;
out = 1;
@@ -107,9 +109,9 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
}
/* Expand the command. */
ft = format_create(item->client, item, FORMAT_NONE, 0);
format_defaults(ft, c, s, wl, wp);
cmd = format_expand_time(ft, args->argv[0]);
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_defaults(ft, tc, s, wl, wp);
cmd = format_expand_time(ft, args_string(args, 0));
format_free(ft);
/* Fork the child. */
@@ -157,10 +159,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
close(pipe_fd[1]);
wp->pipe_fd = pipe_fd[0];
if (wp->fd != -1)
wp->pipe_off = EVBUFFER_LENGTH(wp->event->input);
else
wp->pipe_off = 0;
memcpy(wpo, &wp->offset, sizeof *wpo);
setblocking(wp->pipe_fd, 0);
wp->pipe_event = bufferevent_new(wp->pipe_fd,

View File

@@ -25,8 +25,69 @@
#include "tmux.h"
/* Global command queue. */
static struct cmdq_list global_queue = TAILQ_HEAD_INITIALIZER(global_queue);
/* Command queue flags. */
#define CMDQ_FIRED 0x1
#define CMDQ_WAITING 0x2
/* Command queue item type. */
enum cmdq_type {
CMDQ_COMMAND,
CMDQ_CALLBACK,
};
/* Command queue item. */
struct cmdq_item {
char *name;
struct cmdq_list *queue;
struct cmdq_item *next;
struct client *client;
struct client *target_client;
enum cmdq_type type;
u_int group;
u_int number;
time_t time;
int flags;
struct cmdq_state *state;
struct cmd_find_state source;
struct cmd_find_state target;
struct cmd_list *cmdlist;
struct cmd *cmd;
cmdq_cb cb;
void *data;
TAILQ_ENTRY(cmdq_item) entry;
};
TAILQ_HEAD(cmdq_item_list, cmdq_item);
/*
* Command queue state. This is the context for commands on the command queue.
* It holds information about how the commands were fired (the key and flags),
* any additional formats for the commands, and the current default target.
* Multiple commands can share the same state and a command may update the
* default target.
*/
struct cmdq_state {
int references;
int flags;
struct format_tree *formats;
struct key_event event;
struct cmd_find_state current;
};
/* Command queue. */
struct cmdq_list {
struct cmdq_item *item;
struct cmdq_item_list list;
};
/* Get command queue name. */
static const char *
@@ -47,9 +108,188 @@ cmdq_name(struct client *c)
static struct cmdq_list *
cmdq_get(struct client *c)
{
if (c == NULL)
return (&global_queue);
return (&c->queue);
static struct cmdq_list *global_queue;
if (c == NULL) {
if (global_queue == NULL)
global_queue = cmdq_new();
return (global_queue);
}
return (c->queue);
}
/* Create a queue. */
struct cmdq_list *
cmdq_new(void)
{
struct cmdq_list *queue;
queue = xcalloc (1, sizeof *queue);
TAILQ_INIT (&queue->list);
return (queue);
}
/* Free a queue. */
void
cmdq_free(struct cmdq_list *queue)
{
if (!TAILQ_EMPTY(&queue->list))
fatalx("queue not empty");
free(queue);
}
/* Get item name. */
const char *
cmdq_get_name(struct cmdq_item *item)
{
return (item->name);
}
/* Get item client. */
struct client *
cmdq_get_client(struct cmdq_item *item)
{
return (item->client);
}
/* Get item target client. */
struct client *
cmdq_get_target_client(struct cmdq_item *item)
{
return (item->target_client);
}
/* Get item state. */
struct cmdq_state *
cmdq_get_state(struct cmdq_item *item)
{
return (item->state);
}
/* Get item target. */
struct cmd_find_state *
cmdq_get_target(struct cmdq_item *item)
{
return (&item->target);
}
/* Get item source. */
struct cmd_find_state *
cmdq_get_source(struct cmdq_item *item)
{
return (&item->source);
}
/* Get state event. */
struct key_event *
cmdq_get_event(struct cmdq_item *item)
{
return (&item->state->event);
}
/* Get state current target. */
struct cmd_find_state *
cmdq_get_current(struct cmdq_item *item)
{
return (&item->state->current);
}
/* Get state flags. */
int
cmdq_get_flags(struct cmdq_item *item)
{
return (item->state->flags);
}
/* Create a new state. */
struct cmdq_state *
cmdq_new_state(struct cmd_find_state *current, struct key_event *event,
int flags)
{
struct cmdq_state *state;
state = xcalloc(1, sizeof *state);
state->references = 1;
state->flags = flags;
if (event != NULL)
memcpy(&state->event, event, sizeof state->event);
else
state->event.key = KEYC_NONE;
if (current != NULL && cmd_find_valid_state(current))
cmd_find_copy_state(&state->current, current);
else
cmd_find_clear_state(&state->current, 0);
return (state);
}
/* Add a reference to a state. */
struct cmdq_state *
cmdq_link_state(struct cmdq_state *state)
{
state->references++;
return (state);
}
/* Make a copy of a state. */
struct cmdq_state *
cmdq_copy_state(struct cmdq_state *state)
{
return (cmdq_new_state(&state->current, &state->event, state->flags));
}
/* Free a state. */
void
cmdq_free_state(struct cmdq_state *state)
{
if (--state->references != 0)
return;
if (state->formats != NULL)
format_free(state->formats);
free(state);
}
/* Add a format to command queue. */
void
cmdq_add_format(struct cmdq_state *state, const char *key, const char *fmt, ...)
{
va_list ap;
char *value;
va_start(ap, fmt);
xvasprintf(&value, fmt, ap);
va_end(ap);
if (state->formats == NULL)
state->formats = format_create(NULL, NULL, FORMAT_NONE, 0);
format_add(state->formats, key, "%s", value);
free(value);
}
/* Add formats to command queue. */
void
cmdq_add_formats(struct cmdq_state *state, struct format_tree *ft)
{
if (state->formats == NULL)
state->formats = format_create(NULL, NULL, FORMAT_NONE, 0);
format_merge(state->formats, ft);
}
/* Merge formats from item. */
void
cmdq_merge_formats(struct cmdq_item *item, struct format_tree *ft)
{
const struct cmd_entry *entry;
if (item->cmd != NULL) {
entry = cmd_get_entry(item->cmd);
format_add(ft, "command", "%s", entry->name);
}
if (item->state->formats != NULL)
format_merge(ft, item->state->formats);
}
/* Append an item. */
@@ -59,10 +299,6 @@ cmdq_append(struct client *c, struct cmdq_item *item)
struct cmdq_list *queue = cmdq_get(c);
struct cmdq_item *next;
TAILQ_FOREACH(next, queue, entry) {
log_debug("%s %s: queue %s (%u)", __func__, cmdq_name(c),
next->name, next->group);
}
do {
next = item->next;
item->next = NULL;
@@ -72,12 +308,12 @@ cmdq_append(struct client *c, struct cmdq_item *item)
item->client = c;
item->queue = queue;
TAILQ_INSERT_TAIL(queue, item, entry);
TAILQ_INSERT_TAIL(&queue->list, item, entry);
log_debug("%s %s: %s", __func__, cmdq_name(c), item->name);
item = next;
} while (item != NULL);
return (TAILQ_LAST(queue, cmdq_list));
return (TAILQ_LAST(&queue->list, cmdq_item_list));
}
/* Insert an item. */
@@ -88,10 +324,6 @@ cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item)
struct cmdq_list *queue = after->queue;
struct cmdq_item *next;
TAILQ_FOREACH(next, queue, entry) {
log_debug("%s %s: queue %s (%u)", __func__, cmdq_name(c),
next->name, next->group);
}
do {
next = item->next;
item->next = after->next;
@@ -102,7 +334,7 @@ cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item)
item->client = c;
item->queue = queue;
TAILQ_INSERT_AFTER(queue, after, item, entry);
TAILQ_INSERT_AFTER(&queue->list, after, item, entry);
log_debug("%s %s: %s after %s", __func__, cmdq_name(c),
item->name, after->name);
@@ -115,17 +347,25 @@ cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item)
/* Insert a hook. */
void
cmdq_insert_hook(struct session *s, struct cmdq_item *item,
struct cmd_find_state *fs, const char *fmt, ...)
struct cmd_find_state *current, const char *fmt, ...)
{
struct cmdq_state *state = item->state;
struct cmd *cmd = item->cmd;
struct args *args = cmd_get_args(cmd);
struct args_entry *ae;
struct args_value *av;
struct options *oo;
va_list ap;
char *name;
char *name, tmp[32], flag, *arguments;
u_int i;
const char *value;
struct cmdq_item *new_item;
struct cmdq_state *new_state;
struct options_entry *o;
struct options_array_item *a;
struct cmd_list *cmdlist;
if (item->flags & CMDQ_NOHOOKS)
if (item->state->flags & CMDQ_STATE_NOHOOKS)
return;
if (s == NULL)
oo = global_s_options;
@@ -143,24 +383,58 @@ cmdq_insert_hook(struct session *s, struct cmdq_item *item,
}
log_debug("running hook %s (parent %p)", name, item);
/*
* The hooks get a new state because they should not update the current
* target or formats for any subsequent commands.
*/
new_state = cmdq_new_state(current, &state->event, CMDQ_STATE_NOHOOKS);
cmdq_add_format(new_state, "hook", "%s", name);
arguments = args_print(args);
cmdq_add_format(new_state, "hook_arguments", "%s", arguments);
free(arguments);
for (i = 0; i < args_count(args); i++) {
xsnprintf(tmp, sizeof tmp, "hook_argument_%d", i);
cmdq_add_format(new_state, tmp, "%s", args_string(args, i));
}
flag = args_first(args, &ae);
while (flag != 0) {
value = args_get(args, flag);
if (value == NULL) {
xsnprintf(tmp, sizeof tmp, "hook_flag_%c", flag);
cmdq_add_format(new_state, tmp, "1");
} else {
xsnprintf(tmp, sizeof tmp, "hook_flag_%c", flag);
cmdq_add_format(new_state, tmp, "%s", value);
}
i = 0;
av = args_first_value(args, flag);
while (av != NULL) {
xsnprintf(tmp, sizeof tmp, "hook_flag_%c_%d", flag, i);
cmdq_add_format(new_state, tmp, "%s", av->string);
i++;
av = args_next_value(av);
}
flag = args_next(&ae);
}
a = options_array_first(o);
while (a != NULL) {
cmdlist = options_array_item_value(a)->cmdlist;
if (cmdlist == NULL) {
a = options_array_next(a);
continue;
if (cmdlist != NULL) {
new_item = cmdq_get_command(cmdlist, new_state);
if (item != NULL)
item = cmdq_insert_after(item, new_item);
else
item = cmdq_append(NULL, new_item);
}
new_item = cmdq_get_command(cmdlist, fs, NULL, CMDQ_NOHOOKS);
cmdq_format(new_item, "hook", "%s", name);
if (item != NULL)
item = cmdq_insert_after(item, new_item);
else
item = cmdq_append(NULL, new_item);
a = options_array_next(a);
}
cmdq_free_state(new_state);
free(name);
}
@@ -175,19 +449,13 @@ cmdq_continue(struct cmdq_item *item)
static void
cmdq_remove(struct cmdq_item *item)
{
if (item->shared != NULL && --item->shared->references == 0) {
if (item->shared->formats != NULL)
format_free(item->shared->formats);
free(item->shared);
}
if (item->client != NULL)
server_client_unref(item->client);
if (item->cmdlist != NULL)
cmd_list_free(item->cmdlist);
cmdq_free_state(item->state);
TAILQ_REMOVE(item->queue, item, entry);
TAILQ_REMOVE(&item->queue->list, item, entry);
free(item->name);
free(item);
@@ -210,50 +478,57 @@ cmdq_remove_group(struct cmdq_item *item)
}
}
/* Empty command callback. */
static enum cmd_retval
cmdq_empty_command(__unused struct cmdq_item *item, __unused void *data)
{
return (CMD_RETURN_NORMAL);
}
/* Get a command for the command queue. */
struct cmdq_item *
cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current,
struct mouse_event *m, int flags)
cmdq_get_command(struct cmd_list *cmdlist, struct cmdq_state *state)
{
struct cmdq_item *item, *first = NULL, *last = NULL;
struct cmd *cmd;
struct cmdq_shared *shared = NULL;
u_int group = 0;
const struct cmd_entry *entry;
int created = 0;
TAILQ_FOREACH(cmd, &cmdlist->list, qentry) {
if (cmd->group != group) {
shared = xcalloc(1, sizeof *shared);
if (current != NULL)
cmd_find_copy_state(&shared->current, current);
else
cmd_find_clear_state(&shared->current, 0);
if (m != NULL)
memcpy(&shared->mouse, m, sizeof shared->mouse);
group = cmd->group;
}
if ((cmd = cmd_list_first(cmdlist)) == NULL)
return (cmdq_get_callback(cmdq_empty_command, NULL));
if (state == NULL) {
state = cmdq_new_state(NULL, NULL, 0);
created = 1;
}
while (cmd != NULL) {
entry = cmd_get_entry(cmd);
item = xcalloc(1, sizeof *item);
xasprintf(&item->name, "[%s/%p]", cmd->entry->name, item);
xasprintf(&item->name, "[%s/%p]", entry->name, item);
item->type = CMDQ_COMMAND;
item->group = cmd->group;
item->flags = flags;
item->group = cmd_get_group(cmd);
item->state = cmdq_link_state(state);
item->shared = shared;
item->cmdlist = cmdlist;
item->cmd = cmd;
log_debug("%s: %s group %u", __func__, item->name, item->group);
shared->references++;
cmdlist->references++;
log_debug("%s: %s group %u", __func__, item->name, item->group);
if (first == NULL)
first = item;
if (last != NULL)
last->next = item;
last = item;
cmd = cmd_list_next(cmd);
}
if (created)
cmdq_free_state(state);
return (first);
}
@@ -265,11 +540,11 @@ cmdq_find_flag(struct cmdq_item *item, struct cmd_find_state *fs,
const char *value;
if (flag->flag == 0) {
cmd_find_clear_state(fs, 0);
cmd_find_from_client(fs, item->target_client, 0);
return (CMD_RETURN_NORMAL);
}
value = args_get(item->cmd->args, flag->flag);
value = args_get(cmd_get_args(item->cmd), flag->flag);
if (cmd_find_target(fs, item, value, flag->type, flag->flags) != 0) {
cmd_find_clear_state(fs, 0);
return (CMD_RETURN_ERROR);
@@ -277,31 +552,75 @@ cmdq_find_flag(struct cmdq_item *item, struct cmd_find_state *fs,
return (CMD_RETURN_NORMAL);
}
/* Add message with command. */
static void
cmdq_add_message(struct cmdq_item *item)
{
struct client *c = item->client;
struct cmdq_state *state = item->state;
const char *name, *key;
char *tmp;
tmp = cmd_print(item->cmd);
if (c != NULL) {
name = c->name;
if (c->session != NULL && state->event.key != KEYC_NONE) {
key = key_string_lookup_key(state->event.key, 0);
server_add_message("%s key %s: %s", name, key, tmp);
} else
server_add_message("%s command: %s", name, tmp);
} else
server_add_message("command: %s", tmp);
free(tmp);
}
/* Fire command on command queue. */
static enum cmd_retval
cmdq_fire_command(struct cmdq_item *item)
{
struct client *c = item->client;
const char *name = cmdq_name(c);
struct cmdq_shared *shared = item->shared;
const char *name = cmdq_name(item->client);
struct cmdq_state *state = item->state;
struct cmd *cmd = item->cmd;
const struct cmd_entry *entry = cmd->entry;
struct args *args = cmd_get_args(cmd);
const struct cmd_entry *entry = cmd_get_entry(cmd);
struct client *tc, *saved = item->client;
enum cmd_retval retval;
struct cmd_find_state *fsp, fs;
int flags;
int flags, quiet = 0;
char *tmp;
if (cfg_finished)
cmdq_add_message(item);
if (log_get_level() > 1) {
tmp = cmd_print(cmd);
log_debug("%s %s: (%u) %s", __func__, name, item->group, tmp);
free(tmp);
}
flags = !!(shared->flags & CMDQ_SHARED_CONTROL);
flags = !!(state->flags & CMDQ_STATE_CONTROL);
cmdq_guard(item, "begin", flags);
if (item->client == NULL)
item->client = cmd_find_client(item, NULL, 1);
if (entry->flags & CMD_CLIENT_CANFAIL)
quiet = 1;
if (entry->flags & CMD_CLIENT_CFLAG) {
tc = cmd_find_client(item, args_get(args, 'c'), quiet);
if (tc == NULL && !quiet) {
retval = CMD_RETURN_ERROR;
goto out;
}
} else if (entry->flags & CMD_CLIENT_TFLAG) {
tc = cmd_find_client(item, args_get(args, 't'), quiet);
if (tc == NULL && !quiet) {
retval = CMD_RETURN_ERROR;
goto out;
}
} else
tc = cmd_find_client(item, NULL, 1);
item->target_client = tc;
retval = cmdq_find_flag(item, &item->source, &entry->source);
if (retval == CMD_RETURN_ERROR)
goto out;
@@ -316,8 +635,8 @@ cmdq_fire_command(struct cmdq_item *item)
if (entry->flags & CMD_AFTERHOOK) {
if (cmd_find_valid_state(&item->target))
fsp = &item->target;
else if (cmd_find_valid_state(&item->shared->current))
fsp = &item->shared->current;
else if (cmd_find_valid_state(&item->state->current))
fsp = &item->state->current;
else if (cmd_find_from_client(&fs, item->client, 0) == 0)
fsp = &fs;
else
@@ -326,7 +645,7 @@ cmdq_fire_command(struct cmdq_item *item)
}
out:
item->client = c;
item->client = saved;
if (retval == CMD_RETURN_ERROR)
cmdq_guard(item, "error", flags);
else
@@ -345,7 +664,7 @@ cmdq_get_callback1(const char *name, cmdq_cb cb, void *data)
item->type = CMDQ_CALLBACK;
item->group = 0;
item->flags = 0;
item->state = cmdq_new_state(NULL, NULL, 0);
item->cb = cb;
item->data = data;
@@ -379,25 +698,6 @@ cmdq_fire_callback(struct cmdq_item *item)
return (item->cb(item, item->data));
}
/* Add a format to command queue. */
void
cmdq_format(struct cmdq_item *item, const char *key, const char *fmt, ...)
{
struct cmdq_shared *shared = item->shared;
va_list ap;
char *value;
va_start(ap, fmt);
xvasprintf(&value, fmt, ap);
va_end(ap);
if (shared->formats == NULL)
shared->formats = format_create(NULL, NULL, FORMAT_NONE, 0);
format_add(shared->formats, key, "%s", value);
free(value);
}
/* Process next item on command queue. */
u_int
cmdq_next(struct client *c)
@@ -409,18 +709,18 @@ cmdq_next(struct client *c)
u_int items = 0;
static u_int number;
if (TAILQ_EMPTY(queue)) {
if (TAILQ_EMPTY(&queue->list)) {
log_debug("%s %s: empty", __func__, name);
return (0);
}
if (TAILQ_FIRST(queue)->flags & CMDQ_WAITING) {
if (TAILQ_FIRST(&queue->list)->flags & CMDQ_WAITING) {
log_debug("%s %s: waiting", __func__, name);
return (0);
}
log_debug("%s %s: enter", __func__, name);
for (;;) {
item = TAILQ_FIRST(queue);
item = queue->item = TAILQ_FIRST(&queue->list);
if (item == NULL)
break;
log_debug("%s %s: %s (%d), flags %x", __func__, name,
@@ -470,6 +770,7 @@ cmdq_next(struct client *c)
}
cmdq_remove(item);
}
queue->item = NULL;
log_debug("%s %s: exit (empty)", __func__, name);
return (items);
@@ -479,6 +780,19 @@ waiting:
return (items);
}
/* Get running item if any. */
struct cmdq_item *
cmdq_running(struct client *c)
{
struct cmdq_list *queue = cmdq_get(c);
if (queue->item == NULL)
return (NULL);
if (queue->item->flags & CMDQ_WAITING)
return (NULL);
return (queue->item);
}
/* Print a guard line. */
void
cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
@@ -488,7 +802,7 @@ cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
u_int number = item->number;
if (c != NULL && (c->flags & CLIENT_CONTROL))
file_print(c, "%%%s %ld %u %d\n", guard, t, number, flags);
control_write(c, "%%%s %ld %u %d", guard, t, number, flags);
}
/* Show message from command. */
@@ -515,12 +829,17 @@ cmdq_print(struct cmdq_item *item, const char *fmt, ...)
msg = utf8_sanitize(tmp);
free(tmp);
}
file_print(c, "%s\n", msg);
if (c->flags & CLIENT_CONTROL)
control_write(c, "%s", msg);
else
file_print(c, "%s\n", msg);
} else {
wp = c->session->curw->window->active;
wp = server_client_get_pane(c);
wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode != &window_view_mode)
window_pane_set_mode(wp, &window_view_mode, NULL, NULL);
if (wme == NULL || wme->mode != &window_view_mode) {
window_pane_set_mode(wp, NULL, &window_view_mode, NULL,
NULL);
}
window_copy_add(wp, "%s", msg);
}
@@ -534,8 +853,9 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...)
struct client *c = item->client;
struct cmd *cmd = item->cmd;
va_list ap;
char *msg;
char *tmp;
char *msg, *tmp;
const char *file;
u_int line;
va_start(ap, fmt);
xvasprintf(&msg, fmt, ap);
@@ -543,22 +863,24 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...)
log_debug("%s: %s", __func__, msg);
if (c == NULL)
cfg_add_cause("%s:%u: %s", cmd->file, cmd->line, msg);
else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
if (c == NULL) {
cmd_get_source(cmd, &file, &line);
cfg_add_cause("%s:%u: %s", file, line, msg);
} else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
server_add_message("%s message: %s", c->name, msg);
if (~c->flags & CLIENT_UTF8) {
tmp = msg;
msg = utf8_sanitize(tmp);
free(tmp);
}
if (c->flags & CLIENT_CONTROL)
file_print(c, "%s\n", msg);
control_write(c, "%s", msg);
else
file_error(c, "%s\n", msg);
c->retval = 1;
} else {
*msg = toupper((u_char) *msg);
status_message_set(c, "%s", msg);
status_message_set(c, -1, 1, 0, "%s", msg);
}
free(msg);

View File

@@ -34,28 +34,144 @@ const struct cmd_entry cmd_refresh_client_entry = {
.name = "refresh-client",
.alias = "refresh",
.args = { "cC:DF:lLRSt:U", 0, 1 },
.usage = "[-cDlLRSU] [-C XxY] [-F flags] " CMD_TARGET_CLIENT_USAGE
" [adjustment]",
.args = { "A:B:cC:Df:F:lLRSt:U", 0, 1, NULL },
.usage = "[-cDlLRSU] [-A pane:state] [-B name:what:format] "
"[-C XxY] [-f flags] " CMD_TARGET_CLIENT_USAGE " [adjustment]",
.flags = CMD_AFTERHOOK,
.flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG,
.exec = cmd_refresh_client_exec
};
static void
cmd_refresh_client_update_subscription(struct client *tc, const char *value)
{
char *copy, *split, *name, *what;
enum control_sub_type subtype;
int subid = -1;
copy = name = xstrdup(value);
if ((split = strchr(copy, ':')) == NULL) {
control_remove_sub(tc, copy);
goto out;
}
*split++ = '\0';
what = split;
if ((split = strchr(what, ':')) == NULL)
goto out;
*split++ = '\0';
if (strcmp(what, "%*") == 0)
subtype = CONTROL_SUB_ALL_PANES;
else if (sscanf(what, "%%%d", &subid) == 1 && subid >= 0)
subtype = CONTROL_SUB_PANE;
else if (strcmp(what, "@*") == 0)
subtype = CONTROL_SUB_ALL_WINDOWS;
else if (sscanf(what, "@%d", &subid) == 1 && subid >= 0)
subtype = CONTROL_SUB_WINDOW;
else
subtype = CONTROL_SUB_SESSION;
control_add_sub(tc, name, subtype, subid, split);
out:
free(copy);
}
static enum cmd_retval
cmd_refresh_client_control_client_size(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct client *tc = cmdq_get_target_client(item);
const char *size = args_get(args, 'C');
u_int w, x, y;
struct client_window *cw;
if (sscanf(size, "@%u:%ux%u", &w, &x, &y) == 3) {
if (x < WINDOW_MINIMUM || x > WINDOW_MAXIMUM ||
y < WINDOW_MINIMUM || y > WINDOW_MAXIMUM) {
cmdq_error(item, "size too small or too big");
return (CMD_RETURN_ERROR);
}
log_debug("%s: client %s window @%u: size %ux%u", __func__,
tc->name, w, x, y);
cw = server_client_add_client_window(tc, w);
cw->sx = x;
cw->sy = y;
tc->flags |= CLIENT_WINDOWSIZECHANGED;
recalculate_sizes_now(1);
return (CMD_RETURN_NORMAL);
}
if (sscanf(size, "@%u:", &w) == 1) {
cw = server_client_get_client_window(tc, w);
if (cw != NULL) {
log_debug("%s: client %s window @%u: no size", __func__,
tc->name, w);
cw->sx = 0;
cw->sy = 0;
recalculate_sizes_now(1);
}
return (CMD_RETURN_NORMAL);
}
if (sscanf(size, "%u,%u", &x, &y) != 2 &&
sscanf(size, "%ux%u", &x, &y) != 2) {
cmdq_error(item, "bad size argument");
return (CMD_RETURN_ERROR);
}
if (x < WINDOW_MINIMUM || x > WINDOW_MAXIMUM ||
y < WINDOW_MINIMUM || y > WINDOW_MAXIMUM) {
cmdq_error(item, "size too small or too big");
return (CMD_RETURN_ERROR);
}
tty_set_size(&tc->tty, x, y, 0, 0);
tc->flags |= CLIENT_SIZECHANGED;
recalculate_sizes_now(1);
return (CMD_RETURN_NORMAL);
}
static void
cmd_refresh_client_update_offset(struct client *tc, const char *value)
{
struct window_pane *wp;
char *copy, *split;
u_int pane;
if (*value != '%')
return;
copy = xstrdup(value);
if ((split = strchr(copy, ':')) == NULL)
goto out;
*split++ = '\0';
if (sscanf(copy, "%%%u", &pane) != 1)
goto out;
wp = window_pane_find_by_id(pane);
if (wp == NULL)
goto out;
if (strcmp(split, "on") == 0)
control_set_pane_on(tc, wp);
else if (strcmp(split, "off") == 0)
control_set_pane_off(tc, wp);
else if (strcmp(split, "continue") == 0)
control_continue_pane(tc, wp);
else if (strcmp(split, "pause") == 0)
control_pause_pane(tc, wp);
out:
free(copy);
}
static enum cmd_retval
cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c;
struct tty *tty;
struct window *w;
const char *size, *errstr;
char *copy, *next, *s;
u_int x, y, adjust;
if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL)
return (CMD_RETURN_ERROR);
tty = &c->tty;
struct args *args = cmd_get_args(self);
struct client *tc = cmdq_get_target_client(item);
struct tty *tty = &tc->tty;
struct window *w;
const char *errstr;
u_int adjust;
struct args_value *av;
if (args_has(args, 'c') ||
args_has(args, 'L') ||
@@ -63,10 +179,11 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item)
args_has(args, 'U') ||
args_has(args, 'D'))
{
if (args->argc == 0)
if (args_count(args) == 0)
adjust = 1;
else {
adjust = strtonum(args->argv[0], 1, INT_MAX, &errstr);
adjust = strtonum(args_string(args, 0), 1, INT_MAX,
&errstr);
if (errstr != NULL) {
cmdq_error(item, "adjustment %s", errstr);
return (CMD_RETURN_ERROR);
@@ -74,88 +191,85 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item)
}
if (args_has(args, 'c'))
c->pan_window = NULL;
tc->pan_window = NULL;
else {
w = c->session->curw->window;
if (c->pan_window != w) {
c->pan_window = w;
c->pan_ox = tty->oox;
c->pan_oy = tty->ooy;
w = tc->session->curw->window;
if (tc->pan_window != w) {
tc->pan_window = w;
tc->pan_ox = tty->oox;
tc->pan_oy = tty->ooy;
}
if (args_has(args, 'L')) {
if (c->pan_ox > adjust)
c->pan_ox -= adjust;
if (tc->pan_ox > adjust)
tc->pan_ox -= adjust;
else
c->pan_ox = 0;
tc->pan_ox = 0;
} else if (args_has(args, 'R')) {
c->pan_ox += adjust;
if (c->pan_ox > w->sx - tty->osx)
c->pan_ox = w->sx - tty->osx;
tc->pan_ox += adjust;
if (tc->pan_ox > w->sx - tty->osx)
tc->pan_ox = w->sx - tty->osx;
} else if (args_has(args, 'U')) {
if (c->pan_oy > adjust)
c->pan_oy -= adjust;
if (tc->pan_oy > adjust)
tc->pan_oy -= adjust;
else
c->pan_oy = 0;
tc->pan_oy = 0;
} else if (args_has(args, 'D')) {
c->pan_oy += adjust;
if (c->pan_oy > w->sy - tty->osy)
c->pan_oy = w->sy - tty->osy;
tc->pan_oy += adjust;
if (tc->pan_oy > w->sy - tty->osy)
tc->pan_oy = w->sy - tty->osy;
}
}
tty_update_client_offset(c);
server_redraw_client(c);
tty_update_client_offset(tc);
server_redraw_client(tc);
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'l')) {
if (c->session != NULL)
tty_putcode_ptr2(&c->tty, TTYC_MS, "", "?");
tty_putcode_ptr2(&tc->tty, TTYC_MS, "", "?");
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'C') || args_has(args, 'F')) {
if (args_has(args, 'C')) {
if (!(c->flags & CLIENT_CONTROL)) {
cmdq_error(item, "not a control client");
return (CMD_RETURN_ERROR);
}
size = args_get(args, 'C');
if (sscanf(size, "%u,%u", &x, &y) != 2 &&
sscanf(size, "%ux%u", &x, &y) != 2) {
cmdq_error(item, "bad size argument");
return (CMD_RETURN_ERROR);
}
if (x < WINDOW_MINIMUM || x > WINDOW_MAXIMUM ||
y < WINDOW_MINIMUM || y > WINDOW_MAXIMUM) {
cmdq_error(item, "size too small or too big");
return (CMD_RETURN_ERROR);
}
tty_set_size(&c->tty, x, y, 0, 0);
c->flags |= CLIENT_SIZECHANGED;
recalculate_sizes();
}
if (args_has(args, 'F')) {
if (!(c->flags & CLIENT_CONTROL)) {
cmdq_error(item, "not a control client");
return (CMD_RETURN_ERROR);
}
s = copy = xstrdup(args_get(args, 'F'));
while ((next = strsep(&s, ",")) != NULL) {
/* Unknown flags are ignored. */
if (strcmp(next, "no-output") == 0)
c->flags |= CLIENT_CONTROL_NOOUTPUT;
}
free(copy);
if (args_has(args, 'F')) /* -F is an alias for -f */
server_client_set_flags(tc, args_get(args, 'F'));
if (args_has(args, 'f'))
server_client_set_flags(tc, args_get(args, 'f'));
if (args_has(args, 'A')) {
if (~tc->flags & CLIENT_CONTROL)
goto not_control_client;
av = args_first_value(args, 'A');
while (av != NULL) {
cmd_refresh_client_update_offset(tc, av->string);
av = args_next_value(av);
}
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'B')) {
if (~tc->flags & CLIENT_CONTROL)
goto not_control_client;
av = args_first_value(args, 'B');
while (av != NULL) {
cmd_refresh_client_update_subscription(tc, av->string);
av = args_next_value(av);
}
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'C')) {
if (~tc->flags & CLIENT_CONTROL)
goto not_control_client;
return (cmd_refresh_client_control_client_size(self, item));
}
if (args_has(args, 'S')) {
c->flags |= CLIENT_STATUSFORCE;
server_status_client(c);
tc->flags |= CLIENT_STATUSFORCE;
server_status_client(tc);
} else {
c->flags |= CLIENT_STATUSFORCE;
server_redraw_client(c);
tc->flags |= CLIENT_STATUSFORCE;
server_redraw_client(tc);
}
return (CMD_RETURN_NORMAL);
not_control_client:
cmdq_error(item, "not a control client");
return (CMD_RETURN_ERROR);
}

View File

@@ -34,7 +34,7 @@ const struct cmd_entry cmd_rename_session_entry = {
.name = "rename-session",
.alias = "rename",
.args = { "t:", 1, 1 },
.args = { "t:", 1, 1, NULL },
.usage = CMD_TARGET_SESSION_USAGE " new-name",
.target = { 't', CMD_FIND_SESSION, 0 },
@@ -46,22 +46,23 @@ const struct cmd_entry cmd_rename_session_entry = {
static enum cmd_retval
cmd_rename_session_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c = cmd_find_client(item, NULL, 1);
struct session *s = item->target.s;
char *newname;
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct session *s = target->s;
char *newname, *tmp;
newname = format_single(item, args->argv[0], c, s, NULL, NULL);
tmp = format_single_from_target(item, args_string(args, 0));
newname = session_check_name(tmp);
if (newname == NULL) {
cmdq_error(item, "invalid session: %s", tmp);
free(tmp);
return (CMD_RETURN_ERROR);
}
free(tmp);
if (strcmp(newname, s->name) == 0) {
free(newname);
return (CMD_RETURN_NORMAL);
}
if (!session_check_name(newname)) {
cmdq_error(item, "bad session name: %s", newname);
free(newname);
return (CMD_RETURN_ERROR);
}
if (session_find(newname) != NULL) {
cmdq_error(item, "duplicate session: %s", newname);
free(newname);

View File

@@ -33,7 +33,7 @@ const struct cmd_entry cmd_rename_window_entry = {
.name = "rename-window",
.alias = "renamew",
.args = { "t:", 1, 1 },
.args = { "t:", 1, 1, NULL },
.usage = CMD_TARGET_WINDOW_USAGE " new-name",
.target = { 't', CMD_FIND_WINDOW, 0 },
@@ -45,16 +45,16 @@ const struct cmd_entry cmd_rename_window_entry = {
static enum cmd_retval
cmd_rename_window_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c = cmd_find_client(item, NULL, 1);
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
char *newname;
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct winlink *wl = target->wl;
char *newname;
newname = format_single(item, args->argv[0], c, s, wl, NULL);
newname = format_single_from_target(item, args_string(args, 0));
window_set_name(wl->window, newname);
options_set_number(wl->window->options, "automatic-rename", 0);
server_redraw_window_borders(wl->window);
server_status_window(wl->window);
free(newname);

View File

@@ -36,8 +36,8 @@ 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 " "
.args = { "DLMRTt:Ux:y:Z", 0, 1, NULL },
.usage = "[-DLMRTUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE " "
"[adjustment]",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -49,26 +49,39 @@ const struct cmd_entry cmd_resize_pane_entry = {
static enum cmd_retval
cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct cmdq_shared *shared = item->shared;
struct window_pane *wp = item->target.wp;
struct winlink *wl = item->target.wl;
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct key_event *event = cmdq_get_event(item);
struct window_pane *wp = target->wp;
struct winlink *wl = target->wl;
struct window *w = wl->window;
struct client *c = item->client;
struct session *s = item->target.s;
const char *errstr, *p;
char *cause, *copy;
struct client *c = cmdq_get_client(item);
struct session *s = target->s;
const char *errstr;
char *cause;
u_int adjust;
int x, y, percentage;
size_t plen;
int x, y;
struct grid *gd = wp->base.grid;
if (args_has(args, 'T')) {
if (!TAILQ_EMPTY(&wp->modes))
return (CMD_RETURN_NORMAL);
adjust = screen_size_y(&wp->base) - 1 - wp->base.cy;
if (adjust > gd->hsize)
adjust = gd->hsize;
grid_remove_history(gd, adjust);
wp->base.cy += adjust;
wp->flags |= PANE_REDRAW;
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'M')) {
if (cmd_mouse_window(&shared->mouse, &s) == NULL)
if (!event->m.valid || cmd_mouse_window(&event->m, &s) == NULL)
return (CMD_RETURN_NORMAL);
if (c == NULL || c->session != s)
return (CMD_RETURN_NORMAL);
c->tty.mouse_drag_update = cmd_resize_pane_mouse_update;
cmd_resize_pane_mouse_update(c, &shared->mouse);
cmd_resize_pane_mouse_update(c, &event->m);
return (CMD_RETURN_NORMAL);
}
@@ -78,73 +91,35 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
else
window_zoom(wp);
server_redraw_window(w);
server_status_window(w);
return (CMD_RETURN_NORMAL);
}
server_unzoom_window(w);
if (args->argc == 0)
if (args_count(args) == 0)
adjust = 1;
else {
adjust = strtonum(args->argv[0], 1, INT_MAX, &errstr);
adjust = strtonum(args_string(args, 0), 1, INT_MAX, &errstr);
if (errstr != NULL) {
cmdq_error(item, "adjustment %s", errstr);
return (CMD_RETURN_ERROR);
}
}
if ((p = args_get(args, 'x')) != NULL) {
plen = strlen(p);
if (p[plen - 1] == '%') {
copy = xstrdup(p);
copy[plen - 1] = '\0';
percentage = strtonum(copy, 0, INT_MAX, &errstr);
free(copy);
if (errstr != NULL) {
cmdq_error(item, "width %s", errstr);
return (CMD_RETURN_ERROR);
}
x = (w->sx * percentage) / 100;
if (x < PANE_MINIMUM)
x = PANE_MINIMUM;
if (x > INT_MAX)
x = INT_MAX;
} else {
x = args_strtonum(args, 'x', PANE_MINIMUM, INT_MAX,
&cause);
if (cause != NULL) {
cmdq_error(item, "width %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'x')) {
x = args_percentage(args, 'x', 0, INT_MAX, w->sx, &cause);
if (cause != NULL) {
cmdq_error(item, "width %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
layout_resize_pane_to(wp, LAYOUT_LEFTRIGHT, x);
}
if ((p = args_get(args, 'y')) != NULL) {
plen = strlen(p);
if (p[plen - 1] == '%') {
copy = xstrdup(p);
copy[plen - 1] = '\0';
percentage = strtonum(copy, 0, INT_MAX, &errstr);
free(copy);
if (errstr != NULL) {
cmdq_error(item, "height %s", errstr);
return (CMD_RETURN_ERROR);
}
y = (w->sy * percentage) / 100;
if (y < PANE_MINIMUM)
y = PANE_MINIMUM;
if (y > INT_MAX)
y = INT_MAX;
}
else {
y = args_strtonum(args, 'y', PANE_MINIMUM, INT_MAX,
&cause);
if (cause != NULL) {
cmdq_error(item, "height %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'y')) {
y = args_percentage(args, 'y', 0, INT_MAX, w->sy, &cause);
if (cause != NULL) {
cmdq_error(item, "height %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
layout_resize_pane_to(wp, LAYOUT_TOPBOTTOM, y);
}

View File

@@ -33,7 +33,7 @@ const struct cmd_entry cmd_resize_window_entry = {
.name = "resize-window",
.alias = "resizew",
.args = { "aADLRt:Ux:y:", 0, 1 },
.args = { "aADLRt:Ux:y:", 0, 1, NULL },
.usage = "[-aADLRU] [-x width] [-y height] " CMD_TARGET_WINDOW_USAGE " "
"[adjustment]",
@@ -46,19 +46,20 @@ const struct cmd_entry cmd_resize_window_entry = {
static enum cmd_retval
cmd_resize_window_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct winlink *wl = item->target.wl;
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct winlink *wl = target->wl;
struct window *w = wl->window;
struct session *s = item->target.s;
struct session *s = target->s;
const char *errstr;
char *cause;
u_int adjust, sx, sy;
int xpixel = -1, ypixel = -1;
if (args->argc == 0)
if (args_count(args) == 0)
adjust = 1;
else {
adjust = strtonum(args->argv[0], 1, INT_MAX, &errstr);
adjust = strtonum(args_string(args, 0), 1, INT_MAX, &errstr);
if (errstr != NULL) {
cmdq_error(item, "adjustment %s", errstr);
return (CMD_RETURN_ERROR);
@@ -107,7 +108,9 @@ cmd_resize_window_exec(struct cmd *self, struct cmdq_item *item)
}
options_set_number(w->options, "window-size", WINDOW_SIZE_MANUAL);
resize_window(w, sx, sy, xpixel, ypixel);
w->manual_sx = sx;
w->manual_sy = sy;
recalculate_size(w, 1);
return (CMD_RETURN_NORMAL);
}

View File

@@ -34,9 +34,9 @@ const struct cmd_entry cmd_respawn_pane_entry = {
.name = "respawn-pane",
.alias = "respawnp",
.args = { "c:e:kt:", 0, -1 },
.args = { "c:e:kt:", 0, -1, NULL },
.usage = "[-k] [-c start-directory] [-e environment] "
CMD_TARGET_PANE_USAGE " [command]",
CMD_TARGET_PANE_USAGE " [shell-command]",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -47,32 +47,28 @@ const struct cmd_entry cmd_respawn_pane_entry = {
static enum cmd_retval
cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct spawn_context sc;
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct window_pane *wp = item->target.wp;
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct spawn_context sc = { 0 };
struct session *s = target->s;
struct winlink *wl = target->wl;
struct window_pane *wp = target->wp;
char *cause = NULL;
const char *add;
struct args_value *value;
struct args_value *av;
memset(&sc, 0, sizeof sc);
sc.item = item;
sc.s = s;
sc.wl = wl;
sc.wp0 = wp;
sc.lc = NULL;
sc.name = NULL;
sc.argc = args->argc;
sc.argv = args->argv;
args_to_vector(args, &sc.argc, &sc.argv);
sc.environ = environ_create();
add = args_first_value(args, 'e', &value);
while (add != NULL) {
environ_put(sc.environ, add);
add = args_next_value(&value);
av = args_first_value(args, 'e');
while (av != NULL) {
environ_put(sc.environ, av->string, 0);
av = args_next_value(av);
}
sc.idx = -1;
@@ -85,12 +81,18 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item)
if (spawn_pane(&sc, &cause) == NULL) {
cmdq_error(item, "respawn pane failed: %s", cause);
free(cause);
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);
return (CMD_RETURN_ERROR);
}
wp->flags |= PANE_REDRAW;
server_redraw_window_borders(wp->window);
server_status_window(wp->window);
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);
return (CMD_RETURN_NORMAL);
}

View File

@@ -34,9 +34,9 @@ const struct cmd_entry cmd_respawn_window_entry = {
.name = "respawn-window",
.alias = "respawnw",
.args = { "c:e:kt:", 0, -1 },
.args = { "c:e:kt:", 0, -1, NULL },
.usage = "[-k] [-c start-directory] [-e environment] "
CMD_TARGET_WINDOW_USAGE " [command]",
CMD_TARGET_WINDOW_USAGE " [shell-command]",
.target = { 't', CMD_FIND_WINDOW, 0 },
@@ -47,29 +47,27 @@ const struct cmd_entry cmd_respawn_window_entry = {
static enum cmd_retval
cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct spawn_context sc;
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct spawn_context sc = { 0 };
struct client *tc = cmdq_get_target_client(item);
struct session *s = target->s;
struct winlink *wl = target->wl;
char *cause = NULL;
const char *add;
struct args_value *value;
struct args_value *av;
memset(&sc, 0, sizeof sc);
sc.item = item;
sc.s = s;
sc.wl = wl;
sc.c = cmd_find_client(item, NULL, 1);
sc.tc = tc;
sc.name = NULL;
sc.argc = args->argc;
sc.argv = args->argv;
args_to_vector(args, &sc.argc, &sc.argv);
sc.environ = environ_create();
add = args_first_value(args, 'e', &value);
while (add != NULL) {
environ_put(sc.environ, add);
add = args_next_value(&value);
av = args_first_value(args, 'e');
while (av != NULL) {
environ_put(sc.environ, av->string, 0);
av = args_next_value(av);
}
sc.idx = -1;
@@ -82,11 +80,16 @@ cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item)
if (spawn_window(&sc, &cause) == NULL) {
cmdq_error(item, "respawn window failed: %s", cause);
free(cause);
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);
return (CMD_RETURN_ERROR);
}
server_redraw_window(wl->window);
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);
return (CMD_RETURN_NORMAL);
}

View File

@@ -31,7 +31,7 @@ const struct cmd_entry cmd_rotate_window_entry = {
.name = "rotate-window",
.alias = "rotatew",
.args = { "Dt:UZ", 0, 0 },
.args = { "Dt:UZ", 0, 0, NULL },
.usage = "[-DUZ] " CMD_TARGET_WINDOW_USAGE,
.target = { 't', CMD_FIND_WINDOW, 0 },
@@ -43,16 +43,18 @@ const struct cmd_entry cmd_rotate_window_entry = {
static enum cmd_retval
cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item)
{
struct cmd_find_state *current = &item->shared->current;
struct winlink *wl = item->target.wl;
struct args *args = cmd_get_args(self);
struct cmd_find_state *current = cmdq_get_current(item);
struct cmd_find_state *target = cmdq_get_target(item);
struct winlink *wl = target->wl;
struct window *w = wl->window;
struct window_pane *wp, *wp2;
struct layout_cell *lc;
u_int sx, sy, xoff, yoff;
window_push_zoom(w, args_has(self->args, 'Z'));
window_push_zoom(w, 0, args_has(args, 'Z'));
if (args_has(self->args, 'D')) {
if (args_has(args, 'D')) {
wp = TAILQ_LAST(&w->panes, window_panes);
TAILQ_REMOVE(&w->panes, wp, entry);
TAILQ_INSERT_HEAD(&w->panes, wp, entry);

View File

@@ -20,6 +20,7 @@
#include <sys/types.h>
#include <sys/wait.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
@@ -29,8 +30,12 @@
* Runs a command without a window.
*/
static enum cmd_retval cmd_run_shell_exec(struct cmd *, struct cmdq_item *);
static enum args_parse_type cmd_run_shell_args_parse(struct args *, u_int,
char **);
static enum cmd_retval cmd_run_shell_exec(struct cmd *,
struct cmdq_item *);
static void cmd_run_shell_timer(int, short, void *);
static void cmd_run_shell_callback(struct job *);
static void cmd_run_shell_free(void *);
static void cmd_run_shell_print(struct job *, const char *);
@@ -39,8 +44,8 @@ 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",
.args = { "bd:Ct:", 0, 1, cmd_run_shell_args_parse },
.usage = "[-bC] [-d delay] " CMD_TARGET_PANE_USAGE " [shell-command]",
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
@@ -49,11 +54,26 @@ const struct cmd_entry cmd_run_shell_entry = {
};
struct cmd_run_shell_data {
char *cmd;
struct cmdq_item *item;
int wp_id;
struct client *client;
char *cmd;
struct args_command_state *state;
char *cwd;
struct cmdq_item *item;
struct session *s;
int wp_id;
struct event timer;
int flags;
};
static enum args_parse_type
cmd_run_shell_args_parse(struct args *args, __unused u_int idx,
__unused char **cause)
{
if (args_has(args, 'C'))
return (ARGS_PARSE_COMMANDS_OR_STRING);
return (ARGS_PARSE_STRING);
}
static void
cmd_run_shell_print(struct job *job, const char *msg)
{
@@ -78,48 +98,130 @@ cmd_run_shell_print(struct job *job, const char *msg)
wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode != &window_view_mode)
window_pane_set_mode(wp, &window_view_mode, NULL, NULL);
window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL);
window_copy_add(wp, "%s", msg);
}
static enum cmd_retval
cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct cmd_run_shell_data *cdata;
struct client *c = cmd_find_client(item, NULL, 1);
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct window_pane *wp = item->target.wp;
struct client *tc = cmdq_get_target_client(item);
struct session *s = target->s;
struct window_pane *wp = target->wp;
const char *delay, *cmd;
double d;
struct timeval tv;
char *end;
int wait = !args_has(args, 'b');
if ((delay = args_get(args, 'd')) != NULL) {
d = strtod(delay, &end);
if (*end != '\0') {
cmdq_error(item, "invalid delay time: %s", delay);
return (CMD_RETURN_ERROR);
}
} else if (args_count(args) == 0)
return (CMD_RETURN_NORMAL);
cdata = xcalloc(1, sizeof *cdata);
cdata->cmd = format_single(item, args->argv[0], c, s, wl, wp);
if (!args_has(args, 'C')) {
cmd = args_string(args, 0);
if (cmd != NULL)
cdata->cmd = format_single_from_target(item, cmd);
} else {
cdata->state = args_make_commands_prepare(self, item, 0, NULL,
wait, 1);
}
if (args_has(args, 't') && wp != NULL)
cdata->wp_id = wp->id;
else
cdata->wp_id = -1;
if (!args_has(args, 'b'))
if (wait) {
cdata->client = cmdq_get_client(item);
cdata->item = item;
if (job_run(cdata->cmd, s, server_client_get_cwd(item->client, s), NULL,
cmd_run_shell_callback, cmd_run_shell_free, cdata, 0) == NULL) {
cmdq_error(item, "failed to run command: %s", cdata->cmd);
free(cdata);
return (CMD_RETURN_ERROR);
} else {
cdata->client = tc;
cdata->flags |= JOB_NOWAIT;
}
if (cdata->client != NULL)
cdata->client->references++;
if (args_has(args, 'b'))
cdata->cwd = xstrdup(server_client_get_cwd(cmdq_get_client(item), s));
cdata->s = s;
if (s != NULL)
session_add_ref(s, __func__);
evtimer_set(&cdata->timer, cmd_run_shell_timer, cdata);
if (delay != NULL) {
timerclear(&tv);
tv.tv_sec = (time_t)d;
tv.tv_usec = (d - (double)tv.tv_sec) * 1000000U;
evtimer_add(&cdata->timer, &tv);
} else
event_active(&cdata->timer, EV_TIMEOUT, 1);
if (!wait)
return (CMD_RETURN_NORMAL);
return (CMD_RETURN_WAIT);
}
static void
cmd_run_shell_timer(__unused int fd, __unused short events, void* arg)
{
struct cmd_run_shell_data *cdata = arg;
struct client *c = cdata->client;
const char *cmd = cdata->cmd;
struct cmdq_item *item = cdata->item, *new_item;
struct cmd_list *cmdlist;
char *error;
if (cdata->state == NULL) {
if (cmd == NULL) {
if (cdata->item != NULL)
cmdq_continue(cdata->item);
cmd_run_shell_free(cdata);
return;
}
if (job_run(cmd, 0, NULL, cdata->s, cdata->cwd, NULL,
cmd_run_shell_callback, cmd_run_shell_free, cdata,
cdata->flags, -1, -1) == NULL)
cmd_run_shell_free(cdata);
return;
}
cmdlist = args_make_commands(cdata->state, 0, NULL, &error);
if (cmdlist == NULL) {
if (cdata->item == NULL) {
*error = toupper((u_char)*error);
status_message_set(c, -1, 1, 0, "%s", error);
} else
cmdq_error(cdata->item, "%s", error);
free(error);
} else if (item == NULL) {
new_item = cmdq_get_command(cmdlist, NULL);
cmdq_append(c, new_item);
} else {
new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
cmdq_insert_after(item, new_item);
}
if (cdata->item != NULL)
cmdq_continue(cdata->item);
cmd_run_shell_free(cdata);
}
static void
cmd_run_shell_callback(struct job *job)
{
struct cmd_run_shell_data *cdata = job_get_data(job);
struct bufferevent *event = job_get_event(job);
struct cmdq_item *item = cdata->item;
char *cmd = cdata->cmd, *msg = NULL, *line;
size_t size;
int retcode, status;
@@ -149,13 +251,19 @@ cmd_run_shell_callback(struct job *job)
} else if (WIFSIGNALED(status)) {
retcode = WTERMSIG(status);
xasprintf(&msg, "'%s' terminated by signal %d", cmd, retcode);
}
retcode += 128;
} else
retcode = 0;
if (msg != NULL)
cmd_run_shell_print(job, msg);
free(msg);
if (cdata->item != NULL)
cmdq_continue(cdata->item);
if (item != NULL) {
if (cmdq_get_client(item) != NULL &&
cmdq_get_client(item)->session == NULL)
cmdq_get_client(item)->retval = retcode;
cmdq_continue(item);
}
}
static void
@@ -163,6 +271,14 @@ cmd_run_shell_free(void *data)
{
struct cmd_run_shell_data *cdata = data;
evtimer_del(&cdata->timer);
if (cdata->s != NULL)
session_remove_ref(cdata->s, __func__);
if (cdata->client != NULL)
server_client_unref(cdata->client);
if (cdata->state != NULL)
args_make_commands_free(cdata->state);
free(cdata->cwd);
free(cdata->cmd);
free(cdata);
}

View File

@@ -37,7 +37,7 @@ const struct cmd_entry cmd_save_buffer_entry = {
.name = "save-buffer",
.alias = "saveb",
.args = { "ab:", 1, 1 },
.args = { "ab:", 1, 1, NULL },
.usage = "[-a] " CMD_BUFFER_USAGE " path",
.flags = CMD_AFTERHOOK,
@@ -48,7 +48,7 @@ const struct cmd_entry cmd_show_buffer_entry = {
.name = "show-buffer",
.alias = "showb",
.args = { "b:", 0, 0 },
.args = { "b:", 0, 0, NULL },
.usage = CMD_BUFFER_USAGE,
.flags = CMD_AFTERHOOK,
@@ -72,16 +72,13 @@ cmd_save_buffer_done(__unused struct client *c, const char *path, int error,
static enum cmd_retval
cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c = cmd_find_client(item, NULL, 1);
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct window_pane *wp = item->target.wp;
struct args *args = cmd_get_args(self);
struct client *c = cmdq_get_client(item);
struct paste_buffer *pb;
int flags;
const char *bufname = args_get(args, 'b'), *bufdata;
size_t bufsize;
char *path;
char *path, *tmp;
if (bufname == NULL) {
if ((pb = paste_get_top(NULL)) == NULL) {
@@ -97,15 +94,22 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
}
bufdata = paste_buffer_data(pb, &bufsize);
if (self->entry == &cmd_show_buffer_entry)
if (cmd_get_entry(self) == &cmd_show_buffer_entry) {
if (c->session != NULL || (c->flags & CLIENT_CONTROL)) {
utf8_stravisx(&tmp, bufdata, bufsize,
VIS_OCTAL|VIS_CSTYLE|VIS_TAB);
cmdq_print(item, "%s", tmp);
free(tmp);
return (CMD_RETURN_NORMAL);
}
path = xstrdup("-");
else
path = format_single(item, args->argv[0], c, s, wl, wp);
if (args_has(self->args, 'a'))
} else
path = format_single_from_target(item, args_string(args, 0));
if (args_has(args, 'a'))
flags = O_APPEND;
else
flags = 0;
file_write(item->client, path, flags, bufdata, bufsize,
flags = O_TRUNC;
file_write(cmdq_get_client(item), path, flags, bufdata, bufsize,
cmd_save_buffer_done, item);
free(path);

View File

@@ -33,7 +33,7 @@ const struct cmd_entry cmd_select_layout_entry = {
.name = "select-layout",
.alias = "selectl",
.args = { "Enopt:", 0, 1 },
.args = { "Enopt:", 0, 1, NULL },
.usage = "[-Enop] " CMD_TARGET_PANE_USAGE " [layout-name]",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -46,7 +46,7 @@ const struct cmd_entry cmd_next_layout_entry = {
.name = "next-layout",
.alias = "nextl",
.args = { "t:", 0, 0 },
.args = { "t:", 0, 0, NULL },
.usage = CMD_TARGET_WINDOW_USAGE,
.target = { 't', CMD_FIND_WINDOW, 0 },
@@ -59,7 +59,7 @@ const struct cmd_entry cmd_previous_layout_entry = {
.name = "previous-layout",
.alias = "prevl",
.args = { "t:", 0, 0 },
.args = { "t:", 0, 0, NULL },
.usage = CMD_TARGET_WINDOW_USAGE,
.target = { 't', CMD_FIND_WINDOW, 0 },
@@ -71,20 +71,21 @@ const struct cmd_entry cmd_previous_layout_entry = {
static enum cmd_retval
cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct winlink *wl = item->target.wl;
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct winlink *wl = target->wl;
struct window *w = wl->window;
struct window_pane *wp = item->target.wp;
struct window_pane *wp = target->wp;
const char *layoutname;
char *oldlayout;
int next, previous, layout;
server_unzoom_window(w);
next = self->entry == &cmd_next_layout_entry;
next = (cmd_get_entry(self) == &cmd_next_layout_entry);
if (args_has(args, 'n'))
next = 1;
previous = self->entry == &cmd_previous_layout_entry;
previous = (cmd_get_entry(self) == &cmd_previous_layout_entry);
if (args_has(args, 'p'))
previous = 1;
@@ -104,24 +105,24 @@ cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item)
goto changed;
}
if (args_count(args) != 0)
layoutname = args_string(args, 0);
else if (args_has(args, 'o'))
layoutname = oldlayout;
else
layoutname = NULL;
if (!args_has(args, 'o')) {
if (args->argc == 0)
if (layoutname == NULL)
layout = w->lastlayout;
else
layout = layout_set_lookup(args->argv[0]);
layout = layout_set_lookup(layoutname);
if (layout != -1) {
layout_set_select(w, layout);
goto changed;
}
}
if (args->argc != 0)
layoutname = args->argv[0];
else if (args_has(args, 'o'))
layoutname = oldlayout;
else
layoutname = NULL;
if (layoutname != NULL) {
if (layout_parse(w, layoutname) == -1) {
cmdq_error(item, "can't set layout: %s", layoutname);

View File

@@ -33,7 +33,7 @@ const struct cmd_entry cmd_select_pane_entry = {
.name = "select-pane",
.alias = "selectp",
.args = { "DdegLlMmP:RT:t:UZ", 0, 0 }, /* -P and -g deprecated */
.args = { "DdegLlMmP:RT:t:UZ", 0, 0, NULL }, /* -P and -g deprecated */
.usage = "[-DdeLlMmRUZ] [-T title] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 },
@@ -46,7 +46,7 @@ const struct cmd_entry cmd_last_pane_entry = {
.name = "last-pane",
.alias = "lastp",
.args = { "det:Z", 0, 0 },
.args = { "det:Z", 0, 0, NULL },
.usage = "[-deZ] " CMD_TARGET_WINDOW_USAGE,
.target = { 't', CMD_FIND_WINDOW, 0 },
@@ -83,19 +83,21 @@ cmd_select_pane_redraw(struct window *w)
static enum cmd_retval
cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct cmd_find_state *current = &item->shared->current;
struct client *c = cmd_find_client(item, NULL, 1);
struct winlink *wl = item->target.wl;
struct args *args = cmd_get_args(self);
const struct cmd_entry *entry = cmd_get_entry(self);
struct cmd_find_state *current = cmdq_get_current(item);
struct cmd_find_state *target = cmdq_get_target(item);
struct client *c = cmdq_get_client(item);
struct winlink *wl = target->wl;
struct window *w = wl->window;
struct session *s = item->target.s;
struct window_pane *wp = item->target.wp, *lastwp, *markedwp;
char *pane_title;
struct session *s = target->s;
struct window_pane *wp = target->wp, *activewp, *lastwp, *markedwp;
struct options *oo = wp->options;
char *title;
const char *style;
struct style *sy;
struct options_entry *o;
if (self->entry == &cmd_last_pane_entry || args_has(args, 'l')) {
if (entry == &cmd_last_pane_entry || args_has(args, 'l')) {
lastwp = w->last;
if (lastwp == NULL && window_count_panes(w) == 2) {
lastwp = TAILQ_PREV(w->active, window_panes, entry);
@@ -106,12 +108,16 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
cmdq_error(item, "no last pane");
return (CMD_RETURN_ERROR);
}
if (args_has(self->args, 'e'))
if (args_has(args, 'e')) {
lastwp->flags &= ~PANE_INPUTOFF;
else if (args_has(self->args, 'd'))
server_redraw_window_borders(lastwp->window);
server_status_window(lastwp->window);
} else if (args_has(args, 'd')) {
lastwp->flags |= PANE_INPUTOFF;
else {
if (window_push_zoom(w, args_has(self->args, 'Z')))
server_redraw_window_borders(lastwp->window);
server_status_window(lastwp->window);
} else {
if (window_push_zoom(w, 0, args_has(args, 'Z')))
server_redraw_window(w);
window_redraw_active_switch(w, lastwp);
if (window_set_active_pane(w, lastwp, 1)) {
@@ -127,7 +133,10 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
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 (server_check_marked())
lastwp = marked_pane.wp;
else
lastwp = NULL;
if (args_has(args, 'M') || server_is_marked(s, wl, wp))
server_clear_marked();
@@ -136,83 +145,92 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
markedwp = marked_pane.wp;
if (lastwp != NULL) {
lastwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
server_redraw_window_borders(lastwp->window);
server_status_window(lastwp->window);
}
if (markedwp != NULL) {
markedwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
server_redraw_window_borders(markedwp->window);
server_status_window(markedwp->window);
}
return (CMD_RETURN_NORMAL);
}
if (args_has(self->args, 'P') || args_has(self->args, 'g')) {
if ((style = args_get(args, 'P')) != NULL) {
o = options_set_style(wp->options, "window-style", 0,
style);
if (o == NULL) {
cmdq_error(item, "bad style: %s", style);
return (CMD_RETURN_ERROR);
}
options_set_style(wp->options, "window-active-style", 0,
style);
wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
}
if (args_has(self->args, 'g')) {
sy = options_get_style(wp->options, "window-style");
cmdq_print(item, "%s", style_tostring(sy));
style = args_get(args, 'P');
if (style != NULL) {
o = options_set_string(oo, "window-style", 0, "%s", style);
if (o == NULL) {
cmdq_error(item, "bad style: %s", style);
return (CMD_RETURN_ERROR);
}
options_set_string(oo, "window-active-style", 0, "%s", style);
wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
}
if (args_has(args, 'g')) {
cmdq_print(item, "%s", options_get_string(oo, "window-style"));
return (CMD_RETURN_NORMAL);
}
if (args_has(self->args, 'L')) {
window_push_zoom(w, 1);
if (args_has(args, 'L')) {
window_push_zoom(w, 0, 1);
wp = window_pane_find_left(wp);
window_pop_zoom(w);
} else if (args_has(self->args, 'R')) {
window_push_zoom(w, 1);
} else if (args_has(args, 'R')) {
window_push_zoom(w, 0, 1);
wp = window_pane_find_right(wp);
window_pop_zoom(w);
} else if (args_has(self->args, 'U')) {
window_push_zoom(w, 1);
} else if (args_has(args, 'U')) {
window_push_zoom(w, 0, 1);
wp = window_pane_find_up(wp);
window_pop_zoom(w);
} else if (args_has(self->args, 'D')) {
window_push_zoom(w, 1);
} else if (args_has(args, 'D')) {
window_push_zoom(w, 0, 1);
wp = window_pane_find_down(wp);
window_pop_zoom(w);
}
if (wp == NULL)
return (CMD_RETURN_NORMAL);
if (args_has(self->args, 'e')) {
if (args_has(args, 'e')) {
wp->flags &= ~PANE_INPUTOFF;
server_redraw_window_borders(wp->window);
server_status_window(wp->window);
return (CMD_RETURN_NORMAL);
}
if (args_has(self->args, 'd')) {
if (args_has(args, 'd')) {
wp->flags |= PANE_INPUTOFF;
server_redraw_window_borders(wp->window);
server_status_window(wp->window);
return (CMD_RETURN_NORMAL);
}
if (args_has(self->args, 'T')) {
pane_title = format_single(item, args_get(self->args, 'T'),
c, s, wl, wp);
if (screen_set_title(&wp->base, pane_title))
if (args_has(args, 'T')) {
title = format_single_from_target(item, args_get(args, 'T'));
if (screen_set_title(&wp->base, title)) {
notify_pane("pane-title-changed", wp);
server_redraw_window_borders(wp->window);
server_status_window(wp->window);
free(pane_title);
}
free(title);
return (CMD_RETURN_NORMAL);
}
if (wp == w->active)
if (c != NULL && c->session != NULL && (c->flags & CLIENT_ACTIVEPANE))
activewp = server_client_get_pane(c);
else
activewp = w->active;
if (wp == activewp)
return (CMD_RETURN_NORMAL);
if (window_push_zoom(w, args_has(self->args, 'Z')))
if (window_push_zoom(w, 0, args_has(args, 'Z')))
server_redraw_window(w);
window_redraw_active_switch(w, wp);
if (window_set_active_pane(w, wp, 1)) {
if (c != NULL && c->session != NULL && (c->flags & CLIENT_ACTIVEPANE))
server_client_set_pane(c, wp);
else if (window_set_active_pane(w, wp, 1))
cmd_find_from_winlink_pane(current, wl, wp, 0);
cmdq_insert_hook(s, item, current, "after-select-pane");
cmd_select_pane_redraw(w);
}
cmdq_insert_hook(s, item, current, "after-select-pane");
cmd_select_pane_redraw(w);
if (window_pop_zoom(w))
server_redraw_window(w);

View File

@@ -33,7 +33,7 @@ const struct cmd_entry cmd_select_window_entry = {
.name = "select-window",
.alias = "selectw",
.args = { "lnpTt:", 0, 0 },
.args = { "lnpTt:", 0, 0, NULL },
.usage = "[-lnpT] " CMD_TARGET_WINDOW_USAGE,
.target = { 't', CMD_FIND_WINDOW, 0 },
@@ -46,7 +46,7 @@ const struct cmd_entry cmd_next_window_entry = {
.name = "next-window",
.alias = "next",
.args = { "at:", 0, 0 },
.args = { "at:", 0, 0, NULL },
.usage = "[-a] " CMD_TARGET_SESSION_USAGE,
.target = { 't', CMD_FIND_SESSION, 0 },
@@ -59,7 +59,7 @@ const struct cmd_entry cmd_previous_window_entry = {
.name = "previous-window",
.alias = "prev",
.args = { "at:", 0, 0 },
.args = { "at:", 0, 0, NULL },
.usage = "[-a] " CMD_TARGET_SESSION_USAGE,
.target = { 't', CMD_FIND_SESSION, 0 },
@@ -72,7 +72,7 @@ const struct cmd_entry cmd_last_window_entry = {
.name = "last-window",
.alias = "last",
.args = { "t:", 0, 0 },
.args = { "t:", 0, 0, NULL },
.usage = CMD_TARGET_SESSION_USAGE,
.target = { 't', CMD_FIND_SESSION, 0 },
@@ -84,23 +84,26 @@ const struct cmd_entry cmd_last_window_entry = {
static enum cmd_retval
cmd_select_window_exec(struct cmd *self, struct cmdq_item *item)
{
struct cmd_find_state *current = &item->shared->current;
struct winlink *wl = item->target.wl;
struct session *s = item->target.s;
struct args *args = cmd_get_args(self);
struct client *c = cmdq_get_client(item);
struct cmd_find_state *current = cmdq_get_current(item);
struct cmd_find_state *target = cmdq_get_target(item);
struct winlink *wl = target->wl;
struct session *s = target->s;
int next, previous, last, activity;
next = self->entry == &cmd_next_window_entry;
if (args_has(self->args, 'n'))
next = (cmd_get_entry(self) == &cmd_next_window_entry);
if (args_has(args, 'n'))
next = 1;
previous = self->entry == &cmd_previous_window_entry;
if (args_has(self->args, 'p'))
previous = (cmd_get_entry(self) == &cmd_previous_window_entry);
if (args_has(args, 'p'))
previous = 1;
last = self->entry == &cmd_last_window_entry;
if (args_has(self->args, 'l'))
last = (cmd_get_entry(self) == &cmd_last_window_entry);
if (args_has(args, 'l'))
last = 1;
if (next || previous || last) {
activity = args_has(self->args, 'a');
activity = args_has(args, 'a');
if (next) {
if (session_next(s, activity) != 0) {
cmdq_error(item, "no next window");
@@ -125,7 +128,7 @@ cmd_select_window_exec(struct cmd *self, struct cmdq_item *item)
* 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 (args_has(args, 'T') && wl == s->curw) {
if (session_last(s) != 0) {
cmdq_error(item, "no last window");
return (-1);
@@ -139,6 +142,8 @@ cmd_select_window_exec(struct cmd *self, struct cmdq_item *item)
}
cmdq_insert_hook(s, item, current, "after-select-window");
}
if (c != NULL && c->session != NULL)
s->curw->window->latest = c;
recalculate_sizes();
return (CMD_RETURN_NORMAL);

View File

@@ -33,7 +33,7 @@ const struct cmd_entry cmd_send_keys_entry = {
.name = "send-keys",
.alias = "send",
.args = { "FHlMN:Rt:X", 0, -1 },
.args = { "FHlMN:Rt:X", 0, -1, NULL },
.usage = "[-FHlMRX] [-N repeat-count] " CMD_TARGET_PANE_USAGE
" key ...",
@@ -47,7 +47,7 @@ const struct cmd_entry cmd_send_prefix_entry = {
.name = "send-prefix",
.alias = NULL,
.args = { "2t:", 0, 0 },
.args = { "2t:", 0, 0, NULL },
.usage = "[-2] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 },
@@ -57,43 +57,42 @@ const struct cmd_entry cmd_send_prefix_entry = {
};
static struct cmdq_item *
cmd_send_keys_inject_key(struct client *c, struct cmd_find_state *fs,
struct cmdq_item *item, key_code key)
cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after,
key_code key)
{
struct session *s = fs->s;
struct winlink *wl = fs->wl;
struct window_pane *wp = fs->wp;
struct cmd_find_state *target = cmdq_get_target(item);
struct client *tc = cmdq_get_target_client(item);
struct session *s = target->s;
struct winlink *wl = target->wl;
struct window_pane *wp = target->wp;
struct window_mode_entry *wme;
struct key_table *table;
struct key_binding *bd;
wme = TAILQ_FIRST(&fs->wp->modes);
wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode->key_table == NULL) {
if (options_get_number(fs->wp->window->options, "xterm-keys"))
key |= KEYC_XTERM;
if (window_pane_key(wp, item->client, s, wl, key, NULL) != 0)
if (window_pane_key(wp, tc, s, wl, key, NULL) != 0)
return (NULL);
return (item);
}
table = key_bindings_get_table(wme->mode->key_table(wme), 1);
bd = key_bindings_get(table, key & ~KEYC_XTERM);
bd = key_bindings_get(table, key & ~KEYC_MASK_FLAGS);
if (bd != NULL) {
table->references++;
item = key_bindings_dispatch(bd, item, c, NULL, &item->target);
after = key_bindings_dispatch(bd, after, tc, NULL, target);
key_bindings_unref_table(table);
}
return (item);
return (after);
}
static struct cmdq_item *
cmd_send_keys_inject_string(struct client *c, struct cmd_find_state *fs,
struct cmdq_item *item, struct args *args, int i)
cmd_send_keys_inject_string(struct cmdq_item *item, struct cmdq_item *after,
struct args *args, int i)
{
const char *s = args->argv[i];
struct cmdq_item *new_item;
struct utf8_data *ud, *uc;
wchar_t wc;
const char *s = args_string(args, i);
struct utf8_data *ud, *loop;
utf8_char uc;
key_code key;
char *endptr;
long n;
@@ -103,45 +102,52 @@ cmd_send_keys_inject_string(struct client *c, struct cmd_find_state *fs,
n = strtol(s, &endptr, 16);
if (*s =='\0' || n < 0 || n > 0xff || *endptr != '\0')
return (item);
return (cmd_send_keys_inject_key(c, fs, item, KEYC_LITERAL|n));
return (cmd_send_keys_inject_key(item, after, KEYC_LITERAL|n));
}
literal = args_has(args, 'l');
if (!literal) {
key = key_string_lookup_string(s);
if (key != KEYC_NONE && key != KEYC_UNKNOWN) {
new_item = cmd_send_keys_inject_key(c, fs, item, key);
if (new_item != NULL)
return (new_item);
after = cmd_send_keys_inject_key(item, after, key);
if (after != NULL)
return (after);
}
literal = 1;
}
if (literal) {
ud = utf8_fromcstr(s);
for (uc = ud; uc->size != 0; uc++) {
if (utf8_combine(uc, &wc) != UTF8_DONE)
continue;
item = cmd_send_keys_inject_key(c, fs, item, wc);
for (loop = ud; loop->size != 0; loop++) {
if (loop->size == 1 && loop->data[0] <= 0x7f)
key = loop->data[0];
else {
if (utf8_from_data(loop, &uc) != UTF8_DONE)
continue;
key = uc;
}
after = cmd_send_keys_inject_key(item, after, key);
}
free(ud);
}
return (item);
return (after);
}
static enum cmd_retval
cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c = cmd_find_client(item, NULL, 1);
struct cmd_find_state *fs = &item->target;
struct window_pane *wp = item->target.wp;
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct mouse_event *m = &item->shared->mouse;
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct client *tc = cmdq_get_target_client(item);
struct session *s = target->s;
struct winlink *wl = target->wl;
struct window_pane *wp = target->wp;
struct key_event *event = cmdq_get_event(item);
struct mouse_event *m = &event->m;
struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
int i;
struct cmdq_item *after = item;
key_code key;
u_int np = 1;
u_int i, np = 1;
u_int count = args_count(args);
char *cause = NULL;
if (args_has(args, 'N')) {
@@ -151,8 +157,8 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
free(cause);
return (CMD_RETURN_ERROR);
}
if (wme != NULL && (args_has(args, 'X') || args->argc == 0)) {
if (wme == NULL || wme->mode->command == NULL) {
if (wme != NULL && (args_has(args, 'X') || count == 0)) {
if (wme->mode->command == NULL) {
cmdq_error(item, "not in a mode");
return (CMD_RETURN_ERROR);
}
@@ -167,7 +173,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
}
if (!m->valid)
m = NULL;
wme->mode->command(wme, c, s, wl, args, m);
wme->mode->command(wme, tc, s, wl, args, m);
return (CMD_RETURN_NORMAL);
}
@@ -177,27 +183,36 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
cmdq_error(item, "no mouse target");
return (CMD_RETURN_ERROR);
}
window_pane_key(wp, item->client, s, wl, m->key, m);
window_pane_key(wp, tc, s, wl, m->key, m);
return (CMD_RETURN_NORMAL);
}
if (self->entry == &cmd_send_prefix_entry) {
if (cmd_get_entry(self) == &cmd_send_prefix_entry) {
if (args_has(args, '2'))
key = options_get_number(s->options, "prefix2");
else
key = options_get_number(s->options, "prefix");
cmd_send_keys_inject_key(c, fs, item, key);
cmd_send_keys_inject_key(item, item, key);
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'R')) {
window_pane_reset_palette(wp);
input_reset(wp, 1);
colour_palette_clear(&wp->palette);
input_reset(wp->ictx, 1);
wp->flags |= (PANE_STYLECHANGED|PANE_REDRAW);
}
if (count == 0) {
for (; np != 0; np--)
cmd_send_keys_inject_key(item, NULL, event->key);
return (CMD_RETURN_NORMAL);
}
for (; np != 0; np--) {
for (i = 0; i < args->argc; i++)
item = cmd_send_keys_inject_string(c, fs, item, args, i);
for (i = 0; i < count; i++) {
after = cmd_send_keys_inject_string(item, after, args,
i);
}
}
return (CMD_RETURN_NORMAL);

View File

@@ -33,10 +33,11 @@ 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",
.args = { "ab:t:n:w", 0, 1, NULL },
.usage = "[-aw] " CMD_BUFFER_USAGE " [-n new-buffer-name] "
CMD_TARGET_CLIENT_USAGE " data",
.flags = CMD_AFTERHOOK,
.flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG|CMD_CLIENT_CANFAIL,
.exec = cmd_set_buffer_exec
};
@@ -44,7 +45,7 @@ const struct cmd_entry cmd_delete_buffer_entry = {
.name = "delete-buffer",
.alias = "deleteb",
.args = { "b:", 0, 0 },
.args = { "b:", 0, 0, NULL },
.usage = CMD_BUFFER_USAGE,
.flags = CMD_AFTERHOOK,
@@ -54,7 +55,8 @@ const struct cmd_entry cmd_delete_buffer_entry = {
static enum cmd_retval
cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct args *args = cmd_get_args(self);
struct client *tc = cmdq_get_target_client(item);
struct paste_buffer *pb;
char *bufdata, *cause;
const char *bufname, *olddata;
@@ -66,7 +68,7 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
else
pb = paste_get_name(bufname);
if (self->entry == &cmd_delete_buffer_entry) {
if (cmd_get_entry(self) == &cmd_delete_buffer_entry) {
if (pb == NULL)
pb = paste_get_top(&bufname);
if (pb == NULL) {
@@ -92,11 +94,11 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
if (args->argc != 1) {
if (args_count(args) != 1) {
cmdq_error(item, "no data specified");
return (CMD_RETURN_ERROR);
}
if ((newsize = strlen(args->argv[0])) == 0)
if ((newsize = strlen(args_string(args, 0))) == 0)
return (CMD_RETURN_NORMAL);
bufsize = 0;
@@ -109,7 +111,7 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
}
bufdata = xrealloc(bufdata, bufsize + newsize);
memcpy(bufdata + bufsize, args->argv[0], newsize);
memcpy(bufdata + bufsize, args_string(args, 0), newsize);
bufsize += newsize;
if (paste_set(bufdata, bufsize, bufname, &cause) != 0) {
@@ -118,6 +120,8 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
free(cause);
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'w') && tc != NULL)
tty_set_selection(&tc->tty, bufdata, bufsize);
return (CMD_RETURN_NORMAL);
}

View File

@@ -34,8 +34,8 @@ 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]",
.args = { "Fhgrt:u", 1, 2, NULL },
.usage = "[-Fhgru] " CMD_TARGET_SESSION_USAGE " name [value]",
.target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL },
@@ -46,11 +46,14 @@ const struct cmd_entry cmd_set_environment_entry = {
static enum cmd_retval
cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct environ *env;
const char *name, *value, *target;
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct environ *env;
const char *name = args_string(args, 0), *value;
const char *tflag;
char *expanded = NULL;
enum cmd_retval retval = CMD_RETURN_NORMAL;
name = args->argv[0];
if (*name == '\0') {
cmdq_error(item, "empty variable name");
return (CMD_RETURN_ERROR);
@@ -60,44 +63,57 @@ cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR);
}
if (args->argc < 2)
if (args_count(args) < 2)
value = NULL;
else
value = args->argv[1];
if (args_has(self->args, 'g'))
value = args_string(args, 1);
if (value != NULL && args_has(args, 'F')) {
expanded = format_single_from_target(item, value);
value = expanded;
}
if (args_has(args, 'g'))
env = global_environ;
else {
if (item->target.s == NULL) {
target = args_get(args, 't');
if (target != NULL)
cmdq_error(item, "no such session: %s", target);
if (target->s == NULL) {
tflag = args_get(args, 't');
if (tflag != NULL)
cmdq_error(item, "no such session: %s", tflag);
else
cmdq_error(item, "no current session");
return (CMD_RETURN_ERROR);
retval = CMD_RETURN_ERROR;
goto out;
}
env = item->target.s->environ;
env = target->s->environ;
}
if (args_has(self->args, 'u')) {
if (args_has(args, 'u')) {
if (value != NULL) {
cmdq_error(item, "can't specify a value with -u");
return (CMD_RETURN_ERROR);
retval = CMD_RETURN_ERROR;
goto out;
}
environ_unset(env, name);
} else if (args_has(self->args, 'r')) {
} else if (args_has(args, 'r')) {
if (value != NULL) {
cmdq_error(item, "can't specify a value with -r");
return (CMD_RETURN_ERROR);
retval = CMD_RETURN_ERROR;
goto out;
}
environ_clear(env, name);
} else {
if (value == NULL) {
cmdq_error(item, "no value specified");
return (CMD_RETURN_ERROR);
retval = CMD_RETURN_ERROR;
goto out;
}
environ_set(env, name, "%s", value);
if (args_has(args, 'h'))
environ_set(env, name, ENVIRON_HIDDEN, "%s", value);
else
environ_set(env, name, 0, "%s", value);
}
return (CMD_RETURN_NORMAL);
out:
free(expanded);
return (retval);
}

View File

@@ -18,7 +18,6 @@
#include <sys/types.h>
#include <fnmatch.h>
#include <stdlib.h>
#include <string.h>
@@ -28,23 +27,17 @@
* Set an option.
*/
static enum cmd_retval cmd_set_option_exec(struct cmd *, struct cmdq_item *);
static int cmd_set_option_set(struct cmd *, struct cmdq_item *,
struct options *, struct options_entry *, const char *);
static int cmd_set_option_flag(struct cmdq_item *,
const struct options_table_entry *, struct options *,
const char *);
static int cmd_set_option_choice(struct cmdq_item *,
const struct options_table_entry *, struct options *,
const char *);
static enum args_parse_type cmd_set_option_args_parse(struct args *,
u_int, char **);
static enum cmd_retval cmd_set_option_exec(struct cmd *,
struct cmdq_item *);
const struct cmd_entry cmd_set_option_entry = {
.name = "set-option",
.alias = "set",
.args = { "aFgopqst:uw", 1, 2 },
.usage = "[-aFgopqsuw] " CMD_TARGET_PANE_USAGE " option [value]",
.args = { "aFgopqst:uUw", 1, 2, cmd_set_option_args_parse },
.usage = "[-aFgopqsuUw] " CMD_TARGET_PANE_USAGE " option [value]",
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
@@ -56,7 +49,7 @@ const struct cmd_entry cmd_set_window_option_entry = {
.name = "set-window-option",
.alias = "setw",
.args = { "aFgoqt:u", 1, 2 },
.args = { "aFgoqt:u", 1, 2, cmd_set_option_args_parse },
.usage = "[-aFgoqu] " CMD_TARGET_WINDOW_USAGE " option [value]",
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL },
@@ -69,41 +62,46 @@ const struct cmd_entry cmd_set_hook_entry = {
.name = "set-hook",
.alias = NULL,
.args = { "agRt:u", 1, 2 },
.usage = "[-agRu] " CMD_TARGET_SESSION_USAGE " hook [command]",
.args = { "agpRt:uw", 1, 2, cmd_set_option_args_parse },
.usage = "[-agpRuw] " CMD_TARGET_PANE_USAGE " hook [command]",
.target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL },
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
.flags = CMD_AFTERHOOK,
.exec = cmd_set_option_exec
};
static enum args_parse_type
cmd_set_option_args_parse(__unused struct args *args, u_int idx,
__unused char **cause)
{
if (idx == 1)
return (ARGS_PARSE_COMMANDS_OR_STRING);
return (ARGS_PARSE_STRING);
}
static enum cmd_retval
cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct args *args = cmd_get_args(self);
int append = args_has(args, 'a');
struct cmd_find_state *fs = &item->target;
struct client *c, *loop;
struct session *s = fs->s;
struct winlink *wl = fs->wl;
struct window *w;
struct window_pane *wp;
struct cmd_find_state *target = cmdq_get_target(item);
struct window_pane *loop;
struct options *oo;
struct options_entry *parent, *o;
char *name, *argument, *value = NULL, *cause;
struct options_entry *parent, *o, *po;
char *name, *argument, *expanded = NULL;
char *cause;
const char *value;
int window, idx, already, error, ambiguous;
int scope;
struct style *sy;
window = (self->entry == &cmd_set_window_option_entry);
window = (cmd_get_entry(self) == &cmd_set_window_option_entry);
/* Expand argument. */
c = cmd_find_client(item, NULL, 1);
argument = format_single(item, args->argv[0], c, s, wl, NULL);
argument = format_single_from_target(item, args_string(args, 0));
/* If set-hook -R, fire the hook straight away. */
if (self->entry == &cmd_set_hook_entry && args_has(args, 'R')) {
if (cmd_get_entry(self) == &cmd_set_hook_entry && args_has(args, 'R')) {
notify_hook(item, argument);
free(argument);
return (CMD_RETURN_NORMAL);
@@ -120,15 +118,18 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
cmdq_error(item, "invalid option: %s", argument);
goto fail;
}
if (args->argc < 2)
if (args_count(args) < 2)
value = NULL;
else if (args_has(args, 'F'))
value = format_single(item, args->argv[1], c, s, wl, NULL);
else
value = xstrdup(args->argv[1]);
value = args_string(args, 1);
if (value != NULL && args_has(args, 'F')) {
expanded = format_single_from_target(item, value);
value = expanded;
}
/* Get the scope and table for the option .*/
scope = options_scope_from_name(args, window, name, fs, &oo, &cause);
scope = options_scope_from_name(args, window, name, target, &oo,
&cause);
if (scope == OPTIONS_TABLE_NONE) {
if (args_has(args, 'q'))
goto out;
@@ -140,7 +141,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
parent = options_get(oo, name);
/* Check that array options and indexes match up. */
if (idx != -1 && (*name == '@' || !options_isarray(parent))) {
if (idx != -1 && (*name == '@' || !options_is_array(parent))) {
cmdq_error(item, "not an array: %s", argument);
goto fail;
}
@@ -164,19 +165,22 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
}
/* Change the option. */
if (args_has(args, 'u')) {
if (args_has(args, 'U') && scope == OPTIONS_TABLE_WINDOW) {
TAILQ_FOREACH(loop, &target->w->panes, entry) {
po = options_get_only(loop->options, name);
if (po == NULL)
continue;
if (options_remove_or_default(po, idx, &cause) != 0) {
cmdq_error(item, "%s", cause);
free(cause);
goto fail;
}
}
}
if (args_has(args, 'u') || args_has(args, 'U')) {
if (o == NULL)
goto out;
if (idx == -1) {
if (*name == '@')
options_remove(o);
else if (oo == global_options ||
oo == global_s_options ||
oo == global_w_options)
options_default(oo, options_table_entry(o));
else
options_remove(o);
} else if (options_array_set(o, idx, NULL, 0, &cause) != 0) {
if (options_remove_or_default(o, idx, &cause) != 0) {
cmdq_error(item, "%s", cause);
free(cause);
goto fail;
@@ -187,10 +191,15 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
goto fail;
}
options_set_string(oo, name, append, "%s", value);
} else if (idx == -1 && !options_isarray(parent)) {
error = cmd_set_option_set(self, item, oo, parent, value);
if (error != 0)
} else if (idx == -1 && !options_is_array(parent)) {
error = options_from_string(oo, options_table_entry(parent),
options_table_entry(parent)->name, value,
args_has(args, 'a'), &cause);
if (error != 0) {
cmdq_error(item, "%s", cause);
free(cause);
goto fail;
}
} else {
if (value == NULL) {
cmdq_error(item, "empty value");
@@ -214,198 +223,17 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
}
}
/* Update timers and so on for various options. */
if (strcmp(name, "automatic-rename") == 0) {
RB_FOREACH(w, windows, &windows) {
if (w->active == NULL)
continue;
if (options_get_number(w->options, "automatic-rename"))
w->active->flags |= PANE_CHANGED;
}
}
if (strcmp(name, "key-table") == 0) {
TAILQ_FOREACH(loop, &clients, entry)
server_client_set_key_table(loop, NULL);
}
if (strcmp(name, "user-keys") == 0) {
TAILQ_FOREACH(loop, &clients, entry) {
if (loop->tty.flags & TTY_OPENED)
tty_keys_build(&loop->tty);
}
}
if (strcmp(name, "status-fg") == 0 || strcmp(name, "status-bg") == 0) {
sy = options_get_style(oo, "status-style");
sy->gc.fg = options_get_number(oo, "status-fg");
sy->gc.bg = options_get_number(oo, "status-bg");
}
if (strcmp(name, "status-style") == 0) {
sy = options_get_style(oo, "status-style");
options_set_number(oo, "status-fg", sy->gc.fg);
options_set_number(oo, "status-bg", sy->gc.bg);
}
if (strcmp(name, "status") == 0 ||
strcmp(name, "status-interval") == 0)
status_timer_start_all();
if (strcmp(name, "monitor-silence") == 0)
alerts_reset_all();
if (strcmp(name, "window-style") == 0 ||
strcmp(name, "window-active-style") == 0) {
RB_FOREACH(wp, window_pane_tree, &all_window_panes)
wp->flags |= PANE_STYLECHANGED;
}
if (strcmp(name, "pane-border-status") == 0) {
RB_FOREACH(w, windows, &windows)
layout_fix_panes(w);
}
RB_FOREACH(s, sessions, &sessions)
status_update_cache(s);
/*
* Update sizes and redraw. May not always be necessary but do it
* anyway.
*/
recalculate_sizes();
TAILQ_FOREACH(loop, &clients, entry) {
if (loop->session != NULL)
server_redraw_client(loop);
}
options_push_changes(name);
out:
free(argument);
free(value);
free(expanded);
free(name);
return (CMD_RETURN_NORMAL);
fail:
free(argument);
free(value);
free(expanded);
free(name);
return (CMD_RETURN_ERROR);
}
static int
cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo,
struct options_entry *parent, const char *value)
{
const struct options_table_entry *oe;
struct args *args = self->args;
int append = args_has(args, 'a');
struct options_entry *o;
long long number;
const char *errstr, *new;
char *old;
key_code key;
oe = options_table_entry(parent);
if (value == NULL &&
oe->type != OPTIONS_TABLE_FLAG &&
oe->type != OPTIONS_TABLE_CHOICE) {
cmdq_error(item, "empty value");
return (-1);
}
switch (oe->type) {
case OPTIONS_TABLE_STRING:
old = xstrdup(options_get_string(oo, oe->name));
options_set_string(oo, oe->name, append, "%s", value);
new = options_get_string(oo, oe->name);
if (oe->pattern != NULL && fnmatch(oe->pattern, new, 0) != 0) {
options_set_string(oo, oe->name, 0, "%s", old);
free(old);
cmdq_error(item, "value is invalid: %s", value);
return (-1);
}
free(old);
return (0);
case OPTIONS_TABLE_NUMBER:
number = strtonum(value, oe->minimum, oe->maximum, &errstr);
if (errstr != NULL) {
cmdq_error(item, "value is %s: %s", errstr, value);
return (-1);
}
options_set_number(oo, oe->name, number);
return (0);
case OPTIONS_TABLE_KEY:
key = key_string_lookup_string(value);
if (key == KEYC_UNKNOWN) {
cmdq_error(item, "bad key: %s", value);
return (-1);
}
options_set_number(oo, oe->name, key);
return (0);
case OPTIONS_TABLE_COLOUR:
if ((number = colour_fromstring(value)) == -1) {
cmdq_error(item, "bad colour: %s", value);
return (-1);
}
options_set_number(oo, oe->name, number);
return (0);
case OPTIONS_TABLE_FLAG:
return (cmd_set_option_flag(item, oe, oo, value));
case OPTIONS_TABLE_CHOICE:
return (cmd_set_option_choice(item, oe, oo, value));
case OPTIONS_TABLE_STYLE:
o = options_set_style(oo, oe->name, append, value);
if (o == NULL) {
cmdq_error(item, "bad style: %s", value);
return (-1);
}
return (0);
case OPTIONS_TABLE_COMMAND:
break;
}
return (-1);
}
static int
cmd_set_option_flag(struct cmdq_item *item,
const struct options_table_entry *oe, struct options *oo,
const char *value)
{
int flag;
if (value == NULL || *value == '\0')
flag = !options_get_number(oo, oe->name);
else if (strcmp(value, "1") == 0 ||
strcasecmp(value, "on") == 0 ||
strcasecmp(value, "yes") == 0)
flag = 1;
else if (strcmp(value, "0") == 0 ||
strcasecmp(value, "off") == 0 ||
strcasecmp(value, "no") == 0)
flag = 0;
else {
cmdq_error(item, "bad value: %s", value);
return (-1);
}
options_set_number(oo, oe->name, flag);
return (0);
}
static int
cmd_set_option_choice(struct cmdq_item *item,
const struct options_table_entry *oe, struct options *oo,
const char *value)
{
const char **cp;
int n, choice = -1;
if (value == NULL) {
choice = options_get_number(oo, oe->name);
if (choice < 2)
choice = !choice;
} else {
n = 0;
for (cp = oe->choices; *cp != NULL; cp++) {
if (strcmp(*cp, value) == 0)
choice = n;
n++;
}
if (choice == -1) {
cmdq_error(item, "unknown value: %s", value);
return (-1);
}
}
options_set_number(oo, oe->name, choice);
return (0);
}

View File

@@ -38,8 +38,8 @@ const struct cmd_entry cmd_show_environment_entry = {
.name = "show-environment",
.alias = "showenv",
.args = { "gst:", 0, 1 },
.usage = "[-gs] " CMD_TARGET_SESSION_USAGE " [name]",
.args = { "hgst:", 0, 1, NULL },
.usage = "[-hgs] " CMD_TARGET_SESSION_USAGE " [name]",
.target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL },
@@ -69,9 +69,15 @@ static void
cmd_show_environment_print(struct cmd *self, struct cmdq_item *item,
struct environ_entry *envent)
{
char *escaped;
struct args *args = cmd_get_args(self);
char *escaped;
if (!args_has(self->args, 's')) {
if (!args_has(args, 'h') && (envent->flags & ENVIRON_HIDDEN))
return;
if (args_has(args, 'h') && (~envent->flags & ENVIRON_HIDDEN))
return;
if (!args_has(args, 's')) {
if (envent->value != NULL)
cmdq_print(item, "%s=%s", envent->name, envent->value);
else
@@ -91,36 +97,37 @@ cmd_show_environment_print(struct cmd *self, struct cmdq_item *item,
static enum cmd_retval
cmd_show_environment_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct environ *env;
struct environ_entry *envent;
const char *target;
const char *tflag, *name = args_string(args, 0);
if ((target = args_get(args, 't')) != NULL) {
if (item->target.s == NULL) {
cmdq_error(item, "no such session: %s", target);
if ((tflag = args_get(args, 't')) != NULL) {
if (target->s == NULL) {
cmdq_error(item, "no such session: %s", tflag);
return (CMD_RETURN_ERROR);
}
}
if (args_has(self->args, 'g'))
if (args_has(args, 'g'))
env = global_environ;
else {
if (item->target.s == NULL) {
target = args_get(args, 't');
if (target != NULL)
cmdq_error(item, "no such session: %s", target);
if (target->s == NULL) {
tflag = args_get(args, 't');
if (tflag != NULL)
cmdq_error(item, "no such session: %s", tflag);
else
cmdq_error(item, "no current session");
return (CMD_RETURN_ERROR);
}
env = item->target.s->environ;
env = target->s->environ;
}
if (args->argc != 0) {
envent = environ_find(env, args->argv[0]);
if (name != NULL) {
envent = environ_find(env, name);
if (envent == NULL) {
cmdq_error(item, "unknown variable: %s", args->argv[0]);
cmdq_error(item, "unknown variable: %s", name);
return (CMD_RETURN_ERROR);
}
cmd_show_environment_print(self, item, envent);

View File

@@ -18,16 +18,19 @@
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "tmux.h"
/*
* Show client message log.
* Show message log.
*/
#define SHOW_MESSAGES_TEMPLATE \
"#{t/p:message_time}: #{message_text}"
static enum cmd_retval cmd_show_messages_exec(struct cmd *,
struct cmdq_item *);
@@ -35,29 +38,31 @@ const struct cmd_entry cmd_show_messages_entry = {
.name = "show-messages",
.alias = "showmsgs",
.args = { "JTt:", 0, 0 },
.args = { "JTt:", 0, 0, NULL },
.usage = "[-JT] " CMD_TARGET_CLIENT_USAGE,
.flags = CMD_AFTERHOOK,
.flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG,
.exec = cmd_show_messages_exec
};
static int cmd_show_messages_terminals(struct cmdq_item *, int);
static int
cmd_show_messages_terminals(struct cmdq_item *item, int blank)
cmd_show_messages_terminals(struct cmd *self, struct cmdq_item *item, int blank)
{
struct args *args = cmd_get_args(self);
struct client *tc = cmdq_get_target_client(item);
struct tty_term *term;
u_int i, n;
n = 0;
LIST_FOREACH(term, &tty_terms, entry) {
if (args_has(args, 't') && term != tc->tty.term)
continue;
if (blank) {
cmdq_print(item, "%s", "");
blank = 0;
}
cmdq_print(item, "Terminal %u: %s [references=%u, flags=0x%x]:",
n, term->name, term->references, term->flags);
cmdq_print(item, "Terminal %u: %s for %s, flags=0x%x:", n,
term->name, term->tty->client->name, term->flags);
n++;
for (i = 0; i < tty_term_ncodes(); i++)
cmdq_print(item, "%s", tty_term_describe(term, i));
@@ -68,18 +73,15 @@ cmd_show_messages_terminals(struct cmdq_item *item, int blank)
static enum cmd_retval
cmd_show_messages_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c;
struct args *args = cmd_get_args(self);
struct message_entry *msg;
char *tim;
char *s;
int done, blank;
if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL)
return (CMD_RETURN_ERROR);
struct format_tree *ft;
done = blank = 0;
if (args_has(args, 'T')) {
blank = cmd_show_messages_terminals(item, blank);
blank = cmd_show_messages_terminals(self, item, blank);
done = 1;
}
if (args_has(args, 'J')) {
@@ -89,12 +91,17 @@ cmd_show_messages_exec(struct cmd *self, struct cmdq_item *item)
if (done)
return (CMD_RETURN_NORMAL);
TAILQ_FOREACH(msg, &c->message_log, entry) {
tim = ctime(&msg->msg_time);
*strchr(tim, '\n') = '\0';
ft = format_create_from_target(item);
TAILQ_FOREACH_REVERSE(msg, &message_log, message_list, entry) {
format_add(ft, "message_text", "%s", msg->msg);
format_add(ft, "message_number", "%u", msg->msg_num);
format_add_tv(ft, "message_time", &msg->msg_time);
cmdq_print(item, "%s %s", tim, msg->msg);
s = format_expand(ft, SHOW_MESSAGES_TEMPLATE);
cmdq_print(item, "%s", s);
free(s);
}
format_free(ft);
return (CMD_RETURN_NORMAL);
}

View File

@@ -38,7 +38,7 @@ const struct cmd_entry cmd_show_options_entry = {
.name = "show-options",
.alias = "show",
.args = { "AgHpqst:vw", 0, 1 },
.args = { "AgHpqst:vw", 0, 1, NULL },
.usage = "[-AgHpqsvw] " CMD_TARGET_PANE_USAGE " [option]",
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
@@ -51,7 +51,7 @@ const struct cmd_entry cmd_show_window_options_entry = {
.name = "show-window-options",
.alias = "showw",
.args = { "gvt:", 0, 1 },
.args = { "gvt:", 0, 1, NULL },
.usage = "[-gv] " CMD_TARGET_WINDOW_USAGE " [option]",
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL },
@@ -64,10 +64,10 @@ const struct cmd_entry cmd_show_hooks_entry = {
.name = "show-hooks",
.alias = NULL,
.args = { "gt:", 0, 1 },
.usage = "[-g] " CMD_TARGET_SESSION_USAGE,
.args = { "gpt:w", 0, 1, NULL },
.usage = "[-gpw] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_SESSION, 0 },
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
.flags = CMD_AFTERHOOK,
.exec = cmd_show_options_exec
@@ -76,20 +76,18 @@ const struct cmd_entry cmd_show_hooks_entry = {
static enum cmd_retval
cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct cmd_find_state *fs = &item->target;
struct client *c = cmd_find_client(item, NULL, 1);
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct options *oo;
char *argument, *name = NULL, *cause;
int window, idx, ambiguous, parent, scope;
struct options_entry *o;
window = (self->entry == &cmd_show_window_options_entry);
window = (cmd_get_entry(self) == &cmd_show_window_options_entry);
if (args->argc == 0) {
scope = options_scope_from_flags(args, window, fs, &oo, &cause);
if (args_count(args) == 0) {
scope = options_scope_from_flags(args, window, target, &oo,
&cause);
if (scope == OPTIONS_TABLE_NONE) {
if (args_has(args, 'q'))
return (CMD_RETURN_NORMAL);
@@ -99,7 +97,7 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
}
return (cmd_show_options_all(self, item, scope, oo));
}
argument = format_single(item, args->argv[0], c, s, wl, NULL);
argument = format_single_from_target(item, args_string(args, 0));
name = options_match(argument, &idx, &ambiguous);
if (name == NULL) {
@@ -111,7 +109,8 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
cmdq_error(item, "invalid option: %s", argument);
goto fail;
}
scope = options_scope_from_name(args, window, name, fs, &oo, &cause);
scope = options_scope_from_name(args, window, name, target, &oo,
&cause);
if (scope == OPTIONS_TABLE_NONE) {
if (args_has(args, 'q'))
goto fail;
@@ -142,6 +141,7 @@ static void
cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
struct options_entry *o, int idx, int parent)
{
struct args *args = cmd_get_args(self);
struct options_array_item *a;
const char *name = options_name(o);
char *value, *tmp = NULL, *escaped;
@@ -150,10 +150,10 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
xasprintf(&tmp, "%s[%d]", name, idx);
name = tmp;
} else {
if (options_isarray(o)) {
if (options_is_array(o)) {
a = options_array_first(o);
if (a == NULL) {
if (!args_has(self->args, 'v'))
if (!args_has(args, 'v'))
cmdq_print(item, "%s", name);
return;
}
@@ -167,10 +167,10 @@ cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
}
}
value = options_tostring(o, idx, 0);
if (args_has(self->args, 'v'))
value = options_to_string(o, idx, 0);
if (args_has(args, 'v'))
cmdq_print(item, "%s", value);
else if (options_isstring(o)) {
else if (options_is_string(o)) {
escaped = args_escape(value);
if (parent)
cmdq_print(item, "%s* %s", name, escaped);
@@ -192,6 +192,7 @@ static enum cmd_retval
cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope,
struct options *oo)
{
struct args *args = cmd_get_args(self);
const struct options_table_entry *oe;
struct options_entry *o;
struct options_array_item *a;
@@ -199,28 +200,28 @@ cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope,
u_int idx;
int parent;
o = options_first(oo);
while (o != NULL) {
if (options_table_entry(o) == NULL)
cmd_show_options_print(self, item, o, -1, 0);
o = options_next(o);
if (cmd_get_entry(self) != &cmd_show_hooks_entry) {
o = options_first(oo);
while (o != NULL) {
if (options_table_entry(o) == NULL)
cmd_show_options_print(self, item, o, -1, 0);
o = options_next(o);
}
}
for (oe = options_table; oe->name != NULL; oe++) {
if (~oe->scope & scope)
continue;
if ((self->entry != &cmd_show_hooks_entry &&
!args_has(self->args, 'H') &&
oe != NULL &&
if ((cmd_get_entry(self) != &cmd_show_hooks_entry &&
!args_has(args, 'H') &&
(oe->flags & OPTIONS_TABLE_IS_HOOK)) ||
(self->entry == &cmd_show_hooks_entry &&
(oe == NULL ||
(~oe->flags & OPTIONS_TABLE_IS_HOOK))))
(cmd_get_entry(self) == &cmd_show_hooks_entry &&
(~oe->flags & OPTIONS_TABLE_IS_HOOK)))
continue;
o = options_get_only(oo, oe->name);
if (o == NULL) {
if (!args_has(self->args, 'A'))
if (!args_has(args, 'A'))
continue;
o = options_get(oo, oe->name);
if (o == NULL)
@@ -229,10 +230,10 @@ cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope,
} else
parent = 0;
if (!options_isarray(o))
if (!options_is_array(o))
cmd_show_options_print(self, item, o, -1, parent);
else if ((a = options_array_first(o)) == NULL) {
if (!args_has(self->args, 'v')) {
if (!args_has(args, 'v')) {
name = options_name(o);
if (parent)
cmdq_print(item, "%s*", name);

108
cmd-show-prompt-history.c Normal file
View File

@@ -0,0 +1,108 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2021 Anindya Mukherjee <anindya49@hotmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN 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"
#include <stdlib.h>
/*
* Show or clear prompt history.
*/
static enum cmd_retval cmd_show_prompt_history_exec(struct cmd *,
struct cmdq_item *);
const struct cmd_entry cmd_show_prompt_history_entry = {
.name = "show-prompt-history",
.alias = "showphist",
.args = { "T:", 0, 0, NULL },
.usage = "[-T type]",
.flags = CMD_AFTERHOOK,
.exec = cmd_show_prompt_history_exec
};
const struct cmd_entry cmd_clear_prompt_history_entry = {
.name = "clear-prompt-history",
.alias = "clearphist",
.args = { "T:", 0, 0, NULL },
.usage = "[-T type]",
.flags = CMD_AFTERHOOK,
.exec = cmd_show_prompt_history_exec
};
static enum cmd_retval
cmd_show_prompt_history_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
const char *typestr = args_get(args, 'T');
enum prompt_type type;
u_int tidx, hidx;
if (cmd_get_entry(self) == &cmd_clear_prompt_history_entry) {
if (typestr == NULL) {
for (tidx = 0; tidx < PROMPT_NTYPES; tidx++) {
free(status_prompt_hlist[tidx]);
status_prompt_hlist[tidx] = NULL;
status_prompt_hsize[tidx] = 0;
}
} else {
type = status_prompt_type(typestr);
if (type == PROMPT_TYPE_INVALID) {
cmdq_error(item, "invalid type: %s", typestr);
return (CMD_RETURN_ERROR);
}
free(status_prompt_hlist[type]);
status_prompt_hlist[type] = NULL;
status_prompt_hsize[type] = 0;
}
return (CMD_RETURN_NORMAL);
}
if (typestr == NULL) {
for (tidx = 0; tidx < PROMPT_NTYPES; tidx++) {
cmdq_print(item, "History for %s:\n",
status_prompt_type_string(tidx));
for (hidx = 0; hidx < status_prompt_hsize[tidx];
hidx++) {
cmdq_print(item, "%d: %s", hidx + 1,
status_prompt_hlist[tidx][hidx]);
}
cmdq_print(item, "%s", "");
}
} else {
type = status_prompt_type(typestr);
if (type == PROMPT_TYPE_INVALID) {
cmdq_error(item, "invalid type: %s", typestr);
return (CMD_RETURN_ERROR);
}
cmdq_print(item, "History for %s:\n",
status_prompt_type_string(type));
for (hidx = 0; hidx < status_prompt_hsize[type]; hidx++) {
cmdq_print(item, "%d: %s", hidx + 1,
status_prompt_hlist[type][hidx]);
}
cmdq_print(item, "%s", "");
}
return (CMD_RETURN_NORMAL);
}

View File

@@ -35,8 +35,8 @@ const struct cmd_entry cmd_source_file_entry = {
.name = "source-file",
.alias = "source",
.args = { "nqv", 1, -1 },
.usage = "[-nqv] path ...",
.args = { "Fnqv", 1, -1, NULL },
.usage = "[-Fnqv] path ...",
.flags = 0,
.exec = cmd_source_file_exec
@@ -65,14 +65,19 @@ static void
cmd_source_file_complete(struct client *c, struct cmd_source_file_data *cdata)
{
struct cmdq_item *new_item;
u_int i;
if (cfg_finished) {
if (cdata->retval == CMD_RETURN_ERROR && c->session == NULL)
if (cdata->retval == CMD_RETURN_ERROR &&
c != NULL &&
c->session == NULL)
c->retval = 1;
new_item = cmdq_get_callback(cmd_source_file_complete_cb, NULL);
cmdq_insert_after(cdata->after, new_item);
}
for (i = 0; i < cdata->nfiles; i++)
free(cdata->files[i]);
free(cdata->files);
free(cdata);
}
@@ -122,15 +127,15 @@ cmd_source_file_add(struct cmd_source_file_data *cdata, const char *path)
static enum cmd_retval
cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct args *args = cmd_get_args(self);
struct cmd_source_file_data *cdata;
struct client *c = item->client;
struct client *c = cmdq_get_client(item);
enum cmd_retval retval = CMD_RETURN_NORMAL;
char *pattern, *cwd;
char *pattern, *cwd, *expanded = NULL;
const char *path, *error;
glob_t g;
int i, result;
u_int j;
int result;
u_int i, j;
cdata = xcalloc(1, sizeof *cdata);
cdata->item = item;
@@ -144,8 +149,13 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
utf8_stravis(&cwd, server_client_get_cwd(c, NULL), VIS_GLOB);
for (i = 0; i < args->argc; i++) {
path = args->argv[i];
for (i = 0; i < args_count(args); i++) {
path = args_string(args, i);
if (args_has(args, 'F')) {
free(expanded);
expanded = format_single_from_target(item, path);
path = expanded;
}
if (strcmp(path, "-") == 0) {
cmd_source_file_add(cdata, "-");
continue;
@@ -169,6 +179,7 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
cmdq_error(item, "%s: %s", path, error);
retval = CMD_RETURN_ERROR;
}
globfree(&g);
free(pattern);
continue;
}
@@ -176,7 +187,9 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
for (j = 0; j < g.gl_pathc; j++)
cmd_source_file_add(cdata, g.gl_pathv[j]);
globfree(&g);
}
free(expanded);
cdata->after = item;
cdata->retval = retval;

View File

@@ -39,9 +39,10 @@ const struct cmd_entry cmd_split_window_entry = {
.name = "split-window",
.alias = "splitw",
.args = { "bc:de:fF:hIl:p:Pt:v", 0, -1 },
.usage = "[-bdefhIPv] [-c start-directory] [-e environment] "
"[-F format] [-l size] " CMD_TARGET_PANE_USAGE " [command]",
.args = { "bc:de:fF:hIl:p:Pt:vZ", 0, -1, NULL },
.usage = "[-bdefhIPvZ] [-c start-directory] [-e environment] "
"[-F format] [-l size] " CMD_TARGET_PANE_USAGE
"[shell-command]",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -52,21 +53,23 @@ const struct cmd_entry cmd_split_window_entry = {
static enum cmd_retval
cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct cmd_find_state *current = &item->shared->current;
struct spawn_context sc;
struct client *c = cmd_find_client(item, NULL, 1);
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct window_pane *wp = item->target.wp, *new_wp;
struct args *args = cmd_get_args(self);
struct cmd_find_state *current = cmdq_get_current(item);
struct cmd_find_state *target = cmdq_get_target(item);
struct spawn_context sc = { 0 };
struct client *tc = cmdq_get_target_client(item);
struct session *s = target->s;
struct winlink *wl = target->wl;
struct window_pane *wp = target->wp, *new_wp;
enum layout_type type;
struct layout_cell *lc;
struct cmd_find_state fs;
int size, percentage, flags, input;
const char *template, *add, *errstr, *p;
const char *template, *errstr, *p;
char *cause, *cp, *copy;
size_t plen;
struct args_value *value;
struct args_value *av;
u_int count = args_count(args);
if (args_has(args, 'h'))
type = LAYOUT_LEFTRIGHT;
@@ -109,15 +112,15 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
} else
size = -1;
server_unzoom_window(wp->window);
input = (args_has(args, 'I') && args->argc == 0);
window_push_zoom(wp->window, 1, args_has(args, 'Z'));
input = (args_has(args, 'I') && count == 0);
flags = 0;
if (args_has(args, 'b'))
flags |= SPAWN_BEFORE;
if (args_has(args, 'f'))
flags |= SPAWN_FULLSIZE;
if (input || (args->argc == 1 && *args->argv[0] == '\0'))
if (input || (count == 1 && *args_string(args, 0) == '\0'))
flags |= SPAWN_EMPTY;
lc = layout_split_pane(wp, type, size, flags);
@@ -126,7 +129,6 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR);
}
memset(&sc, 0, sizeof sc);
sc.item = item;
sc.s = s;
sc.wl = wl;
@@ -134,15 +136,13 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
sc.wp0 = wp;
sc.lc = lc;
sc.name = NULL;
sc.argc = args->argc;
sc.argv = args->argv;
args_to_vector(args, &sc.argc, &sc.argv);
sc.environ = environ_create();
add = args_first_value(args, 'e', &value);
while (add != NULL) {
environ_put(sc.environ, add);
add = args_next_value(&value);
av = args_first_value(args, 'e');
while (av != NULL) {
environ_put(sc.environ, av->string, 0);
av = args_next_value(av);
}
sc.idx = -1;
@@ -151,29 +151,44 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
sc.flags = flags;
if (args_has(args, 'd'))
sc.flags |= SPAWN_DETACHED;
if (args_has(args, 'Z'))
sc.flags |= SPAWN_ZOOM;
if ((new_wp = spawn_pane(&sc, &cause)) == NULL) {
layout_close_pane(new_wp);
cmdq_error(item, "create pane failed: %s", cause);
free(cause);
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);
return (CMD_RETURN_ERROR);
}
if (input && window_pane_start_input(new_wp, item, &cause) != 0) {
layout_close_pane(new_wp);
window_remove_pane(wp->window, new_wp);
cmdq_error(item, "%s", cause);
free(cause);
return (CMD_RETURN_ERROR);
if (input) {
switch (window_pane_start_input(new_wp, item, &cause)) {
case -1:
server_client_remove_pane(new_wp);
layout_close_pane(new_wp);
window_remove_pane(wp->window, new_wp);
cmdq_error(item, "%s", cause);
free(cause);
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);
return (CMD_RETURN_ERROR);
case 1:
input = 0;
break;
}
}
if (!args_has(args, 'd'))
cmd_find_from_winlink_pane(current, wl, new_wp, 0);
window_pop_zoom(wp->window);
server_redraw_window(wp->window);
server_status_session(s);
if (args_has(args, 'P')) {
if ((template = args_get(args, 'F')) == NULL)
template = SPLIT_WINDOW_TEMPLATE;
cp = format_single(item, template, c, s, wl, new_wp);
cp = format_single(item, template, tc, s, wl, new_wp);
cmdq_print(item, "%s", cp);
free(cp);
}
@@ -181,6 +196,8 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
cmd_find_from_winlink_pane(&fs, wl, new_wp, 0);
cmdq_insert_hook(s, item, &fs, "after-split-window");
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);
if (input)
return (CMD_RETURN_WAIT);

View File

@@ -32,7 +32,7 @@ const struct cmd_entry cmd_swap_pane_entry = {
.name = "swap-pane",
.alias = "swapp",
.args = { "dDs:t:UZ", 0, 0 },
.args = { "dDs:t:UZ", 0, 0, NULL },
.usage = "[-dDUZ] " CMD_SRCDST_PANE_USAGE,
.source = { 's', CMD_FIND_PANE, CMD_FIND_DEFAULT_MARKED },
@@ -45,18 +45,20 @@ const struct cmd_entry cmd_swap_pane_entry = {
static enum cmd_retval
cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct args *args = cmd_get_args(self);
struct cmd_find_state *source = cmdq_get_source(item);
struct cmd_find_state *target = cmdq_get_target(item);
struct window *src_w, *dst_w;
struct window_pane *tmp_wp, *src_wp, *dst_wp;
struct layout_cell *src_lc, *dst_lc;
u_int sx, sy, xoff, yoff;
dst_w = item->target.wl->window;
dst_wp = item->target.wp;
src_w = item->source.wl->window;
src_wp = item->source.wp;
dst_w = target->wl->window;
dst_wp = target->wp;
src_w = source->wl->window;
src_wp = source->wp;
if (window_push_zoom(dst_w, args_has(args, 'Z')))
if (window_push_zoom(dst_w, 0, args_has(args, 'Z')))
server_redraw_window(dst_w);
if (args_has(args, 'D')) {
@@ -71,12 +73,15 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
src_wp = TAILQ_LAST(&dst_w->panes, window_panes);
}
if (src_w != dst_w && window_push_zoom(src_w, args_has(args, 'Z')))
if (src_w != dst_w && window_push_zoom(src_w, 0, args_has(args, 'Z')))
server_redraw_window(src_w);
if (src_wp == dst_wp)
goto out;
server_client_remove_pane(src_wp);
server_client_remove_pane(dst_wp);
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);

View File

@@ -32,7 +32,7 @@ const struct cmd_entry cmd_swap_window_entry = {
.name = "swap-window",
.alias = "swapw",
.args = { "ds:t:", 0, 0 },
.args = { "ds:t:", 0, 0, NULL },
.usage = "[-d] " CMD_SRCDST_WINDOW_USAGE,
.source = { 's', CMD_FIND_WINDOW, CMD_FIND_DEFAULT_MARKED },
@@ -45,20 +45,20 @@ const struct cmd_entry cmd_swap_window_entry = {
static enum cmd_retval
cmd_swap_window_exec(struct cmd *self, struct cmdq_item *item)
{
struct session *src, *dst;
struct args *args = cmd_get_args(self);
struct cmd_find_state *source = cmdq_get_source(item);
struct cmd_find_state *target = cmdq_get_target(item);
struct session *src = source->s, *dst = target->s;
struct session_group *sg_src, *sg_dst;
struct winlink *wl_src, *wl_dst;
struct winlink *wl_src = source->wl, *wl_dst = target->wl;
struct window *w_src, *w_dst;
wl_src = item->source.wl;
src = item->source.s;
sg_src = session_group_contains(src);
wl_dst = item->target.wl;
dst = item->target.s;
sg_dst = session_group_contains(dst);
if (src != dst && sg_src != NULL && sg_dst != NULL &&
if (src != dst &&
sg_src != NULL &&
sg_dst != NULL &&
sg_src == sg_dst) {
cmdq_error(item, "can't move window, sessions are grouped");
return (CMD_RETURN_ERROR);
@@ -77,7 +77,7 @@ cmd_swap_window_exec(struct cmd *self, struct cmdq_item *item)
wl_src->window = w_dst;
TAILQ_INSERT_TAIL(&w_dst->winlinks, wl_src, wentry);
if (args_has(self->args, 'd')) {
if (args_has(args, 'd')) {
session_select(dst, wl_dst->idx);
if (src != dst)
session_select(src, wl_src->idx);

View File

@@ -34,24 +34,26 @@ const struct cmd_entry cmd_switch_client_entry = {
.name = "switch-client",
.alias = "switchc",
.args = { "lc:Enpt:rT:Z", 0, 0 },
.args = { "lc:EFnpt:rT:Z", 0, 0, NULL },
.usage = "[-ElnprZ] [-c target-client] [-t target-session] "
"[-T key-table]",
/* -t is special */
.flags = CMD_READONLY,
.flags = CMD_READONLY|CMD_CLIENT_CFLAG,
.exec = cmd_switch_client_exec
};
static enum cmd_retval
cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct args *args = cmd_get_args(self);
struct cmd_find_state *current = cmdq_get_current(item);
struct cmd_find_state target;
const char *tflag = args_get(args, 't');
enum cmd_find_type type;
int flags;
struct client *c;
struct client *tc = cmdq_get_target_client(item);
struct session *s;
struct winlink *wl;
struct window *w;
@@ -59,9 +61,6 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
const char *tablename;
struct key_table *table;
if ((c = cmd_find_client(item, args_get(args, 'c'), 0)) == NULL)
return (CMD_RETURN_ERROR);
if (tflag != NULL && tflag[strcspn(tflag, ":.%")] != '\0') {
type = CMD_FIND_PANE;
flags = 0;
@@ -69,15 +68,18 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
type = CMD_FIND_SESSION;
flags = CMD_FIND_PREFER_UNATTACHED;
}
if (cmd_find_target(&item->target, item, tflag, type, flags) != 0)
if (cmd_find_target(&target, item, tflag, type, flags) != 0)
return (CMD_RETURN_ERROR);
s = item->target.s;
wl = item->target.wl;
w = wl->window;
wp = item->target.wp;
s = target.s;
wl = target.wl;
wp = target.wp;
if (args_has(args, 'r'))
c->flags ^= CLIENT_READONLY;
if (args_has(args, 'r')) {
if (tc->flags & CLIENT_READONLY)
tc->flags &= ~(CLIENT_READONLY|CLIENT_IGNORESIZE);
else
tc->flags |= (CLIENT_READONLY|CLIENT_IGNORESIZE);
}
tablename = args_get(args, 'T');
if (tablename != NULL) {
@@ -87,24 +89,24 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR);
}
table->references++;
key_bindings_unref_table(c->keytable);
c->keytable = table;
key_bindings_unref_table(tc->keytable);
tc->keytable = table;
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'n')) {
if ((s = session_next_session(c->session)) == NULL) {
if ((s = session_next_session(tc->session)) == NULL) {
cmdq_error(item, "can't find next session");
return (CMD_RETURN_ERROR);
}
} else if (args_has(args, 'p')) {
if ((s = session_previous_session(c->session)) == NULL) {
if ((s = session_previous_session(tc->session)) == NULL) {
cmdq_error(item, "can't find previous session");
return (CMD_RETURN_ERROR);
}
} else if (args_has(args, 'l')) {
if (c->last_session != NULL && session_alive(c->last_session))
s = c->last_session;
if (tc->last_session != NULL && session_alive(tc->last_session))
s = tc->last_session;
else
s = NULL;
if (s == NULL) {
@@ -112,10 +114,11 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR);
}
} else {
if (item->client == NULL)
if (cmdq_get_client(item) == NULL)
return (CMD_RETURN_NORMAL);
if (wl != NULL && wp != NULL) {
if (window_push_zoom(w, args_has(self->args, 'Z')))
if (wl != NULL && wp != NULL && wp != wl->window->active) {
w = wl->window;
if (window_push_zoom(w, 0, args_has(args, 'Z')))
server_redraw_window(w);
window_redraw_active_switch(w, wp);
window_set_active_pane(w, wp, 1);
@@ -124,29 +127,16 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
}
if (wl != NULL) {
session_set_current(s, wl);
cmd_find_from_session(&item->shared->current, s, 0);
cmd_find_from_session(current, s, 0);
}
}
if (!args_has(args, 'E'))
environ_update(s->options, c->environ, s->environ);
environ_update(s->options, tc->environ, s->environ);
if (c->session != NULL && c->session != s)
c->last_session = c->session;
c->session = s;
if (~item->shared->flags & CMDQ_SHARED_REPEAT)
server_client_set_key_table(c, NULL);
tty_update_client_offset(c);
status_timer_start(c);
notify_client("client-session-changed", c);
session_update_activity(s, NULL);
gettimeofday(&s->last_attached_time, NULL);
recalculate_sizes();
server_check_unattached();
server_redraw_client(c);
s->curw->flags &= ~WINLINK_ALERTFLAGS;
alerts_check_session(s);
server_client_set_session(tc, s);
if (~cmdq_get_flags(item) & CMDQ_STATE_REPEAT)
server_client_set_key_table(tc, NULL);
return (CMD_RETURN_NORMAL);
}

View File

@@ -32,8 +32,8 @@ const struct cmd_entry cmd_unbind_key_entry = {
.name = "unbind-key",
.alias = "unbind",
.args = { "anT:", 0, 1 },
.usage = "[-an] [-T key-table] key",
.args = { "anqT:", 0, 1, NULL },
.usage = "[-anq] [-T key-table] key",
.flags = CMD_AFTERHOOK,
.exec = cmd_unbind_key_exec
@@ -42,47 +42,57 @@ const struct cmd_entry cmd_unbind_key_entry = {
static enum cmd_retval
cmd_unbind_key_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct args *args = cmd_get_args(self);
key_code key;
const char *tablename;
const char *tablename, *keystr = args_string(args, 0);
int quiet = args_has(args, 'q');
if (!args_has(args, 'a')) {
if (args->argc != 1) {
cmdq_error(item, "missing key");
if (args_has(args, 'a')) {
if (keystr != NULL) {
if (!quiet)
cmdq_error(item, "key given with -a");
return (CMD_RETURN_ERROR);
}
key = key_string_lookup_string(args->argv[0]);
if (key == KEYC_NONE || key == KEYC_UNKNOWN) {
cmdq_error(item, "unknown key: %s", args->argv[0]);
return (CMD_RETURN_ERROR);
}
} else {
if (args->argc != 0) {
cmdq_error(item, "key given with -a");
return (CMD_RETURN_ERROR);
}
key = KEYC_UNKNOWN;
}
if (key == KEYC_UNKNOWN) {
tablename = args_get(args, 'T');
if (tablename == NULL) {
key_bindings_remove_table("root");
key_bindings_remove_table("prefix");
return (CMD_RETURN_NORMAL);
if (args_has(args, 'n'))
tablename = "root";
else
tablename = "prefix";
}
if (key_bindings_get_table(tablename, 0) == NULL) {
cmdq_error(item, "table %s doesn't exist", tablename);
if (!quiet) {
cmdq_error(item, "table %s doesn't exist" ,
tablename);
}
return (CMD_RETURN_ERROR);
}
key_bindings_remove_table(tablename);
return (CMD_RETURN_NORMAL);
}
if (keystr == NULL) {
if (!quiet)
cmdq_error(item, "missing key");
return (CMD_RETURN_ERROR);
}
key = key_string_lookup_string(keystr);
if (key == KEYC_NONE || key == KEYC_UNKNOWN) {
if (!quiet)
cmdq_error(item, "unknown key: %s", keystr);
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'T')) {
tablename = args_get(args, 'T');
if (key_bindings_get_table(tablename, 0) == NULL) {
cmdq_error(item, "table %s doesn't exist", tablename);
if (!quiet) {
cmdq_error(item, "table %s doesn't exist" ,
tablename);
}
return (CMD_RETURN_ERROR);
}
} else if (args_has(args, 'n'))

View File

@@ -34,7 +34,7 @@ const struct cmd_entry cmd_wait_for_entry = {
.name = "wait-for",
.alias = "wait",
.args = { "LSU", 1, 1 },
.args = { "LSU", 1, 1, NULL },
.usage = "[-L|-S|-U] channel",
.flags = 0,
@@ -120,12 +120,12 @@ cmd_wait_for_remove(struct wait_channel *wc)
static enum cmd_retval
cmd_wait_for_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
const char *name = args->argv[0];
struct wait_channel *wc, wc0;
struct args *args = cmd_get_args(self);
const char *name = args_string(args, 0);
struct wait_channel *wc, find;
wc0.name = name;
wc = RB_FIND(wait_channels, &wait_channels, &wc0);
find.name = name;
wc = RB_FIND(wait_channels, &wait_channels, &find);
if (args_has(args, 'S'))
return (cmd_wait_for_signal(item, name, wc));
@@ -167,7 +167,7 @@ static enum cmd_retval
cmd_wait_for_wait(struct cmdq_item *item, const char *name,
struct wait_channel *wc)
{
struct client *c = item->client;
struct client *c = cmdq_get_client(item);
struct wait_item *wi;
if (c == NULL) {
@@ -198,7 +198,7 @@ cmd_wait_for_lock(struct cmdq_item *item, const char *name,
{
struct wait_item *wi;
if (item->client == NULL) {
if (cmdq_get_client(item) == NULL) {
cmdq_error(item, "not able to lock");
return (CMD_RETURN_ERROR);
}

275
cmd.c
View File

@@ -35,14 +35,17 @@ extern const struct cmd_entry cmd_choose_buffer_entry;
extern const struct cmd_entry cmd_choose_client_entry;
extern const struct cmd_entry cmd_choose_tree_entry;
extern const struct cmd_entry cmd_clear_history_entry;
extern const struct cmd_entry cmd_clear_prompt_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_customize_mode_entry;
extern const struct cmd_entry cmd_delete_buffer_entry;
extern const struct cmd_entry cmd_detach_client_entry;
extern const struct cmd_entry cmd_display_menu_entry;
extern const struct cmd_entry cmd_display_message_entry;
extern const struct cmd_entry cmd_display_popup_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;
@@ -102,6 +105,7 @@ 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_prompt_history_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;
@@ -124,14 +128,17 @@ const struct cmd_entry *cmd_table[] = {
&cmd_choose_client_entry,
&cmd_choose_tree_entry,
&cmd_clear_history_entry,
&cmd_clear_prompt_history_entry,
&cmd_clock_mode_entry,
&cmd_command_prompt_entry,
&cmd_confirm_before_entry,
&cmd_copy_mode_entry,
&cmd_customize_mode_entry,
&cmd_delete_buffer_entry,
&cmd_detach_client_entry,
&cmd_display_menu_entry,
&cmd_display_message_entry,
&cmd_display_popup_entry,
&cmd_display_panes_entry,
&cmd_find_window_entry,
&cmd_has_session_entry,
@@ -190,6 +197,7 @@ const struct cmd_entry *cmd_table[] = {
&cmd_show_hooks_entry,
&cmd_show_messages_entry,
&cmd_show_options_entry,
&cmd_show_prompt_history_entry,
&cmd_show_window_options_entry,
&cmd_source_file_entry,
&cmd_split_window_entry,
@@ -204,8 +212,23 @@ const struct cmd_entry *cmd_table[] = {
NULL
};
/* Instance of a command. */
struct cmd {
const struct cmd_entry *entry;
struct args *args;
u_int group;
char *file;
u_int line;
TAILQ_ENTRY(cmd) qentry;
};
TAILQ_HEAD(cmds, cmd);
/* Next group number for new command list. */
static u_int cmd_list_next_group = 1;
/* Log an argument vector. */
void printflike(3, 4)
cmd_log_argv(int argc, char **argv, const char *fmt, ...)
{
@@ -222,8 +245,9 @@ cmd_log_argv(int argc, char **argv, const char *fmt, ...)
free(prefix);
}
/* Prepend to an argument vector. */
void
cmd_prepend_argv(int *argc, char ***argv, char *arg)
cmd_prepend_argv(int *argc, char ***argv, const char *arg)
{
char **new_argv;
int i;
@@ -238,13 +262,15 @@ cmd_prepend_argv(int *argc, char ***argv, char *arg)
(*argc)++;
}
/* Append to an argument vector. */
void
cmd_append_argv(int *argc, char ***argv, char *arg)
cmd_append_argv(int *argc, char ***argv, const char *arg)
{
*argv = xreallocarray(*argv, (*argc) + 1, sizeof **argv);
(*argv)[(*argc)++] = xstrdup(arg);
}
/* Pack an argument vector up into a buffer. */
int
cmd_pack_argv(int argc, char **argv, char *buf, size_t len)
{
@@ -267,6 +293,7 @@ cmd_pack_argv(int argc, char **argv, char *buf, size_t len)
return (0);
}
/* Unpack an argument vector from a packed buffer. */
int
cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv)
{
@@ -295,6 +322,7 @@ cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv)
return (0);
}
/* Copy an argument vector, ensuring it is terminated by NULL. */
char **
cmd_copy_argv(int argc, char **argv)
{
@@ -311,6 +339,7 @@ cmd_copy_argv(int argc, char **argv)
return (new_argv);
}
/* Free an argument vector. */
void
cmd_free_argv(int argc, char **argv)
{
@@ -323,32 +352,67 @@ cmd_free_argv(int argc, char **argv)
free(argv);
}
/* Convert argument vector to a string. */
char *
cmd_stringify_argv(int argc, char **argv)
{
char *buf;
char *buf = NULL, *s;
size_t len = 0;
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;
s = args_escape(argv[i]);
log_debug("%s: %u %s = %s", __func__, i, argv[i], s);
len += strlen(s) + 1;
buf = xrealloc(buf, len);
if (i == 0)
*buf = '\0';
else
strlcat(buf, " ", len);
strlcat(buf, argv[i], len);
strlcat(buf, s, len);
free(s);
}
return (buf);
}
/* Get entry for command. */
const struct cmd_entry *
cmd_get_entry(struct cmd *cmd)
{
return (cmd->entry);
}
/* Get arguments for command. */
struct args *
cmd_get_args(struct cmd *cmd)
{
return (cmd->args);
}
/* Get group for command. */
u_int
cmd_get_group(struct cmd *cmd)
{
return (cmd->group);
}
/* Get file and line for command. */
void
cmd_get_source(struct cmd *cmd, const char **file, u_int *line)
{
if (file != NULL)
*file = cmd->file;
if (line != NULL)
*line = cmd->line;
}
/* Look for an alias for a command. */
char *
cmd_get_alias(const char *name)
{
@@ -379,6 +443,7 @@ cmd_get_alias(const char *name)
return (NULL);
}
/* Look up a command entry by name. */
static const struct cmd_entry *
cmd_find(const char *name, char **cause)
{
@@ -428,32 +493,34 @@ ambiguous:
return (NULL);
}
/* Parse a single command from an argument vector. */
struct cmd *
cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause)
cmd_parse(struct args_value *values, u_int count, const char *file, u_int line,
char **cause)
{
const struct cmd_entry *entry;
const char *name;
struct cmd *cmd;
struct args *args;
char *error = NULL;
if (argc == 0) {
if (count == 0 || values[0].type != ARGS_STRING) {
xasprintf(cause, "no command");
return (NULL);
}
name = argv[0];
entry = cmd_find(name, cause);
entry = cmd_find(values[0].string, cause);
if (entry == NULL)
return (NULL);
cmd_log_argv(argc, argv, "%s: %s", __func__, entry->name);
args = args_parse(entry->args.template, argc, argv);
if (args == NULL)
goto usage;
if (entry->args.lower != -1 && args->argc < entry->args.lower)
goto usage;
if (entry->args.upper != -1 && args->argc > entry->args.upper)
goto usage;
args = args_parse(&entry->args, values, count, &error);
if (args == NULL && error == NULL) {
xasprintf(cause, "usage: %s %s", entry->name, entry->usage);
return (NULL);
}
if (args == NULL) {
xasprintf(cause, "command %s: %s", entry->name, error);
free(error);
return (NULL);
}
cmd = xcalloc(1, sizeof *cmd);
cmd->entry = entry;
@@ -463,31 +530,37 @@ cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause)
cmd->file = xstrdup(file);
cmd->line = line;
cmd->alias = NULL;
cmd->argc = argc;
cmd->argv = cmd_copy_argv(argc, argv);
return (cmd);
usage:
if (args != NULL)
args_free(args);
xasprintf(cause, "usage: %s %s", entry->name, entry->usage);
return (NULL);
}
/* Free a command. */
void
cmd_free(struct cmd *cmd)
{
free(cmd->alias);
cmd_free_argv(cmd->argc, cmd->argv);
free(cmd->file);
args_free(cmd->args);
free(cmd);
}
/* Copy a command. */
struct cmd *
cmd_copy(struct cmd *cmd, int argc, char **argv)
{
struct cmd *new_cmd;
new_cmd = xcalloc(1, sizeof *new_cmd);
new_cmd->entry = cmd->entry;
new_cmd->args = args_copy(cmd->args, argc, argv);
if (cmd->file != NULL)
new_cmd->file = xstrdup(cmd->file);
new_cmd->line = cmd->line;
return (new_cmd);
}
/* Get a command as a string. */
char *
cmd_print(struct cmd *cmd)
{
@@ -503,6 +576,7 @@ cmd_print(struct cmd *cmd)
return (out);
}
/* Create a new command list. */
struct cmd_list *
cmd_list_new(void)
{
@@ -511,29 +585,39 @@ cmd_list_new(void)
cmdlist = xcalloc(1, sizeof *cmdlist);
cmdlist->references = 1;
cmdlist->group = cmd_list_next_group++;
TAILQ_INIT(&cmdlist->list);
cmdlist->list = xcalloc(1, sizeof *cmdlist->list);
TAILQ_INIT(cmdlist->list);
return (cmdlist);
}
/* Append a command to a command list. */
void
cmd_list_append(struct cmd_list *cmdlist, struct cmd *cmd)
{
cmd->group = cmdlist->group;
TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry);
TAILQ_INSERT_TAIL(cmdlist->list, cmd, qentry);
}
/* Append all commands from one list to another. */
void
cmd_list_append_all(struct cmd_list *cmdlist, struct cmd_list *from)
{
struct cmd *cmd;
TAILQ_FOREACH(cmd, from->list, qentry)
cmd->group = cmdlist->group;
TAILQ_CONCAT(cmdlist->list, from->list, qentry);
}
/* Move all commands from one command list to another. */
void
cmd_list_move(struct cmd_list *cmdlist, struct cmd_list *from)
{
struct cmd *cmd, *cmd1;
TAILQ_FOREACH_SAFE(cmd, &from->list, qentry, cmd1) {
TAILQ_REMOVE(&from->list, cmd, qentry);
TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry);
}
TAILQ_CONCAT(cmdlist->list, from->list, qentry);
cmdlist->group = cmd_list_next_group++;
}
/* Free a command list. */
void
cmd_list_free(struct cmd_list *cmdlist)
{
@@ -542,36 +626,77 @@ cmd_list_free(struct cmd_list *cmdlist)
if (--cmdlist->references != 0)
return;
TAILQ_FOREACH_SAFE(cmd, &cmdlist->list, qentry, cmd1) {
TAILQ_REMOVE(&cmdlist->list, cmd, qentry);
TAILQ_FOREACH_SAFE(cmd, cmdlist->list, qentry, cmd1) {
TAILQ_REMOVE(cmdlist->list, cmd, qentry);
cmd_free(cmd);
}
free(cmdlist->list);
free(cmdlist);
}
/* Copy a command list, expanding %s in arguments. */
struct cmd_list *
cmd_list_copy(struct cmd_list *cmdlist, int argc, char **argv)
{
struct cmd *cmd;
struct cmd_list *new_cmdlist;
struct cmd *new_cmd;
u_int group = cmdlist->group;
char *s;
s = cmd_list_print(cmdlist, 0);
log_debug("%s: %s", __func__, s);
free(s);
new_cmdlist = cmd_list_new();
TAILQ_FOREACH(cmd, cmdlist->list, qentry) {
if (cmd->group != group) {
new_cmdlist->group = cmd_list_next_group++;
group = cmd->group;
}
new_cmd = cmd_copy(cmd, argc, argv);
cmd_list_append(new_cmdlist, new_cmd);
}
s = cmd_list_print(new_cmdlist, 0);
log_debug("%s: %s", __func__, s);
free(s);
return (new_cmdlist);
}
/* Get a command list as a string. */
char *
cmd_list_print(struct cmd_list *cmdlist, int escaped)
{
struct cmd *cmd;
struct cmd *cmd, *next;
char *buf, *this;
size_t len;
len = 1;
buf = xcalloc(1, len);
TAILQ_FOREACH(cmd, &cmdlist->list, qentry) {
TAILQ_FOREACH(cmd, cmdlist->list, qentry) {
this = cmd_print(cmd);
len += strlen(this) + 4;
len += strlen(this) + 6;
buf = xrealloc(buf, len);
strlcat(buf, this, len);
if (TAILQ_NEXT(cmd, qentry) != NULL) {
if (escaped)
strlcat(buf, " \\; ", len);
else
strlcat(buf, " ; ", len);
next = TAILQ_NEXT(cmd, qentry);
if (next != NULL) {
if (cmd->group != next->group) {
if (escaped)
strlcat(buf, " \\;\\; ", len);
else
strlcat(buf, " ;; ", len);
} else {
if (escaped)
strlcat(buf, " \\; ", len);
else
strlcat(buf, " ; ", len);
}
}
free(this);
@@ -580,6 +705,46 @@ cmd_list_print(struct cmd_list *cmdlist, int escaped)
return (buf);
}
/* Get first command in list. */
struct cmd *
cmd_list_first(struct cmd_list *cmdlist)
{
return (TAILQ_FIRST(cmdlist->list));
}
/* Get next command in list. */
struct cmd *
cmd_list_next(struct cmd *cmd)
{
return (TAILQ_NEXT(cmd, qentry));
}
/* Do all of the commands in this command list have this flag? */
int
cmd_list_all_have(struct cmd_list *cmdlist, int flag)
{
struct cmd *cmd;
TAILQ_FOREACH(cmd, cmdlist->list, qentry) {
if (~cmd->entry->flags & flag)
return (0);
}
return (1);
}
/* Do any of the commands in this command list have this flag? */
int
cmd_list_any_have(struct cmd_list *cmdlist, int flag)
{
struct cmd *cmd;
TAILQ_FOREACH(cmd, cmdlist->list, qentry) {
if (cmd->entry->flags & flag)
return (1);
}
return (0);
}
/* Adjust current mouse position for a pane. */
int
cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp,

729
colour.c
View File

@@ -22,6 +22,7 @@
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "tmux.h"
@@ -111,6 +112,9 @@ colour_tostring(int c)
static char s[32];
u_char r, g, b;
if (c == -1)
return ("invalid");
if (c & COLOUR_FLAG_RGB) {
colour_split_rgb(c, &r, &g, &b);
xsnprintf(s, sizeof s, "#%02x%02x%02x", r, g, b);
@@ -189,6 +193,12 @@ colour_fromstring(const char *s)
return (-1);
return (n | COLOUR_FLAG_256);
}
if (strncasecmp(s, "color", (sizeof "color") - 1) == 0) {
n = strtonum(s + (sizeof "color") - 1, 0, 255, &errstr);
if (errstr != NULL)
return (-1);
return (n | COLOUR_FLAG_256);
}
if (strcasecmp(s, "default") == 0)
return (8);
@@ -227,7 +237,7 @@ colour_fromstring(const char *s)
return (96);
if (strcasecmp(s, "brightwhite") == 0 || strcmp(s, "97") == 0)
return (97);
return (-1);
return (colour_byname(s));
}
/* Convert 256 colour to RGB colour. */
@@ -329,3 +339,720 @@ colour_256to16(int c)
return (table[c & 0xff]);
}
/* Get colour by X11 colour name. */
int
colour_byname(const char *name)
{
static const struct {
const char *name;
int c;
} colours[] = {
{ "AliceBlue", 0xf0f8ff },
{ "AntiqueWhite", 0xfaebd7 },
{ "AntiqueWhite1", 0xffefdb },
{ "AntiqueWhite2", 0xeedfcc },
{ "AntiqueWhite3", 0xcdc0b0 },
{ "AntiqueWhite4", 0x8b8378 },
{ "BlanchedAlmond", 0xffebcd },
{ "BlueViolet", 0x8a2be2 },
{ "CadetBlue", 0x5f9ea0 },
{ "CadetBlue1", 0x98f5ff },
{ "CadetBlue2", 0x8ee5ee },
{ "CadetBlue3", 0x7ac5cd },
{ "CadetBlue4", 0x53868b },
{ "CornflowerBlue", 0x6495ed },
{ "DarkBlue", 0x00008b },
{ "DarkCyan", 0x008b8b },
{ "DarkGoldenrod", 0xb8860b },
{ "DarkGoldenrod1", 0xffb90f },
{ "DarkGoldenrod2", 0xeead0e },
{ "DarkGoldenrod3", 0xcd950c },
{ "DarkGoldenrod4", 0x8b6508 },
{ "DarkGray", 0xa9a9a9 },
{ "DarkGreen", 0x006400 },
{ "DarkGrey", 0xa9a9a9 },
{ "DarkKhaki", 0xbdb76b },
{ "DarkMagenta", 0x8b008b },
{ "DarkOliveGreen", 0x556b2f },
{ "DarkOliveGreen1", 0xcaff70 },
{ "DarkOliveGreen2", 0xbcee68 },
{ "DarkOliveGreen3", 0xa2cd5a },
{ "DarkOliveGreen4", 0x6e8b3d },
{ "DarkOrange", 0xff8c00 },
{ "DarkOrange1", 0xff7f00 },
{ "DarkOrange2", 0xee7600 },
{ "DarkOrange3", 0xcd6600 },
{ "DarkOrange4", 0x8b4500 },
{ "DarkOrchid", 0x9932cc },
{ "DarkOrchid1", 0xbf3eff },
{ "DarkOrchid2", 0xb23aee },
{ "DarkOrchid3", 0x9a32cd },
{ "DarkOrchid4", 0x68228b },
{ "DarkRed", 0x8b0000 },
{ "DarkSalmon", 0xe9967a },
{ "DarkSeaGreen", 0x8fbc8f },
{ "DarkSeaGreen1", 0xc1ffc1 },
{ "DarkSeaGreen2", 0xb4eeb4 },
{ "DarkSeaGreen3", 0x9bcd9b },
{ "DarkSeaGreen4", 0x698b69 },
{ "DarkSlateBlue", 0x483d8b },
{ "DarkSlateGray", 0x2f4f4f },
{ "DarkSlateGray1", 0x97ffff },
{ "DarkSlateGray2", 0x8deeee },
{ "DarkSlateGray3", 0x79cdcd },
{ "DarkSlateGray4", 0x528b8b },
{ "DarkSlateGrey", 0x2f4f4f },
{ "DarkTurquoise", 0x00ced1 },
{ "DarkViolet", 0x9400d3 },
{ "DeepPink", 0xff1493 },
{ "DeepPink1", 0xff1493 },
{ "DeepPink2", 0xee1289 },
{ "DeepPink3", 0xcd1076 },
{ "DeepPink4", 0x8b0a50 },
{ "DeepSkyBlue", 0x00bfff },
{ "DeepSkyBlue1", 0x00bfff },
{ "DeepSkyBlue2", 0x00b2ee },
{ "DeepSkyBlue3", 0x009acd },
{ "DeepSkyBlue4", 0x00688b },
{ "DimGray", 0x696969 },
{ "DimGrey", 0x696969 },
{ "DodgerBlue", 0x1e90ff },
{ "DodgerBlue1", 0x1e90ff },
{ "DodgerBlue2", 0x1c86ee },
{ "DodgerBlue3", 0x1874cd },
{ "DodgerBlue4", 0x104e8b },
{ "FloralWhite", 0xfffaf0 },
{ "ForestGreen", 0x228b22 },
{ "GhostWhite", 0xf8f8ff },
{ "GreenYellow", 0xadff2f },
{ "HotPink", 0xff69b4 },
{ "HotPink1", 0xff6eb4 },
{ "HotPink2", 0xee6aa7 },
{ "HotPink3", 0xcd6090 },
{ "HotPink4", 0x8b3a62 },
{ "IndianRed", 0xcd5c5c },
{ "IndianRed1", 0xff6a6a },
{ "IndianRed2", 0xee6363 },
{ "IndianRed3", 0xcd5555 },
{ "IndianRed4", 0x8b3a3a },
{ "LavenderBlush", 0xfff0f5 },
{ "LavenderBlush1", 0xfff0f5 },
{ "LavenderBlush2", 0xeee0e5 },
{ "LavenderBlush3", 0xcdc1c5 },
{ "LavenderBlush4", 0x8b8386 },
{ "LawnGreen", 0x7cfc00 },
{ "LemonChiffon", 0xfffacd },
{ "LemonChiffon1", 0xfffacd },
{ "LemonChiffon2", 0xeee9bf },
{ "LemonChiffon3", 0xcdc9a5 },
{ "LemonChiffon4", 0x8b8970 },
{ "LightBlue", 0xadd8e6 },
{ "LightBlue1", 0xbfefff },
{ "LightBlue2", 0xb2dfee },
{ "LightBlue3", 0x9ac0cd },
{ "LightBlue4", 0x68838b },
{ "LightCoral", 0xf08080 },
{ "LightCyan", 0xe0ffff },
{ "LightCyan1", 0xe0ffff },
{ "LightCyan2", 0xd1eeee },
{ "LightCyan3", 0xb4cdcd },
{ "LightCyan4", 0x7a8b8b },
{ "LightGoldenrod", 0xeedd82 },
{ "LightGoldenrod1", 0xffec8b },
{ "LightGoldenrod2", 0xeedc82 },
{ "LightGoldenrod3", 0xcdbe70 },
{ "LightGoldenrod4", 0x8b814c },
{ "LightGoldenrodYellow", 0xfafad2 },
{ "LightGray", 0xd3d3d3 },
{ "LightGreen", 0x90ee90 },
{ "LightGrey", 0xd3d3d3 },
{ "LightPink", 0xffb6c1 },
{ "LightPink1", 0xffaeb9 },
{ "LightPink2", 0xeea2ad },
{ "LightPink3", 0xcd8c95 },
{ "LightPink4", 0x8b5f65 },
{ "LightSalmon", 0xffa07a },
{ "LightSalmon1", 0xffa07a },
{ "LightSalmon2", 0xee9572 },
{ "LightSalmon3", 0xcd8162 },
{ "LightSalmon4", 0x8b5742 },
{ "LightSeaGreen", 0x20b2aa },
{ "LightSkyBlue", 0x87cefa },
{ "LightSkyBlue1", 0xb0e2ff },
{ "LightSkyBlue2", 0xa4d3ee },
{ "LightSkyBlue3", 0x8db6cd },
{ "LightSkyBlue4", 0x607b8b },
{ "LightSlateBlue", 0x8470ff },
{ "LightSlateGray", 0x778899 },
{ "LightSlateGrey", 0x778899 },
{ "LightSteelBlue", 0xb0c4de },
{ "LightSteelBlue1", 0xcae1ff },
{ "LightSteelBlue2", 0xbcd2ee },
{ "LightSteelBlue3", 0xa2b5cd },
{ "LightSteelBlue4", 0x6e7b8b },
{ "LightYellow", 0xffffe0 },
{ "LightYellow1", 0xffffe0 },
{ "LightYellow2", 0xeeeed1 },
{ "LightYellow3", 0xcdcdb4 },
{ "LightYellow4", 0x8b8b7a },
{ "LimeGreen", 0x32cd32 },
{ "MediumAquamarine", 0x66cdaa },
{ "MediumBlue", 0x0000cd },
{ "MediumOrchid", 0xba55d3 },
{ "MediumOrchid1", 0xe066ff },
{ "MediumOrchid2", 0xd15fee },
{ "MediumOrchid3", 0xb452cd },
{ "MediumOrchid4", 0x7a378b },
{ "MediumPurple", 0x9370db },
{ "MediumPurple1", 0xab82ff },
{ "MediumPurple2", 0x9f79ee },
{ "MediumPurple3", 0x8968cd },
{ "MediumPurple4", 0x5d478b },
{ "MediumSeaGreen", 0x3cb371 },
{ "MediumSlateBlue", 0x7b68ee },
{ "MediumSpringGreen", 0x00fa9a },
{ "MediumTurquoise", 0x48d1cc },
{ "MediumVioletRed", 0xc71585 },
{ "MidnightBlue", 0x191970 },
{ "MintCream", 0xf5fffa },
{ "MistyRose", 0xffe4e1 },
{ "MistyRose1", 0xffe4e1 },
{ "MistyRose2", 0xeed5d2 },
{ "MistyRose3", 0xcdb7b5 },
{ "MistyRose4", 0x8b7d7b },
{ "NavajoWhite", 0xffdead },
{ "NavajoWhite1", 0xffdead },
{ "NavajoWhite2", 0xeecfa1 },
{ "NavajoWhite3", 0xcdb38b },
{ "NavajoWhite4", 0x8b795e },
{ "NavyBlue", 0x000080 },
{ "OldLace", 0xfdf5e6 },
{ "OliveDrab", 0x6b8e23 },
{ "OliveDrab1", 0xc0ff3e },
{ "OliveDrab2", 0xb3ee3a },
{ "OliveDrab3", 0x9acd32 },
{ "OliveDrab4", 0x698b22 },
{ "OrangeRed", 0xff4500 },
{ "OrangeRed1", 0xff4500 },
{ "OrangeRed2", 0xee4000 },
{ "OrangeRed3", 0xcd3700 },
{ "OrangeRed4", 0x8b2500 },
{ "PaleGoldenrod", 0xeee8aa },
{ "PaleGreen", 0x98fb98 },
{ "PaleGreen1", 0x9aff9a },
{ "PaleGreen2", 0x90ee90 },
{ "PaleGreen3", 0x7ccd7c },
{ "PaleGreen4", 0x548b54 },
{ "PaleTurquoise", 0xafeeee },
{ "PaleTurquoise1", 0xbbffff },
{ "PaleTurquoise2", 0xaeeeee },
{ "PaleTurquoise3", 0x96cdcd },
{ "PaleTurquoise4", 0x668b8b },
{ "PaleVioletRed", 0xdb7093 },
{ "PaleVioletRed1", 0xff82ab },
{ "PaleVioletRed2", 0xee799f },
{ "PaleVioletRed3", 0xcd6889 },
{ "PaleVioletRed4", 0x8b475d },
{ "PapayaWhip", 0xffefd5 },
{ "PeachPuff", 0xffdab9 },
{ "PeachPuff1", 0xffdab9 },
{ "PeachPuff2", 0xeecbad },
{ "PeachPuff3", 0xcdaf95 },
{ "PeachPuff4", 0x8b7765 },
{ "PowderBlue", 0xb0e0e6 },
{ "RebeccaPurple", 0x663399 },
{ "RosyBrown", 0xbc8f8f },
{ "RosyBrown1", 0xffc1c1 },
{ "RosyBrown2", 0xeeb4b4 },
{ "RosyBrown3", 0xcd9b9b },
{ "RosyBrown4", 0x8b6969 },
{ "RoyalBlue", 0x4169e1 },
{ "RoyalBlue1", 0x4876ff },
{ "RoyalBlue2", 0x436eee },
{ "RoyalBlue3", 0x3a5fcd },
{ "RoyalBlue4", 0x27408b },
{ "SaddleBrown", 0x8b4513 },
{ "SandyBrown", 0xf4a460 },
{ "SeaGreen", 0x2e8b57 },
{ "SeaGreen1", 0x54ff9f },
{ "SeaGreen2", 0x4eee94 },
{ "SeaGreen3", 0x43cd80 },
{ "SeaGreen4", 0x2e8b57 },
{ "SkyBlue", 0x87ceeb },
{ "SkyBlue1", 0x87ceff },
{ "SkyBlue2", 0x7ec0ee },
{ "SkyBlue3", 0x6ca6cd },
{ "SkyBlue4", 0x4a708b },
{ "SlateBlue", 0x6a5acd },
{ "SlateBlue1", 0x836fff },
{ "SlateBlue2", 0x7a67ee },
{ "SlateBlue3", 0x6959cd },
{ "SlateBlue4", 0x473c8b },
{ "SlateGray", 0x708090 },
{ "SlateGray1", 0xc6e2ff },
{ "SlateGray2", 0xb9d3ee },
{ "SlateGray3", 0x9fb6cd },
{ "SlateGray4", 0x6c7b8b },
{ "SlateGrey", 0x708090 },
{ "SpringGreen", 0x00ff7f },
{ "SpringGreen1", 0x00ff7f },
{ "SpringGreen2", 0x00ee76 },
{ "SpringGreen3", 0x00cd66 },
{ "SpringGreen4", 0x008b45 },
{ "SteelBlue", 0x4682b4 },
{ "SteelBlue1", 0x63b8ff },
{ "SteelBlue2", 0x5cacee },
{ "SteelBlue3", 0x4f94cd },
{ "SteelBlue4", 0x36648b },
{ "VioletRed", 0xd02090 },
{ "VioletRed1", 0xff3e96 },
{ "VioletRed2", 0xee3a8c },
{ "VioletRed3", 0xcd3278 },
{ "VioletRed4", 0x8b2252 },
{ "WebGray", 0x808080 },
{ "WebGreen", 0x008000 },
{ "WebGrey", 0x808080 },
{ "WebMaroon", 0x800000 },
{ "WebPurple", 0x800080 },
{ "WhiteSmoke", 0xf5f5f5 },
{ "X11Gray", 0xbebebe },
{ "X11Green", 0x00ff00 },
{ "X11Grey", 0xbebebe },
{ "X11Maroon", 0xb03060 },
{ "X11Purple", 0xa020f0 },
{ "YellowGreen", 0x9acd32 },
{ "alice blue", 0xf0f8ff },
{ "antique white", 0xfaebd7 },
{ "aqua", 0x00ffff },
{ "aquamarine", 0x7fffd4 },
{ "aquamarine1", 0x7fffd4 },
{ "aquamarine2", 0x76eec6 },
{ "aquamarine3", 0x66cdaa },
{ "aquamarine4", 0x458b74 },
{ "azure", 0xf0ffff },
{ "azure1", 0xf0ffff },
{ "azure2", 0xe0eeee },
{ "azure3", 0xc1cdcd },
{ "azure4", 0x838b8b },
{ "beige", 0xf5f5dc },
{ "bisque", 0xffe4c4 },
{ "bisque1", 0xffe4c4 },
{ "bisque2", 0xeed5b7 },
{ "bisque3", 0xcdb79e },
{ "bisque4", 0x8b7d6b },
{ "black", 0x000000 },
{ "blanched almond", 0xffebcd },
{ "blue violet", 0x8a2be2 },
{ "blue", 0x0000ff },
{ "blue1", 0x0000ff },
{ "blue2", 0x0000ee },
{ "blue3", 0x0000cd },
{ "blue4", 0x00008b },
{ "brown", 0xa52a2a },
{ "brown1", 0xff4040 },
{ "brown2", 0xee3b3b },
{ "brown3", 0xcd3333 },
{ "brown4", 0x8b2323 },
{ "burlywood", 0xdeb887 },
{ "burlywood1", 0xffd39b },
{ "burlywood2", 0xeec591 },
{ "burlywood3", 0xcdaa7d },
{ "burlywood4", 0x8b7355 },
{ "cadet blue", 0x5f9ea0 },
{ "chartreuse", 0x7fff00 },
{ "chartreuse1", 0x7fff00 },
{ "chartreuse2", 0x76ee00 },
{ "chartreuse3", 0x66cd00 },
{ "chartreuse4", 0x458b00 },
{ "chocolate", 0xd2691e },
{ "chocolate1", 0xff7f24 },
{ "chocolate2", 0xee7621 },
{ "chocolate3", 0xcd661d },
{ "chocolate4", 0x8b4513 },
{ "coral", 0xff7f50 },
{ "coral1", 0xff7256 },
{ "coral2", 0xee6a50 },
{ "coral3", 0xcd5b45 },
{ "coral4", 0x8b3e2f },
{ "cornflower blue", 0x6495ed },
{ "cornsilk", 0xfff8dc },
{ "cornsilk1", 0xfff8dc },
{ "cornsilk2", 0xeee8cd },
{ "cornsilk3", 0xcdc8b1 },
{ "cornsilk4", 0x8b8878 },
{ "crimson", 0xdc143c },
{ "cyan", 0x00ffff },
{ "cyan1", 0x00ffff },
{ "cyan2", 0x00eeee },
{ "cyan3", 0x00cdcd },
{ "cyan4", 0x008b8b },
{ "dark blue", 0x00008b },
{ "dark cyan", 0x008b8b },
{ "dark goldenrod", 0xb8860b },
{ "dark gray", 0xa9a9a9 },
{ "dark green", 0x006400 },
{ "dark grey", 0xa9a9a9 },
{ "dark khaki", 0xbdb76b },
{ "dark magenta", 0x8b008b },
{ "dark olive green", 0x556b2f },
{ "dark orange", 0xff8c00 },
{ "dark orchid", 0x9932cc },
{ "dark red", 0x8b0000 },
{ "dark salmon", 0xe9967a },
{ "dark sea green", 0x8fbc8f },
{ "dark slate blue", 0x483d8b },
{ "dark slate gray", 0x2f4f4f },
{ "dark slate grey", 0x2f4f4f },
{ "dark turquoise", 0x00ced1 },
{ "dark violet", 0x9400d3 },
{ "deep pink", 0xff1493 },
{ "deep sky blue", 0x00bfff },
{ "dim gray", 0x696969 },
{ "dim grey", 0x696969 },
{ "dodger blue", 0x1e90ff },
{ "firebrick", 0xb22222 },
{ "firebrick1", 0xff3030 },
{ "firebrick2", 0xee2c2c },
{ "firebrick3", 0xcd2626 },
{ "firebrick4", 0x8b1a1a },
{ "floral white", 0xfffaf0 },
{ "forest green", 0x228b22 },
{ "fuchsia", 0xff00ff },
{ "gainsboro", 0xdcdcdc },
{ "ghost white", 0xf8f8ff },
{ "gold", 0xffd700 },
{ "gold1", 0xffd700 },
{ "gold2", 0xeec900 },
{ "gold3", 0xcdad00 },
{ "gold4", 0x8b7500 },
{ "goldenrod", 0xdaa520 },
{ "goldenrod1", 0xffc125 },
{ "goldenrod2", 0xeeb422 },
{ "goldenrod3", 0xcd9b1d },
{ "goldenrod4", 0x8b6914 },
{ "green yellow", 0xadff2f },
{ "green", 0x00ff00 },
{ "green1", 0x00ff00 },
{ "green2", 0x00ee00 },
{ "green3", 0x00cd00 },
{ "green4", 0x008b00 },
{ "honeydew", 0xf0fff0 },
{ "honeydew1", 0xf0fff0 },
{ "honeydew2", 0xe0eee0 },
{ "honeydew3", 0xc1cdc1 },
{ "honeydew4", 0x838b83 },
{ "hot pink", 0xff69b4 },
{ "indian red", 0xcd5c5c },
{ "indigo", 0x4b0082 },
{ "ivory", 0xfffff0 },
{ "ivory1", 0xfffff0 },
{ "ivory2", 0xeeeee0 },
{ "ivory3", 0xcdcdc1 },
{ "ivory4", 0x8b8b83 },
{ "khaki", 0xf0e68c },
{ "khaki1", 0xfff68f },
{ "khaki2", 0xeee685 },
{ "khaki3", 0xcdc673 },
{ "khaki4", 0x8b864e },
{ "lavender blush", 0xfff0f5 },
{ "lavender", 0xe6e6fa },
{ "lawn green", 0x7cfc00 },
{ "lemon chiffon", 0xfffacd },
{ "light blue", 0xadd8e6 },
{ "light coral", 0xf08080 },
{ "light cyan", 0xe0ffff },
{ "light goldenrod yellow", 0xfafad2 },
{ "light goldenrod", 0xeedd82 },
{ "light gray", 0xd3d3d3 },
{ "light green", 0x90ee90 },
{ "light grey", 0xd3d3d3 },
{ "light pink", 0xffb6c1 },
{ "light salmon", 0xffa07a },
{ "light sea green", 0x20b2aa },
{ "light sky blue", 0x87cefa },
{ "light slate blue", 0x8470ff },
{ "light slate gray", 0x778899 },
{ "light slate grey", 0x778899 },
{ "light steel blue", 0xb0c4de },
{ "light yellow", 0xffffe0 },
{ "lime green", 0x32cd32 },
{ "lime", 0x00ff00 },
{ "linen", 0xfaf0e6 },
{ "magenta", 0xff00ff },
{ "magenta1", 0xff00ff },
{ "magenta2", 0xee00ee },
{ "magenta3", 0xcd00cd },
{ "magenta4", 0x8b008b },
{ "maroon", 0xb03060 },
{ "maroon1", 0xff34b3 },
{ "maroon2", 0xee30a7 },
{ "maroon3", 0xcd2990 },
{ "maroon4", 0x8b1c62 },
{ "medium aquamarine", 0x66cdaa },
{ "medium blue", 0x0000cd },
{ "medium orchid", 0xba55d3 },
{ "medium purple", 0x9370db },
{ "medium sea green", 0x3cb371 },
{ "medium slate blue", 0x7b68ee },
{ "medium spring green", 0x00fa9a },
{ "medium turquoise", 0x48d1cc },
{ "medium violet red", 0xc71585 },
{ "midnight blue", 0x191970 },
{ "mint cream", 0xf5fffa },
{ "misty rose", 0xffe4e1 },
{ "moccasin", 0xffe4b5 },
{ "navajo white", 0xffdead },
{ "navy blue", 0x000080 },
{ "navy", 0x000080 },
{ "old lace", 0xfdf5e6 },
{ "olive drab", 0x6b8e23 },
{ "olive", 0x808000 },
{ "orange red", 0xff4500 },
{ "orange", 0xffa500 },
{ "orange1", 0xffa500 },
{ "orange2", 0xee9a00 },
{ "orange3", 0xcd8500 },
{ "orange4", 0x8b5a00 },
{ "orchid", 0xda70d6 },
{ "orchid1", 0xff83fa },
{ "orchid2", 0xee7ae9 },
{ "orchid3", 0xcd69c9 },
{ "orchid4", 0x8b4789 },
{ "pale goldenrod", 0xeee8aa },
{ "pale green", 0x98fb98 },
{ "pale turquoise", 0xafeeee },
{ "pale violet red", 0xdb7093 },
{ "papaya whip", 0xffefd5 },
{ "peach puff", 0xffdab9 },
{ "peru", 0xcd853f },
{ "pink", 0xffc0cb },
{ "pink1", 0xffb5c5 },
{ "pink2", 0xeea9b8 },
{ "pink3", 0xcd919e },
{ "pink4", 0x8b636c },
{ "plum", 0xdda0dd },
{ "plum1", 0xffbbff },
{ "plum2", 0xeeaeee },
{ "plum3", 0xcd96cd },
{ "plum4", 0x8b668b },
{ "powder blue", 0xb0e0e6 },
{ "purple", 0xa020f0 },
{ "purple1", 0x9b30ff },
{ "purple2", 0x912cee },
{ "purple3", 0x7d26cd },
{ "purple4", 0x551a8b },
{ "rebecca purple", 0x663399 },
{ "red", 0xff0000 },
{ "red1", 0xff0000 },
{ "red2", 0xee0000 },
{ "red3", 0xcd0000 },
{ "red4", 0x8b0000 },
{ "rosy brown", 0xbc8f8f },
{ "royal blue", 0x4169e1 },
{ "saddle brown", 0x8b4513 },
{ "salmon", 0xfa8072 },
{ "salmon1", 0xff8c69 },
{ "salmon2", 0xee8262 },
{ "salmon3", 0xcd7054 },
{ "salmon4", 0x8b4c39 },
{ "sandy brown", 0xf4a460 },
{ "sea green", 0x2e8b57 },
{ "seashell", 0xfff5ee },
{ "seashell1", 0xfff5ee },
{ "seashell2", 0xeee5de },
{ "seashell3", 0xcdc5bf },
{ "seashell4", 0x8b8682 },
{ "sienna", 0xa0522d },
{ "sienna1", 0xff8247 },
{ "sienna2", 0xee7942 },
{ "sienna3", 0xcd6839 },
{ "sienna4", 0x8b4726 },
{ "silver", 0xc0c0c0 },
{ "sky blue", 0x87ceeb },
{ "slate blue", 0x6a5acd },
{ "slate gray", 0x708090 },
{ "slate grey", 0x708090 },
{ "snow", 0xfffafa },
{ "snow1", 0xfffafa },
{ "snow2", 0xeee9e9 },
{ "snow3", 0xcdc9c9 },
{ "snow4", 0x8b8989 },
{ "spring green", 0x00ff7f },
{ "steel blue", 0x4682b4 },
{ "tan", 0xd2b48c },
{ "tan1", 0xffa54f },
{ "tan2", 0xee9a49 },
{ "tan3", 0xcd853f },
{ "tan4", 0x8b5a2b },
{ "teal", 0x008080 },
{ "thistle", 0xd8bfd8 },
{ "thistle1", 0xffe1ff },
{ "thistle2", 0xeed2ee },
{ "thistle3", 0xcdb5cd },
{ "thistle4", 0x8b7b8b },
{ "tomato", 0xff6347 },
{ "tomato1", 0xff6347 },
{ "tomato2", 0xee5c42 },
{ "tomato3", 0xcd4f39 },
{ "tomato4", 0x8b3626 },
{ "turquoise", 0x40e0d0 },
{ "turquoise1", 0x00f5ff },
{ "turquoise2", 0x00e5ee },
{ "turquoise3", 0x00c5cd },
{ "turquoise4", 0x00868b },
{ "violet red", 0xd02090 },
{ "violet", 0xee82ee },
{ "web gray", 0x808080 },
{ "web green", 0x008000 },
{ "web grey", 0x808080 },
{ "web maroon", 0x800000 },
{ "web purple", 0x800080 },
{ "wheat", 0xf5deb3 },
{ "wheat1", 0xffe7ba },
{ "wheat2", 0xeed8ae },
{ "wheat3", 0xcdba96 },
{ "wheat4", 0x8b7e66 },
{ "white smoke", 0xf5f5f5 },
{ "white", 0xffffff },
{ "x11 gray", 0xbebebe },
{ "x11 green", 0x00ff00 },
{ "x11 grey", 0xbebebe },
{ "x11 maroon", 0xb03060 },
{ "x11 purple", 0xa020f0 },
{ "yellow green", 0x9acd32 },
{ "yellow", 0xffff00 },
{ "yellow1", 0xffff00 },
{ "yellow2", 0xeeee00 },
{ "yellow3", 0xcdcd00 },
{ "yellow4", 0x8b8b00 }
};
u_int i;
int c;
if (strncmp(name, "grey", 4) == 0 || strncmp(name, "gray", 4) == 0) {
if (!isdigit((u_char)name[4]))
return (0xbebebe|COLOUR_FLAG_RGB);
c = round(2.55 * atoi(name + 4));
if (c < 0 || c > 255)
return (-1);
return (colour_join_rgb(c, c, c));
}
for (i = 0; i < nitems(colours); i++) {
if (strcasecmp(colours[i].name, name) == 0)
return (colours[i].c|COLOUR_FLAG_RGB);
}
return (-1);
}
/* Initialize palette. */
void
colour_palette_init(struct colour_palette *p)
{
p->fg = 8;
p->bg = 8;
p->palette = NULL;
p->default_palette = NULL;
}
/* Clear palette. */
void
colour_palette_clear(struct colour_palette *p)
{
if (p != NULL) {
p->fg = 8;
p->bg = 8;
free(p->palette);
p->palette = NULL;
}
}
/* Free a palette. */
void
colour_palette_free(struct colour_palette *p)
{
if (p != NULL) {
free(p->palette);
p->palette = NULL;
free(p->default_palette);
p->default_palette = NULL;
}
}
/* Get a colour from a palette. */
int
colour_palette_get(struct colour_palette *p, int c)
{
if (p == NULL)
return (-1);
if (c >= 90 && c <= 97)
c = 8 + c - 90;
else if (c & COLOUR_FLAG_256)
c &= ~COLOUR_FLAG_256;
else if (c >= 8)
return (-1);
if (p->palette != NULL && p->palette[c] != -1)
return (p->palette[c]);
if (p->default_palette != NULL && p->default_palette[c] != -1)
return (p->default_palette[c]);
return (-1);
}
/* Set a colour in a palette. */
int
colour_palette_set(struct colour_palette *p, int n, int c)
{
u_int i;
if (p == NULL || n > 255)
return (0);
if (c == -1 && p->palette == NULL)
return (0);
if (c != -1 && p->palette == NULL) {
if (p->palette == NULL)
p->palette = xcalloc(256, sizeof *p->palette);
for (i = 0; i < 256; i++)
p->palette[i] = -1;
}
p->palette[n] = c;
return (1);
}
/* Build palette defaults from an option. */
void
colour_palette_from_option(struct colour_palette *p, struct options *oo)
{
struct options_entry *o;
struct options_array_item *a;
u_int i, n;
int c;
if (p == NULL)
return;
o = options_get(oo, "pane-colours");
if ((a = options_array_first(o)) == NULL) {
if (p->default_palette != NULL) {
free(p->default_palette);
p->default_palette = NULL;
}
return;
}
if (p->default_palette == NULL)
p->default_palette = xcalloc(256, sizeof *p->default_palette);
for (i = 0; i < 256; i++)
p->default_palette[i] = -1;
while (a != NULL) {
n = options_array_item_index(a);
if (n < 256) {
c = options_array_item_value(a)->number;
p->default_palette[n] = c;
}
a = options_array_next(a);
}
}

View File

@@ -27,10 +27,35 @@
#include <termios.h>
#include <wchar.h>
#ifdef HAVE_EVENT2_EVENT_H
#include <event2/event.h>
#include <event2/event_compat.h>
#include <event2/event_struct.h>
#include <event2/buffer.h>
#include <event2/buffer_compat.h>
#include <event2/bufferevent.h>
#include <event2/bufferevent_struct.h>
#include <event2/bufferevent_compat.h>
#else
#include <event.h>
#endif
#ifdef HAVE_MALLOC_TRIM
#include <malloc.h>
#endif
#ifdef HAVE_UTF8PROC
#include <utf8proc.h>
#endif
#ifndef __GNUC__
#define __attribute__(a)
#endif
#ifdef BROKEN___DEAD
#undef __dead
#endif
#ifndef __unused
#define __unused __attribute__ ((__unused__))
#endif
@@ -40,6 +65,9 @@
#ifndef __packed
#define __packed __attribute__ ((__packed__))
#endif
#ifndef __weak
#define __weak __attribute__ ((__weak__))
#endif
#ifndef ECHOPRT
#define ECHOPRT 0
@@ -90,10 +118,18 @@ void warnx(const char *, ...);
#define _PATH_DEFPATH "/usr/bin:/bin"
#endif
#ifndef _PATH_VI
#define _PATH_VI "/usr/bin/vi"
#endif
#ifndef __OpenBSD__
#define pledge(s, p) (0)
#endif
#ifndef IMAXBEL
#define IMAXBEL 0
#endif
#ifdef HAVE_STDINT_H
#include <stdint.h>
#else
@@ -229,6 +265,13 @@ void warnx(const char *, ...);
#define HOST_NAME_MAX 255
#endif
#ifndef CLOCK_REALTIME
#define CLOCK_REALTIME 0
#endif
#ifndef CLOCK_MONOTONIC
#define CLOCK_MONOTONIC CLOCK_REALTIME
#endif
#ifndef HAVE_FLOCK
#define LOCK_SH 0
#define LOCK_EX 0
@@ -306,6 +349,11 @@ const char *getprogname(void);
void setproctitle(const char *, ...);
#endif
#ifndef HAVE_CLOCK_GETTIME
/* clock_gettime.c */
int clock_gettime(int, struct timespec *);
#endif
#ifndef HAVE_B64_NTOP
/* base64.c */
#undef b64_ntop
@@ -337,6 +385,11 @@ int vasprintf(char **, const char *, va_list);
char *fgetln(FILE *, size_t *);
#endif
#ifndef HAVE_GETLINE
/* getline.c */
ssize_t getline(char **, size_t *, FILE *);
#endif
#ifndef HAVE_SETENV
/* setenv.c */
int setenv(const char *, const char *, int);
@@ -370,7 +423,11 @@ int utf8proc_mbtowc(wchar_t *, const char *, size_t);
int utf8proc_wctomb(char *, wchar_t);
#endif
#ifndef HAVE_GETOPT
#ifdef NEED_FUZZING
/* tmux.c */
#define main __weak main
#endif
/* getopt.c */
extern int BSDopterr;
extern int BSDoptind;
@@ -384,6 +441,5 @@ int BSDgetopt(int, char *const *, const char *);
#define optopt BSDoptopt
#define optreset BSDoptreset
#define optarg BSDoptarg
#endif
#endif /* COMPAT_H */

37
compat/clock_gettime.c Normal file
View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2021 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <sys/time.h>
#include "compat.h"
#ifndef TIMEVAL_TO_TIMESPEC
#define TIMEVAL_TO_TIMESPEC(tv, ts) do { \
(ts)->tv_sec = (tv)->tv_sec; \
(ts)->tv_nsec = (tv)->tv_usec * 1000; \
} while (0)
#endif
int
clock_gettime(int clock, struct timespec *ts)
{
struct timeval tv;
gettimeofday(&tv, NULL);
TIMEVAL_TO_TIMESPEC(&tv, ts);
return 0;
}

View File

@@ -44,6 +44,9 @@
# include <ndir.h>
# endif
#endif
#if defined(HAVE_LIBPROC_H)
# include <libproc.h>
#endif
#include "compat.h"
@@ -55,39 +58,15 @@
__unused static const char rcsid[] = "$Sudo: closefrom.c,v 1.11 2006/08/17 15:26:54 millert Exp $";
#endif /* lint */
#ifndef HAVE_FCNTL_CLOSEM
/*
* Close all file descriptors greater than or equal to lowfd.
*/
#ifdef HAVE_FCNTL_CLOSEM
void
closefrom(int lowfd)
static void
closefrom_fallback(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;
long fd, maxfd;
/* 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
@@ -99,11 +78,78 @@ closefrom(int lowfd)
maxfd = getdtablesize();
#endif /* HAVE_SYSCONF */
if (maxfd < 0)
maxfd = OPEN_MAX;
maxfd = OPEN_MAX;
for (fd = lowfd; fd < maxfd; fd++)
(void) close((int) fd);
(void) close((int) fd);
}
#endif /* HAVE_FCNTL_CLOSEM */
#ifdef HAVE_FCNTL_CLOSEM
void
closefrom(int lowfd)
{
(void) fcntl(lowfd, F_CLOSEM, 0);
}
#elif defined(HAVE_LIBPROC_H) && defined(HAVE_PROC_PIDINFO)
void
closefrom(int lowfd)
{
int i, r, sz;
pid_t pid = getpid();
struct proc_fdinfo *fdinfo_buf = NULL;
sz = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
if (sz == 0)
return; /* no fds, really? */
else if (sz == -1)
goto fallback;
if ((fdinfo_buf = malloc(sz)) == NULL)
goto fallback;
r = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fdinfo_buf, sz);
if (r < 0 || r > sz)
goto fallback;
for (i = 0; i < r / (int)PROC_PIDLISTFD_SIZE; i++) {
if (fdinfo_buf[i].proc_fd >= lowfd)
close(fdinfo_buf[i].proc_fd);
}
free(fdinfo_buf);
return;
fallback:
free(fdinfo_buf);
closefrom_fallback(lowfd);
return;
}
#elif defined(HAVE_DIRFD) && defined(HAVE_PROC_PID)
void
closefrom(int lowfd)
{
long fd;
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);
return;
}
/* /proc/$$/fd strategy failed, fall back to brute force closure */
closefrom_fallback(lowfd);
}
#else
void
closefrom(int lowfd)
{
closefrom_fallback(lowfd);
}
#endif /* !HAVE_FCNTL_CLOSEM */
#endif /* HAVE_CLOSEFROM */

82
compat/forkpty-haiku.c Normal file
View File

@@ -0,0 +1,82 @@
/*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include "compat.h"
void fatal(const char *, ...);
void fatalx(const char *, ...);
pid_t
forkpty(int *master, char *name, struct termios *tio, struct winsize *ws)
{
int slave = -1;
char *path;
pid_t pid;
if ((*master = open("/dev/ptmx", O_RDWR|O_NOCTTY)) == -1)
return (-1);
if (grantpt(*master) != 0)
goto out;
if (unlockpt(*master) != 0)
goto out;
if ((path = ptsname(*master)) == NULL)
goto out;
if (name != NULL)
strlcpy(name, path, TTY_NAME_MAX);
if ((slave = open(path, O_RDWR|O_NOCTTY)) == -1)
goto out;
switch (pid = fork()) {
case -1:
goto out;
case 0:
close(*master);
setsid();
if (ioctl(slave, TIOCSCTTY, NULL) == -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);
}

29
compat/getdtablesize.c Normal file
View File

@@ -0,0 +1,29 @@
/*
* Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <unistd.h>
#include "compat.h"
#ifdef HAVE_SYSCONF
int
getdtablesize(void)
{
return (sysconf(_SC_OPEN_MAX));
}
#endif

93
compat/getline.c Normal file
View File

@@ -0,0 +1,93 @@
/* $NetBSD: getline.c,v 1.1.1.6 2015/01/02 20:34:27 christos Exp $ */
/* NetBSD: getline.c,v 1.2 2014/09/16 17:23:50 christos Exp */
/*-
* Copyright (c) 2011 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Christos Zoulas.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/* NETBSD ORIGINAL: external/bsd/file/dist/src/getline.c */
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include "tmux.h"
static ssize_t
getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp)
{
char *ptr, *eptr;
if (*buf == NULL || *bufsiz == 0) {
if ((*buf = malloc(BUFSIZ)) == NULL)
return -1;
*bufsiz = BUFSIZ;
}
for (ptr = *buf, eptr = *buf + *bufsiz;;) {
int c = fgetc(fp);
if (c == -1) {
if (feof(fp)) {
ssize_t diff = (ssize_t)(ptr - *buf);
if (diff != 0) {
*ptr = '\0';
return diff;
}
}
return -1;
}
*ptr++ = c;
if (c == delimiter) {
*ptr = '\0';
return ptr - *buf;
}
if (ptr + 2 >= eptr) {
char *nbuf;
size_t nbufsiz = *bufsiz * 2;
ssize_t d = ptr - *buf;
if ((nbuf = realloc(*buf, nbufsiz)) == NULL)
return -1;
*buf = nbuf;
*bufsiz = nbufsiz;
eptr = nbuf + nbufsiz;
ptr = nbuf + d;
}
}
}
ssize_t
getline(char **buf, size_t *bufsiz, FILE *fp)
{
return getdelim(buf, bufsiz, '\n', fp);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: imsg-buffer.c,v 1.11 2017/12/14 09:27:44 kettenis Exp $ */
/* $OpenBSD: imsg-buffer.c,v 1.12 2019/01/20 02:50:03 bcook Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -70,7 +70,7 @@ ibuf_dynamic(size_t len, size_t max)
static int
ibuf_realloc(struct ibuf *buf, size_t len)
{
u_char *b;
unsigned char *b;
/* on static buffers max is eq size and so the following fails */
if (buf->wpos + len > buf->max) {

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: imsg.h,v 1.4 2017/03/24 09:34:12 nicm Exp $ */
/* $OpenBSD: imsg.h,v 1.5 2019/01/20 02:50:03 bcook Exp $ */
/*
* Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -21,13 +21,15 @@
#ifndef _IMSG_H_
#define _IMSG_H_
#include <stdint.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;
unsigned char *buf;
size_t size;
size_t max;
size_t wpos;
@@ -42,8 +44,8 @@ struct msgbuf {
};
struct ibuf_read {
u_char buf[IBUF_READ_SIZE];
u_char *rptr;
unsigned char buf[IBUF_READ_SIZE];
unsigned char *rptr;
size_t wpos;
};

View File

@@ -1,6 +1,6 @@
# configure.ac
AC_INIT([tmux], next-3.2)
AC_INIT([tmux], next-3.4)
AC_PREREQ([2.60])
AC_CONFIG_AUX_DIR(etc)
@@ -21,6 +21,26 @@ SAVED_CFLAGS="$CFLAGS"
SAVED_CPPFLAGS="$CPPFLAGS"
SAVED_LDFLAGS="$LDFLAGS"
# Is this oss-fuzz build?
AC_ARG_ENABLE(
fuzzing,
AS_HELP_STRING(--enable-fuzzing, build fuzzers)
)
AC_ARG_VAR(
FUZZING_LIBS,
AS_HELP_STRING(libraries to link fuzzing targets with)
)
# Set up convenient fuzzing defaults before initializing compiler.
if test "x$enable_fuzzing" = xyes; then
AC_DEFINE(NEED_FUZZING)
test "x$CC" = x && CC=clang
test "x$FUZZING_LIBS" = x && \
FUZZING_LIBS="-fsanitize=fuzzer"
test "x$SAVED_CFLAGS" = x && \
AM_CFLAGS="-g -fsanitize=fuzzer-no-link,address"
fi
# Set up the compiler in two different ways and say yes we may want to install.
AC_PROG_CC
AM_PROG_CC_C_O
@@ -39,14 +59,14 @@ test "$sysconfdir" = '${prefix}/etc' && sysconfdir=/etc
case "x$VERSION" in xnext*) enable_debug=yes;; esac
AC_ARG_ENABLE(
debug,
AC_HELP_STRING(--enable-debug, enable debug build flags),
AS_HELP_STRING(--enable-debug, enable debug build flags),
)
AM_CONDITIONAL(IS_DEBUG, test "x$enable_debug" = xyes)
# Is this a static build?
AC_ARG_ENABLE(
static,
AC_HELP_STRING(--enable-static, create a static build)
AS_HELP_STRING(--enable-static, create a static build)
)
if test "x$enable_static" = xyes; then
test "x$PKG_CONFIG" != x && PKG_CONFIG="$PKG_CONFIG --static"
@@ -54,8 +74,26 @@ if test "x$enable_static" = xyes; then
LDFLAGS="$AM_LDFLAGS $SAVED_LDFLAGS"
fi
# Allow default TERM to be set.
AC_ARG_WITH(
TERM,
AS_HELP_STRING(--with-TERM, set default TERM),
[DEFAULT_TERM=$withval],
[DEFAULT_TERM=]
)
case "x$DEFAULT_TERM" in
xscreen*|xtmux*|x)
;;
*)
AC_MSG_ERROR("unsuitable TERM (must be screen* or tmux*)")
;;
esac
# Do we need fuzzers?
AM_CONDITIONAL(NEED_FUZZING, test "x$enable_fuzzing" = xyes)
# Is this gcc?
AM_CONDITIONAL(IS_GCC, test "x$GCC" = xyes)
AM_CONDITIONAL(IS_GCC, test "x$GCC" = xyes -a "x$enable_fuzzing" != xyes)
# Is this Sun CC?
AC_EGREP_CPP(
@@ -76,6 +114,7 @@ AC_CHECK_HEADERS([ \
dirent.h \
fcntl.h \
inttypes.h \
libproc.h \
libutil.h \
ndir.h \
paths.h \
@@ -87,6 +126,12 @@ AC_CHECK_HEADERS([ \
util.h \
])
# Look for sys_signame.
AC_SEARCH_LIBS(sys_signame, , AC_DEFINE(HAVE_SYS_SIGNAME))
# Look for fmod.
AC_CHECK_LIB(m, fmod)
# Look for library needed for flock.
AC_SEARCH_LIBS(flock, bsd)
@@ -95,22 +140,24 @@ AC_CHECK_FUNCS([ \
dirfd \
flock \
prctl \
sysconf \
proc_pidinfo \
sysconf
])
# Check for functions with a compatibility implementation.
AC_REPLACE_FUNCS([ \
asprintf \
cfmakeraw \
clock_gettime \
closefrom \
explicit_bzero \
fgetln \
freezero \
getdtablecount \
getdtablesize \
getline \
getprogname \
memmem \
recallocarray \
reallocarray \
setenv \
setproctitle \
strcasestr \
@@ -118,95 +165,158 @@ AC_REPLACE_FUNCS([ \
strlcpy \
strndup \
strsep \
strtonum \
])
AC_FUNC_STRNLEN
# Check if strtonum works.
AC_MSG_CHECKING([for working strtonum])
AC_RUN_IFELSE([AC_LANG_PROGRAM(
[#include <stdlib.h>],
[return (strtonum("0", 0, 1, NULL) == 0 ? 0 : 1);]
)],
[AC_DEFINE(HAVE_STRTONUM) AC_MSG_RESULT(yes)],
[AC_LIBOBJ(strtonum) AC_MSG_RESULT(no)],
[AC_LIBOBJ(strtonum) AC_MSG_RESULT(no)]
)
# Clang sanitizers wrap reallocarray even if it isn't available on the target
# system. When compiled it always returns NULL and crashes the program. To
# detect this we need a more complicated test.
AC_MSG_CHECKING([for working reallocarray])
AC_RUN_IFELSE([AC_LANG_PROGRAM(
[#include <stdlib.h>],
[return (reallocarray(NULL, 1, 1) == NULL);]
)],
AC_MSG_RESULT(yes),
[AC_LIBOBJ(reallocarray) AC_MSG_RESULT([no])],
[AC_LIBOBJ(reallocarray) AC_MSG_RESULT([no])]
)
AC_MSG_CHECKING([for working recallocarray])
AC_RUN_IFELSE([AC_LANG_PROGRAM(
[#include <stdlib.h>],
[return (recallocarray(NULL, 1, 1, 1) == NULL);]
)],
AC_MSG_RESULT(yes),
[AC_LIBOBJ(recallocarray) AC_MSG_RESULT([no])],
[AC_LIBOBJ(recallocarray) AC_MSG_RESULT([no])]
)
# Look for clock_gettime. Must come before event_init.
AC_SEARCH_LIBS(clock_gettime, rt)
# Look for libevent.
# Always use our getopt because 1) glibc's doesn't enforce argument order 2)
# musl does not set optarg to NULL for flags without arguments (although it is
# not required to, but it is helpful) 3) there are probably other weird
# implementations.
AC_LIBOBJ(getopt)
# Look for libevent. Try libevent_core or libevent with pkg-config first then
# look for the library.
PKG_CHECK_MODULES(
LIBEVENT,
libevent,
LIBEVENT_CORE,
[libevent_core >= 2],
[
AM_CFLAGS="$LIBEVENT_CFLAGS $AM_CFLAGS"
CFLAGS="$AM_CFLAGS $SAVED_CFLAGS"
LIBS="$LIBEVENT_LIBS $LIBS"
AM_CPPFLAGS="$LIBEVENT_CORE_CFLAGS $AM_CPPFLAGS"
CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBEVENT_CORE_LIBS $LIBS"
found_libevent=yes
],
found_libevent=no
)
if test x$found_libevent = xno; then
PKG_CHECK_MODULES(
LIBEVENT,
[libevent >= 2],
[
AM_CPPFLAGS="$LIBEVENT_CFLAGS $AM_CPPFLAGS"
CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBEVENT_LIBS $LIBS"
found_libevent=yes
],
found_libevent=no
)
fi
if test x$found_libevent = xno; then
AC_SEARCH_LIBS(
event_init,
[event_core event event-1.4],
found_libevent=yes,
found_libevent=no
)
fi
AC_CHECK_HEADER(
event2/event.h,
AC_DEFINE(HAVE_EVENT2_EVENT_H),
[
AC_SEARCH_LIBS(
event_init,
[event event-1.4 event2],
found_libevent=yes,
AC_CHECK_HEADER(
event.h,
AC_DEFINE(HAVE_EVENT_H),
found_libevent=no
)
]
)
AC_CHECK_HEADER(
event.h,
,
found_libevent=no
)
if test "x$found_libevent" = xno; then
AC_MSG_ERROR("libevent not found")
fi
# Look for ncurses.
# Look for ncurses or curses. Try pkg-config first then directly for the
# library.
PKG_CHECK_MODULES(
LIBTINFO,
tinfo,
found_ncurses=yes,
[
AM_CPPFLAGS="$LIBTINFO_CFLAGS $AM_CPPFLAGS"
CPPFLAGS="$LIBTINFO_CFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBTINFO_LIBS $LIBS"
found_ncurses=yes
],
found_ncurses=no
)
if test "x$found_ncurses" = xno; then
PKG_CHECK_MODULES(
LIBNCURSES,
ncurses,
found_ncurses=yes,
[
AM_CPPFLAGS="$LIBNCURSES_CFLAGS $AM_CPPFLAGS"
CPPFLAGS="$LIBNCURSES_CFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBNCURSES_LIBS $LIBS"
found_ncurses=yes
],
found_ncurses=no
)
fi
if test "x$found_ncurses" = xno; then
PKG_CHECK_MODULES(
LIBNCURSES,
LIBNCURSESW,
ncursesw,
found_ncurses=yes,
[
AM_CPPFLAGS="$LIBNCURSESW_CFLAGS $AM_CPPFLAGS"
CPPFLAGS="$LIBNCURSESW_CFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBNCURSESW_LIBS $LIBS"
found_ncurses=yes
],
found_ncurses=no
)
fi
if test "x$found_ncurses" = xyes; then
AM_CFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $AM_CFLAGS"
CFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $CFLAGS"
LIBS="$LIBNCURSES_LIBS $LIBTINFO_LIBS $LIBS"
else
# pkg-config didn't work, try ncurses.
AC_CHECK_LIB(
tinfo,
if test "x$found_ncurses" = xno; then
AC_SEARCH_LIBS(
setupterm,
[tinfo ncurses ncursesw],
found_ncurses=yes,
found_ncurses=no
)
if test "x$found_ncurses" = xno; then
AC_CHECK_LIB(
ncurses,
setupterm,
found_ncurses=yes,
found_ncurses=no
)
fi
if test "x$found_ncurses" = xyes; then
AC_CHECK_HEADER(
ncurses.h,
LIBS="$LIBS -lncurses",
found_ncurses=no)
found_ncurses=no
)
fi
fi
if test "x$found_ncurses" = xyes; then
CPPFLAGS="$CPPFLAGS -DHAVE_NCURSES_H"
AC_DEFINE(HAVE_NCURSES_H)
else
# No ncurses, try curses.
AC_CHECK_LIB(
curses,
setupterm,
@@ -216,9 +326,11 @@ else
AC_CHECK_HEADER(
curses.h,
,
found_curses=no)
found_curses=no
)
if test "x$found_curses" = xyes; then
LIBS="$LIBS -lcurses"
CPPFLAGS="$CPPFLAGS -DHAVE_CURSES_H"
AC_DEFINE(HAVE_CURSES_H)
else
AC_MSG_ERROR("curses not found")
@@ -228,7 +340,7 @@ fi
# Look for utempter.
AC_ARG_ENABLE(
utempter,
AC_HELP_STRING(--enable-utempter, use utempter if it is installed)
AS_HELP_STRING(--enable-utempter, use utempter if it is installed)
)
if test "x$enable_utempter" = xyes; then
AC_CHECK_HEADER(utempter.h, enable_utempter=yes, enable_utempter=no)
@@ -250,7 +362,7 @@ fi
# Look for utf8proc.
AC_ARG_ENABLE(
utf8proc,
AC_HELP_STRING(--enable-utf8proc, use utf8proc if it is installed)
AS_HELP_STRING(--enable-utf8proc, use utf8proc if it is installed)
)
if test "x$enable_utf8proc" = xyes; then
AC_CHECK_HEADER(utf8proc.h, enable_utf8proc=yes, enable_utf8proc=no)
@@ -272,41 +384,58 @@ AM_CONDITIONAL(HAVE_UTF8PROC, [test "x$enable_utf8proc" = xyes])
# Check for b64_ntop. If we have b64_ntop, we assume b64_pton as well.
AC_MSG_CHECKING(for b64_ntop)
AC_TRY_LINK(
AC_LINK_IFELSE([AC_LANG_PROGRAM(
[
#include <sys/types.h>
#include <netinet/in.h>
#include <resolv.h>
],
[b64_ntop(NULL, 0, NULL, 0);],
[
b64_ntop(NULL, 0, NULL, 0);
])],
found_b64_ntop=yes,
found_b64_ntop=no
)
AC_MSG_RESULT($found_b64_ntop)
OLD_LIBS="$LIBS"
if test "x$found_b64_ntop" = xno; then
AC_MSG_RESULT(no)
AC_MSG_CHECKING(for b64_ntop with -lresolv)
OLD_LIBS="$LIBS"
LIBS="$LIBS -lresolv"
AC_TRY_LINK(
LIBS="$OLD_LIBS -lresolv"
AC_LINK_IFELSE([AC_LANG_PROGRAM(
[
#include <sys/types.h>
#include <netinet/in.h>
#include <resolv.h>
],
[b64_ntop(NULL, 0, NULL, 0);],
[
b64_ntop(NULL, 0, NULL, 0);
])],
found_b64_ntop=yes,
found_b64_ntop=no
)
if test "x$found_b64_ntop" = xno; then
LIBS="$OLD_LIBS"
AC_MSG_RESULT(no)
fi
AC_MSG_RESULT($found_b64_ntop)
fi
if test "x$found_b64_ntop" = xno; then
AC_MSG_CHECKING(for b64_ntop with -lnetwork)
LIBS="$OLD_LIBS -lnetwork"
AC_LINK_IFELSE([AC_LANG_PROGRAM(
[
#include <sys/types.h>
#include <netinet/in.h>
#include <resolv.h>
],
[
b64_ntop(NULL, 0, NULL, 0);
])],
found_b64_ntop=yes,
found_b64_ntop=no
)
AC_MSG_RESULT($found_b64_ntop)
fi
if test "x$found_b64_ntop" = xyes; then
AC_DEFINE(HAVE_B64_NTOP)
AC_MSG_RESULT(yes)
else
LIBS="$OLD_LIBS"
AC_LIBOBJ(base64)
fi
@@ -315,8 +444,34 @@ 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).
# Check if using glibc and have malloc_trim(3). The glibc free(3) is pretty bad
# about returning memory to the kernel unless the application tells it when to
# with malloc_trim(3).
AC_MSG_CHECKING(if free doesn't work very well)
AC_LINK_IFELSE([AC_LANG_SOURCE(
[
#include <stdlib.h>
#ifdef __GLIBC__
#include <malloc.h>
int main(void) {
malloc_trim (0);
exit(0);
}
#else
no
#endif
])],
found_malloc_trim=yes,
found_malloc_trim=no
)
AC_MSG_RESULT($found_malloc_trim)
if test "x$found_malloc_trim" = xyes; then
AC_DEFINE(HAVE_MALLOC_TRIM)
fi
# Check for CMSG_DATA. On some platforms like HP-UX this requires UNIX 95
# (_XOPEN_SOURCE and _XOPEN_SOURCE_EXTENDED) (see xopen_networking(7)). On
# others, UNIX 03 (_XOPEN_SOURCE 600, see standards(7) on Solaris).
XOPEN_DEFINES=
AC_MSG_CHECKING(for CMSG_DATA)
AC_EGREP_CPP(
@@ -349,6 +504,25 @@ if test "x$found_cmsg_data" = xno; then
AC_MSG_RESULT($found_cmsg_data)
if test "x$found_cmsg_data" = xyes; then
XOPEN_DEFINES="-D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED"
fi
fi
if test "x$found_cmsg_data" = xno; then
AC_MSG_CHECKING(if CMSG_DATA needs _XOPEN_SOURCE 600)
AC_EGREP_CPP(
yes,
[
#define _XOPEN_SOURCE 600
#include <sys/socket.h>
#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=600"
else
AC_MSG_ERROR("CMSG_DATA not found")
fi
@@ -424,40 +598,6 @@ else
AC_LIBOBJ(unvis)
fi
# Look for getopt. glibc's getopt does not enforce argument order and the ways
# of making it do so are stupid, so just use our own instead.
AC_CHECK_FUNC(getopt, found_getopt=yes, found_getopt=no)
if test "x$found_getopt" != xno; then
AC_MSG_CHECKING(if getopt is suitable)
AC_EGREP_CPP(
yes,
[
#include <features.h>
#ifdef __GLIBC__
yes
#endif
],
[
found_getopt=no
AC_MSG_RESULT(no)
],
AC_MSG_RESULT(yes))
fi
if test "x$found_getopt" != xno; then
AC_CHECK_DECLS(
[optarg, optind, optreset],
,
found_getopt=no,
[
#include <unistd.h>
])
fi
if test "x$found_getopt" != xno; then
AC_DEFINE(HAVE_GETOPT)
else
AC_LIBOBJ(getopt)
fi
# Look for fdforkpty and forkpty in libutil.
AC_SEARCH_LIBS(fdforkpty, util, found_fdforkpty=yes, found_fdforkpty=no)
if test "x$found_fdforkpty" = xyes; then
@@ -483,7 +623,7 @@ AC_CHECK_DECL(
)
AC_CHECK_DECL(
TAILQ_PREV,
found_queue_h=yes,
,
found_queue_h=no,
[#include <sys/queue.h>]
)
@@ -556,6 +696,74 @@ else
AC_MSG_RESULT(no)
fi
# Try to figure out what the best value for TERM might be.
if test "x$DEFAULT_TERM" = x; then
DEFAULT_TERM=screen
AC_MSG_CHECKING(TERM)
AC_RUN_IFELSE([AC_LANG_SOURCE(
[
#include <stdio.h>
#include <stdlib.h>
#if defined(HAVE_CURSES_H)
#include <curses.h>
#elif defined(HAVE_NCURSES_H)
#include <ncurses.h>
#endif
#include <term.h>
int main(void) {
if (setupterm("screen-256color", -1, NULL) != OK)
exit(1);
exit(0);
}
])],
[DEFAULT_TERM=screen-256color],
,
[DEFAULT_TERM=screen]
)
AC_RUN_IFELSE([AC_LANG_SOURCE(
[
#include <stdio.h>
#include <stdlib.h>
#if defined(HAVE_CURSES_H)
#include <curses.h>
#elif defined(HAVE_NCURSES_H)
#include <ncurses.h>
#endif
#include <term.h>
int main(void) {
if (setupterm("tmux", -1, NULL) != OK)
exit(1);
exit(0);
}
])],
[DEFAULT_TERM=tmux],
,
[DEFAULT_TERM=screen]
)
AC_RUN_IFELSE([AC_LANG_SOURCE(
[
#include <stdio.h>
#include <stdlib.h>
#if defined(HAVE_CURSES_H)
#include <curses.h>
#elif defined(HAVE_NCURSES_H)
#include <ncurses.h>
#endif
#include <term.h>
int main(void) {
if (setupterm("tmux-256color", -1, NULL) != OK)
exit(1);
exit(0);
}
])],
[DEFAULT_TERM=tmux-256color],
,
[DEFAULT_TERM=screen]
)
AC_MSG_RESULT($DEFAULT_TERM)
fi
AC_SUBST(DEFAULT_TERM)
# Man page defaults to mdoc.
MANFORMAT=mdoc
AC_SUBST(MANFORMAT)
@@ -571,13 +779,32 @@ case "$host_os" in
AC_MSG_RESULT(darwin)
PLATFORM=darwin
#
# OS X CMSG_FIRSTHDR is broken, so redefine it with a working
# one. daemon works but has some stupid side effects, so use
# our internal version which has a workaround.
# macOS uses __dead2 instead of __dead, like FreeBSD. But it defines
# __dead away so it needs to be removed before we can replace it.
#
AC_DEFINE(BROKEN___DEAD)
#
# macOS CMSG_FIRSTHDR is broken, so redefine it with a working one.
# daemon works but has some stupid side effects, so use our internal
# version which has a workaround.
#
AC_DEFINE(BROKEN_CMSG_FIRSTHDR)
AC_LIBOBJ(daemon)
AC_LIBOBJ(daemon-darwin)
#
# macOS wcwidth(3) is bad, so complain and suggest using utf8proc
# instead.
#
if test "x$enable_utf8proc" = x; then
AC_MSG_NOTICE([])
AC_MSG_NOTICE([ macOS library support for Unicode is very poor,])
AC_MSG_NOTICE([ particularly for complex codepoints like emojis;])
AC_MSG_NOTICE([ to use these correctly, configuring with])
AC_MSG_NOTICE([ --enable-utf8proc is recommended. To build])
AC_MSG_NOTICE([ without anyway, use --disable-utf8proc])
AC_MSG_NOTICE([])
AC_MSG_ERROR([must give --enable-utf8proc or --disable-utf8proc])
fi
;;
*dragonfly*)
AC_MSG_RESULT(dragonfly)
@@ -625,6 +852,10 @@ case "$host_os" in
AC_MSG_RESULT(cygwin)
PLATFORM=cygwin
;;
*haiku*)
AC_MSG_RESULT(haiku)
PLATFORM=haiku
;;
*)
AC_MSG_RESULT(unknown)
PLATFORM=unknown
@@ -640,6 +871,7 @@ 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_HAIKU, test "x$PLATFORM" = xhaiku)
AM_CONDITIONAL(IS_UNKNOWN, test "x$PLATFORM" = xunknown)
# Save our CFLAGS/CPPFLAGS/LDFLAGS for the Makefile and restore the old user
@@ -652,4 +884,5 @@ AC_SUBST(AM_LDFLAGS)
LDFLAGS="$SAVED_LDFLAGS"
# autoconf should create a Makefile.
AC_OUTPUT(Makefile)
AC_CONFIG_FILES(Makefile)
AC_OUTPUT

View File

@@ -26,39 +26,6 @@
#define CONTROL_SHOULD_NOTIFY_CLIENT(c) \
((c) != NULL && ((c)->flags & CLIENT_CONTROL))
void
control_notify_input(struct client *c, struct window_pane *wp,
const u_char *buf, size_t len)
{
struct evbuffer *message;
u_int i;
if (c->session == NULL)
return;
if (c->flags & CLIENT_CONTROL_NOOUTPUT)
return;
/*
* Only write input if the window pane is linked to a window belonging
* to the client's session.
*/
if (winlink_find_by_window(&c->session->windows, wp->window) != NULL) {
message = evbuffer_new();
if (message == NULL)
fatalx("out of memory");
evbuffer_add_printf(message, "%%output %%%u ", wp->id);
for (i = 0; i < len; i++) {
if (buf[i] < ' ' || buf[i] == '\\')
evbuffer_add_printf(message, "\\%03o", buf[i]);
else
evbuffer_add_printf(message, "%c", buf[i]);
}
control_write(c, "%s", EVBUFFER_DATA(message));
evbuffer_free(message);
}
}
void
control_notify_pane_mode_changed(int pane)
{
@@ -82,7 +49,7 @@ control_notify_window_layout_changed(struct window *w)
char *cp;
template = "%layout-change #{window_id} #{window_layout} "
"#{window_visible_layout} #{window_flags}";
"#{window_visible_layout} #{window_raw_flags}";
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
@@ -204,6 +171,17 @@ control_notify_client_session_changed(struct client *cc)
}
}
void
control_notify_client_detached(struct client *cc)
{
struct client *c;
TAILQ_FOREACH(c, &clients, entry) {
if (CONTROL_SHOULD_NOTIFY_CLIENT(c))
control_write(c, "%%client-detached %s", cc->name);
}
}
void
control_notify_session_renamed(struct session *s)
{

1067
control.c

File diff suppressed because it is too large Load Diff

View File

@@ -18,6 +18,7 @@
#include <sys/types.h>
#include <fnmatch.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@@ -86,8 +87,10 @@ environ_copy(struct environ *srcenv, struct environ *dstenv)
RB_FOREACH(envent, environ, srcenv) {
if (envent->value == NULL)
environ_clear(dstenv, envent->name);
else
environ_set(dstenv, envent->name, "%s", envent->value);
else {
environ_set(dstenv, envent->name, envent->flags,
"%s", envent->value);
}
}
}
@@ -103,18 +106,21 @@ environ_find(struct environ *env, const char *name)
/* Set an environment variable. */
void
environ_set(struct environ *env, const char *name, const char *fmt, ...)
environ_set(struct environ *env, const char *name, int flags, const char *fmt,
...)
{
struct environ_entry *envent;
va_list ap;
va_start(ap, fmt);
if ((envent = environ_find(env, name)) != NULL) {
envent->flags = flags;
free(envent->value);
xvasprintf(&envent->value, fmt, ap);
} else {
envent = xmalloc(sizeof *envent);
envent->name = xstrdup(name);
envent->flags = flags;
xvasprintf(&envent->value, fmt, ap);
RB_INSERT(environ, env, envent);
}
@@ -133,6 +139,7 @@ environ_clear(struct environ *env, const char *name)
} else {
envent = xmalloc(sizeof *envent);
envent->name = xstrdup(name);
envent->flags = 0;
envent->value = NULL;
RB_INSERT(environ, env, envent);
}
@@ -140,7 +147,7 @@ environ_clear(struct environ *env, const char *name)
/* Set an environment variable from a NAME=VALUE string. */
void
environ_put(struct environ *env, const char *var)
environ_put(struct environ *env, const char *var, int flags)
{
char *name, *value;
@@ -152,7 +159,7 @@ environ_put(struct environ *env, const char *var)
name = xstrdup(var);
name[strcspn(name, "=")] = '\0';
environ_set(env, name, "%s", value);
environ_set(env, name, flags, "%s", value);
free(name);
}
@@ -170,7 +177,7 @@ environ_unset(struct environ *env, const char *name)
free(envent);
}
/* Copy variables from a destination into a source * environment. */
/* Copy variables from a destination into a source environment. */
void
environ_update(struct options *oo, struct environ *src, struct environ *dst)
{
@@ -185,10 +192,14 @@ environ_update(struct options *oo, struct environ *src, struct environ *dst)
a = options_array_first(o);
while (a != NULL) {
ov = options_array_item_value(a);
if ((envent = environ_find(src, ov->string)) == NULL)
RB_FOREACH(envent, environ, src) {
if (fnmatch(ov->string, envent->name, 0) == 0)
break;
}
if (envent == NULL)
environ_clear(dst, ov->string);
else
environ_set(dst, envent->name, "%s", envent->value);
environ_set(dst, envent->name, 0, "%s", envent->value);
a = options_array_next(a);
}
}
@@ -201,7 +212,9 @@ environ_push(struct environ *env)
environ = xcalloc(1, sizeof *environ);
RB_FOREACH(envent, environ, env) {
if (envent->value != NULL && *envent->name != '\0')
if (envent->value != NULL &&
*envent->name != '\0' &&
(~envent->flags & ENVIRON_HIDDEN))
setenv(envent->name, envent->value, 1);
}
}
@@ -243,14 +256,17 @@ environ_for_session(struct session *s, int no_TERM)
if (!no_TERM) {
value = options_get_string(global_options, "default-terminal");
environ_set(env, "TERM", "%s", value);
environ_set(env, "TERM", 0, "%s", value);
environ_set(env, "TERM_PROGRAM", 0, "%s", "tmux");
environ_set(env, "TERM_PROGRAM_VERSION", 0, "%s", getversion());
}
if (s != NULL)
idx = s->id;
else
idx = -1;
environ_set(env, "TMUX", "%s,%ld,%d", socket_path, (long)getpid(), idx);
environ_set(env, "TMUX", 0, "%s,%ld,%d", socket_path, (long)getpid(),
idx);
return (env);
}

View File

@@ -61,7 +61,7 @@ bind y set synchronize-panes\; display 'synchronize-panes #{?synchronize-panes,o
# should be started with "tmux attach" rather than "tmux new"
new -d -s0 -nirssi 'exec irssi'
set -t0:0 monitor-activity on
set -t0:0 aggressive-resize on
set -t0:0 aggressive-resize on
neww -d -ntodo 'exec emacs ~/TODO'
setw -t0:1 aggressive-resize on
neww -d -nmutt 'exec mutt'

463
file.c
View File

@@ -17,7 +17,6 @@
*/
#include <sys/types.h>
#include <sys/queue.h>
#include <errno.h>
#include <fcntl.h>
@@ -28,10 +27,17 @@
#include "tmux.h"
/*
* IPC file handling. Both client and server use the same data structures
* (client_file and client_files) to store list of active files. Most functions
* are for use either in client or server but not both.
*/
static int file_next_stream = 3;
RB_GENERATE(client_files, client_file, entry, file_cmp);
/* Get path for file, either as given or from working directory. */
static char *
file_get_path(struct client *c, const char *file)
{
@@ -44,6 +50,7 @@ file_get_path(struct client *c, const char *file)
return (path);
}
/* Tree comparison function. */
int
file_cmp(struct client_file *cf1, struct client_file *cf2)
{
@@ -54,11 +61,47 @@ file_cmp(struct client_file *cf1, struct client_file *cf2)
return (0);
}
/*
* Create a file object in the client process - the peer is the server to send
* messages to. Check callback is fired when the file is finished with so the
* process can decide if it needs to exit (if it is waiting for files to
* flush).
*/
struct client_file *
file_create(struct client *c, int stream, client_file_cb cb, void *cbdata)
file_create_with_peer(struct tmuxpeer *peer, struct client_files *files,
int stream, client_file_cb cb, void *cbdata)
{
struct client_file *cf;
cf = xcalloc(1, sizeof *cf);
cf->c = NULL;
cf->references = 1;
cf->stream = stream;
cf->buffer = evbuffer_new();
if (cf->buffer == NULL)
fatalx("out of memory");
cf->cb = cb;
cf->data = cbdata;
cf->peer = peer;
cf->tree = files;
RB_INSERT(client_files, files, cf);
return (cf);
}
/* Create a file object in the server, communicating with the given client. */
struct client_file *
file_create_with_client(struct client *c, int stream, client_file_cb cb,
void *cbdata)
{
struct client_file *cf;
if (c != NULL && (c->flags & CLIENT_ATTACHED))
c = NULL;
cf = xcalloc(1, sizeof *cf);
cf->c = c;
cf->references = 1;
@@ -72,6 +115,8 @@ file_create(struct client *c, int stream, client_file_cb cb, void *cbdata)
cf->data = cbdata;
if (cf->c != NULL) {
cf->peer = cf->c->peer;
cf->tree = &cf->c->files;
RB_INSERT(client_files, &cf->c->files, cf);
cf->c->references++;
}
@@ -79,6 +124,7 @@ file_create(struct client *c, int stream, client_file_cb cb, void *cbdata)
return (cf);
}
/* Free a file. */
void
file_free(struct client_file *cf)
{
@@ -88,13 +134,15 @@ file_free(struct client_file *cf)
evbuffer_free(cf->buffer);
free(cf->path);
if (cf->c != NULL) {
RB_REMOVE(client_files, &cf->c->files, cf);
if (cf->tree != NULL)
RB_REMOVE(client_files, cf->tree, cf);
if (cf->c != NULL)
server_client_unref(cf->c);
}
free(cf);
}
/* Event to fire the done callback. */
static void
file_fire_done_cb(__unused int fd, __unused short events, void *arg)
{
@@ -106,21 +154,22 @@ file_fire_done_cb(__unused int fd, __unused short events, void *arg)
file_free(cf);
}
/* Add an event to fire the done callback (used by the server). */
void
file_fire_done(struct client_file *cf)
{
event_once(-1, EV_TIMEOUT, file_fire_done_cb, cf, NULL);
}
/* Fire the read callback. */
void
file_fire_read(struct client_file *cf)
{
struct client *c = cf->c;
if (cf->cb != NULL)
cf->cb(c, cf->path, cf->error, 0, cf->buffer, cf->data);
cf->cb(cf->c, cf->path, cf->error, 0, cf->buffer, cf->data);
}
/* Can this file be printed to? */
int
file_can_print(struct client *c)
{
@@ -131,6 +180,7 @@ file_can_print(struct client *c)
return (1);
}
/* Print a message to a file. */
void
file_print(struct client *c, const char *fmt, ...)
{
@@ -141,6 +191,7 @@ file_print(struct client *c, const char *fmt, ...)
va_end(ap);
}
/* Print a message to a file. */
void
file_vprint(struct client *c, const char *fmt, va_list ap)
{
@@ -152,7 +203,7 @@ file_vprint(struct client *c, const char *fmt, va_list ap)
find.stream = 1;
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
cf = file_create(c, 1, NULL, NULL);
cf = file_create_with_client(c, 1, NULL, NULL);
cf->path = xstrdup("-");
evbuffer_add_vprintf(cf->buffer, fmt, ap);
@@ -167,6 +218,7 @@ file_vprint(struct client *c, const char *fmt, va_list ap)
}
}
/* Print a buffer to a file. */
void
file_print_buffer(struct client *c, void *data, size_t size)
{
@@ -178,7 +230,7 @@ file_print_buffer(struct client *c, void *data, size_t size)
find.stream = 1;
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
cf = file_create(c, 1, NULL, NULL);
cf = file_create_with_client(c, 1, NULL, NULL);
cf->path = xstrdup("-");
evbuffer_add(cf->buffer, data, size);
@@ -193,6 +245,7 @@ file_print_buffer(struct client *c, void *data, size_t size)
}
}
/* Report an error to a file. */
void
file_error(struct client *c, const char *fmt, ...)
{
@@ -207,7 +260,7 @@ file_error(struct client *c, const char *fmt, ...)
find.stream = 2;
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
cf = file_create(c, 2, NULL, NULL);
cf = file_create_with_client(c, 2, NULL, NULL);
cf->path = xstrdup("-");
evbuffer_add_vprintf(cf->buffer, fmt, ap);
@@ -224,30 +277,34 @@ file_error(struct client *c, const char *fmt, ...)
va_end(ap);
}
/* Write data to a file. */
void
file_write(struct client *c, const char *path, int flags, const void *bdata,
size_t bsize, client_file_cb cb, void *cbdata)
{
struct client_file *cf;
FILE *f;
struct msg_write_open *msg;
size_t msglen;
int fd = -1;
u_int stream = file_next_stream++;
FILE *f;
const char *mode;
if (strcmp(path, "-") == 0) {
cf = file_create(c, file_next_stream++, cb, cbdata);
cf = file_create_with_client(c, stream, cb, cbdata);
cf->path = xstrdup("-");
fd = STDOUT_FILENO;
if (c == NULL || c->flags & CLIENT_ATTACHED) {
if (c == NULL ||
(c->flags & CLIENT_ATTACHED) ||
(c->flags & CLIENT_CONTROL)) {
cf->error = EBADF;
goto done;
}
goto skip;
}
cf = file_create(c, file_next_stream++, cb, cbdata);
cf = file_create_with_client(c, stream, cb, cbdata);
cf->path = file_get_path(c, path);
if (c == NULL || c->flags & CLIENT_ATTACHED) {
@@ -282,7 +339,7 @@ skip:
msg->fd = fd;
msg->flags = flags;
memcpy(msg + 1, cf->path, msglen - sizeof *msg);
if (proc_send(c->peer, MSG_WRITE_OPEN, -1, msg, msglen) != 0) {
if (proc_send(cf->peer, MSG_WRITE_OPEN, -1, msg, msglen) != 0) {
free(msg);
cf->error = EINVAL;
goto done;
@@ -294,29 +351,34 @@ done:
file_fire_done(cf);
}
/* Read a file. */
void
file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
{
struct client_file *cf;
FILE *f;
struct msg_read_open *msg;
size_t msglen, size;
size_t msglen;
int fd = -1;
u_int stream = file_next_stream++;
FILE *f;
size_t size;
char buffer[BUFSIZ];
if (strcmp(path, "-") == 0) {
cf = file_create(c, file_next_stream++, cb, cbdata);
cf = file_create_with_client(c, stream, cb, cbdata);
cf->path = xstrdup("-");
fd = STDIN_FILENO;
if (c == NULL || c->flags & CLIENT_ATTACHED) {
if (c == NULL ||
(c->flags & CLIENT_ATTACHED) ||
(c->flags & CLIENT_CONTROL)) {
cf->error = EBADF;
goto done;
}
goto skip;
}
cf = file_create(c, file_next_stream++, cb, cbdata);
cf = file_create_with_client(c, stream, cb, cbdata);
cf->path = file_get_path(c, path);
if (c == NULL || c->flags & CLIENT_ATTACHED) {
@@ -352,7 +414,7 @@ skip:
msg->stream = cf->stream;
msg->fd = fd;
memcpy(msg + 1, cf->path, msglen - sizeof *msg);
if (proc_send(c->peer, MSG_READ_OPEN, -1, msg, msglen) != 0) {
if (proc_send(cf->peer, MSG_READ_OPEN, -1, msg, msglen) != 0) {
free(msg);
cf->error = EINVAL;
goto done;
@@ -364,21 +426,21 @@ done:
file_fire_done(cf);
}
/* Push event, fired if there is more writing to be done. */
static void
file_push_cb(__unused int fd, __unused short events, void *arg)
{
struct client_file *cf = arg;
struct client *c = cf->c;
if (~c->flags & CLIENT_DEAD)
if (cf->c == NULL || ~cf->c->flags & CLIENT_DEAD)
file_push(cf);
file_free(cf);
}
/* Push uwritten data to the client for a file, if it will accept it. */
void
file_push(struct client_file *cf)
{
struct client *c = cf->c;
struct msg_write_data *msg;
size_t msglen, sent, left;
struct msg_write_close close;
@@ -394,21 +456,364 @@ file_push(struct client_file *cf)
msg = xrealloc(msg, msglen);
msg->stream = cf->stream;
memcpy(msg + 1, EVBUFFER_DATA(cf->buffer), sent);
if (proc_send(c->peer, MSG_WRITE, -1, msg, msglen) != 0)
if (proc_send(cf->peer, MSG_WRITE, -1, msg, msglen) != 0)
break;
evbuffer_drain(cf->buffer, sent);
left = EVBUFFER_LENGTH(cf->buffer);
log_debug("%s: file %d sent %zu, left %zu", c->name, cf->stream,
sent, left);
log_debug("file %d sent %zu, left %zu", cf->stream, sent, left);
}
if (left != 0) {
cf->references++;
event_once(-1, EV_TIMEOUT, file_push_cb, cf, NULL);
} else if (cf->stream > 2) {
close.stream = cf->stream;
proc_send(c->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close);
proc_send(cf->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close);
file_fire_done(cf);
}
free(msg);
}
/* Check if any files have data left to write. */
int
file_write_left(struct client_files *files)
{
struct client_file *cf;
size_t left;
int waiting = 0;
RB_FOREACH(cf, client_files, files) {
if (cf->event == NULL)
continue;
left = EVBUFFER_LENGTH(cf->event->output);
if (left != 0) {
waiting++;
log_debug("file %u %zu bytes left", cf->stream, left);
}
}
return (waiting != 0);
}
/* Client file write error callback. */
static void
file_write_error_callback(__unused struct bufferevent *bev, __unused short what,
void *arg)
{
struct client_file *cf = arg;
log_debug("write error file %d", cf->stream);
bufferevent_free(cf->event);
cf->event = NULL;
close(cf->fd);
cf->fd = -1;
if (cf->cb != NULL)
cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
}
/* Client file write callback. */
static void
file_write_callback(__unused struct bufferevent *bev, void *arg)
{
struct client_file *cf = arg;
log_debug("write check file %d", cf->stream);
if (cf->cb != NULL)
cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) {
bufferevent_free(cf->event);
close(cf->fd);
RB_REMOVE(client_files, cf->tree, cf);
file_free(cf);
}
}
/* Handle a file write open message (client). */
void
file_write_open(struct client_files *files, struct tmuxpeer *peer,
struct imsg *imsg, int allow_streams, int close_received,
client_file_cb cb, void *cbdata)
{
struct msg_write_open *msg = imsg->data;
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
const char *path;
struct msg_write_ready reply;
struct client_file find, *cf;
const int flags = O_NONBLOCK|O_WRONLY|O_CREAT;
int error = 0;
if (msglen < sizeof *msg)
fatalx("bad MSG_WRITE_OPEN size");
if (msglen == sizeof *msg)
path = "-";
else
path = (const char *)(msg + 1);
log_debug("open write file %d %s", msg->stream, path);
find.stream = msg->stream;
if (RB_FIND(client_files, files, &find) != NULL) {
error = EBADF;
goto reply;
}
cf = file_create_with_peer(peer, files, msg->stream, cb, cbdata);
if (cf->closed) {
error = EBADF;
goto reply;
}
cf->fd = -1;
if (msg->fd == -1)
cf->fd = open(path, msg->flags|flags, 0644);
else if (allow_streams) {
if (msg->fd != STDOUT_FILENO && msg->fd != STDERR_FILENO)
errno = EBADF;
else {
cf->fd = dup(msg->fd);
if (close_received)
close(msg->fd); /* can only be used once */
}
} else
errno = EBADF;
if (cf->fd == -1) {
error = errno;
goto reply;
}
cf->event = bufferevent_new(cf->fd, NULL, file_write_callback,
file_write_error_callback, cf);
bufferevent_enable(cf->event, EV_WRITE);
goto reply;
reply:
reply.stream = msg->stream;
reply.error = error;
proc_send(peer, MSG_WRITE_READY, -1, &reply, sizeof reply);
}
/* Handle a file write data message (client). */
void
file_write_data(struct client_files *files, struct imsg *imsg)
{
struct msg_write_data *msg = imsg->data;
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
struct client_file find, *cf;
size_t size = msglen - sizeof *msg;
if (msglen < sizeof *msg)
fatalx("bad MSG_WRITE size");
find.stream = msg->stream;
if ((cf = RB_FIND(client_files, files, &find)) == NULL)
fatalx("unknown stream number");
log_debug("write %zu to file %d", size, cf->stream);
if (cf->event != NULL)
bufferevent_write(cf->event, msg + 1, size);
}
/* Handle a file write close message (client). */
void
file_write_close(struct client_files *files, struct imsg *imsg)
{
struct msg_write_close *msg = imsg->data;
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
struct client_file find, *cf;
if (msglen != sizeof *msg)
fatalx("bad MSG_WRITE_CLOSE size");
find.stream = msg->stream;
if ((cf = RB_FIND(client_files, files, &find)) == NULL)
fatalx("unknown stream number");
log_debug("close file %d", cf->stream);
if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) {
if (cf->event != NULL)
bufferevent_free(cf->event);
if (cf->fd != -1)
close(cf->fd);
RB_REMOVE(client_files, files, cf);
file_free(cf);
}
}
/* Client file read error callback. */
static void
file_read_error_callback(__unused struct bufferevent *bev, __unused short what,
void *arg)
{
struct client_file *cf = arg;
struct msg_read_done msg;
log_debug("read error file %d", cf->stream);
msg.stream = cf->stream;
msg.error = 0;
proc_send(cf->peer, MSG_READ_DONE, -1, &msg, sizeof msg);
bufferevent_free(cf->event);
close(cf->fd);
RB_REMOVE(client_files, cf->tree, cf);
file_free(cf);
}
/* Client file read callback. */
static void
file_read_callback(__unused struct bufferevent *bev, void *arg)
{
struct client_file *cf = arg;
void *bdata;
size_t bsize;
struct msg_read_data *msg;
size_t msglen;
msg = xmalloc(sizeof *msg);
for (;;) {
bdata = EVBUFFER_DATA(cf->event->input);
bsize = EVBUFFER_LENGTH(cf->event->input);
if (bsize == 0)
break;
if (bsize > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg)
bsize = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg;
log_debug("read %zu from file %d", bsize, cf->stream);
msglen = (sizeof *msg) + bsize;
msg = xrealloc(msg, msglen);
msg->stream = cf->stream;
memcpy(msg + 1, bdata, bsize);
proc_send(cf->peer, MSG_READ, -1, msg, msglen);
evbuffer_drain(cf->event->input, bsize);
}
free(msg);
}
/* Handle a file read open message (client). */
void
file_read_open(struct client_files *files, struct tmuxpeer *peer,
struct imsg *imsg, int allow_streams, int close_received, client_file_cb cb,
void *cbdata)
{
struct msg_read_open *msg = imsg->data;
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
const char *path;
struct msg_read_done reply;
struct client_file find, *cf;
const int flags = O_NONBLOCK|O_RDONLY;
int error;
if (msglen < sizeof *msg)
fatalx("bad MSG_READ_OPEN size");
if (msglen == sizeof *msg)
path = "-";
else
path = (const char *)(msg + 1);
log_debug("open read file %d %s", msg->stream, path);
find.stream = msg->stream;
if (RB_FIND(client_files, files, &find) != NULL) {
error = EBADF;
goto reply;
}
cf = file_create_with_peer(peer, files, msg->stream, cb, cbdata);
if (cf->closed) {
error = EBADF;
goto reply;
}
cf->fd = -1;
if (msg->fd == -1)
cf->fd = open(path, flags);
else if (allow_streams) {
if (msg->fd != STDIN_FILENO)
errno = EBADF;
else {
cf->fd = dup(msg->fd);
if (close_received)
close(msg->fd); /* can only be used once */
}
} else
errno = EBADF;
if (cf->fd == -1) {
error = errno;
goto reply;
}
cf->event = bufferevent_new(cf->fd, file_read_callback, NULL,
file_read_error_callback, cf);
bufferevent_enable(cf->event, EV_READ);
return;
reply:
reply.stream = msg->stream;
reply.error = error;
proc_send(peer, MSG_READ_DONE, -1, &reply, sizeof reply);
}
/* Handle a write ready message (server). */
void
file_write_ready(struct client_files *files, struct imsg *imsg)
{
struct msg_write_ready *msg = imsg->data;
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
struct client_file find, *cf;
if (msglen != sizeof *msg)
fatalx("bad MSG_WRITE_READY size");
find.stream = msg->stream;
if ((cf = RB_FIND(client_files, files, &find)) == NULL)
return;
if (msg->error != 0) {
cf->error = msg->error;
file_fire_done(cf);
} else
file_push(cf);
}
/* Handle read data message (server). */
void
file_read_data(struct client_files *files, struct imsg *imsg)
{
struct msg_read_data *msg = imsg->data;
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
struct client_file find, *cf;
void *bdata = msg + 1;
size_t bsize = msglen - sizeof *msg;
if (msglen < sizeof *msg)
fatalx("bad MSG_READ_DATA size");
find.stream = msg->stream;
if ((cf = RB_FIND(client_files, files, &find)) == NULL)
return;
log_debug("file %d read %zu bytes", cf->stream, bsize);
if (cf->error == 0) {
if (evbuffer_add(cf->buffer, bdata, bsize) != 0) {
cf->error = ENOMEM;
file_fire_done(cf);
} else
file_fire_read(cf);
}
}
/* Handle a read done message (server). */
void
file_read_done(struct client_files *files, struct imsg *imsg)
{
struct msg_read_done *msg = imsg->data;
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
struct client_file find, *cf;
if (msglen != sizeof *msg)
fatalx("bad MSG_READ_DONE size");
find.stream = msg->stream;
if ((cf = RB_FIND(client_files, files, &find)) == NULL)
return;
log_debug("file %d read done", cf->stream);
cf->error = msg->error;
file_fire_done(cf);
}

View File

@@ -142,7 +142,8 @@ format_draw_put_list(struct screen_write_ctx *octx,
width -= list_left->cx;
}
if (start + width < list->cx && width > list_right->cx) {
screen_write_cursormove(octx, ocx + offset + width - 1, ocy, 0);
screen_write_cursormove(octx, ocx + offset + width -
list_right->cx, ocy, 0);
screen_write_fast_copy(octx, list_right, 0, 0, list_right->cx,
1);
width -= list_right->cx;
@@ -156,13 +157,14 @@ format_draw_put_list(struct screen_write_ctx *octx,
static void
format_draw_none(struct screen_write_ctx *octx, u_int available, u_int ocx,
u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
struct format_ranges *frs)
struct screen *abs_centre, struct format_ranges *frs)
{
u_int width_left, width_centre, width_right;
u_int width_left, width_centre, width_right, width_abs_centre;
width_left = left->cx;
width_centre = centre->cx;
width_right = right->cx;
width_abs_centre = abs_centre->cx;
/*
* Try to keep as much of the left and right as possible at the expense
@@ -198,23 +200,34 @@ format_draw_none(struct screen_write_ctx *octx, u_int available, u_int ocx,
- width_centre / 2,
centre->cx / 2 - width_centre / 2,
width_centre);
/*
* Write abs_centre in the perfect centre of all horizontal space.
*/
if (width_abs_centre > available)
width_abs_centre = available;
format_draw_put(octx, ocx, ocy, abs_centre, frs,
(available - width_abs_centre) / 2,
0,
width_abs_centre);
}
/* Draw format with list on the left. */
static void
format_draw_left(struct screen_write_ctx *octx, u_int available, u_int ocx,
u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
struct screen *list, struct screen *list_left, struct screen *list_right,
struct screen *after, int focus_start, int focus_end,
struct format_ranges *frs)
struct screen *abs_centre, struct screen *list, struct screen *list_left,
struct screen *list_right, struct screen *after, int focus_start,
int focus_end, struct format_ranges *frs)
{
u_int width_left, width_centre, width_right;
u_int width_list, width_after;
u_int width_list, width_after, width_abs_centre;
struct screen_write_ctx ctx;
width_left = left->cx;
width_centre = centre->cx;
width_right = right->cx;
width_abs_centre = abs_centre->cx;
width_list = list->cx;
width_after = after->cx;
@@ -241,12 +254,12 @@ format_draw_left(struct screen_write_ctx *octx, u_int available, u_int ocx,
/* If there is no list left, pass off to the no list function. */
if (width_list == 0) {
screen_write_start(&ctx, NULL, left);
screen_write_start(&ctx, left);
screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
screen_write_stop(&ctx);
format_draw_none(octx, available, ocx, ocy, left, centre,
right, frs);
right, abs_centre, frs);
return;
}
@@ -290,23 +303,34 @@ format_draw_left(struct screen_write_ctx *octx, u_int available, u_int ocx,
focus_start = focus_end = 0;
format_draw_put_list(octx, ocx, ocy, width_left, width_list, list,
list_left, list_right, focus_start, focus_end, frs);
/*
* Write abs_centre in the perfect centre of all horizontal space.
*/
if (width_abs_centre > available)
width_abs_centre = available;
format_draw_put(octx, ocx, ocy, abs_centre, frs,
(available - width_abs_centre) / 2,
0,
width_abs_centre);
}
/* Draw format with list in the centre. */
static void
format_draw_centre(struct screen_write_ctx *octx, u_int available, u_int ocx,
u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
struct screen *list, struct screen *list_left, struct screen *list_right,
struct screen *after, int focus_start, int focus_end,
struct format_ranges *frs)
struct screen *abs_centre, struct screen *list, struct screen *list_left,
struct screen *list_right, struct screen *after, int focus_start,
int focus_end, struct format_ranges *frs)
{
u_int width_left, width_centre, width_right;
u_int width_list, width_after, middle;
u_int width_left, width_centre, width_right, middle;
u_int width_list, width_after, width_abs_centre;
struct screen_write_ctx ctx;
width_left = left->cx;
width_centre = centre->cx;
width_right = right->cx;
width_abs_centre = abs_centre->cx;
width_list = list->cx;
width_after = after->cx;
@@ -333,12 +357,12 @@ format_draw_centre(struct screen_write_ctx *octx, u_int available, u_int ocx,
/* If there is no list left, pass off to the no list function. */
if (width_list == 0) {
screen_write_start(&ctx, NULL, centre);
screen_write_start(&ctx, centre);
screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
screen_write_stop(&ctx);
format_draw_none(octx, available, ocx, ocy, left, centre,
right, frs);
right, abs_centre, frs);
return;
}
@@ -387,23 +411,34 @@ format_draw_centre(struct screen_write_ctx *octx, u_int available, u_int ocx,
format_draw_put_list(octx, ocx, ocy, middle - width_list / 2,
width_list, list, list_left, list_right, focus_start, focus_end,
frs);
/*
* Write abs_centre in the perfect centre of all horizontal space.
*/
if (width_abs_centre > available)
width_abs_centre = available;
format_draw_put(octx, ocx, ocy, abs_centre, frs,
(available - width_abs_centre) / 2,
0,
width_abs_centre);
}
/* Draw format with list on the right. */
static void
format_draw_right(struct screen_write_ctx *octx, u_int available, u_int ocx,
u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
struct screen *list, struct screen *list_left, struct screen *list_right,
struct screen *after, int focus_start, int focus_end,
struct format_ranges *frs)
struct screen *abs_centre, struct screen *list,
struct screen *list_left, struct screen *list_right, struct screen *after,
int focus_start, int focus_end, struct format_ranges *frs)
{
u_int width_left, width_centre, width_right;
u_int width_list, width_after;
u_int width_list, width_after, width_abs_centre;
struct screen_write_ctx ctx;
width_left = left->cx;
width_centre = centre->cx;
width_right = right->cx;
width_abs_centre = abs_centre->cx;
width_list = list->cx;
width_after = after->cx;
@@ -430,12 +465,12 @@ format_draw_right(struct screen_write_ctx *octx, u_int available, u_int ocx,
/* If there is no list left, pass off to the no list function. */
if (width_list == 0) {
screen_write_start(&ctx, NULL, right);
screen_write_start(&ctx, right);
screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
screen_write_stop(&ctx);
format_draw_none(octx, available, ocx, ocy, left, centre,
right, frs);
right, abs_centre, frs);
return;
}
@@ -483,6 +518,160 @@ format_draw_right(struct screen_write_ctx *octx, u_int available, u_int ocx,
format_draw_put_list(octx, ocx, ocy, available - width_list -
width_after, width_list, list, list_left, list_right, focus_start,
focus_end, frs);
/*
* Write abs_centre in the perfect centre of all horizontal space.
*/
if (width_abs_centre > available)
width_abs_centre = available;
format_draw_put(octx, ocx, ocy, abs_centre, frs,
(available - width_abs_centre) / 2,
0,
width_abs_centre);
}
static void
format_draw_absolute_centre(struct screen_write_ctx *octx, u_int available,
u_int ocx, u_int ocy, struct screen *left, struct screen *centre,
struct screen *right, struct screen *abs_centre, struct screen *list,
struct screen *list_left, struct screen *list_right, struct screen *after,
int focus_start, int focus_end, struct format_ranges *frs)
{
u_int width_left, width_centre, width_right, width_abs_centre;
u_int width_list, width_after, middle, abs_centre_offset;
width_left = left->cx;
width_centre = centre->cx;
width_right = right->cx;
width_abs_centre = abs_centre->cx;
width_list = list->cx;
width_after = after->cx;
/*
* Trim first centre, then the right, then the left.
*/
while (width_left +
width_centre +
width_right > available) {
if (width_centre > 0)
width_centre--;
else if (width_right > 0)
width_right--;
else
width_left--;
}
/*
* We trim list after and abs_centre independently, as we are drawing
* them over the rest. Trim first the list, then after the list, then
* abs_centre.
*/
while (width_list + width_after + width_abs_centre > available) {
if (width_list > 0)
width_list--;
else if (width_after > 0)
width_after--;
else
width_abs_centre--;
}
/* Write left at 0. */
format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
/* Write right at available - width_right. */
format_draw_put(octx, ocx, ocy, right, frs,
available - width_right,
right->cx - width_right,
width_right);
/*
* Keep writing centre at the relative centre. Only the list is written
* in the absolute centre of the horizontal space.
*/
middle = (width_left + ((available - width_right) - width_left) / 2);
/*
* Write centre at
* middle - width_centre.
*/
format_draw_put(octx, ocx, ocy, centre, frs,
middle - width_centre,
0,
width_centre);
/*
* If there is no focus given, keep the centre in focus.
*/
if (focus_start == -1 || focus_end == -1)
focus_start = focus_end = list->cx / 2;
/*
* We centre abs_centre and the list together, so their shared centre is
* in the perfect centre of horizontal space.
*/
abs_centre_offset = (available - width_list - width_abs_centre) / 2;
/*
* Write abs_centre before the list.
*/
format_draw_put(octx, ocx, ocy, abs_centre, frs, abs_centre_offset,
0, width_abs_centre);
abs_centre_offset += width_abs_centre;
/*
* Draw the list in the absolute centre
*/
format_draw_put_list(octx, ocx, ocy, abs_centre_offset, width_list,
list, list_left, list_right, focus_start, focus_end, frs);
abs_centre_offset += width_list;
/*
* Write after at the end of the centre
*/
format_draw_put(octx, ocx, ocy, after, frs, abs_centre_offset, 0,
width_after);
}
/* Get width and count of any leading #s. */
static const char *
format_leading_hashes(const char *cp, u_int *n, u_int *width)
{
for (*n = 0; cp[*n] == '#'; (*n)++)
/* nothing */;
if (*n == 0) {
*width = 0;
return (cp);
}
if (cp[*n] != '[') {
if ((*n % 2) == 0)
*width = (*n / 2);
else
*width = (*n / 2) + 1;
return (cp + *n);
}
*width = (*n / 2);
if ((*n % 2) == 0) {
/*
* An even number of #s means that all #s are escaped, so not a
* style. The caller should not skip this. Return pointing to
* the [.
*/
return (cp + *n);
}
/* This is a style, so return pointing to the #. */
return (cp + *n - 1);
}
/* Draw multiple characters. */
static void
format_draw_many(struct screen_write_ctx *ctx, struct style *sy, char ch,
u_int n)
{
u_int i;
utf8_set(&sy->gc.data, ch);
for (i = 0; i < n; i++)
screen_write_cell(ctx, &sy->gc);
}
/* Draw a format to a screen. */
@@ -493,6 +682,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
enum { LEFT,
CENTRE,
RIGHT,
ABSOLUTE_CENTRE,
LIST,
LIST_LEFT,
LIST_RIGHT,
@@ -501,6 +691,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
const char *names[] = { "LEFT",
"CENTRE",
"RIGHT",
"ABSOLUTE_CENTRE",
"LIST",
"LIST_LEFT",
"LIST_RIGHT",
@@ -508,10 +699,14 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
size_t size = strlen(expanded);
struct screen *os = octx->s, s[TOTAL];
struct screen_write_ctx ctx[TOTAL];
u_int ocx = os->cx, ocy = os->cy, i, width[TOTAL];
u_int map[] = { LEFT, LEFT, CENTRE, RIGHT };
u_int ocx = os->cx, ocy = os->cy, n, i, width[TOTAL];
u_int map[] = { LEFT,
LEFT,
CENTRE,
RIGHT,
ABSOLUTE_CENTRE };
int focus_start = -1, focus_end = -1;
int list_state = -1, fill = -1;
int list_state = -1, fill = -1, even;
enum style_align list_align = STYLE_ALIGN_DEFAULT;
struct grid_cell gc, current_default;
struct style sy, saved_sy;
@@ -535,7 +730,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
*/
for (i = 0; i < TOTAL; i++) {
screen_init(&s[i], size, 1, 0);
screen_write_start(&ctx[i], NULL, &s[i]);
screen_write_start(&ctx[i], &s[i]);
screen_write_clearendofline(&ctx[i], current_default.bg);
width[i] = 0;
}
@@ -546,7 +741,39 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
*/
cp = expanded;
while (*cp != '\0') {
if (cp[0] != '#' || cp[1] != '[') {
/* Handle sequences of #. */
if (cp[0] == '#' && cp[1] != '[' && cp[1] != '\0') {
for (n = 1; cp[n] == '#'; n++)
/* nothing */;
even = ((n % 2) == 0);
if (cp[n] != '[') {
cp += n;
if (even)
n = (n / 2);
else
n = (n / 2) + 1;
width[current] += n;
format_draw_many(&ctx[current], &sy, '#', n);
continue;
}
if (even)
cp += (n + 1);
else
cp += (n - 1);
if (sy.ignore)
continue;
format_draw_many(&ctx[current], &sy, '#', n / 2);
width[current] += (n / 2);
if (even) {
utf8_set(ud, '[');
screen_write_cell(&ctx[current], &sy.gc);
width[current]++;
}
continue;
}
/* Is this not a style? */
if (cp[0] != '#' || cp[1] != '[' || sy.ignore) {
/* See if this is a UTF-8 character. */
if ((more = utf8_open(ud, *cp)) == UTF8_MORE) {
while (*++cp != '\0' && more == UTF8_MORE)
@@ -599,7 +826,8 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
/* If this style pushed or popped the default, update it. */
if (sy.default_type == STYLE_DEFAULT_PUSH) {
memcpy(&current_default, &saved_sy.gc, sizeof current_default);
memcpy(&current_default, &saved_sy.gc,
sizeof current_default);
sy.default_type = STYLE_DEFAULT_BASE;
} else if (sy.default_type == STYLE_DEFAULT_POP) {
memcpy(&current_default, base, sizeof current_default);
@@ -737,31 +965,41 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
/*
* Draw the screens. How they are arranged depends on where the list
* appearsq.
* appears.
*/
switch (list_align) {
case STYLE_ALIGN_DEFAULT:
/* No list. */
format_draw_none(octx, available, ocx, ocy, &s[LEFT],
&s[CENTRE], &s[RIGHT], &frs);
&s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &frs);
break;
case STYLE_ALIGN_LEFT:
/* List is part of the left. */
format_draw_left(octx, available, ocx, ocy, &s[LEFT],
&s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT],
&s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs);
&s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
&s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
focus_start, focus_end, &frs);
break;
case STYLE_ALIGN_CENTRE:
/* List is part of the centre. */
format_draw_centre(octx, available, ocx, ocy, &s[LEFT],
&s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT],
&s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs);
&s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
&s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
focus_start, focus_end, &frs);
break;
case STYLE_ALIGN_RIGHT:
/* List is part of the right. */
format_draw_right(octx, available, ocx, ocy, &s[LEFT],
&s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT],
&s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs);
&s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
&s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
focus_start, focus_end, &frs);
break;
case STYLE_ALIGN_ABSOLUTE_CENTRE:
/* List is in the centre of the entire horizontal space. */
format_draw_absolute_centre(octx, available, ocx, ocy, &s[LEFT],
&s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
&s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
focus_start, focus_end, &frs);
break;
}
@@ -794,17 +1032,22 @@ u_int
format_width(const char *expanded)
{
const char *cp, *end;
u_int width = 0;
u_int n, leading_width, width = 0;
struct utf8_data ud;
enum utf8_state more;
cp = expanded;
while (*cp != '\0') {
if (cp[0] == '#' && cp[1] == '[') {
end = format_skip(cp + 2, "]");
if (end == NULL)
return 0;
cp = end + 1;
if (*cp == '#') {
end = format_leading_hashes(cp, &n, &leading_width);
width += leading_width;
cp = end;
if (*cp == '#') {
end = format_skip(cp + 2, "]");
if (end == NULL)
return (0);
cp = end + 1;
}
} else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
while (*++cp != '\0' && more == UTF8_MORE)
more = utf8_append(&ud, *cp);
@@ -821,25 +1064,46 @@ format_width(const char *expanded)
return (width);
}
/* Trim on the left, taking #[] into account. */
/*
* Trim on the left, taking #[] into account. Note, we copy the whole set of
* unescaped #s, but only add their escaped size to width. This is because the
* format_draw function will actually do the escaping when it runs
*/
char *
format_trim_left(const char *expanded, u_int limit)
{
char *copy, *out;
const char *cp = expanded, *end;
u_int width = 0;
u_int n, width = 0, leading_width;
struct utf8_data ud;
enum utf8_state more;
out = copy = xmalloc(strlen(expanded) + 1);
out = copy = xcalloc(1, strlen(expanded) + 1);
while (*cp != '\0') {
if (cp[0] == '#' && cp[1] == '[') {
end = format_skip(cp + 2, "]");
if (end == NULL)
break;
memcpy(out, cp, end + 1 - cp);
out += (end + 1 - cp);
cp = end + 1;
if (width >= limit)
break;
if (*cp == '#') {
end = format_leading_hashes(cp, &n, &leading_width);
if (leading_width > limit - width)
leading_width = limit - width;
if (leading_width != 0) {
if (n == 1)
*out++ = '#';
else {
memset(out, '#', 2 * leading_width);
out += 2 * leading_width;
}
width += leading_width;
}
cp = end;
if (*cp == '#') {
end = format_skip(cp + 2, "]");
if (end == NULL)
break;
memcpy(out, cp, end + 1 - cp);
out += (end + 1 - cp);
cp = end + 1;
}
} else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
while (*++cp != '\0' && more == UTF8_MORE)
more = utf8_append(&ud, *cp);
@@ -871,7 +1135,8 @@ format_trim_right(const char *expanded, u_int limit)
{
char *copy, *out;
const char *cp = expanded, *end;
u_int width = 0, total_width, skip;
u_int width = 0, total_width, skip, n;
u_int leading_width, copy_width;
struct utf8_data ud;
enum utf8_state more;
@@ -880,15 +1145,35 @@ format_trim_right(const char *expanded, u_int limit)
return (xstrdup(expanded));
skip = total_width - limit;
out = copy = xmalloc(strlen(expanded) + 1);
out = copy = xcalloc(1, strlen(expanded) + 1);
while (*cp != '\0') {
if (cp[0] == '#' && cp[1] == '[') {
end = format_skip(cp + 2, "]");
if (end == NULL)
break;
memcpy(out, cp, end + 1 - cp);
out += (end + 1 - cp);
cp = end + 1;
if (*cp == '#') {
end = format_leading_hashes(cp, &n, &leading_width);
if (width <= skip) {
if (skip - width >= leading_width)
copy_width = 0;
else
copy_width -= (skip - width);
} else
copy_width = leading_width;
if (copy_width != 0) {
if (n == 1)
*out++ = '#';
else {
memset(out, '#', 2 * copy_width);
out += 2 * copy_width;
}
}
width += leading_width;
cp = end;
if (*cp == '#') {
end = format_skip(cp + 2, "]");
if (end == NULL)
break;
memcpy(out, cp, end + 1 - cp);
out += (end + 1 - cp);
cp = end + 1;
}
} else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
while (*++cp != '\0' && more == UTF8_MORE)
more = utf8_append(&ud, *cp);

3898
format.c

File diff suppressed because it is too large Load Diff

96
fuzz/input-fuzzer.c Normal file
View File

@@ -0,0 +1,96 @@
/*
* Copyright (c) 2020 Sergey Nizovtsev <snizovtsev@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stddef.h>
#include <assert.h>
#include <fcntl.h>
#include "tmux.h"
#define FUZZER_MAXLEN 512
#define PANE_WIDTH 80
#define PANE_HEIGHT 25
struct event_base *libevent;
int
LLVMFuzzerTestOneInput(const u_char *data, size_t size)
{
struct bufferevent *vpty[2];
struct window *w;
struct window_pane *wp;
int error;
/*
* Since AFL doesn't support -max_len paramenter we have to
* discard long inputs manually.
*/
if (size > FUZZER_MAXLEN)
return 0;
w = window_create(PANE_WIDTH, PANE_HEIGHT, 0, 0);
wp = window_add_pane(w, NULL, 0, 0);
bufferevent_pair_new(libevent, BEV_OPT_CLOSE_ON_FREE, vpty);
wp->ictx = input_init(wp, vpty[0], NULL);
window_add_ref(w, __func__);
wp->fd = open("/dev/null", O_WRONLY);
if (wp->fd == -1)
errx(1, "open(\"/dev/null\") failed");
wp->event = bufferevent_new(wp->fd, NULL, NULL, NULL, NULL);
input_parse_buffer(wp, (u_char *)data, size);
while (cmdq_next(NULL) != 0)
;
error = event_base_loop(libevent, EVLOOP_NONBLOCK);
if (error == -1)
errx(1, "event_base_loop failed");
assert(w->references == 1);
window_remove_ref(w, __func__);
bufferevent_free(vpty[0]);
bufferevent_free(vpty[1]);
return 0;
}
int
LLVMFuzzerInitialize(__unused int *argc, __unused char ***argv)
{
const struct options_table_entry *oe;
global_environ = environ_create();
global_options = options_create(NULL);
global_s_options = options_create(NULL);
global_w_options = options_create(NULL);
for (oe = options_table; oe->name != NULL; oe++) {
if (oe->scope & OPTIONS_TABLE_SERVER)
options_default(global_options, oe);
if (oe->scope & OPTIONS_TABLE_SESSION)
options_default(global_s_options, oe);
if (oe->scope & OPTIONS_TABLE_WINDOW)
options_default(global_w_options, oe);
}
libevent = osdep_event_init();
options_set_number(global_w_options, "monitor-bell", 0);
options_set_number(global_w_options, "allow-rename", 1);
options_set_number(global_options, "set-clipboard", 2);
socket_path = xstrdup("dummy");
return 0;
}

Some files were not shown because too many files have changed in this diff Show More