Compare commits

..

1 Commits

Author SHA1 Message Date
Nicholas Marriott
0623d1e968 Bump version. 2026-05-20 16:39:41 +01:00
443 changed files with 15445 additions and 58180 deletions

View File

@@ -63,7 +63,7 @@ tmux -vv -Ltest -f/dev/null new
Or if you need your configuration:
~~~bash
~~~base
tmux kill-server
tmux -vv new
~~~

2
.github/README.md vendored
View File

@@ -79,7 +79,7 @@ A small example configuration is in `example_tmux.conf`.
And a bash(1) completion file at:
https://github.com/scop/bash-completion/blob/main/completions-core/tmux.bash
https://github.com/scop/bash-completion/blob/main/completions/tmux
For debugging, run tmux with `-v` or `-vv` to generate server and client log
files in the current directory.

View File

@@ -1,11 +0,0 @@
For pull request code reviews in this repository, do not leave any review comments under any circumstances.
If asked to review a pull request, produce no comments.
Do not comment on pull requests in this repository.
For pull request code reviews in this repository, do not leave a pull request overview.
Do not leave inline review comments.
If asked to review a pull request, provide no summary, no overview, and no code comments.

View File

@@ -16,10 +16,8 @@ concurrency:
jobs:
action:
runs-on: ubuntu-latest
if: github.repository == 'tmux/tmux'
timeout-minutes: 10
steps:
- uses: dessant/lock-threads@89ae32b08ed1a541efecbab17912962a5e38981c # v6
- uses: dessant/lock-threads@v5
with:
github-token: ${{ github.token }}
issue-inactive-days: '30'

View File

@@ -1,90 +0,0 @@
name: 'Run Tests'
on:
workflow_dispatch:
schedule:
- cron: '33 3 * * *'
permissions:
contents: read
concurrency:
group: tmux-tests
cancel-in-progress: true
jobs:
regress:
name: ${{ matrix.name }}
runs-on: ${{ matrix.runner }}
if: github.repository == 'tmux/tmux'
timeout-minutes: 300
strategy:
fail-fast: false
matrix:
include:
- name: ubuntu-24.04-x64
runner: ubuntu-24.04
make: make
configure: --enable-utf8proc --enable-asan
#- name: ubuntu-24.04-arm64
# runner: ubuntu-24.04-arm
# make: make
# configure: --enable-utf8proc --enable-asan
- name: macos-26-arm64
runner: macos-26
make: gmake
configure: --enable-utf8proc --enable-asan
steps:
- name: checkout
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
persist-credentials: false
- name: dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y \
autoconf \
automake \
bison \
build-essential \
libevent-dev \
libncurses-dev \
libutf8proc-dev \
pkg-config
- name: dependencies
if: runner.os == 'macOS'
run: |
brew install \
autoconf \
automake \
bison \
libevent \
jemalloc \
make \
ncurses \
utf8proc \
pkg-config
- name: build
run: |
sh autogen.sh
./configure ${{ matrix.configure }}
${{ matrix.make }} -j"$(getconf _NPROCESSORS_ONLN)"
- name: test
run: |
cd regress
${{ matrix.make }}
- name: logs
if: failure()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: regress-logs-${{ matrix.name }}
path: regress/logs/*.log
if-no-files-found: ignore

3
.gitignore vendored
View File

@@ -7,8 +7,6 @@
*~
.deps/
.dirstamp
.serena/
AGENTS.md
Makefile
Makefile.in
aclocal.m4
@@ -21,7 +19,6 @@ configure
core
etc/
fuzz/*-fuzzer
regress/logs/
tags
tmux
tmux.1.*

View File

@@ -29,7 +29,6 @@ Ted Unangst <tedu@openbsd.org> tedu <tedu>
Theo de Raadt <deraadt@openbsd.org> Theo Deraadt <deraadt@openbsd.org>
Theo de Raadt <deraadt@openbsd.org> deraadt <deraadt>
Thomas Adam <thomas@xteddy.org> Thomas <thomas@xteddy.org>
Thomas Adam <thomas@xteddy.org> Thomas Adam <thomas.adam22@gmail.com>
Thomas Adam <thomas@xteddy.org> Thomas Adam <thomas.adam@smoothwall.net>
Thomas Adam <thomas@xteddy.org> n6tadam <n6tadam@xteddy.org>
Tim van der Molen <tim@openbsd.org> tim <tim>

522
CHANGES
View File

@@ -1,525 +1,3 @@
CHANGES FROM 3.7c TO 3.8
* Many improvements to floating panes:
- new-pane and split-window can now set the title with -T and border lines
with -B, and new-pane -W waits for the pane command to exit;
- dragging with Ctrl now creates a new floating pane;
- modal panes may be created with new-pane -O; a window can have one modal
pane and it prevents interaction with the other panes while it is active.
A modal pane is now used for the editor in buffer mode;
- move-pane and resize-pane can now move and resize floating panes, including
mouse dragging;
- split-window in a floating pane creates a new floating pane that does not
overlap the active pane, if possible;
- break-pane can float a tiled pane, join-pane can tile a floating pane, and
swap-pane works with floating panes;
- choose-tree -h and choose-client -h hide the pane containing the mode, and
-k for choose commands kills the pane when the mode exits, which is useful
for modes in floating panes;
- pane-border-status has top-floating and bottom-floating, and there are new
default bindings under C-b g for common move and resize operations;
- floating panes are unzoomed before creation to avoid a crash.
* Add support for themes and improve default colours:
- tmux now has builtin light and dark colour themes for terminals with 256 or
more colours; a new theme option controls whether tmux detects the terminal
theme, uses terminal colours, or forces light or dark (set to terminal for
the old colours);
- styles can use new theme colour names such as themeblack, themewhite and
themegreen, and colours in styles are expanded as formats;
- the terminal's own theme is reported to panes instead of being guessed from
the background, and inferred more accurately when theme reporting is not
supported (Ayman Bagabas, issue 5343);
- tree-mode-selection-style has been added, tree mode selection styling now
covers the whole line, and built-in modes use improved theme colours.
* Improvements to customize mode:
- hooks and environment variables are now shown;
- e opens the selected value in an editor;
- C toggles showing only changed items;
- array item keys can now be changed.
* Hooks (and control mode notifications) are now handled internally by using
events, each of which may carry keys and values as a payload. The control
mode subscriptions mechanism has been extended to allow general-purpose
"monitors" which check a format every second and trigger a hook when it
changes or becomes true:
- set-hook -B adds a monitor hook using the same subscription syntax as
refresh-client -B, and show-hooks -B lists them. -T runs the hook only when
the format is true;
- wait-for -E waits for hooks, notifications or user events, and set-hook -E
fires an event immediately;
- hooks have additional keys available. wait-for -v can be used to see the
keys sent with a hook event;
- new hooks to cover areas including client create and destroy, pane
activity, pane mode and prompt changes, pane movement and resizing, session
group changes and window creation, close and zoom;
- OSC 133 escape sequences now trigger events: pane-command-started,
pane-command-finished and pane-shell-prompt.
* Add switch-mode, a fast switcher bound by default to Tab for windows and BTab
(S-Tab) for sessions.
* Pane scrollbars now support auto-hide. This replaces the previous modal
behaviour where the pane was resized. With auto-hide, the scrollbar appears
while scrolling or while hovered and disappears after pane-scrollbars-timeout
(Michael Grant).
* Extend the fill-character option so both inside and outside the window can be
changed.
* Change set-option and set-hooks to use formats and add a -F flag to each.
* Add a #{A/count:frames} modifier to show a series of frames as an animation
in the status line (issue 5412 from Fernando Daciuk).
* Change command templates so %% is escaped for single quotes while %1 remains
unquoted, preventing single quotes in session or target names from
terminating quoted command sections (reported by Aliz Hammond).
* display-panes is now a mode rather than an overlay, and can be run inside
another pane. The -b flag has been removed.
* Add new-window -E, respawn-pane -E and respawn-window -E as more convenient
methods to create an empty pane (rather than using '' for the command).
* Menus now belong to the window, so appear on all clients.
* Dragging over an existing copy mode selection now adjusts it (Michael Grant).
* Rectangle selection in copy mode may extend past the end of the current line
to match vi's virtualedit=block behaviour (Mark Kelly, issue 5227).
* With mode-keys set to vi, scrolling in copy mode now keeps the cursor in the
same position relative to the text (Arseniy Simonov, issue 5216).
* Copy mode no longer exits at the bottom while a selection is in progress
(issue 5349).
* Add style attributes for dimming colours (dim=) and for hyperlinks (link= and
nolink) (issues 4842 and 4280 from Moritz Angermann).
* New format modifiers: O: loops over options, V: loops over environments,
the I modifier reports client terminal information, t/d gives a time
difference in seconds, c/f and c/b emit colours, and the m operator supports
multiple terms and fuzzy matching.
* Add or improve format variables including client_colours,
pane_start_command_list, window_manual_width, window_manual_height,
pane_last_output_time, pane_modal_flag and window_modal_pane.
* Add additional pane sort orders, and a z sort order for choose-tree so
floating panes can be sorted by z-index.
* Add -f filters to kill-pane -a, kill-window -a and kill-session -a (issue
4782).
* Allow server ACLs to use groups as well as users (issue 4917).
* The mouse option now defaults to on.
* Fix send-keys -K so keys are inserted in the correct place in the input
queue, like keys from key bindings (issue 3476).
* Fix control mode clients hanging on exit if pty data was still queued (Ben
Maurer, issue 5356), and avoid sending notifications to clients which are
already exiting (Ben Maurer, issue 5357).
* Fix grouped sessions sometimes being left as unusable command targets while
they are being killed (Bryce Miller, issue 5180).
* Fix choose mode filtering when more than one pane exists (issue 5326), and
fix an infinite loop in customize mode when a filter does not match.
* Fix regsub when the pattern is anchored at the start (issue 5341).
* Fix redraw issues around synchronized updates, copy mode, tabs, padding,
status lines, menus, popups, wide characters and floating panes. This
includes fixes for flicker with alternate screen applications and scrollbars
(Michael Grant, issue 5351; Noam Stolero, issue 5350; Aung Myo Kyaw, issue
5098).
* Do not crash looking for the next or previous session (issue 5344), or when
no client is available.
CHANGES FROM 3.7b TO 3.7c
* Build with jemalloc on macOS to avoid what appears to be a bug in calloc
(issue 5385).
* Fix scrollbar initial state so they appear on new windows (issue 5339).
* Check time periodically in loops rather than every one (issue 5367).
* Use message-style again as default for message-format.
* Unzoom before creating floating panes to avoid a crash.
CHANGES FROM 3.7a TO 3.7b
* Fix so that the end of a synchronized update again triggers a redraw.
CHANGES FROM 3.7 TO 3.7a
* Fix crash in break-pane when no name is provided.
* Scrollbar options are now cached rather than being looked up for every redraw
(issue 5298).
* Only forbid #( in names, allow #[, empty names, : and .
CHANGES FROM 3.6b TO 3.7
* Add floating panes. These are panes which sit above the layout ("tiled
panes") like popups but unlike popups are not modal and behave like panes (so
the same escape sequence support). Floating panes are created with the
new-pane command, bound to * by default.
This is an early release of this feature and they are relatively limited.
Currently floating panes can only be moved and resized using the mouse. The
default second status line (if status-format is set to 2) has changed to show
a list of panes. Many obvious features are not yet available for floating
panes (notably the ability to swap floating panes, resize them using
resize-pane, change them between floating and tiles, and restore custom
layouts with floating panes).
Mostly written by Michael Grant with help from Dane Jensen; testing and fixes
from others.
* Allow run-shell arguments after a shell command to be expanded as #{1}, #{2}
and so on (from Rasmus Thystrup Karstensen in issue 5121).
* Add -g to kill-session to kill all sessions in a session group (issue 5157
from github at jiku dot jp).
* Tighten up read-only checks on attach-session, detach-client and
switch-client so that a user should be able to only detach their own client
(reported by John Walker).
* Increase escape delay if the buffer contains a partial paste end, fixes
issues with at least Windows Terminal (from jing dot empty at gmail.com issue
5088).
* When mode-keys is set to vi, do not allow the cursor to go into the invisible
extra cell to the right of the visible text; this is closer to what vi(1)
does (from Max Vim in issue 5070).
* Add a five second limit on pasting for terminals which mysteriously lose the
end sequence if the paste is too big (that is, Terminal.app) (reported by
Garri Djavadyan in issue 4527).
* Show file open errors more sensibly (reported by Meriel Luna Mittelbach in
issue 5081).
* Update supported features list for Foot terminal (from Meriel Luna Mittelbach
in issue 5079).
* Turn off the "is this a paste" guessing if the terminal supports bracket
pasting instead (issue 5031).
* Check FIONREAD for all panes not just piped panes.
* Add emacs-style recentre-top-bottom command to copy mode (issue 5053 from
sinyax75 at gmail dot com).
* Allow the indicator in tree mode to be customized by two new options:
tree-mode-preview-format and tree-mode-preview-style.
* Fix control client hang on exit after toggling no-output (issue 5049 from
Aaron Campbell). Also various other control mode fixes.
* Add support for line numbers in copy mode. There is a new
copy-mode-line-numbers option which may be set to off, default (tmux's normal
line numbering where 0 is the top visible line), absolute (first line in
history is 1), relative (relative to the cursor) and hybrid (current line is
absolute, others relative). Also adds copy-mode-line-number-style and
copy-mode-current-line-number-style to set the style of the line numbers.
When copy mode is entered with the mouse, line numbers stay off. From Leo
Henon in issue 5025.
* Make C-[ have the same bindings as Escape for terminals with extended
keys where they are different (issue 5035 from Eric Nicolas).
* Sanitize paste buffer names in paste_set and paste_rename (issue 5032 from
Barrett Ruth).
* Do not hang in run-shell when job_run fails (from Barrett Ruth in issue
5037).
* Add ability to forward progress bar to outside terminal (issue 4972
from Eric Dorland).
* Translate keypad keys to text in prompt input (from Barrett Ruth in issue
4996).
* Sanitize pane titles and window and session names more consistently and
strictly, prevents C0 characters and other invisible characters causing
problems (reported by Chris Monardo in issue 4999).
* Make clock visible on terminals without colours (from Manuel Einfalt in issue
5001).
* Add detach to default session menu (suggested by Przemyslaw Sztoch).
* Include window format variables for pane notifications (issue 5007 from Saul
Nogueras).
* Limit precision to 100 for formats to stop silly formats from running out of
memory, reported by z1281552865 at gmail dot com. Also various other similar
changes, mostly found by OSS-Fuzz.
* Add WAYLAND_DISPLAY to default update-environment (issue 4965). Also some
additional XDG_* variables (issue 5169).
* Add -C flag to command-prompt to match display-message -C (do not freeze
panes) (from Barrett Ruth in issue 4978).
* Cache user from getpwuid because it can be very expensive on some
platforms (from Ben Maurer in issue 4973).
* Add remain-on-exit key to keep pane around until a key is pressed (from
Michael Grant).
* Add some new mouse ranges called "control0" to "control9" and use to add some
mouse controls to the pane state line (from Dane Jensen with some bits from
Michael Grant).
* Handle OSC 9;4 progress bar sequence and store in format variables (from Eric
Dorland in issue 4954).
* Correctly size buffer used for parsing clipboard sequences (from Michal
Majchrowicz).
* Limit MSG_COMMAND argument to between 0 and 1000 to prevent a misbehaving
client from crashing the server (from Michal Majchrowicz).
* Reorganize host keys are represented internally so they can be built more
easily (from Dane Jensen in issue 4953).
* Add new fuzzers for command parsing, formats and styles (from David
Korczynski in issue 4957). Also fix various issues shown from these.
* Add bracket_paste_flag format flag (from George Nachman in issue 4951).
* Use \- for hyphens in tmux.1 to cause newer groff versions to render them
correctly (from Keith Thompson in issue 4948).
* Various minor code improvements and fixes from Pavel Lavrukhin (issue 4936
and others).
* Use window options for cursor-style to avoid crash when no pane (from Arden
Packeer in issue 4942).
* Fix issue where popup window gets overwritten by background updates (from
Conor Taylor in issue 4920).
* Protect against overflow when scrollbar is off screen (from san65384 at gmail
dot com in issue 4933).
* Copy hyperlinks when redrawing popup so they do not vanish (from Antoine
Gaudreau Simard in issue 4925).
* Work around systemd killing panes early during system shutdown by creating
dependencies from the panes to the service which started tmux (issue 4926
from Dmitry Torokhov).
* Allow codepoint-widths to accept ranges (from san65384 at gmail dot com in
issue 4930).
* Add a short builtin help text for each mode accessible with C-h (based on
code from Patrick Motard in issue 4751).
* Draw message as one format, allowing prompts and messages to occupy only a
portion of the status bar, overlaying the normal status content rather than
replacing the entire line. A new message-format option now controls the
entire message (like status-format). The message-style option now need to
include "fill" in order to cover the whole width (the default has
"fill=yellow"). From Conor Taylor in issue 4861.
* Add next/previous variables for windows in W: loop (from Conor Taylor in
issue 4856).
* Various bug and memory leak fixes from Renaud Allard (issue 4916).
* Add pane_pipe_pid with pipe file descriptor.
* Make -c work with new-session -A (from Jody Frankowski in issue 4906).
* Allow copy mode to work for readonly clients, except for copy commands (from
Dane Jensen).
* Pass paste buffer through vis(3) when pasting to prevent buffers containing
for example the bracket end sequence causing issues, a new -S flag disables
(reported by Mason Davis).
* Add sorting (-O flag) and a custom format (-F) to list-keys (from Dane Jensen
in issue 4845).
* Add scroll-exit-on, scroll-exit-off, scroll-exit-toggle commands to copy mode
(from xcdnlgd at hotmail dot com in issue 4884).
* Respond to DECRQM 2026 (from David Turnbull in issue 4887) and various others
(from Ayman Bagabas in issue 5118).
* Fix various memory leaks reported by Huihui Huang (issue 4872).
* Pass which clipboard is set through to the terminal (from Axel Lindskog in
issue 4858).
* Reuse extended entry when clearing RGB cell, to prevent memory growth when
cells are repeatedly cleared (from Michael K Darling in issue 4862).
* Do not write before buffer when parsing empty clipboard or palette replies,
or try to allocate zero bytes with an empty clipboard sequence (reported by
DongHan Kim).
* Various bug fixes and code improvements from Conor Taylor (issue 4848).
* Clear search counts when clearing marks in case of repeated search (reported
by Daniel Pereira in issue 4817).
* Make OSC 52 work in popups (from gogongxt at 163 dot com in issue 4797).
* Refresh copy mode when style changes (from Josh Cooper in issue 4830).
* Make sorting code common and add -O for sorting to the list commands (from
Dane Jensen in issue 4813).
* Do not treat cells as empty unless the background colour stays the same,
fixes invisible clock in clock mode (reported by Theo Buehler).
* When history-limit is changed, apply to existing panes, not just new
ones (issue 4705).
* Reevaluate menu and popup styles on each draw to allow them to change when
options change (from Josh Cooper in issues 4828 and 4829).
* Handle theme keys earlier so they are processed even if a popup is open (from
Josh Cooper in issue 4827).
* Fix window-size=latest not resizing on switch-client in session groups (from
Ilya Grigoriev in issue 4818).
* Add -e flag to command-prompt to close if empty (from Dane Jensen in issue
4812).
* Correctly draw indicators when pane-border-indicators is set to both
(reported by Ilya Grigoriev in issue 4780).
* Remember last pane or type of location for double and triple clicks and
correctly handle it changes between first and second or second and third
(issue 4795 from Shaobo Song).
* Add paste to the default pane menu (issue 4763).
* Reduce request timeout to 500 milliseconds to match the extended escape-time,
and discard palette requests if receiving a reply for a different index.
* Extend escape timeout if there are active forwarded requests not just
tmux's own requests (issue 4793).
* Correct redrawing of wide characters when overwritten (reported by Jake
Stewart in issue 4737).
* If cannot find a terminator for palette responses, treat as a partial key not
complete (issue 4749).
* Do not send theme unless it has changed, and do not send immediately when
updates are enabled (issue 5787).
* Do not use ;;s in list-keys output as it is confusing and cannot be
parsed on input (from Patrick Motard in issue 4750).
* Redraw pane borders when entering or leaving alternate screen (from Mike
Jonkmans in issue 4788).
* Add focus-follows-mouse option (from Barry Wasdell in issue 4771).
* Add selection_mode format variable for copy mode (from Mike Jonkmans in issue
4773).
* Add prompt-command-cursor-style (from Joshua Cooper in issue 4765).
* With status-keys vi, move the cursor left by one when pressing Escape to
enter command mode, like vi (issue 4767 from Joshua Cooper).
* Add {current}/{active} for -t for current window or active pane (from Manuel
Einfalt, issue 4766).
* Add support for applications to use synchronized output mode (DECSET 2026) to
prevent screen tearing during rapid updates (from Chris Lloyd in issue 4744).
* Do not set a default prompt cursor colour because some terminals (urxvt, st)
do not support the reset sequence (issue 4759).
* Add a scroll-to-mouse command for copy mode to scroll to the mouse position
and bind to the scrollbar, brings the scrollbar keys into line with the other
mouse keys (from Michael Grant in issue 4731).
* Bump the maximum number of SIXEL images to 20.
* Fix key code for M-BSpace (issue 4717).
* Fix calculation of scaled SIXEL size (from nincsnevem662 at gmail dot com in
issue 4739). Also various other fixes and improvements for SIXEL.
* Fix a race between fork and pane_current_path, most noticeable on systems
where starting processes is slow (issue 4719).
* Fix mouse position calculation on scrollbar with pane status line at the top
(issue 4738 from Michael Grant).
* Do not read outside buffer if format is a single #, and do not loop forever
if UTF-8 is unfinished in a format (reported by Giorgi Kobakhia in issue
4735).
* Add a missing skin tone character (from Jake Stewart in issue 4736).
* Allow UTF-8 characters to be combined in either order (reported by Jake
Stewart in issue 4726).
* Do not show scrollbar when entering copy mode from a pane in the alternate
screen (issue 4728 from Michael Grant).
* Fix the size calculation for left-right windows used to spread out cells
horizontally evenly (from Michael Grant in issue 4724).
* Add a get-clipboard option which when enabled (the default is off) and a
clipboard is requested from a pane, requests it from the terminal and
forwards to the requesting pane; also remove the now-redundant
forward-to-pane ability from "refresh-client -l" (issue 4275).
* Fix the noattr attribute in styles, used by the default mode-style (issue
4713).
* Do not remove TERM for commands run from config file (regression reported by
Dennis Eriksen).
* Add seconds options for clock mode (from augustus7613 dot mail at pm dot me,
issue 4697; later improved by Joao Pedro in issue 4760).
CHANGES FROM 3.6a TO 3.6b
* Remove images from the correct list when they are removed while in the

View File

@@ -1,4 +1,9 @@
Copyright (c) Various Authors
THIS IS FOR INFORMATION ONLY, CODE IS UNDER THE LICENCE AT THE TOP OF ITS FILE.
The README, CHANGES, FAQ and TODO files are licensed under the ISC license. All
other files have a license and copyright notice at their start, typically:
Copyright (c) <author>
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above

View File

@@ -4,7 +4,7 @@ CLEANFILES = tmux.1.mdoc tmux.1.man cmd-parse.c
# Distribution tarball options.
EXTRA_DIST = \
CHANGES README COPYING example_tmux.conf \
CHANGES README README.ja COPYING example_tmux.conf \
osdep-*.c mdoc2man.awk tmux.1
dist_EXTRA_tmux_SOURCES = compat/*.[ch]
@@ -13,40 +13,28 @@ AM_CPPFLAGS += @XOPEN_DEFINES@ \
-DTMUX_VERSION='"@VERSION@"' \
-DTMUX_CONF='"$(sysconfdir)/tmux.conf:~/.tmux.conf:$$XDG_CONFIG_HOME/tmux/tmux.conf:~/.config/tmux/tmux.conf"' \
-DTMUX_LOCK_CMD='"@DEFAULT_LOCK_CMD@"' \
-DTMUX_TERM='"@DEFAULT_TERM@"' \
-DTMUX_MOUSE=1
-DTMUX_TERM='"@DEFAULT_TERM@"'
# Additional object files.
LDADD = $(LIBOBJS)
# Set flags for gcc.
if IS_GCC
AM_CFLAGS += -std=gnu99
if IS_OPTIMIZED
AM_CFLAGS += -O2
else
AM_CFLAGS += -O0
endif
AM_CFLAGS += -std=gnu99 -O2
if IS_DEBUG
AM_CFLAGS += -g3 -ggdb
AM_CFLAGS += -Wno-long-long -Wall -W -Wformat=2 -Wno-use-after-free
AM_CFLAGS += -g
AM_CFLAGS += -Wno-long-long -Wall -W -Wformat=2
AM_CFLAGS += -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations
AM_CFLAGS += -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare
AM_CFLAGS += -Wundef -Wbad-function-cast -Winline -Wno-cast-align
AM_CFLAGS += -Wundef -Wbad-function-cast -Winline -Wcast-align
AM_CFLAGS += -Wdeclaration-after-statement -Wno-pointer-sign -Wno-attributes
AM_CFLAGS += -Wno-unused-result -Wno-format-y2k -Wno-unknown-warning-option
AM_CFLAGS += -Wno-maybe-uninitialized
AM_CFLAGS += -Wno-unused-result -Wno-format-y2k
if IS_DARWIN
AM_CFLAGS += -Wno-deprecated-declarations -Wno-macro-redefined
AM_CFLAGS += -Wno-deprecated-declarations -Wno-cast-align -Wno-macro-redefined
endif
AM_CPPFLAGS += -DDEBUG
endif
AM_CPPFLAGS += -iquote.
if IS_ASAN
AM_CPPFLAGS += -DASAN
AM_CFLAGS += -fsanitize=address -fno-omit-frame-pointer
AM_LDFLAGS += -fsanitize=address
endif
endif
# Set flags for Solaris.
@@ -101,10 +89,10 @@ dist_tmux_SOURCES = \
cmd-detach-client.c \
cmd-display-menu.c \
cmd-display-message.c \
cmd-display-panes.c \
cmd-find-window.c \
cmd-find.c \
cmd-if-shell.c \
cmd-invoke.c \
cmd-join-pane.c \
cmd-kill-pane.c \
cmd-kill-server.c \
@@ -112,7 +100,6 @@ dist_tmux_SOURCES = \
cmd-kill-window.c \
cmd-list-buffers.c \
cmd-list-clients.c \
cmd-list-commands.c \
cmd-list-keys.c \
cmd-list-panes.c \
cmd-list-sessions.c \
@@ -161,16 +148,12 @@ dist_tmux_SOURCES = \
control-notify.c \
control.c \
environ.c \
events-payload.c \
events.c \
file.c \
format-draw.c \
format.c \
fuzzy.c \
format-draw.c \
grid-reader.c \
grid-view.c \
grid.c \
hooks.c \
hyperlinks.c \
input-keys.c \
input.c \
@@ -183,15 +166,13 @@ dist_tmux_SOURCES = \
log.c \
menu.c \
mode-tree.c \
monitor.c \
names.c \
notify.c \
options-table.c \
options.c \
paste.c \
popup.c \
proc.c \
prompt-history.c \
prompt.c \
regsub.c \
resize.c \
screen-redraw.c \
@@ -202,31 +183,25 @@ dist_tmux_SOURCES = \
server-fn.c \
server.c \
session.c \
sort.c \
spawn.c \
status.c \
style.c \
tmux-protocol.h \
tmux.c \
tmux.h \
tmux-protocol.h \
tty-acs.c \
tty-draw.c \
tty-features.c \
tty-keys.c \
tty-term.c \
tty.c \
utf8-combined.c \
utf8.c \
window-border.c \
window-buffer.c \
window-client.c \
window-clock.c \
window-copy.c \
window-customize.c \
window-panes.c \
window-switch.c \
window-tree.c \
window-visible.c \
window.c \
xmalloc.c \
xmalloc.h
@@ -252,21 +227,10 @@ if ENABLE_SIXEL
dist_tmux_SOURCES += image.c image-sixel.c
endif
# Add bits for fuzzing if enabled.
if NEED_FUZZING
check_PROGRAMS = \
fuzz/input-fuzzer \
fuzz/cmd-parse-fuzzer \
fuzz/format-fuzzer \
fuzz/style-fuzzer
check_PROGRAMS = fuzz/input-fuzzer
fuzz_input_fuzzer_LDFLAGS = $(FUZZING_LIBS)
fuzz_input_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS)
fuzz_cmd_parse_fuzzer_LDFLAGS = $(FUZZING_LIBS)
fuzz_cmd_parse_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS)
fuzz_format_fuzzer_LDFLAGS = $(FUZZING_LIBS)
fuzz_format_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS)
fuzz_style_fuzzer_LDFLAGS = $(FUZZING_LIBS)
fuzz_style_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS)
endif
# Install tmux.1 in the right format.
@@ -281,7 +245,3 @@ install-exec-hook:
$(mkdir_p) $(DESTDIR)$(mandir)/man1
$(INSTALL_DATA) $(srcdir)/tmux.1.@MANFORMAT@ \
$(DESTDIR)$(mandir)/man1/tmux.1
# And uninstall it.
uninstall-hook:
rm -f $(DESTDIR)$(mandir)/man1/tmux.1

62
README.ja Normal file
View File

@@ -0,0 +1,62 @@
tmuxへようこそ!
tmuxはターミナルマルチプレクサーです。複数のターミナルを一つのスクリーン内に作成し、操作することができます。
バックグラウンドで処理を実行中に一度スクリーンから離れて後から復帰することも可能です。
OpenBSD、FreeBSD、NetBSD、Linux、macOS、Solarisで実行できます。
tmuxはlibevent 2.x.に依存します。 下記からダウンロードしてください。
http://libevent.org
また、ncursesも必要です。こちらからどうぞ。
http://invisible-island.net/ncurses/
tarballでのtmuxのビルドとインストール方法。
$ ./configure && make
$ sudo make install
tmuxはutmp(5)をアップデートするためにutempterを使うことができます。もしインストール済みであればオプション「--enable-utempter」をつけて実行してください。
リポジトリから最新バージョンを手に入れるためには下記を実行。
$ git clone https://github.com/tmux/tmux.git
$ cd tmux
$ sh autogen.sh
$ ./configure && make
(ビルドのためにはlibevent、ncurses libraries、headersに加えて、C compiler、make、autoconf、automake、pkg-configが必要です。)
詳しい情報はhttp://git-scm.comをご覧ください。修正はメール<tmux-users@googlegroups.com>宛、もしくはhttps://github.com/tmux/tmux/issuesにて受け付けています。
tmuxのドキュメントについてはtmux.1マニュアルをご覧ください。こちらのコマンドで参照可能です。
$ nroff -mdoc tmux.1|less
サンプル設定は本リポジトリのexample_tmux.confに
また、bash-completionファイルは下記にあります。
https://github.com/scop/bash-completion/blob/main/completions/tmux
「-v」や「-vv」を指定することでデバッグモードでの起動が可能です。カレントディレクトリにサーバーやクライアントのログファイルが生成されます。
議論やバグレポート用のメーリングリストにはこちらから参加可能です。
https://groups.google.com/forum/#!forum/tmux-users
gitコミットについての連絡先
https://groups.google.com/forum/#!forum/tmux-git
購読は<tmux-users+subscribe@googlegroups.com>までメールをお願いします。
バグレポートや機能追加(特にコードへの貢献)は大歓迎です。こちらにご連絡ください。
tmux-users@googlegroups.com
本ファイル、CHANGES、 FAQ、SYNCINGそしてTODOはISC licenseで保護されています。
その他のファイルのライセンスや著作権については、ファイルの上部に明記されています。
-- Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1 +0,0 @@
Please report security issues to nicholas.marriott@gmail.com.

173
SYNCING Normal file
View File

@@ -0,0 +1,173 @@
Preamble
========
Tmux portable relies on repositories "tmux" and "tmux-obsd".
Here's a description of them:
* "tmux" is the portable version, the one which contains code for other
operating systems, and autotools, etc., which isn't found or needed in the
OpenBSD base system.
* "tmux-obsd" is the version of tmux in OpenBSD base system which provides
the basis of the portable tmux version.
Note: The "tmux-obsd" repository is actually handled by "git cvsimport"
running at 15 minute intervals, so a commit made to OpenBSD's tmux CVS
repository will take at least that long to appear in this git repository.
(It might take longer, depending on the CVS mirror used to import the
OpenBSD code).
If you've never used git before, git tracks meta-data about the committer
and the author, as part of a commit, hence:
% git config [--global] user.name "Your name"
% git config [--global] user.email "you@yourdomain.com"
Note that, if you already have this in the global ~/.gitconfig option, then
this will be used. Setting this per-repository would involve not using the
"--global" flag above. If you wish to use the same credentials always,
pass the "--global" option, as shown.
This is a one-off operation once the repository has been cloned, assuming
this information has ever been set before.
Cloning repositories
====================
This involves having both tmux and tmux-obsd cloned, as in:
% cd /some/where/useful
% git clone https://github.com/tmux/tmux.git
% git clone https://github.com/ThomasAdam/tmux-obsd.git
Note that you do not need additional checkouts to manage the sync -- an
existing clone of either repositories will suffice. So if you already have
these checkouts existing, skip that.
Adding in git-remotes
=====================
Because the portable "tmux" git repository and the "tmux-obsd"
repository do not inherently share any history between each other, the
history has been faked between them. This "faking of history" is something
which has to be told to git for the purposes of comparing the "tmux" and
"tmux-obsd" repositories for syncing. To do this, we must reference the
clone of the "tmux-obsd" repository from the "tmux" repository, as
shown by the following command:
% cd /path/to/tmux
% git remote add obsd-tmux file:///path/to/tmux-obsd
So that now, the remote "obsd-tmux" can be used to reference branches and
commits from the "tmux-obsd" repository, but from the context of the
portable "tmux" repository, which makes sense because it's the "tmux"
repository which will have the updates applied to them.
Fetching updates
================
To ensure the latest commits from "tmux-obsd" can be found from within
"tmux", we have to ensure the "master" branch from "tmux-obsd" is
up-to-date first, and then reference that update in "tmux", as in:
% cd /path/to/tmux-obsd
% git checkout master
% git pull
Then back in "tmux":
% cd /path/to/tmux
% git fetch obsd-tmux
Creating the necessary branches
===============================
Now that "tmux" can see commits and branches from "tmux-obsd" by way
of the remote name "obsd-tmux", we can now create the master branch from
"tmux-obsd" in the "tmux" repository:
% git checkout -b obsd-master obsd-tmux/master
Adding in the fake history points
=================================
To tie both the "master" branch from "tmux" and the "obsd-master"
branch from "tmux-obsd" together, the fake history points added to the
"tmux" repository need to be added. To do this, we must add an
additional refspec line, as in:
% cd /path/to/tmux
% git config --add remote.origin.fetch '+refs/replace/*:refs/replace/*'
% git fetch origin
Performing the Sync
===================
Make sure the "master" branch is checked out:
% git checkout master
The following will show commits on OpenBSD not yet synched with "tmux":
% git log master..obsd-master
From there, merge the result in, fixing up any conflicts which might arise.
% git merge obsd-master
Then ensure things look correct by BUILDING the result of that sync:
% make clean && ./autogen.sh && ./configure && make
Compare the git merge result with what's on origin/master -- that is, check
which commits you're about to push:
% git log origin/master..master
And if happy:
% git push origin master
Keeping an eye on libutil in OpenBSD
====================================
A lot of the compat/ code in tmux comes from libutil, especially imsg.
Sometimes the API can change, etc., which might cause interesting problems
trying to run the portable version of tmux. It's worth checking
periodically for any changes to libutil in OpenBSD and syncing those files
to compat/ as and when appropriate.
Release tmux for next version
=============================
1. Update and commit README and CHANGES. The former should be checked for
anything outdated and updated with a list of things that might break
upgrades and the latter should mention all the major changes since
the last version.
2. Make sure configure.ac has the new version number.
3. Tag with:
% git tag -a 2.X
Where "2.X" is the next version.
Push the tag out with:
% git push --tags
4. Build the tarball with 'make dist'.
5. Check the tarball. If it's good, go here to select the tag just pushed:
https://github.com/tmux/tmux/tags
Click the "Add release notes", upload the tarball and add a link in the
description field to the CHANGES file.
6. Clone the tmux.github.io repository, and change the RELEASE version in the
Makefile. Commit it, and run 'make' to replace %%RELEASE%%. Push the
result out.
7. Change version back to master in configure.ac.

View File

@@ -1,234 +0,0 @@
# Preamble
tmux portable is maintained from two repositories:
* `tmux` is the portable repository. It contains the portability layer,
autotools build files, regression tests, documentation, and code needed for
platforms outside OpenBSD.
* `tmux-openbsd-cutover` is the OpenBSD tmux import repository. Its `master`
branch is the OpenBSD tmux history after the OpenBSD Git cutover. Portable
merges this branch when importing OpenBSD changes.
To create the commits in the tmux-openbsd-cutover repo, the source repo is the
Github mirror of OpenBSD's src repo:
```text
https://github.com/openbsd/src.git
```
The update automation filters the OpenBSD source tree to `usr.bin/tmux/`,
publishes that filtered history as `openbsd-git` in the cutover repository,
cherry-picks new filtered commits onto cutover `master`, then merges cutover
`master` into portable `master`.
If you've never used git before, configure your identity before committing:
```sh
git config [--global] user.name "Your name"
git config [--global] user.email "you@yourdomain.com"
```
# Repository layout
The usual local layout is:
```sh
cd /some/where/useful
git clone https://github.com/tmux/tmux.git tmux-portable
git clone https://github.com/ThomasAdam/tmux-obsd.git tmux-openbsd-cutover
```
The exact directory names do not matter, but the examples below use:
```text
/path/to/tmux-portable
/path/to/tmux-openbsd-cutover
```
The cutover repository has three important branches:
* `master` is the branch portable consumes. It should contain tmux source
only, not automation files.
* `openbsd-git` is the raw filtered OpenBSD tmux branch generated from
OpenBSD src `master`.
* `automation` is an orphan branch containing the GitHub Actions workflow. It
is separate so that workflow files are not merged into portable.
# Adding the OpenBSD remote to portable
In the portable repository, add the cutover repository as a remote:
```sh
cd /path/to/tmux-portable
git remote add tmux-openbsd /path/to/tmux-openbsd-cutover
git config remote.tmux-openbsd.tagOpt --no-tags
```
If the remote already exists, update it instead:
```sh
git remote set-url tmux-openbsd /path/to/tmux-openbsd-cutover
git config remote.tmux-openbsd.tagOpt --no-tags
```
Fetch the cutover master branch explicitly:
```sh
git fetch --no-tags tmux-openbsd master:refs/remotes/tmux-openbsd/master
```
# Automated syncing
The normal sync is performed by the GitHub Actions workflow in the
`automation` branch of the cutover repository. That workflow:
1. Fetches or clones OpenBSD src using a blobless clone.
2. Filters `usr.bin/tmux/` into a local `tmux-openbsd` branch.
3. Updates `tmux-openbsd-cutover/openbsd-git`.
4. Cherry-picks new `openbsd-git` commits onto cutover `master`.
5. Merges cutover `master` into portable `master`.
6. Pushes the changed repositories.
The workflow deliberately runs from the orphan `automation` branch but checks
out cutover `master` as the branch to update. This keeps `.github/` out of
cutover `master`, so portable does not import workflow files when it merges
OpenBSD changes.
# Manual portable merge
If the workflow fails while merging into portable, do the merge locally and
push the result.
Start from an up-to-date portable master:
```sh
cd /path/to/tmux-portable
git fetch origin
git checkout master
git pull --ff-only origin master
```
Fetch the cutover branch:
```sh
git remote add tmux-openbsd /path/to/tmux-openbsd-cutover 2>/dev/null || true
git config remote.tmux-openbsd.tagOpt --no-tags
git fetch --no-tags tmux-openbsd master:refs/remotes/tmux-openbsd/master
```
Merge it:
```sh
git merge --no-ff --log refs/remotes/tmux-openbsd/master
```
Resolve conflicts by deciding whether portable or OpenBSD owns the file.
Useful commands:
```sh
git checkout --ours path/to/file
git add path/to/file
```
This keeps the portable version of a conflicted file.
```sh
git checkout --theirs path/to/file
git add path/to/file
```
This takes the OpenBSD/cutover version of a conflicted file.
Before committing, inspect the result:
```sh
git status
git diff --check
git diff --cached --stat
```
Then finish and publish:
```sh
git commit
git push origin master
```
If the merge attempt is wrong, abort it:
```sh
git merge --abort
```
After pushing a manually resolved merge, rerun the workflow. It should either
no-op or continue from the now-merged portable state.
# Manual cutover update
Normally this is handled by the workflow. If it must be done manually, update
`openbsd-git` in the cutover repository from a filtered OpenBSD src checkout,
then cherry-pick the new filtered commits onto cutover `master`.
The important range is:
```text
origin/openbsd-git..openbsd-git
```
where `origin/openbsd-git` is the previously published filtered OpenBSD tmux
branch and `openbsd-git` is the newly generated filtered branch.
In the cutover repository:
```sh
cd /path/to/tmux-openbsd-cutover
git checkout master
git cherry-pick origin/openbsd-git..openbsd-git
git push origin master openbsd-git
```
Do not put workflow files on cutover `master`; keep them on the orphan
`automation` branch.
# Keeping an eye on libutil in OpenBSD
A lot of the `compat/` code in tmux comes from OpenBSD libraries, especially
imsg. Sometimes APIs change in OpenBSD in ways that require corresponding
portable changes. It is worth checking periodically for relevant OpenBSD
libutil changes and syncing those files into `compat/` as appropriate.
# Release tmux for next version
1. Update and commit README and CHANGES. The former should be checked for
anything outdated and updated with a list of things that might break
upgrades and the latter should mention all the major changes since the last
version.
2. Make sure `configure.ac` has the new version number.
3. Tag with:
```sh
% git tag -a 2.X
```
Where `2.X` is the next version.
Push the tag out with:
```sh
% git push --tags
```
4. Build the tarball with `make dist`.
5. Check the tarball. If it is good, go here to select the tag just pushed:
```text
https://github.com/tmux/tmux/tags
```
Click "Add release notes", upload the tarball and add a link in the
description field to the CHANGES file.
6. Clone the tmux.github.io repository, and change the RELEASE version in the
Makefile. Commit it, and run `make` to replace `%%RELEASE%%`. Push the
result out.
7. Change version back to master in `configure.ac`.

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: alerts.c,v 1.35 2026/07/10 13:38:45 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2015 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -205,7 +205,7 @@ alerts_check_bell(struct window *w)
}
if (!alerts_action_applies(wl, "bell-action"))
continue;
events_fire_winlink("alert-bell", wl);
notify_winlink("alert-bell", wl);
if (s->flags & SESSION_ALERTED)
continue;
@@ -241,7 +241,7 @@ alerts_check_activity(struct window *w)
}
if (!alerts_action_applies(wl, "activity-action"))
continue;
events_fire_winlink("alert-activity", wl);
notify_winlink("alert-activity", wl);
if (s->flags & SESSION_ALERTED)
continue;
@@ -277,7 +277,7 @@ alerts_check_silence(struct window *w)
}
if (!alerts_action_applies(wl, "silence-action"))
continue;
events_fire_winlink("alert-silence", wl);
notify_winlink("alert-silence", wl);
if (s->flags & SESSION_ALERTED)
continue;

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: arguments.c,v 1.67 2026/08/25 06:04:33 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -51,6 +51,13 @@ struct args {
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 *);
@@ -82,7 +89,8 @@ args_copy_value(struct args_value *to, struct args_value *from)
case ARGS_NONE:
break;
case ARGS_COMMANDS:
to->cmd = cmd_parse_add_ref(from->cmd);
to->cmdlist = from->cmdlist;
to->cmdlist->references++;
break;
case ARGS_STRING:
to->string = xstrdup(from->string);
@@ -115,7 +123,7 @@ args_value_as_string(struct args_value *value)
return ("");
case ARGS_COMMANDS:
if (value->cached == NULL)
value->cached = cmd_parse_print(value->cmd, 0);
value->cached = cmd_list_print(value->cmdlist, 0);
return (value->cached);
case ARGS_STRING:
return (value->string);
@@ -141,7 +149,7 @@ args_parse_flag_argument(struct args_value *values, u_int count, char **cause,
int optional_argument)
{
struct args_value *argument, *new;
const char *s, *as;
const char *s;
new = xcalloc(1, sizeof *new);
if (*string != '\0') {
@@ -172,24 +180,12 @@ args_parse_flag_argument(struct args_value *values, u_int count, char **cause,
xasprintf(cause, "-%c expects an argument", flag);
return (-1);
}
if (optional_argument && argument->type == ARGS_STRING) {
as = argument->string;
if (as[0] == '-' && (as[1] == '-' || isalpha((u_char)as[1]))) {
args_free_value(new);
free(new);
log_debug("%s: -%c (optional)", __func__, flag);
args_set(args, flag, NULL, ARGS_ENTRY_OPTIONAL_VALUE);
return (0);
}
}
args_copy_value(new, argument);
(*i)++;
out:
s = args_value_as_string(new);
log_debug("%s: -%c = %s", __func__, flag, s);
args_set(args, flag, new, 0);
return (0);
}
@@ -359,7 +355,7 @@ args_copy_copy_value(struct args_value *to, struct args_value *from, int argc,
to->string = expanded;
break;
case ARGS_COMMANDS:
to->cmd = cmd_parse_add_ref(from->cmd);
to->cmdlist = cmd_list_copy(from->cmdlist, argc, argv);
break;
}
}
@@ -410,7 +406,7 @@ args_free_value(struct args_value *value)
free(value->string);
break;
case ARGS_COMMANDS:
cmd_parse_free(value->cmd);
cmd_list_free(value->cmdlist);
break;
}
free(value->cached);
@@ -469,7 +465,7 @@ args_to_vector(struct args *args, int *argc, char ***argv)
cmd_append_argv(argc, argv, args->values[i].string);
break;
case ARGS_COMMANDS:
s = cmd_parse_print(args->values[i].cmd, 0);
s = cmd_list_print(args->values[i].cmdlist, 0);
cmd_append_argv(argc, argv, s);
free(s);
break;
@@ -524,7 +520,7 @@ args_print_add_value(char **buf, size_t *len, struct args_value *value)
case ARGS_NONE:
break;
case ARGS_COMMANDS:
expanded = cmd_parse_print(value->cmd, 0);
expanded = cmd_list_print(value->cmdlist, 0);
args_print_add(buf, len, "{ %s }", expanded);
break;
case ARGS_STRING:
@@ -739,53 +735,144 @@ args_string(struct args *args, u_int idx)
return (args_value_as_string(&args->values[idx]));
}
/*
* Convert one command argument into a parse tree. A string value is parsed;
* existing commands are referenced.
*/
struct cmd_parse_tree *
args_to_commands(struct cmd *self, struct cmdq_item *item, u_int idx,
const char *default_command, int expand, char **cause)
/* 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 cmd_parse_input pi = { 0 };
const char *cmd = NULL, *file;
char *expanded;
struct cmd_parse_tree *tree;
struct args_command_state *state;
const char *cmd;
const char *file;
*cause = NULL;
state = xcalloc(1, sizeof *state);
if (idx >= args->count)
cmd = default_command;
else {
if (idx < args->count) {
value = &args->values[idx];
switch (value->type) {
case ARGS_COMMANDS:
return (cmd_parse_add_ref(value->cmd));
case ARGS_STRING:
if (value->type == ARGS_COMMANDS) {
state->cmdlist = value->cmdlist;
state->cmdlist->references++;
return (state);
}
cmd = value->string;
break;
case ARGS_NONE:
break;
}
}
if (cmd == NULL)
} else {
if (default_command == NULL)
fatalx("argument out of range");
cmd = default_command;
}
if (expand)
expanded = format_single_from_target(item, cmd);
state->cmd = format_single_from_target(item, cmd);
else
expanded = xstrdup(cmd);
log_debug("%s: %s", __func__, expanded);
state->cmd = xstrdup(cmd);
log_debug("%s: %s", __func__, state->cmd);
cmd_get_source(self, &file, &pi.line);
if (wait)
state->pi.item = item;
cmd_get_source(self, &file, &state->pi.line);
if (file != NULL)
pi.file = file;
pi.flags = cmd_get_parse_flags(self);
tree = cmd_parse_from_string(expanded, &pi, cause);
free(expanded);
return (tree);
state->pi.file = xstrdup(file);
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);
log_debug("%s: %s", __func__, cmd);
cmd_log_argv(argc, argv, __func__);
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);
}
fatalx("invalid parse return state");
}
/* 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((void *)state->pi.file);
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. */
@@ -911,7 +998,7 @@ args_string_percentage(const char *value, long long minval, long long maxval,
copy = xstrdup(value);
copy[valuelen - 1] = '\0';
ll = strtonum(copy, 0, 1000, &errstr);
ll = strtonum(copy, 0, 100, &errstr);
free(copy);
if (errstr != NULL) {
*cause = xstrdup(errstr);
@@ -979,7 +1066,7 @@ args_string_percentage_and_expand(const char *value, long long minval,
copy[valuelen - 1] = '\0';
f = format_single_from_target(item, copy);
ll = strtonum(f, 0, 1000, &errstr);
ll = strtonum(f, 0, 100, &errstr);
free(f);
free(copy);
if (errstr != NULL) {

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: attributes.c,v 1.13 2026/07/02 08:51:05 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Joshua Elsasser <josh@elsasser.org>

71
cfg.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cfg.c,v 1.91 2026/08/03 13:38:42 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -56,7 +56,7 @@ cfg_done(__unused struct cmdq_item *item, __unused void *data)
if (cfg_item != NULL)
cmdq_continue(cfg_item);
prompt_load_history();
status_prompt_load_history();
return (CMD_RETURN_NORMAL);
}
@@ -87,21 +87,20 @@ start_cfg(void)
if (cfg_quiet)
flags = CMD_PARSE_QUIET;
for (i = 0; i < cfg_nfiles; i++)
load_cfg(cfg_files[i], NULL, NULL, flags, NULL);
load_cfg(cfg_files[i], c, NULL, NULL, flags, NULL);
cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL));
}
int
load_cfg(const char *path, struct cmdq_item *item,
load_cfg(const char *path, struct client *c, struct cmdq_item *item,
struct cmd_find_state *current, int flags, struct cmdq_item **new_item)
{
FILE *f;
struct cmd_parse_input pi;
struct cmd_parse_tree *tree;
struct cmd_parse_result *pr;
struct cmdq_item *new_item0;
struct cmdq_state *state;
char *cause = NULL;
if (new_item != NULL)
*new_item = NULL;
@@ -118,16 +117,18 @@ load_cfg(const char *path, struct cmdq_item *item,
pi.flags = flags;
pi.file = path;
pi.line = 1;
tree = cmd_parse_from_file(f, &pi, &cause);
pi.item = item;
pi.c = c;
pr = cmd_parse_from_file(f, &pi);
fclose(f);
if (tree == NULL) {
cfg_add_cause("%s", cause);
free(cause);
if (pr->status == CMD_PARSE_ERROR) {
cfg_add_cause("%s", pr->error);
free(pr->error);
return (-1);
}
cmd_parse_log(__func__, tree);
if (flags & CMD_PARSE_PARSEONLY) {
cmd_parse_free(tree);
cmd_list_free(pr->cmdlist);
return (0);
}
@@ -137,12 +138,12 @@ load_cfg(const char *path, struct cmdq_item *item,
state = cmdq_new_state(NULL, NULL, 0);
cmdq_add_format(state, "current_file", "%s", pi.file);
new_item0 = cmd_invoke_get(tree, state, NULL);
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_parse_free(tree);
cmd_list_free(pr->cmdlist);
cmdq_free_state(state);
if (new_item != NULL)
@@ -152,14 +153,13 @@ load_cfg(const char *path, struct cmdq_item *item,
int
load_cfg_from_buffer(const void *buf, size_t len, const char *path,
struct cmdq_item *item, struct cmd_find_state *current, int flags,
struct cmdq_item **new_item)
struct client *c, struct cmdq_item *item, struct cmd_find_state *current,
int flags, struct cmdq_item **new_item)
{
struct cmd_parse_input pi;
struct cmd_parse_tree *tree;
struct cmd_parse_result *pr;
struct cmdq_item *new_item0;
struct cmdq_state *state;
char *cause = NULL;
if (new_item != NULL)
*new_item = NULL;
@@ -170,15 +170,17 @@ load_cfg_from_buffer(const void *buf, size_t len, const char *path,
pi.flags = flags;
pi.file = path;
pi.line = 1;
tree = cmd_parse_from_buffer(buf, len, &pi, &cause);
if (tree == NULL) {
cfg_add_cause("%s", cause);
free(cause);
pi.item = item;
pi.c = c;
pr = cmd_parse_from_buffer(buf, len, &pi);
if (pr->status == CMD_PARSE_ERROR) {
cfg_add_cause("%s", pr->error);
free(pr->error);
return (-1);
}
cmd_parse_log(__func__, tree);
if (flags & CMD_PARSE_PARSEONLY) {
cmd_parse_free(tree);
cmd_list_free(pr->cmdlist);
return (0);
}
@@ -188,12 +190,12 @@ load_cfg_from_buffer(const void *buf, size_t len, const char *path,
state = cmdq_new_state(NULL, NULL, 0);
cmdq_add_format(state, "current_file", "%s", pi.file);
new_item0 = cmd_invoke_get(tree, state, NULL);
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_parse_free(tree);
cmd_list_free(pr->cmdlist);
cmdq_free_state(state);
if (new_item != NULL)
@@ -221,15 +223,13 @@ cfg_print_causes(struct cmdq_item *item)
{
struct client *c = cmdq_get_client(item);
u_int i;
char *cause;
for (i = 0; i < cfg_ncauses; i++) {
cause = cfg_causes[i];
if (c != NULL && (c->flags & CLIENT_CONTROL))
control_notify_write(c, "%%config-error %s", cause);
control_write(c, "%%config-error %s", cfg_causes[i]);
else
cmdq_print(item, "%s", cause);
free(cause);
cmdq_print(item, "%s", cfg_causes[i]);
free(cfg_causes[i]);
}
free(cfg_causes);
@@ -244,16 +244,14 @@ cfg_show_causes(struct session *s)
struct window_pane *wp;
struct window_mode_entry *wme;
u_int i;
char *cause;
if (cfg_ncauses == 0)
return;
if (c != NULL && (c->flags & CLIENT_CONTROL)) {
for (i = 0; i < cfg_ncauses; i++) {
cause = cfg_causes[i];
control_notify_write(c, "%%config-error %s", cause);
free(cause);
control_write(c, "%%config-error %s", cfg_causes[i]);
free(cfg_causes[i]);
}
goto out;
}
@@ -270,8 +268,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, NULL, &window_view_mode, NULL, NULL,
NULL);
window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL);
for (i = 0; i < cfg_ncauses; i++) {
window_copy_add(wp, 0, "%s", cfg_causes[i]);
free(cfg_causes[i]);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: client.c,v 1.168 2026/08/17 20:14:31 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -69,25 +69,6 @@ static void client_dispatch_attached(struct imsg *);
static void client_dispatch_wait(struct imsg *);
static const char *client_exit_message(void);
/* Does any command have a flag? */
static int
client_command_has_flag(int argc, char **argv, int flag)
{
struct args_value *values;
struct cmd_parse_tree *tree;
int found;
values = args_from_vector(argc, argv);
tree = cmd_parse_from_arguments(values, argc, NULL);
found = cmd_parse_any_have(tree, flag);
cmd_parse_free(tree);
args_free_values(values, argc);
free(values);
return (found);
}
/*
* Get server create lock. If already held then server start is happening in
* another client, so block until the lock is released and return -2 to
@@ -251,15 +232,18 @@ int
client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
int feat)
{
struct cmd_parse_result *pr;
struct msg_command *data;
int fd, i;
const char *ttynam, *termname, *cwd;
pid_t ppid;
enum msgtype msg;
struct termios tio, saved_tio;
size_t size;
char **caps = NULL, *cause;
size_t size, linesize = 0;
ssize_t linelen;
char *line = NULL, **caps = NULL, *cause;
u_int ncaps = 0;
struct args_value *values;
/* Set up the initial command. */
if (shell_command != NULL) {
@@ -270,8 +254,22 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
flags |= CLIENT_STARTSERVER;
} else {
msg = MSG_COMMAND;
if (client_command_has_flag(argc, argv, CMD_STARTSERVER))
/*
* It's annoying parsing the command string twice (in client
* and later in server) but it is necessary to get the start
* server flag.
*/
values = args_from_vector(argc, argv);
pr = cmd_parse_from_arguments(values, argc, NULL);
if (pr->status == CMD_PARSE_SUCCESS) {
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). */
@@ -279,7 +277,7 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
proc_set_signals(client_proc, client_signal);
/* Save the flags. */
client_flags = flags|CLIENT_WRITE_ACK;
client_flags = flags;
log_debug("flags are %#llx", (unsigned long long)client_flags);
/* Initialize the client socket and start the server. */
@@ -409,6 +407,11 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t 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)
@@ -423,8 +426,15 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
else
printf("%%exit\n");
fflush(stdout);
if (client_flags & CLIENT_CONTROL_WAITEXIT)
control_wait_exit(STDIN_FILENO);
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);
@@ -432,12 +442,6 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
}
} else if (client_exitreason != CLIENT_EXIT_NONE)
fprintf(stderr, "%s\n", client_exit_message());
/* Restore the streams to blocking. */
setblocking(STDIN_FILENO, 1);
setblocking(STDOUT_FILENO, 1);
setblocking(STDERR_FILENO, 1);
return (client_exitval);
}
@@ -715,9 +719,6 @@ client_dispatch_wait(struct imsg *imsg)
fprintf(stderr, "server version is too old for client\n");
proc_exit(client_proc);
break;
default:
log_debug("unknown message type %u", imsg->hdr.type);
break;
}
}
@@ -804,8 +805,5 @@ client_dispatch_attached(struct imsg *imsg)
system(data);
proc_send(client_peer, MSG_UNLOCK, -1, NULL, 0);
break;
default:
log_debug("unknown message type %u", imsg->hdr.type);
break;
}
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-attach-session.c,v 1.91 2026/07/10 13:38:45 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -61,7 +61,6 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
struct window_pane *wp;
char *cwd, *cause;
enum msgtype msgtype;
uid_t uid;
if (RB_EMPTY(&sessions)) {
cmdq_error(item, "no sessions");
@@ -107,16 +106,8 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
}
if (fflag)
server_client_set_flags(c, fflag);
if (rflag) {
if (c->flags & CLIENT_READONLY) {
uid = proc_get_peer_uid(c->peer);
if (uid != getuid()) {
cmdq_error(item, "client is read-only");
return (CMD_RETURN_ERROR);
}
}
if (rflag)
c->flags |= (CLIENT_READONLY|CLIENT_IGNORESIZE);
}
c->last_session = c->session;
if (c->session != NULL) {
@@ -163,7 +154,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
if (~c->flags & CLIENT_CONTROL)
proc_send(c->peer, MSG_READY, -1, NULL, 0);
events_fire_client("client-attached", c);
notify_client("client-attached", c);
c->flags |= CLIENT_ATTACHED;
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-bind-key.c,v 1.47 2025/04/09 07:03:04 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -56,12 +56,11 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
key_code key;
const char *tablename, *note = args_get(args, 'N'), *file;
const char *tablename, *note = args_get(args, 'N');
struct cmd_parse_result *pr;
int repeat;
struct args_value *value;
u_int count = args_count(args);
struct cmd_parse_tree *cmd;
struct cmd_parse_input pi;
key = key_string_lookup_string(args_string(args, 0));
if (key == KEYC_NONE || key == KEYC_UNKNOWN) {
@@ -84,18 +83,25 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item)
value = args_value(args, 1);
if (count == 2 && value->type == ARGS_COMMANDS) {
cmd = cmd_parse_add_ref(value->cmd);
key_bindings_add(tablename, key, note, repeat, cmd);
key_bindings_add(tablename, key, note, repeat, value->cmdlist);
value->cmdlist->references++;
return (CMD_RETURN_NORMAL);
}
memset(&pi, 0, sizeof pi);
cmd_get_source(self, &file, &pi.line);
if (file != NULL)
pi.file = file;
pi.flags = cmd_get_parse_flags(self);
cmd = cmd_parse_from_arguments(args_values(args) + 1, count - 1, &pi);
key_bindings_add(tablename, key, note, repeat, cmd);
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_ERROR:
cmdq_error(item, "%s", pr->error);
free(pr->error);
return (CMD_RETURN_ERROR);
case CMD_PARSE_SUCCESS:
break;
}
key_bindings_add(tablename, key, note, repeat, pr->cmdlist);
return (CMD_RETURN_NORMAL);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-break-pane.c,v 1.76 2026/08/17 07:04:45 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -34,10 +34,9 @@ const struct cmd_entry cmd_break_pane_entry = {
.name = "break-pane",
.alias = "breakp",
.args = { "abdPF:n:s:t:Wx:X:y:Y:", 0, 0, NULL },
.usage = "[-abdPW] [-F format] [-n window-name] [-s src-pane] "
"[-t dst-window] [-x width] [-y height] [-X x-position] "
"[-Y y-position]",
.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 },
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX },
@@ -46,46 +45,6 @@ const struct cmd_entry cmd_break_pane_entry = {
.exec = cmd_break_pane_exec
};
static enum cmd_retval
cmd_break_pane_float(struct cmdq_item *item, struct args *args,
struct window *w, struct window_pane *wp)
{
struct layout_cell *lc = wp->layout_cell;
char *cause = NULL;
enum pane_lines lines = window_get_pane_lines(w);
struct layout_geometry *fg = &lc->fg;
if (window_pane_is_floating(wp)) {
cmdq_error(item, "pane is already floating");
return (CMD_RETURN_ERROR);
}
if (w->flags & WINDOW_ZOOMED) {
cmdq_error(item, "can't float a pane while window is zoomed");
return (CMD_RETURN_ERROR);
}
if (layout_floating_args_parse(item, args, lines, w, fg, &cause) != 0) {
cmdq_error(item, "failed to float pane: %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
layout_remove_tile(w, lc);
layout_set_size(lc, fg->sx, fg->sy, fg->xoff, fg->yoff);
lc->flags |= LAYOUT_CELL_FLOATING;
TAILQ_REMOVE(&w->z_index, wp, zentry);
TAILQ_INSERT_HEAD(&w->z_index, wp, zentry);
if (!args_has(args, 'd'))
window_set_active_pane(w, wp, 1);
layout_fix_offsets(w);
layout_fix_panes(w, NULL);
events_fire_window("window-layout-changed", w);
server_redraw_window(w);
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
{
@@ -98,23 +57,10 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
struct session *src_s = source->s;
struct session *dst_s = target->s;
struct window_pane *wp = source->wp;
struct window *w = wl->window, *old_w = w;
char *cause, *cp;
int idx = target->idx, before, old_idx = wl->idx;
const char *template, *name = args_get(args, 'n');
if (wp == w->modal) {
cmdq_error(item, "pane is modal");
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'W'))
return (cmd_break_pane_float(item, args, w, wp));
if (name != NULL && !check_name(name)) {
cmdq_error(item, "invalid window name: %s", name);
return (CMD_RETURN_ERROR);
}
struct window *w = wl->window;
char *name, *cause, *cp;
int idx = target->idx, before;
const char *template;
before = args_has(args, 'b');
if (args_has(args, 'a') || before) {
@@ -127,22 +73,21 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
}
server_unzoom_window(w);
if (window_count_panes(w, 1) == 1) {
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 (name != NULL) {
window_set_name(w, name, 0);
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);
wl = winlink_find_by_window(&dst_s->windows, w);
if (wl == NULL)
return (CMD_RETURN_ERROR);
window_fire_pane_moved(wp, old_w, old_idx, w, wl->idx);
goto out;
}
if (idx != -1 && winlink_find_by_index(&dst_s->windows, idx) != NULL) {
@@ -151,40 +96,33 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
}
TAILQ_REMOVE(&w->panes, wp, entry);
TAILQ_REMOVE(&w->z_index, wp, zentry);
server_client_remove_pane(wp);
window_lost_pane(w, wp);
layout_close_pane(wp);
w = wp->window = window_create(w->sx, w->sy, w->xpixel, w->ypixel);
window_add_ref(w, __func__);
options_set_parent(wp->options, w->options);
wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
TAILQ_INSERT_HEAD(&w->panes, wp, entry);
TAILQ_INSERT_HEAD(&w->z_index, wp, zentry);
w->active = wp;
w->latest = tc;
free(w->name);
if (name == NULL)
w->name = default_window_name(w);
else {
w->name = clean_name(name, 0);
if (!args_has(args, 'n')) {
name = default_window_name(w);
window_set_name(w, name);
free(name);
} else {
window_set_name(w, args_get(args, 'n'));
options_set_number(w->options, "automatic-rename", 0);
}
window_set_fill_cells(w);
if (idx == -1)
idx = -1 - options_get_number(dst_s->options, "base-index");
wl = session_attach(dst_s, w, idx, &cause); /* can't fail */
layout_init(w, wp);
wp->flags |= PANE_CHANGED;
colour_palette_from_option(&wp->palette, wp->options);
window_remove_ref(w, __func__);
events_fire_window("window-created", w);
window_fire_pane_moved(wp, old_w, old_idx, w, wl->idx);
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(args, 'd')) {
session_select(dst_s, wl->idx);
cmd_find_from_session(current, dst_s, 0);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-capture-pane.c,v 1.68 2026/07/20 11:16:33 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Jonathan Alvarado <radobobo@users.sourceforge.net>
@@ -29,21 +29,18 @@
static enum cmd_retval cmd_capture_pane_exec(struct cmd *, struct cmdq_item *);
static char *cmd_capture_pane_append(char *, size_t *, const char *,
size_t);
static char *cmd_capture_pane_append(char *, size_t *, char *, size_t);
static char *cmd_capture_pane_pending(struct args *, struct window_pane *,
size_t *);
static char *cmd_capture_pane_history(struct args *, struct cmdq_item *,
struct window_pane *, size_t *);
static char *cmd_capture_pane_hyperlinks(struct grid *, struct screen *,
u_int, u_int *, u_int *, size_t *);
const struct cmd_entry cmd_capture_pane_entry = {
.name = "capture-pane",
.alias = "capturep",
.args = { "ab:CeE:FHJLMNpPqRS:Tt:", 0, 0, NULL },
.usage = "[-aCeFHJLMNpPqRT] " CMD_BUFFER_USAGE " [-E end-line] "
.args = { "ab:CeE:JMNpPqS:Tt:", 0, 0, NULL },
.usage = "[-aCeJMNpPqT] " CMD_BUFFER_USAGE " [-E end-line] "
"[-S start-line] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 },
@@ -66,8 +63,7 @@ const struct cmd_entry cmd_clear_history_entry = {
};
static char *
cmd_capture_pane_append(char *buf, size_t *len, const char *line,
size_t linelen)
cmd_capture_pane_append(char *buf, size_t *len, char *line, size_t linelen)
{
buf = xrealloc(buf, *len + linelen + 1);
memcpy(buf + *len, line, linelen);
@@ -75,108 +71,6 @@ cmd_capture_pane_append(char *buf, size_t *len, const char *line,
return (buf);
}
static char *
cmd_capture_pane_cell(struct screen *s, u_int xx, u_int yy)
{
struct grid *gd = s->grid;
struct hyperlinks *hl = s->hyperlinks;
struct grid_cell gc;
char *line, *data, *link, *linkid, *f, *b, *u;
char c[UTF8_SIZE + 1];
const char *uri, *iid;
u_int flags;
grid_get_cell(gd, xx, yy, &gc);
memcpy(c, gc.data.data, gc.data.size);
c[gc.data.size] = '\0';
utf8_stravis(&data, c, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL);
if (gc.link != 0 && hyperlinks_get(hl, gc.link, &uri, &iid, NULL)) {
xasprintf(&link, "%s", uri);
if (iid != NULL && *iid != '\0')
xasprintf(&linkid, "%s", iid);
else
xasprintf(&linkid, "NONE");
} else {
xasprintf(&link, "NONE");
xasprintf(&linkid, "NONE");
}
flags = gc.flags;
if (gc.fg & COLOUR_FLAG_256)
flags |= GRID_FLAG_FG256;
if (gc.bg & COLOUR_FLAG_256)
flags |= GRID_FLAG_BG256;
xasprintf(&f, "%s[%x]", colour_tostring(gc.fg), gc.fg);
xasprintf(&b, "%s[%x]", colour_tostring(gc.bg), gc.bg);
xasprintf(&u, "%s[%x]", colour_tostring(gc.us), gc.us);
xasprintf(&line, "\t\tC %u,%u data=(%u,%u,%s) flags=%s[%x] "
"attr=%s[%x] fg=%s bg=%s us=%s link=%s linkid=%s\n",
yy, xx, gc.data.width, gc.data.size, data,
grid_cell_flags_string(flags), flags,
grid_cell_attr_string(gc.attr), gc.attr, f, b, u, link, linkid);
free(f);
free(b);
free(u);
free(link);
free(linkid);
free(data);
return (line);
}
static char *
cmd_capture_pane_grid(struct window_pane *wp, size_t *len)
{
struct screen *s = &wp->base;
struct grid *gd = s->grid;
struct grid_line *gl;
struct osc133_data *od;
char *buf = xstrdup(""), *line;
char p[11];
u_int yy, xx, total = gd->hsize + gd->sy;
xasprintf(&line, "G %ux%u (%u/%u)\n", gd->sx, gd->sy, gd->hsize,
gd->hlimit);
buf = cmd_capture_pane_append(buf, len, line, strlen(line));
free(line);
for (yy = 0; yy < total; yy++) {
gl = grid_get_line(gd, yy);
if (yy < gd->hsize)
snprintf(p, sizeof p, "-");
else
snprintf(p, sizeof p, "%u", yy - gd->hsize);
od = &gl->osc133_data;
if (gl->flags & GRID_LINE_OSC133_FLAGS) {
xasprintf(&line, "\tL %u (%s) flags=%s[%x] "
"%u/%u osc133=%u,%u,%u,%u,%u\n", yy, p,
grid_line_flags_string(gl->flags), gl->flags,
gl->cellused, gl->cellsize, od->prompt_col,
od->cmd_col, od->out_start_col,
od->out_end_col, od->exit_status);
} else {
xasprintf(&line, "\tL %u (%s) flags=%s[%x] "
"%u/%u\n", yy, p,
grid_line_flags_string(gl->flags), gl->flags,
gl->cellused, gl->cellsize);
}
buf = cmd_capture_pane_append(buf, len, line, strlen(line));
free(line);
for (xx = 0; xx < gd->sx; xx++) {
line = cmd_capture_pane_cell(s, xx, yy);
buf = cmd_capture_pane_append(buf, len, line,
strlen(line));
free(line);
}
}
return (buf);
}
static char *
cmd_capture_pane_pending(struct args *args, struct window_pane *wp,
size_t *len)
@@ -209,46 +103,6 @@ cmd_capture_pane_pending(struct args *args, struct window_pane *wp,
return (buf);
}
static char *
cmd_capture_pane_hyperlinks(struct grid *gd, struct screen *s, u_int py,
u_int *links, u_int *nlinks, size_t *len)
{
const struct grid_line *gl = grid_peek_line(gd, py);
struct grid_cell gc;
const char *uri;
char *line = xstrdup("");
u_int i, j;
*len = 0;
if (s->hyperlinks == NULL || (~gl->flags & GRID_LINE_HYPERLINK))
return (line);
for (i = 0; i < gl->cellused; i++) {
grid_get_cell(gd, i, py, &gc);
if (gc.link == 0)
continue;
for (j = 0; j < *nlinks; j++) {
if (links[j] == gc.link)
break;
}
if (j != *nlinks)
continue;
if (!hyperlinks_get(s->hyperlinks, gc.link, &uri, NULL, NULL))
continue;
if (*nlinks == gd->sx)
break;
links[(*nlinks)++] = gc.link;
if (*len != 0)
line = cmd_capture_pane_append(line, len, " ", 1);
line = cmd_capture_pane_append(line, len, uri, strlen(uri));
}
return (line);
}
static char *
cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
struct window_pane *wp, size_t *len)
@@ -258,11 +112,9 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
struct screen *s;
struct grid_cell *gc = NULL;
struct window_mode_entry *wme;
int n, join_lines, number_lines, flags = 0;
int show_flags, hyperlinks;
u_int *links = NULL, nlinks = 0;
int n, join_lines, flags = 0;
u_int i, sx, top, bottom, tmp;
char *cause, *buf = NULL, *line, b[64], *cp;
char *cause, *buf, *line;
const char *Sflag, *Eflag;
size_t linelen;
@@ -340,57 +192,12 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
flags |= GRID_STRING_EMPTY_CELLS;
if (!join_lines && !args_has(args, 'N'))
flags |= GRID_STRING_TRIM_SPACES;
number_lines = args_has(args, 'L');
show_flags = args_has(args, 'F');
hyperlinks = args_has(args, 'H');
if (hyperlinks)
links = xreallocarray(NULL, gd->sx, sizeof *links);
buf = NULL;
for (i = top; i <= bottom; i++) {
if (hyperlinks) {
line = cmd_capture_pane_hyperlinks(gd, s, i, links,
&nlinks, &linelen);
} else {
line = grid_string_cells(gd, 0, i, sx, &gc, flags, s);
linelen = strlen(line);
}
if (hyperlinks && linelen == 0) {
free(line);
continue;
}
if (number_lines) {
if (i >= gd->hsize)
n = i - gd->hsize;
else
n = (int)i - (int)gd->hsize;
n = snprintf(b, sizeof b, "%d ", n);
if (n >= 0)
buf = cmd_capture_pane_append(buf, len, b, n);
}
if (show_flags) {
cp = b;
*cp = '\0';
gl = grid_peek_line(gd, i);
if (gl->flags & GRID_LINE_DEAD)
*cp++ = 'D';
if (gl->flags & GRID_LINE_HYPERLINK)
*cp++ = 'H';
if (gl->flags & GRID_LINE_START_OUTPUT)
*cp++ = 'O';
if (gl->flags & GRID_LINE_START_PROMPT)
*cp++ = 'P';
if (gl->flags & GRID_LINE_WRAPPED)
*cp++ = 'W';
if (gl->flags & GRID_LINE_EXTENDED)
*cp++ = 'X';
if (b == cp)
*cp++ = '-';
*cp++ = ' ';
*cp = '\0';
buf = cmd_capture_pane_append(buf, len, b, strlen (b));
}
buf = cmd_capture_pane_append(buf, len, line, linelen);
gl = grid_peek_line(gd, i);
@@ -399,9 +206,6 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
free(line);
}
free(links);
if (buf == NULL)
buf = xstrdup("");
return (buf);
}
@@ -420,14 +224,11 @@ cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
grid_clear_history(wp->base.grid);
if (args_has(args, 'H'))
screen_reset_hyperlinks(wp->screen);
server_redraw_window(wp->window);
return (CMD_RETURN_NORMAL);
}
len = 0;
if (args_has(args, 'R'))
buf = cmd_capture_pane_grid(wp, &len);
else if (args_has(args, 'P') && !args_has(args, 'H'))
if (args_has(args, 'P'))
buf = cmd_capture_pane_pending(args, wp, &len);
else
buf = cmd_capture_pane_history(args, item, wp, &len);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-choose-tree.c,v 1.58 2026/07/14 17:17:17 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2012 Thomas Adam <thomas@xteddy.org>
@@ -33,8 +33,8 @@ const struct cmd_entry cmd_choose_tree_entry = {
.name = "choose-tree",
.alias = NULL,
.args = { "F:f:GhK:kNO:rst:wyZ", 0, 1, cmd_choose_tree_args_parse },
.usage = "[-GhkNrswZ] [-F format] [-f filter] [-K key-format] "
.args = { "F:f:GK:NO:rst:wyZ", 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 },
@@ -47,8 +47,8 @@ const struct cmd_entry cmd_choose_client_entry = {
.name = "choose-client",
.alias = NULL,
.args = { "F:f:hiK:kNO:rt:yZ", 0, 1, cmd_choose_tree_args_parse },
.usage = "[-hikNrZ] [-F format] [-f filter] [-K key-format] "
.args = { "F:f:K:NO:rt:yZ", 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 },
@@ -61,8 +61,8 @@ const struct cmd_entry cmd_choose_buffer_entry = {
.name = "choose-buffer",
.alias = NULL,
.args = { "F:f:K:kNO:rt:yZ", 0, 1, cmd_choose_tree_args_parse },
.usage = "[-kNrZ] [-F format] [-f filter] [-K key-format] "
.args = { "F:f:K:NO:rt:yZ", 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 },
@@ -75,8 +75,8 @@ const struct cmd_entry cmd_customize_mode_entry = {
.name = "customize-mode",
.alias = NULL,
.args = { "F:f:kNt:yZ", 0, 0, NULL },
.usage = "[-kNZ] [-F format] [-f filter] " CMD_TARGET_PANE_USAGE,
.args = { "F:f:Nt:yZ", 0, 0, NULL },
.usage = "[-NZ] [-F format] [-f filter] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 },
@@ -84,34 +84,6 @@ const struct cmd_entry cmd_customize_mode_entry = {
.exec = cmd_choose_tree_exec
};
const struct cmd_entry cmd_switch_mode_entry = {
.name = "switch-mode",
.alias = NULL,
.args = { "F:kst:wZ", 0, 1, cmd_choose_tree_args_parse },
.usage = "[-kswZ] [-F format] " CMD_TARGET_PANE_USAGE " [command]",
.target = { 't', CMD_FIND_PANE, 0 },
.flags = 0,
.exec = cmd_choose_tree_exec
};
const struct cmd_entry cmd_display_panes_entry = {
.name = "display-panes",
.alias = "displayp",
.args = { "d:kNs:t:Z", 0, 1, cmd_choose_tree_args_parse },
.usage = "[-kNZ] [-d duration] [-s source-window] "
CMD_TARGET_PANE_USAGE " [template]",
.source = { 's', CMD_FIND_WINDOW, 0 },
.target = { 't', CMD_FIND_PANE, 0 },
.flags = CMD_AFTERHOOK,
.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)
@@ -126,13 +98,6 @@ cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item)
struct cmd_find_state *target = cmdq_get_target(item);
struct window_pane *wp = target->wp;
const struct window_mode *mode;
enum sort_order order;
order = sort_order_from_string(args_get(args, 'O'));
if (order == SORT_END && args_has(args, 'O')) {
cmdq_error(item, "invalid sort order");
return (CMD_RETURN_ERROR);
}
if (cmd_get_entry(self) == &cmd_choose_buffer_entry) {
if (paste_is_empty())
@@ -144,13 +109,9 @@ cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item)
mode = &window_client_mode;
} else if (cmd_get_entry(self) == &cmd_customize_mode_entry)
mode = &window_customize_mode;
else if (cmd_get_entry(self) == &cmd_switch_mode_entry)
mode = &window_switch_mode;
else if (cmd_get_entry(self) == &cmd_display_panes_entry)
mode = &window_panes_mode;
else
mode = &window_tree_mode;
window_pane_set_mode(wp, NULL, mode, item, target, args);
window_pane_set_mode(wp, NULL, mode, target, args);
return (CMD_RETURN_NORMAL);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-command-prompt.c,v 1.76 2026/08/25 06:04:33 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -34,16 +34,16 @@ static enum args_parse_type cmd_command_prompt_args_parse(struct args *,
static enum cmd_retval cmd_command_prompt_exec(struct cmd *,
struct cmdq_item *);
static enum prompt_result cmd_command_prompt_callback(struct client *, void *,
const char *, enum prompt_key_result);
static int cmd_command_prompt_callback(struct client *, void *,
const char *, int);
static void cmd_command_prompt_free(void *);
const struct cmd_entry cmd_command_prompt_entry = {
.name = "command-prompt",
.alias = NULL,
.args = { "1CbeFiklI:NPp:t:T:", 0, 1, cmd_command_prompt_args_parse },
.usage = "[-1CbeFiklNP] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE
.args = { "1bFkliI:Np:t:T:", 0, 1, cmd_command_prompt_args_parse },
.usage = "[-1bFkliN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE
" [-T prompt-type] [template]",
.flags = CMD_CLIENT_TFLAG,
@@ -57,13 +57,11 @@ struct cmd_command_prompt_prompt {
struct cmd_command_prompt_cdata {
struct cmdq_item *item;
struct cmd_parse_tree *tree;
struct args_command_state *state;
int flags;
enum prompt_type prompt_type;
struct window_pane *wp;
struct cmd_command_prompt_prompt *prompts;
u_int count;
u_int current;
@@ -85,19 +83,14 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
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, *cmd;
const char *type, *s, *input;
struct cmd_command_prompt_cdata *cdata;
char *tmp, *prompts, *prompt, *next_prompt;
char *inputs = NULL, *next_input, *cause = NULL;
struct window_pane *wp = target->wp;
char *inputs = NULL, *next_input;
u_int count = args_count(args);
int wait = !args_has(args, 'b'), space = 1, n;
int pane = args_has(args, 'P');
int wait = !args_has(args, 'b'), space = 1;
if (pane) {
if (wp == NULL || window_pane_has_prompt(wp))
return (CMD_RETURN_NORMAL);
} else if (tc->prompt != NULL)
if (tc->prompt_string != NULL)
return (CMD_RETURN_NORMAL);
if (args_has(args, 'i'))
wait = 0;
@@ -105,24 +98,14 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
cdata = xcalloc(1, sizeof *cdata);
if (wait)
cdata->item = item;
if (pane)
cdata->wp = wp;
if (count != 0) {
cdata->tree = args_to_commands(self, item, 0, NULL,
args_has(args, 'F'), &cause);
if (cdata->tree == NULL) {
cmdq_error(item, "%s", cause);
free(cause);
free(cdata);
return (CMD_RETURN_ERROR);
}
}
cdata->state = args_make_commands_prepare(self, item, 0, "%1", wait,
args_has(args, 'F'));
if ((s = args_get(args, 'p')) == NULL) {
if (count != 0) {
cmd = args_string(args, 0);
n = strcspn(cmd, " ,");
xasprintf(&prompts, "(%.*s)", n, cmd);
tmp = args_make_commands_get_command(cdata->state);
xasprintf(&prompts, "(%s)", tmp);
free(tmp);
} else {
prompts = xstrdup(":");
space = 0;
@@ -163,7 +146,7 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
}
if ((type = args_get(args, 'T')) != NULL) {
cdata->prompt_type = prompt_type(type);
cdata->prompt_type = status_prompt_type(type);
if (cdata->prompt_type == PROMPT_TYPE_INVALID) {
cmdq_error(item, "unknown type: %s", type);
cmd_command_prompt_free(cdata);
@@ -180,118 +163,72 @@ 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;
else if (args_has(args, 'e'))
cdata->flags |= PROMPT_BSPACE_EXIT;
if (args_has(args, 'C'))
cdata->flags |= PROMPT_NOFREEZE;
if (pane) {
cdata->flags |= PROMPT_ISPANE;
window_pane_set_prompt(wp, tc, target, cdata->prompts[0].prompt,
cdata->prompts[0].input, cmd_command_prompt_callback,
cmd_command_prompt_free, cdata, cdata->flags,
cdata->prompt_type);
} else {
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);
}
cmd_command_prompt_free, cdata, cdata->flags, cdata->prompt_type);
if (!wait)
return (CMD_RETURN_NORMAL);
return (CMD_RETURN_WAIT);
}
static enum prompt_result
static int
cmd_command_prompt_callback(struct client *c, void *data, const char *s,
enum prompt_key_result key)
int done)
{
struct cmd_command_prompt_cdata *cdata = data;
char *error;
struct cmdq_item *item = cdata->item, *new_item;
struct cmdq_state *cs = NULL;
struct cmd_list *cmdlist;
struct cmd_command_prompt_prompt *prompt;
struct cmd_invoke_input ci = { 0 };
struct cmd_parse_tree *tree;
struct cmd_parse_input pi = { 0 };
int argc = 0;
char **argv = NULL;
if (s == NULL || key == PROMPT_KEY_MOVE)
if (s == NULL)
goto out;
if (key == PROMPT_KEY_CLOSE) {
if (done) {
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];
if (cdata->wp != NULL) {
window_pane_update_prompt(cdata->wp,
prompt->prompt, prompt->input);
} else
status_prompt_update(c, prompt->prompt,
prompt->input);
return (PROMPT_CONTINUE);
status_prompt_update(c, prompt->prompt, prompt->input);
return (1);
}
}
argc = cdata->argc;
argv = cmd_copy_argv(cdata->argc, cdata->argv);
if (key != PROMPT_KEY_CLOSE)
if (!done)
cmd_append_argv(&argc, &argv, s);
else {
if (done) {
cmd_free_argv(cdata->argc, cdata->argv);
cdata->argc = argc;
cdata->argv = cmd_copy_argv(argc, argv);
}
if (item != NULL)
cs = cmdq_get_state(item);
if (cdata->tree != NULL) {
/* Explicit body: prompt inputs become the template argv. */
ci.argc = argc;
ci.argv = argv;
new_item = cmd_invoke_get(cdata->tree, cs, &ci);
if (item == NULL)
cmdq_append(c, new_item);
else
cmdq_insert_after(item, new_item);
} else if (argc > 0 && argv[0] != NULL) {
/* No body: parse the submitted text as command language. */
tree = cmd_parse_from_string(argv[0], &pi, &error);
if (tree == NULL) {
if (item == NULL)
cmdlist = args_make_commands(cdata->state, argc, argv, &error);
if (cmdlist == NULL) {
cmdq_append(c, cmdq_get_error(error));
else
cmdq_error(item, "%s", error);
free(error);
} else {
new_item = cmd_invoke_get(tree, cs, NULL);
cmd_parse_free(tree);
if (item == NULL)
} else if (item == NULL) {
new_item = cmdq_get_command(cmdlist, NULL);
cmdq_append(c, new_item);
else
} else {
new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
cmdq_insert_after(item, new_item);
}
}
cmd_free_argv(argc, argv);
/*
* An incremental prompt fires its callback on every edit but must stay
* open for further typing; only an explicit close (handled above) ends
* it.
*/
if (cdata->flags & PROMPT_INCREMENTAL)
return (PROMPT_CONTINUE);
if (c->prompt_inputcb != cmd_command_prompt_callback)
return (1);
out:
if (item != NULL) {
cdata->item = NULL;
if (item != NULL)
cmdq_continue(item);
}
return (PROMPT_CLOSE);
return (0);
}
static void
@@ -300,17 +237,12 @@ cmd_command_prompt_free(void *data)
struct cmd_command_prompt_cdata *cdata = data;
u_int i;
if (cdata->item != NULL) {
cmdq_continue(cdata->item);
cdata->item = NULL;
}
for (i = 0; i < cdata->count; i++) {
free(cdata->prompts[i].prompt);
free(cdata->prompts[i].input);
}
free(cdata->prompts);
cmd_free_argv(cdata->argc, cdata->argv);
cmd_parse_free(cdata->tree);
args_make_commands_free(cdata->state);
free(cdata);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-confirm-before.c,v 1.61 2026/06/24 10:55:39 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
@@ -33,8 +33,8 @@ static enum args_parse_type cmd_confirm_before_args_parse(struct args *,
static enum cmd_retval cmd_confirm_before_exec(struct cmd *,
struct cmdq_item *);
static enum prompt_result cmd_confirm_before_callback(struct client *, void *,
const char *, enum prompt_key_result);
static int cmd_confirm_before_callback(struct client *, void *,
const char *, int);
static void cmd_confirm_before_free(void *);
const struct cmd_entry cmd_confirm_before_entry = {
@@ -51,7 +51,7 @@ const struct cmd_entry cmd_confirm_before_entry = {
struct cmd_confirm_before_data {
struct cmdq_item *item;
struct cmd_parse_tree *tree;
struct cmd_list *cmdlist;
u_char confirm_key;
int default_yes;
};
@@ -70,15 +70,13 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item)
struct cmd_confirm_before_data *cdata;
struct client *tc = cmdq_get_target_client(item);
struct cmd_find_state *target = cmdq_get_target(item);
char *new_prompt, *cause = NULL;
char *new_prompt;
const char *confirm_key, *prompt, *cmd;
int wait = !args_has(args, 'b'), n;
int wait = !args_has(args, 'b');
cdata = xcalloc(1, sizeof *cdata);
cdata->tree = args_to_commands(self, item, 0, NULL, 1, &cause);
if (cdata->tree == NULL) {
cmdq_error(item, "%s", cause);
free(cause);
cdata->cmdlist = args_make_commands_now(self, item, 0, 1);
if (cdata->cmdlist == NULL) {
free(cdata);
return (CMD_RETURN_ERROR);
}
@@ -94,7 +92,7 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item)
cdata->confirm_key = confirm_key[0];
else {
cmdq_error(item, "invalid confirm key");
cmd_confirm_before_free(cdata);
free(cdata);
return (CMD_RETURN_ERROR);
}
}
@@ -104,9 +102,8 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item)
if ((prompt = args_get(args, 'p')) != NULL)
xasprintf(&new_prompt, "%s ", prompt);
else {
cmd = args_string(args, 0);
n = strcspn(cmd, " ,");
xasprintf(&new_prompt, "Confirm '%.*s'? (%c/n) ", n, cmd,
cmd = cmd_get_entry(cmd_list_first(cdata->cmdlist))->name;
xasprintf(&new_prompt, "Confirm '%s'? (%c/n) ", cmd,
cdata->confirm_key);
}
@@ -120,13 +117,12 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_WAIT);
}
static enum prompt_result
static int
cmd_confirm_before_callback(struct client *c, void *data, const char *s,
__unused enum prompt_key_result key)
__unused int done)
{
struct cmd_confirm_before_data *cdata = data;
struct cmdq_item *item = cdata->item, *new_item;
struct cmdq_state *cs;
int retcode = 1;
if (c->flags & CLIENT_DEAD)
@@ -139,11 +135,11 @@ cmd_confirm_before_callback(struct client *c, void *data, const char *s,
retcode = 0;
if (item == NULL) {
new_item = cmd_invoke_get(cdata->tree, NULL, NULL);
new_item = cmdq_get_command(cdata->cmdlist, NULL);
cmdq_append(c, new_item);
} else {
cs = cmdq_get_state(item);
new_item = cmd_invoke_get(cdata->tree, cs, NULL);
new_item = cmdq_get_command(cdata->cmdlist,
cmdq_get_state(item));
cmdq_insert_after(item, new_item);
}
@@ -154,7 +150,7 @@ out:
cmdq_get_client(item)->retval = retcode;
cmdq_continue(item);
}
return (PROMPT_CLOSE);
return (0);
}
static void
@@ -162,6 +158,6 @@ cmd_confirm_before_free(void *data)
{
struct cmd_confirm_before_data *cdata = data;
cmd_parse_free(cdata->tree);
cmd_list_free(cdata->cmdlist);
free(cdata);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-copy-mode.c,v 1.54 2026/07/14 17:17:17 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -30,13 +30,13 @@ const struct cmd_entry cmd_copy_mode_entry = {
.name = "copy-mode",
.alias = NULL,
.args = { "dekHMqSs:t:u", 0, 0, NULL },
.usage = "[-dekHMqSu] [-s src-pane] " CMD_TARGET_PANE_USAGE,
.args = { "deHMqSs:t:u", 0, 0, NULL },
.usage = "[-deHMqSu] [-s src-pane] " CMD_TARGET_PANE_USAGE,
.source = { 's', CMD_FIND_PANE, 0 },
.target = { 't', CMD_FIND_PANE, 0 },
.flags = CMD_AFTERHOOK|CMD_READONLY,
.flags = CMD_AFTERHOOK,
.exec = cmd_copy_mode_exec
};
@@ -63,8 +63,6 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
struct client *c = cmdq_get_client(item);
struct session *s;
struct window_pane *wp = target->wp, *swp;
u_int tty_ox, tty_oy, tty_sx, tty_sy;
int line_numbers;
if (args_has(args, 'q')) {
window_pane_reset_mode_all(wp);
@@ -79,8 +77,7 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
}
if (cmd_get_entry(self) == &cmd_clock_mode_entry) {
window_pane_set_mode(wp, NULL, &window_clock_mode, item, NULL,
NULL);
window_pane_set_mode(wp, NULL, &window_clock_mode, NULL, NULL);
return (CMD_RETURN_NORMAL);
}
@@ -88,24 +85,17 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
swp = source->wp;
else
swp = wp;
line_numbers = 1;
if (event != NULL && KEYC_IS_MOUSE(event->key))
line_numbers = 0;
if (!window_pane_set_mode(wp, swp, &window_copy_mode, item, NULL,
args)) {
window_copy_set_line_numbers(wp, line_numbers);
if (!window_pane_set_mode(wp, swp, &window_copy_mode, NULL, args)) {
if (args_has(args, 'M'))
window_copy_start_drag(c, &event->m);
} else
window_copy_set_line_numbers(wp, line_numbers);
}
if (args_has(args, 'u'))
window_copy_pageup(wp, 0);
if (args_has(args, 'd'))
window_copy_pagedown(wp, 0, args_has(args, 'e'));
if (args_has(args, 'S')) {
tty_window_offset(&c->tty, &tty_ox, &tty_oy, &tty_sx, &tty_sy);
window_copy_scroll(wp, c->tty.mouse_slider_mpos, event->m.y,
tty_oy, args_has(args, 'e'));
args_has(args, 'e'));
return (CMD_RETURN_NORMAL);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-detach-client.c,v 1.39 2026/05/22 15:22:43 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -59,7 +59,6 @@ cmd_detach_client_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct cmd_find_state *source = cmdq_get_source(item);
struct client *c = cmdq_get_client(item);
struct client *tc = cmdq_get_target_client(item), *loop;
struct session *s;
enum msgtype msgtype;
@@ -70,13 +69,6 @@ cmd_detach_client_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
if (c->flags & CLIENT_READONLY) {
if (args_has(args, 's') || args_has(args, 'a') || c != tc) {
cmdq_error(item, "client is read-only");
return (CMD_RETURN_ERROR);
}
}
if (args_has(args, 'P'))
msgtype = MSG_DETACHKILL;
else

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-display-menu.c,v 1.53 2026/08/31 07:46:55 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -92,7 +92,7 @@ cmd_display_menu_args_parse(struct args *args, u_int idx, __unused char **cause)
}
static int
cmd_display_menu_get_popup_pos(struct client *tc, struct cmdq_item *item,
cmd_display_menu_get_pos(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;
@@ -126,10 +126,6 @@ cmd_display_menu_get_popup_pos(struct client *tc, struct cmdq_item *item,
format_add(ft, "popup_mouse_y", "%u", event->m.y);
}
/* Position of the previous menu, for -x/-y L. */
format_add(ft, "popup_last_x", "%u", target->w->menu_last_px);
format_add(ft, "popup_last_y", "%u", target->w->menu_last_py + h);
/*
* If there are any status lines, add this window position and the
* status line position.
@@ -242,8 +238,6 @@ cmd_display_menu_get_popup_pos(struct client *tc, struct cmdq_item *item,
xp = "#{popup_pane_left}";
else if (strcmp(xp, "M") == 0)
xp = "#{popup_mouse_centre_x}";
else if (strcmp(xp, "L") == 0)
xp = "#{popup_last_x}";
else if (strcmp(xp, "W") == 0)
xp = "#{popup_window_status_line_x}";
p = format_expand(ft, xp);
@@ -264,8 +258,6 @@ cmd_display_menu_get_popup_pos(struct client *tc, struct cmdq_item *item,
yp = "#{popup_pane_bottom}";
else if (strcmp(yp, "M") == 0)
yp = "#{popup_mouse_top}";
else if (strcmp(yp, "L") == 0)
yp = "#{popup_last_y}";
else if (strcmp(yp, "S") == 0)
yp = "#{popup_status_line_y}";
else if (strcmp(yp, "W") == 0)
@@ -288,184 +280,6 @@ cmd_display_menu_get_popup_pos(struct client *tc, struct cmdq_item *item,
return (1);
}
static int
cmd_display_menu_get_menu_pos(struct client *tc, struct cmdq_item *item,
struct args *args, u_int *px, u_int *py, u_int w, u_int h)
{
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 *window = target->w;
struct window_pane *wp = target->wp;
struct style_ranges *ranges = NULL;
struct style_range *sr = NULL;
const char *xp, *yp;
char *p;
u_int line, ox, oy, sx, sy, lines, position;
long n, max_x, max_y, mouse_x = 0, mouse_y = 0;
struct format_tree *ft;
max_x = window->sx > w ? window->sx - w : 0;
max_y = window->sy > h ? window->sy - h : 0;
tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy);
ft = format_create_from_target(item);
if (event->m.valid) {
mouse_x = event->m.x + ox;
if (event->m.statusat == 0) {
if (event->m.y >= event->m.statuslines)
mouse_y = event->m.y - event->m.statuslines + oy;
else
mouse_y = oy;
} else if (event->m.statusat > 0 &&
event->m.y >= (u_int)event->m.statusat) {
mouse_y = oy + sy - 1;
} else
mouse_y = event->m.y + oy;
format_add(ft, "popup_mouse_x", "%ld", mouse_x);
format_add(ft, "popup_mouse_y", "%ld", mouse_y);
}
format_add(ft, "popup_last_x", "%u", window->menu_last_px);
format_add(ft, "popup_last_y", "%u", window->menu_last_py + h);
lines = status_line_size(tc);
position = options_get_number(s->options, "status-position");
if (status_at_line(tc) != -1 && lines != 0) {
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 + ox);
if (position == 0) {
format_add(ft, "popup_window_status_line_y",
"%u", h);
} else {
format_add(ft, "popup_window_status_line_y",
"%u", window->sy);
}
}
if (position == 0)
format_add(ft, "popup_status_line_y", "%u", h);
else
format_add(ft, "popup_status_line_y", "%u", window->sy);
}
format_add(ft, "popup_width", "%u", w);
format_add(ft, "popup_height", "%u", h);
n = ((long)window->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 = ((long)window->sy - 1) / 2 + h / 2;
if (n >= window->sy)
format_add(ft, "popup_centre_y", "%ld", max_y);
else
format_add(ft, "popup_centre_y", "%ld", n);
if (event->m.valid) {
n = mouse_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 = mouse_y - h / 2;
if (n + h >= window->sy)
format_add(ft, "popup_mouse_centre_y", "%ld", max_y);
else
format_add(ft, "popup_mouse_centre_y", "%ld", n);
n = mouse_y + h;
if (n >= window->sy)
format_add(ft, "popup_mouse_top", "%u", window->sy - 1);
else
format_add(ft, "popup_mouse_top", "%ld", n);
n = mouse_y - h;
if (n < 0)
format_add(ft, "popup_mouse_bottom", "%u", 0);
else
format_add(ft, "popup_mouse_bottom", "%ld", n);
}
n = wp->yoff + h;
if (n >= window->sy)
format_add(ft, "popup_pane_top", "%ld", max_y);
else
format_add(ft, "popup_pane_top", "%ld", n);
format_add(ft, "popup_pane_bottom", "%u", wp->yoff + wp->sy);
format_add(ft, "popup_pane_left", "%u", wp->xoff);
n = (long)wp->xoff + wp->sx - w;
if (n < 0)
format_add(ft, "popup_pane_right", "%u", 0);
else
format_add(ft, "popup_pane_right", "%ld", n);
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, "L") == 0)
xp = "#{popup_last_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 < 0)
n = 0;
if (n > max_x)
n = max_x;
*px = n;
log_debug("%s: -x: %s = %s = %u (-w %u)", __func__, xp, p, *px, w);
free(p);
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, "L") == 0)
yp = "#{popup_last_y}";
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 < 0)
n = 0;
if (n > max_y)
n = max_y;
*py = n;
log_debug("%s: -y: %s = %s = %u (-h %u)", __func__, yp, p, *py, h);
free(p);
format_free(ft);
return (1);
}
static enum cmd_retval
cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
{
@@ -480,12 +294,16 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
const char *border_style = args_get(args, 'S');
const char *selected_style = args_get(args, 'H');
enum box_lines lines = BOX_LINES_DEFAULT;
char *title, *cause = NULL;
char *title, *cause;
int flags = 0, starting_choice = 0;
u_int px, py, i, count = args_count(args);
struct options *o = target->s->curw->window->options;
struct options_entry *oe;
if (tc->overlay_draw != NULL)
return (CMD_RETURN_NORMAL);
if (args_has(args, 'C')) {
if (strcmp(args_get(args, 'C'), "-") == 0)
starting_choice = -1;
@@ -494,7 +312,8 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
&cause);
if (cause != NULL) {
cmdq_error(item, "starting choice %s", cause);
goto fail;
free(cause);
return (CMD_RETURN_ERROR);
}
}
}
@@ -515,7 +334,8 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
if (count - i < 2) {
cmdq_error(item, "not enough arguments");
goto fail;
menu_free(menu);
return (CMD_RETURN_ERROR);
}
key = args_string(args, i++);
@@ -527,13 +347,17 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
}
if (menu == NULL) {
cmdq_error(item, "invalid menu arguments");
goto fail;
return (CMD_RETURN_ERROR);
}
if (menu->count == 0) {
menu_free(menu);
return (CMD_RETURN_NORMAL);
}
if (!cmd_display_menu_get_pos(tc, item, args, &px, &py, menu->width + 4,
menu->count + 2)) {
menu_free(menu);
return (CMD_RETURN_NORMAL);
}
if (menu->count == 0)
goto out;
if (!cmd_display_menu_get_menu_pos(tc, item, args, &px, &py,
menu->width + 4, menu->count + 2))
goto out;
value = args_get(args, 'b');
if (value != NULL) {
@@ -542,7 +366,8 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
&cause);
if (lines == -1) {
cmdq_error(item, "menu-border-lines %s", cause);
goto fail;
free(cause);
return (CMD_RETURN_ERROR);
}
}
@@ -552,17 +377,8 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
flags |= MENU_NOMOUSE;
if (menu_display(menu, flags, starting_choice, item, px, py, tc, lines,
style, selected_style, border_style, target, NULL, NULL) != 0)
goto out;
return (CMD_RETURN_NORMAL);
out:
menu_free(menu);
return (CMD_RETURN_NORMAL);
fail:
free(cause);
menu_free(menu);
return (CMD_RETURN_ERROR);
return (CMD_RETURN_WAIT);
}
static enum cmd_retval
@@ -577,7 +393,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
const char *style = args_get(args, 's');
const char *border_style = args_get(args, 'S');
char *cwd = NULL, *cause = NULL, **argv = NULL;
char *title = NULL;
char *title;
int modify = popup_present(tc);
int flags = -1, argc = 0;
enum box_lines lines = BOX_LINES_DEFAULT;
@@ -591,8 +407,6 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
server_client_clear_overlay(tc);
return (CMD_RETURN_NORMAL);
}
if (tc->flags & CLIENT_CONTROL)
return (CMD_RETURN_NORMAL);
if (!modify && tc->overlay_draw != NULL)
return (CMD_RETURN_NORMAL);
@@ -603,7 +417,8 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
&cause);
if (cause != NULL) {
cmdq_error(item, "height %s", cause);
goto fail;
free(cause);
return (CMD_RETURN_ERROR);
}
}
@@ -613,7 +428,8 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
&cause);
if (cause != NULL) {
cmdq_error(item, "width %s", cause);
goto fail;
free(cause);
return (CMD_RETURN_ERROR);
}
}
@@ -621,9 +437,8 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
w = tty->sx;
if (h > tty->sy)
h = tty->sy;
if (!cmd_display_menu_get_popup_pos(tc, item, args, &px, &py,
w, h))
goto out;
if (!cmd_display_menu_get_pos(tc, item, args, &px, &py, w, h))
return (CMD_RETURN_NORMAL);
value = args_get(args, 'd');
if (value != NULL)
@@ -663,7 +478,8 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
&cause);
if (cause != NULL) {
cmdq_error(item, "popup-border-lines %s", cause);
goto fail;
free(cause);
return (CMD_RETURN_ERROR);
}
}
@@ -691,29 +507,22 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
if (modify) {
popup_modify(tc, title, style, border_style, lines, flags);
goto out;
free(title);
return (CMD_RETURN_NORMAL);
}
if (popup_display(flags, lines, item, px, py, w, h, env, shellcmd, argc,
argv, cwd, title, tc, s, style, border_style, NULL, NULL) != 0)
goto out;
argv, cwd, title, tc, s, style, border_style, NULL, NULL) != 0) {
cmd_free_argv(argc, argv);
if (env != NULL)
environ_free(env);
free(cwd);
free(title);
return (CMD_RETURN_NORMAL);
}
if (env != NULL)
environ_free(env);
free(cwd);
free(title);
cmd_free_argv(argc, argv);
return (CMD_RETURN_WAIT);
out:
cmd_free_argv(argc, argv);
environ_free(env);
free(cwd);
free(title);
return (CMD_RETURN_NORMAL);
fail:
free(cause);
cmd_free_argv(argc, argv);
environ_free(env);
free(cwd);
free(title);
return (CMD_RETURN_ERROR);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-display-message.c,v 1.65 2026/02/23 08:46:57 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
@@ -131,7 +131,6 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'a')) {
format_each(ft, cmd_display_message_each, item);
format_free(ft);
return (CMD_RETURN_NORMAL);
}
@@ -156,5 +155,6 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
free(msg);
format_free(ft);
return (CMD_RETURN_NORMAL);
}

312
cmd-display-panes.c Normal file
View File

@@ -0,0 +1,312 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2009 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 <stdlib.h>
#include <string.h>
#include "tmux.h"
/*
* Display panes on a client.
*/
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:Nt:", 0, 1, cmd_display_panes_args_parse },
.usage = "[-bN] [-d duration] " CMD_TARGET_CLIENT_USAGE " [template]",
.flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG,
.exec = cmd_display_panes_exec
};
struct cmd_display_panes_data {
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)
{
struct client *c = ctx->c;
struct tty *tty = &c->tty;
struct session *s = c->session;
struct options *oo = s->options;
struct window *w = wp->window;
struct grid_cell fgc, bgc;
u_int pane, idx, px, py, i, j, xoff, yoff, sx, sy;
int colour, active_colour;
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 ||
wp->yoff + wp->sy <= ctx->oy ||
wp->yoff >= ctx->oy + ctx->sy)
return;
if (wp->xoff >= ctx->ox && wp->xoff + wp->sx <= ctx->ox + ctx->sx) {
/* All visible. */
xoff = wp->xoff - ctx->ox;
sx = wp->sx;
} else if (wp->xoff < ctx->ox &&
wp->xoff + wp->sx > ctx->ox + ctx->sx) {
/* Both left and right not visible. */
xoff = 0;
sx = ctx->sx;
} else if (wp->xoff < ctx->ox) {
/* Left not visible. */
xoff = 0;
sx = wp->sx - (ctx->ox - wp->xoff);
} else {
/* Right not visible. */
xoff = wp->xoff - ctx->ox;
sx = wp->sx - xoff;
}
if (wp->yoff >= ctx->oy && wp->yoff + wp->sy <= ctx->oy + ctx->sy) {
/* All visible. */
yoff = wp->yoff - ctx->oy;
sy = wp->sy;
} else if (wp->yoff < ctx->oy &&
wp->yoff + wp->sy > ctx->oy + ctx->sy) {
/* Both top and bottom not visible. */
yoff = 0;
sy = ctx->sy;
} else if (wp->yoff < ctx->oy) {
/* Top not visible. */
yoff = 0;
sy = wp->sy - (ctx->oy - wp->yoff);
} else {
/* Bottom not visible. */
yoff = wp->yoff - ctx->oy;
sy = wp->sy - yoff;
}
if (ctx->statustop)
yoff += ctx->statuslines;
px = sx / 2;
py = sy / 2;
if (window_pane_index(wp, &pane) != 0)
fatalx("index not found");
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_attributes(tty, &fgc, &grid_default_cell, NULL, 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;
tty_attributes(tty, &bgc, &grid_default_cell, NULL, NULL);
for (ptr = buf; *ptr != '\0'; ptr++) {
if (*ptr < '0' || *ptr > '9')
continue;
idx = *ptr - '0';
for (j = 0; j < 5; j++) {
for (i = px; i < px + 5; i++) {
tty_cursor(tty, xoff + i, yoff + py + j);
if (window_clock_table[idx][j][i - px])
tty_putc(tty, ' ');
}
}
px += 6;
}
if (sy <= 6)
goto out;
tty_attributes(tty, &fgc, &grid_default_cell, NULL, 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, __unused void *data,
struct screen_redraw_ctx *ctx)
{
struct window *w = c->session->curw->window;
struct window_pane *wp;
log_debug("%s: %s @%u", __func__, c->name, w->id);
TAILQ_FOREACH(wp, &w->panes, entry) {
if (window_pane_visible(wp))
cmd_display_panes_draw_pane(ctx, wp);
}
}
static void
cmd_display_panes_free(__unused struct client *c, void *data)
{
struct cmd_display_panes_data *cdata = data;
if (cdata->item != NULL)
cmdq_continue(cdata->item);
args_make_commands_free(cdata->state);
free(cdata);
}
static int
cmd_display_panes_key(struct client *c, void *data, struct key_event *event)
{
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;
u_int index;
key_code key;
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, index);
if (wp == NULL)
return (1);
window_unzoom(w, 1);
xasprintf(&expanded, "%%%u", wp->id);
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);
} else {
new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
cmdq_insert_after(item, new_item);
}
free(expanded);
return (1);
}
static enum cmd_retval
cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item)
{
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 (tc->overlay_draw != NULL)
return (CMD_RETURN_NORMAL);
if (args_has(args, 'd')) {
delay = args_strtonum(args, 'd', 0, UINT_MAX, &cause);
if (cause != NULL) {
cmdq_error(item, "delay %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
} else
delay = options_get_number(s->options, "display-panes-time");
cdata = xcalloc(1, sizeof *cdata);
if (wait)
cdata->item = item;
cdata->state = args_make_commands_prepare(self, item, 0,
"select-pane -t \"%%%\"", wait, 0);
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 (!wait)
return (CMD_RETURN_NORMAL);
return (CMD_RETURN_WAIT);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-find-window.c,v 1.57 2026/07/14 17:17:17 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -109,8 +109,7 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item)
args_set(new_args, 'Z', NULL, 0);
args_set(new_args, 'f', filter, 0);
window_pane_set_mode(wp, NULL, &window_tree_mode, item, target,
new_args);
window_pane_set_mode(wp, NULL, &window_tree_mode, target, new_args);
args_free(new_args);
return (CMD_RETURN_NORMAL);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-find.c,v 1.87 2026/07/17 12:42:51 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2015 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -147,18 +147,6 @@ cmd_find_session_better(struct session *s, struct session *than, int flags)
return (timercmp(&s->activity_time, &than->activity_time, >));
}
/* Can this session be usefully targeted? */
static int
cmd_find_session_valid(struct session *s)
{
if (!session_alive(s) ||
s->curw == NULL ||
s->curw->window == NULL ||
s->curw->window->active == NULL)
return (0);
return (1);
}
/* Find best session from a list, or all if list is NULL. */
static struct session *
cmd_find_best_session(struct session **slist, u_int ssize, int flags)
@@ -171,15 +159,11 @@ cmd_find_best_session(struct session **slist, u_int ssize, int flags)
s = NULL;
if (slist != NULL) {
for (i = 0; i < ssize; i++) {
if (!cmd_find_session_valid(slist[i]))
continue;
if (cmd_find_session_better(slist[i], s, flags))
s = slist[i];
}
} else {
RB_FOREACH(s_loop, sessions, &sessions) {
if (!cmd_find_session_valid(s_loop))
continue;
if (cmd_find_session_better(s_loop, s, flags))
s = s_loop;
}
@@ -884,7 +868,7 @@ cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags)
if (c->session != NULL) {
cmd_find_clear_state(fs, flags);
fs->wp = c->session->curw->window->active;
fs->wp = server_client_get_pane(c);
if (fs->wp == NULL) {
cmd_find_from_session(fs, c->session, flags);
return (0);
@@ -940,7 +924,6 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
const char *target, enum cmd_find_type type, int flags)
{
struct mouse_event *m;
struct client *c;
struct cmd_find_state current;
char *colon, *period, *copy = NULL, tmp[256];
const char *session, *window, *pane, *s;
@@ -1007,20 +990,6 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
if (target == NULL || *target == '\0')
goto current;
if (strcmp(target, "@") == 0 ||
strcmp(target, "{active}") == 0 ||
strcmp(target, "{current}") == 0) {
c = cmdq_get_client(item);
if (c == NULL || c->session == NULL) {
cmdq_error(item, "no current client");
goto error;
}
fs->wl = c->session->curw;
fs->wp = c->session->curw->window->active;
fs->w = c->session->curw->window;
goto found;
}
/* Mouse target is a plain = or {mouse}. */
if (strcmp(target, "=") == 0 || strcmp(target, "{mouse}") == 0) {
m = &cmdq_get_event(item)->m;

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-if-shell.c,v 1.87 2026/08/25 06:04:33 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
@@ -53,9 +53,8 @@ const struct cmd_entry cmd_if_shell_entry = {
};
struct cmd_if_shell_data {
struct cmd_parse_tree *cmd_if;
struct cmd_parse_tree *cmd_else;
struct args_command_state *cmd_if;
struct args_command_state *cmd_else;
struct client *client;
struct cmdq_item *item;
@@ -77,58 +76,45 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
struct cmd_find_state *target = cmdq_get_target(item);
struct cmd_if_shell_data *cdata;
struct cmdq_item *new_item;
struct cmd_parse_tree *tree;
char *shellcmd, *cause = NULL;
char *shellcmd;
struct client *tc = cmdq_get_target_client(item);
struct session *s = target->s;
u_int count = args_count(args), idx;
struct cmd_list *cmdlist;
u_int count = args_count(args);
int wait = !args_has(args, 'b');
shellcmd = format_single_from_target(item, args_string(args, 0));
if (args_has(args, 'F')) {
if (*shellcmd != '0' && *shellcmd != '\0')
idx = 1;
cmdlist = args_make_commands_now(self, item, 1, 0);
else if (count == 3)
idx = 2;
cmdlist = args_make_commands_now(self, item, 2, 0);
else {
free(shellcmd);
return (CMD_RETURN_NORMAL);
}
free(shellcmd);
tree = args_to_commands(self, item, idx, NULL, 0, &cause);
if (tree == NULL) {
cmdq_error(item, "%s", cause);
free(cause);
if (cmdlist == NULL)
return (CMD_RETURN_ERROR);
}
new_item = cmd_invoke_get(tree, cmdq_get_state(item), NULL);
cmd_parse_free(tree);
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 = args_to_commands(self, item, 1, NULL, 0, &cause);
if (cdata->cmd_if == NULL) {
cmdq_error(item, "%s", cause);
goto fail;
}
cdata->cmd_if = args_make_commands_prepare(self, item, 1, NULL, wait,
0);
if (count == 3) {
cdata->cmd_else = args_to_commands(self, item, 2, NULL, 0,
&cause);
if (cdata->cmd_else == NULL) {
cmdq_error(item, "%s", cause);
goto fail;
}
cdata->cmd_else = args_make_commands_prepare(self, item, 2,
NULL, wait, 0);
}
if (!wait)
cdata->client = tc;
else {
if (wait) {
cdata->client = cmdq_get_client(item);
cdata->item = item;
}
} else
cdata->client = tc;
if (cdata->client != NULL)
cdata->client->references++;
@@ -137,19 +123,15 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
cmd_if_shell_callback, cmd_if_shell_free, cdata, 0, -1,
-1) == NULL) {
cmdq_error(item, "failed to run command: %s", shellcmd);
goto fail;
free(shellcmd);
cmd_if_shell_free(cdata);
return (CMD_RETURN_ERROR);
}
free(shellcmd);
if (!wait)
return (CMD_RETURN_NORMAL);
return (CMD_RETURN_WAIT);
fail:
free(cause);
free(shellcmd);
cmd_if_shell_free(cdata);
return (CMD_RETURN_ERROR);
}
static void
@@ -158,22 +140,32 @@ cmd_if_shell_callback(struct job *job)
struct cmd_if_shell_data *cdata = job_get_data(job);
struct client *c = cdata->client;
struct cmdq_item *item = cdata->item, *new_item;
struct cmd_parse_tree *tree;
struct args_command_state *state;
struct cmd_list *cmdlist;
char *error;
int status;
status = job_get_status(job);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
tree = cdata->cmd_else;
state = cdata->cmd_else;
else
tree = cdata->cmd_if;
if (tree == NULL)
state = cdata->cmd_if;
if (state == NULL)
goto out;
if (item == NULL) {
new_item = cmd_invoke_get(tree, NULL, NULL);
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, 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 = cmd_invoke_get(tree, cmdq_get_state(item), NULL);
new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
cmdq_insert_after(item, new_item);
}
@@ -191,8 +183,8 @@ cmd_if_shell_free(void *data)
server_client_unref(cdata->client);
if (cdata->cmd_else != NULL)
cmd_parse_free(cdata->cmd_else);
if (cdata->cmd_if != NULL)
cmd_parse_free(cdata->cmd_if);
args_make_commands_free(cdata->cmd_else);
args_make_commands_free(cdata->cmd_if);
free(cdata);
}

View File

@@ -1,594 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2026 Nicholas Marriott <nicm@users.sourceforge.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <pwd.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
#include "tmux-parser.h"
/* One frame in the parse tree stack. */
struct cmd_invoke_frame {
struct cmd_parse_tree *tree;
struct cmd_parse_node *node;
struct cmd_parse_node *next;
struct cmd_parse_node *end;
};
/* Shared state for a parsed command tree invocation. */
struct cmd_invoke_state {
u_int references;
struct cmd_parse_tree *tree;
struct cmd_parse_tree *current_tree;
struct cmd_invoke_frame *stack;
u_int nstack;
enum cmd_retval last;
int have_last;
int argc;
char **argv;
};
static void cmd_invoke_push(struct cmd_invoke_state *,
struct cmd_parse_tree *, struct cmd_parse_node *,
struct cmd_parse_node *, struct cmd_parse_node *);
static struct cmd_parse_tree *cmd_invoke_tree(struct cmd_invoke_state *);
static struct cmd_parse_node *cmd_invoke_next(struct cmd_invoke_state *);
static void cmd_invoke_skip_sequence(struct cmd_invoke_state *);
static int cmd_invoke_expand_string(struct cmdq_item *,
struct cmd_invoke_state *, struct cmd_parse_node *,
char **);
static int cmd_invoke_assignment(struct cmdq_item *,
struct cmd_invoke_state *, struct cmd_parse_node *);
static int cmd_invoke_if(struct cmdq_item *, struct cmd_invoke_state *,
struct cmd_parse_node *);
static struct cmd *cmd_invoke_build_command(struct cmdq_item *,
struct cmd_invoke_state *, struct cmd_parse_node *);
static int cmd_invoke_read_only(struct cmdq_item *, struct cmd *);
/* Push a node's child range onto the traversal stack. */
static void
cmd_invoke_push(struct cmd_invoke_state *is, struct cmd_parse_tree *tree,
struct cmd_parse_node *node, struct cmd_parse_node *first,
struct cmd_parse_node *end)
{
u_int n = is->nstack + 1;
is->stack = xreallocarray(is->stack, n, sizeof *is->stack);
is->stack[is->nstack].tree = cmd_parse_add_ref(tree);
is->stack[is->nstack].node = node;
is->stack[is->nstack].next = first;
is->stack[is->nstack].end = end;
is->nstack = n;
}
static struct cmd_parse_tree *
cmd_invoke_tree(struct cmd_invoke_state *is)
{
if (is->current_tree != NULL)
return (is->current_tree);
return (is->tree);
}
/* Return the next node to execute from the traversal stack. */
static struct cmd_parse_node *
cmd_invoke_next(struct cmd_invoke_state *is)
{
struct cmd_invoke_frame *frame;
struct cmd_parse_node *node;
for (;;) {
if (is->nstack == 0)
return (NULL);
frame = &is->stack[is->nstack - 1];
if (frame->next != NULL && frame->next != frame->end)
break;
cmd_parse_free(frame->tree);
is->nstack--;
}
node = frame->next;
frame->next = cmd_parse_node_next(node);
is->current_tree = frame->tree;
return (node);
}
/*
* Skip the rest of the active sequence after a command failure. Failure scope
* is the active CMD_PARSE_SEQUENCE. Discard nested frames and advance only as
* far as the end of that sequence.
*/
static void
cmd_invoke_skip_sequence(struct cmd_invoke_state *is)
{
enum cmd_parse_node_type type;
u_int i;
for (i = is->nstack; i > 0; i--) {
type = cmd_parse_node_type(is->stack[i - 1].node);
if (type == CMD_PARSE_SEQUENCE) {
is->stack[i - 1].next = is->stack[i - 1].end;
while (is->nstack > i)
cmd_parse_free(is->stack[--is->nstack].tree);
is->nstack = i;
break;
}
}
is->have_last = 0;
}
/* Append a string to a dynamically allocated buffer. */
static void
cmd_invoke_append(char **buf, size_t *len, const char *s)
{
size_t slen;
if (s != NULL) {
slen = strlen(s);
*buf = xrealloc(*buf, *len + slen + 1);
memcpy(*buf + *len, s, slen + 1);
*len += slen;
}
}
/* Look up an environment variable. */
static const char *
cmd_invoke_getenv(struct cmdq_item *item, const char *name)
{
struct client *c = cmdq_get_client(item);
struct environ_entry *envent;
if (c != NULL && c->environ != NULL) {
envent = environ_find(c->environ, name);
if (envent != NULL && envent->value != NULL)
return (envent->value);
}
envent = environ_find(global_environ, name);
if (envent != NULL && envent->value != NULL)
return (envent->value);
return ("");
}
/* Resolve a tilde expansion to a home directory. */
static const char *
cmd_invoke_tilde(const char *name)
{
struct passwd *pw;
if (name == NULL || *name == '\0')
return (find_home());
if ((pw = getpwnam(name)) == NULL)
return ("");
return (pw->pw_dir);
}
/* Expand a parsed string node into an argv string. */
static int
cmd_invoke_expand_string(struct cmdq_item *item, struct cmd_invoke_state *is,
struct cmd_parse_node *node, char **out)
{
struct cmd_parse_node *child;
const char *s, *value;
char *buf = NULL, *new;
size_t len = 0;
int i;
child = cmd_parse_node_first_child(node);
while (child != NULL) {
value = cmd_parse_node_value(child);
switch (cmd_parse_node_type(child)) {
case CMD_PARSE_TEXT:
s = value;
break;
case CMD_PARSE_ENVIRONMENT:
s = cmd_invoke_getenv(item, value);
break;
case CMD_PARSE_TILDE:
s = cmd_invoke_tilde(value);
break;
default:
fatalx("unexpected node type in string");
}
cmd_invoke_append(&buf, &len, s);
child = cmd_parse_node_next(child);
}
if (buf == NULL)
buf = xstrdup("");
for (i = 0; i < is->argc; i++) {
new = cmd_template_replace(buf, is->argv[i], i + 1);
free(buf);
buf = new;
}
*out = buf;
return (0);
}
/* Execute an assignment node by updating the environment. */
static int
cmd_invoke_assignment(struct cmdq_item *item, struct cmd_invoke_state *is,
struct cmd_parse_node *node)
{
struct cmd_parse_node *value;
const char *name;
char *expanded;
int flags = 0;
value = cmd_parse_node_first_child(node);
if (value == NULL)
return (-1);
if (cmd_invoke_expand_string(item, is, value, &expanded) != 0)
return (-1);
name = cmd_parse_node_value(node);
if (cmd_parse_node_type(node) == CMD_PARSE_HIDDEN_ASSIGN)
flags |= ENVIRON_HIDDEN;
environ_set(global_environ, name, flags, "%s", expanded);
free(expanded);
return (0);
}
/* Expand and evaluate a conditional expression. */
static int
cmd_invoke_is_true(struct cmdq_item *item,
struct cmd_invoke_state *is, struct cmd_parse_node *node, int *result)
{
struct format_tree *ft;
char *s, *expanded;
if (cmd_invoke_expand_string(item, is, node, &s) != 0)
return (-1);
ft = format_create_from_target(item);
expanded = format_expand(ft, s);
*result = format_true(expanded);
free(expanded);
format_free(ft);
free(s);
return (0);
}
/* Skip else branches. */
static struct cmd_parse_node *
cmd_invoke_if_branch_end(struct cmd_parse_node *first)
{
struct cmd_parse_node *node;
for (node = first; node != NULL; node = cmd_parse_node_next(node)) {
switch (cmd_parse_node_type(node)) {
case CMD_PARSE_ELIF:
case CMD_PARSE_ELSE:
return (node);
default:
break;
}
}
return (NULL);
}
/* Select and queue the branch for a conditional node. */
static int
cmd_invoke_if(struct cmdq_item *item, struct cmd_invoke_state *is,
struct cmd_parse_node *node)
{
struct cmd_parse_tree *tree = cmd_invoke_tree(is);
struct cmd_parse_node *child, *first, *next;
int r;
next = cmd_parse_node_first_child(node);
if (next == NULL)
return (0);
first = cmd_parse_node_next(next);
if (cmd_invoke_is_true(item, is, next, &r) != 0)
return (-1);
if (r) {
next = cmd_invoke_if_branch_end(first);
cmd_invoke_push(is, tree, node, first, next);
return (0);
}
for (child = first; child != NULL; child = cmd_parse_node_next(child)) {
switch (cmd_parse_node_type(child)) {
case CMD_PARSE_ELIF:
next = cmd_parse_node_first_child(child);
if (next == NULL)
break;
if (cmd_invoke_is_true(item, is, next, &r) != 0)
return (-1);
if (r) {
next = cmd_parse_node_next(next);
cmd_invoke_push(is, tree, child, next, NULL);
return (0);
}
break;
case CMD_PARSE_ELSE:
next = cmd_parse_node_first_child(child);
cmd_invoke_push(is, tree, child, next, NULL);
return (0);
default:
break;
}
}
return (0);
}
/* Report an error from an invoke item. */
static void
cmd_invoke_error(struct cmdq_item *item, struct cmd_invoke_state *is,
struct cmd_parse_node *node, const char *cause)
{
struct client *c = cmdq_get_client(item);
struct cmd_parse_tree *tree = cmd_invoke_tree(is);
const char *file = cmd_parse_file(tree);
u_int line = cmd_parse_node_line(node);
if (c != NULL && (~c->flags & CLIENT_CONTROL || file == NULL)) {
cmdq_error(item, "%s", cause);
return;
}
if (file != NULL)
cfg_add_cause("%s:%u: %s", file, line, cause);
else
cfg_add_cause("%s", cause);
}
/* Build one command from a parsed command node. */
static struct cmd *
cmd_invoke_build_command(struct cmdq_item *item, struct cmd_invoke_state *is,
struct cmd_parse_node *node)
{
struct cmd_parse_tree *tree = cmd_invoke_tree(is);
struct cmd_parse_node *child;
struct args_value *values = NULL;
struct cmd *cmd;
const char *file = cmd_parse_file(tree);
u_int line = cmd_parse_node_line(node);
int flags = cmd_parse_flags(tree);
char *cause = NULL;
u_int count = 0;
child = cmd_parse_node_first_child(node);
while (child != NULL) {
values = xreallocarray(values, count + 1, sizeof *values);
memset(&values[count], 0, sizeof values[count]);
switch (cmd_parse_node_type(child)) {
case CMD_PARSE_STRING:
values[count].type = ARGS_STRING;
if (cmd_invoke_expand_string(item, is, child,
&values[count].string) != 0)
goto fail;
break;
case CMD_PARSE_COMMANDS:
values[count].type = ARGS_COMMANDS;
values[count].cmd = cmd_parse_from_node(tree, child);
break;
default:
fatalx("unexpected node type in command");
}
count++;
child = cmd_parse_node_next(child);
}
cmd = cmd_parse(values, count, file, line, flags, &cause);
if (cmd == NULL) {
cmd_invoke_error(item, is, node, cause);
free(cause);
goto fail;
}
args_free_values(values, count);
free(values);
return (cmd);
fail:
args_free_values(values, count);
free(values);
return (NULL);
}
static int
cmd_invoke_read_only(struct cmdq_item *item, struct cmd *cmd)
{
struct client *c = cmdq_get_client(item);
const struct cmd_entry *entry = cmd_get_entry(cmd);
if (c == NULL || (~c->flags & CLIENT_READONLY))
return (0);
if (entry->flags & CMD_READONLY)
return (0);
cmdq_error(item, "client is read-only");
return (-1);
}
/* Create the first invoke queue item for a parsed tree. */
struct cmdq_item *
cmd_invoke_get(struct cmd_parse_tree *tree, struct cmdq_state *state,
const struct cmd_invoke_input *input)
{
struct cmd_invoke_state *is;
struct cmd_parse_node *root = cmd_parse_root(tree), *first;
struct cmdq_item *item;
struct cmd_invoke_input new_input = { 0 };
int i;
if (input == NULL)
input = &new_input;
is = xcalloc(1, sizeof *is);
is->references = 1;
is->tree = cmd_parse_add_ref(tree);
is->argc = input->argc;
if (input->argc != 0) {
is->argv = xreallocarray(NULL, input->argc, sizeof *is->argv);
for (i = 0; i < input->argc; i++)
is->argv[i] = xstrdup(input->argv[i]);
}
first = cmd_parse_node_first_child(root);
cmd_invoke_push(is, tree, root, first, NULL);
item = cmdq_get_invoke(is, state);
cmd_invoke_state_free(is);
return (item);
}
/* Add a reference to shared invoke state. */
struct cmd_invoke_state *
cmd_invoke_state_add_ref(struct cmd_invoke_state *is)
{
is->references++;
return (is);
}
/* Release a reference to shared invoke state. */
void
cmd_invoke_state_free(struct cmd_invoke_state *is)
{
int i;
u_int j;
if (is == NULL)
return;
if (--is->references != 0)
return;
for (i = 0; i < is->argc; i++)
free(is->argv[i]);
free(is->argv);
for (j = 0; j < is->nstack; j++)
cmd_parse_free(is->stack[j].tree);
cmd_parse_free(is->tree);
free(is->stack);
free(is);
}
/* Record the result from the last command item. */
void
cmd_invoke_result(struct cmd_invoke_state *is, enum cmd_retval retval)
{
is->last = retval;
is->have_last = 1;
}
/* Fire an invoke item and queue the next command plus continuation. */
enum cmd_retval
cmd_invoke_fire(struct cmdq_item *item, struct cmd_invoke_state *is)
{
struct cmd_parse_node *node, *first;
struct cmd_parse_tree *tree, *alias;
struct cmdq_item *new_item, *next;
struct cmdq_state *state;
struct client *c;
struct cmd *cmd;
const char *file;
char *cause;
int r;
if (is->have_last && is->last == CMD_RETURN_ERROR)
cmd_invoke_skip_sequence(is);
else
is->have_last = 0;
for (;;) {
node = cmd_invoke_next(is);
if (node == NULL)
return (CMD_RETURN_NORMAL);
cmd_parse_log_node(__func__, node);
tree = cmd_invoke_tree(is);
switch (cmd_parse_node_type(node)) {
case CMD_PARSE_ROOT:
case CMD_PARSE_SEQUENCE:
first = cmd_parse_node_first_child(node);
cmd_invoke_push(is, tree, node, first, NULL);
break;
case CMD_PARSE_ASSIGN:
case CMD_PARSE_HIDDEN_ASSIGN:
if (cmd_invoke_assignment(item, is, node) == 0)
break;
cmd_invoke_error(item, is, node, "bad assignment");
cmd_invoke_skip_sequence(is);
break;
case CMD_PARSE_IF:
if (cmd_invoke_if(item, is, node) == 0)
break;
cmd_invoke_error(item, is, node, "bad condition");
cmd_invoke_skip_sequence(is);
break;
case CMD_PARSE_ELIF:
case CMD_PARSE_ELSE:
break;
case CMD_PARSE_COMMAND:
r = cmd_parse_expand_alias(tree, node, &alias, &cause);
if (r == 1) {
node = cmd_parse_root(alias);
first = cmd_parse_node_first_child(node);
cmd_invoke_push(is, alias, node, first, NULL);
cmd_parse_free(alias);
break;
}
if (r == -1) {
c = cmdq_get_client(item);
file = cmd_parse_file(tree);
if (c != NULL &&
(~c->flags & CLIENT_CONTROL || file == NULL))
cmdq_error(item, "%s", cause);
else
cfg_add_cause("%s", cause);
free(cause);
cmd_invoke_skip_sequence(is);
break;
}
cmd = cmd_invoke_build_command(item, is, node);
if (cmd == NULL) {
cmd_invoke_skip_sequence(is);
break;
}
if (cmd_invoke_read_only(item, cmd) != 0) {
cmd_free(cmd);
cmd_invoke_skip_sequence(is);
break;
}
/*
* Queue one command followed by this walker. WAIT and
* command-inserted items therefore run before resume.
*/
state = cmdq_get_state(item);
new_item = cmdq_get_one_command(cmd, state, is);
next = cmdq_get_invoke(is, state);
cmdq_insert_after(item, next);
cmdq_insert_after(item, new_item);
return (CMD_RETURN_NORMAL);
case CMD_PARSE_STRING:
case CMD_PARSE_COMMANDS:
case CMD_PARSE_TEXT:
case CMD_PARSE_ENVIRONMENT:
case CMD_PARSE_TILDE:
fatalx("unexpected node type");
}
}
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-join-pane.c,v 1.74 2026/08/03 20:29:52 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2011 George Nachman <tmux@georgester.com>
@@ -30,9 +30,6 @@
*/
static enum cmd_retval cmd_join_pane_exec(struct cmd *, struct cmdq_item *);
static enum cmd_retval cmd_join_pane_mouse_update(struct cmdq_item *);
static void cmd_join_pane_mouse_move(struct client *,
struct mouse_event *);
const struct cmd_entry cmd_join_pane_entry = {
.name = "join-pane",
@@ -52,10 +49,8 @@ const struct cmd_entry cmd_move_pane_entry = {
.name = "move-pane",
.alias = "movep",
.args = { "bdD::fhMvl:L::P:R::s:t:U::X:Y:z:", 0, 0, NULL },
.usage = "[-bdfhMv] [-D lines] [-l size] [-L columns] [-P position] "
"[-R columns] " CMD_SRCDST_PANE_USAGE " [-U lines] "
"[-X x-position] [-Y y-position] [-z z-index]",
.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 },
@@ -64,348 +59,6 @@ const struct cmd_entry cmd_move_pane_entry = {
.exec = cmd_join_pane_exec
};
static enum cmd_retval
cmd_join_pane_place(struct cmdq_item *item, struct winlink *wl,
struct window_pane *wp, const char *position)
{
struct window *w = wl->window;
struct layout_cell *lc = wp->layout_cell;
struct window_pane *owp;
int wx = w->sx, wy = w->sy;
int px = lc->g.sx, py = lc->g.sy;
int xoff = lc->g.xoff, yoff = lc->g.yoff;
int border = 1;
if (window_pane_get_pane_lines(wp) == PANE_LINES_NONE)
border = 0;
if (strcmp(position, "top-left") == 0) {
xoff = border;
yoff = border;
} else if (strcmp(position, "top-centre") == 0 ||
strcmp(position, "top-center") == 0) {
xoff = (wx - px) / 2;
yoff = border;
} else if (strcmp(position, "top-right") == 0) {
xoff = wx - px - border;
yoff = border;
} else if (strcmp(position, "centre-left") == 0 ||
strcmp(position, "center-left") == 0) {
xoff = border;
yoff = (wy - py) / 2;
} else if (strcmp(position, "centre") == 0 ||
strcmp(position, "center") == 0) {
xoff = (wx - px) / 2;
yoff = (wy - py) / 2;
} else if (strcmp(position, "centre-right") == 0 ||
strcmp(position, "center-right") == 0) {
xoff = wx - px - border;
yoff = (wy - py) / 2;
} else if (strcmp(position, "bottom-left") == 0) {
xoff = border;
yoff = wy - py - border;
} else if (strcmp(position, "bottom-centre") == 0 ||
strcmp(position, "bottom-center") == 0) {
xoff = (wx - px) / 2;
yoff = wy - py - border;
} else if (strcmp(position, "bottom-right") == 0) {
xoff = wx - px - border;
yoff = wy - py - border;
} else if (strcmp(position, "top-left-centre") == 0 ||
strcmp(position, "top-left-center") == 0) {
xoff = wx / 4 - px / 2;
yoff = wy / 4 - py / 2;
} else if (strcmp(position, "top-right-centre") == 0 ||
strcmp(position, "top-right-center") == 0) {
xoff = (3 * wx) / 4 - px / 2;
yoff = wy / 4 - py / 2;
} else if (strcmp(position, "bottom-left-centre") == 0 ||
strcmp(position, "bottom-left-center") == 0) {
xoff = wx / 4 - px / 2;
yoff = (3 * wy) / 4 - py / 2;
} else if (strcmp(position, "bottom-right-centre") == 0 ||
strcmp(position, "bottom-right-center") == 0) {
xoff = (3 * wx) / 4 - px / 2;
yoff = (3 * wy) / 4 - py / 2;
} else if (strcmp(position, "front") == 0) {
TAILQ_REMOVE(&w->z_index, wp, zentry);
TAILQ_INSERT_HEAD(&w->z_index, wp, zentry);
} else if (strcmp(position, "back") == 0) {
TAILQ_REMOVE(&w->z_index, wp, zentry);
TAILQ_FOREACH(owp, &w->z_index, zentry) {
if (!window_pane_is_floating(owp))
break;
}
if (owp != NULL)
TAILQ_INSERT_BEFORE(owp, wp, zentry);
else
TAILQ_INSERT_TAIL(&w->z_index, wp, zentry);
} else if (strcmp(position, "forward") == 0) {
owp = TAILQ_PREV(wp, window_panes_zindex, zentry);
if (owp != NULL) {
TAILQ_REMOVE(&w->z_index, wp, zentry);
TAILQ_INSERT_BEFORE(owp, wp, zentry);
}
} else if (strcmp(position, "backward") == 0) {
owp = TAILQ_NEXT(wp, zentry);
if (owp != NULL && window_pane_is_floating(owp)) {
TAILQ_REMOVE(&w->z_index, wp, zentry);
TAILQ_INSERT_AFTER(&w->z_index, owp, wp, zentry);
}
} else if (strcmp(position, "forward-loop") == 0) {
owp = TAILQ_PREV(wp, window_panes_zindex, zentry);
TAILQ_REMOVE(&w->z_index, wp, zentry);
if (owp != NULL)
TAILQ_INSERT_BEFORE(owp, wp, zentry);
else {
TAILQ_FOREACH(owp, &w->z_index, zentry) {
if (!window_pane_is_floating(owp))
break;
}
if (owp != NULL)
TAILQ_INSERT_BEFORE(owp, wp, zentry);
else
TAILQ_INSERT_TAIL(&w->z_index, wp, zentry);
}
} else if (strcmp(position, "backward-loop") == 0) {
owp = TAILQ_NEXT(wp, zentry);
if (owp != NULL && window_pane_is_floating(owp)) {
TAILQ_REMOVE(&w->z_index, wp, zentry);
TAILQ_INSERT_AFTER(&w->z_index, owp, wp, zentry);
} else {
TAILQ_REMOVE(&w->z_index, wp, zentry);
TAILQ_INSERT_HEAD(&w->z_index, wp, zentry);
}
} else {
cmdq_error(item, "unknown position: %s", position);
return (CMD_RETURN_ERROR);
}
if (xoff != lc->g.xoff || yoff != lc->g.yoff) {
lc->g.xoff = xoff;
lc->g.yoff = yoff;
layout_fix_panes(w, NULL);
}
redraw_invalidate_scene(w);
events_fire_window("window-layout-changed", w);
server_redraw_window(w);
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_join_pane_move(struct cmdq_item *item, struct args *args,
struct winlink *wl, struct window_pane *wp)
{
struct window *w = wl->window;
struct layout_cell *lc = wp->layout_cell;
const char *errstr, *argval;
const char flags[] = { 'U', 'D', 'L', 'R' };
char *cause = NULL, flag;
int xoff = lc->g.xoff, yoff = lc->g.yoff, adjust;
u_int i;
enum pane_lines lines = window_pane_get_pane_lines(wp);
if (args_has(args, 'X')) {
xoff = args_percentage_and_expand(args, 'X', -(int)w->sx,
w->sx, w->sx, item, &cause);
if (cause != NULL) {
cmdq_error(item, "position %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
if (lines != PANE_LINES_NONE)
xoff += 1;
}
if (args_has(args, 'Y')) {
yoff = args_percentage_and_expand(args, 'Y', -(int)w->sy,
w->sy, w->sy, item, &cause);
if (cause != NULL) {
cmdq_error(item, "position %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
if (lines != PANE_LINES_NONE)
yoff += 1;
}
for (i = 0; i < nitems(flags); i++) {
flag = flags[i];
if (!args_has(args, flag))
continue;
argval = args_get(args, flag);
if (argval == NULL)
argval = "1";
adjust = strtonum(argval, INT_MIN, INT_MAX, &errstr);
if (errstr != NULL) {
cmdq_error(item, "offset %s", errstr);
return (CMD_RETURN_ERROR);
}
if (flag == 'U')
yoff -= adjust;
else if (flag == 'D')
yoff += adjust;
else if (flag == 'L')
xoff -= adjust;
else
xoff += adjust;
}
if (xoff != lc->g.xoff || yoff != lc->g.yoff) {
lc->g.xoff = xoff;
lc->g.yoff = yoff;
layout_fix_panes(w, NULL);
events_fire_window("window-layout-changed", w);
server_redraw_window(w);
}
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_join_pane_mouse_update(struct cmdq_item *item)
{
struct cmd_find_state *target = cmdq_get_target(item);
struct key_event *event = cmdq_get_event(item);
struct client *c = cmdq_get_client(item);
struct session *s = target->s;
struct winlink *wl;
struct window *w;
struct window_pane *wp;
if (!event->m.valid)
return (CMD_RETURN_NORMAL);
wp = cmd_mouse_pane(&event->m, &s, &wl);
if (wp == NULL || c == NULL || c->session != s)
return (CMD_RETURN_NORMAL);
if (!window_pane_is_floating(wp))
return (CMD_RETURN_NORMAL);
w = wl->window;
window_redraw_active_switch(w, wp);
window_set_active_pane(w, wp, 1);
c->tty.mouse_drag_update = cmd_join_pane_mouse_move;
cmd_join_pane_mouse_move(c, &event->m);
return (CMD_RETURN_NORMAL);
}
static void
cmd_join_pane_mouse_move(struct client *c, struct mouse_event *m)
{
struct winlink *wl;
struct window *w;
struct window_pane *wp;
struct layout_cell *lc;
int y, ly, x, lx;
wp = cmd_mouse_pane(m, NULL, &wl);
if (wp == NULL) {
c->tty.mouse_drag_update = NULL;
return;
}
w = wl->window;
lc = wp->layout_cell;
y = m->y + m->oy; x = m->x + m->ox;
if (m->statusat == 0 && y >= (int)m->statuslines)
y -= m->statuslines;
else if (m->statusat > 0 && y >= m->statusat)
y = m->statusat - 1;
ly = m->ly + m->oy; lx = m->lx + m->ox;
if (m->statusat == 0 && ly >= (int)m->statuslines)
ly -= m->statuslines;
else if (m->statusat > 0 && ly >= m->statusat)
ly = m->statusat - 1;
if (x != lx || y != ly) {
lc->g.xoff += x - lx;
lc->g.yoff += y - ly;
layout_fix_panes(w, NULL);
server_redraw_window(w);
server_redraw_window_borders(w);
}
}
static enum cmd_retval
cmd_join_pane_zindex(struct cmdq_item *item, struct winlink *wl,
struct window_pane *wp, const char *s)
{
struct window *w = wl->window;
struct window_pane *owp;
const char *errstr;
u_int n, z;
z = strtonum(s, 0, UINT_MAX, &errstr);
if (errstr != NULL) {
cmdq_error(item, "z-index %s", errstr);
return (CMD_RETURN_ERROR);
}
TAILQ_REMOVE(&w->z_index, wp, zentry);
n = 0;
TAILQ_FOREACH(owp, &w->z_index, zentry) {
if (!window_pane_is_floating(owp))
break;
if (n >= z)
break;
n++;
}
if (owp != NULL)
TAILQ_INSERT_BEFORE(owp, wp, zentry);
else
TAILQ_INSERT_TAIL(&w->z_index, wp, zentry);
redraw_invalidate_scene(w);
events_fire_window("window-layout-changed", w);
server_redraw_window(w);
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_join_pane_tile(struct cmdq_item *item, struct args *args, struct window *w,
struct window_pane *wp)
{
struct layout_cell *lc = wp->layout_cell;
if (!window_pane_is_floating(wp)) {
cmdq_error(item, "pane is not floating");
return (CMD_RETURN_ERROR);
}
if (w->flags & WINDOW_ZOOMED) {
cmdq_error(item, "can't tile a pane while window is zoomed");
return (CMD_RETURN_ERROR);
}
lc->fg.sx = lc->g.sx;
lc->fg.sy = lc->g.sy;
lc->fg.xoff = lc->g.xoff;
lc->fg.yoff = lc->g.yoff;
if (layout_insert_tile(w, lc) != 0) {
cmdq_error(item, "no space for a new pane");
return (CMD_RETURN_ERROR);
}
lc->flags &= ~LAYOUT_CELL_FLOATING;
TAILQ_REMOVE(&w->z_index, wp, zentry);
TAILQ_INSERT_TAIL(&w->z_index, wp, zentry);
if (!args_has(args, 'd'))
window_set_active_pane(w, wp, 1);
layout_fix_offsets(w);
layout_fix_panes(w, NULL);
redraw_invalidate_scene(w);
events_fire_window("window-layout-changed", w);
server_redraw_window(w);
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
{
@@ -417,71 +70,74 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
struct winlink *src_wl, *dst_wl;
struct window *src_w, *dst_w;
struct window_pane *src_wp, *dst_wp;
const char *s;
char *cause = NULL;
int flags = 0, dst_idx;
int size, dst_idx;
int flags;
enum layout_type type;
struct layout_cell *lc;
u_int curval = 0;
dst_s = target->s;
dst_wl = target->wl;
dst_wp = target->wp;
dst_w = dst_wl->window;
dst_idx = dst_wl->idx;
if (cmd_get_entry(self) == &cmd_move_pane_entry) {
if (args_has(args, 'M'))
return (cmd_join_pane_mouse_update(item));
if (args_has(args, 'P') ||
args_has(args, 'z') ||
args_has(args, 'X') ||
args_has(args, 'Y') ||
args_has(args, 'U') ||
args_has(args, 'D') ||
args_has(args, 'L') ||
args_has(args, 'R')) {
if (!window_pane_is_floating(dst_wp)) {
cmdq_error(item, "pane is not floating");
return (CMD_RETURN_ERROR);
}
server_unzoom_window(dst_w);
if ((s = args_get(args, 'P')) != NULL)
return (cmd_join_pane_place(item, dst_wl, dst_wp, s));
if ((s = args_get(args, 'z')) != NULL)
return (cmd_join_pane_zindex(item, dst_wl, dst_wp, s));
return (cmd_join_pane_move(item, args, dst_wl, dst_wp));
}
}
src_wl = source->wl;
src_wp = source->wp;
src_w = src_wl->window;
if (src_wp == src_w->modal || dst_wp == dst_w->modal) {
cmdq_error(item, "pane is modal");
return (CMD_RETURN_ERROR);
}
server_unzoom_window(dst_w);
server_unzoom_window(src_w);
if (src_wp == dst_wp) {
if (window_pane_is_floating(src_wp))
return (cmd_join_pane_tile(item, args, src_w, src_wp));
cmdq_error(item, "source and target panes must be different");
return (CMD_RETURN_ERROR);
}
type = LAYOUT_TOPBOTTOM;
if (args_has(args, 'h'))
flags |= SPAWN_HORIZONTAL;
type = LAYOUT_LEFTRIGHT;
/* If the 'p' flag is dropped then this bit can be moved into 'l'. */
if (args_has(args, 'l') || args_has(args, 'p')) {
if (args_has(args, 'f')) {
if (type == LAYOUT_TOPBOTTOM)
curval = dst_w->sy;
else
curval = dst_w->sx;
} else {
if (type == LAYOUT_TOPBOTTOM)
curval = dst_wp->sy;
else
curval = dst_wp->sx;
}
}
size = -1;
if (args_has(args, 'l')) {
size = args_percentage_and_expand(args, 'l', 0, INT_MAX, curval,
item, &cause);
} else if (args_has(args, 'p')) {
size = args_strtonum_and_expand(args, 'l', 0, 100, item,
&cause);
if (cause == NULL)
size = curval * size / 100;
}
if (cause != NULL) {
cmdq_error(item, "size %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
flags = 0;
if (args_has(args, 'b'))
flags |= SPAWN_BEFORE;
if (args_has(args, 'f'))
flags |= SPAWN_FULLSIZE;
lc = layout_get_tiled_cell(item, args, dst_w, dst_wp, flags, &cause);
if (cause != NULL) {
cmdq_error(item, "size or position %s", cause);
free(cause);
lc = layout_split_pane(dst_wp, type, size, flags);
if (lc == NULL) {
cmdq_error(item, "create pane failed: pane too small");
return (CMD_RETURN_ERROR);
}
@@ -490,18 +146,14 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
server_client_remove_pane(src_wp);
window_lost_pane(src_w, src_wp);
TAILQ_REMOVE(&src_w->panes, src_wp, entry);
TAILQ_REMOVE(&src_w->z_index, src_wp, zentry);
src_wp->window = dst_w;
options_set_parent(src_wp->options, dst_w->options);
src_wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
if (flags & SPAWN_BEFORE) {
if (flags & SPAWN_BEFORE)
TAILQ_INSERT_BEFORE(dst_wp, src_wp, entry);
TAILQ_INSERT_BEFORE(dst_wp, src_wp, zentry);
} else {
else
TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry);
TAILQ_INSERT_AFTER(&dst_w->z_index, dst_wp, src_wp, zentry);
}
layout_assign_pane(lc, src_wp, 0);
colour_palette_from_option(&src_wp->palette, src_wp->options);
@@ -518,12 +170,11 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
} else
server_status_session(dst_s);
window_fire_pane_moved(src_wp, src_w, src_wl->idx, dst_w, dst_idx);
if (window_count_panes(src_w, 1) == 0)
if (window_count_panes(src_w) == 0)
server_kill_window(src_w, 1);
else
events_fire_window("window-layout-changed", src_w);
events_fire_window("window-layout-changed", dst_w);
notify_window("window-layout-changed", src_w);
notify_window("window-layout-changed", dst_w);
return (CMD_RETURN_NORMAL);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-kill-pane.c,v 1.34 2026/06/09 21:22:22 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -27,17 +27,13 @@
*/
static enum cmd_retval cmd_kill_pane_exec(struct cmd *, struct cmdq_item *);
static enum cmd_retval cmd_kill_pane_all(struct cmdq_item *, const char *);
static int cmd_kill_pane_filter(struct cmdq_item *,
struct session *, struct winlink *,
struct window_pane *, const char *);
const struct cmd_entry cmd_kill_pane_entry = {
.name = "kill-pane",
.alias = "killp",
.args = { "af:t:", 0, 0, NULL },
.usage = "[-a] [-f filter] " CMD_TARGET_PANE_USAGE,
.args = { "at:", 0, 0, NULL },
.usage = "[-a] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 },
@@ -50,40 +46,14 @@ cmd_kill_pane_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 window_pane *wp = target->wp;
const char *filter = args_get(args, 'f');
if (filter != NULL && !args_has(args, 'a')) {
cmdq_error(item, "-f only valid with -a");
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'a'))
return (cmd_kill_pane_all(item, filter));
if (wp == NULL) {
cmdq_error(item, "no active pane to kill");
return (CMD_RETURN_ERROR);
}
server_kill_pane(wp);
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_kill_pane_all(struct cmdq_item *item, const char *filter)
{
struct cmd_find_state *target = cmdq_get_target(item);
struct session *s = target->s;
struct winlink *wl = target->wl;
struct window_pane *wp = target->wp;
struct window_pane *loopwp, *tmpwp;
struct window_pane *loopwp, *tmpwp, *wp = target->wp;
if (args_has(args, 'a')) {
server_unzoom_window(wl->window);
TAILQ_FOREACH_SAFE(loopwp, &wl->window->panes, entry, tmpwp) {
if (loopwp == wp)
continue;
if (!cmd_kill_pane_filter(item, s, wl, loopwp, filter))
continue;
server_client_remove_pane(loopwp);
layout_close_pane(loopwp);
window_remove_pane(wl->window, loopwp);
@@ -92,24 +62,6 @@ cmd_kill_pane_all(struct cmdq_item *item, const char *filter)
return (CMD_RETURN_NORMAL);
}
static int
cmd_kill_pane_filter(struct cmdq_item *item, struct session *s,
struct winlink *wl, struct window_pane *wp, const char *filter)
{
struct format_tree *ft;
char *expanded;
int flag;
if (filter == NULL)
return (1);
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_defaults(ft, NULL, s, wl, wp);
expanded = format_expand(ft, filter);
flag = format_true(expanded);
free(expanded);
format_free(ft);
return (flag);
server_kill_pane(wp);
return (CMD_RETURN_NORMAL);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-kill-server.c,v 1.20 2021/08/21 10:22:39 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-kill-session.c,v 1.31 2026/06/09 12:57:40 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -18,8 +18,6 @@
#include <sys/types.h>
#include <stdlib.h>
#include "tmux.h"
/*
@@ -30,16 +28,13 @@
*/
static enum cmd_retval cmd_kill_session_exec(struct cmd *, struct cmdq_item *);
static enum cmd_retval cmd_kill_session_all(struct cmdq_item *, const char *);
static int cmd_kill_session_filter(struct cmdq_item *,
struct session *, const char *);
const struct cmd_entry cmd_kill_session_entry = {
.name = "kill-session",
.alias = NULL,
.args = { "aCgf:t:", 0, 0, NULL },
.usage = "[-aCg] [-f filter] " CMD_TARGET_SESSION_USAGE,
.args = { "aCt:", 0, 0, NULL },
.usage = "[-aC] " CMD_TARGET_SESSION_USAGE,
.target = { 't', CMD_FIND_SESSION, 0 },
@@ -53,14 +48,7 @@ cmd_kill_session_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, *sloop, *stmp;
struct session_group *sg;
struct winlink *wl;
const char *filter = args_get(args, 'f');
if (filter != NULL && (!args_has(args, 'a') || args_has(args, 'C'))) {
cmdq_error(item, "-f only valid with -a");
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'C')) {
RB_FOREACH(wl, winlinks, &s->windows) {
@@ -68,56 +56,16 @@ cmd_kill_session_exec(struct cmd *self, struct cmdq_item *item)
wl->flags &= ~WINLINK_ALERTFLAGS;
}
server_redraw_session(s);
} else if (args_has(args, 'a'))
return (cmd_kill_session_all(item, filter));
else if (args_has(args, 'g') &&
(sg = session_group_contains(s)) != NULL) {
TAILQ_FOREACH_SAFE(sloop, &sg->sessions, gentry, stmp) {
} else if (args_has(args, 'a')) {
RB_FOREACH_SAFE(sloop, sessions, &sessions, stmp) {
if (sloop != s) {
server_destroy_session(sloop);
session_destroy(sloop, 1, __func__);
}
}
} else {
server_destroy_session(s);
session_destroy(s, 1, __func__);
}
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_kill_session_all(struct cmdq_item *item, const char *filter)
{
struct session *s = cmdq_get_target(item)->s;
struct session *sloop, *stmp;
RB_FOREACH_SAFE(sloop, sessions, &sessions, stmp) {
if (sloop == s)
continue;
if (!cmd_kill_session_filter(item, sloop, filter))
continue;
server_destroy_session(sloop);
session_destroy(sloop, 1, __func__);
}
return (CMD_RETURN_NORMAL);
}
static int
cmd_kill_session_filter(struct cmdq_item *item, struct session *s,
const char *filter)
{
struct format_tree *ft;
char *expanded;
int flag;
if (filter == NULL)
return (1);
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_defaults(ft, NULL, s, NULL, NULL);
expanded = format_expand(ft, filter);
flag = format_true(expanded);
free(expanded);
format_free(ft);
return (flag);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-kill-window.c,v 1.30 2026/06/09 12:57:40 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -18,8 +18,6 @@
#include <sys/types.h>
#include <stdlib.h>
#include "tmux.h"
/*
@@ -27,16 +25,13 @@
*/
static enum cmd_retval cmd_kill_window_exec(struct cmd *, struct cmdq_item *);
static enum cmd_retval cmd_kill_window_all(struct cmdq_item *, const char *);
static int cmd_kill_window_filter(struct cmdq_item *,
struct session *, struct winlink *, const char *);
const struct cmd_entry cmd_kill_window_entry = {
.name = "kill-window",
.alias = "killw",
.args = { "af:t:", 0, 0, NULL },
.usage = "[-a] [-f filter] " CMD_TARGET_WINDOW_USAGE,
.args = { "at:", 0, 0, NULL },
.usage = "[-a] " CMD_TARGET_WINDOW_USAGE,
.target = { 't', CMD_FIND_WINDOW, 0 },
@@ -62,15 +57,10 @@ cmd_kill_window_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 winlink *wl = target->wl;
struct winlink *wl = target->wl, *loop;
struct window *w = wl->window;
struct session *s = target->s;
const char *filter = args_get(args, 'f');
if (filter != NULL && !args_has(args, 'a')) {
cmdq_error(item, "-f only valid with -a");
return (CMD_RETURN_ERROR);
}
u_int found;
if (cmd_get_entry(self) == &cmd_unlink_window_entry) {
if (!args_has(args, 'k') && !session_is_linked(s, w)) {
@@ -82,22 +72,7 @@ cmd_kill_window_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'a'))
return (cmd_kill_window_all(item, filter));
server_kill_window(wl->window, 1);
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_kill_window_all(struct cmdq_item *item, const char *filter)
{
struct cmd_find_state *target = cmdq_get_target(item);
struct session *s = target->s;
struct winlink *wl = target->wl;
struct winlink *loop;
u_int found, kill_current;
if (args_has(args, 'a')) {
if (RB_PREV(winlinks, &s->windows, wl) == NULL &&
RB_NEXT(winlinks, &s->windows, wl) == NULL)
return (CMD_RETURN_NORMAL);
@@ -106,8 +81,7 @@ cmd_kill_window_all(struct cmdq_item *item, const char *filter)
do {
found = 0;
RB_FOREACH(loop, winlinks, &s->windows) {
if (loop->window != wl->window &&
cmd_kill_window_filter(item, s, loop, filter)) {
if (loop->window != wl->window) {
server_kill_window(loop->window, 0);
found++;
break;
@@ -116,42 +90,21 @@ cmd_kill_window_all(struct cmdq_item *item, const char *filter)
} while (found != 0);
/*
* If the current window appears in the session more than once, kill it
* as well if it matches the filter.
* If the current window appears in the session more than once,
* kill it as well.
*/
found = kill_current = 0;
found = 0;
RB_FOREACH(loop, winlinks, &s->windows) {
if (loop->window == wl->window) {
if (loop->window == wl->window)
found++;
if (cmd_kill_window_filter(item, s, loop, filter))
kill_current = 1;
}
}
if (kill_current && found > 1)
if (found > 1)
server_kill_window(wl->window, 0);
server_renumber_all();
return (CMD_RETURN_NORMAL);
}
static int
cmd_kill_window_filter(struct cmdq_item *item, struct session *s,
struct winlink *wl, const char *filter)
{
struct format_tree *ft;
char *expanded;
int flag;
if (filter == NULL)
return (1);
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_defaults(ft, NULL, s, wl, NULL);
expanded = format_expand(ft, filter);
flag = format_true(expanded);
free(expanded);
format_free(ft);
return (flag);
server_kill_window(wl->window, 1);
return (CMD_RETURN_NORMAL);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-list-buffers.c,v 1.41 2026/02/27 08:25:12 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -36,8 +36,8 @@ const struct cmd_entry cmd_list_buffers_entry = {
.name = "list-buffers",
.alias = "lsb",
.args = { "F:f:O:r", 0, 0, NULL },
.usage = "[-F format] [-f filter] [-O order]",
.args = { "F:f:", 0, 0, NULL },
.usage = "[-F format] [-f filter]",
.flags = CMD_AFTERHOOK,
.exec = cmd_list_buffers_exec
@@ -47,29 +47,20 @@ static enum cmd_retval
cmd_list_buffers_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct paste_buffer **l;
struct paste_buffer *pb;
struct format_tree *ft;
const char *template, *filter;
char *line, *expanded;
int flag;
u_int i, n;
struct sort_criteria sort_crit;
if ((template = args_get(args, 'F')) == NULL)
template = LIST_BUFFERS_TEMPLATE;
filter = args_get(args, 'f');
sort_crit.order = sort_order_from_string(args_get(args, 'O'));
if (sort_crit.order == SORT_END && args_has(args, 'O')) {
cmdq_error(item, "invalid sort order");
return (CMD_RETURN_ERROR);
}
sort_crit.reversed = args_has(args, 'r');
l = sort_get_buffers(&n, &sort_crit);
for (i = 0; i < n; i++) {
pb = NULL;
while ((pb = paste_walk(pb)) != NULL) {
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_defaults_paste_buffer(ft, l[i]);
format_defaults_paste_buffer(ft, pb);
if (filter != NULL) {
expanded = format_expand(ft, filter);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-list-clients.c,v 1.42 2026/02/27 08:25:12 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -41,8 +41,8 @@ const struct cmd_entry cmd_list_clients_entry = {
.name = "list-clients",
.alias = "lsc",
.args = { "F:f:O:rt:", 0, 0, NULL },
.usage = "[-F format] [-f filter] [-O order]" CMD_TARGET_SESSION_USAGE,
.args = { "F:f:t:", 0, 0, NULL },
.usage = "[-F format] [-f filter] " CMD_TARGET_SESSION_USAGE,
.target = { 't', CMD_FIND_SESSION, 0 },
@@ -55,14 +55,13 @@ cmd_list_clients_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 client **l;
struct client *c;
struct session *s;
struct format_tree *ft;
const char *template, *filter;
u_int i, n;
u_int idx;
char *line, *expanded;
int flag;
struct sort_criteria sort_crit;
if (args_has(args, 't'))
s = target->s;
@@ -73,21 +72,14 @@ cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item)
template = LIST_CLIENTS_TEMPLATE;
filter = args_get(args, 'f');
sort_crit.order = sort_order_from_string(args_get(args, 'O'));
if (sort_crit.order == SORT_END && args_has(args, 'O')) {
cmdq_error(item, "invalid sort order");
return (CMD_RETURN_ERROR);
}
sort_crit.reversed = args_has(args, 'r');
l = sort_get_clients(&n, &sort_crit);
for (i = 0; i < n; i++) {
if (l[i]->session == NULL || (s != NULL && s != l[i]->session))
idx = 0;
TAILQ_FOREACH(c, &clients, entry) {
if (c->session == NULL || (s != NULL && s != c->session))
continue;
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_add(ft, "line", "%u", i);
format_defaults(ft, l[i], NULL, NULL, NULL);
format_add(ft, "line", "%u", idx);
format_defaults(ft, c, NULL, NULL, NULL);
if (filter != NULL) {
expanded = format_expand(ft, filter);
@@ -102,6 +94,8 @@ cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item)
}
format_free(ft);
idx++;
}
return (CMD_RETURN_NORMAL);

View File

@@ -1,107 +0,0 @@
/* $OpenBSD: cmd-list-commands.c,v 1.15 2026/02/24 08:22:13 nicm Exp $ */
/*
* Copyright (c) 2007 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 <stdlib.h>
#include <string.h>
#include "tmux.h"
/*
* List all commands.
*/
#define LIST_COMMANDS_TEMPLATE \
"#{command_list_name}" \
"#{?command_list_alias, (#{command_list_alias}),} " \
"#{command_list_usage}"
static enum cmd_retval cmd_list_commands(struct cmd *, struct cmdq_item *);
const struct cmd_entry cmd_list_commands_entry = {
.name = "list-commands",
.alias = "lscm",
.args = { "F:", 0, 1, NULL },
.usage = "[-F format] [command]",
.flags = CMD_STARTSERVER|CMD_AFTERHOOK,
.exec = cmd_list_commands
};
static void
cmd_list_single_command(const struct cmd_entry *entry, struct format_tree *ft,
const char *template, struct cmdq_item *item)
{
const char *s;
char *line;
format_add(ft, "command_list_name", "%s", entry->name);
if (entry->alias != NULL)
s = entry->alias;
else
s = "";
format_add(ft, "command_list_alias", "%s", s);
if (entry->usage != NULL)
s = entry->usage;
else
s = "";
format_add(ft, "command_list_usage", "%s", s);
line = format_expand(ft, template);
if (*line != '\0')
cmdq_print(item, "%s", line);
free(line);
}
static enum cmd_retval
cmd_list_commands(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
const struct cmd_entry **entryp;
const struct cmd_entry *entry;
struct format_tree *ft;
const char *template, *command;
char *cause;
if ((template = args_get(args, 'F')) == NULL)
template = LIST_COMMANDS_TEMPLATE;
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_defaults(ft, NULL, NULL, NULL, NULL);
command = args_string(args, 0);
if (command == NULL) {
for (entryp = cmd_table; *entryp != NULL; entryp++)
cmd_list_single_command(*entryp, ft, template, item);
} else {
entry = cmd_find(command, &cause);
if (entry != NULL)
cmd_list_single_command(entry, ft, template, item);
else {
cmdq_error(item, "%s", cause);
free(cause);
format_free(ft);
return (CMD_RETURN_ERROR);
}
}
format_free(ft);
return (CMD_RETURN_NORMAL);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-list-keys.c,v 1.78 2026/07/01 13:12:17 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -27,149 +27,121 @@
* List key bindings.
*/
#define LIST_KEYS_TEMPLATE \
"#{?notes_only," \
"#{key_prefix} " \
"#{p|#{key_string_width}:key_string} " \
"#{?key_note,#{key_note},#{key_command}}" \
"," \
"bind-key #{?key_has_repeat,#{?key_repeat,-r, },} " \
"-T #{p|#{key_table_width}:key_table} " \
"#{p|#{key_string_width}:#{q|a:key_string}} " \
"#{key_command}}"
#define LIST_KEYS_TEMPLATE_PRETTY \
"#{?notes_only," \
"#{key_prefix} " \
"#{key_string} " \
"#{?key_note,#{key_note},#{key_command}}" \
"," \
"bind-key #{?key_repeat,-r ,}" \
"-T #{key_table} " \
"#{q|a:key_string} " \
"#{key_command}}"
static enum cmd_retval cmd_list_keys_exec(struct cmd *, struct cmdq_item *);
static enum cmd_retval cmd_list_keys_commands(struct cmd *,
struct cmdq_item *);
const struct cmd_entry cmd_list_keys_entry = {
.name = "list-keys",
.alias = "lsk",
.args = { "1aF:NO:P:prT:", 0, 1, NULL },
.usage = "[-1aNpr] [-F format] [-O order] [-P prefix-string]"
"[-T key-table] [key]",
.args = { "1aNP:T:", 0, 1, NULL },
.usage = "[-1aN] [-P prefix-string] [-T key-table] [key]",
.flags = CMD_STARTSERVER|CMD_AFTERHOOK,
.exec = cmd_list_keys_exec
};
static char *
cmd_list_keys_get_prefix(struct args *args)
{
key_code prefix;
const struct cmd_entry cmd_list_commands_entry = {
.name = "list-commands",
.alias = "lscm",
if (args_has(args, 'P'))
return (xstrdup(args_get(args, 'P')));
.args = { "F:", 0, 1, NULL },
.usage = "[-F format] [command]",
prefix = options_get_number(global_s_options, "prefix");
if (prefix == KEYC_NONE)
return (xstrdup(""));
return (xstrdup(key_string_lookup_key(prefix, 0)));
}
.flags = CMD_STARTSERVER|CMD_AFTERHOOK,
.exec = cmd_list_keys_exec
};
static u_int
cmd_list_keys_get_width(struct key_binding **l, u_int n)
cmd_list_keys_get_width(const char *tablename, key_code only)
{
u_int i, width, keywidth = 0;
struct key_table *table;
struct key_binding *bd;
u_int width, keywidth = 0;
for (i = 0; i < n; i++) {
width = utf8_cstrwidth(key_string_lookup_key(l[i]->key, 0));
table = key_bindings_get_table(tablename, 0);
if (table == NULL)
return (0);
bd = key_bindings_first(table);
while (bd != NULL) {
if ((only != KEYC_UNKNOWN && bd->key != only) ||
KEYC_IS_MOUSE(bd->key) ||
bd->note == NULL ||
*bd->note == '\0') {
bd = key_bindings_next(table, bd);
continue;
}
width = utf8_cstrwidth(key_string_lookup_key(bd->key, 0));
if (width > keywidth)
keywidth = width;
bd = key_bindings_next(table, bd);
}
return (keywidth);
}
static u_int
cmd_list_keys_get_table_width(struct key_binding **l, u_int n)
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)
{
u_int i, width, tablewidth = 0;
struct client *tc = cmdq_get_target_client(item);
struct key_table *table;
struct key_binding *bd;
const char *key;
char *tmp, *note;
int found = 0;
for (i = 0; i < n; i++) {
width = utf8_cstrwidth(l[i]->tablename);
if (width > tablewidth)
tablewidth = width;
}
return (tablewidth);
}
static struct key_binding **
cmd_list_keys_get_root_and_prefix(u_int *n, struct sort_criteria *sort_crit)
{
const char *tables[] = { "prefix", "root" };
struct key_table *t;
struct key_binding **lt;
u_int i, ltsz, len = 0, offset = 0;
static struct key_binding **l = NULL;
static u_int lsz = 0;
for (i = 0; i < nitems(tables); i++) {
t = key_bindings_get_table(tables[i], 0);
lt = sort_get_key_bindings_table(t, &ltsz, sort_crit);
len += ltsz;
if (lsz <= len) {
lsz = len + 100;
l = xreallocarray(l, lsz, sizeof *l);
}
memcpy(l + offset, lt, ltsz * sizeof *l);
offset += ltsz;
}
*n = len;
return (l);
}
static void
cmd_list_keys_do_filter(int filter_notes, int filter_key, key_code only,
struct key_binding **l, u_int *n)
{
key_code key;
u_int i, j = 0;
for (i = 0; i < *n; i++) {
key = l[i]->key & (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS);
if (filter_key && only != key)
table = key_bindings_get_table(tablename, 0);
if (table == NULL)
return (0);
bd = key_bindings_first(table);
while (bd != NULL) {
if ((only != KEYC_UNKNOWN && bd->key != only) ||
KEYC_IS_MOUSE(bd->key) ||
((bd->note == NULL || *bd->note == '\0') &&
!args_has(args, 'a'))) {
bd = key_bindings_next(table, bd);
continue;
if (filter_notes && l[i]->note == NULL)
continue;
l[j++] = l[i];
}
*n = j;
found = 1;
key = key_string_lookup_key(bd->key, 0);
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') && tc != NULL) {
status_message_set(tc, -1, 1, 0, 0, "%s%s%s", prefix,
tmp, note);
} else
cmdq_print(item, "%s%s%s", prefix, tmp, note);
free(tmp);
free(note);
if (args_has(args, '1'))
break;
bd = key_bindings_next(table, bd);
}
return (found);
}
static void
cmd_list_keys_format_key_binding(struct format_tree *ft,
const struct key_binding *bd, const char *prefix, int flags)
static char *
cmd_list_keys_get_prefix(struct args *args, key_code *prefix)
{
char *s;
if (bd->flags & KEY_BINDING_REPEAT)
format_add(ft, "key_repeat", "1");
*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, 0));
else
format_add(ft, "key_repeat", "0");
if (bd->note != NULL)
format_add(ft, "key_note", "%s", bd->note);
else
format_add(ft, "key_note", "%s", "");
format_add(ft, "key_prefix", "%s", prefix);
format_add(ft, "key_table", "%s", bd->tablename);
format_add(ft, "key_string", "%s", key_string_lookup_key(bd->key, 0));
s = cmd_parse_print(bd->cmd, flags);
format_add(ft, "key_command", "%s", s);
free(s);
s = xstrdup("");
} else
s = xstrdup(args_get(args, 'P'));
return (s);
}
static enum cmd_retval
@@ -177,17 +149,16 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct client *tc = cmdq_get_target_client(item);
struct format_tree *ft;
struct key_table *table = NULL;
struct key_binding **l;
key_code only = KEYC_UNKNOWN;
const char *template, *tablename, *keystr;
char *line;
char *prefix = NULL;
u_int i, n;
int single, notes_only, filter_notes, filter_key;
int print_flags = 0;
struct sort_criteria sort_crit;
struct key_table *table;
struct key_binding *bd;
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 (cmd_get_entry(self) == &cmd_list_commands_entry)
return (cmd_list_keys_commands(self, item));
if ((keystr = args_string(args, 0)) != NULL) {
only = key_string_lookup_string(keystr);
@@ -198,75 +169,218 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
only &= (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS);
}
sort_crit.order = sort_order_from_string(args_get(args, 'O'));
if (sort_crit.order == SORT_END && args_has(args, 'O')) {
cmdq_error(item, "invalid sort order");
return (CMD_RETURN_ERROR);
}
sort_crit.reversed = args_has(args, 'r');
if ((tablename = args_get(args, 'T')) != NULL) {
table = key_bindings_get_table(tablename, 0);
if (table == NULL) {
tablename = args_get(args, 'T');
if (tablename != NULL && key_bindings_get_table(tablename, 0) == NULL) {
cmdq_error(item, "table %s doesn't exist", tablename);
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'N')) {
if (tablename == NULL) {
start = cmd_list_keys_get_prefix(args, &prefix);
keywidth = cmd_list_keys_get_width("root", only);
if (prefix != KEYC_NONE) {
width = cmd_list_keys_get_width("prefix", only);
if (width == 0)
prefix = KEYC_NONE;
else if (width > keywidth)
keywidth = width;
}
empty = utf8_padcstr("", utf8_cstrwidth(start));
found = cmd_list_keys_print_notes(item, args, "root",
keywidth, only, empty);
if (prefix != KEYC_NONE) {
if (cmd_list_keys_print_notes(item, args,
"prefix", keywidth, only, start))
found = 1;
}
free(empty);
} else {
if (args_has(args, 'P'))
start = xstrdup(args_get(args, 'P'));
else
start = xstrdup("");
keywidth = cmd_list_keys_get_width(tablename, only);
found = cmd_list_keys_print_notes(item, args, tablename,
keywidth, only, start);
}
free(start);
goto out;
}
prefix = cmd_list_keys_get_prefix(args);
single = args_has(args, '1');
notes_only = args_has(args, 'N');
if (args_has(args, 'p'))
print_flags |= CMD_PARSE_PRINT_MULTILINE;
repeat = 0;
tablewidth = keywidth = 0;
table = key_bindings_first_table();
while (table != NULL) {
if (tablename != NULL && strcmp(table->name, tablename) != 0) {
table = key_bindings_next_table(table);
continue;
}
bd = key_bindings_first(table);
while (bd != NULL) {
if (only != KEYC_UNKNOWN && bd->key != only) {
bd = key_bindings_next(table, bd);
continue;
}
key = args_escape(key_string_lookup_key(bd->key, 0));
if ((template = args_get(args, 'F')) == NULL) {
if (print_flags & CMD_PARSE_PRINT_MULTILINE)
template = LIST_KEYS_TEMPLATE_PRETTY;
else
template = LIST_KEYS_TEMPLATE;
if (bd->flags & KEY_BINDING_REPEAT)
repeat = 1;
width = utf8_cstrwidth(table->name);
if (width > tablewidth)
tablewidth = width;
width = utf8_cstrwidth(key);
if (width > keywidth)
keywidth = width;
free(key);
bd = key_bindings_next(table, bd);
}
table = key_bindings_next_table(table);
}
if (table)
l = sort_get_key_bindings_table(table, &n, &sort_crit);
else if (notes_only)
l = cmd_list_keys_get_root_and_prefix(&n, &sort_crit);
else
l = sort_get_key_bindings(&n, &sort_crit);
tmpsize = 256;
tmp = xmalloc(tmpsize);
filter_notes = notes_only && !args_has(args, 'a');
filter_key = only != KEYC_UNKNOWN;
if (filter_notes || filter_key)
cmd_list_keys_do_filter(filter_notes, filter_key, only, l, &n);
if (filter_key && n == 0) {
cmdq_error(item, "unknown key: %s", keystr);
free(prefix);
table = key_bindings_first_table();
while (table != NULL) {
if (tablename != NULL && strcmp(table->name, tablename) != 0) {
table = key_bindings_next_table(table);
continue;
}
bd = key_bindings_first(table);
while (bd != NULL) {
if (only != KEYC_UNKNOWN && bd->key != only) {
bd = key_bindings_next(table, bd);
continue;
}
found = 1;
key = args_escape(key_string_lookup_key(bd->key, 0));
if (!repeat)
r = "";
else if (bd->flags & KEY_BINDING_REPEAT)
r = "-r ";
else
r = " ";
tmpused = xsnprintf(tmp, tmpsize, "%s-T ", r);
cp = utf8_padcstr(table->name, tablewidth);
cplen = strlen(cp) + 1;
while (tmpused + cplen + 1 >= tmpsize) {
tmpsize *= 2;
tmp = xrealloc(tmp, tmpsize);
}
strlcat(tmp, cp, tmpsize);
tmpused = strlcat(tmp, " ", tmpsize);
free(cp);
cp = utf8_padcstr(key, keywidth);
cplen = strlen(cp) + 1;
while (tmpused + cplen + 1 >= tmpsize) {
tmpsize *= 2;
tmp = xrealloc(tmp, tmpsize);
}
strlcat(tmp, cp, tmpsize);
tmpused = strlcat(tmp, " ", tmpsize);
free(cp);
cp = cmd_list_print(bd->cmdlist, 1);
cplen = strlen(cp);
while (tmpused + cplen + 1 >= tmpsize) {
tmpsize *= 2;
tmp = xrealloc(tmp, tmpsize);
}
strlcat(tmp, cp, tmpsize);
free(cp);
if (args_has(args, '1') && tc != NULL) {
status_message_set(tc, -1, 1, 0, 0,
"bind-key %s", tmp);
} else
cmdq_print(item, "bind-key %s", tmp);
free(key);
if (args_has(args, '1'))
break;
bd = key_bindings_next(table, bd);
}
table = key_bindings_next_table(table);
}
free(tmp);
out:
if (only != KEYC_UNKNOWN && !found) {
cmdq_error(item, "unknown key: %s", args_string(args, 0));
return (CMD_RETURN_ERROR);
}
if (single && n > 1)
n = 1;
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_defaults(ft, tc, NULL, NULL, NULL);
format_add(ft, "notes_only", "%d", notes_only);
format_add(ft, "key_has_repeat", "%d", key_bindings_has_repeat(l, n));
format_add(ft, "key_string_width", "%u", cmd_list_keys_get_width(l, n));
format_add(ft, "key_table_width", "%u",
cmd_list_keys_get_table_width(l, n));
for (i = 0; i < n; i++) {
cmd_list_keys_format_key_binding(ft, l[i], prefix, print_flags);
line = format_expand(ft, template);
if (single && tc != NULL && (~tc->flags & CLIENT_CONTROL))
status_message_set(tc, -1, 1, 0, 0, "%s", line);
else if (*line != '\0')
cmdq_print(item, "%s", line);
free(line);
if (single)
break;
}
format_free(ft);
free(prefix);
return (CMD_RETURN_NORMAL);
}
static void
cmd_list_single_command(const struct cmd_entry *entry, struct format_tree *ft,
const char *template, struct cmdq_item *item)
{
const char *s;
char *line;
format_add(ft, "command_list_name", "%s", entry->name);
if (entry->alias != NULL)
s = entry->alias;
else
s = "";
format_add(ft, "command_list_alias", "%s", s);
if (entry->usage != NULL)
s = entry->usage;
else
s = "";
format_add(ft, "command_list_usage", "%s", s);
line = format_expand(ft, template);
if (*line != '\0')
cmdq_print(item, "%s", line);
free(line);
}
static enum cmd_retval
cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
const struct cmd_entry **entryp;
const struct cmd_entry *entry;
struct format_tree *ft;
const char *template, *command;
char *cause;
if ((template = args_get(args, 'F')) == NULL) {
template = "#{command_list_name}"
"#{?command_list_alias, (#{command_list_alias}),} "
"#{command_list_usage}";
}
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_defaults(ft, NULL, NULL, NULL, NULL);
command = args_string(args, 0);
if (command == NULL) {
for (entryp = cmd_table; *entryp != NULL; entryp++)
cmd_list_single_command(*entryp, ft, template, item);
} else {
entry = cmd_find(command, &cause);
if (entry != NULL)
cmd_list_single_command(entry, ft, template, item);
else {
cmdq_error(item, "%s", cause);
free(cause);
format_free(ft);
return (CMD_RETURN_ERROR);
}
}
format_free(ft);
return (CMD_RETURN_NORMAL);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-list-panes.c,v 1.40 2026/06/01 14:01:09 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -38,9 +38,8 @@ const struct cmd_entry cmd_list_panes_entry = {
.name = "list-panes",
.alias = "lsp",
.args = { "aF:f:O:rst:", 0, 0, NULL },
.usage = "[-asr] [-F format] [-f filter] [-O order]"
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 },
@@ -55,13 +54,6 @@ cmd_list_panes_exec(struct cmd *self, struct cmdq_item *item)
struct cmd_find_state *target = cmdq_get_target(item);
struct session *s = target->s;
struct winlink *wl = target->wl;
enum sort_order order;
order = sort_order_from_string(args_get(args, 'O'));
if (order == SORT_END && args_has(args, 'O')) {
cmdq_error(item, "invalid sort order");
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'a'))
cmd_list_panes_server(self, item);
@@ -97,41 +89,34 @@ cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl,
struct cmdq_item *item, int type)
{
struct args *args = cmd_get_args(self);
struct window_pane *wp, **l;
u_int i, n;
struct window_pane *wp;
u_int n;
struct format_tree *ft;
const char *template, *filter;
char *line, *expanded;
int flag;
struct sort_criteria sort_crit;
template = args_get(args, 'F');
if (template == NULL) {
switch (type) {
case 0:
template = "#{pane_index}: "
"[#{pane_width}x#{pane_height}"
"#{?pane_floating_flag, "
"#{pane_x}#,#{pane_y}#,#{pane_z}}] [history "
"[#{pane_width}x#{pane_height}] [history "
"#{history_size}/#{history_limit}, "
"#{history_bytes} bytes] #{pane_id}"
"#{?pane_active, (active),}#{?pane_dead, (dead),}";
break;
case 1:
template = "#{window_index}.#{pane_index}: "
"[#{pane_width}x#{pane_height}"
"#{?pane_floating_flag, "
"#{pane_x}#,#{pane_y}#,#{pane_z}}] [history "
"[#{pane_width}x#{pane_height}] [history "
"#{history_size}/#{history_limit}, "
"#{history_bytes} bytes] #{pane_id}"
"#{?pane_active, (active),}#{?pane_dead, (dead),}";
break;
case 2:
template = "#{session_name}:#{window_index}."
"#{pane_index}: [#{pane_width}x#{pane_height}"
"#{?pane_floating_flag, "
"#{pane_x}#,#{pane_y}#,#{pane_z}}] [history "
"#{history_size}/#{history_limit}, "
"#{pane_index}: [#{pane_width}x#{pane_height}] "
"[history #{history_size}/#{history_limit}, "
"#{history_bytes} bytes] #{pane_id}"
"#{?pane_active, (active),}#{?pane_dead, (dead),}";
break;
@@ -139,12 +124,8 @@ cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl,
}
filter = args_get(args, 'f');
sort_crit.order = sort_order_from_string(args_get(args, 'O'));
sort_crit.reversed = args_has(args, 'r');
l = sort_get_panes_window(wl->window, &n, &sort_crit);
for (i = 0; i < n; i++) {
wp = l[i];
n = 0;
TAILQ_FOREACH(wp, &wl->window->panes, entry) {
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_add(ft, "line", "%u", n);
format_defaults(ft, NULL, s, wl, wp);
@@ -162,5 +143,6 @@ cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl,
}
format_free(ft);
n++;
}
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-list-sessions.c,v 1.36 2026/02/27 08:25:12 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -42,8 +42,8 @@ const struct cmd_entry cmd_list_sessions_entry = {
.name = "list-sessions",
.alias = "ls",
.args = { "F:f:O:r", 0, 0, NULL },
.usage = "[-r] [-F format] [-f filter] [-O order]",
.args = { "F:f:", 0, 0, NULL },
.usage = "[-F format] [-f filter]",
.flags = CMD_AFTERHOOK,
.exec = cmd_list_sessions_exec
@@ -53,30 +53,22 @@ static enum cmd_retval
cmd_list_sessions_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct session **l;
u_int n, i;
struct session *s;
u_int n;
struct format_tree *ft;
const char *template, *filter;
char *line, *expanded;
int flag;
struct sort_criteria sort_crit;
if ((template = args_get(args, 'F')) == NULL)
template = LIST_SESSIONS_TEMPLATE;
filter = args_get(args, 'f');
sort_crit.order = sort_order_from_string(args_get(args, 'O'));
if (sort_crit.order == SORT_END && args_has(args, 'O')) {
cmdq_error(item, "invalid sort order");
return (CMD_RETURN_ERROR);
}
sort_crit.reversed = args_has(args, 'r');
l = sort_get_sessions(&n, &sort_crit);
for (i = 0; i < n; i++) {
n = 0;
RB_FOREACH(s, sessions, &sessions) {
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_add(ft, "line", "%u", i);
format_defaults(ft, NULL, l[i], NULL, NULL);
format_add(ft, "line", "%u", n);
format_defaults(ft, NULL, s, NULL, NULL);
if (filter != NULL) {
expanded = format_expand(ft, filter);
@@ -91,6 +83,7 @@ cmd_list_sessions_exec(struct cmd *self, struct cmdq_item *item)
}
format_free(ft);
n++;
}
return (CMD_RETURN_NORMAL);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-list-windows.c,v 1.50 2026/02/27 08:25:12 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -41,13 +41,16 @@
static enum cmd_retval cmd_list_windows_exec(struct cmd *, struct cmdq_item *);
static void cmd_list_windows_server(struct cmd *, struct cmdq_item *);
static void cmd_list_windows_session(struct cmd *, struct session *,
struct cmdq_item *, int);
const struct cmd_entry cmd_list_windows_entry = {
.name = "list-windows",
.alias = "lsw",
.args = { "aF:f:O:rt:", 0, 0, NULL },
.usage = "[-ar] [-F format] [-f filter] [-O order]"
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 },
@@ -60,38 +63,51 @@ cmd_list_windows_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 winlink *wl, **l;
if (args_has(args, 'a'))
cmd_list_windows_server(self, item);
else
cmd_list_windows_session(self, target->s, item, 0);
return (CMD_RETURN_NORMAL);
}
static void
cmd_list_windows_server(struct cmd *self, struct cmdq_item *item)
{
struct session *s;
u_int i, n;
RB_FOREACH(s, sessions, &sessions)
cmd_list_windows_session(self, s, item, 1);
}
static void
cmd_list_windows_session(struct cmd *self, struct session *s,
struct cmdq_item *item, int type)
{
struct args *args = cmd_get_args(self);
struct winlink *wl;
u_int n;
struct format_tree *ft;
const char *template, *filter;
char *line, *expanded;
int flag;
struct sort_criteria sort_crit;
template = args_get(args, 'F');
if (template == NULL) {
switch (type) {
case 0:
template = LIST_WINDOWS_TEMPLATE;
break;
case 1:
template = LIST_WINDOWS_WITH_SESSION_TEMPLATE;
break;
}
}
filter = args_get(args, 'f');
sort_crit.order = sort_order_from_string(args_get(args, 'O'));
if (sort_crit.order == SORT_END && args_has(args, 'O')) {
cmdq_error(item, "invalid sort order");
return (CMD_RETURN_ERROR);
}
sort_crit.reversed = args_has(args, 'r');
if (args_has(args, 'a')) {
l = sort_get_winlinks(&n, &sort_crit);
if (template == NULL)
template = LIST_WINDOWS_WITH_SESSION_TEMPLATE;
} else {
l = sort_get_winlinks_session(target->s, &n, &sort_crit);
if (template == NULL)
template = LIST_WINDOWS_TEMPLATE;
}
for (i = 0; i < n; i++) {
wl = l[i];
s = wl->session;
n = 0;
RB_FOREACH(wl, winlinks, &s->windows) {
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_add(ft, "line", "%u", n);
format_defaults(ft, NULL, s, wl, NULL);
@@ -109,7 +125,6 @@ cmd_list_windows_exec(struct cmd *self, struct cmdq_item *item)
}
format_free(ft);
n++;
}
return (CMD_RETURN_NORMAL);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-load-buffer.c,v 1.66 2025/10/28 07:32:26 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-lock-server.c,v 1.31 2021/08/21 10:22:39 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-move-window.c,v 1.34 2021/08/21 10:22:39 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-new-session.c,v 1.152 2026/07/10 13:38:45 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -77,8 +77,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
struct termios tio, *tiop;
struct session_group *sg = NULL;
const char *errstr, *template, *group, *tmp;
char *cause, *cwd = NULL, *cp, *ename;
char *wname = NULL, *sname = NULL, *prefix = NULL;
char *cause, *cwd = NULL, *cp, *newname = NULL;
char *name, *prefix = NULL;
int detached, already_attached, is_control = 0;
u_int sx, sy, dsx, dsy, count = args_count(args);
struct spawn_context sc = { 0 };
@@ -99,43 +99,32 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR);
}
if ((tmp = args_get(args, 'n')) != NULL) {
ename = format_single(item, tmp, c, NULL, NULL, NULL);
if (!check_name(ename)) {
cmdq_error(item, "invalid window name: %s", ename);
free(ename);
tmp = args_get(args, 's');
if (tmp != NULL) {
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);
}
wname = clean_name(ename, 0);
free(ename);
}
if ((tmp = args_get(args, 's')) != NULL) {
ename = format_single(item, tmp, c, NULL, NULL, NULL);
if (!check_name(ename)) {
cmdq_error(item, "invalid session name: %s", ename);
free(ename);
goto fail;
}
sname = clean_name(ename, 0);
free(ename);
free(name);
}
if (args_has(args, 'A')) {
if (sname != NULL)
as = session_find(sname);
if (newname != NULL)
as = session_find(newname);
else
as = target->s;
if (as != NULL) {
retval = cmd_attach_session(item, as->name,
args_has(args, 'D'), args_has(args, 'X'), 0,
args_get(args, 'c'), args_has(args, 'E'),
args_get(args, 'f'));
free(wname);
free(sname);
args_has(args, 'D'), args_has(args, 'X'), 0, NULL,
args_has(args, 'E'), args_get(args, 'f'));
free(newname);
return (retval);
}
}
if (sname != NULL && session_find(sname) != NULL) {
cmdq_error(item, "duplicate session: %s", sname);
if (newname != NULL && session_find(newname) != NULL) {
cmdq_error(item, "duplicate session: %s", newname);
goto fail;
}
@@ -152,12 +141,12 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
else if (groupwith != NULL)
prefix = xstrdup(groupwith->name);
else {
if (!check_name(group)) {
cmdq_error(item,
"invalid session group name: %s", group);
prefix = session_check_name(group);
if (prefix == NULL) {
cmdq_error(item, "invalid session group: %s",
group);
goto fail;
}
prefix = clean_name(group, 0);
}
}
@@ -286,7 +275,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
environ_put(env, av->string, 0);
av = args_next_value(av);
}
s = session_create(prefix, sname, cwd, env, oo, tiop);
s = session_create(prefix, newname, cwd, env, oo, tiop);
/* Spawn the initial window. */
sc.item = item;
@@ -294,7 +283,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (!detached)
sc.tc = c;
sc.name = wname;
sc.name = args_get(args, 'n');
args_to_vector(args, &sc.argc, &sc.argv);
sc.idx = -1;
@@ -325,7 +314,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
session_group_synchronize_to(s);
session_select(s, RB_MIN(winlinks, &s->windows)->idx);
}
events_fire_session("session-created", s);
notify_session("session-created", s);
/*
* Set the client to the new session. If a command client exists, it is
@@ -367,8 +356,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
free(cwd);
free(wname);
free(sname);
free(newname);
free(prefix);
return (CMD_RETURN_NORMAL);
@@ -376,8 +364,7 @@ fail:
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
free(cwd);
free(wname);
free(sname);
free(newname);
free(prefix);
return (CMD_RETURN_ERROR);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-new-window.c,v 1.103 2026/07/08 08:07:42 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -38,8 +38,8 @@ const struct cmd_entry cmd_new_window_entry = {
.name = "new-window",
.alias = "neww",
.args = { "abc:de:EF:kn:PSt:", 0, -1, NULL },
.usage = "[-abdEkPS] [-c start-directory] [-e environment] [-F format] "
.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 [argument ...]]",
@@ -60,36 +60,19 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
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, count = args_count(args);
char *cause = NULL, *cp, *expanded, *wname = NULL;
int idx = target->idx, before;
char *cause = NULL, *cp, *expanded;
const char *template, *name;
struct cmd_find_state fs;
struct args_value *av;
if (args_has(args, 'E') &&
count != 0 &&
(count != 1 || *args_string(args, 0) != '\0')) {
cmdq_error(item, "command cannot be given for empty pane");
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 (name != NULL) {
if (args_has(args, 'S') && name != NULL && target->idx == -1) {
expanded = format_single(item, name, c, s, NULL, NULL);
if (!check_name(expanded)) {
cmdq_error(item, "invalid window name: %s", expanded);
free(expanded);
return (CMD_RETURN_ERROR);
}
wname = clean_name(expanded, 0);
free(expanded);
}
if (args_has(args, 'S') && wname != NULL && target->idx == -1) {
expanded = format_single(item, wname, c, s, NULL, NULL);
RB_FOREACH(wl, winlinks, &s->windows) {
if (strcmp(wl->window->name, expanded) != 0)
continue;
@@ -97,14 +80,12 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
new_wl = wl;
continue;
}
cmdq_error(item, "multiple windows named %s", wname);
free(wname);
cmdq_error(item, "multiple windows named %s", name);
free(expanded);
return (CMD_RETURN_ERROR);
}
free(expanded);
if (new_wl != NULL) {
free(wname);
if (args_has(args, 'd'))
return (CMD_RETURN_NORMAL);
if (session_set_current(s, new_wl) == 0)
@@ -127,7 +108,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
sc.s = s;
sc.tc = tc;
sc.name = wname;
sc.name = args_get(args, 'n');
args_to_vector(args, &sc.argc, &sc.argv);
sc.environ = environ_create();
@@ -141,8 +122,6 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
sc.cwd = args_get(args, 'c');
sc.flags = 0;
if (args_has(args, 'E') || (count == 1 && *args_string(args, 0) == '\0'))
sc.flags |= SPAWN_EMPTY;
if (args_has(args, 'd'))
sc.flags |= SPAWN_DETACHED;
if (args_has(args, 'k'))
@@ -151,7 +130,10 @@ 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);
goto fail;
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) {
cmd_find_from_winlink(current, new_wl, 0);
@@ -174,13 +156,5 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);
free(wname);
return (CMD_RETURN_NORMAL);
fail:
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);
free(wname);
return (CMD_RETURN_ERROR);
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-paste-buffer.c,v 1.44 2026/03/04 07:19:32 tb Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -33,8 +33,8 @@ const struct cmd_entry cmd_paste_buffer_entry = {
.name = "paste-buffer",
.alias = "pasteb",
.args = { "db:prSs:t:", 0, 0, NULL },
.usage = "[-dprS] [-s separator] " CMD_BUFFER_USAGE " "
.args = { "db:prs:t:", 0, 0, NULL },
.usage = "[-dpr] [-s separator] " CMD_BUFFER_USAGE " "
CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 },
@@ -43,17 +43,6 @@ const struct cmd_entry cmd_paste_buffer_entry = {
.exec = cmd_paste_buffer_exec
};
static void
cmd_paste_buffer_paste(struct window_pane *wp, const char *buf, size_t len)
{
char *cp;
size_t n;
n = utf8_stravisx(&cp, buf, len, VIS_SAFE|VIS_NOSLASH);
bufferevent_write(wp->event, cp, n);
free(cp);
}
static enum cmd_retval
cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item)
{
@@ -62,7 +51,7 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item)
struct window_pane *wp = target->wp;
struct paste_buffer *pb;
const char *sepstr, *bufname, *bufdata, *bufend, *line;
size_t seplen, bufsize, len;
size_t seplen, bufsize;
int bracket = args_has(args, 'p');
if (window_pane_exited(wp)) {
@@ -104,22 +93,14 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item)
line = memchr(bufdata, '\n', bufend - bufdata);
if (line == NULL)
break;
len = line - bufdata;
if (args_has(args, 'S'))
bufferevent_write(wp->event, bufdata, len);
else
cmd_paste_buffer_paste(wp, bufdata, len);
bufferevent_write(wp->event, bufdata, line - bufdata);
bufferevent_write(wp->event, sepstr, seplen);
bufdata = line + 1;
}
if (bufdata != bufend) {
len = bufend - bufdata;
if (args_has(args, 'S'))
bufferevent_write(wp->event, bufdata, len);
else
cmd_paste_buffer_paste(wp, bufdata, len);
}
if (bufdata != bufend)
bufferevent_write(wp->event, bufdata, bufend - bufdata);
if (bracket && (wp->screen->mode & MODE_BRACKETPASTE))
bufferevent_write(wp->event, "\033[201~", 6);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-pipe-pane.c,v 1.64 2026/07/27 14:25:46 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -123,13 +123,11 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
/* Fork the child. */
sigfillset(&set);
sigprocmask(SIG_BLOCK, &set, &oldset);
switch ((wp->pipe_pid = fork())) {
switch (fork()) {
case -1:
sigprocmask(SIG_SETMASK, &oldset, NULL);
cmdq_error(item, "fork error: %s", strerror(errno));
close(pipe_fd[0]);
close(pipe_fd[1]);
free(cmd);
return (CMD_RETURN_ERROR);
case 0:
@@ -138,9 +136,6 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
sigprocmask(SIG_SETMASK, &oldset, NULL);
close(pipe_fd[0]);
if (setpgid(0, 0) == -1)
_exit(1);
null_fd = open(_PATH_DEVNULL, O_WRONLY);
if (out) {
if (dup2(pipe_fd[1], STDIN_FILENO) == -1)
@@ -194,13 +189,9 @@ static void
cmd_pipe_pane_read_callback(__unused struct bufferevent *bufev, void *data)
{
struct window_pane *wp = data;
struct evbuffer *evb;
struct evbuffer *evb = wp->pipe_event->input;
size_t available;
if (wp->pipe_event == NULL)
return;
evb = wp->pipe_event->input;
available = EVBUFFER_LENGTH(evb);
log_debug("%%%u pipe read %zu", wp->id, available);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-queue.c,v 1.123 2026/08/24 20:34:26 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -35,7 +35,6 @@
enum cmdq_type {
CMDQ_COMMAND,
CMDQ_CALLBACK,
CMDQ_INVOKE,
};
/* Command queue item. */
@@ -48,6 +47,7 @@ struct cmdq_item {
struct client *target_client;
enum cmdq_type type;
u_int group;
u_int number;
time_t time;
@@ -58,8 +58,8 @@ struct cmdq_item {
struct cmd_find_state source;
struct cmd_find_state target;
struct cmd_list *cmdlist;
struct cmd *cmd;
struct cmd_invoke_state *invoke_state;
cmdq_cb cb;
void *data;
@@ -147,13 +147,6 @@ cmdq_get_name(struct cmdq_item *item)
return (item->name);
}
/* Get item command. */
struct cmd *
cmdq_get_cmd(struct cmdq_item *item)
{
return (item->cmd);
}
/* Get item client. */
struct client *
cmdq_get_client(struct cmdq_item *item)
@@ -357,53 +350,74 @@ cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item)
/* Insert a hook. */
void
cmdq_insert_hook(__unused struct session *s, struct cmdq_item *item,
cmdq_insert_hook(struct session *s, struct cmdq_item *item,
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 event_payload *ep;
struct options *oo;
va_list ap;
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->state->flags & CMDQ_STATE_NOHOOKS)
return;
if (s == NULL)
oo = global_s_options;
else
oo = s->options;
va_start(ap, fmt);
xvasprintf(&name, fmt, ap);
va_end(ap);
ep = event_payload_create();
if (current != NULL)
event_payload_set_target(ep, current);
event_payload_set_pointer(ep, "_cmdq_item", item, NULL, NULL);
o = options_get(oo, name);
if (o == NULL) {
free(name);
return;
}
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);
event_payload_set_string(ep, "arguments", "%s", arguments);
cmdq_add_format(new_state, "hook_arguments", "%s", arguments);
free(arguments);
for (i = 0; i < args_count(args); i++) {
xsnprintf(tmp, sizeof tmp, "argument_%u", i);
event_payload_set_string(ep, tmp, "%s", args_string(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);
xsnprintf(tmp, sizeof tmp, "flag_%c", flag);
if (value == NULL)
event_payload_set_string(ep, tmp, "1");
else
event_payload_set_string(ep, tmp, "%s", value);
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, "flag_%c_%u", flag, i);
event_payload_set_string(ep, tmp, "%s", av->string);
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);
}
@@ -411,7 +425,20 @@ cmdq_insert_hook(__unused struct session *s, struct cmdq_item *item,
flag = args_next(&ae);
}
events_fire(name, ep);
a = options_array_first(o);
while (a != NULL) {
cmdlist = options_array_item_value(a)->cmdlist;
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);
}
a = options_array_next(a);
}
cmdq_free_state(new_state);
free(name);
}
@@ -428,10 +455,8 @@ cmdq_remove(struct cmdq_item *item)
{
if (item->client != NULL)
server_client_unref(item->client);
if (item->cmd != NULL)
cmd_free(item->cmd);
if (item->invoke_state != NULL)
cmd_invoke_state_free(item->invoke_state);
if (item->cmdlist != NULL)
cmd_list_free(item->cmdlist);
cmdq_free_state(item->state);
TAILQ_REMOVE(&item->queue->list, item, entry);
@@ -440,46 +465,75 @@ cmdq_remove(struct cmdq_item *item)
free(item);
}
/* Get a single command for command queue with invoke state. */
struct cmdq_item *
cmdq_get_one_command(struct cmd *cmd, struct cmdq_state *state,
struct cmd_invoke_state *invoke_state)
/* Remove all subsequent items that match this item's group. */
static void
cmdq_remove_group(struct cmdq_item *item)
{
struct cmdq_item *item;
const struct cmd_entry *entry = cmd_get_entry(cmd);
struct cmdq_item *this, *next;
if (item->group == 0)
return;
this = TAILQ_NEXT(item, entry);
while (this != NULL) {
next = TAILQ_NEXT(this, entry);
if (this->group == item->group)
cmdq_remove(this);
this = next;
}
}
/* 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 cmdq_state *state)
{
struct cmdq_item *item, *first = NULL, *last = NULL;
struct cmd *cmd;
const struct cmd_entry *entry;
int created = 0;
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]", entry->name, item);
item->type = CMDQ_COMMAND;
item->group = cmd_get_group(cmd);
item->state = cmdq_link_state(state);
item->cmdlist = cmdlist;
item->cmd = cmd;
if (state == NULL)
item->state = cmdq_new_state(NULL, NULL, 0);
else
item->state = cmdq_link_state(state);
cmdlist->references++;
log_debug("%s: %s group %u", __func__, item->name, item->group);
if (invoke_state != NULL)
item->invoke_state = cmd_invoke_state_add_ref(invoke_state);
if (first == NULL)
first = item;
if (last != NULL)
last->next = item;
last = item;
log_debug("%s: %s", __func__, item->name);
return (item);
cmd = cmd_list_next(cmd);
}
/* Get an invoke continuation for command queue. */
struct cmdq_item *
cmdq_get_invoke(struct cmd_invoke_state *invoke_state, struct cmdq_state *state)
{
struct cmdq_item *item;
item = xcalloc(1, sizeof *item);
xasprintf(&item->name, "[invoke/%p]", item);
item->type = CMDQ_INVOKE;
if (state == NULL)
item->state = cmdq_new_state(NULL, NULL, 0);
else
item->state = cmdq_link_state(state);
item->invoke_state = cmd_invoke_state_add_ref(invoke_state);
return (item);
if (created)
cmdq_free_state(state);
return (first);
}
/* Fill in flag for a command. */
@@ -557,7 +611,7 @@ cmdq_fire_command(struct cmdq_item *item)
cmdq_add_message(item);
if (log_get_level() > 1) {
tmp = cmd_print(cmd);
log_debug("%s %s: %s", __func__, name, tmp);
log_debug("%s %s: (%u) %s", __func__, name, item->group, tmp);
free(tmp);
}
@@ -636,6 +690,7 @@ cmdq_get_callback1(const char *name, cmdq_cb cb, void *data)
xasprintf(&item->name, "[%s/%p]", name, item);
item->type = CMDQ_CALLBACK;
item->group = 0;
item->state = cmdq_new_state(NULL, NULL, 0);
item->cb = cb;
@@ -717,16 +772,17 @@ cmdq_next(struct client *c)
switch (item->type) {
case CMDQ_COMMAND:
retval = cmdq_fire_command(item);
if (item->invoke_state != NULL)
cmd_invoke_result(item->invoke_state, retval);
/*
* If a command returns an error, remove any
* subsequent commands in the same group.
*/
if (retval == CMD_RETURN_ERROR)
cmdq_remove_group(item);
break;
case CMDQ_CALLBACK:
retval = cmdq_fire_callback(item);
break;
case CMDQ_INVOKE:
retval = cmd_invoke_fire(item, item->invoke_state);
break;
default:
retval = CMD_RETURN_ERROR;
break;
@@ -773,7 +829,7 @@ cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
u_int number = item->number;
if (c != NULL && (c->flags & CLIENT_CONTROL))
control_write_guard(c, guard, t, number, flags);
control_write(c, "%%%s %ld %u %d", guard, t, number, flags);
}
/* Show message from command. */
@@ -821,18 +877,7 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...)
if (c == NULL) {
cmd_get_source(cmd, &file, &line);
if (!cfg_finished) {
if (file != NULL)
cfg_add_cause("%s:%u: %s", file, line, msg);
else
cfg_add_cause("%s", msg);
} else {
if (file != NULL) {
server_add_message("message: %s:%u: %s", file,
line, msg);
} else
server_add_message("message: %s", msg);
}
} else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
server_add_message("%s message: %s", c->name, msg);
if (~c->flags & CLIENT_UTF8) {

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-refresh-client.c,v 1.55 2026/07/17 08:37:29 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -34,7 +34,7 @@ const struct cmd_entry cmd_refresh_client_entry = {
.name = "refresh-client",
.alias = "refresh",
.args = { "A:B:cC:Df:r:F:lLRSt:U", 0, 1, NULL },
.args = { "A:B:cC:Df:r:F:l::LRSt:U", 0, 1, NULL },
.usage = "[-cDlLRSU] [-A pane:state] [-B name:what:format] "
"[-C XxY] [-f flags] [-r pane:report] " CMD_TARGET_CLIENT_USAGE
" [adjustment]",
@@ -46,17 +46,36 @@ const struct cmd_entry cmd_refresh_client_entry = {
static void
cmd_refresh_client_update_subscription(struct client *tc, const char *value)
{
char *name, *format;
enum monitor_type type;
int id;
char *copy, *split, *name, *what;
enum control_sub_type subtype;
int subid = -1;
if (monitor_parse(value, &name, &type, &id, &format) != 0) {
control_remove_sub(tc, value);
return;
copy = name = xstrdup(value);
if ((split = strchr(copy, ':')) == NULL) {
control_remove_sub(tc, copy);
goto out;
}
control_add_sub(tc, name, type, id, format);
free(name);
free(format);
*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
@@ -66,6 +85,7 @@ cmd_refresh_client_control_client_size(struct cmd *self, struct cmdq_item *item)
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 ||
@@ -75,16 +95,22 @@ cmd_refresh_client_control_client_size(struct cmd *self, struct cmdq_item *item)
}
log_debug("%s: client %s window @%u: size %ux%u", __func__,
tc->name, w, x, y);
control_set_window_size(tc, 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);
control_clear_window_size(tc, w);
cw->sx = 0;
cw->sy = 0;
recalculate_sizes_now(1);
}
return (CMD_RETURN_NORMAL);
}
@@ -137,12 +163,42 @@ out:
free(copy);
}
static enum cmd_retval
cmd_refresh_client_clipboard(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct client *tc = cmdq_get_target_client(item);
const char *p;
u_int i;
struct cmd_find_state fs;
p = args_get(args, 'l');
if (p == NULL) {
if (tc->flags & CLIENT_CLIPBOARDBUFFER)
return (CMD_RETURN_NORMAL);
tc->flags |= CLIENT_CLIPBOARDBUFFER;
} else {
if (cmd_find_target(&fs, item, p, CMD_FIND_PANE, 0) != 0)
return (CMD_RETURN_ERROR);
for (i = 0; i < tc->clipboard_npanes; i++) {
if (tc->clipboard_panes[i] == fs.wp->id)
break;
}
if (i != tc->clipboard_npanes)
return (CMD_RETURN_NORMAL);
tc->clipboard_panes = xreallocarray(tc->clipboard_panes,
tc->clipboard_npanes + 1, sizeof *tc->clipboard_panes);
tc->clipboard_panes[tc->clipboard_npanes++] = fs.wp->id;
}
tty_clipboard_query(&tc->tty);
return (CMD_RETURN_NORMAL);
}
static void
cmd_refresh_report(struct tty *tty, const char *value)
{
struct window_pane *wp;
u_int pane;
int fg, bg;
size_t size = 0;
char *copy, *split;
@@ -159,14 +215,8 @@ cmd_refresh_report(struct tty *tty, const char *value)
if (wp == NULL)
goto out;
fg = wp->control_fg;
bg = wp->control_bg;
if (tty_keys_colours(tty, split, strlen(split), &size, &fg, &bg) == 0) {
if (bg != wp->control_bg)
wp->flags |= PANE_THEMECHANGED;
wp->control_fg = fg;
wp->control_bg = bg;
}
tty_keys_colours(tty, split, strlen(split), &size, &wp->control_fg,
&wp->control_bg);
out:
free(copy);
@@ -234,10 +284,8 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'l')) {
tty_clipboard_query(&tc->tty);
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'l'))
return (cmd_refresh_client_clipboard(self, item));
if (args_has(args, 'F')) /* -F is an alias for -f */
server_client_set_flags(tc, args_get(args, 'F'));

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-rename-session.c,v 1.39 2026/07/10 13:38:45 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -49,17 +49,15 @@ cmd_rename_session_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 event_payload *ep;
struct cmd_find_state fs;
char *newname, *tmp;
tmp = format_single_from_target(item, args_string(args, 0));
if (!check_name(tmp)) {
cmdq_error(item, "invalid session name: %s", tmp);
newname = session_check_name(tmp);
if (newname == NULL) {
cmdq_error(item, "invalid session: %s", tmp);
free(tmp);
return (CMD_RETURN_ERROR);
}
newname = clean_name(tmp, 0);
free(tmp);
if (strcmp(newname, s->name) == 0) {
free(newname);
@@ -71,20 +69,13 @@ cmd_rename_session_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR);
}
ep = event_payload_create();
cmd_find_from_session(&fs, s, 0);
event_payload_set_target(ep, &fs);
event_payload_set_session(ep, "session", s);
event_payload_set_string(ep, "old_name", "%s", s->name);
event_payload_set_string(ep, "new_name", "%s", newname);
RB_REMOVE(sessions, &sessions, s);
free(s->name);
s->name = newname;
RB_INSERT(sessions, &sessions, s);
server_status_session(s);
events_fire("session-renamed", ep);
notify_session("session-renamed", s);
return (CMD_RETURN_NORMAL);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-rename-window.c,v 1.30 2026/06/29 18:17:28 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -48,21 +48,15 @@ cmd_rename_window_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 winlink *wl = target->wl;
char *name;
char *newname;
name = format_single_from_target(item, args_string(args, 0));
if (!check_name(name)) {
cmdq_error(item, "invalid window name: %s", name);
free(name);
return (CMD_RETURN_ERROR);
}
window_set_name(wl->window, name, 0);
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);
free(name);
server_redraw_window_borders(wl->window);
server_status_window(wl->window);
free(newname);
return (CMD_RETURN_NORMAL);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-resize-pane.c,v 1.68 2026/08/31 07:44:39 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -18,7 +18,6 @@
#include <sys/types.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
@@ -30,20 +29,16 @@
static enum cmd_retval cmd_resize_pane_exec(struct cmd *, struct cmdq_item *);
static enum cmd_retval cmd_resize_pane_mouse_update(struct cmd *,
struct cmdq_item *);
static void cmd_resize_pane_mouse_resize_move_floating(
struct client *, struct mouse_event *);
static void cmd_resize_pane_mouse_resize_tiled(struct client *,
static void cmd_resize_pane_mouse_update(struct client *,
struct mouse_event *);
const struct cmd_entry cmd_resize_pane_entry = {
.name = "resize-pane",
.alias = "resizep",
.args = { "D::L::MR::Tt:U::x:y:Z", 0, 1, NULL },
.usage = "[-MTZ] [-D lines] [-L columns] [-R columns] [-U lines] "
"[-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 },
@@ -56,23 +51,23 @@ cmd_resize_pane_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 key_event *event = cmdq_get_event(item);
struct window_pane *wp = target->wp;
struct winlink *wl = target->wl;
struct window *w = wl->window;
struct layout_cell *lc = wp->layout_cell;
enum layout_type type;
const char *errstr, *argval;
const char flags[4] = { 'U', 'D', 'L', 'R' };
char *cause = NULL, flag;
int adjust, x, y, status, opposite = 0;
long unsigned i;
struct client *c = cmdq_get_client(item);
struct session *s = target->s;
const char *errstr;
char *cause;
u_int adjust;
int x, y, status;
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 > (int)gd->hsize)
if (adjust > gd->hsize)
adjust = gd->hsize;
grid_remove_history(gd, adjust);
wp->base.cy += adjust;
@@ -80,8 +75,15 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'M'))
return (cmd_resize_pane_mouse_update(self, item));
if (args_has(args, 'M')) {
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, &event->m);
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'Z')) {
if (w->flags & WINDOW_ZOOMED)
@@ -92,33 +94,34 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
server_unzoom_window(w);
lc = wp->layout_cell; /* may have been replaced by unzoom */
if (args_count(args) == 0)
adjust = 1;
else {
adjust = strtonum(args_string(args, 0), 1, INT_MAX, &errstr);
if (errstr != NULL) {
cmdq_error(item, "adjustment %s", errstr);
return (CMD_RETURN_ERROR);
}
}
if (args_has(args, 'x')) {
x = args_percentage(args, 'x', 0, PANE_MAXIMUM, w->sx, &cause);
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);
}
if (window_pane_is_floating(wp)) {
if (layout_resize_floating_pane_to(wp, LAYOUT_LEFTRIGHT,
x, &cause) != 0) {
cmdq_error(item, "size %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
} else
layout_resize_pane_to(wp, LAYOUT_LEFTRIGHT, x);
}
if (args_has(args, 'y')) {
y = args_percentage(args, 'y', 0, PANE_MAXIMUM, w->sy, &cause);
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);
}
status = window_get_pane_status(w);
status = options_get_number(w->options, "pane-border-status");
switch (status) {
case PANE_STATUS_TOP:
if (y != INT_MAX && wp->yoff == 1)
@@ -129,231 +132,24 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
y++;
break;
}
if (window_pane_is_floating(wp)) {
if (layout_resize_floating_pane_to(wp, LAYOUT_TOPBOTTOM,
y, &cause) != 0) {
cmdq_error(item, "size %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
} else
layout_resize_pane_to(wp, LAYOUT_TOPBOTTOM, y);
}
for (i = 0; i < nitems(flags); i++) {
flag = flags[i];
if (!args_has(args, flag))
continue;
argval = args_get(args, flag);
if (argval == NULL) {
if (args_count(args) == 0)
argval = "1";
else
argval = args_string(args, 0);
}
adjust = strtonum(argval, INT_MIN, INT_MAX, &errstr);
if (errstr != NULL) {
cmdq_error(item, "adjustment %s", errstr);
return (CMD_RETURN_ERROR);
}
type = LAYOUT_TOPBOTTOM;
if (flag == 'L' || flag == 'R')
type = LAYOUT_LEFTRIGHT;
if (window_pane_is_floating(wp)) {
if (flag == 'L' || flag == 'U')
opposite = 1;
if (layout_resize_floating_pane(wp, type, adjust,
opposite, &cause) != 0) {
cmdq_error(item, "adjustment %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
} else {
if (flag == 'L' || flag == 'U')
adjust = -adjust;
layout_resize_pane(wp, type, adjust, 1);
}
}
if (lc->parent != NULL)
layout_fix_offsets(w);
layout_fix_panes(w, NULL);
events_fire_window("window-layout-changed", w);
server_redraw_window(w);
if (args_has(args, 'L'))
layout_resize_pane(wp, LAYOUT_LEFTRIGHT, -adjust, 1);
else if (args_has(args, 'R'))
layout_resize_pane(wp, LAYOUT_LEFTRIGHT, adjust, 1);
else if (args_has(args, 'U'))
layout_resize_pane(wp, LAYOUT_TOPBOTTOM, -adjust, 1);
else if (args_has(args, 'D'))
layout_resize_pane(wp, LAYOUT_TOPBOTTOM, adjust, 1);
server_redraw_window(wl->window);
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_resize_pane_mouse_update(__unused struct cmd *self, struct cmdq_item *item)
{
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 = cmdq_get_client(item);
struct session *s = target->s;
if (!event->m.valid)
return (CMD_RETURN_NORMAL);
wp = cmd_mouse_pane(&event->m, &s, NULL);
if (wp == NULL || c == NULL || c->session != s)
return (CMD_RETURN_NORMAL);
if (!window_pane_is_floating(wp)) {
c->tty.mouse_drag_update = cmd_resize_pane_mouse_resize_tiled;
cmd_resize_pane_mouse_resize_tiled(c, &event->m);
return (CMD_RETURN_NORMAL);
}
window_redraw_active_switch(w, wp);
window_set_active_pane(w, wp, 1);
c->tty.mouse_drag_update = cmd_resize_pane_mouse_resize_move_floating;
cmd_resize_pane_mouse_resize_move_floating(c, &event->m);
return (CMD_RETURN_NORMAL);
}
/*
* Resizes or moves the pane by dragging. Resize a floating pane by dragging
* the borders or corners. Grabbing an edge only resizes that axis (special
* case). Moves the pane if dragging the top border. Since characters are
* generally rectangular, to make it easier to grab the corner, the character
* next to the corner is also considered the corner.
*/
static void
cmd_resize_pane_mouse_resize_move_floating(struct client *c,
struct mouse_event *m)
{
struct winlink *wl;
struct window *w;
struct window_pane *wp;
struct layout_cell *lc;
int y, ly, x, lx, sx, sy, new_sx, new_sy;
int left, right;
int new_xoff, new_yoff, resizes = 0;
wp = cmd_mouse_pane(m, NULL, &wl);
if (wp == NULL) {
c->tty.mouse_drag_update = NULL;
return;
}
w = wl->window;
lc = wp->layout_cell;
sx = wp->sx;
sy = wp->sy;
left = wp->xoff - 1;
right = wp->xoff + sx;
if (window_pane_scrollbar_reserve(wp) &&
w->sb_pos == PANE_SCROLLBARS_LEFT) {
left -= wp->scrollbar_style.width + wp->scrollbar_style.pad;
} else if (window_pane_scrollbar_reserve(wp) &&
w->sb_pos == PANE_SCROLLBARS_RIGHT) {
right += wp->scrollbar_style.width + wp->scrollbar_style.pad;
}
y = m->y + m->oy; x = m->x + m->ox;
if (m->statusat == 0 && y >= (int)m->statuslines)
y -= m->statuslines;
else if (m->statusat > 0 && y >= m->statusat)
y = m->statusat - 1;
ly = m->ly + m->oy; lx = m->lx + m->ox;
if (m->statusat == 0 && ly >= (int)m->statuslines)
ly -= m->statuslines;
else if (m->statusat > 0 && ly >= m->statusat)
ly = m->statusat - 1;
if ((lx == left || lx == left + 1) && ly == wp->yoff - 1) {
/* Top left corner. */
new_sx = lc->g.sx + (lx - x);
if (new_sx < PANE_MINIMUM)
new_sx = PANE_MINIMUM;
new_sy = lc->g.sy + (ly - y);
if (new_sy < PANE_MINIMUM)
new_sy = PANE_MINIMUM;
new_xoff = x + 1; /* because mouse is on border at xoff - 1 */
new_yoff = y + 1;
layout_set_size(lc, new_sx, new_sy, new_xoff, new_yoff);
resizes++;
} else if ((lx == right + 1 || lx == right) &&
ly == wp->yoff - 1) {
/* Top right corner. */
new_sx = x - lc->g.xoff;
if (new_sx < PANE_MINIMUM)
new_sx = PANE_MINIMUM;
new_sy = lc->g.sy + (ly - y);
if (new_sy < PANE_MINIMUM)
new_sy = PANE_MINIMUM;
new_yoff = y + 1;
layout_set_size(lc, new_sx, new_sy, lc->g.xoff, new_yoff);
resizes++;
} else if ((lx == left || lx == left + 1) &&
ly == wp->yoff + sy) {
/* Bottom left corner. */
new_sx = lc->g.sx + (lx - x);
if (new_sx < PANE_MINIMUM)
new_sx = PANE_MINIMUM;
new_sy = y - lc->g.yoff;
if (new_sy < PANE_MINIMUM)
return;
new_xoff = x + 1;
layout_set_size(lc, new_sx, new_sy, new_xoff, lc->g.yoff);
resizes++;
} else if ((lx == right + 1 || lx == right) &&
ly == wp->yoff + sy) {
/* Bottom right corner. */
new_sx = x - lc->g.xoff;
if (new_sx < PANE_MINIMUM)
new_sx = PANE_MINIMUM;
new_sy = y - lc->g.yoff;
if (new_sy < PANE_MINIMUM)
new_sy = PANE_MINIMUM;
layout_set_size(lc, new_sx, new_sy, lc->g.xoff, lc->g.yoff);
resizes++;
} else if (lx == right) {
/* Right border. */
new_sx = x - lc->g.xoff;
if (new_sx < PANE_MINIMUM)
return;
layout_set_size(lc, new_sx, lc->g.sy, lc->g.xoff, lc->g.yoff);
resizes++;
} else if (lx == left) {
/* Left border. */
new_sx = lc->g.sx + (lx - x);
if (new_sx < PANE_MINIMUM)
return;
new_xoff = x + 1;
layout_set_size(lc, new_sx, lc->g.sy, new_xoff, lc->g.yoff);
resizes++;
} else if (ly == wp->yoff + sy) {
/* Bottom border. */
new_sy = y - lc->g.yoff;
if (new_sy < PANE_MINIMUM)
return;
layout_set_size(lc, lc->g.sx, new_sy, lc->g.xoff, lc->g.yoff);
resizes++;
} else if (ly == wp->yoff - 1) {
/* Top border (move instead of resize). */
new_xoff = lc->g.xoff + (x - lx);
new_yoff = y + 1;
layout_set_size(lc, lc->g.sx, lc->g.sy, new_xoff, new_yoff);
resizes++;
}
if (resizes != 0) {
layout_fix_panes(w, NULL);
server_redraw_window(w);
server_redraw_window_borders(w);
}
}
static void
cmd_resize_pane_mouse_resize_tiled(struct client *c, struct mouse_event *m)
cmd_resize_pane_mouse_update(struct client *c, struct mouse_event *m)
{
struct winlink *wl;
struct window *w;

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-resize-window.c,v 1.10 2023/06/30 13:19:32 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2018 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-respawn-pane.c,v 1.40 2026/07/03 16:09:49 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -34,8 +34,8 @@ const struct cmd_entry cmd_respawn_pane_entry = {
.name = "respawn-pane",
.alias = "respawnp",
.args = { "c:e:Ekt:", 0, -1, NULL },
.usage = "[-Ek] [-c start-directory] [-e environment] "
.args = { "c:e:kt:", 0, -1, NULL },
.usage = "[-k] [-c start-directory] [-e environment] "
CMD_TARGET_PANE_USAGE " [shell-command [argument ...]]",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -75,8 +75,6 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item)
sc.cwd = args_get(args, 'c');
sc.flags = SPAWN_RESPAWN;
if (args_has(args, 'E'))
sc.flags |= SPAWN_EMPTY;
if (args_has(args, 'k'))
sc.flags |= SPAWN_KILL;

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-respawn-window.c,v 1.51 2026/07/03 16:09:49 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -34,8 +34,8 @@ const struct cmd_entry cmd_respawn_window_entry = {
.name = "respawn-window",
.alias = "respawnw",
.args = { "c:e:Ekt:", 0, -1, NULL },
.usage = "[-Ek] [-c start-directory] [-e environment] "
.args = { "c:e:kt:", 0, -1, NULL },
.usage = "[-k] [-c start-directory] [-e environment] "
CMD_TARGET_WINDOW_USAGE " [shell-command [argument ...]]",
.target = { 't', CMD_FIND_WINDOW, 0 },
@@ -74,8 +74,6 @@ cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item)
sc.cwd = args_get(args, 'c');
sc.flags = SPAWN_RESPAWN;
if (args_has(args, 'E'))
sc.flags |= SPAWN_EMPTY;
if (args_has(args, 'k'))
sc.flags |= SPAWN_KILL;

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-rotate-window.c,v 1.34 2026/06/22 08:47:45 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -109,7 +109,6 @@ cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item)
window_set_active_pane(w, wp, 1);
cmd_find_from_winlink_pane(current, wl, wp, 0);
window_pop_zoom(w);
redraw_invalidate_scene(w);
server_redraw_window(w);
return (CMD_RETURN_NORMAL);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-run-shell.c,v 1.94 2026/08/25 06:04:33 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
@@ -44,9 +44,9 @@ const struct cmd_entry cmd_run_shell_entry = {
.name = "run-shell",
.alias = "run",
.args = { "bd:Ct:Es:c:", 0, -1, cmd_run_shell_args_parse },
.args = { "bd:Ct:Es:c:", 0, 1, cmd_run_shell_args_parse },
.usage = "[-bCE] [-c start-directory] [-d delay] " CMD_TARGET_PANE_USAGE
" [shell-command [argument ...]]",
" [shell-command]",
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
@@ -57,7 +57,7 @@ const struct cmd_entry cmd_run_shell_entry = {
struct cmd_run_shell_data {
struct client *client;
char *cmd;
struct cmd_parse_tree *tree;
struct args_command_state *state;
char *cwd;
struct cmdq_item *item;
struct session *s;
@@ -90,8 +90,8 @@ cmd_run_shell_print(struct job *job, const char *msg)
cmdq_print(cdata->item, "%s", msg);
return;
}
if (cdata->client != NULL && cdata->client->session != NULL)
wp = cdata->client->session->curw->window->active;
if (cdata->item != NULL && cdata->client != NULL)
wp = server_client_get_pane(cdata->client);
if (wp == NULL && cmd_find_from_nothing(&fs, 0) == 0)
wp = fs.wp;
if (wp == NULL)
@@ -100,8 +100,7 @@ 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, NULL, &window_view_mode, NULL, NULL,
NULL);
window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL);
window_copy_add(wp, 1, "%s", msg);
}
@@ -116,11 +115,9 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
struct session *s = target->s;
struct window_pane *wp = target->wp;
const char *delay, *cmd;
struct format_tree *ft;
double d;
struct timeval tv;
char *end, key[16], *cause = NULL;
u_int i;
char *end;
int wait = !args_has(args, 'b');
if ((delay = args_get(args, 'd')) != NULL) {
@@ -129,31 +126,17 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
cmdq_error(item, "invalid delay time: %s", delay);
return (CMD_RETURN_ERROR);
}
} else {
if (args_count(args) == 0)
} else if (args_count(args) == 0)
return (CMD_RETURN_NORMAL);
}
cdata = xcalloc(1, sizeof *cdata);
if (!args_has(args, 'C')) {
cmd = args_string(args, 0);
if (cmd != NULL) {
ft = format_create_from_target(item);
for (i = 1; i < args_count(args); i++) {
xsnprintf(key, sizeof key, "%u", i);
format_add(ft, key, "%s", args_string(args, i));
}
cdata->cmd = format_expand(ft, cmd);
format_free(ft);
}
if (cmd != NULL)
cdata->cmd = format_single_from_target(item, cmd);
} else {
cdata->tree = args_to_commands(self, item, 0, NULL, 1, &cause);
if (cdata->tree == NULL) {
cmdq_error(item, "%s", cause);
free(cause);
cmd_run_shell_free(cdata);
return (CMD_RETURN_ERROR);
}
cdata->state = args_make_commands_prepare(self, item, 0, NULL,
wait, 1);
}
if (args_has(args, 't') && wp != NULL)
@@ -170,7 +153,6 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
}
if (cdata->client != NULL)
cdata->client->references++;
if (args_has(args, 'c'))
cdata->cwd = xstrdup(args_get(args, 'c'));
else
@@ -179,20 +161,18 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'E'))
cdata->flags |= JOB_SHOWSTDERR;
if (s != NULL) {
cdata->s = s;
if (s != NULL)
session_add_ref(s, __func__);
}
evtimer_set(&cdata->timer, cmd_run_shell_timer, cdata);
if (delay == NULL)
event_active(&cdata->timer, EV_TIMEOUT, 1);
else {
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);
@@ -206,9 +186,10 @@ cmd_run_shell_timer(__unused int fd, __unused short events, void* arg)
struct client *c = cdata->client;
const char *cmd = cdata->cmd;
struct cmdq_item *item = cdata->item, *new_item;
struct cmdq_state *cs = NULL;
struct cmd_list *cmdlist;
char *error;
if (cdata->tree == NULL) {
if (cdata->state == NULL) {
if (cmd == NULL) {
if (cdata->item != NULL)
cmdq_continue(cdata->item);
@@ -217,27 +198,26 @@ cmd_run_shell_timer(__unused int fd, __unused short events, void* arg)
}
if (job_run(cmd, 0, NULL, NULL, cdata->s, cdata->cwd, NULL,
cmd_run_shell_callback, cmd_run_shell_free, cdata,
cdata->flags, -1, -1) == NULL) {
if (cdata->item == NULL)
status_message_set(c, -1, 1, 0, 0,
"failed to run command: %s", cmd);
else {
cmdq_error(cdata->item,
"failed to run command: %s", cmd);
cmdq_continue(cdata->item);
}
cdata->flags, -1, -1) == NULL)
cmd_run_shell_free(cdata);
}
return;
}
if (item != NULL)
cs = cmdq_get_state(item);
new_item = cmd_invoke_get(cdata->tree, cs, NULL);
if (item == NULL)
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, 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
} 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);
@@ -305,7 +285,8 @@ cmd_run_shell_free(void *data)
session_remove_ref(cdata->s, __func__);
if (cdata->client != NULL)
server_client_unref(cdata->client);
cmd_parse_free(cdata->tree);
if (cdata->state != NULL)
args_make_commands_free(cdata->state);
free(cdata->cwd);
free(cdata->cmd);
free(cdata);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-save-buffer.c,v 1.57 2025/10/28 07:32:26 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-select-layout.c,v 1.43 2026/07/10 13:38:45 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -90,7 +90,7 @@ cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item)
previous = 1;
oldlayout = w->old_layout;
w->old_layout = layout_dump(w, w->layout_root);
w->old_layout = layout_dump(w->layout_root);
if (next || previous) {
if (next)
@@ -139,7 +139,7 @@ changed:
free(oldlayout);
recalculate_sizes();
server_redraw_window(w);
events_fire_window("window-layout-changed", w);
notify_window("window-layout-changed", w);
return (CMD_RETURN_NORMAL);
error:

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-select-pane.c,v 1.78 2026/08/20 09:19:24 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -80,69 +80,6 @@ cmd_select_pane_redraw(struct window *w)
}
}
static enum cmd_retval
cmd_select_pane_marked_pane(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct event_payload *ep;
struct cmd_find_state fs;
struct winlink *wl = target->wl;
struct window_pane *wp = target->wp, *lwp = NULL, *mwp;
struct session *s = target->s;
if (args_has(args, 'm') && !window_pane_is_visible(wp))
return (CMD_RETURN_NORMAL);
if (server_check_marked())
lwp = marked_pane.wp;
if (args_has(args, 'M') || server_is_marked(s, wl, wp))
server_clear_marked();
else
server_set_marked(s, wl, wp);
mwp = marked_pane.wp;
ep = event_payload_create();
if (mwp != NULL)
cmd_find_from_pane(&fs, mwp, 0);
else if (lwp != NULL)
cmd_find_from_pane(&fs, lwp, 0);
else
cmd_find_from_pane(&fs, wp, 0);
event_payload_set_target(ep, &fs);
if (mwp != NULL) {
event_payload_set_pane(ep, "pane", mwp);
event_payload_set_pane(ep, "new_pane", mwp);
event_payload_set_window(ep, "window", mwp->window);
} else if (lwp != NULL) {
event_payload_set_pane(ep, "pane", lwp);
event_payload_set_window(ep, "window", lwp->window);
} else {
event_payload_set_pane(ep, "pane", wp);
event_payload_set_window(ep, "window", wp->window);
}
if (lwp != NULL)
event_payload_set_pane(ep, "old_pane", lwp);
event_payload_set_int(ep, "marked", mwp != NULL);
events_fire("marked-pane-changed", ep);
if (lwp != NULL) {
lwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|PANE_THEMECHANGED);
server_redraw_window_borders(lwp->window);
server_status_window(lwp->window);
}
if (mwp != NULL) {
mwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|PANE_THEMECHANGED);
server_redraw_window_borders(mwp->window);
server_status_window(mwp->window);
}
if (window_pane_is_floating(wp)) {
window_redraw_active_switch(wp->window, wp);
window_set_active_pane(wp->window, wp, 1);
}
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
{
@@ -150,17 +87,15 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
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 event_payload *ep;
struct cmd_find_state fs;
struct client *c = cmdq_get_client(item);
struct winlink *wl = target->wl;
struct window *w = wl->window;
struct session *s = target->s;
struct window_pane *wp = target->wp, *lastwp;
struct window_pane *wp = target->wp, *activewp, *lastwp, *markedwp;
struct options *oo = wp->options;
char *title;
const char *style;
struct options_entry *o;
int visible, Zflag = args_has(args, 'Z');
if (entry == &cmd_last_pane_entry || args_has(args, 'l')) {
/*
@@ -168,7 +103,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
* spawned without being visited (for example split-window -d).
*/
lastwp = TAILQ_FIRST(&w->last_panes);
if (lastwp == NULL && window_count_panes(w, 1) == 2) {
if (lastwp == NULL && window_count_panes(w) == 2) {
lastwp = TAILQ_PREV(w->active, window_panes, entry);
if (lastwp == NULL)
lastwp = TAILQ_NEXT(w->active, entry);
@@ -186,25 +121,47 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
server_redraw_window_borders(lastwp->window);
server_status_window(lastwp->window);
} else {
if (w->modal != NULL && lastwp != w->modal)
visible = 1;
else
visible = window_pane_is_visible(lastwp);
if (!visible && window_push_zoom(w, 0, Zflag))
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)) {
cmd_find_from_winlink(current, wl, 0);
cmd_select_pane_redraw(w);
}
if (!visible && window_pop_zoom(w))
if (window_pop_zoom(w))
server_redraw_window(w);
}
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'm') || args_has(args, 'M'))
return (cmd_select_pane_marked_pane(self, item));
if (args_has(args, 'm') || args_has(args, 'M')) {
if (args_has(args, 'm') && !window_pane_visible(wp))
return (CMD_RETURN_NORMAL);
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();
else
server_set_marked(s, wl, wp);
markedwp = marked_pane.wp;
if (lastwp != NULL) {
lastwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|
PANE_THEMECHANGED);
server_redraw_window_borders(lastwp->window);
server_status_window(lastwp->window);
}
if (markedwp != NULL) {
markedwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|
PANE_THEMECHANGED);
server_redraw_window_borders(markedwp->window);
server_status_window(markedwp->window);
}
return (CMD_RETURN_NORMAL);
}
style = args_get(args, 'P');
if (style != NULL) {
@@ -256,14 +213,8 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'T')) {
title = format_single_from_target(item, args_get(args, 'T'));
if (screen_set_title(&wp->base, title, 0)) {
ep = event_payload_create();
cmd_find_from_pane(&fs, wp, 0);
event_payload_set_target(ep, &fs);
event_payload_set_pane(ep, "pane", wp);
event_payload_set_window(ep, "window", wp->window);
event_payload_set_string(ep, "new_title", "%s", title);
events_fire("pane-title-changed", ep);
if (screen_set_title(&wp->base, title)) {
notify_pane("pane-title-changed", wp);
server_redraw_window_borders(wp->window);
server_status_window(wp->window);
}
@@ -271,20 +222,22 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
if (wp == w->active)
return (CMD_RETURN_NORMAL);
if (w->modal != NULL && wp != w->modal)
visible = 1;
if (c != NULL && c->session != NULL && (c->flags & CLIENT_ACTIVEPANE))
activewp = server_client_get_pane(c);
else
visible = window_pane_is_visible(wp);
if (!visible && window_push_zoom(w, 0, Zflag))
activewp = w->active;
if (wp == activewp)
return (CMD_RETURN_NORMAL);
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);
if (!visible && window_pop_zoom(w))
if (window_pop_zoom(w))
server_redraw_window(w);
return (CMD_RETURN_NORMAL);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-select-window.c,v 1.30 2021/08/21 10:22:39 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-send-keys.c,v 1.81 2026/06/11 19:13:34 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -39,8 +39,7 @@ const struct cmd_entry cmd_send_keys_entry = {
.target = { 't', CMD_FIND_PANE, 0 },
.flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG|CMD_CLIENT_CANFAIL|
CMD_READONLY,
.flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG|CMD_CLIENT_CANFAIL,
.exec = cmd_send_keys_exec
};
@@ -70,7 +69,6 @@ cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after,
struct key_table *table = NULL;
struct key_binding *bd;
struct key_event *event;
struct cmdq_item *new_after = after;
if (args_has(args, 'K')) {
if (tc == NULL)
@@ -78,16 +76,10 @@ cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after,
event = xcalloc(1, sizeof *event);
event->key = key|KEYC_SENT;
memset(&event->m, 0, sizeof event->m);
if (after == NULL) {
if (server_client_handle_key(tc, event) != 0)
return (item);
} else {
if (server_client_handle_key_after(tc, event, after,
&new_after) != 0)
return (new_after);
}
if (server_client_handle_key(tc, event) == 0) {
free(event->buf);
free(event);
}
return (item);
}
@@ -175,11 +167,6 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
u_int count = args_count(args);
char *cause = NULL;
if (tc != NULL && tc->flags & CLIENT_READONLY && !args_has(args, 'X')) {
cmdq_error(item, "client is read-only");
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'N')) {
np = args_strtonum_and_expand(args, 'N', 1, UINT_MAX, item,
&cause);
@@ -236,10 +223,8 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
if (count == 0) {
if (args_has(args, 'N') || args_has(args, 'R'))
return (CMD_RETURN_NORMAL);
after = args_has(args, 'K') ? item : NULL;
for (; np != 0; np--)
after = cmd_send_keys_inject_key(item, after, args,
event->key);
cmd_send_keys_inject_key(item, NULL, args, event->key);
return (CMD_RETURN_NORMAL);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-server-access.c,v 1.6 2026/06/09 12:58:40 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2021 Dallas Lyons <dallasdlyons@gmail.com>
@@ -19,7 +19,6 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <grp.h>
#include <pwd.h>
#include <stdio.h>
#include <string.h>
@@ -38,71 +37,66 @@ const struct cmd_entry cmd_server_access_entry = {
.name = "server-access",
.alias = NULL,
.args = { "adglrw", 0, 1, NULL },
.usage = "[-adglrw] " CMD_TARGET_PANE_USAGE " [user|group]",
.args = { "adlrw", 0, 1, NULL },
.usage = "[-adlrw] " CMD_TARGET_PANE_USAGE " [user]",
.flags = CMD_CLIENT_CANFAIL,
.exec = cmd_server_access_exec
};
static enum cmd_retval
cmd_server_access_deny(struct cmdq_item *item, id_t id, int flags,
const char *type, const char *name)
cmd_server_access_deny(struct cmdq_item *item, struct passwd *pw)
{
if (!server_acl_find(id, flags)) {
cmdq_error(item, "%s %s not found", type, name);
struct client *loop;
struct server_acl_user *user;
uid_t uid;
if ((user = server_acl_user_find(pw->pw_uid)) == NULL) {
cmdq_error(item, "user %s not found", pw->pw_name);
return (CMD_RETURN_ERROR);
}
server_acl_deny(id, flags);
TAILQ_FOREACH(loop, &clients, entry) {
uid = proc_get_peer_uid(loop->peer);
if (uid == server_acl_get_uid(user)) {
loop->exit_message = xstrdup("access not allowed");
loop->flags |= CLIENT_EXIT;
}
}
server_acl_user_deny(pw->pw_uid);
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_server_access_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct client *c = cmdq_get_target_client(item);
char *arg;
const char *name = NULL, *type;
struct passwd *pw;
struct group *gr;
id_t id;
int flags = 0;
char *name;
struct passwd *pw = NULL;
if (args_has(args, 'l')) {
server_acl_display(item);
return (CMD_RETURN_NORMAL);
}
if (args_count(args) == 0) {
cmdq_error(item, "missing user or group argument");
cmdq_error(item, "missing user argument");
return (CMD_RETURN_ERROR);
}
arg = format_single(item, args_string(args, 0), c, NULL, NULL, NULL);
if (args_has(args, 'g')) {
type = "group";
if ((gr = getgrnam(arg)) != NULL) {
id = gr->gr_gid;
name = gr->gr_name;
flags |= SERVER_ACL_IS_GROUP;
}
} else {
type = "user";
if ((pw = getpwnam(arg)) != NULL) {
id = pw->pw_uid;
name = pw->pw_name;
}
}
if (name == NULL) {
cmdq_error(item, "unknown %s: %s", type, arg);
free(arg);
name = format_single(item, args_string(args, 0), c, NULL, NULL, NULL);
if (*name != '\0')
pw = getpwnam(name);
if (pw == NULL) {
cmdq_error(item, "unknown user: %s", name);
return (CMD_RETURN_ERROR);
}
free(arg);
free(name);
if ((~flags & SERVER_ACL_IS_GROUP) && (id == 0 || id == getuid())) {
if (pw->pw_uid == 0 || pw->pw_uid == getuid()) {
cmdq_error(item, "%s owns the server, can't change access",
name);
pw->pw_name);
return (CMD_RETURN_ERROR);
}
@@ -116,35 +110,36 @@ cmd_server_access_exec(struct cmd *self, struct cmdq_item *item)
}
if (args_has(args, 'd'))
return (cmd_server_access_deny(item, id, flags, type, name));
return (cmd_server_access_deny(item, pw));
if (args_has(args, 'a')) {
if (server_acl_find(id, flags)) {
cmdq_error(item, "%s %s is already added", type, name);
if (server_acl_user_find(pw->pw_uid) != NULL) {
cmdq_error(item, "user %s is already added",
pw->pw_name);
return (CMD_RETURN_ERROR);
}
server_acl_allow(id, flags);
server_acl_user_allow(pw->pw_uid);
/* Do not return - allow -r or -w with -a. */
} else if (args_has(args, 'r') || args_has(args, 'w')) {
/* -r or -w implies -a if the entry does not exist. */
if (!server_acl_find(id, flags))
server_acl_allow(id, flags);
/* -r or -w implies -a if user does not exist. */
if (server_acl_user_find(pw->pw_uid) == NULL)
server_acl_user_allow(pw->pw_uid);
}
if (args_has(args, 'w')) {
if (!server_acl_find(id, flags)) {
cmdq_error(item, "%s %s not found", type, name);
if (server_acl_user_find(pw->pw_uid) == NULL) {
cmdq_error(item, "user %s not found", pw->pw_name);
return (CMD_RETURN_ERROR);
}
server_acl_allow_write(id, flags);
server_acl_user_allow_write(pw->pw_uid);
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'r')) {
if (!server_acl_find(id, flags)) {
cmdq_error(item, "%s %s not found", type, name);
if (server_acl_user_find(pw->pw_uid) == NULL) {
cmdq_error(item, "user %s not found", pw->pw_name);
return (CMD_RETURN_ERROR);
}
server_acl_deny_write(id, flags);
server_acl_user_deny_write(pw->pw_uid);
return (CMD_RETURN_NORMAL);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-set-buffer.c,v 1.38 2026/08/24 07:05:23 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -57,30 +57,30 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct client *tc = cmdq_get_target_client(item);
struct paste_buffer *pb = NULL;
char *bufname = NULL, *bufdata = NULL, *cause = NULL;
const char *olddata;
size_t bufsize = 0, newsize;
struct paste_buffer *pb;
char *bufdata, *cause;
const char *bufname, *olddata;
size_t bufsize, newsize;
if (args_get(args, 'b') != NULL) {
bufname = xstrdup(args_get(args, 'b'));
bufname = args_get(args, 'b');
if (bufname == NULL)
pb = NULL;
else
pb = paste_get_name(bufname);
}
if (cmd_get_entry(self) == &cmd_delete_buffer_entry) {
if (pb == NULL) {
if (bufname != NULL) {
cmdq_error(item, "unknown buffer: %s", bufname);
goto fail;
return (CMD_RETURN_ERROR);
}
pb = paste_get_top(&bufname);
}
if (pb == NULL) {
cmdq_error(item, "no buffer");
goto fail;
return (CMD_RETURN_ERROR);
}
paste_free(pb);
free(bufname);
return (CMD_RETURN_NORMAL);
}
@@ -88,30 +88,31 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
if (pb == NULL) {
if (bufname != NULL) {
cmdq_error(item, "unknown buffer: %s", bufname);
goto fail;
return (CMD_RETURN_ERROR);
}
pb = paste_get_top(&bufname);
}
if (pb == NULL) {
cmdq_error(item, "no buffer");
goto fail;
return (CMD_RETURN_ERROR);
}
if (paste_rename(bufname, args_get(args, 'n'), &cause) != 0) {
cmdq_error(item, "%s", cause);
goto fail;
free(cause);
return (CMD_RETURN_ERROR);
}
free(bufname);
return (CMD_RETURN_NORMAL);
}
if (args_count(args) != 1) {
cmdq_error(item, "no data specified");
goto fail;
return (CMD_RETURN_ERROR);
}
if ((newsize = strlen(args_string(args, 0))) == 0) {
free(bufname);
if ((newsize = strlen(args_string(args, 0))) == 0)
return (CMD_RETURN_NORMAL);
}
bufsize = 0;
bufdata = NULL;
if (args_has(args, 'a') && pb != NULL) {
olddata = paste_buffer_data(pb, &bufsize);
@@ -125,17 +126,12 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
if (paste_set(bufdata, bufsize, bufname, &cause) != 0) {
cmdq_error(item, "%s", cause);
goto fail;
free(bufdata);
free(cause);
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'w') && tc != NULL)
tty_set_selection(&tc->tty, "", bufdata, bufsize);
free(bufname);
return (CMD_RETURN_NORMAL);
fail:
free(bufdata);
free(bufname);
free(cause);
return (CMD_RETURN_ERROR);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-set-environment.c,v 1.29 2025/04/09 06:27:43 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-set-option.c,v 1.146 2026/07/10 15:20:06 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -31,10 +31,6 @@ 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 *);
static enum cmd_retval cmd_set_hook_event_exec(struct cmd *,
struct cmdq_item *);
static enum cmd_retval cmd_set_hook_monitor_exec(struct cmdq_item *,
struct args *, int);
const struct cmd_entry cmd_set_option_entry = {
.name = "set-option",
@@ -66,9 +62,8 @@ const struct cmd_entry cmd_set_hook_entry = {
.name = "set-hook",
.alias = NULL,
.args = { "agpERTt:uB:w", 0, 2, cmd_set_option_args_parse },
.usage = "[-agpERTuw] [-B name:what:format] " CMD_TARGET_PANE_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_PANE, CMD_FIND_CANFAIL },
@@ -77,152 +72,14 @@ const struct cmd_entry cmd_set_hook_entry = {
};
static enum args_parse_type
cmd_set_option_args_parse(struct args *args, u_int idx,
cmd_set_option_args_parse(__unused struct args *args, u_int idx,
__unused char **cause)
{
if (args_has(args, 'B'))
return (ARGS_PARSE_COMMANDS_OR_STRING);
if (idx == 1)
return (ARGS_PARSE_COMMANDS_OR_STRING);
return (ARGS_PARSE_STRING);
}
static enum cmd_retval
cmd_set_hook_event_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 event_payload *ep;
struct client *c;
char *argument;
if (args_count(args) == 0) {
cmdq_error(item, "missing argument");
return (CMD_RETURN_ERROR);
}
if (args_count(args) != 1) {
cmdq_error(item, "too many arguments");
return (CMD_RETURN_ERROR);
}
argument = format_single_from_target(item, args_string(args, 0));
if (*argument != '@') {
cmdq_error(item, "event name must start with @");
free(argument);
return (CMD_RETURN_ERROR);
}
ep = event_payload_create();
event_payload_set_target(ep, target);
c = cmdq_get_client(item);
if (c != NULL)
event_payload_set_client(ep, "client", c);
if (target->s != NULL)
event_payload_set_session(ep, "session", target->s);
if (target->w != NULL)
event_payload_set_window(ep, "window", target->w);
if (target->wl != NULL)
event_payload_set_int(ep, "window_index", target->wl->idx);
else if (target->idx != -1)
event_payload_set_int(ep, "window_index", target->idx);
if (target->wp != NULL)
event_payload_set_pane(ep, "pane", target->wp);
events_fire(argument, ep);
free(argument);
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_set_hook_monitor_exec(struct cmdq_item *item, struct args *args, int window)
{
struct cmd_find_state *target = cmdq_get_target(item), fs;
struct options *oo;
struct options_entry *o;
struct session *s = NULL;
char *cause = NULL, *name = NULL, *format = NULL;
char *expanded = NULL, *newvalue = NULL;
const char *value, *old;
enum monitor_type type;
int id, scope, flags = 0;
if (args_count(args) > 1) {
cmdq_error(item, "too many arguments");
return (CMD_RETURN_ERROR);
}
value = args_get(args, 'B');
if (args_has(args, 'u')) {
if (monitor_parse(value, &name, &type, &id, &format) != 0)
name = xstrdup(value);
free(format);
format = NULL;
} else {
if (monitor_parse(value, &name, &type, &id, &format) != 0) {
cmdq_error(item, "invalid subscription: %s", value);
return (CMD_RETURN_ERROR);
}
}
if (*name != '@') {
cmdq_error(item, "monitor hook name must start with @");
goto fail;
}
scope = options_scope_from_name(args, window, name, target, &oo,
&cause);
if (scope == OPTIONS_TABLE_NONE) {
cmdq_error(item, "%s", cause);
free(cause);
goto fail;
}
cmd_find_copy_state(&fs, target);
if (args_has(args, 'u')) {
hooks_monitor_remove(oo, name);
goto out;
}
if (args_count(args) != 0) {
value = args_string(args, 0);
if (args_has(args, 'F')) {
expanded = format_single_from_target(item, value);
value = expanded;
}
o = options_get_only(oo, name);
if (!args_has(args, 'o') || o == NULL) {
if (args_has(args, 'a') && o != NULL) {
old = options_get_string(oo, name);
xasprintf(&newvalue, "%s%s", old, value);
value = newvalue;
}
options_set_string(oo, name, 0, "%s", value);
options_push_changes(name);
}
}
if (oo != global_options &&
oo != global_s_options &&
oo != global_w_options)
s = target->s;
if (args_has(args, 'T'))
flags |= MONITOR_NOTIFY_TRUE;
hooks_monitor_add(item, oo, name, type, id, format, flags, &fs, s);
out:
free(newvalue);
free(expanded);
free(name);
free(format);
return (CMD_RETURN_NORMAL);
fail:
free(newvalue);
free(expanded);
free(name);
free(format);
return (CMD_RETURN_ERROR);
}
static enum cmd_retval
cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
{
@@ -232,34 +89,26 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
struct window_pane *loop;
struct options *oo;
struct options_entry *parent, *o, *po;
char *name, *argument, *cause;
char *expanded = NULL, *array_key = NULL;
char *name, *argument, *expanded = NULL;
char *cause;
const char *value;
int window, already, error, ambiguous;
int window, idx, already, error, ambiguous;
int scope;
window = (cmd_get_entry(self) == &cmd_set_window_option_entry);
if (cmd_get_entry(self) == &cmd_set_hook_entry && args_has(args, 'E'))
return (cmd_set_hook_event_exec(self, item));
if (cmd_get_entry(self) == &cmd_set_hook_entry && args_has(args, 'B'))
return (cmd_set_hook_monitor_exec(item, args, window));
if (args_count(args) == 0) {
cmdq_error(item, "missing argument");
return (CMD_RETURN_ERROR);
}
/* Expand argument. */
argument = format_single_from_target(item, args_string(args, 0));
/* If set-hook -R, fire the hook straight away. */
if (cmd_get_entry(self) == &cmd_set_hook_entry && args_has(args, 'R')) {
hooks_run(item, argument);
notify_hook(item, argument);
free(argument);
return (CMD_RETURN_NORMAL);
}
/* Parse option name and array key. */
name = options_match(argument, &array_key, &ambiguous);
/* Parse option name and index. */
name = options_match(argument, &idx, &ambiguous);
if (name == NULL) {
if (args_has(args, 'q'))
goto out;
@@ -291,23 +140,21 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
o = options_get_only(oo, name);
parent = options_get(oo, name);
/* Check that array options and keys match up. */
if (array_key != NULL && (*name == '@' || !options_is_array(parent))) {
/* Check that array options and indexes match up. */
if (idx != -1 && (*name == '@' || !options_is_array(parent))) {
cmdq_error(item, "not an array: %s", argument);
goto fail;
}
/* With -o, check this option is not already set. */
if (!args_has(args, 'u') && args_has(args, 'o')) {
if (array_key == NULL)
if (idx == -1)
already = (o != NULL);
else {
if (o == NULL)
already = 0;
else if (options_array_get(o, array_key) != NULL)
already = 1;
else
already = 0;
already = (options_array_get(o, idx) != NULL);
}
if (already) {
if (args_has(args, 'q'))
@@ -323,8 +170,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
po = options_get_only(loop->options, name);
if (po == NULL)
continue;
if (options_remove_or_default(po, array_key,
&cause) != 0) {
if (options_remove_or_default(po, idx, &cause) != 0) {
cmdq_error(item, "%s", cause);
free(cause);
goto fail;
@@ -334,7 +180,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'u') || args_has(args, 'U')) {
if (o == NULL)
goto out;
if (options_remove_or_default(o, array_key, &cause) != 0) {
if (options_remove_or_default(o, idx, &cause) != 0) {
cmdq_error(item, "%s", cause);
free(cause);
goto fail;
@@ -345,9 +191,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
goto fail;
}
options_set_string(oo, name, append, "%s", value);
if (cmd_get_entry(self) == &cmd_set_hook_entry)
hooks_add_event(name);
} else if (array_key == NULL && !options_is_array(parent)) {
} 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);
@@ -363,7 +207,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
}
if (o == NULL)
o = options_empty(oo, options_table_entry(parent));
if (array_key == NULL) {
if (idx == -1) {
if (!append)
options_array_clear(o);
if (options_array_assign(o, value, &cause) != 0) {
@@ -371,7 +215,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
free(cause);
goto fail;
}
} else if (options_array_set(o, array_key, value, append,
} else if (options_array_set(o, idx, value, append,
&cause) != 0) {
cmdq_error(item, "%s", cause);
free(cause);
@@ -385,13 +229,11 @@ out:
free(argument);
free(expanded);
free(name);
free(array_key);
return (CMD_RETURN_NORMAL);
fail:
free(argument);
free(expanded);
free(name);
free(array_key);
return (CMD_RETURN_ERROR);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-show-environment.c,v 1.29 2025/04/09 06:27:43 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-show-messages.c,v 1.37 2025/11/18 08:37:54 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-show-options.c,v 1.76 2026/07/27 19:15:58 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -27,23 +27,10 @@
* Show options.
*/
#define SHOW_OPTIONS_TEMPLATE \
"#{?option_value_only," \
"#{option_value}," \
"#{option_name}#{?option_has_array_key," \
"[#{option_array_key}],}" \
"#{?option_is_parent,*,}" \
"#{?option_has_value, " \
"#{?option_is_string,#{q/a:option_value},#{option_value}},}}"
#define SHOW_HOOKS_MONITOR_TEMPLATE \
"#{option_name}:#{hook_monitor_target}:#{hook_monitor_format}"
static enum cmd_retval cmd_show_options_exec(struct cmd *, struct cmdq_item *);
static void cmd_show_options_print(struct cmd *, struct cmdq_item *,
struct options_entry *, const char *, int);
static void cmd_show_hooks_print_monitor(struct cmd *,
struct cmdq_item *, struct options_entry *);
struct options_entry *, int, int);
static enum cmd_retval cmd_show_options_all(struct cmd *, struct cmdq_item *,
int, struct options *);
@@ -51,8 +38,8 @@ const struct cmd_entry cmd_show_options_entry = {
.name = "show-options",
.alias = "show",
.args = { "AgF:Hpqst:vw", 0, 1, NULL },
.usage = "[-AgHpqsvw] [-F format] " CMD_TARGET_PANE_USAGE " [option]",
.args = { "AgHpqst:vw", 0, 1, NULL },
.usage = "[-AgHpqsvw] " CMD_TARGET_PANE_USAGE " [option]",
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
@@ -64,8 +51,8 @@ const struct cmd_entry cmd_show_window_options_entry = {
.name = "show-window-options",
.alias = "showw",
.args = { "F:gvt:", 0, 1, NULL },
.usage = "[-gv] [-F format] " CMD_TARGET_WINDOW_USAGE " [option]",
.args = { "gvt:", 0, 1, NULL },
.usage = "[-gv] " CMD_TARGET_WINDOW_USAGE " [option]",
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL },
@@ -77,8 +64,8 @@ const struct cmd_entry cmd_show_hooks_entry = {
.name = "show-hooks",
.alias = NULL,
.args = { "BF:gpt:w", 0, 1, NULL },
.usage = "[-Bgpw] [-F format] " CMD_TARGET_PANE_USAGE " [hook]",
.args = { "gpt:w", 0, 1, NULL },
.usage = "[-gpw] " CMD_TARGET_PANE_USAGE " [hook]",
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
@@ -93,8 +80,7 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
struct cmd_find_state *target = cmdq_get_target(item);
struct options *oo;
char *argument, *name = NULL, *cause;
char *array_key = NULL;
int window, ambiguous, parent, print_parent, scope;
int window, idx, ambiguous, parent, scope;
struct options_entry *o;
window = (cmd_get_entry(self) == &cmd_show_window_options_entry);
@@ -109,20 +95,11 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
free(cause);
return (CMD_RETURN_ERROR);
}
if (cmd_get_entry(self) == &cmd_show_hooks_entry &&
args_has(args, 'B')) {
o = options_first(oo);
while (o != NULL) {
cmd_show_hooks_print_monitor(self, item, o);
o = options_next(o);
}
return (CMD_RETURN_NORMAL);
}
return (cmd_show_options_all(self, item, scope, oo));
}
argument = format_single_from_target(item, args_string(args, 0));
name = options_match(argument, &array_key, &ambiguous);
name = options_match(argument, &idx, &ambiguous);
if (name == NULL) {
if (args_has(args, 'q'))
goto out;
@@ -147,20 +124,9 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
parent = 1;
} else
parent = 0;
if (o != NULL) {
if (cmd_get_entry(self) == &cmd_show_hooks_entry &&
args_has(args, 'B'))
cmd_show_hooks_print_monitor(self, item, o);
else {
print_parent = parent;
if (array_key == NULL && options_is_array(o) &&
options_array_first(o) == NULL) {
print_parent = 0;
}
cmd_show_options_print(self, item, o, array_key,
print_parent);
}
} else if (*name == '@') {
if (o != NULL)
cmd_show_options_print(self, item, o, idx, parent);
else if (*name == '@') {
if (args_has(args, 'q'))
goto out;
cmdq_error(item, "invalid option: %s", argument);
@@ -169,171 +135,64 @@ cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
out:
free(name);
free(array_key);
free(argument);
return (CMD_RETURN_NORMAL);
fail:
free(name);
free(array_key);
free(argument);
return (CMD_RETURN_ERROR);
}
static void
cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
struct options_entry *o, const char *array_key, int parent)
struct options_entry *o, int idx, int parent)
{
struct args *args = cmd_get_args(self);
struct options_array_item *a;
struct format_tree *ft;
const char *name = options_name(o);
const char *template = args_get(args, 'F');
char *value, *line;
struct timeval tv = { 0 };
u_int fire_count;
time_t fire_time;
int is_hook = 0, is_user = 0;
int has_value = 1;
const struct options_table_entry *oe = options_table_entry(o);
char *value, *tmp = NULL, *escaped;
if (array_key != NULL)
value = options_to_string(o, array_key, 0);
else if (options_is_array(o)) {
if (idx != -1) {
xasprintf(&tmp, "%s[%d]", name, idx);
name = tmp;
} else {
if (options_is_array(o)) {
a = options_array_first(o);
if (a != NULL) {
if (a == NULL) {
if (!args_has(args, 'v'))
cmdq_print(item, "%s", name);
return;
}
while (a != NULL) {
array_key = options_array_item_key(a);
cmd_show_options_print(self, item, o, array_key,
idx = options_array_item_index(a);
cmd_show_options_print(self, item, o, idx,
parent);
a = options_array_next(a);
}
return;
}
if (template == NULL && args_has(args, 'v'))
return;
value = xstrdup("");
has_value = 0;
} else
value = options_to_string(o, NULL, 0);
if (template == NULL)
template = SHOW_OPTIONS_TEMPLATE;
if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_HOOK))
is_hook = 1;
else if (oe == NULL)
is_user = 1;
ft = format_create_from_target(item);
format_add(ft, "option_name", "%s", name);
format_add(ft, "option_value", "%s", value);
format_add(ft, "option_value_only", "%d", args_has(args, 'v'));
format_add(ft, "option_is_parent", "%d", parent);
format_add(ft, "option_is_array", "%d", options_is_array(o));
format_add(ft, "option_is_string", "%d", options_is_string(o));
format_add(ft, "option_is_hook", "%d", is_hook);
format_add(ft, "option_is_user", "%d", is_user);
format_add(ft, "option_has_value", "%d", has_value);
if (cmd_get_entry(self) == &cmd_show_hooks_entry) {
fire_count = options_get_fire_count(o);
format_add(ft, "hook_fire_count", "%u", fire_count);
fire_time = options_get_fire_time(o);
if (fire_time != 0) {
tv.tv_sec = fire_time;
format_add_tv(ft, "hook_fire_time", &tv);
}
}
if (array_key != NULL) {
format_add(ft, "option_array_key", "%s", array_key);
format_add(ft, "option_has_array_key", "1");
value = options_to_string(o, idx, 0);
if (args_has(args, 'v'))
cmdq_print(item, "%s", value);
else if (options_is_string(o)) {
escaped = args_escape(value);
if (parent)
cmdq_print(item, "%s* %s", name, escaped);
else
cmdq_print(item, "%s %s", name, escaped);
free(escaped);
} else {
format_add(ft, "option_array_key", "%s", "");
format_add(ft, "option_has_array_key", "0");
if (parent)
cmdq_print(item, "%s* %s", name, value);
else
cmdq_print(item, "%s %s", name, value);
}
line = format_expand(ft, template);
format_free(ft);
cmdq_print(item, "%s", line);
free(line);
free(value);
}
static void
cmd_show_hooks_print_monitor(struct cmd *self, struct cmdq_item *item,
struct options_entry *o)
{
struct args *args = cmd_get_args(self);
struct format_tree *ft;
enum monitor_type type;
const char *template = args_get(args, 'F'), *format;
char *value, *target, *line;
struct timeval tv = { 0 };
u_int fire_count;
time_t fire_time;
int id;
value = hooks_monitor_to_string(o);
if (value == NULL)
return;
if (!hooks_monitor_get(o, &type, &id, &format)) {
free(value);
return;
}
if (template == NULL)
template = SHOW_HOOKS_MONITOR_TEMPLATE;
switch (type) {
case MONITOR_SESSION:
target = xstrdup("");
break;
case MONITOR_PANE:
xasprintf(&target, "%%%d", id);
break;
case MONITOR_ALL_PANES:
target = xstrdup("%*");
break;
case MONITOR_WINDOW:
xasprintf(&target, "@%d", id);
break;
case MONITOR_ALL_WINDOWS:
target = xstrdup("@*");
break;
}
ft = format_create_from_target(item);
format_add(ft, "option_name", "%s", options_name(o));
format_add(ft, "option_value", "%s", value);
format_add(ft, "option_value_only", "%d", 0);
format_add(ft, "option_is_parent", "%d", 0);
format_add(ft, "option_is_array", "%d", 0);
format_add(ft, "option_is_string", "%d", 1);
format_add(ft, "option_is_hook", "%d", 1);
format_add(ft, "option_is_user", "%d", 1);
format_add(ft, "option_has_value", "%d", 1);
format_add(ft, "option_array_key", "%s", "");
format_add(ft, "option_has_array_key", "0");
format_add(ft, "hook_monitor_target", "%s", target);
format_add(ft, "hook_monitor_format", "%s", format);
fire_count = hooks_monitor_get_fire_count(o);
format_add(ft, "hook_fire_count", "%u", fire_count);
fire_time = hooks_monitor_get_fire_time(o);
if (fire_time != 0) {
tv.tv_sec = fire_time;
format_add_tv(ft, "hook_fire_time", &tv);
}
line = format_expand(ft, template);
format_free(ft);
cmdq_print(item, "%s", line);
free(line);
free(target);
free(value);
free(tmp);
}
static enum cmd_retval
@@ -343,30 +202,19 @@ cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope,
struct args *args = cmd_get_args(self);
const struct options_table_entry *oe;
struct options_entry *o;
struct options_array_item *a;
const char *name;
int parent, is_user_hook;
u_int idx;
int parent;
if (cmd_get_entry(self) != &cmd_show_hooks_entry) {
o = options_first(oo);
while (o != NULL) {
if (options_table_entry(o) == NULL) {
name = options_name(o);
is_user_hook = 0;
if (*name == '@') {
if (hooks_is_event(name) ||
options_get_monitor_data(o) != NULL) {
is_user_hook = 1;
}
}
if (cmd_get_entry(self) != &cmd_show_hooks_entry) {
if (!is_user_hook || args_has(args, 'H')) {
cmd_show_options_print(self, item, o,
NULL, 0);
}
} else if (is_user_hook)
cmd_show_options_print(self, item, o, NULL, 0);
}
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;
@@ -389,7 +237,24 @@ cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope,
} else
parent = 0;
cmd_show_options_print(self, item, o, NULL, parent);
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(args, 'v')) {
name = options_name(o);
if (parent)
cmdq_print(item, "%s*", name);
else
cmdq_print(item, "%s", name);
}
} else {
while (a != NULL) {
idx = options_array_item_index(a);
cmd_show_options_print(self, item, o, idx,
parent);
a = options_array_next(a);
}
}
}
return (CMD_RETURN_NORMAL);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-show-prompt-history.c,v 1.5 2026/06/25 11:39:11 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2021 Anindya Mukherjee <anindya49@hotmail.com>
@@ -55,44 +55,51 @@ 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 t, h;
const char *v;
u_int tidx, hidx;
if (cmd_get_entry(self) == &cmd_clear_prompt_history_entry) {
if (typestr == NULL) {
for (t = 0; t < PROMPT_NTYPES; t++)
prompt_history_clear(t);
for (tidx = 0; tidx < PROMPT_NTYPES; tidx++) {
free(status_prompt_hlist[tidx]);
status_prompt_hlist[tidx] = NULL;
status_prompt_hsize[tidx] = 0;
}
} else {
type = prompt_type(typestr);
type = status_prompt_type(typestr);
if (type == PROMPT_TYPE_INVALID) {
cmdq_error(item, "invalid type: %s", typestr);
return (CMD_RETURN_ERROR);
}
prompt_history_clear(type);
free(status_prompt_hlist[type]);
status_prompt_hlist[type] = NULL;
status_prompt_hsize[type] = 0;
}
return (CMD_RETURN_NORMAL);
}
if (typestr == NULL) {
for (t = 0; t < PROMPT_NTYPES; t++) {
typestr = prompt_type_string(t);
cmdq_print(item, "History for %s:\n", typestr);
for (h = 0; h < prompt_history_size(t); h++) {
v = prompt_history_get(t, h);
cmdq_print(item, "%d: %s", h + 1, v);
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 = prompt_type(typestr);
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", prompt_type_string(type));
for (h = 0; h < prompt_history_size(type); h++) {
v = prompt_history_get(type, h);
cmdq_print(item, "%d: %s", h + 1, v);
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", "");
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-source-file.c,v 1.63 2026/08/18 08:05:05 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2008 Tiago Cunha <me@tiagocunha.org>
@@ -50,7 +50,6 @@ const struct cmd_entry cmd_source_file_entry = {
struct cmd_source_file_data {
struct cmdq_item *item;
struct client *client;
int flags;
struct cmdq_item *after;
@@ -61,24 +60,10 @@ struct cmd_source_file_data {
u_int nfiles;
};
static void
cmd_source_file_free_data(struct cmd_source_file_data *cdata)
{
u_int i;
for (i = 0; i < cdata->nfiles; i++)
free(cdata->files[i]);
free(cdata->files);
if (cdata->client != NULL)
server_client_unref(cdata->client);
free(cdata);
}
static enum cmd_retval
cmd_source_file_complete_cb(struct cmdq_item *item, void *data)
cmd_source_file_complete_cb(struct cmdq_item *item, __unused void *data)
{
struct cmd_source_file_data *cdata = data;
struct client *c = cdata->client;
struct client *c = cmdq_get_client(item);
if (c == NULL) {
cmd_source_file_depth--;
@@ -89,36 +74,36 @@ cmd_source_file_complete_cb(struct cmdq_item *item, void *data)
}
cfg_print_causes(item);
cmd_source_file_free_data(cdata);
return (CMD_RETURN_NORMAL);
}
static void
cmd_source_file_complete(struct cmd_source_file_data *cdata)
cmd_source_file_complete(struct client *c, struct cmd_source_file_data *cdata)
{
struct client *c = cdata->client;
struct cmdq_item *new_item;
u_int i;
if (!cfg_finished) {
cmd_source_file_free_data(cdata);
return;
}
if (cfg_finished) {
if (cdata->retval == CMD_RETURN_ERROR &&
c != NULL &&
c->session == NULL)
c->retval = 1;
new_item = cmdq_get_callback(cmd_source_file_complete_cb, cdata);
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);
}
static void
cmd_source_file_done(__unused struct client *oc, const char *path,
int error, int closed, struct evbuffer *buffer, void *data)
cmd_source_file_done(struct client *c, const char *path, int error,
int closed, struct evbuffer *buffer, void *data)
{
struct cmd_source_file_data *cdata = data;
struct cmdq_item *item = cdata->item;
struct client *c = cdata->client;
void *bdata = EVBUFFER_DATA(buffer);
size_t bsize = EVBUFFER_LENGTH(buffer);
u_int n;
@@ -131,7 +116,7 @@ cmd_source_file_done(__unused struct client *oc, const char *path,
if (error != 0)
cmdq_error(item, "%s: %s", strerror(error), path);
else if (bsize != 0) {
if (load_cfg_from_buffer(bdata, bsize, path, cdata->after,
if (load_cfg_from_buffer(bdata, bsize, path, c, cdata->after,
target, cdata->flags, &new_item) < 0)
cdata->retval = CMD_RETURN_ERROR;
else if (new_item != NULL)
@@ -142,7 +127,7 @@ cmd_source_file_done(__unused struct client *oc, const char *path,
if (n < cdata->nfiles)
file_read(c, cdata->files[n], cmd_source_file_done, cdata);
else {
cmd_source_file_complete(cdata);
cmd_source_file_complete(c, cdata);
cmdq_continue(item);
}
}
@@ -202,9 +187,6 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
cdata = xcalloc(1, sizeof *cdata);
cdata->item = item;
cdata->client = c;
if (c != NULL)
c->references++;
if (args_has(args, 'q'))
cdata->flags |= CMD_PARSE_QUIET;
@@ -267,7 +249,7 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
file_read(c, cdata->files[0], cmd_source_file_done, cdata);
retval = CMD_RETURN_WAIT;
} else
cmd_source_file_complete(cdata);
cmd_source_file_complete(c, cdata);
free(cwd);
return (retval);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-split-window.c,v 1.150 2026/08/20 09:19:24 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -27,44 +27,22 @@
#include "tmux.h"
/*
* Create a new pane.
* Split a window (add a new pane).
*/
#define SPLIT_WINDOW_TEMPLATE "#{session_name}:#{window_index}.#{pane_index}"
static enum cmd_retval cmd_split_window_exec(struct cmd *, struct cmdq_item *);
static void cmd_split_window_mouse_resize(struct client *,
struct mouse_event *);
const struct cmd_entry cmd_new_pane_entry = {
.name = "new-pane",
.alias = "newp",
.args = { "AbB:Cc:de:EfF:hIkl:KLMm:Op:PR:s:S:t:T:vWx:X:y:Y:Z", 0, -1, NULL },
.usage = "[-AbCdefhIkKLMOPvWZ] [-B border-lines] "
"[-c start-directory] [-e environment] "
"[-F format] [-l size] [-m message] [-p percentage] "
"[-s style] [-S active-border-style] "
"[-R inactive-border-style] [-T title] [-x width] [-y height] "
"[-X x-position] [-Y y-position] " CMD_TARGET_PANE_USAGE " "
"[shell-command [argument ...]]",
.target = { 't', CMD_FIND_PANE, 0 },
.flags = 0,
.exec = cmd_split_window_exec
};
static enum cmd_retval cmd_split_window_exec(struct cmd *,
struct cmdq_item *);
const struct cmd_entry cmd_split_window_entry = {
.name = "split-window",
.alias = "splitw",
.args = { "bB:c:de:EfF:hIkl:m:p:PR:s:S:t:T:vWZ", 0, -1, NULL },
.usage = "[-bdefhIklPvWZ] [-B border-lines] [-c start-directory] "
"[-e environment] [-F format] [-l size] [-m message] "
"[-p percentage] [-s style] [-S active-border-style] "
"[-R inactive-border-style] [-T title] "
CMD_TARGET_PANE_USAGE " [shell-command [argument ...]]",
.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 [argument ...]]",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -83,105 +61,65 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
struct session *s = target->s;
struct winlink *wl = target->wl;
struct window *w = wl->window;
struct window_pane *wp = target->wp, *new_wp = NULL;
struct layout_cell *lc = NULL;
struct event_payload *ep;
struct window_pane *wp = target->wp, *new_wp;
enum layout_type type;
struct layout_cell *lc;
struct cmd_find_state fs;
struct key_event *event = cmdq_get_event(item);
int input, empty, is_floating, flags = 0;
int restore_zoom = 0;
const char *template, *style, *value;
char *cause = NULL, *cp, *title;
const struct options_table_entry *oe;
int size, flags, input;
const char *template;
char *cause = NULL, *cp;
struct args_value *av;
enum pane_lines lines;
u_int count = args_count(args);
u_int count = args_count(args), curval = 0;
if (window_active_pane_is_over_zoom(w))
restore_zoom = 1;
if (cmd_get_entry(self) == &cmd_new_pane_entry)
is_floating = !args_has(args, 'L');
else {
if (!window_pane_is_visible(wp))
restore_zoom = 0;
if (!restore_zoom)
window_unzoom(w, 1);
is_floating = window_pane_is_floating(wp);
flags |= SPAWN_SPLIT;
}
if (args_has(args, 'O')) {
if (!is_floating) {
cmdq_error(item, "modal pane must be floating");
return (CMD_RETURN_ERROR);
}
if (w->modal != NULL) {
cmdq_error(item, "window already has a modal pane");
return (CMD_RETURN_ERROR);
}
}
if (args_has(args, 'M') && is_floating) {
if (event == NULL || !event->m.valid || tc == NULL)
return (CMD_RETURN_NORMAL);
}
if (is_floating)
flags |= SPAWN_FLOATING;
type = LAYOUT_TOPBOTTOM;
if (args_has(args, 'h'))
flags |= SPAWN_HORIZONTAL;
type = LAYOUT_LEFTRIGHT;
/* If the 'p' flag is dropped then this bit can be moved into 'l'. */
if (args_has(args, 'l') || args_has(args, 'p')) {
if (args_has(args, 'f')) {
if (type == LAYOUT_TOPBOTTOM)
curval = w->sy;
else
curval = w->sx;
} else {
if (type == LAYOUT_TOPBOTTOM)
curval = wp->sy;
else
curval = wp->sx;
}
}
size = -1;
if (args_has(args, 'l')) {
size = args_percentage_and_expand(args, 'l', 0, INT_MAX, curval,
item, &cause);
} else if (args_has(args, 'p')) {
size = args_strtonum_and_expand(args, 'p', 0, 100, item,
&cause);
if (cause == NULL)
size = curval * size / 100;
}
if (cause != NULL) {
cmdq_error(item, "size %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
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 (args_has(args, 'd'))
flags |= SPAWN_DETACHED;
if (args_has(args, 'Z'))
flags |= SPAWN_ZOOM;
if (args_has(args, 'O'))
flags |= SPAWN_MODAL|SPAWN_FLOATOVERZOOM;
if (is_floating && args_has(args, 'A'))
flags |= SPAWN_FLOATOVERZOOM;
if ((w->flags & WINDOW_ZOOMED) && (flags & SPAWN_FLOATOVERZOOM))
restore_zoom = 1;
input = args_has(args, 'I');
if (input || (count == 1 && *args_string(args, 0) == '\0'))
empty = 1;
else
empty = args_has(args, 'E');
if (empty &&
count != 0 &&
(count != 1 || *args_string(args, 0) != '\0')) {
cmdq_error(item, "command cannot be given for empty pane");
return (CMD_RETURN_ERROR);
}
if (empty)
flags |= SPAWN_EMPTY;
if ((value = args_get(args, 'B')) == NULL)
lines = window_get_pane_lines(w);
else {
oe = options_search("pane-border-lines");
lines = options_find_choice(oe, value, &cause);
if (cause != NULL) {
cmdq_error(item, "pane-border-lines %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
}
if (flags & SPAWN_FLOATING) {
lc = layout_get_floating_cell(item, args, lines, w, wp, flags,
&cause);
} else
lc = layout_get_tiled_cell(item, args, w, wp, flags, &cause);
if (cause != NULL) {
cmdq_error(item, "%s", cause);
free(cause);
if (restore_zoom)
window_pop_zoom(w);
lc = layout_split_pane(wp, type, size, flags);
if (lc == NULL) {
cmdq_error(item, "no space for new pane");
return (CMD_RETURN_ERROR);
}
@@ -203,103 +141,43 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
sc.idx = -1;
sc.cwd = args_get(args, 'c');
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) {
cmdq_error(item, "create pane failed: %s", cause);
free(cause);
/*
* spawn_pane has already torn the half-built pane down (its
* fork-failure path removes the pane and destroys the layout
* cell), so new_wp is NULL and there is nothing for fail to do.
*/
goto fail;
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'K') && args_has(args, 'O'))
new_wp->flags |= PANE_CAPTUREALLKEYS;
if (args_has(args, 'C') && args_has(args, 'O'))
new_wp->flags |= PANE_CLOSEONCLICK;
style = args_get(args, 's');
if (style != NULL) {
if (options_set_string(new_wp->options, "window-style", 0,
"%s", style) == NULL) {
cmdq_error(item, "bad style: %s", style);
goto fail;
}
options_set_string(new_wp->options, "window-active-style", 0,
"%s", style);
new_wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|
PANE_THEMECHANGED);
}
style = args_get(args, 'S');
if (style != NULL) {
if (options_set_string(new_wp->options,
"pane-active-border-style", 0, "%s", style) == NULL) {
cmdq_error(item, "bad active border style: %s", style);
goto fail;
}
}
style = args_get(args, 'R');
if (style != NULL) {
if (options_set_string(new_wp->options, "pane-border-style", 0,
"%s", style) == NULL) {
cmdq_error(item, "bad inactive border style: %s",
style);
goto fail;
}
}
if (args_has(args, 'B'))
options_set_number(new_wp->options, "pane-border-lines", lines);
if (args_has(args, 'k') || args_has(args, 'm')) {
options_set_number(new_wp->options, "remain-on-exit", 3);
if (args_has(args, 'm')) {
options_set_string(new_wp->options,
"remain-on-exit-format", 0, "%s",
args_get(args, 'm'));
}
}
if (args_has(args, 'T')) {
title = format_single_from_target(item, args_get(args, 'T'));
screen_set_title(&new_wp->base, title, 0);
ep = event_payload_create();
cmd_find_from_pane(&fs, new_wp, 0);
event_payload_set_target(ep, &fs);
event_payload_set_pane(ep, "pane", new_wp);
event_payload_set_window(ep, "window", new_wp->window);
event_payload_set_string(ep, "new_title", "%s", title);
events_fire("pane-title-changed", ep);
free(title);
}
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);
goto fail;
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 (~flags & SPAWN_DETACHED)
if (!args_has(args, 'd'))
cmd_find_from_winlink_pane(current, wl, new_wp, 0);
if (restore_zoom) {
window_pop_zoom(wp->window);
server_redraw_window(wp->window);
} else if ((~flags & SPAWN_FLOATING) && !args_has(args, 'O')) {
window_pop_zoom(wp->window);
server_redraw_window(wp->window);
}
server_redraw_session(s);
if (args_has(args, 'M') && is_floating) {
tc->tty.mouse_last_pane = new_wp->id;
tc->tty.mouse_drag_update = cmd_split_window_mouse_resize;
cmd_split_window_mouse_resize(tc, &event->m);
}
server_status_session(s);
if (args_has(args, 'P')) {
if ((template = args_get(args, 'F')) == NULL)
@@ -317,106 +195,5 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
environ_free(sc.environ);
if (input)
return (CMD_RETURN_WAIT);
if (args_has(args, 'W')) {
/*
* With -W, block this command queue item until the pane's
* command exits; window_pane_wait_finish will be called to
* continue it.
*/
new_wp->wait_item = item;
return (CMD_RETURN_WAIT);
}
return (CMD_RETURN_NORMAL);
fail:
/*
* If the pane was spawned before we failed, tear it down here; this
* also destroys its layout cell. spawn_pane's own failure path has
* already done this, so new_wp is NULL in that case.
*/
if (new_wp != NULL) {
server_client_remove_pane(new_wp);
if (!is_floating)
layout_close_pane(new_wp);
window_remove_pane(wp->window, new_wp);
}
if (restore_zoom || (~flags & SPAWN_FLOATING))
window_pop_zoom(wp->window);
if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv);
environ_free(sc.environ);
return (CMD_RETURN_ERROR);
}
static void
cmd_split_window_mouse_resize(struct client *c, struct mouse_event *m)
{
struct window_pane *wp;
struct window *w;
struct layout_cell *lc;
enum pane_lines lines;
u_int sx, sy;
int x, y, xoff, yoff, border;
if (c->tty.mouse_last_pane == -1)
return;
wp = window_pane_find_by_id(c->tty.mouse_last_pane);
if (wp == NULL || !window_pane_is_floating(wp)) {
c->tty.mouse_drag_update = NULL;
return;
}
w = wp->window;
lc = wp->layout_cell;
x = m->x + m->ox;
y = m->y + m->oy;
if (m->statusat == 0 && y >= (int)m->statuslines)
y -= m->statuslines;
else if (m->statusat > 0 && y >= m->statusat)
y = m->statusat - 1;
lines = window_pane_get_pane_lines(wp);
border = (lines != PANE_LINES_NONE);
if (x >= (int)c->tty.mouse_drag_x) {
xoff = c->tty.mouse_drag_x + border;
sx = x - c->tty.mouse_drag_x + 1;
} else {
sx = c->tty.mouse_drag_x - x + 1;
xoff = c->tty.mouse_drag_x - sx + 1;
if (border)
xoff++;
}
if (y >= (int)c->tty.mouse_drag_y) {
yoff = c->tty.mouse_drag_y + border;
sy = y - c->tty.mouse_drag_y + 1;
} else {
sy = c->tty.mouse_drag_y - y + 1;
yoff = c->tty.mouse_drag_y - sy + 1;
if (border)
yoff++;
}
if (border) {
if (sx <= 2)
sx = PANE_MINIMUM;
else
sx -= 2;
if (sy <= 2)
sy = PANE_MINIMUM;
else
sy -= 2;
}
if (sx < PANE_MINIMUM)
sx = PANE_MINIMUM;
if (sy < PANE_MINIMUM)
sy = PANE_MINIMUM;
layout_set_size(lc, sx, sy, xoff, yoff);
layout_fix_panes(w, NULL);
server_redraw_window(w);
server_redraw_window_borders(w);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-swap-pane.c,v 1.55 2026/07/15 13:02:33 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -42,22 +42,6 @@ const struct cmd_entry cmd_swap_pane_entry = {
.exec = cmd_swap_pane_exec
};
static struct window_pane *
cmd_swap_pane_next_tiled_pane(struct window_pane *wp)
{
while (wp != NULL && !layout_cell_is_tiled(wp->layout_cell))
wp = TAILQ_NEXT(wp, entry);
return (wp);
}
static struct window_pane *
cmd_swap_pane_prev_tiled_pane(struct window_pane *wp)
{
while (wp != NULL && !layout_cell_is_tiled(wp->layout_cell))
wp = TAILQ_PREV(wp, window_panes, entry);
return (wp);
}
static enum cmd_retval
cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
{
@@ -68,47 +52,25 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
struct window_pane *tmp_wp, *src_wp, *dst_wp;
struct layout_cell *src_lc, *dst_lc;
u_int sx, sy, xoff, yoff;
int src_idx, dst_idx;
dst_w = target->wl->window;
dst_wp = target->wp;
dst_idx = target->wl->idx;
src_w = source->wl->window;
src_wp = source->wp;
src_idx = source->wl->idx;
if (src_wp == src_w->modal || dst_wp == dst_w->modal) {
cmdq_error(item, "pane is modal");
return (CMD_RETURN_ERROR);
}
if (window_push_zoom(dst_w, 0, args_has(args, 'Z')))
server_redraw_window(dst_w);
if (args_has(args, 'D')) {
if (window_pane_is_floating(dst_wp)) {
cmdq_error(item, "cannot swap down on floating pane");
return (CMD_RETURN_ERROR);
}
src_w = dst_w;
src_wp = TAILQ_NEXT(dst_wp, entry);
src_wp = cmd_swap_pane_next_tiled_pane(src_wp);
if (src_wp == NULL) {
if (src_wp == NULL)
src_wp = TAILQ_FIRST(&dst_w->panes);
src_wp = cmd_swap_pane_next_tiled_pane(src_wp);
}
} else if (args_has(args, 'U')) {
if (window_pane_is_floating(dst_wp)) {
cmdq_error(item, "cannot swap up on floating pane");
return (CMD_RETURN_ERROR);
}
src_w = dst_w;
src_wp = TAILQ_PREV(dst_wp, window_panes, entry);
src_wp = cmd_swap_pane_prev_tiled_pane(src_wp);
if (src_wp == NULL) {
if (src_wp == NULL)
src_wp = TAILQ_LAST(&dst_w->panes, window_panes);
src_wp = cmd_swap_pane_prev_tiled_pane(src_wp);
}
}
if (src_w != dst_w && window_push_zoom(src_w, 0, args_has(args, 'Z')))
@@ -130,16 +92,6 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
else
TAILQ_INSERT_AFTER(&dst_w->panes, tmp_wp, src_wp, entry);
tmp_wp = TAILQ_PREV(dst_wp, window_panes, zentry);
TAILQ_REMOVE(&dst_w->z_index, dst_wp, zentry);
TAILQ_REPLACE(&src_w->z_index, src_wp, dst_wp, zentry);
if (tmp_wp == src_wp)
tmp_wp = dst_wp;
if (tmp_wp == NULL)
TAILQ_INSERT_HEAD(&dst_w->z_index, src_wp, zentry);
else
TAILQ_INSERT_AFTER(&dst_w->z_index, tmp_wp, src_wp, zentry);
src_lc = src_wp->layout_cell;
dst_lc = dst_wp->layout_cell;
src_lc->wp = dst_wp;
@@ -181,20 +133,14 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
colour_palette_from_option(&src_wp->palette, src_wp->options);
colour_palette_from_option(&dst_wp->palette, dst_wp->options);
layout_fix_panes(src_w, NULL);
redraw_invalidate_scene(src_w);
server_redraw_window(src_w);
}
layout_fix_panes(dst_w, NULL);
redraw_invalidate_scene(dst_w);
server_redraw_window(dst_w);
if (src_w != dst_w) {
window_fire_pane_moved(src_wp, src_w, src_idx, dst_w, dst_idx);
window_fire_pane_moved(dst_wp, dst_w, dst_idx, src_w, src_idx);
}
events_fire_window("window-layout-changed", src_w);
notify_window("window-layout-changed", src_w);
if (src_w != dst_w)
events_fire_window("window-layout-changed", dst_w);
notify_window("window-layout-changed", dst_w);
out:
if (window_pop_zoom(src_w))

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-swap-window.c,v 1.30 2026/07/27 08:03:01 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -38,7 +38,7 @@ const struct cmd_entry cmd_swap_window_entry = {
.source = { 's', CMD_FIND_WINDOW, CMD_FIND_DEFAULT_MARKED },
.target = { 't', CMD_FIND_WINDOW, 0 },
.flags = CMD_AFTERHOOK,
.flags = 0,
.exec = cmd_swap_window_exec
};

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-switch-client.c,v 1.75 2026/08/20 09:19:24 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -20,7 +20,6 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "tmux.h"
@@ -35,9 +34,9 @@ const struct cmd_entry cmd_switch_client_entry = {
.name = "switch-client",
.alias = "switchc",
.args = { "c:EFlnO:pt:rT:Z", 0, 0, NULL },
.args = { "lc:EFnpt:rT:Z", 0, 0, NULL },
.usage = "[-ElnprZ] [-c target-client] [-t target-session] "
"[-T key-table] [-O order]",
"[-T key-table]",
/* -t is special */
@@ -53,8 +52,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
struct cmd_find_state target;
const char *tflag = args_get(args, 't');
enum cmd_find_type type;
int flags, visible, Zflag = args_has(args, 'Z');
struct client *c = cmdq_get_client(item);
int flags;
struct client *tc = cmdq_get_target_client(item);
struct session *s;
struct winlink *wl;
@@ -62,8 +60,6 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
struct window_pane *wp;
const char *tablename;
struct key_table *table;
struct sort_criteria sort_crit;
uid_t uid;
if (tflag != NULL &&
(tflag[strcspn(tflag, ":.%")] != '\0' || strcmp(tflag, "=") == 0)) {
@@ -80,13 +76,6 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
wp = target.wp;
if (args_has(args, 'r')) {
if (tc->flags & CLIENT_READONLY) {
uid = proc_get_peer_uid(c->peer);
if (uid != getuid()) {
cmdq_error(item, "client is read-only");
return (CMD_RETURN_ERROR);
}
}
if (tc->flags & CLIENT_READONLY)
tc->flags &= ~(CLIENT_READONLY|CLIENT_IGNORESIZE);
else
@@ -106,22 +95,13 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
sort_crit.order = sort_order_from_string(args_get(args, 'O'));
if (sort_crit.order == SORT_END && args_has(args, 'O')) {
cmdq_error(item, "invalid sort order");
return (CMD_RETURN_ERROR);
}
sort_crit.reversed = args_has(args, 'r');
if (args_has(args, 'n')) {
s = session_next_session(tc->session, &sort_crit);
if (s == 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')) {
s = session_previous_session(tc->session, &sort_crit);
if (s == NULL) {
if ((s = session_previous_session(tc->session)) == NULL) {
cmdq_error(item, "can't find previous session");
return (CMD_RETURN_ERROR);
}
@@ -139,15 +119,11 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
if (wl != NULL && wp != NULL && wp != wl->window->active) {
w = wl->window;
if (w->modal != NULL && wp != w->modal)
visible = 1;
else
visible = window_pane_is_visible(wp);
if (!visible && window_push_zoom(w, 0, Zflag))
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);
if (!visible && window_pop_zoom(w))
if (window_pop_zoom(w))
server_redraw_window(w);
}
if (wl != NULL) {

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-unbind-key.c,v 1.34 2021/08/21 10:22:39 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-wait-for.c,v 1.23 2026/07/10 13:38:45 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2013 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -34,8 +34,8 @@ const struct cmd_entry cmd_wait_for_entry = {
.name = "wait-for",
.alias = "wait",
.args = { "EF:LSUlvw:", 1, 1, NULL },
.usage = "[-ELSUlv] [-F format] [-w waiter] name",
.args = { "LSU", 1, 1, NULL },
.usage = "[-L|-S|-U] channel",
.flags = 0,
.exec = cmd_wait_for_exec
@@ -46,17 +46,6 @@ struct wait_item {
TAILQ_ENTRY(wait_item) entry;
};
struct wait_event_item {
struct cmdq_item *item;
struct events_sink *sink;
char *name;
char *filter;
int verbose;
TAILQ_ENTRY(wait_event_item) entry;
};
static TAILQ_HEAD(, wait_event_item) wait_event_items =
TAILQ_HEAD_INITIALIZER(wait_event_items);
struct wait_channel {
const char *name;
int locked;
@@ -87,28 +76,9 @@ static enum cmd_retval cmd_wait_for_lock(struct cmdq_item *, const char *,
struct wait_channel *);
static enum cmd_retval cmd_wait_for_unlock(struct cmdq_item *, const char *,
struct wait_channel *);
static enum cmd_retval cmd_wait_for_event(struct cmdq_item *, const char *,
struct args *);
static void cmd_wait_for_event_cb(const char *,
struct event_payload *, void *);
static void cmd_wait_for_event_free(struct wait_event_item *);
static enum cmd_retval cmd_wait_for_event_list(struct cmdq_item *,
const char *);
static enum cmd_retval cmd_wait_for_event_wake(struct cmdq_item *,
const char *, struct args *);
static enum cmd_retval cmd_wait_for_list(struct cmdq_item *,
struct wait_channel *);
static enum cmd_retval cmd_wait_for_wake(struct cmdq_item *, const char *,
struct args *, struct wait_channel *);
static struct wait_channel *cmd_wait_for_add(const char *);
static void cmd_wait_for_remove(struct wait_channel *);
static void cmd_wait_for_remove_empty(
struct wait_channel *);
static const char *cmd_wait_for_item_client_name(
struct cmdq_item *);
static const char *cmd_wait_for_client_name(
struct wait_event_item *);
static struct wait_channel *
cmd_wait_for_add(const char *name)
@@ -147,53 +117,16 @@ cmd_wait_for_remove(struct wait_channel *wc)
free(wc);
}
static void
cmd_wait_for_remove_empty(struct wait_channel *wc)
{
if (wc->locked || wc->woken)
return;
if (!TAILQ_EMPTY(&wc->waiters) || !TAILQ_EMPTY(&wc->lockers))
return;
log_debug("remove empty wait channel %s", wc->name);
RB_REMOVE(wait_channels, &wait_channels, wc);
free((void *)wc->name);
free(wc);
}
static const char *
cmd_wait_for_item_client_name(struct cmdq_item *item)
{
struct client *c = cmdq_get_client(item);
if (c == NULL || c->name == NULL)
return ("");
return (c->name);
}
static const char *
cmd_wait_for_client_name(struct wait_event_item *wei)
{
return (cmd_wait_for_item_client_name(wei->item));
}
static enum cmd_retval
cmd_wait_for_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
const char *name = args_string(args, 0);
struct wait_channel *wc, find = { .name = name };
if (args_has(args, 'E'))
return (cmd_wait_for_event(item, name, args));
struct wait_channel *wc, find;
find.name = name;
wc = RB_FIND(wait_channels, &wait_channels, &find);
if (args_has(args, 'l'))
return (cmd_wait_for_list(item, wc));
if (args_has(args, 'w'))
return (cmd_wait_for_wake(item, name, args, wc));
if (args_has(args, 'S'))
return (cmd_wait_for_signal(item, name, wc));
if (args_has(args, 'L'))
@@ -203,179 +136,6 @@ cmd_wait_for_exec(struct cmd *self, struct cmdq_item *item)
return (cmd_wait_for_wait(item, name, wc));
}
static void
cmd_wait_for_event_print(struct wait_event_item *wei, struct event_payload *ep)
{
struct event_payload_item *epi;
const char *key;
char *value;
epi = event_payload_first(ep);
while (epi != NULL) {
key = event_payload_item_name(epi);
if (*key != '_') {
value = event_payload_item_print(epi);
cmdq_print(wei->item, "%s=%s", key, value);
free(value);
}
epi = event_payload_next(epi);
}
}
static void
cmd_wait_for_event_cb(__unused const char *name, struct event_payload *ep,
void *item_data)
{
struct wait_event_item *wei = item_data;
struct format_tree *ft;
char *expanded;
int flag;
if (wei->verbose)
cmd_wait_for_event_print(wei, ep);
if (wei->filter != NULL) {
ft = format_create(cmdq_get_client(wei->item), wei->item,
FORMAT_NONE, FORMAT_NOJOBS);
event_payload_add_formats(ep, ft, NULL);
expanded = format_expand(ft, wei->filter);
flag = format_true(expanded);
free(expanded);
format_free(ft);
if (!flag)
return;
}
TAILQ_REMOVE(&wait_event_items, wei, entry);
cmdq_continue(wei->item);
cmd_wait_for_event_free(wei);
}
static void
cmd_wait_for_event_free(struct wait_event_item *wei)
{
events_remove_sink(wei->sink);
free(wei->name);
free(wei->filter);
free(wei);
}
static enum cmd_retval
cmd_wait_for_event(struct cmdq_item *item, const char *name, struct args *args)
{
struct wait_event_item *wei;
const char *filter = args_get(args, 'F');
if (!hooks_valid_event_name(name)) {
cmdq_error(item, "invalid event: %s", name);
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'l'))
return (cmd_wait_for_event_list(item, name));
if (args_has(args, 'w'))
return (cmd_wait_for_event_wake(item, name, args));
if (cmdq_get_client(item) == NULL) {
cmdq_error(item, "not able to wait");
return (CMD_RETURN_ERROR);
}
wei = xcalloc(1, sizeof *wei);
wei->item = item;
wei->name = xstrdup(name);
wei->filter = (filter != NULL ? xstrdup(filter) : NULL);
wei->verbose = args_has(args, 'v');
wei->sink = events_add_sink(name, cmd_wait_for_event_cb, wei);
TAILQ_INSERT_TAIL(&wait_event_items, wei, entry);
return (CMD_RETURN_WAIT);
}
static enum cmd_retval
cmd_wait_for_event_list(struct cmdq_item *item, const char *name)
{
struct wait_event_item *wei;
TAILQ_FOREACH(wei, &wait_event_items, entry) {
if (strcmp(wei->name, name) == 0)
cmdq_print(item, "%s", cmd_wait_for_client_name(wei));
}
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_wait_for_event_wake(struct cmdq_item *item, const char *name,
struct args *args)
{
struct wait_event_item *wei, *wei1;
const char *client_name = args_get(args, 'w');
TAILQ_FOREACH_SAFE(wei, &wait_event_items, entry, wei1) {
if (strcmp(wei->name, name) != 0)
continue;
if (strcmp(cmd_wait_for_client_name(wei), client_name) != 0)
continue;
TAILQ_REMOVE(&wait_event_items, wei, entry);
cmdq_continue(wei->item);
cmd_wait_for_event_free(wei);
return (CMD_RETURN_NORMAL);
}
cmdq_error(item, "waiter %s not found", client_name);
return (CMD_RETURN_ERROR);
}
static enum cmd_retval
cmd_wait_for_list(struct cmdq_item *item, struct wait_channel *wc)
{
struct wait_item *wi;
if (wc == NULL)
return (CMD_RETURN_NORMAL);
TAILQ_FOREACH(wi, &wc->waiters, entry)
cmdq_print(item, "%s", cmd_wait_for_item_client_name(wi->item));
TAILQ_FOREACH(wi, &wc->lockers, entry)
cmdq_print(item, "%s", cmd_wait_for_item_client_name(wi->item));
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_wait_for_wake(__unused struct cmdq_item *item, const char *name,
struct args *args, struct wait_channel *wc)
{
struct wait_item *wi, *wi1;
const char *client_name = args_get(args, 'w');
if (wc != NULL) {
TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) {
name = cmd_wait_for_item_client_name(wi->item);
if (strcmp(name, client_name) != 0)
continue;
cmdq_continue(wi->item);
TAILQ_REMOVE(&wc->waiters, wi, entry);
free(wi);
cmd_wait_for_remove_empty(wc);
return (CMD_RETURN_NORMAL);
}
TAILQ_FOREACH_SAFE(wi, &wc->lockers, entry, wi1) {
name = cmd_wait_for_item_client_name(wi->item);
if (strcmp(name, client_name) != 0)
continue;
cmdq_continue(wi->item);
TAILQ_REMOVE(&wc->lockers, wi, entry);
free(wi);
cmd_wait_for_remove_empty(wc);
return (CMD_RETURN_NORMAL);
}
}
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_wait_for_signal(__unused struct cmdq_item *item, const char *name,
struct wait_channel *wc)
@@ -485,13 +245,6 @@ cmd_wait_for_flush(void)
{
struct wait_channel *wc, *wc1;
struct wait_item *wi, *wi1;
struct wait_event_item *wei, *wei1;
TAILQ_FOREACH_SAFE(wei, &wait_event_items, entry, wei1) {
TAILQ_REMOVE(&wait_event_items, wei, entry);
cmdq_continue(wei->item);
cmd_wait_for_event_free(wei);
}
RB_FOREACH_SAFE(wc, wait_channels, &wait_channels, wc1) {
TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) {

265
cmd.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd.c,v 1.189 2026/07/17 08:29:34 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -71,7 +71,6 @@ extern const struct cmd_entry cmd_lock_server_entry;
extern const struct cmd_entry cmd_lock_session_entry;
extern const struct cmd_entry cmd_move_pane_entry;
extern const struct cmd_entry cmd_move_window_entry;
extern const struct cmd_entry cmd_new_pane_entry;
extern const struct cmd_entry cmd_new_session_entry;
extern const struct cmd_entry cmd_new_window_entry;
extern const struct cmd_entry cmd_next_layout_entry;
@@ -115,7 +114,6 @@ extern const struct cmd_entry cmd_suspend_client_entry;
extern const struct cmd_entry cmd_swap_pane_entry;
extern const struct cmd_entry cmd_swap_window_entry;
extern const struct cmd_entry cmd_switch_client_entry;
extern const struct cmd_entry cmd_switch_mode_entry;
extern const struct cmd_entry cmd_unbind_key_entry;
extern const struct cmd_entry cmd_unlink_window_entry;
extern const struct cmd_entry cmd_wait_for_entry;
@@ -165,7 +163,6 @@ const struct cmd_entry *cmd_table[] = {
&cmd_lock_session_entry,
&cmd_move_pane_entry,
&cmd_move_window_entry,
&cmd_new_pane_entry,
&cmd_new_session_entry,
&cmd_new_window_entry,
&cmd_next_layout_entry,
@@ -209,7 +206,6 @@ const struct cmd_entry *cmd_table[] = {
&cmd_swap_pane_entry,
&cmd_swap_window_entry,
&cmd_switch_client_entry,
&cmd_switch_mode_entry,
&cmd_unbind_key_entry,
&cmd_unlink_window_entry,
&cmd_wait_for_entry,
@@ -220,11 +216,18 @@ const struct cmd_entry *cmd_table[] = {
struct cmd {
const struct cmd_entry *entry;
struct args *args;
u_int group;
char *file;
u_int line;
int parse_flags;
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)
@@ -300,8 +303,6 @@ cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv)
if (argc == 0)
return (0);
if (argc < 0 || argc > 1000)
return (-1);
*argv = xcalloc(argc, sizeof **argv);
buf[len - 1] = '\0';
@@ -395,6 +396,13 @@ 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)
@@ -544,6 +552,23 @@ cmd_free(struct cmd *cmd)
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)
@@ -560,6 +585,175 @@ cmd_print(struct cmd *cmd)
return (out);
}
/* Create a new command list. */
struct cmd_list *
cmd_list_new(void)
{
struct cmd_list *cmdlist;
cmdlist = xcalloc(1, sizeof *cmdlist);
cmdlist->references = 1;
cmdlist->group = cmd_list_next_group++;
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);
}
/* 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)
{
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)
{
struct cmd *cmd, *cmd1;
if (--cmdlist->references != 0)
return;
TAILQ_FOREACH_SAFE(cmd, cmdlist->list, qentry, cmd1) {
TAILQ_REMOVE(cmdlist->list, cmd, qentry);
cmd_free(cmd);
}
free(cmdlist->list);
free(cmdlist);
}
/* Copy a command list, expanding %s in arguments. */
struct cmd_list *
cmd_list_copy(const 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(const struct cmd_list *cmdlist, int escaped)
{
struct cmd *cmd, *next;
char *buf, *this;
size_t len;
len = 1;
buf = xcalloc(1, len);
TAILQ_FOREACH(cmd, cmdlist->list, qentry) {
this = cmd_print(cmd);
len += strlen(this) + 6;
buf = xrealloc(buf, len);
strlcat(buf, this, 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);
}
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,
@@ -579,9 +773,9 @@ cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp,
if (m->statusat == 0 && y >= m->statuslines)
y -= m->statuslines;
if ((int)x < wp->xoff || (int)x >= wp->xoff + (int)wp->sx)
if (x < wp->xoff || x >= wp->xoff + wp->sx)
return (-1);
if ((int)y < wp->yoff || (int)y >= wp->yoff + (int)wp->sy)
if (y < wp->yoff || y >= wp->yoff + wp->sy)
return (-1);
if (xp != NULL)
@@ -633,27 +827,20 @@ cmd_mouse_pane(struct mouse_event *m, struct session **sp,
if (!window_has_pane(wl->window, wp))
return (NULL);
}
if (wl->window->modal != NULL && wp != wl->window->modal)
return (NULL);
if (wlp != NULL)
*wlp = wl;
return (wp);
}
/*
* Replace the first %% or any %idx in template by s. %% is intended for use in
* single quotes, so ' is escaped. %%% and %idx% are for double quotes, so a
* list of special characters is escaped. %idx is left unescaped.
*/
/* Replace the first %% or %idx in template by s. */
char *
cmd_template_replace(const char *template, const char *s, int idx)
{
char ch, *buf;
const char *ptr, *cp, dquote[] = "\"\\$;~";
int replaced;
size_t len, slen;
enum { NQ, SQ, DQ } quote;
const char *ptr, *cp, quote[] = "\"\\$;~";
int replaced, quoted;
size_t len;
if (strchr(template, '%') == NULL)
return (xstrdup(template));
@@ -667,52 +854,30 @@ cmd_template_replace(const char *template, const char *s, int idx)
while (*ptr != '\0') {
switch (ch = *ptr++) {
case '%':
if (*ptr >= '1' && *ptr <= '9' && *ptr - '0' == idx) {
ptr++;
quote = NQ;
if (*ptr == '%') {
quote = DQ;
ptr++;
}
} else {
if (*ptr < '1' || *ptr > '9' || *ptr - '0' != idx) {
if (*ptr != '%' || replaced)
break;
replaced = 1;
ptr++;
quote = SQ;
if (*ptr == '%') {
quote = DQ;
ptr++;
}
}
ptr++;
slen = strlen(s);
if (slen >= SIZE_MAX / 4 ||
len > SIZE_MAX - (slen * 4) - 1)
fatalx("argument too long");
buf = xrealloc(buf, len + (slen * 4) + 1);
quoted = (*ptr == '%');
if (quoted)
ptr++;
buf = xrealloc(buf, len + (strlen(s) * 3) + 1);
for (cp = s; *cp != '\0'; cp++) {
if (quote == SQ && *cp == '\'') {
buf[len++] = '\'';
buf[len++] = '\\';
buf[len++] = '\'';
buf[len++] = '\'';
continue;
}
if (quote == DQ && strchr(dquote, *cp) != NULL)
if (quoted && strchr(quote, *cp) != NULL)
buf[len++] = '\\';
buf[len++] = *cp;
}
buf[len] = '\0';
continue;
}
if (len > SIZE_MAX - 2)
fatalx("argument too long");
buf = xrealloc(buf, len + 2);
buf[len++] = ch;
buf[len] = '\0';
}
log_debug("%s: %s -> %s", __func__, template, buf);
return (buf);
}

209
colour.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: colour.c,v 1.35 2026/07/06 14:29:10 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -26,85 +26,6 @@
#include "tmux.h"
/* Theme colour slots and their server options. */
static const struct {
const char *name;
const char *dark_option;
const char *light_option;
int terminal_colour;
} colour_theme_table[] = {
{ "themeblack",
"dark-theme-black",
"light-theme-black",
0
},
{ "themewhite",
"dark-theme-white",
"light-theme-white",
7
},
{ "themelightgrey",
"dark-theme-light-grey",
"light-theme-light-grey",
7
},
{ "themedarkgrey",
"dark-theme-dark-grey",
"light-theme-dark-grey",
0
},
{ "themegreen",
"dark-theme-green",
"light-theme-green",
2
},
{ "themeyellow",
"dark-theme-yellow",
"light-theme-yellow",
3
},
{ "themered",
"dark-theme-red",
"light-theme-red",
1
},
{ "themeblue",
"dark-theme-blue",
"light-theme-blue",
4
},
{ "themecyan",
"dark-theme-cyan",
"light-theme-cyan",
6
},
{ "thememagenta",
"dark-theme-magenta",
"light-theme-magenta",
5
}
};
/* Get theme colour option. */
const char *
colour_theme_option(u_int n, enum client_theme theme)
{
if (n >= nitems(colour_theme_table))
return (NULL);
if (theme == THEME_LIGHT)
return (colour_theme_table[n].light_option);
return (colour_theme_table[n].dark_option);
}
/* Get theme terminal colour. */
int
colour_theme_terminal_colour(u_int n)
{
if (n >= nitems(colour_theme_table))
return (8);
return (colour_theme_table[n].terminal_colour);
}
static int
colour_dist_sq(int R, int G, int B, int r, int g, int b)
{
@@ -199,28 +120,6 @@ colour_force_rgb(int c)
return (-1);
}
/* Dim colour by a percentage. */
int
colour_dim(int c, u_int dim)
{
u_char r, g, b;
if (dim == 0 || COLOUR_DEFAULT(c) || (c & COLOUR_FLAG_THEME))
return (c);
if (dim >= 100)
return (colour_join_rgb(0, 0, 0));
c = colour_force_rgb(c);
if (c == -1)
return (-1);
colour_split_rgb(c, &r, &g, &b);
r = (r * (100 - dim)) / 100;
g = (g * (100 - dim)) / 100;
b = (b * (100 - dim)) / 100;
return (colour_join_rgb(r, g, b));
}
/* Convert colour to a string. */
const char *
colour_tostring(int c)
@@ -231,13 +130,6 @@ colour_tostring(int c)
if (c == -1)
return ("none");
if (c & COLOUR_FLAG_THEME) {
c &= 0xff;
if (c >= 0 && (u_int)c < nitems(colour_theme_table))
return (colour_theme_table[c].name);
return ("invalid");
}
if (c & COLOUR_FLAG_RGB) {
colour_split_rgb(c, &r, &g, &b);
xsnprintf(s, sizeof s, "#%02x%02x%02x", r, g, b);
@@ -290,58 +182,6 @@ colour_tostring(int c)
return ("invalid");
}
/* Convert colour to an SGR escape sequence. */
const char *
colour_toescape(struct client *c, int colour, int bg)
{
static char s[32];
u_char r, g, b;
int n, flags = (TERM_256COLOURS|TERM_RGBCOLOURS);
u_int o = (bg ? 40 : 30);
if (c != NULL && (c->tty.flags & TTY_OPENED) && c->tty.term != NULL)
flags = c->tty.term->flags;
if (colour & COLOUR_FLAG_THEME) {
n = colour & 0xff;
if (c != NULL && (u_int)n < COLOUR_THEME_COUNT)
colour = c->theme_colours[n];
else
colour = colour_theme_terminal_colour(n);
}
if (colour == 8 || colour == 9) {
xsnprintf(s, sizeof s, "\033[%dm", o + 9);
return (s);
}
if ((~flags & TERM_RGBCOLOURS) & (colour & COLOUR_FLAG_RGB)) {
colour_split_rgb(colour, &r, &g, &b);
colour = colour_find_rgb(r, g, b);
}
if ((~flags & TERM_256COLOURS) & (colour & COLOUR_FLAG_256))
colour = colour_256to16(colour);
if (colour & COLOUR_FLAG_RGB) {
colour_split_rgb(colour, &r, &g, &b);
xsnprintf(s, sizeof s, "\033[%d;2;%u;%u;%um", o + 8, r, g, b);
return (s);
}
if (colour & COLOUR_FLAG_256) {
xsnprintf(s, sizeof s, "\033[%d;5;%um", o + 8, colour & 0xff);
return (s);
}
if (colour >= 0 && colour <= 7) {
xsnprintf(s, sizeof s, "\033[%dm", colour + o);
return (s);
}
if (colour >= 90 && colour <= 97) {
xsnprintf(s, sizeof s, "\033[%dm", colour + o - 30);
return (s);
}
return (NULL);
}
/* Convert background colour to theme. */
enum client_theme
colour_totheme(int c)
@@ -390,7 +230,6 @@ colour_fromstring(const char *s)
const char *cp;
int n;
u_char r, g, b;
u_int i;
if (*s == '#' && strlen(s) == 7) {
for (cp = s + 1; isxdigit((u_char) *cp); cp++)
@@ -421,11 +260,6 @@ colour_fromstring(const char *s)
if (strcasecmp(s, "terminal") == 0)
return (9);
for (i = 0; i < nitems(colour_theme_table); i++) {
if (strcasecmp(s, colour_theme_table[i].name) == 0)
return (i|COLOUR_FLAG_THEME);
}
if (strcasecmp(s, "black") == 0 || strcmp(s, "0") == 0)
return (0);
if (strcasecmp(s, "red") == 0 || strcmp(s, "1") == 0)
@@ -1248,22 +1082,22 @@ colour_palette_free(struct colour_palette *p)
/* Get a colour from a palette. */
int
colour_palette_get(struct colour_palette *p, int n)
colour_palette_get(struct colour_palette *p, int c)
{
if (p == NULL)
return (-1);
if (n >= 90 && n <= 97)
n = 8 + n - 90;
else if (n & COLOUR_FLAG_256)
n &= ~COLOUR_FLAG_256;
else if (n >= 8)
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[n] != -1)
return (p->palette[n]);
if (p->default_palette != NULL && p->default_palette[n] != -1)
return (p->default_palette[n]);
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);
}
@@ -1273,13 +1107,14 @@ colour_palette_set(struct colour_palette *p, int n, int c)
{
u_int i;
if (p == NULL || n < 0 || n > 255)
if (p == NULL || n > 255)
return (0);
if (c == -1 && p->palette == NULL)
return (0);
if (p->palette == NULL) {
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;
@@ -1294,8 +1129,7 @@ colour_palette_from_option(struct colour_palette *p, struct options *oo)
{
struct options_entry *o;
struct options_array_item *a;
union options_value *ov;
u_int i;
u_int i, n;
int c;
if (p == NULL)
@@ -1313,11 +1147,12 @@ colour_palette_from_option(struct colour_palette *p, struct options *oo)
p->default_palette = xcalloc(256, sizeof *p->default_palette);
for (i = 0; i < 256; i++)
p->default_palette[i] = -1;
for (i = 0; i < 256; i++) {
ov = options_array_getv(o, "%u", i);
if (ov != NULL) {
c = ov->number;
p->default_palette[i] = c;
}
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

@@ -388,7 +388,7 @@ int clock_gettime(int, struct timespec *);
/* base64.c */
#undef b64_ntop
#undef b64_pton
int b64_ntop(const u_char *, size_t, char *, size_t);
int b64_ntop(const char *, size_t, char *, size_t);
int b64_pton(const char *, u_char *, size_t);
#endif
@@ -410,6 +410,11 @@ int asprintf(char **, const char *, ...);
int vasprintf(char **, const char *, va_list);
#endif
#ifndef HAVE_FGETLN
/* fgetln.c */
char *fgetln(FILE *, size_t *);
#endif
#ifndef HAVE_GETLINE
/* getline.c */
ssize_t getline(char **, size_t *, FILE *);
@@ -458,9 +463,6 @@ int utf8proc_wctomb(char *, wchar_t);
#ifdef NEED_FUZZING
/* tmux.c */
#define main __weak main
#define regcomp(preg, pattern, cflags) (0)
#define regexec(preg, string, nmatch, pmatch, eflags) (REG_NOMATCH)
#define regfree(preg) ((void)0)
#endif
/* getopt.c */

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: base64.c,v 1.15 2021/10/25 14:41:09 jca Exp $ */
/* $OpenBSD: base64.c,v 1.8 2015/01/16 16:48:51 deraadt Exp $ */
/*
* Copyright (c) 1996 by Internet Software Consortium.
@@ -46,15 +46,15 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <ctype.h>
#include <resolv.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "compat.h"
static const char Base64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const char Pad64 = '=';
@@ -123,12 +123,15 @@ static const char Pad64 = '=';
*/
int
b64_ntop(unsigned char const *src, size_t srclength, char *target,
size_t targsize)
b64_ntop(src, srclength, target, targsize)
u_char const *src;
size_t srclength;
char *target;
size_t targsize;
{
size_t datalength = 0;
unsigned char input[3];
unsigned char output[4];
u_char input[3];
u_char output[4];
int i;
while (2 < srclength) {
@@ -184,10 +187,13 @@ b64_ntop(unsigned char const *src, size_t srclength, char *target,
*/
int
b64_pton(char const *src, unsigned char *target, size_t targsize)
b64_pton(src, target, targsize)
char const *src;
u_char *target;
size_t targsize;
{
int tarindex, state, ch;
unsigned char nextbyte;
u_char nextbyte;
char *pos;
state = 0;

61
compat/fgetln.c Normal file
View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) 2015 Joerg Jung <jung@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* portable fgetln() version, NOT reentrant
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include "compat.h"
char *
fgetln(FILE *fp, size_t *len)
{
static char *buf = NULL;
static size_t bufsz = 0;
size_t r = 0;
char *p;
int c, e;
if (!fp || !len) {
errno = EINVAL;
return NULL;
}
if (!buf) {
if (!(buf = calloc(1, BUFSIZ)))
return NULL;
bufsz = BUFSIZ;
}
while ((c = getc(fp)) != EOF) {
buf[r++] = c;
if (r == bufsz) {
if (!(p = reallocarray(buf, 2, bufsz))) {
e = errno;
free(buf);
errno = e;
buf = NULL, bufsz = 0;
return NULL;
}
buf = p, bufsz = 2 * bufsz;
}
if (c == '\n')
break;
}
return (*len = r) ? buf : NULL;
}

View File

@@ -74,14 +74,8 @@ getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp)
}
if (ptr + 2 >= eptr) {
char *nbuf;
size_t nbufsiz;
size_t nbufsiz = *bufsiz * 2;
ssize_t d = ptr - *buf;
if (*bufsiz > SIZE_MAX / 2) {
errno = EOVERFLOW;
return -1;
}
nbufsiz = *bufsiz * 2;
if ((nbuf = realloc(*buf, nbufsiz)) == NULL)
return -1;
*buf = nbuf;

View File

@@ -339,7 +339,7 @@ static int
getopt_internal(int nargc, char * const *nargv, const char *options,
const struct option *long_options, int *idx, int flags)
{
const char *oli; /* option letter list index */
char *oli; /* option letter list index */
int optchar, short_too;
static int posixly_correct = -1;

View File

@@ -31,7 +31,7 @@ getpeereid(int s, uid_t *uid, gid_t *gid)
{
#ifdef HAVE_SO_PEERCRED
struct ucred uc;
socklen_t len = sizeof uc;
int len = sizeof uc;
if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &uc, &len) == -1)
return (-1);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: imsg-buffer.c,v 1.36 2025/08/25 08:29:49 claudio Exp $ */
/* $OpenBSD: imsg-buffer.c,v 1.35 2025/06/04 09:06:56 claudio Exp $ */
/*
* Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org>
@@ -699,6 +699,8 @@ msgbuf_queuelen(struct msgbuf *msgbuf)
void
msgbuf_clear(struct msgbuf *msgbuf)
{
struct ibuf *buf;
/* write side */
ibufq_flush(&msgbuf->bufs);

View File

@@ -81,8 +81,7 @@ struct systemd_job_watch {
};
static int
job_removed_handler(sd_bus_message *m, void *userdata,
__unused sd_bus_error *ret_error)
job_removed_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
struct systemd_job_watch *watch = userdata;
const char *path = NULL;
@@ -110,7 +109,7 @@ systemd_move_to_new_cgroup(char **cause)
sd_bus_message *m = NULL, *reply = NULL;
sd_bus *bus = NULL;
sd_bus_slot *slot = NULL;
char *name, *desc, *slice, *unit;
char *name, *desc, *slice;
sd_id128_t uuid;
int r;
uint64_t elapsed_usec;
@@ -242,26 +241,6 @@ systemd_move_to_new_cgroup(char **cause)
goto finish;
}
/*
* Try locating systemd unit that started the server, and mark pane units
* as dependent on it. Use "Before" to make sure systemd will not try to
* kill them first.
*/
if (sd_pid_get_user_unit(parent_pid, &unit) == 0 ||
sd_pid_get_unit(parent_pid, &unit) == 0) {
r = sd_bus_message_append(m, "(sv)", "Before", "as", 1, unit);
if (r >= 0) {
r = sd_bus_message_append(m, "(sv)", "PartOf", "as", 1,
unit);
}
free(unit);
if (r < 0) {
xasprintf(cause, "failed to append to properties: %s",
strerror(-r));
goto finish;
}
}
/* End properties array. */
r = sd_bus_message_close_container(m);
if (r < 0) {

View File

@@ -1,6 +1,6 @@
# configure.ac
AC_INIT([tmux], next-3.8)
AC_INIT([tmux], 3.6b)
AC_PREREQ([2.60])
AC_CONFIG_AUX_DIR(etc)
@@ -64,22 +64,6 @@ AC_ARG_ENABLE(
)
AM_CONDITIONAL(IS_DEBUG, test "x$enable_debug" = xyes)
# Is this --enable-optimizations?
AC_ARG_ENABLE(
optimizations,
AS_HELP_STRING(--enable-optimizations, enable optimization build flags),
,
enable_optimizations=yes
)
AM_CONDITIONAL(IS_OPTIMIZED, test "x$enable_optimizations" = xyes)
# Is this --enable-asan?
AC_ARG_ENABLE(
asan,
AS_HELP_STRING(--enable-asan, enable ASAN build flags)
)
AM_CONDITIONAL(IS_ASAN, test "x$enable_asan" = xyes)
# Is this a static build?
AC_ARG_ENABLE(
static,
@@ -175,6 +159,7 @@ AC_REPLACE_FUNCS([ \
clock_gettime \
closefrom \
explicit_bzero \
fgetln \
freezero \
getdtablecount \
getdtablesize \
@@ -238,7 +223,6 @@ AC_LIBOBJ(getopt_long)
# Look for libevent. Try libevent_core or libevent with pkg-config first then
# look for the library.
libevent_version=off
PKG_CHECK_MODULES(
LIBEVENT_CORE,
[libevent_core >= 2],
@@ -247,8 +231,6 @@ PKG_CHECK_MODULES(
CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBEVENT_CORE_LIBS $LIBS"
found_libevent=yes
libevent_version=`$PKG_CONFIG --modversion libevent_core 2>/dev/null`
test "x$libevent_version" = x && libevent_version=on
],
found_libevent=no
)
@@ -261,8 +243,6 @@ if test x$found_libevent = xno; then
CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBEVENT_LIBS $LIBS"
found_libevent=yes
libevent_version=`$PKG_CONFIG --modversion libevent 2>/dev/null`
test "x$libevent_version" = x && libevent_version=on
],
found_libevent=no
)
@@ -271,8 +251,7 @@ if test x$found_libevent = xno; then
AC_SEARCH_LIBS(
event_init,
[event_core event event-1.4],
[found_libevent=yes
libevent_version=on],
found_libevent=yes,
found_libevent=no
)
fi
@@ -299,21 +278,6 @@ fi
# Look for ncurses or curses. Try pkg-config first then directly for the
# library.
ncurses_version=off
PKG_CHECK_MODULES(
LIBTINFOW,
tinfow,
[
AM_CPPFLAGS="$LIBTINFOW_CFLAGS $AM_CPPFLAGS"
CPPFLAGS="$LIBTINFOW_CFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBTINFOW_LIBS $LIBS"
found_ncurses=yes
ncurses_version=`$PKG_CONFIG --modversion tinfow 2>/dev/null`
test "x$ncurses_version" = x && ncurses_version=on
],
found_ncurses=no
)
if test "x$found_ncurses" = xno; then
PKG_CHECK_MODULES(
LIBTINFO,
tinfo,
@@ -322,8 +286,18 @@ if test "x$found_ncurses" = xno; then
CPPFLAGS="$LIBTINFO_CFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBTINFO_LIBS $LIBS"
found_ncurses=yes
ncurses_version=`$PKG_CONFIG --modversion tinfo 2>/dev/null`
test "x$ncurses_version" = x && ncurses_version=on
],
found_ncurses=no
)
if test "x$found_ncurses" = xno; then
PKG_CHECK_MODULES(
LIBNCURSES,
ncurses,
[
AM_CPPFLAGS="$LIBNCURSES_CFLAGS $AM_CPPFLAGS"
CPPFLAGS="$LIBNCURSES_CFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBNCURSES_LIBS $LIBS"
found_ncurses=yes
],
found_ncurses=no
)
@@ -337,23 +311,6 @@ if test "x$found_ncurses" = xno; then
CPPFLAGS="$LIBNCURSESW_CFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBNCURSESW_LIBS $LIBS"
found_ncurses=yes
ncurses_version=`$PKG_CONFIG --modversion ncursesw 2>/dev/null`
test "x$ncurses_version" = x && ncurses_version=on
],
found_ncurses=no
)
fi
if test "x$found_ncurses" = xno; then
PKG_CHECK_MODULES(
LIBNCURSES,
ncurses,
[
AM_CPPFLAGS="$LIBNCURSES_CFLAGS $AM_CPPFLAGS"
CPPFLAGS="$LIBNCURSES_CFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBNCURSES_LIBS $LIBS"
found_ncurses=yes
ncurses_version=`$PKG_CONFIG --modversion ncurses 2>/dev/null`
test "x$ncurses_version" = x && ncurses_version=on
],
found_ncurses=no
)
@@ -361,9 +318,8 @@ fi
if test "x$found_ncurses" = xno; then
AC_SEARCH_LIBS(
setupterm,
[tinfow tinfo terminfo ncursesw ncurses],
[found_ncurses=yes
ncurses_version=on],
[tinfo terminfo ncurses ncursesw],
found_ncurses=yes,
found_ncurses=no
)
if test "x$found_ncurses" = xyes; then
@@ -393,7 +349,6 @@ else
LIBS="$LIBS -lcurses"
CPPFLAGS="$CPPFLAGS -DHAVE_CURSES_H"
AC_DEFINE(HAVE_CURSES_H)
ncurses_version=on
else
AC_MSG_ERROR("curses not found")
fi
@@ -404,7 +359,6 @@ AC_CHECK_FUNCS([ \
])
# Look for utempter.
utempter_version=off
AC_ARG_ENABLE(
utempter,
AS_HELP_STRING(--enable-utempter, use utempter if it is installed)
@@ -421,34 +375,17 @@ if test "x$enable_utempter" = xyes; then
fi
if test "x$enable_utempter" = xyes; then
AC_DEFINE(HAVE_UTEMPTER)
utempter_version=on
else
AC_MSG_ERROR("utempter not found")
fi
fi
# Look for utf8proc.
utf8proc_version=off
AC_ARG_ENABLE(
utf8proc,
AS_HELP_STRING(--enable-utf8proc, use utf8proc if it is installed)
)
try_utf8proc=no
require_utf8proc=no
if test "x$enable_utf8proc" = xyes; then
try_utf8proc=yes
elif test "x$enable_utf8proc" = x; then
case "$host_os" in
*darwin*)
try_utf8proc=yes
require_utf8proc=yes
;;
esac
fi
if test "x$try_utf8proc" = xyes; then
SAVED_AM_CPPFLAGS="$AM_CPPFLAGS"
SAVED_OLD_CPPFLAGS="$CPPFLAGS"
SAVED_LIBS="$LIBS"
PKG_CHECK_MODULES(
LIBUTF8PROC,
libutf8proc,
@@ -456,10 +393,7 @@ if test "x$try_utf8proc" = xyes; then
AM_CPPFLAGS="$LIBUTF8PROC_CFLAGS $AM_CPPFLAGS"
CPPFLAGS="$LIBUTF8PROC_CFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBUTF8PROC_LIBS $LIBS"
utf8proc_version=`$PKG_CONFIG --modversion libutf8proc 2>/dev/null`
test "x$utf8proc_version" = x && utf8proc_version=on
],
[:]
]
)
AC_CHECK_HEADER(utf8proc.h, enable_utf8proc=yes, enable_utf8proc=no)
if test "x$enable_utf8proc" = xyes; then
@@ -472,23 +406,13 @@ if test "x$try_utf8proc" = xyes; then
fi
if test "x$enable_utf8proc" = xyes; then
AC_DEFINE(HAVE_UTF8PROC)
test "x$utf8proc_version" = xoff && utf8proc_version=on
else
AM_CPPFLAGS="$SAVED_AM_CPPFLAGS"
CPPFLAGS="$SAVED_OLD_CPPFLAGS"
LIBS="$SAVED_LIBS"
utf8proc_version=off
if test "x$require_utf8proc" = xyes; then
enable_utf8proc=
else
AC_MSG_ERROR("utf8proc not found")
fi
fi
fi
AM_CONDITIONAL(HAVE_UTF8PROC, [test "x$enable_utf8proc" = xyes])
# Check for systemd support.
systemd_version=off
AC_ARG_ENABLE(
systemd,
AS_HELP_STRING(--enable-systemd, enable systemd integration)
@@ -502,8 +426,6 @@ if test x"$enable_systemd" = xyes; then
CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS"
LIBS="$SYSTEMD_LIBS $LIBS"
found_systemd=yes
systemd_version=`$PKG_CONFIG --modversion libsystemd 2>/dev/null`
test "x$systemd_version" = x && systemd_version=on
],
found_systemd=no
)
@@ -628,34 +550,11 @@ if test "x$found_malloc_trim" = xyes; then
fi
# Build against jemalloc if requested.
jemalloc_version=off
AC_ARG_ENABLE(
jemalloc,
AS_HELP_STRING(--enable-jemalloc, use jemalloc if it is installed)
)
try_jemalloc=no
require_jemalloc=no
if test "x$enable_asan" = xyes && test "x$enable_jemalloc" = xyes; then
AC_MSG_ERROR([--enable-asan and --enable-jemalloc cannot be used together])
fi
if test "x$enable_jemalloc" = xyes; then
try_jemalloc=yes
elif test "x$enable_jemalloc" = x; then
case "$host_os" in
*darwin*)
if test "x$enable_asan" = xyes; then
enable_jemalloc=no
else
try_jemalloc=yes
require_jemalloc=yes
fi
;;
esac
fi
if test "x$try_jemalloc" = xyes; then
SAVED_AM_CPPFLAGS="$AM_CPPFLAGS"
SAVED_OLD_CPPFLAGS="$CPPFLAGS"
SAVED_LIBS="$LIBS"
PKG_CHECK_MODULES(
JEMALLOC,
jemalloc,
@@ -663,26 +562,9 @@ if test "x$try_jemalloc" = xyes; then
AM_CPPFLAGS="$JEMALLOC_CFLAGS $AM_CPPFLAGS"
CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBS $JEMALLOC_LIBS"
jemalloc_version=`$PKG_CONFIG --modversion jemalloc 2>/dev/null`
test "x$jemalloc_version" = x && jemalloc_version=on
],
enable_jemalloc=no
)
if test "x$enable_jemalloc" = xno; then
AM_CPPFLAGS="$SAVED_AM_CPPFLAGS"
CPPFLAGS="$SAVED_OLD_CPPFLAGS"
LIBS="$SAVED_LIBS"
jemalloc_version=off
if test "x$require_jemalloc" = xyes; then
enable_jemalloc=
else
AC_MSG_ERROR("jemalloc not found")
fi
else
enable_jemalloc=yes
AC_DEFINE(HAVE_JEMALLOC)
test "x$jemalloc_version" = xoff && jemalloc_version=on
fi
)
fi
# Check for CMSG_DATA. On some platforms like HP-UX this requires UNIX 95
@@ -1029,20 +911,6 @@ case "$host_os" in
AC_MSG_NOTICE([])
AC_MSG_ERROR([must give --enable-utf8proc or --disable-utf8proc])
fi
#
# macOS calloc(3) does not appear to always zero memory correctly,
# so complain and suggest using jemalloc instead.
#
if test "x$enable_jemalloc" = x; then
AC_MSG_NOTICE([])
AC_MSG_NOTICE([ macOS calloc(3) appears not to correctly])
AC_MSG_NOTICE([ zero allocations in some circumstances;])
AC_MSG_NOTICE([ to avoid this, configuring with])
AC_MSG_NOTICE([ --enable-jemalloc is recommended. To build])
AC_MSG_NOTICE([ without anyway, use --disable-jemalloc])
AC_MSG_NOTICE([])
AC_MSG_ERROR([must give --enable-jemalloc or --disable-jemalloc])
fi
;;
*dragonfly*)
AC_MSG_RESULT(dragonfly)
@@ -1130,31 +998,6 @@ AC_MSG_CHECKING(lock-command)
AC_MSG_RESULT($DEFAULT_LOCK_CMD)
AC_SUBST(DEFAULT_LOCK_CMD)
# Print a summary.
AC_MSG_NOTICE([])
if test "x$enable_asan" = xyes; then
AC_MSG_NOTICE([ASAN: on])
else
AC_MSG_NOTICE([ASAN: off])
fi
if test "x$enable_sixel" = xyes; then
AC_MSG_NOTICE([SIXEL: on])
else
AC_MSG_NOTICE([SIXEL: off])
fi
if test "x$enable_debug" = xyes; then
AC_MSG_NOTICE([debug: on])
else
AC_MSG_NOTICE([debug: off])
fi
AC_MSG_NOTICE([jemalloc: $jemalloc_version])
AC_MSG_NOTICE([libevent: $libevent_version])
AC_MSG_NOTICE([ncurses: $ncurses_version])
AC_MSG_NOTICE([systemd: $systemd_version])
AC_MSG_NOTICE([utempter: $utempter_version])
AC_MSG_NOTICE([utf8proc: $utf8proc_version])
AC_MSG_NOTICE([])
# Save our CFLAGS/CPPFLAGS/LDFLAGS for the Makefile and restore the old user
# variables.
AC_SUBST(AM_CPPFLAGS)

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: control-notify.c,v 1.38 2026/08/03 13:38:42 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2012 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -23,187 +23,137 @@
#include "tmux.h"
/* Should this client be sent events? */
#define CONTROL_SHOULD_NOTIFY_CLIENT(c) \
((c) != NULL && \
((c)->flags & CLIENT_CONTROL) && \
(~(c)->flags & CLIENT_EXIT) && \
(c)->control_state != NULL)
((c) != NULL && ((c)->flags & CLIENT_CONTROL))
/* Notify control clients that pane mode changed. */
static void
control_pane_mode_changed_cb(__unused const char *name,
struct event_payload *ep, __unused void *sink_data)
void
control_notify_pane_mode_changed(int pane)
{
struct window_pane *wp;
struct client *c;
char *value;
wp = event_payload_get_pane(ep, "pane");
if (wp != NULL) {
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_notify_write(c, "%%pane-mode-changed %%%u",
wp->id);
control_write(c, "%%pane-mode-changed %%%u", pane);
}
return;
}
value = event_payload_print(ep, "pane");
if (value == NULL)
return;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_notify_write(c, "%%pane-mode-changed %s", value);
}
free(value);
}
/* Notify control clients that window layout changed. */
static void
control_window_layout_changed_cb(__unused const char *name,
struct event_payload *ep, __unused void *sink_data)
void
control_notify_window_layout_changed(struct window *w)
{
struct client *c;
struct session *s;
struct winlink *wl;
struct window *w = event_payload_get_window(ep, "window");
const char *template;
char *cp;
if (w == NULL)
return;
template = "%layout-change #{window_id} #{window_layout} "
"#{window_visible_layout} #{window_raw_flags}";
/*
* When the last pane in a window is closed it won't have a layout root
* and we don't need to inform the client about the layout change
* because the whole window will go away soon.
*/
wl = TAILQ_FIRST(&w->winlinks);
if (wl == NULL || w->layout_root == NULL)
return;
cp = format_single(NULL, template, NULL, NULL, wl, NULL);
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
continue;
s = c->session;
if (winlink_find_by_window_id(&s->windows, w->id) != NULL)
control_notify_write(c, "%s", cp);
}
if (winlink_find_by_window_id(&s->windows, w->id) == NULL)
continue;
/*
* When the last pane in a window is closed it won't have a
* layout root and we don't need to inform the client about the
* layout change because the whole window will go away soon.
*/
if (w->layout_root == NULL)
continue;
wl = winlink_find_by_window(&s->windows, w);
if (wl != NULL) {
cp = format_single(NULL, template, c, NULL, wl, NULL);
control_write(c, "%s", cp);
free(cp);
}
}
}
/* Notify control clients that window pane changed. */
static void
control_window_pane_changed_cb(__unused const char *name,
struct event_payload *ep, __unused void *sink_data)
void
control_notify_window_pane_changed(struct window *w)
{
struct client *c;
struct window *w = event_payload_get_window(ep, "window");
if (w == NULL || w->active == NULL)
return;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_notify_write(c, "%%window-pane-changed @%u %%%u", w->id,
control_write(c, "%%window-pane-changed @%u %%%u", w->id,
w->active->id);
}
}
/* Notify control clients that a window was unlinked. */
static void
control_window_unlinked_cb(__unused const char *name, struct event_payload *ep,
__unused void *sink_data)
void
control_notify_window_unlinked(__unused struct session *s, struct window *w)
{
struct client *c;
struct session *cs;
struct window *w = event_payload_get_window(ep, "window");
if (w == NULL)
return;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
continue;
cs = c->session;
if (winlink_find_by_window_id(&cs->windows, w->id) != NULL)
control_notify_write(c, "%%window-close @%u", w->id);
else {
control_notify_write(c, "%%unlinked-window-close @%u",
w->id);
}
control_write(c, "%%window-close @%u", w->id);
else
control_write(c, "%%unlinked-window-close @%u", w->id);
}
}
/* Notify control clients that a window was linked. */
static void
control_window_linked_cb(__unused const char *name, struct event_payload *ep,
__unused void *sink_data)
void
control_notify_window_linked(__unused struct session *s, struct window *w)
{
struct client *c;
struct session *cs;
struct window *w = event_payload_get_window(ep, "window");
if (w == NULL)
return;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
continue;
cs = c->session;
if (winlink_find_by_window_id(&cs->windows, w->id) != NULL)
control_notify_write(c, "%%window-add @%u", w->id);
else {
control_notify_write(c, "%%unlinked-window-add @%u",
w->id);
}
control_write(c, "%%window-add @%u", w->id);
else
control_write(c, "%%unlinked-window-add @%u", w->id);
}
}
/* Notify control clients that a window was renamed. */
static void
control_window_renamed_cb(__unused const char *name, struct event_payload *ep,
__unused void *sink_data)
void
control_notify_window_renamed(struct window *w)
{
struct client *c;
struct session *cs;
struct window *w = event_payload_get_window(ep, "window");
if (w == NULL)
return;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
continue;
cs = c->session;
if (winlink_find_by_window_id(&cs->windows, w->id) != NULL) {
control_notify_write(c, "%%window-renamed @%u %s",
w->id, w->name);
control_write(c, "%%window-renamed @%u %s", w->id,
w->name);
} else {
control_notify_write(c,
"%%unlinked-window-renamed @%u %s", w->id, w->name);
control_write(c, "%%unlinked-window-renamed @%u %s",
w->id, w->name);
}
}
}
/* Notify control clients that a client changed session. */
static void
control_client_session_changed_cb(__unused const char *name,
struct event_payload *ep, __unused void *sink_data)
void
control_notify_client_session_changed(struct client *cc)
{
struct client *cc = event_payload_get_client(ep, "client");
struct client *c;
struct session *s;
if (cc == NULL || cc->session == NULL)
if (cc->session == NULL)
return;
s = cc->session;
@@ -212,163 +162,101 @@ control_client_session_changed_cb(__unused const char *name,
continue;
if (cc == c) {
control_notify_write(c, "%%session-changed $%u %s",
s->id, s->name);
} else {
control_notify_write(c,
"%%client-session-changed %s $%u %s", cc->name,
s->id, s->name);
}
}
}
/* Notify control clients that a client detached. */
static void
control_client_detached_cb(__unused const char *name, struct event_payload *ep,
__unused void *sink_data)
{
struct client *cc = event_payload_get_client(ep, "client");
struct client *c;
if (cc == NULL)
return;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_notify_write(c, "%%client-detached %s", cc->name);
}
}
/* Notify control clients that a session was renamed. */
static void
control_session_renamed_cb(__unused const char *name, struct event_payload *ep,
__unused void *sink_data)
{
struct session *s = event_payload_get_session(ep, "session");
struct client *c;
if (s == NULL)
return;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_notify_write(c, "%%session-renamed $%u %s", s->id,
control_write(c, "%%session-changed $%u %s", s->id,
s->name);
} else {
control_write(c, "%%client-session-changed %s $%u %s",
cc->name, s->id, s->name);
}
}
}
/* Notify control clients that sessions changed. */
static void
control_session_created_cb(__unused const char *name,
__unused struct event_payload *ep, __unused void *sink_data)
{
struct client *c;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_notify_write(c, "%%sessions-changed");
}
}
/* Notify control clients that sessions changed. */
static void
control_session_closed_cb(__unused const char *name,
__unused struct event_payload *ep, __unused void *sink_data)
{
struct client *c;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_notify_write(c, "%%sessions-changed");
}
}
/* Notify control clients that the current window changed. */
static void
control_session_window_changed_cb(__unused const char *name,
struct event_payload *ep, __unused void *sink_data)
{
struct session *s = event_payload_get_session(ep, "session");
struct client *c;
/*
* A deferred session-window-changed notification can fire after the
* session has been destroyed (which sets curw to NULL) but is kept
* alive by the notification's reference. Skip the notification.
*/
if (s == NULL || s->curw == NULL)
return;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_notify_write(c, "%%session-window-changed $%u @%u",
s->id, s->curw->window->id);
}
}
/* Notify control clients that a paste buffer changed. */
static void
control_paste_buffer_changed_cb(__unused const char *name,
struct event_payload *ep, __unused void *sink_data)
{
const char *pbname = event_payload_get_string(ep, "paste_buffer");
struct client *c;
if (pbname == NULL)
return;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_notify_write(c, "%%paste-buffer-changed %s", pbname);
}
}
/* Notify control clients that a paste buffer was deleted. */
static void
control_paste_buffer_deleted_cb(__unused const char *name,
struct event_payload *ep, __unused void *sink_data)
{
const char *pbname = event_payload_get_string(ep, "paste_buffer");
struct client *c;
if (pbname == NULL)
return;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_notify_write(c, "%%paste-buffer-deleted %s", pbname);
}
}
/* Build control event sinks. */
void
control_build_events(void)
control_notify_client_detached(struct client *cc)
{
/* Control event sink callbacks. */
static struct {
const char *name;
events_cb cb;
} events[] = {
{ "pane-mode-changed", control_pane_mode_changed_cb },
{ "window-layout-changed", control_window_layout_changed_cb },
{ "window-pane-changed", control_window_pane_changed_cb },
{ "window-unlinked", control_window_unlinked_cb },
{ "window-linked", control_window_linked_cb },
{ "window-renamed", control_window_renamed_cb },
{ "client-session-changed", control_client_session_changed_cb },
{ "client-detached", control_client_detached_cb },
{ "session-renamed", control_session_renamed_cb },
{ "session-created", control_session_created_cb },
{ "session-closed", control_session_closed_cb },
{ "session-window-changed", control_session_window_changed_cb },
{ "paste-buffer-changed", control_paste_buffer_changed_cb },
{ "paste-buffer-deleted", control_paste_buffer_deleted_cb }
};
u_int i;
struct client *c;
for (i = 0; i < nitems(events); i++)
events_add_sink(events[i].name, events[i].cb, NULL);
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)
{
struct client *c;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_write(c, "%%session-renamed $%u %s", s->id, s->name);
}
}
void
control_notify_session_created(__unused struct session *s)
{
struct client *c;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_write(c, "%%sessions-changed");
}
}
void
control_notify_session_closed(__unused struct session *s)
{
struct client *c;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_write(c, "%%sessions-changed");
}
}
void
control_notify_session_window_changed(struct session *s)
{
struct client *c;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_write(c, "%%session-window-changed $%u @%u", s->id,
s->curw->window->id);
}
}
void
control_notify_paste_buffer_changed(const char *name)
{
struct client *c;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_write(c, "%%paste-buffer-changed %s", name);
}
}
void
control_notify_paste_buffer_deleted(const char *name)
{
struct client *c;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_write(c, "%%paste-buffer-deleted %s", name);
}
}

743
control.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: control.c,v 1.66 2026/08/18 07:43:44 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2012 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -19,9 +19,6 @@
#include <sys/types.h>
#include <errno.h>
#include <event.h>
#include <poll.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
@@ -53,17 +50,6 @@ struct control_block {
TAILQ_ENTRY(control_block) all_entry;
};
/*
* A notification line deferred because it was generated while a command's
* %begin/%end guard block was open. Notifications must never appear inside a
* guard block, so they are held here and flushed once the block closes.
*/
struct control_line {
char *line;
TAILQ_ENTRY(control_line) entry;
};
/* Control client pane. */
struct control_pane {
u_int pane;
@@ -89,20 +75,45 @@ struct control_pane {
};
RB_HEAD(control_panes, control_pane);
/* Control client window size. */
struct control_window {
u_int window;
u_int sx;
u_int sy;
/* Subscription pane. */
struct control_sub_pane {
u_int pane;
u_int idx;
char *last;
RB_ENTRY(control_window) entry;
RB_ENTRY(control_sub_pane) entry;
};
RB_HEAD(control_windows, control_window);
RB_HEAD(control_sub_panes, control_sub_pane);
/* Subscription window. */
struct control_sub_window {
u_int window;
u_int idx;
char *last;
RB_ENTRY(control_sub_window) entry;
};
RB_HEAD(control_sub_windows, control_sub_window);
/* Control client subscription. */
struct control_sub {
char *name;
char *format;
enum control_sub_type type;
u_int id;
char *last;
struct control_sub_panes panes;
struct control_sub_windows windows;
RB_ENTRY(control_sub) entry;
};
RB_HEAD(control_subs, control_sub);
/* Control client state. */
struct control_state {
struct control_panes panes;
struct control_windows windows;
TAILQ_HEAD(, control_pane) pending_list;
u_int pending_count;
@@ -112,14 +123,8 @@ struct control_state {
struct bufferevent *read_event;
struct bufferevent *write_event;
struct monitor_set *subs;
/*
* Depth of open %begin/%end guard blocks and notifications deferred
* until the outermost block closes.
*/
int guard_depth;
TAILQ_HEAD(, control_line) deferred;
struct control_subs subs;
struct event subs_timer;
};
/* Low and high watermarks. */
@@ -149,17 +154,72 @@ control_pane_cmp(struct control_pane *cp1, struct control_pane *cp2)
}
RB_GENERATE_STATIC(control_panes, control_pane, entry, control_pane_cmp);
/* Compare control windows. */
/* Compare client subs. */
static int
control_window_cmp(struct control_window *cw1, struct control_window *cw2)
control_sub_cmp(struct control_sub *csub1, struct control_sub *csub2)
{
if (cw1->window < cw2->window)
return (strcmp(csub1->name, csub2->name));
}
RB_GENERATE_STATIC(control_subs, control_sub, entry, control_sub_cmp);
/* Compare client subscription panes. */
static int
control_sub_pane_cmp(struct control_sub_pane *csp1,
struct control_sub_pane *csp2)
{
if (csp1->pane < csp2->pane)
return (-1);
if (cw1->window > cw2->window)
if (csp1->pane > csp2->pane)
return (1);
if (csp1->idx < csp2->idx)
return (-1);
if (csp1->idx > csp2->idx)
return (1);
return (0);
}
RB_GENERATE_STATIC(control_windows, control_window, entry, control_window_cmp);
RB_GENERATE_STATIC(control_sub_panes, control_sub_pane, entry,
control_sub_pane_cmp);
/* Compare client subscription windows. */
static int
control_sub_window_cmp(struct control_sub_window *csw1,
struct control_sub_window *csw2)
{
if (csw1->window < csw2->window)
return (-1);
if (csw1->window > csw2->window)
return (1);
if (csw1->idx < csw2->idx)
return (-1);
if (csw1->idx > csw2->idx)
return (1);
return (0);
}
RB_GENERATE_STATIC(control_sub_windows, control_sub_window, entry,
control_sub_window_cmp);
/* Free a subscription. */
static void
control_free_sub(struct control_state *cs, struct control_sub *csub)
{
struct control_sub_pane *csp, *csp1;
struct control_sub_window *csw, *csw1;
RB_FOREACH_SAFE(csp, control_sub_panes, &csub->panes, csp1) {
RB_REMOVE(control_sub_panes, &csub->panes, csp);
free(csp);
}
RB_FOREACH_SAFE(csw, control_sub_windows, &csub->windows, csw1) {
RB_REMOVE(control_sub_windows, &csub->windows, csw);
free(csw);
}
free(csub->last);
RB_REMOVE(control_subs, &cs->subs, csub);
free(csub->name);
free(csub->format);
free(csub);
}
/* Free a block. */
static void
@@ -202,66 +262,6 @@ control_add_pane(struct client *c, struct window_pane *wp)
return (cp);
}
/* Get window for this client. */
static struct control_window *
control_get_window(struct client *c, u_int window)
{
struct control_state *cs = c->control_state;
struct control_window cw = { .window = window };
if (cs == NULL)
return (NULL);
return (RB_FIND(control_windows, &cs->windows, &cw));
}
/* Set window size for this client. */
void
control_set_window_size(struct client *c, u_int window, u_int sx, u_int sy)
{
struct control_state *cs = c->control_state;
struct control_window *cw;
if (cs == NULL)
return;
cw = control_get_window(c, window);
if (cw == NULL) {
cw = xcalloc(1, sizeof *cw);
cw->window = window;
RB_INSERT(control_windows, &cs->windows, cw);
}
cw->sx = sx;
cw->sy = sy;
}
/* Get window size for this client. */
int
control_get_window_size(struct client *c, u_int window, u_int *sx, u_int *sy)
{
struct control_window *cw;
if ((cw = control_get_window(c, window)) == NULL)
return (0);
*sx = cw->sx;
*sy = cw->sy;
return (1);
}
/* Clear window size for this client. */
void
control_clear_window_size(struct client *c, u_int window)
{
struct control_state *cs = c->control_state;
struct control_window *cw;
if (cs == NULL)
return;
cw = control_get_window(c, window);
if (cw != NULL) {
RB_REMOVE(control_windows, &cs->windows, cw);
free(cw);
}
}
/* Discard output for a pane. */
static void
control_discard_pane(struct client *c, struct control_pane *cp)
@@ -298,7 +298,6 @@ control_reset_offsets(struct client *c)
struct control_pane *cp, *cp1;
RB_FOREACH_SAFE(cp, control_panes, &cs->panes, cp1) {
control_discard_pane(c, cp);
RB_REMOVE(control_panes, &cs->panes, cp);
free(cp);
}
@@ -353,9 +352,6 @@ control_set_pane_off(struct client *c, struct window_pane *wp)
struct control_pane *cp;
cp = control_add_pane(c, wp);
control_discard_pane(c, cp);
memcpy(&cp->offset, &wp->offset, sizeof cp->offset);
memcpy(&cp->queued, &wp->offset, sizeof cp->queued);
cp->flags |= CONTROL_PANE_OFF;
}
@@ -370,7 +366,7 @@ control_continue_pane(struct client *c, struct window_pane *wp)
cp->flags &= ~CONTROL_PANE_PAUSED;
memcpy(&cp->offset, &wp->offset, sizeof cp->offset);
memcpy(&cp->queued, &wp->offset, sizeof cp->queued);
control_notify_write(c, "%%continue %%%u", wp->id);
control_write(c, "%%continue %%%u", wp->id);
}
}
@@ -384,139 +380,52 @@ control_pause_pane(struct client *c, struct window_pane *wp)
if (~cp->flags & CONTROL_PANE_PAUSED) {
cp->flags |= CONTROL_PANE_PAUSED;
control_discard_pane(c, cp);
control_notify_write(c, "%%pause %%%u", wp->id);
control_write(c, "%%pause %%%u", wp->id);
}
}
/*
* Reset a pane after its buffer has been replaced: drop any output still
* queued from the old buffer and start again from the pane's own offset.
*/
void
control_reset_pane(struct client *c, struct window_pane *wp)
/* Write a line. */
static void printflike(2, 0)
control_vwrite(struct client *c, const char *fmt, va_list ap)
{
struct control_pane *cp;
struct control_state *cs = c->control_state;
char *s;
if (c->control_state == NULL)
return;
cp = control_get_pane(c, wp);
if (cp == NULL)
return;
control_discard_pane(c, cp);
memcpy(&cp->offset, &wp->offset, sizeof cp->offset);
memcpy(&cp->queued, &wp->offset, sizeof cp->queued);
xvasprintf(&s, fmt, ap);
log_debug("%s: %s: writing line: %s", __func__, c->name, s);
bufferevent_write(cs->write_event, s, strlen(s));
bufferevent_write(cs->write_event, "\n", 1);
bufferevent_enable(cs->write_event, EV_WRITE);
free(s);
}
/* Write an already-formatted line, queueing it behind %output if needed. */
static void
control_write_line(struct client *c, char *line)
/* Write a line. */
void
control_write(struct client *c, const char *fmt, ...)
{
struct control_state *cs = c->control_state;
struct control_block *cb;
va_list ap;
va_start(ap, fmt);
if (TAILQ_EMPTY(&cs->all_blocks)) {
log_debug("%s: %s: writing line: %s", __func__, c->name, line);
bufferevent_write(cs->write_event, line, strlen(line));
bufferevent_write(cs->write_event, "\n", 1);
bufferevent_enable(cs->write_event, EV_WRITE);
free(line);
control_vwrite(c, fmt, ap);
va_end(ap);
return;
}
cb = xcalloc(1, sizeof *cb);
cb->line = line;
xvasprintf(&cb->line, fmt, ap);
TAILQ_INSERT_TAIL(&cs->all_blocks, cb, all_entry);
cb->t = get_timer();
log_debug("%s: %s: storing line: %s", __func__, c->name, cb->line);
bufferevent_enable(cs->write_event, EV_WRITE);
}
/* Flush notifications that were deferred while a guard block was open. */
static void
control_flush_deferred(struct client *c)
{
struct control_state *cs = c->control_state;
struct control_line *cl, *cl1;
TAILQ_FOREACH_SAFE(cl, &cs->deferred, entry, cl1) {
TAILQ_REMOVE(&cs->deferred, cl, entry);
control_write_line(c, cl->line);
free(cl);
}
}
/*
* Write a line of command output or error text. This is a sink for arbitrary
* user-controlled text (command output, capture-pane, error messages), so it
* must never try to interpret the content: guard tracking is done only in
* control_write_guard.
*/
void
control_write(struct client *c, const char *fmt, ...)
{
va_list ap;
char *line;
va_start(ap, fmt);
xvasprintf(&line, fmt, ap);
va_end(ap);
control_write_line(c, line);
}
/*
* Write a %begin, %end or %error guard around a command's output. This is the
* only place guard lines are produced, so the block depth is maintained here;
* when the outermost block closes any deferred notifications are flushed after
* it. "guard" is always one of the fixed strings from cmdq_guard, never user
* text.
*/
void
control_write_guard(struct client *c, const char *guard, long t, u_int number,
int flags)
{
struct control_state *cs = c->control_state;
char *line;
if (strcmp(guard, "begin") == 0)
cs->guard_depth++;
xasprintf(&line, "%%%s %ld %u %d", guard, t, number, flags);
control_write_line(c, line);
if (strcmp(guard, "begin") != 0 && cs->guard_depth > 0 &&
--cs->guard_depth == 0)
control_flush_deferred(c);
}
/*
* Write a notification line. Notifications must never appear inside a command's
* %begin/%end guard block, so if one is open the line is deferred until it
* closes.
*/
void
control_notify_write(struct client *c, const char *fmt, ...)
{
struct control_state *cs = c->control_state;
struct control_line *cl;
va_list ap;
char *line;
va_start(ap, fmt);
xvasprintf(&line, fmt, ap);
va_end(ap);
if (cs->guard_depth == 0) {
control_write_line(c, line);
return;
}
log_debug("%s: %s: deferring notification: %s", __func__, c->name,
line);
cl = xcalloc(1, sizeof *cl);
cl->line = line;
TAILQ_INSERT_TAIL(&cs->deferred, cl, entry);
}
/* Check age for this pane. */
@@ -543,7 +452,7 @@ control_check_age(struct client *c, struct window_pane *wp,
return (0);
cp->flags |= CONTROL_PANE_PAUSED;
control_discard_pane(c, cp);
control_notify_write(c, "%%pause %%%u", wp->id);
control_write(c, "%%pause %%%u", wp->id);
} else {
if (age < CONTROL_MAXIMUM_AGE)
return (0);
@@ -566,7 +475,7 @@ control_write_output(struct client *c, struct window_pane *wp)
if (winlink_find_by_window(&c->session->windows, wp->window) == NULL)
return;
if (c->flags & (CONTROL_IGNORE_FLAGS|CLIENT_EXIT)) {
if (c->flags & CONTROL_IGNORE_FLAGS) {
cp = control_get_pane(c, wp);
if (cp != NULL)
goto ignore;
@@ -641,9 +550,8 @@ control_read_callback(__unused struct bufferevent *bufev, void *data)
struct control_state *cs = c->control_state;
struct evbuffer *buffer = cs->read_event->input;
char *line, *error;
struct cmd_parse_tree *tree;
struct cmdq_item *new_item;
struct cmdq_state *state;
enum cmd_parse_status status;
for (;;) {
line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_LF);
@@ -657,14 +565,9 @@ control_read_callback(__unused struct bufferevent *bufev, void *data)
}
state = cmdq_new_state(NULL, NULL, CMDQ_STATE_CONTROL);
tree = cmd_parse_from_string(line, NULL, &error);
if (tree == NULL)
status = cmd_parse_and_append(line, NULL, c, state, &error);
if (status == CMD_PARSE_ERROR)
cmdq_append(c, cmdq_get_callback(control_error, error));
else {
new_item = cmd_invoke_get(tree, state, NULL);
cmdq_append(c, new_item);
cmd_parse_free(tree);
}
cmdq_free_state(state);
free(line);
@@ -682,53 +585,6 @@ control_all_done(struct client *c)
return (EVBUFFER_LENGTH(cs->write_event->output) == 0);
}
/*
* Wait for the terminal to send an empty line or close, used by a control
* client after printing %exit so a wrapping terminal (such as iTerm2) can
* finish reading.
*/
void
control_wait_exit(int fd)
{
struct pollfd pfd;
struct evbuffer *evb;
char *line;
int n;
evb = evbuffer_new();
if (evb == NULL)
fatalx("out of memory");
for (;;) {
line = evbuffer_readln(evb, NULL, EVBUFFER_EOL_LF);
if (line != NULL) {
if (*line == '\0') { /* empty line, stop */
free(line);
break;
}
free(line);
continue; /* drain buffered lines first */
}
memset(&pfd, 0, sizeof pfd);
pfd.fd = fd;
pfd.events = POLLIN;
if (poll(&pfd, 1, INFTIM) == -1) {
if (errno == EINTR)
continue;
break;
}
n = evbuffer_read(evb, fd, -1);
if (n == 0)
break;
if (n == -1 && errno != EAGAIN && errno != EINTR)
break;
}
evbuffer_free(evb);
}
/* Flush all blocks until output. */
static void
control_flush_all_blocks(struct client *c)
@@ -754,7 +610,7 @@ control_append_data(struct client *c, struct control_pane *cp, uint64_t age,
struct evbuffer *message, struct window_pane *wp, size_t size)
{
u_char *new_data;
size_t new_size, start;
size_t new_size;
u_int i;
if (message == NULL) {
@@ -773,16 +629,10 @@ control_append_data(struct client *c, struct control_pane *cp, uint64_t age,
if (new_size < size)
fatalx("not enough data: %zu < %zu", new_size, size);
for (i = 0; i < size; i++) {
if (new_data[i] < ' ' || new_data[i] == '\\') {
if (new_data[i] < ' ' || new_data[i] == '\\')
evbuffer_add_printf(message, "\\%03o", new_data[i]);
} else {
start = i;
while (i + 1 < size &&
new_data[i + 1] >= ' ' &&
new_data[i + 1] != '\\')
i++;
evbuffer_add(message, new_data + start, i - start + 1);
}
else
evbuffer_add_printf(message, "%c", new_data[i]);
}
window_pane_update_used_data(wp, &cp->offset, size);
return (message);
@@ -904,33 +754,6 @@ control_write_callback(__unused struct bufferevent *bufev, void *data)
bufferevent_disable(cs->write_event, EV_WRITE);
}
/* Write a subscription change. */
static void
control_sub_change(struct monitor_change *change, __unused void *data)
{
struct client *c = change->c;
struct session *s = change->s;
struct winlink *wl = change->wl;
struct window_pane *wp = change->wp;
struct window *w;
if (wp != NULL) {
w = wp->window;
control_notify_write(c,
"%%subscription-changed %s $%u @%u %u %%%u : %s",
change->name, s->id, w->id, wl->idx, wp->id, change->value);
} else if (wl != NULL) {
w = wl->window;
control_notify_write(c,
"%%subscription-changed %s $%u @%u %u - : %s",
change->name, s->id, w->id, wl->idx, change->value);
} else {
control_notify_write(c,
"%%subscription-changed %s $%u - - - : %s",
change->name, s->id, change->value);
}
}
/* Initialize for control mode. */
void
control_start(struct client *c)
@@ -946,11 +769,9 @@ control_start(struct client *c)
cs = c->control_state = xcalloc(1, sizeof *cs);
RB_INIT(&cs->panes);
RB_INIT(&cs->windows);
TAILQ_INIT(&cs->pending_list);
TAILQ_INIT(&cs->all_blocks);
TAILQ_INIT(&cs->deferred);
cs->subs = monitor_create_client(c, control_sub_change, NULL);
RB_INIT(&cs->subs);
cs->read_event = bufferevent_new(c->fd, control_read_callback,
control_write_callback, control_error_callback, c);
@@ -993,63 +814,292 @@ control_discard(struct client *c)
bufferevent_disable(cs->read_event, EV_READ);
}
/* Discard all tmux-owned queued control blocks and stop writing. */
void
control_discard_all(struct client *c)
{
struct control_state *cs = c->control_state;
struct control_block *cb, *cb1;
control_discard(c);
TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1)
control_free_block(cs, cb);
bufferevent_disable(cs->write_event, EV_WRITE);
}
/* Stop control mode. */
void
control_stop(struct client *c)
{
struct control_state *cs = c->control_state;
struct control_block *cb, *cb1;
struct control_window *cw, *cw1;
struct control_line *cl, *cl1;
if (cs == NULL)
return;
monitor_destroy(cs->subs);
TAILQ_FOREACH_SAFE(cl, &cs->deferred, entry, cl1) {
TAILQ_REMOVE(&cs->deferred, cl, entry);
free(cl->line);
free(cl);
}
struct control_sub *csub, *csub1;
if (~c->flags & CLIENT_CONTROLCONTROL)
bufferevent_free(cs->write_event);
bufferevent_free(cs->read_event);
control_reset_offsets(c);
RB_FOREACH_SAFE(cw, control_windows, &cs->windows, cw1) {
RB_REMOVE(control_windows, &cs->windows, cw);
free(cw);
}
RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1)
control_free_sub(cs, csub);
if (evtimer_initialized(&cs->subs_timer))
evtimer_del(&cs->subs_timer);
TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1)
control_free_block(cs, cb);
control_reset_offsets(c);
c->control_state = NULL;
free(cs);
}
/* Check session subscription. */
static void
control_check_subs_session(struct client *c, struct control_sub *csub)
{
struct session *s = c->session;
struct format_tree *ft;
char *value;
ft = format_create_defaults(NULL, c, s, NULL, NULL);
value = format_expand(ft, csub->format);
format_free(ft);
if (csub->last != NULL && strcmp(value, csub->last) == 0) {
free(value);
return;
}
control_write(c,
"%%subscription-changed %s $%u - - - : %s",
csub->name, s->id, value);
free(csub->last);
csub->last = value;
}
/* Check pane subscription. */
static void
control_check_subs_pane(struct client *c, struct control_sub *csub)
{
struct session *s = c->session;
struct window_pane *wp;
struct window *w;
struct winlink *wl;
struct format_tree *ft;
char *value;
struct control_sub_pane *csp, find;
wp = window_pane_find_by_id(csub->id);
if (wp == NULL || wp->fd == -1)
return;
w = wp->window;
TAILQ_FOREACH(wl, &w->winlinks, wentry) {
if (wl->session != s)
continue;
ft = format_create_defaults(NULL, c, s, wl, wp);
value = format_expand(ft, csub->format);
format_free(ft);
find.pane = wp->id;
find.idx = wl->idx;
csp = RB_FIND(control_sub_panes, &csub->panes, &find);
if (csp == NULL) {
csp = xcalloc(1, sizeof *csp);
csp->pane = wp->id;
csp->idx = wl->idx;
RB_INSERT(control_sub_panes, &csub->panes, csp);
}
if (csp->last != NULL && strcmp(value, csp->last) == 0) {
free(value);
continue;
}
control_write(c,
"%%subscription-changed %s $%u @%u %u %%%u : %s",
csub->name, s->id, w->id, wl->idx, wp->id, value);
free(csp->last);
csp->last = value;
}
}
/* Check all panes subscription. */
static void
control_check_subs_all_panes(struct client *c, struct control_sub *csub)
{
struct session *s = c->session;
struct window_pane *wp;
struct window *w;
struct winlink *wl;
struct format_tree *ft;
char *value;
struct control_sub_pane *csp, find;
RB_FOREACH(wl, winlinks, &s->windows) {
w = wl->window;
TAILQ_FOREACH(wp, &w->panes, entry) {
ft = format_create_defaults(NULL, c, s, wl, wp);
value = format_expand(ft, csub->format);
format_free(ft);
find.pane = wp->id;
find.idx = wl->idx;
csp = RB_FIND(control_sub_panes, &csub->panes, &find);
if (csp == NULL) {
csp = xcalloc(1, sizeof *csp);
csp->pane = wp->id;
csp->idx = wl->idx;
RB_INSERT(control_sub_panes, &csub->panes, csp);
}
if (csp->last != NULL &&
strcmp(value, csp->last) == 0) {
free(value);
continue;
}
control_write(c,
"%%subscription-changed %s $%u @%u %u %%%u : %s",
csub->name, s->id, w->id, wl->idx, wp->id, value);
free(csp->last);
csp->last = value;
}
}
}
/* Check window subscription. */
static void
control_check_subs_window(struct client *c, struct control_sub *csub)
{
struct session *s = c->session;
struct window *w;
struct winlink *wl;
struct format_tree *ft;
char *value;
struct control_sub_window *csw, find;
w = window_find_by_id(csub->id);
if (w == NULL)
return;
TAILQ_FOREACH(wl, &w->winlinks, wentry) {
if (wl->session != s)
continue;
ft = format_create_defaults(NULL, c, s, wl, NULL);
value = format_expand(ft, csub->format);
format_free(ft);
find.window = w->id;
find.idx = wl->idx;
csw = RB_FIND(control_sub_windows, &csub->windows, &find);
if (csw == NULL) {
csw = xcalloc(1, sizeof *csw);
csw->window = w->id;
csw->idx = wl->idx;
RB_INSERT(control_sub_windows, &csub->windows, csw);
}
if (csw->last != NULL && strcmp(value, csw->last) == 0) {
free(value);
continue;
}
control_write(c,
"%%subscription-changed %s $%u @%u %u - : %s",
csub->name, s->id, w->id, wl->idx, value);
free(csw->last);
csw->last = value;
}
}
/* Check all windows subscription. */
static void
control_check_subs_all_windows(struct client *c, struct control_sub *csub)
{
struct session *s = c->session;
struct window *w;
struct winlink *wl;
struct format_tree *ft;
char *value;
struct control_sub_window *csw, find;
RB_FOREACH(wl, winlinks, &s->windows) {
w = wl->window;
ft = format_create_defaults(NULL, c, s, wl, NULL);
value = format_expand(ft, csub->format);
format_free(ft);
find.window = w->id;
find.idx = wl->idx;
csw = RB_FIND(control_sub_windows, &csub->windows, &find);
if (csw == NULL) {
csw = xcalloc(1, sizeof *csw);
csw->window = w->id;
csw->idx = wl->idx;
RB_INSERT(control_sub_windows, &csub->windows, csw);
}
if (csw->last != NULL && strcmp(value, csw->last) == 0) {
free(value);
continue;
}
control_write(c,
"%%subscription-changed %s $%u @%u %u - : %s",
csub->name, s->id, w->id, wl->idx, value);
free(csw->last);
csw->last = value;
}
}
/* Check subscriptions timer. */
static void
control_check_subs_timer(__unused int fd, __unused short events, void *data)
{
struct client *c = data;
struct control_state *cs = c->control_state;
struct control_sub *csub, *csub1;
struct timeval tv = { .tv_sec = 1 };
log_debug("%s: timer fired", __func__);
evtimer_add(&cs->subs_timer, &tv);
RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1) {
switch (csub->type) {
case CONTROL_SUB_SESSION:
control_check_subs_session(c, csub);
break;
case CONTROL_SUB_PANE:
control_check_subs_pane(c, csub);
break;
case CONTROL_SUB_ALL_PANES:
control_check_subs_all_panes(c, csub);
break;
case CONTROL_SUB_WINDOW:
control_check_subs_window(c, csub);
break;
case CONTROL_SUB_ALL_WINDOWS:
control_check_subs_all_windows(c, csub);
break;
}
}
}
/* Add a subscription. */
void
control_add_sub(struct client *c, const char *name, enum monitor_type type,
control_add_sub(struct client *c, const char *name, enum control_sub_type type,
int id, const char *format)
{
struct control_state *cs = c->control_state;
struct control_sub *csub, find;
struct timeval tv = { .tv_sec = 1 };
monitor_add(cs->subs, name, type, id, format, MONITOR_NOTIFY_INITIAL);
find.name = (char *)name;
if ((csub = RB_FIND(control_subs, &cs->subs, &find)) != NULL)
control_free_sub(cs, csub);
csub = xcalloc(1, sizeof *csub);
csub->name = xstrdup(name);
csub->type = type;
csub->id = id;
csub->format = xstrdup(format);
RB_INSERT(control_subs, &cs->subs, csub);
RB_INIT(&csub->panes);
RB_INIT(&csub->windows);
if (!evtimer_initialized(&cs->subs_timer))
evtimer_set(&cs->subs_timer, control_check_subs_timer, c);
if (!evtimer_pending(&cs->subs_timer, NULL))
evtimer_add(&cs->subs_timer, &tv);
}
/* Remove a subscription. */
@@ -1057,6 +1107,11 @@ void
control_remove_sub(struct client *c, const char *name)
{
struct control_state *cs = c->control_state;
struct control_sub *csub, find;
monitor_remove(cs->subs, name);
find.name = (char *)name;
if ((csub = RB_FIND(control_subs, &cs->subs, &find)) != NULL)
control_free_sub(cs, csub);
if (RB_EMPTY(&cs->subs))
evtimer_del(&cs->subs_timer);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: environ.c,v 1.31 2026/06/13 20:07:30 nicm Exp $ */
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -57,9 +57,6 @@ environ_free(struct environ *env)
{
struct environ_entry *envent, *envent1;
if (env == NULL)
return;
RB_FOREACH_SAFE(envent, environ, env, envent1) {
RB_REMOVE(environ, env, envent);
free(envent->name);
@@ -152,8 +149,7 @@ environ_clear(struct environ *env, const char *name)
void
environ_put(struct environ *env, const char *var, int flags)
{
char *name;
const char *value;
char *name, *value;
value = strchr(var, '=');
if (value == NULL)
@@ -267,6 +263,11 @@ environ_for_session(struct session *s, int no_TERM)
environ_set(env, "TERM_PROGRAM", 0, "%s", "tmux");
environ_set(env, "TERM_PROGRAM_VERSION", 0, "%s", getversion());
environ_set(env, "COLORTERM", 0, "truecolor");
} else {
environ_unset(env, "TERM");
environ_unset(env, "TERM_PROGRAM");
environ_unset(env, "TERM_PROGRAM_VERSION");
environ_unset(env, "COLORTERM");
}
#ifdef HAVE_SYSTEMD

View File

@@ -1,672 +0,0 @@
/* $OpenBSD: events-payload.c,v 1.1 2026/07/10 13:38:45 nicm Exp $ */
/*
* Copyright (c) 2026 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 USE, DATA OR PROFITS, WHETHER
* IN 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 <event.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "tmux.h"
/* Event payload item. */
struct event_payload_item {
char *name;
enum event_payload_type type;
union {
char *string;
time_t time;
int number;
u_int unsigned_number;
struct client *client;
struct session *session;
struct window *window;
struct window_pane *pane;
struct {
void *ptr;
event_payload_free_cb free_cb;
event_payload_print_cb print_cb;
} pointer;
};
RB_ENTRY(event_payload_item) entry;
};
RB_HEAD(event_payload_tree, event_payload_item);
struct event_payload {
struct event_payload_tree items;
struct cmd_find_state target;
};
static int
event_payload_cmp(struct event_payload_item *epi1,
struct event_payload_item *epi2)
{
return (strcmp(epi1->name, epi2->name));
}
RB_GENERATE_STATIC(event_payload_tree, event_payload_item, entry,
event_payload_cmp);
/* Find an item. */
static struct event_payload_item *
event_payload_find(struct event_payload *ep, const char *name)
{
struct event_payload_item find = { .name = (char *)name };
return (RB_FIND(event_payload_tree, &ep->items, &find));
}
/* Free the target in a payload. */
static void
event_payload_free_target(struct event_payload *ep)
{
struct cmd_find_state *target = &ep->target;
if (target->s != NULL)
session_remove_ref(target->s, __func__);
if (target->w != NULL)
window_remove_ref(target->w, __func__);
if (target->wp != NULL)
window_pane_remove_ref(target->wp, __func__);
cmd_find_clear_state(target, 0);
}
/* Free the value in an item. */
static void
event_payload_free_value(struct event_payload_item *epi)
{
switch (epi->type) {
case EVENT_PAYLOAD_STRING:
free(epi->string);
break;
case EVENT_PAYLOAD_CLIENT:
server_client_unref(epi->client);
break;
case EVENT_PAYLOAD_SESSION:
session_remove_ref(epi->session, __func__);
break;
case EVENT_PAYLOAD_WINDOW:
window_remove_ref(epi->window, __func__);
break;
case EVENT_PAYLOAD_PANE:
window_pane_remove_ref(epi->pane, __func__);
break;
case EVENT_PAYLOAD_POINTER:
if (epi->pointer.free_cb != NULL)
epi->pointer.free_cb(epi->pointer.ptr);
break;
case EVENT_PAYLOAD_INT:
case EVENT_PAYLOAD_UINT:
case EVENT_PAYLOAD_TIME:
break;
}
}
/* Set an item. */
static void
event_payload_set_item(struct event_payload *ep, const char *name,
struct event_payload_item *new)
{
struct event_payload_item *old;
new->name = xstrdup(name);
old = RB_INSERT(event_payload_tree, &ep->items, new);
if (old != NULL) {
RB_REMOVE(event_payload_tree, &ep->items, old);
event_payload_free_value(old);
free(old->name);
free(old);
RB_INSERT(event_payload_tree, &ep->items, new);
}
}
/* Create an event payload. */
struct event_payload *
event_payload_create(void)
{
struct event_payload *ep;
ep = xcalloc(1, sizeof *ep);
RB_INIT(&ep->items);
cmd_find_clear_state(&ep->target, 0);
return (ep);
}
/* Free an event payload. */
void
event_payload_free(struct event_payload *ep)
{
struct event_payload_item *epi, *epi1;
if (ep != NULL) {
RB_FOREACH_SAFE(epi, event_payload_tree, &ep->items, epi1) {
RB_REMOVE(event_payload_tree, &ep->items, epi);
event_payload_free_value(epi);
free(epi->name);
free(epi);
}
event_payload_free_target(ep);
free(ep);
}
}
/* Set the target. */
void
event_payload_set_target(struct event_payload *ep, struct cmd_find_state *fs)
{
struct cmd_find_state *target = &ep->target;
event_payload_free_target(ep);
if (fs->s != NULL) {
session_add_ref(fs->s, __func__);
target->s = fs->s;
}
if (fs->wl != NULL) {
target->idx = fs->wl->idx;
if (target->s == NULL) {
session_add_ref(fs->wl->session, __func__);
target->s = fs->wl->session;
}
} else
target->idx = -1;
if (fs->w != NULL) {
window_add_ref(fs->w, __func__);
target->w = fs->w;
} else if (fs->wl != NULL) {
window_add_ref(fs->wl->window, __func__);
target->w = fs->wl->window;
}
if (fs->wp != NULL) {
window_pane_add_ref(fs->wp, __func__);
target->wp = fs->wp;
}
}
/* Get the target. */
int
event_payload_get_target(struct event_payload *ep, struct cmd_find_state *fs)
{
struct cmd_find_state *t = &ep->target;
struct winlink *wl = NULL;
int flags = fs->flags;
if (t->idx != -1 &&
t->s != NULL &&
t->w != NULL &&
session_alive(t->s)) {
wl = winlink_find_by_index(&t->s->windows, t->idx);
if (wl != NULL && wl->window != t->w)
wl = NULL;
}
cmd_find_clear_state(fs, flags);
fs->s = t->s;
fs->w = t->w;
fs->wp = t->wp;
fs->wl = wl;
fs->idx = (wl != NULL ? wl->idx : -1);
if (cmd_find_valid_state(fs))
return (1);
if (wl != NULL &&
t->wp != NULL &&
window_has_pane(wl->window, t->wp)) {
cmd_find_from_winlink_pane(fs, wl, t->wp, flags);
if (cmd_find_valid_state(fs))
return (1);
}
if (t->wp != NULL &&
cmd_find_from_pane(fs, t->wp, flags) == 0 &&
cmd_find_valid_state(fs))
return (1);
if (wl != NULL) {
cmd_find_from_winlink(fs, wl, flags);
if (cmd_find_valid_state(fs))
return (1);
}
if (t->s != NULL &&
t->w != NULL &&
session_alive(t->s) &&
cmd_find_from_session_window(fs, t->s, t->w, flags) == 0 &&
cmd_find_valid_state(fs))
return (1);
if (t->s != NULL && session_alive(t->s)) {
cmd_find_from_session(fs, t->s, flags);
if (cmd_find_valid_state(fs))
return (1);
}
if (cmd_find_from_nothing(fs, flags) == 0)
return (1);
cmd_find_clear_state(fs, flags);
return (0);
}
/* Set a string item. */
void
event_payload_set_string(struct event_payload *ep, const char *name,
const char *fmt, ...)
{
struct event_payload_item *epi;
va_list ap;
va_start(ap, fmt);
epi = xcalloc(1, sizeof *epi);
epi->type = EVENT_PAYLOAD_STRING;
xvasprintf(&epi->string, fmt, ap);
event_payload_set_item(ep, name, epi);
va_end(ap);
}
/* Set a time item. */
void
event_payload_set_time(struct event_payload *ep, const char *name,
time_t value)
{
struct event_payload_item *epi;
epi = xcalloc(1, sizeof *epi);
epi->type = EVENT_PAYLOAD_TIME;
epi->time = value;
event_payload_set_item(ep, name, epi);
}
/* Set a number item. */
void
event_payload_set_int(struct event_payload *ep, const char *name, int value)
{
struct event_payload_item *epi;
epi = xcalloc(1, sizeof *epi);
epi->type = EVENT_PAYLOAD_INT;
epi->number = value;
event_payload_set_item(ep, name, epi);
}
/* Set an unsigned number item. */
void
event_payload_set_uint(struct event_payload *ep, const char *name, u_int value)
{
struct event_payload_item *epi;
epi = xcalloc(1, sizeof *epi);
epi->type = EVENT_PAYLOAD_UINT;
epi->unsigned_number = value;
event_payload_set_item(ep, name, epi);
}
/* Set a client item. */
void
event_payload_set_client(struct event_payload *ep, const char *name,
struct client *c)
{
struct event_payload_item *epi;
c->references++;
epi = xcalloc(1, sizeof *epi);
epi->type = EVENT_PAYLOAD_CLIENT;
epi->client = c;
event_payload_set_item(ep, name, epi);
}
/* Set a session item. */
void
event_payload_set_session(struct event_payload *ep, const char *name,
struct session *s)
{
struct event_payload_item *epi;
session_add_ref(s, __func__);
epi = xcalloc(1, sizeof *epi);
epi->type = EVENT_PAYLOAD_SESSION;
epi->session = s;
event_payload_set_item(ep, name, epi);
}
/* Set a window item. */
void
event_payload_set_window(struct event_payload *ep, const char *name,
struct window *w)
{
struct event_payload_item *epi;
window_add_ref(w, __func__);
epi = xcalloc(1, sizeof *epi);
epi->type = EVENT_PAYLOAD_WINDOW;
epi->window = w;
event_payload_set_item(ep, name, epi);
}
/* Set a pane item. */
void
event_payload_set_pane(struct event_payload *ep, const char *name,
struct window_pane *wp)
{
struct event_payload_item *epi;
window_pane_add_ref(wp, __func__);
epi = xcalloc(1, sizeof *epi);
epi->type = EVENT_PAYLOAD_PANE;
epi->pane = wp;
event_payload_set_item(ep, name, epi);
}
/* Set a pointer item. */
void
event_payload_set_pointer(struct event_payload *ep, const char *name,
void *ptr, event_payload_free_cb free_cb, event_payload_print_cb print_cb)
{
struct event_payload_item *epi;
epi = xcalloc(1, sizeof *epi);
epi->type = EVENT_PAYLOAD_POINTER;
epi->pointer.ptr = ptr;
epi->pointer.free_cb = free_cb;
epi->pointer.print_cb = print_cb;
event_payload_set_item(ep, name, epi);
}
/* Get a string item. */
const char *
event_payload_get_string(struct event_payload *ep, const char *name)
{
struct event_payload_item *epi;
epi = event_payload_find(ep, name);
if (epi == NULL || epi->type != EVENT_PAYLOAD_STRING)
return (NULL);
return (epi->string);
}
/* Print a payload item. */
static void
event_payload_add_item(struct event_payload_item *epi, struct evbuffer *evb)
{
switch (epi->type) {
case EVENT_PAYLOAD_STRING:
evbuffer_add_printf(evb, "%s", epi->string);
break;
case EVENT_PAYLOAD_TIME:
evbuffer_add_printf(evb, "%lld", (long long)epi->time);
break;
case EVENT_PAYLOAD_INT:
evbuffer_add_printf(evb, "%d", epi->number);
break;
case EVENT_PAYLOAD_UINT:
evbuffer_add_printf(evb, "%u", epi->unsigned_number);
break;
case EVENT_PAYLOAD_CLIENT:
evbuffer_add_printf(evb, "%s", epi->client->name);
break;
case EVENT_PAYLOAD_SESSION:
evbuffer_add_printf(evb, "$%u", epi->session->id);
break;
case EVENT_PAYLOAD_WINDOW:
evbuffer_add_printf(evb, "@%u", epi->window->id);
break;
case EVENT_PAYLOAD_PANE:
evbuffer_add_printf(evb, "%%%u", epi->pane->id);
break;
case EVENT_PAYLOAD_POINTER:
if (epi->pointer.print_cb != NULL)
epi->pointer.print_cb(epi->pointer.ptr, evb);
else
evbuffer_add_printf(evb, "%p", epi->pointer.ptr);
break;
}
}
/* Print a payload item. */
char *
event_payload_item_print(struct event_payload_item *epi)
{
struct evbuffer *evb;
char *value = NULL;
size_t size;
evb = evbuffer_new();
if (evb == NULL)
fatalx("out of memory");
event_payload_add_item(epi, evb);
if ((size = EVBUFFER_LENGTH(evb)) != 0)
value = xmemdup(EVBUFFER_DATA(evb), size);
else
value = xstrdup("");
evbuffer_free(evb);
return (value);
}
/* Print an item value. */
char *
event_payload_print(struct event_payload *ep, const char *name)
{
struct event_payload_item *epi;
epi = event_payload_find(ep, name);
if (epi == NULL)
return (NULL);
return (event_payload_item_print(epi));
}
/* Add payload items as formats. */
void
event_payload_add_formats(struct event_payload *ep, struct format_tree *ft,
const char *prefix)
{
struct event_payload_item *epi;
char *name, *value;
const char *key;
if (prefix == NULL)
prefix = "";
RB_FOREACH(epi, event_payload_tree, &ep->items) {
key = epi->name;
if (*key == '_')
continue;
value = event_payload_item_print(epi);
xasprintf(&name, "%s%s", prefix, key);
format_add(ft, name, "%s", value);
free(name);
free(value);
if (epi->type == EVENT_PAYLOAD_SESSION) {
xasprintf(&name, "%s%s_name", prefix, key);
format_add(ft, name, "%s", epi->session->name);
free(name);
} else if (epi->type == EVENT_PAYLOAD_WINDOW) {
xasprintf(&name, "%s%s_name", prefix, key);
format_add(ft, name, "%s", epi->window->name);
free(name);
}
}
}
/* Get the first payload item. */
struct event_payload_item *
event_payload_first(struct event_payload *ep)
{
return (RB_MIN(event_payload_tree, &ep->items));
}
/* Get the next payload item. */
struct event_payload_item *
event_payload_next(struct event_payload_item *epi)
{
return (RB_NEXT(event_payload_tree, , epi));
}
/* Get a payload item name. */
const char *
event_payload_item_name(struct event_payload_item *epi)
{
return (epi->name);
}
/* Get a payload item type. */
enum event_payload_type
event_payload_item_type(struct event_payload_item *epi)
{
return (epi->type);
}
/* Log a payload. */
void
event_payload_log(struct event_payload *ep, const char *fmt, ...)
{
struct event_payload_item *epi;
struct evbuffer *evb;
va_list ap;
char *prefix;
va_start(ap, fmt);
xvasprintf(&prefix, fmt, ap);
va_end(ap);
evb = evbuffer_new();
if (evb == NULL)
fatalx("out of memory");
if (ep != NULL) {
RB_FOREACH(epi, event_payload_tree, &ep->items) {
if (EVBUFFER_LENGTH(evb) != 0)
evbuffer_add_printf(evb, ", ");
evbuffer_add_printf(evb, "%s=", epi->name);
event_payload_add_item(epi, evb);
}
}
log_debug("%s%.*s", prefix, (int)EVBUFFER_LENGTH(evb),
(char *)EVBUFFER_DATA(evb));
evbuffer_free(evb);
free(prefix);
}
/* Get a time item. */
time_t
event_payload_get_time(struct event_payload *ep, const char *name)
{
struct event_payload_item *epi;
epi = event_payload_find(ep, name);
if (epi == NULL || epi->type != EVENT_PAYLOAD_TIME)
return (0);
return (epi->time);
}
/* Get a number item. */
int
event_payload_get_int(struct event_payload *ep, const char *name, int *value)
{
struct event_payload_item *epi;
epi = event_payload_find(ep, name);
if (epi == NULL || epi->type != EVENT_PAYLOAD_INT)
return (-1);
*value = epi->number;
return (0);
}
/* Get an unsigned number item. */
int
event_payload_get_uint(struct event_payload *ep, const char *name, u_int *value)
{
struct event_payload_item *epi;
epi = event_payload_find(ep, name);
if (epi == NULL || epi->type != EVENT_PAYLOAD_UINT)
return (-1);
*value = epi->unsigned_number;
return (0);
}
/* Get a client item. */
struct client *
event_payload_get_client(struct event_payload *ep, const char *name)
{
struct event_payload_item *epi;
epi = event_payload_find(ep, name);
if (epi == NULL || epi->type != EVENT_PAYLOAD_CLIENT)
return (NULL);
return (epi->client);
}
/* Get a session item. */
struct session *
event_payload_get_session(struct event_payload *ep, const char *name)
{
struct event_payload_item *epi;
epi = event_payload_find(ep, name);
if (epi == NULL || epi->type != EVENT_PAYLOAD_SESSION)
return (NULL);
return (epi->session);
}
/* Get a window item. */
struct window *
event_payload_get_window(struct event_payload *ep, const char *name)
{
struct event_payload_item *epi;
epi = event_payload_find(ep, name);
if (epi == NULL || epi->type != EVENT_PAYLOAD_WINDOW)
return (NULL);
return (epi->window);
}
/* Get a pane item. */
struct window_pane *
event_payload_get_pane(struct event_payload *ep, const char *name)
{
struct event_payload_item *epi;
epi = event_payload_find(ep, name);
if (epi == NULL || epi->type != EVENT_PAYLOAD_PANE)
return (NULL);
return (epi->pane);
}
/* Get a pointer item. */
void *
event_payload_get_pointer(struct event_payload *ep, const char *name)
{
struct event_payload_item *epi;
epi = event_payload_find(ep, name);
if (epi == NULL || epi->type != EVENT_PAYLOAD_POINTER)
return (NULL);
return (epi->pointer.ptr);
}

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