740 Commits

Author SHA1 Message Date
Thomas Adam
1cf7889fd7 Feedback from nicm 2026-03-06 18:59:26 +00:00
Thomas Adam
9c3ec2b8eb Refactor kitty images to use unified image cache API
The original kitty implementation used a passthrough approach where images
were forwarded directly to the outer terminal without being stored in tmux's
image cache.

This refactors kitty images to work like sixel images.
2026-03-05 17:21:40 +00:00
Thomas Adam
aefdb34846 rename: ENABLE_SIXEL to ENABLE_SIXEL_IMAGES 2026-03-05 17:21:18 +00:00
Thomas Adam
5a233e26ec rename ENABLE_KITTY to ENABLE_KITTY_IMAGES 2026-03-05 17:21:18 +00:00
Thomas Adam
1634db8589 configure: deprecate enable-sixel
This moves --enable-sixel to --enable-sixel-images
2026-03-05 17:21:18 +00:00
Thomas Adam
3b7e9fb175 format: add image_support
This adds a new image_support format which returns:

   "sixel" -- if sixel is compiled in
   "kitty" -- if kitty is compiled in
   "kitty,sixel" -- if compiled with both kitty and sixel
2026-03-05 17:21:18 +00:00
Thomas Adam
cf6cbe430c Add support for the kitty graphics protocol
Kitty-capable terminals (kitty, ghostty, and others) can display
inline images via APC escape sequences.

Kitty's image support uses a passthrough model: APC sequences from
programs running inside panes are relayed verbatim to the outer
terminal with cursor positioning adjusted for the pane offset.

The outer terminal handles all image rendering and therefore itself must
be kitty-aware.
2026-03-05 17:21:18 +00:00
Thomas Adam
d9d2b2f1ee Merge branch 'obsd-master' 2026-03-05 12:01:09 +00:00
nicm
1e208abd93 Add pane_pipe_pid with pipe fd and call setpgid to make it easier to kill. 2026-03-05 09:22:08 +00:00
Thomas Adam
d32088b58a Merge branch 'obsd-master' 2026-03-04 12:01:09 +00:00
nicm
6d37cc94a7 Make -c work with new-session -A, from Jody Frankowski in GitHub issue
4906.
2026-03-04 08:16:47 +00:00
nicm
55722a7ed7 Another memory leak, from Huihui Huang. 2026-03-04 08:15:24 +00:00
tb
d2fa20a390 tmux: use VIS_NOSLASH to avoid annoying double escaping on paste
from nicm
2026-03-04 07:19:32 +00:00
tb
bcbad050d4 tmux: tc can be NULL, so check before deref to avoid crashing the server
ok nicm
2026-03-04 07:17:01 +00:00
Thomas Adam
27698c468b Merge branch 'obsd-master' 2026-03-03 16:01:09 +00:00
nicm
49bb43047d Allow copy mode to work for readonly clients, except for copy commands,
from Dane Jensen.
2026-03-03 12:26:14 +00:00
nicm
77ce0a837d Check window is not NULL, from Chema Gonzalez in GitHub issue 4908. 2026-03-03 12:24:18 +00:00
Thomas Adam
bbbfa8f360 Merge branch 'obsd-master' 2026-03-02 12:01:09 +00:00
Nicholas Marriott
d781d2eaa1 Add regression test from Ilya Grigoriev, GitHub issue 4818. 2026-03-02 09:32:45 +00:00
nicm
f33b14bda6 Revert r1.343 for the moment since it breaks behaviour (#() in
status-left) that we need to keep.
2026-03-02 08:48:57 +00:00
nicm
e603549563 Do not leak active/all strings in format_loop_windows; from Huihui Huang
in GitHub issue 4898.
2026-03-02 08:41:43 +00:00
nicm
e80e0c761a Fix incorrect placement of It Xo, from Dane Jensen. 2026-03-02 08:39:44 +00:00
nicm
9e804202b6 Fix memory leak, from Emmanuel Ugwu in GitHub issue 4900. 2026-03-02 08:38:23 +00:00
Thomas Adam
0c678b1e1a mailcap: update entry for Thomas Adam 2026-03-01 17:01:08 +00:00
Thomas Adam
4cb29deb93 Merge branch 'obsd-master' 2026-02-27 12:01:08 +00:00
nicm
5413953d30 Validate -O flags, from Dane Jensen in GitHub issue 4889. 2026-02-27 08:25:12 +00:00
nicm
50e51b4513 Fix memory leak, from Chris Lewis, reported by Huihui Huang. 2026-02-27 08:23:02 +00:00
Thomas Adam
ee0647aba6 Merge branch 'obsd-master' 2026-02-26 16:01:09 +00:00
Nicholas Marriott
35be70f1f0 Add DECRPM test from David Turnbull. 2026-02-26 11:32:15 +00:00
nicm
dbb23d2182 Do not free buffer after adding to paste (since it now owns it). 2026-02-26 11:01:48 +00:00
Nicholas Marriott
478eaea982 Update base64 compat. 2026-02-26 10:55:41 +00:00
Thomas Adam
0800e51d41 Merge branch 'obsd-master' 2026-02-26 10:18:15 +00:00
nicm
5ff385f8a9 Tweak previous to reset cursor again as well. 2026-02-25 07:59:45 +00:00
nicm
5b3c642195 Pass paste buffer through vis(3) when pasting to prevent buffers
containing for example the bracket end sequence causing issues. -S flag
disables. Reported by Mason Davis.
2026-02-25 07:53:41 +00:00
nicm
bd6e201926 Do not expand #() in E: and T:, from Mason Davis. 2026-02-24 18:06:41 +00:00
Nicholas Marriott
08779aa2a3 Fix breakage in fuzzer, from David Korczynski, GitHub issue 4871. 2026-02-24 08:27:59 +00:00
nicm
09bd686327 Add sorting (-O flag) and a custom format (-F) to list-keys, from Dane
Jensen in GitHub issue 4845.
2026-02-24 08:20:52 +00:00
nicm
9316476a73 Add commands to turn exit on scroll on, off, toggle. From xcdnlgd at
hotmail dot com in GitHub issue 4884.
2026-02-24 08:00:43 +00:00
nicm
00946007d8 Respond to DECRQM 2026, from David Turnbull in GitHub issue 4887. 2026-02-24 07:50:27 +00:00
Thomas Adam
bc47bc7c22 Merge branch 'obsd-master' 2026-02-23 12:01:08 +00:00
Thomas Adam
dc693601f4 Merge branch 'obsd-master' 2026-02-23 10:01:08 +00:00
nicm
f1f9c63cdb Free history entries properly, from Huihui Huang in GitHub issue 4870. 2026-02-23 09:12:57 +00:00
nicm
0cc4f0fd76 Use buffer size for b64_pton, from someone in GitHub issue 4882. 2026-02-23 09:08:07 +00:00
nicm
0dc1b5adfb Do not leak list on failure, reported by Huihui Huang. 2026-02-23 08:58:40 +00:00
nicm
a76e6eca6b Another memory leak from Huihui Huang. 2026-02-23 08:54:56 +00:00
nicm
8c7278b53a Memory leaks in cmd_display_menu, from Huihui Huang. 2026-02-23 08:50:00 +00:00
nicm
50a3b4c777 Free format on -a, reported by Huihui Huang. 2026-02-23 08:46:57 +00:00
nicm
3094ca1da5 Fix memory leaks, reported by Huihui Huang in GitHub issue 4872. 2026-02-23 08:45:27 +00:00
nicm
23ad10c849 Set cell to default when off screen to avoid crash when logging it. 2026-02-23 08:29:30 +00:00
Thomas Adam
c9fb989a31 Merge branch 'obsd-master' 2026-02-23 08:11:38 +00:00
Nicholas Marriott
8356578a54 Update tmux bash completion file link in README, from Hyun Seungmin. 2026-02-20 08:43:35 +00:00
nicm
0310404155 Reuse the same extended slot when clearing non-RGB cells as well. From
Michael K Darling in GitHub issue 4865.
2026-02-20 08:41:23 +00:00
nicm
03f8690f9c Pass which clipboard is set through to the terminal, from Axel Lindskog
in GitHub issue 4858.
2026-02-18 09:10:31 +00:00
Thomas Adam
4cc3de4f84 Merge branch 'obsd-master' 2026-02-17 08:51:05 +00:00
nicm
fedd4440f0 Reuse extended entry when clearing RGB cell, to prevent memory growth
when cells are repeatedly cleared (they are only compacted when scrolled
off screen). From Michael K Darling in GitHub issue 4862.
2026-02-17 07:42:58 +00:00
nicm
55cedc24fa Format layout change string once per window in control notify, from
Conor Taylor in GitHub issue 4848.
2026-02-16 08:45:38 +00:00
nicm
c9162837a0 Pull format allocation outside of loop for control subs, from Conor
Taylor in GitHub issue 4848.
2026-02-16 08:42:57 +00:00
nicm
f218463976 grid_peek_cell can return NULL, so check for it. From Conor Taylor in
GitHub issue 4848.
2026-02-16 08:02:04 +00:00
nicm
1f0c54f7ea Initialize bufname, reported by Mark Kelly. 2026-02-15 17:43:26 +00:00
Thomas Adam
d3522c328c Merge branch 'obsd-master' 2026-02-12 11:10:01 +00:00
nicm
7e50eb0e83 Make paste_get_top return a copy of the buffer name which is more
sensible and avoids a double free pointed out by DongHan Kim.
2026-02-11 08:30:37 +00:00
nicm
5b455abecc 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.
2026-02-11 08:23:21 +00:00
nicm
7d41761e84 Fix clients_calculate_size for manual type when window is NULL. From
Elias Oenal in GitHub issue 4849.
2026-02-10 10:02:11 +00:00
nicm
25f6d8b1e9 Implement some obvious missing sort orders, from Dane Jensen in GitHub
issue 4813.
2026-02-10 09:55:53 +00:00
nicm
5a33616e65 Check for no window when updating clients, GitHub issue 4851. 2026-02-10 09:00:30 +00:00
nicm
1cf17b06ae Batch printable output in control mode, from Conor Taylor in GitHub issue 4848. 2026-02-10 08:40:03 +00:00
nicm
aa03706ed0 Remove redundant call to tty_attributes, from Conor Taylor in GitHub
issue 4848.
2026-02-10 08:34:43 +00:00
nicm
d8794e2b30 Check log level before log_debug in tty_draw_line, from Conor Taylor in
GitHub issue 4848.
2026-02-10 08:31:45 +00:00
nicm
b7939eb266 Don't call event_add again if the event is already pending, from Conor
Taylor in GitHub issue 4848.
2026-02-10 08:30:21 +00:00
nicm
19b9a34c48 Only loop over clients if table actually found, from Conor Taylor in
GitHub issue 4848.
2026-02-10 08:28:53 +00:00
nicm
f016e08153 Fix wrong TAILQ member in input_cancel_requests from Conor Taylor in
GitHub issue 4848.
2026-02-10 08:27:17 +00:00
nicm
7b1c503086 Clear search counts when clearing marks in case of repeated search,
reported by Daniel Pereira in GitHub issue 4817.
2026-02-06 10:28:42 +00:00
nicm
5865001e4a Also check PANE_STATUSREADY for pane_dead format to match
pane_dead_status. GitHub issue 4841 from Joshua Pollack.
2026-02-06 10:23:26 +00:00
nicm
62944da74b Make OSC 52 work in popups, from gogongxt at 163 dot com in GitHub issue
4797.
2026-02-03 09:07:44 +00:00
nicm
588013bb44 Refresh copy mode when style changes, from Josh Cooper in GitHub issue 4830. 2026-02-03 08:53:58 +00:00
Thomas Adam
615c27c117 Merge branch 'obsd-master' 2026-02-02 19:06:25 +00:00
nicm
3c3d9ce3a9 Break sorting out into a common file so formats and modes use the same
code. Also add -O for sorting to the list commands. From Dane Jensen in
GitHub issue 4813.
2026-02-02 10:08:30 +00:00
nicm
66011fe48b Do not treat cells as empty unless the background colour stays the same.
Fixes invisible clock in clock mode, reported by tb@.
2026-01-29 09:08:19 +00:00
Thomas Adam
d7f59eccaf Merge branch 'obsd-master' 2026-01-28 17:25:42 +00:00
Thomas Adam
e652213855 Merge branch 'obsd-master' 2026-01-23 10:50:55 +00:00
nicm
f70150a663 Replace overlay_ranges with visible_ranges which can hold more than
three ranges (will be needed for floating panes); move the visible
ranges checks outside of tty_draw_line and rewrite it to fix issues with
partially-obscured wide characters. With Michael Grant.
2026-01-23 10:45:53 +00:00
Nicholas Marriott
fe52f76913 Fiddle with some more warnings. 2026-01-22 15:02:42 +00:00
Nicholas Marriott
0d9c3c895c Stick the pragmas outside the function (make GCC 4 happier) and under __GNUC__. 2026-01-22 14:29:29 +00:00
Nicholas Marriott
6525bb7cef Line length/style nits. 2026-01-22 14:16:39 +00:00
Michael Grant
0790e74f84 Fix strftime warning in gcc. 2026-01-22 14:11:00 +00:00
nicm
195a9cfd88 When history-limit is changed, apply to existing panes, not just new
ones. GitHub issue 4705.
2026-01-22 08:55:01 +00:00
nicm
ecbf8d76d0 Reevaluate menu and popup styles on each draw to allow them to change
when options change, from Josh Cooper in GitHub issues 4828 and 4829.
2026-01-22 07:52:24 +00:00
nicm
818745a605 Set PANE_STYLECHANGED when user options change, from Josh Cooper in
GitHub issue 4825.
2026-01-22 07:42:30 +00:00
nicm
26aacd0e32 Handle theme keys earlier so they are processed even if a popup is open.
From Josh Cooper in GitHub issue 4827.
2026-01-22 07:39:35 +00:00
nicm
8e06739e26 Fix window-size=latest not resizing on switch-client in session groups.
From Ilya Grigoriev in GitHub issue 4818.
2026-01-20 22:50:08 +00:00
nicm
ab1f2ef71c Add a function to convert a screen to a string, from Michael Grant. 2026-01-20 16:32:05 +00:00
Nicholas Marriott
d2e09cb259 Add a --enable-optimizations configure flag. 2026-01-20 15:42:22 +00:00
Thomas Adam
bf8ea85bd7 Merge branch 'obsd-master' 2026-01-19 10:01:08 +00:00
nicm
9c0aeaff40 Now the copy mode indicator can be changed, we need to redraw it when
the cursor is moved. GitHub issue 4774.
2026-01-19 08:20:51 +00:00
Thomas Adam
95b4bb51c6 Merge branch 'obsd-master' 2026-01-14 22:01:08 +00:00
nicm
1e5f93b7b6 Add -e flag to command-prompt to close if empty, from Dane Jensen in
GitHub issue 4812.
2026-01-14 19:43:43 +00:00
Nicholas Marriott
b89d46bb00 Add a regression test from Ilya Grigoriev. 2026-01-13 20:29:21 +00:00
Thomas Adam
97d61131df Merge branch 'obsd-master' 2026-01-12 10:01:08 +00:00
nicm
5721767921 Correctly draw indicators when pane-border-indicators is set to both.
Reported by Ilya Grigoriev in GitHub issue 4780.
2026-01-12 07:50:16 +00:00
nicm
a443531280 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. GitHub issue 4795 from Shaobo Song.
2026-01-12 07:48:36 +00:00
Thomas Adam
ad083677c5 Merge branch 'obsd-master' 2026-01-11 14:01:08 +00:00
nicm
426467856d Add paste to the default pane menu, GitHub issue 4763. 2026-01-11 11:48:46 +00:00
Thomas Adam
844042744e Merge branch 'obsd-master' 2026-01-09 18:01:07 +00:00
nicm
65b3a95be8 Removing padding needs to go through screen_write_collect_trim or there
may end up multiple items covering the same region.
2026-01-09 15:04:00 +00:00
Thomas Adam
ca95950148 Merge branch 'obsd-master' 2026-01-08 14:01:08 +00:00
nicm
6234d79852 Do not set manual size if no window. 2026-01-08 12:05:02 +00:00
Thomas Adam
a5d8b92012 Merge branch 'obsd-master' 2026-01-08 10:01:08 +00:00
nicm
890cfc6dc3 Copy palette responses before processing them since they are not strings. 2026-01-08 07:54:23 +00:00
Thomas Adam
0e6fc2e88b Merge branch 'obsd-master' 2026-01-07 22:01:07 +00:00
nicm
c8ccd420be Reduce request timeout to 500 milliseconds to match the extended
escape-time, and discard palette requests if receiving a reply for a
different index.
2026-01-07 20:16:32 +00:00
nicm
ff4f6b9066 Extend escape timeout if there are active forwarded requests not just
tmux's own requests. GitHub issue 4793.
2026-01-07 20:03:34 +00:00
Thomas Adam
7cffa2de78 Merge branch 'obsd-master' 2026-01-07 20:01:09 +00:00
nicm
e2afaaea75 Correct redrawing of wide characters when overwritten. Reported by Jake
Stewart in GitHub issue 4737.
2026-01-07 18:29:15 +00:00
Thomas Adam
75d3974c12 Merge branch 'obsd-master' 2026-01-07 10:01:08 +00:00
nicm
583f12ea71 Work out the default command from the queue in case it has been set from
the config file, GitHub issue 4791.
2026-01-07 08:16:20 +00:00
Thomas Adam
3de7a8ad8d Merge branch 'obsd-master' 2026-01-06 22:01:07 +00:00
nicm
f6c9052082 If cannot find a terminator for palette responses, treat as a partial
key not complete. GitHub issue 4749.
2026-01-06 20:09:42 +00:00
nicm
f226804149 Do not send theme unless it has changed, and do not send immediately
when updates are enabled. GitHub issue 5787.
2026-01-06 20:05:57 +00:00
Thomas Adam
cf94486e32 Merge branch 'obsd-master' 2026-01-06 16:01:07 +00:00
nicm
035a2f35d4 Clear trimmed lines after moving, from Antony Raj in GitHub issue 4790. 2026-01-06 14:33:05 +00:00
Thomas Adam
9b7732eac4 Merge branch 'obsd-master' 2026-01-06 12:01:07 +00:00
nicm
5f9dac8abc Do not use ;;s in list-keys output as it is confusing and cannot be
parsed on input, from Patrick Motard in GitHub issue 4750.
2026-01-06 10:17:29 +00:00
nicm
ccd4dd7ff2 Do not log theme if pane is NULL. 2026-01-06 09:11:15 +00:00
Thomas Adam
77417b7cbe Merge branch 'obsd-master' 2026-01-05 10:01:08 +00:00
nicm
6a7cd79a63 Redraw pane borders when entering or leaving alternate screen, from Mike
Jonkmans in GitHub issue 4788.
2026-01-05 08:32:19 +00:00
nicm
baa3b51b3e Do not use client if there isn't one, GitHub issue 4789. 2026-01-05 08:30:30 +00:00
Thomas Adam
abeba4910c Merge branch 'obsd-master' 2026-01-04 10:01:08 +00:00
Nicholas Marriott
356d0aedbd Remove an unused declaration (from OpenBSD, same change also sent by Mike
Jonkmans).
2026-01-04 08:41:38 +00:00
nicm
45f23f3a59 Minor tidying of cmd_list_print from Pavel Roskin. 2026-01-04 08:38:26 +00:00
nicm
6ef7375ade Add some missing logging bits for themes. 2026-01-04 08:05:14 +00:00
Thomas Adam
00030aabf7 Merge branch 'obsd-master' 2025-12-25 20:01:08 +00:00
nicm
f72832cc08 Add focus-follows-mouse option, from Barry Wasdell in GitHub issue 4771. 2025-12-25 18:07:00 +00:00
nicm
b5c33ca2b7 Add selection_mode format variable for copy mode, from Mike Jonkmans in
GitHub issue 4773.
2025-12-25 18:05:15 +00:00
Thomas Adam
d607399850 Merge branch 'obsd-master' 2025-12-22 10:01:08 +00:00
nicm
d005803934 Add prompt-command-cursor-style, from Joshua Cooper in GitHub issue 4765. 2025-12-22 08:41:01 +00:00
nicm
a22ec275b4 With status-keys vi, move the cursor left by one when pressing Escape to
enter command mode, like vi. GitHub issue 4767 from Joshua Cooper.
2025-12-22 08:39:35 +00:00
nicm
188f963fe0 Add {current}/{active} for -t for current window or active pane, from
Manuel Einfalt in GitHub issue 4766.
2025-12-22 08:35:04 +00:00
Thomas Adam
9c5df33325 Merge branch 'obsd-master' 2025-12-19 10:01:07 +00:00
nicm
74f60951cc Do not leak items if not using them because of synchronized update. 2025-12-19 08:46:25 +00:00
Nicholas Marriott
54202fcaad Check image size against available screen size properly, GitHub issue 4739. 2025-12-18 13:44:17 +00:00
Nicholas Marriott
fe645c5bcc Fix image scrolling at exact screen height, and add more logging for images. 2025-12-18 09:05:37 +00:00
Thomas Adam
86c15d7ae9 Merge branch 'obsd-master' 2025-12-17 14:01:08 +00:00
Thomas Adam
3c36d5e433 Merge branch 'obsd-master' 2025-12-17 12:01:07 +00:00
nicm
1c7e164c22 Add support for applications to use synchronized output mode (DECSET
2026) to prevent screen tearing during rapid updates. When an
application sends SM ?2026, tmux buffers output until RM ?2026 is received
or a 1-second timeout expires.

From Chris Lloyd with the assistance of Claude Code, GitHub issue 4744.
2025-12-17 11:49:29 +00:00
nicm
ce7eb22e3a Do not have a default prompt cursor colour because some terminals
(urxvt, st) do not support the reset sequence. GitHub issue 4759.
2025-12-17 10:20:21 +00:00
Thomas Adam
739a97140f Merge branch 'obsd-master' 2025-12-17 10:01:07 +00:00
nicm
99ed397e9c Make clock mode seconds synchronized to the second, GitHub issue 4760
from Joao Pedro.
2025-12-17 08:38:12 +00:00
Thomas Adam
d2c7668bc2 Merge branch 'obsd-master' 2025-12-16 00:01:08 +00:00
nicm
7abf3e8bdf Note that switch-client only changes the key table for one key, pointed
out by Jeenu Viswambharan.
2025-12-15 21:21:25 +00:00
Thomas Adam
506b4db762 Merge branch 'obsd-master' 2025-12-15 15:19:29 +00:00
bket
21c27fdcae Simplify argument move using TAILQ_CONCAT()
Replace the manual loop moving each argument from cmd->arguments to
last->arguments with a single TAILQ_CONCAT() call. This makes the code
clearer and more efficient, while preserving identical behavior.

OK nicm@
2025-12-11 04:17:17 +00:00
nicm
672e89a640 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, GitHub issue 4731.
2025-12-10 21:24:43 +00:00
Nicholas Marriott
4b810ae493 Add a define for max images and bump to 20 for the moment. 2025-12-09 08:46:00 +00:00
nicm
52e2a7d990 Fix key code for M-BSpace, GitHub issue 4717. 2025-12-09 08:13:59 +00:00
Nicholas Marriott
2e5e9c0298 Fix calculation of scaled SIXEL size, from nincsnevem662 at gmail dot com in
GitHub issue 4739.
2025-12-08 21:41:30 +00:00
nicm
afa05ae15e Use correct style for bottom line when pane status line is on, GitHub
issue 4732.
2025-12-08 21:32:50 +00:00
nicm
f58b8d0d6a Setting working directory after fork means there is a race with
pane_current_path (especially on platforms with systemd which have to
take time to do some additional faffing around). To avoid this, change
it before fork and back in the parent afterwards. GitHub issue 4719.
2025-12-08 08:04:35 +00:00
Nicholas Marriott
5c73d3878d FIx combine test again... 2025-12-07 22:11:45 +00:00
Nicholas Marriott
ef0a7e328c Merge branch 'tmux-3.6a' 2025-12-05 06:20:32 +00:00
Nicholas Marriott
cc117b5048 Update CHANGES. 2025-12-05 05:39:46 +00:00
nicm
faebe7a70a Do not read over buffer if format is a single #, and do not loop forever
if UTF-8 is unfinished in a format. Reported by Giorgi Kobakhia im
GitHub issue 4735.
2025-12-05 05:38:05 +00:00
nicm
01962e25dc Allow drag in alternate screen again, GitHub issue 4743 reported by Brad
King.
2025-12-05 05:37:57 +00:00
nicm
ff207eb583 Fix y offset of mouse if status at top. GitHub issue 4738 from Michael
Grant.
2025-12-05 05:37:48 +00:00
Thomas Adam
8e922ab690 Merge branch 'obsd-master' 2025-12-05 02:01:07 +00:00
Thomas Adam
1a3db6b65e Merge branch 'obsd-master' 2025-12-05 00:01:08 +00:00
nicm
987e05ff31 Allow drag in alternate screen again, GitHub issue 4743 reported by Brad
King.
2025-12-04 22:50:34 +00:00
Nicholas Marriott
2fc123cf4a Update CHANGES. 2025-12-04 20:54:44 +00:00
nicm
3b57077d01 Add a missing skin tone, from Jake Stewart in GitHub issue 4736. 2025-12-04 20:53:01 +00:00
nicm
33c1ba1549 Allow characters to be combined in either order, reported by Jake
Stewart in GitHub issue 4726.
2025-12-04 20:52:55 +00:00
nicm
bd16b22dac Do not read over buffer if format is a single #, and do not loop forever
if UTF-8 is unfinished in a format. Reported by Giorgi Kobakhia im
GitHub issue 4735.
2025-12-04 20:49:57 +00:00
Thomas Adam
0929d8ddfa Merge branch 'obsd-master' 2025-12-04 16:01:08 +00:00
nicm
9d6c69ebde Fix y offset of mouse if status at top. GitHub issue 4738 from Michael
Grant.
2025-12-04 14:45:32 +00:00
Thomas Adam
1bcd360dfe Merge branch 'obsd-master' 2025-12-04 08:01:07 +00:00
nicm
1f2210a3ce Add a missing skin tone, from Jake Stewart in GitHub issue 4736. 2025-12-04 06:04:21 +00:00
nicm
2fe1378d3a Allow characters to be combined in either order, reported by Jake
Stewart in GitHub issue 4726.
2025-12-04 06:02:27 +00:00
Thomas Adam
796539c60b Merge branch 'obsd-master' 2025-12-03 10:01:09 +00:00
Thomas Adam
700936b2ad Merge branch 'obsd-master' 2025-12-03 08:01:07 +00:00
nicm
a28dbe3a59 Improve code readability in colour_palette_get and colour_palette_set.
GitHub issue 4730 from Pavel Roskin.
2025-12-03 07:41:38 +00:00
nicm
820df0f551 Add check that the pane is not in alternate screen mode when in
copy-mode. From Michael Grant in GitHub issue 4728.
2025-12-03 07:38:46 +00:00
nicm
ee9cf1bbaa Fix the size caluation for left-right windows used to spread out cells
horizontally evenly. From Michael Grant in GitHub issue 4724.
2025-12-03 07:35:32 +00:00
Nicholas Marriott
52917abe21 Update CHANGES. 2025-12-02 14:26:30 +00:00
nicm
a40f98df0a Change noattr to be an explicit attribute in the style so that it works
correctly and does not delete attributes set in the style itself, GitHub
issue 4713.
2025-12-02 14:25:05 +00:00
nicm
2c78a5aceb Add horizontal border case to server_client_check_mouse_in_pane to fix
mouse resizing. GitHub issue 4720 from Michael Grant, reported by
someone in GitHub issue 4715.
2025-12-02 14:24:57 +00:00
Thomas Adam
02a8e3fd34 Merge branch 'obsd-master' 2025-12-02 10:01:08 +00:00
nicm
322adfbdde Add a get-clipboard option which when enabled (the default is off) uses
the same mechanism as palette requests to request clipboard from the
terminal and forward to the requesting pane. Remove the now-redundant
forward-to-pane ability from "refresh-client -l". GitHub issue 4275.
2025-12-02 08:20:32 +00:00
Thomas Adam
194d0a0e25 Merge branch 'obsd-master' 2025-12-01 10:01:08 +00:00
Nicholas Marriott
b2d6ebaa10 Fix combine-test.result, GitHub issue 4717. 2025-12-01 08:22:12 +00:00
Nicholas Marriott
bfecbb0685 Fix combine-test.result, GitHub issue 4717. 2025-12-01 08:21:04 +00:00
nicm
e4c552f5a5 Change noattr to be an explicit attribute in the style so that it works
correctly and does not delete attributes set in the style itself, GitHub
issue 4713.
2025-12-01 08:14:29 +00:00
nicm
04f32073c0 Add horizontal border case to server_client_check_mouse_in_pane to fix
mouse resizing. GitHub issue 4720 from Michael Grant, reported by
someone in GitHub issue 4715.
2025-12-01 08:04:26 +00:00
Thomas Adam
e9afd2bb5e Merge branch 'obsd-master' 2025-11-28 12:01:09 +00:00
nicm
55d660a548 Do not remove TERM etc for commands run from config file, reported by
Dennis Eriksen.
2025-11-28 09:42:48 +00:00
nicm
dfaf47d97c session_index was never actually implemented, remove from man page. 2025-11-28 09:14:17 +00:00
Nicholas Marriott
640e1a7643 Update CHANGES. 2025-11-27 07:26:09 +00:00
nicm
2a0b078e15 Place cursor on correct line if message-line is not 0, reported by
Alexis Hildebrandt.
2025-11-27 07:24:50 +00:00
nicm
0af04295f3 Newer libevents do not allow event_del on a zero'd event. 2025-11-27 07:24:47 +00:00
Thomas Adam
3542bfa5b8 Merge branch 'obsd-master' 2025-11-26 22:01:08 +00:00
nicm
e3a54ed0f4 Newer libevents do not allow event_del on a zero'd event. 2025-11-26 19:02:03 +00:00
Nicholas Marriott
ec4b5b52af Version and CHANGES. 2025-11-26 19:00:17 +00:00
nicm
a0dfef3b04 Place cursor on correct line if message-line is not 0, reported by
Alexis Hildebrandt.
2025-11-26 18:57:18 +00:00
Nicholas Marriott
6db6a30ab5 Need signal.h for utempter, from Yasuhiro Kimura. 2025-11-26 18:43:11 +00:00
Nicholas Marriott
bfa2f73335 Need signal.h for utempter, from Yasuhiro Kimura. 2025-11-26 10:41:17 +00:00
Nicholas Marriott
1decccace7 Update version. 2025-11-26 08:44:41 +00:00
Nicholas Marriott
ee53d49059 Merge branch 'tmux-3.6' 2025-11-26 08:44:18 +00:00
Nicholas Marriott
0dac7fe434 Add to CHANGES. 2025-11-26 08:34:33 +00:00
nicm
a6690032ad Add seconds options for clock mode, from augustus7613 dot mail at pm dot
me in GitHub issue 4697.
2025-11-26 08:33:32 +00:00
Thomas Adam
f949f9a23a Merge branch 'obsd-master' 2025-11-26 00:01:08 +00:00
Nicholas Marriott
00cef3e6be Add to CHANGES. 2025-11-25 22:07:09 +00:00
Nicholas Marriott
3f4a8713c9 Merge branch 'master' into tmux-3.6 2025-11-25 22:06:41 +00:00
Thomas Adam
7c188aec3c Merge branch 'obsd-master' 2025-11-25 22:01:08 +00:00
nicm
f0dec832b8 Add seconds options for clock mode, from augustus7613 dot mail at pm dot
me in GitHub issue 4697.
2025-11-25 21:24:27 +00:00
Nicholas Marriott
ca74fa1db0 Update version and CHANGES. 2025-11-25 21:21:59 +00:00
Nicholas Marriott
987986631d Merge tag '3.5a'
tmux 3.5a.
2025-11-25 20:42:10 +00:00
nicm
1d89233047 Add a resize callback for menus so that they are correctly moved on
resize. From m-einfalt at gmx dot de in GitHub issue 4696.
2025-11-25 20:27:23 +00:00
Thomas Adam
348f16093c Merge branch 'obsd-master' 2025-11-18 10:01:07 +00:00
nicm
2a3ec87887 Make -v to source-file pass through to subsequent source-file commands,
GitHub issue 4216.
2025-11-18 08:42:09 +00:00
nicm
b52dcff745 Allow show-messages to work without a client. 2025-11-18 08:37:54 +00:00
Thomas Adam
f372112a8d Merge branch 'obsd-master' 2025-11-14 10:01:08 +00:00
nicm
72117debf7 Send matching terminator on queued requests, reported by Henry Qin. 2025-11-14 07:55:23 +00:00
Thomas Adam
2f3c71595b Merge branch 'obsd-master' 2025-11-13 16:01:07 +00:00
nicm
113aaf37fc When copying a line from one screen to another, stop only when both
source and target are beyond the allocated line length. Fixes problems
with trailing spaces on pane-border-format. GitHub issue 4688 from
Dmitry Ryabkov.
2025-11-13 13:08:44 +00:00
Nicholas Marriott
768042d29d Fix build afte merge, from Vladimir Lomov. 2025-11-13 07:18:53 +00:00
Thomas Adam
db274f9c39 Merge branch 'obsd-master' 2025-11-12 21:26:29 +00:00
nicm
66279c124e Make requests to the external terminal one queue instead of one by type,
and include any other requests as well so that ordering is maintained.
2025-11-12 20:41:06 +00:00
Thomas Adam
b335419505 Merge branch 'obsd-master' 2025-11-12 16:01:09 +00:00
nicm
3c9e10139f If display-popup is used inside a popup, modify that popup. From
m-einfalt at gmx dot de in GitHub issue 4678.
2025-11-12 13:47:51 +00:00
Thomas Adam
34898c89cd Merge branch 'obsd-master' 2025-11-12 10:01:08 +00:00
nicm
40600eebfa Do not send theme updates to panes that have exited, GitHub issue 4671. 2025-11-12 08:06:33 +00:00
nicm
12497ecade Convert all keys for backspace, not just A-Z. 2025-11-12 07:54:52 +00:00
nicm
9e21f426c0 Add a command to explcitly set the selection mode in copy mode, GitHub
issue 3842.
2025-11-12 07:53:22 +00:00
Nicholas Marriott
c77d49f67e Save and restore images in alternate screen, GitHub issue 3732. 2025-11-12 07:49:17 +00:00
Thomas Adam
33cfe8b809 Merge branch 'obsd-master' 2025-11-07 14:01:08 +00:00
jsg
0d0ef5d0d2 correct test for COLORTERM containing 256
found with smatch, ok nicm@
2025-11-07 12:43:47 +00:00
Nicholas Marriott
d65bac979d Do not try to allocate zero size colours, GitHub issue 4674. 2025-11-07 12:36:07 +00:00
jsg
c1667bd85e consistently use tabs for indentation
found with smatch, ok nicm@
2025-11-07 12:30:36 +00:00
Thomas Adam
815f7ecffb Merge branch 'obsd-master' 2025-11-03 12:01:06 +00:00
Thomas Adam
44c0443983 Merge branch 'obsd-master' 2025-11-03 09:57:35 +00:00
nicm
3051076dd1 Ignore Hangul filler character. There doesn't seem to be much agreement
on what to do with this but ignoring it seems rightand does improve
things. GitHub issue 3998.
2025-11-03 09:27:06 +00:00
nicm
d90b414223 Handle regional indicators and emoji modifiers in a better way, GitHub
issue 3998.
2025-11-01 16:44:24 +00:00
nicm
8cb2805eb7 Handle ~ correctly when loading a file, GitHub issue 3518. 2025-11-01 16:42:59 +00:00
nicm
e5ab5995db Fix now-incorrect text about {}s. 2025-10-31 09:00:26 +00:00
nicm
e774b89062 Preserve marked pane with swap-window and move-window, GitHub issue 3443. 2025-10-30 13:52:08 +00:00
nicm
29db8ac36e Set and check COLORTERM as a hint for RGB colour. 2025-10-30 11:52:25 +00:00
Thomas Adam
be2d4aa434 Merge branch 'obsd-master' 2025-10-30 10:01:07 +00:00
nicm
1e61e52400 If tmux receives a palette request (OSC 4) in a pane and the palette
entry has not been set, send a request to the most recently used client
and forward any response instead. Based on change from Tim Culverhouse
in GitHub issue 4665.
2025-10-30 07:41:19 +00:00
Thomas Adam
4bdfbfa854 Merge branch 'obsd-master' 2025-10-29 12:01:07 +00:00
nicm
2c08960f4e Add -l flag to command-prompt to disable splitting into multiple prompts,
GitHub issue 4483.
2025-10-29 09:37:36 +00:00
nicm
1a419609e3 Don't enter copy mode on mouse wheel in alternate screen, GitHub issue 3705. 2025-10-29 09:33:20 +00:00
Nicholas Marriott
f812b8d304 Cast to avoid warnings on 32-bit architectures. GitHub issue 4597. 2025-10-29 08:49:16 +00:00
Thomas Adam
ff6eefd7c7 Merge branch 'obsd-master' 2025-10-28 18:01:08 +00:00
nicm
0c5abfefd3 Add commands to centre the cursor in copy mode, from m-einfalt at gmx
dot de in GitHub issue 4662.
2025-10-28 16:36:52 +00:00
Thomas Adam
e560a09db0 Merge branch 'obsd-master' 2025-10-28 15:16:58 +00:00
nicm
9bf8ca5856 Support case insensitive search in modes in the same way as copy mode
(like emacs, so all-lowercase means case insensitive). GitHub issue
4396.
2025-10-28 14:21:06 +00:00
Thomas Adam
410abb5afb Merge branch 'obsd-master' 2025-10-28 14:01:08 +00:00
Thomas Adam
482aa937b1 Merge branch 'obsd-master' 2025-10-28 12:01:07 +00:00
nicm
b4ba6e49af Do not realpath() everything since it is pointless and breaks symlinks.
GitHub issue 4427.
2025-10-28 10:51:30 +00:00
Thomas Adam
010394ab63 Merge branch 'obsd-master' 2025-10-28 10:01:07 +00:00
nicm
7cbb96528c Do not try to reflow if line is NULL. 2025-10-28 09:01:12 +00:00
nicm
aa420cd54a Tweak error messages so that file name isn't modified when we capitalize
the first letter, from Pavel Roskin in GitHub issue 4666.
2025-10-28 07:32:26 +00:00
Thomas Adam
d5302ec327 Merge branch 'obsd-master' 2025-10-27 22:01:08 +00:00
nicm
f31a2d229c Do not play games with lines for view mode output since it stops them
wrapping, GitHub issue 4462.
2025-10-27 20:31:40 +00:00
Thomas Adam
35ad72e56f Merge branch 'obsd-master' 2025-10-20 10:01:07 +01:00
nicm
f501817154 Fix the logic of the no-detached case for detach-on-destroy option - a
previous change made it so that even in the no-detached case, tmux would
always re-attach to a session, even if there weren't any detached ones.
From Martin Louazel in GitHub issue 4649.
2025-10-20 07:28:38 +00:00
Thomas Adam
449f255f3e Merge branch 'obsd-master' 2025-10-13 10:01:08 +01:00
nicm
21d890e6df Add buffer_full format variable, from Mohammad AlSaleh in GitHub issue
4630.
2025-10-13 07:29:53 +00:00
nicm
feb3b90596 Check allow-set-title for APC as well. 2025-10-13 07:19:08 +00:00
Nicholas Marriott
0ff2676a25 Implement getdtablecount(3) for Darwin, from Chip Davis in GitHub issue 4636. 2025-10-06 11:13:53 +01:00
Thomas Adam
3e28777ecb Merge branch 'obsd-master' 2025-09-12 12:01:09 +01:00
nicm
7516f8e94f Check the right flags member, reported by someone in GitHub issue 4634. 2025-09-12 08:46:29 +00:00
Thomas Adam
ac574b6782 Merge branch 'obsd-master' 2025-09-09 12:01:09 +01:00
nicm
50dafd6187 Remove some unnecessary assignments. 2025-09-09 08:49:22 +00:00
Thomas Adam
d4f129b513 Merge branch 'obsd-master' 2025-09-08 14:01:08 +01:00
Nicholas Marriott
9a72317c4f Check for vlock earlier to neaten output. 2025-09-08 12:27:26 +01:00
nicm
b21b1ee9af Don't try to change directory if it is NULL. 2025-09-08 11:21:56 +00:00
nicm
c62fe6dac8 The client can be NULL, fixes -v in config file. 2025-09-08 11:21:29 +00:00
Thomas Adam
9dc136f445 Merge branch 'obsd-master' 2025-09-08 12:01:10 +01:00
Thomas Adam
418bf148e0 Merge branch 'obsd-master' 2025-09-08 10:01:08 +01:00
jsg
408721ae37 correct indentation, no functional change
found with smatch, ok nicm@
2025-09-08 08:23:19 +00:00
Nicholas Marriott
b1b6850fa9 Add test for control mode, from Sergei Nizovtsev. 2025-09-08 08:31:23 +01:00
nicm
d73196750f Change so that source-file errors are reported correctly in control
mode, from Sergei Nizovtsev in GitHub issue 4626.
2025-09-08 07:30:15 +00:00
Thomas Adam
093754db3f Merge branch 'obsd-master' 2025-09-07 18:01:08 +01:00
nicm
7cc15969ff Quote cwd for glob in a way that actually works, from Pavel Roskin in
GitHub issue 4623.
2025-09-07 14:17:25 +00:00
nicm
b53cf70262 Introduce a new window option: tiled-layout-max-columns. It configures
the maximum number of columns in the tiled layout. The default value is
0 which means no limit and makes the change backward-compatible. From
vkadlcik at redhat dot com.
2025-09-07 14:03:18 +00:00
Nicholas Marriott
1dadd6c9ae Do not force SIXEL images to a multiple of 6 lines. From nincsnevem662 at gmail
dot com in GitHub issue 4622.
2025-09-07 14:55:18 +01:00
Nicholas Marriott
a7a94cb86f Fix OOB access in sixel_print by ensuring that the number of chunks actually
matches the highest color used on the image, even if not defined as a color
register. From nincsnevem662 at gmail dot com in GitHub issue 4621.
2025-09-07 14:53:08 +01:00
Thomas Adam
3d6954f0f0 Merge branch 'obsd-master' 2025-09-05 10:01:08 +01:00
nicm
267efefdfa Restore default blinking state when restoring default cursor style, from
Andrea Alberti.
2025-09-05 06:15:48 +00:00
Thomas Adam
6a3170386e Merge branch 'obsd-master' 2025-09-02 18:36:44 +01:00
nicm
4d83eef756 Add support for DECRQSS SP q (report cursor style), DECRQM ?12 (report
cursor blink state) and DECRQM ?2004, ?1004, ?1006 (report mouse state).

From Andrea Alberti in GitHub issue 4618.
2025-09-02 15:15:12 +00:00
Thomas Adam
44b7a2d1f9 Merge branch 'obsd-master' 2025-09-02 16:01:08 +01:00
nicm
c32570c602 Set PWD for jobs as well as panes. 2025-09-02 13:39:14 +00:00
Thomas Adam
9702ef3f91 Merge branch 'obsd-master' 2025-09-02 02:01:07 +01:00
Thomas Adam
b2194ac549 Merge branch 'obsd-master' 2025-09-02 00:01:08 +01:00
nicm
b5413dacc7 Delete timer before adding it again. 2025-09-01 22:11:23 +00:00
nicm
aa33757e45 Fix missing argument from OSC 4 reply, from someone in GitHub issue 4596. 2025-09-01 21:44:37 +00:00
Thomas Adam
a0065bea04 Merge branch 'obsd-master' 2025-09-01 12:01:08 +01:00
nicm
5c89d835a6 Add -k flag to display-popup which allows any key to dismiss the popup
once the command has exited. From Meriel Luna Mittelbach in GitHub issue
4612.
2025-09-01 08:03:07 +00:00
nicm
cfb906a0ce Increase the escape delay when waiting for an RGB response no matter
when it is triggered (they can also be sent on resize). GitHub issue
4569.
2025-09-01 07:58:09 +00:00
nicm
af36d7c430 Ensure break-pane -P prints when only one pane is left. From Chaoyi Yin
in GitHub issue 4615.
2025-09-01 07:53:49 +00:00
Nicholas Marriott
0c9165fc71 Give another bit to the first component of SIXEL colours to fix the HLS color
space which uses 360 degrees in its first component. From nincsnevem662 at
gmail dot com in GitHub issue 4616.
2025-09-01 08:31:57 +01:00
Thomas Adam
fa63088dce Merge branch 'obsd-master' 2025-08-26 10:01:07 +01:00
nicm
0b7235c5f0 Correctly calculate lines to clear for deletions, from Pavel Roskin. 2025-08-26 07:17:51 +00:00
nicm
ca0f0419e6 input_stop_utf8 can move the cursor, so it is now wrong to store the
cursor position before calling it. Problem found by Jayakrishna
Vadayath.
2025-08-26 07:15:00 +00:00
nicm
3520e833a4 Be more robust against misbehaving clients, prompted by deraadt based on
a report from sai02 at student dot ubc dot ca.
2025-08-26 07:00:22 +00:00
Nicholas Marriott
bb4866047a Fix build. 2025-08-22 12:58:19 +01:00
Thomas Adam
2070731af1 Merge branch 'obsd-master' 2025-08-22 12:01:08 +01:00
Thomas Adam
a5909b450c Merge branch 'obsd-master' 2025-08-22 10:01:08 +01:00
nicm
94bafa5aaf A couple of memory leaks from someone in GitHub issue 4590. 2025-08-22 07:48:23 +00:00
nicm
7325da3052 Extend pane lookup special case for switch-client to mouse target ("=")
so that it works for panes on status line.
2025-08-22 07:42:51 +00:00
nicm
e6d275b371 Do not leak label if it is too long, GitHub issue 4591. 2025-08-22 07:39:40 +00:00
Thomas Adam
f4c0fe2ede Merge branch 'obsd-master' 2025-08-22 08:37:26 +01:00
nicm
12452f4427 Do not double free argv from MSG_COMMAND if it is too long, reported by
sai02 at student dot ubc dot ca via deraadt. ok deraadt
2025-08-22 07:26:25 +00:00
nicm
1370791734 Add a nicer default second and third status line, from Michael Grant in
GitHub 4490.
2025-08-14 07:15:40 +00:00
nicm
0646814235 Add some NULL checks, GitHub issue 4586. 2025-08-14 06:49:53 +00:00
nicm
84775130a0 Add pane-border-lines value to use spaces for pane borders, from lukas
dot 23022005 at gmail dot com in GitHub issue 4587.
2025-08-14 06:44:50 +00:00
nicm
4ce893ebc0 Do not leak opened file, from lg65438805 at gmail dot com in GitHub
issue 4577.
2025-08-14 06:37:29 +00:00
nicm
3ae0d560be Replace invalid UTF-8 with the placeholder instead of ignoring them
(this is what most terminals do), GitHub issue 4514.
2025-08-12 12:33:05 +00:00
nicm
06c84dba59 Fix PageDown in menus, from Pavel Roskin in GitHub issue 4581. 2025-08-12 12:29:58 +00:00
nicm
d90e7623c6 Free command on failure, GitHub issue 4578. 2025-08-12 12:26:09 +00:00
Thomas Adam
b13005e802 Merge branch 'obsd-master' 2025-08-04 16:01:07 +01:00
nicm
05b2893b9f Do not leak buffer if not used, reported by someone in GitHub issue
4575.
2025-08-04 13:22:10 +00:00
nicm
1bf2023ed4 Flush scrolling when wrapping so UTF-8 in last position draws correctly,
GitHub issue 4518.
2025-08-04 13:16:13 +00:00
Thomas Adam
6f9bcb7fee Merge branch 'obsd-master' 2025-08-01 12:01:08 +01:00
nicm
6d94909c08 Do not leak on failure, GitHub 4565. 2025-08-01 09:05:51 +00:00
Nicholas Marriott
86153fbba9 Add UTF-8 regress test. 2025-08-01 09:45:18 +01:00
nicm
4f38af01e0 Fix reverse documentation of display-popup -s and -S, from Ricardo Signes. 2025-08-01 08:17:27 +00:00
Thomas Adam
a6588d6918 Merge branch 'obsd-master' 2025-07-22 10:01:08 +01:00
Nicholas Marriott
720d1e831d The AIX functions hang on 7300-01-01-2246 according to GitHub issue 4400.
Remove them.
2025-07-22 09:09:56 +01:00
nicm
206f492401 Fix incorrect handling of Korean Hangul Jamo characters. GitHub issue
4546 from Roy Jung.
2025-07-22 07:42:52 +00:00
Nicholas Marriott
fa5f3cef3d Send SIGCHLD after utempter_remove_record as well. GitHub issue 4559. 2025-07-20 19:59:11 +01:00
Thomas Adam
c70d0a6a69 Merge branch 'obsd-master' 2025-07-19 22:01:08 +01:00
nicm
6549aaf393 yyerror should be void. 2025-07-19 19:30:37 +00:00
nicm
6378c87d15 Only accept DA and DA2 when they end in 'c', stops them being confused
with theme responses.. GitHub issue 4561.
2025-07-19 19:20:10 +00:00
Thomas Adam
4a0eff1296 Merge branch 'obsd-master' 2025-07-19 00:01:08 +01:00
nicm
9c07903ae9 Correctly remove overwritten wide characters at column 1. 2025-07-18 20:44:13 +00:00
Thomas Adam
32b7b3a6f7 Merge branch 'obsd-master' 2025-07-17 14:01:08 +01:00
nicm
041bbf8bdf Allow uppercase letters in gray/grey color names, GitHub issue 4560 from
Pavel Roskin.
2025-07-17 11:39:23 +00:00
Nicholas Marriott
701f6b4b2f Update use-this-issue-template.md 2025-07-17 10:59:53 +01:00
Nicholas Marriott
bddd019a04 Update use-this-issue-template.md 2025-07-17 10:58:28 +01:00
Thomas Adam
865117a05f Merge branch 'obsd-master' 2025-07-13 10:01:08 +01:00
nicm
e6bac234db Increase delay for foreground and background queries as well as device
attributes, GitHub issue 4541.
2025-07-13 06:32:34 +00:00
Nicholas Marriott
9f1fac9142 Use MAC_OS_X_VERSION_MIN_REQUIRED. From Sevan Janiyan, GitHub issue 4550. 2025-07-13 07:26:35 +01:00
nicm
9831175a2d Use window index not ID for sorting, reported by naru at naruaway dot
com in GitHub issue 4551.
2025-07-13 06:16:28 +00:00
Thomas Adam
7e43953937 Merge branch 'obsd-master' 2025-07-02 12:01:08 +01:00
nicm
28481e984b Add sorting to W, P, L operators as well, and add some new session
format variables. From Michael Grant in GitHub issue 4516.
2025-07-02 08:13:09 +00:00
Thomas Adam
30e4f119d3 Merge branch 'obsd-master' 2025-07-01 12:01:08 +01:00
jsg
01da87c112 new sentence, new line 2025-07-01 08:54:50 +00:00
Nicholas Marriott
e7f15d09be Missed a header. 2025-06-24 15:04:24 +01:00
Nicholas Marriott
cbb3c4bfed Update imsg from OpenBSD. 2025-06-24 15:02:16 +01:00
Thomas Adam
d24993aeab Merge branch 'obsd-master' 2025-06-24 14:01:08 +01:00
nicm
d858ad1179 Detect support for OSC 52 using the device attributes report. Some
terminals are using this to indicate that they support copying to the
clipboard with XTerm's OSC 52 sequence. From James Holderness in GitHub
issue 4539.
2025-06-24 10:57:35 +00:00
Thomas Adam
96244dd727 Merge branch 'obsd-master' 2025-06-20 18:01:08 +01:00
Thomas Adam
b38b5d3df2 Merge branch 'obsd-master' 2025-06-20 16:01:08 +01:00
nicm
ad3e6ff054 Add noattr and use in mode-style to allow whether attributes are ignored
or used to be configured. GitHub issue 4498.
2025-06-20 14:54:33 +00:00
nicm
522652913f Add S: to list sessions with modifiers for sorting, from Michael Grant. 2025-06-20 13:31:59 +00:00
Thomas Adam
43e88c892d Merge branch 'obsd-master' 2025-06-16 02:01:07 +01:00
nicm
d82d9468b4 Expand mode-style with E: so # is correctly processed, GitHub issue
4533.
2025-06-15 22:24:51 +00:00
nicm
d7f75ac985 Do not replace SHELL when using /bin/sh. From someone in GitHub issue 4528. 2025-06-15 21:57:58 +00:00
Thomas Adam
1dbceaa790 Merge branch 'obsd-master' 2025-05-28 14:01:08 +01:00
nicm
77ca59acac Add missing theme hooks, from Eric NICOLAS in GitHub issue 4519. 2025-05-28 09:53:57 +00:00
Thomas Adam
7be0613657 Merge branch 'obsd-master' 2025-05-22 12:01:07 +01:00
Thomas Adam
b9ad9186fe Merge branch 'obsd-master' 2025-05-22 10:01:08 +01:00
nicm
3f4b154b70 Bump UTF8_SIZE to the maximum 32 because there are some crazy long UTF-8
sequences out there (GitHub issue 4506). This should not significantly
increase typical memory consumption because we only store each Unicode
character once in the shared cache.
2025-05-22 08:01:29 +00:00
Nicholas Marriott
ff73c95547 New bash completion URL, from David Mandelberg. 2025-05-22 08:52:10 +01:00
Nicholas Marriott
aa0d05f676 Typos, from someone in GitHub issue 4511. 2025-05-22 08:51:14 +01:00
nicm
545832e9fa Typo, from someone in GitHub issue 4511. 2025-05-22 07:49:24 +00:00
nicm
a0ac2a5d63 When there are more than two horizontal windows and the active window is
in not on an edge, correctly highlight both its left and right borders.
GitHub issue 4513 from Michael Grant.
2025-05-22 07:46:38 +00:00
nicm
833c7fbf6d Add a set-default style attribute which replaces the current default
colours and attributes completely, useful at the start of compound
format strings (like status-format) to set the default colours for all
the following options.
2025-05-22 07:43:38 +00:00
Thomas Adam
faf2a44890 Merge branch 'obsd-master' 2025-05-12 14:01:10 +01:00
Thomas Adam
d194ceb604 Merge branch 'obsd-master' 2025-05-12 12:01:10 +01:00
nicm
37a2c98d3d Expand prompts when they are used rather than ahead of time, so the
input can be used as part of the format.
2025-05-12 10:34:13 +00:00
nicm
79b02998a6 Add R format modifier to repeat an argument. 2025-05-12 10:26:19 +00:00
nicm
1ce1e7ef27 Add -E to run-shell to forward stderr as well as stdout, from github at
jyn dot dev in GitHub issue 4246.
2025-05-12 10:16:42 +00:00
nicm
7499d925da Do not downgrade styled underscores to standard underscore if the
terminal does not support them, this matches what would happen if the
application tried to use them on a terminal without support.
2025-05-12 09:50:00 +00:00
nicm
367f17a4ff Preserve colours in selection if the option style is default, GitHub
issue 4498.
2025-05-12 09:17:42 +00:00
Thomas Adam
3d2b26dcfe Merge branch 'obsd-master' 2025-05-01 10:01:11 +01:00
nicm
6106a0f2c4 Add an option variation-selector-always-wide to instruct tmux not to
always interpret VS16 as a wide character and assume the terminal does
likewise. This is behaviour seen in a number of newer terminals' Unicode
14 support but not in older terminals; it seems to be a little
contentious and is currently difficult to detect.

Probably in the long run tmux should pick a behaviour, look at a (new)
terminfo(5) capability to tell it what the terminal will do, and emulate
as required, but at this point I'm not sure that is worth it for
something where support is mixed, seems to be in flux, and that mostly
only matters for emojis.

GitHub issues 3923 and 4475 and others before that.
2025-05-01 07:12:00 +00:00
nicm
1926c1b683 Don't map 256 to white-on-white either, and tidy code a bit. 2025-05-01 06:59:32 +00:00
Nicholas Marriott
6cc1a607fc optarg should be "extern char *" not "extern const char *". 2025-05-01 07:53:02 +01:00
Nicholas Marriott
1905afb3d8 tmux has its own warnx so there is no need for logit (and this gets rid of a
warning).
2025-04-29 17:39:56 +01:00
Nicholas Marriott
345a45083d Run the regress/style-trim.sh test in a plain Bash session, from Koichi Murase. 2025-04-29 17:29:32 +01:00
Nicholas Marriott
3a3babf597 Use printf not echo, from Koichi Murase. 2025-04-28 14:51:27 +01:00
Nicholas Marriott
f367e3a6c8 Switch to getopt_long from OpenSSH, from Koichi Murase in GitHub issue 4492. 2025-04-28 09:02:15 +01:00
Nicholas Marriott
f2283b0cb5 Fix some regress tests. 2025-04-28 08:57:29 +01:00
Nicholas Marriott
26081ac125 Create config.yml 2025-04-28 07:27:57 +01:00
Thomas Adam
36fba4cc54 Merge branch 'obsd-master' 2025-04-25 16:01:08 +01:00
Thomas Adam
58b4286edb Merge branch 'obsd-master' 2025-04-25 14:01:10 +01:00
nicm
b0b6a6f665 Revert change to handle command parsing failures in the client because
it breaks aliases.
2025-04-25 12:25:32 +00:00
nicm
63e5989f53 Remove a stray period, reported by David Mandelberg. 2025-04-25 11:39:39 +00:00
Nicholas Marriott
9da5707d3e Add a run-shell output test. 2025-04-25 12:06:51 +01:00
Thomas Adam
55292b72d1 Merge branch 'obsd-master' 2025-04-25 12:01:09 +01:00
Nicholas Marriott
06b1ad6241 Tests for boolean expressions from David Mandelberg. 2025-04-25 09:28:57 +01:00
nicm
1efe41b9b9 Add more features for boolean expressions in formats: 1) extend && and
|| to support arbitrarily many arguments and 2) add ! and !! for not and
not-not.
2025-04-25 08:28:21 +00:00
Nicholas Marriott
1198eed632 Currently the DCS dispatcher detects Sixel sequences based entirely on the
final character of the sequence, ignoring any intermediates. This means that
other DCS sequences with a q final (like DECRQSS), can be mistakenly
interpreted as Sixel. Add a check to make sure there are no intermediates
before dispatching potential Sixel sequences. From James Holderness in GitHub
issue 4488.
2025-04-25 08:26:26 +01:00
Thomas Adam
0772fe822a Merge branch 'obsd-master' 2025-04-24 12:01:10 +01:00
nicm
f53fac1b56 Do not add a trailing / if there is nothing to follow it, Johannes
Altmanninger in GitHub issue 4472.
2025-04-24 08:55:40 +00:00
nicm
68f2ac9296 Fix examples with too many backslashes, pointed out by David Mandelberg. 2025-04-24 08:54:40 +00:00
Thomas Adam
b4f8340f16 Merge branch 'obsd-master' 2025-04-22 16:01:12 +01:00
nicm
d2f73c17d5 Add calls to layout_fix_panes after a pane swap to fix case if one of
the panes is in alternate screen mode and had a scrollbar. From Michael
Grant in GitHub issue 4481.
2025-04-22 12:36:03 +00:00
nicm
5db914b745 Add notes to menu keys, from someone in GitHub issue 4478. 2025-04-22 12:34:56 +00:00
nicm
cd3d2910ee Fix description of pane_in_mode, from Julian Prein in GitHub issue 4469. 2025-04-22 12:33:35 +00:00
Nicholas Marriott
de7653f5a4 Update format tests for #? changes, from David Mandelberg. 2025-04-22 13:23:33 +01:00
nicm
b905039ed2 Improve #? conditional expression in formats:
1) add support for else if, so
   #{?cond1,value1,#{?cond2,value2,else-value}} can be changed to
   #{?cond1,value1,cond2,value2,else-value};

2) add default empty string if there's no else value, so
   #{?cond1,value1,} can be changed to #{?cond1,value1}.

From David Mandelberg in GitHub issue 4451.
2025-04-22 12:23:26 +00:00
Nicholas Marriott
f0a85d0469 Move cgroup dbus requests to the child to avoid a race where a spawned child
that quickly forks will have only the parent process moved to the newly created
cgroup. From Daniel De Graaf, GitHub issue 4435.
2025-04-14 07:27:02 +01:00
Thomas Adam
ac2779f4d8 Merge branch 'obsd-master' 2025-04-09 10:01:12 +01:00
nicm
68ffe65499 Fix documentation around optional arguments. This includes:
- Syncing between the usage string in code and in the man page.
- Adding optional arguments that were not mentioned (such as
  shell-command arguments).
- Adding square brackets around arguments that are actually optional.

From Julian Prein (julian at druck dot dev) in GitHub issue 4419.
2025-04-09 07:03:04 +00:00
nicm
3e14e7c48b Formats can use environment variables, from David Mandelberg. 2025-04-09 06:51:31 +00:00
nicm
b526e678b5 Make some usages more consistent and add -h to show usage, GitHub issue
4455 from David Mandelberg.
2025-04-09 06:27:43 +00:00
Thomas Adam
d3c39375d5 Merge branch 'obsd-master' 2025-04-03 14:01:10 +01:00
nicm
a7991dcbad Fix padding for word function as well, GitHub issue 4425. 2025-04-03 11:52:25 +00:00
nicm
47dff97834 Use backing grid for word in copy mode for wrapped flags, from someone
in GitHub issue 4447.
2025-04-03 11:51:27 +00:00
Thomas Adam
2905395695 Merge branch 'obsd-master' 2025-04-02 12:01:13 +01:00
nicm
4bf38da4d4 Only align panes and windows, not sessions, from David Mandelberg in
GitHub issue 4444.
2025-04-02 09:31:00 +00:00
nicm
9b37b9285e Popup window should not be draggable while mouse still pressed, and do
not try to work out theme if no pane. From Michael Grant in GitHub issue
4330.
2025-04-02 09:12:05 +00:00
Thomas Adam
dfdced2ab1 Merge branch 'obsd-master' 2025-03-31 00:01:10 +01:00
Nicholas Marriott
c2a95a372f Add a note about closed issues, from David Mandelberg. 2025-03-30 23:04:22 +01:00
nicm
ef923d28ff Do not try to trigger theme changed if session is NULL. 2025-03-30 22:01:55 +00:00
nicm
53b0e0bc02 Missing space, from David Mandelberg. 2025-03-30 21:47:01 +00:00
Thomas Adam
096c4b3e63 Merge branch 'obsd-master' 2025-03-28 20:01:10 +00:00
Thomas Adam
b8189d6ee3 Merge branch 'obsd-master' 2025-03-28 18:01:11 +00:00
nicm
f41dc91357 Only copy the key string not two bytes extra, found by David Mandelberg. 2025-03-28 17:15:25 +00:00
nicm
ef0c12ad9e Fix read of uninitialized memory for jobs with JOB_PTY flag. From David
Mandelberg.
2025-03-28 17:03:49 +00:00
Thomas Adam
9e1f110db0 Merge branch 'obsd-master' 2025-03-24 22:01:11 +00:00
nicm
483b2b3edb Correctly skip wide characters in hyperlinks, from someone in GitHub
issue 4425.
2025-03-24 20:17:24 +00:00
nicm
34a35b0f09 Expand formats with the pane modifier in tree mode so that #() doesn't
always use the same value. From Michael Grant in GitHub issues 4412 and
4420.
2025-03-24 20:13:03 +00:00
nicm
aca3ffb30a Add default-client-command to set the command used is tmux is run
without a command (the default stays new-session). From David Mandelberg
in GitHub issue 4422.
2025-03-24 20:01:03 +00:00
Thomas Adam
c3c4524def Merge branch 'obsd-master' 2025-03-21 16:01:11 +00:00
nicm
f101762d1b Fix mouse_hyperlink format in copy mode. From someone in GitHub issue
4418.
2025-03-21 14:04:26 +00:00
nicm
a541be3951 Add S-Up and S-Down to move windows in tree mode. From David Mandelberg
in GitHub issue 4415.
2025-03-21 13:36:42 +00:00
nicm
b7d640e764 Add some additional cursor format variables. From shiro at usagi dot io
in GitHub issue 4414.
2025-03-21 13:26:39 +00:00
Thomas Adam
969f6a60c3 portable: SYNCING: correct tmux-openbsd
The tmux-openbsd repository is called tmux-obsd.

Noticed via Github issue #4419
2025-03-21 12:46:06 +00:00
Thomas Adam
6703cb85d9 Merge branch 'obsd-master' 2025-03-21 04:01:10 +00:00
jsg
111e16e772 remove prototypes for removed functions 2025-03-21 02:10:42 +00:00
Thomas Adam
3eb93383a3 Merge branch 'obsd-master' 2025-03-17 22:01:10 +00:00
nicm
817b621d20 If there is an active query, set escape time temporarily to a higher
value (the old default - 500). Some Windows terminals are very slow to
respond, or the network may be slow. From github at jyn dot dev.
2025-03-17 20:43:29 +00:00
nicm
5eb30c1543 Handle padding cells correctly for regular expression searching, GitHub issue 4399 from
github at jyn dot dev.
2025-03-17 20:33:20 +00:00
Thomas Adam
d4b8635f50 Merge branch 'obsd-master' 2025-03-11 10:01:12 +00:00
nicm
4e4fe3eb39 Cleanup window_get_active_at function. GitHub issue 4401 from Michael
Grant.
2025-03-11 08:14:26 +00:00
Thomas Adam
882fb4d295 Merge branch 'obsd-master' 2025-03-04 10:01:15 +00:00
nicm
eaf70c955b Add mode 2031 support to automatically report dark or light theme. tmux
will guess the theme from the background colour on terminals which do
not themselves support the escape sequence. Written by Jonathan
Slenders, GitHub issue 4353.
2025-03-04 08:45:04 +00:00
nicm
3543d79048 Free fill character string if it cannot be used, GitHub issue 4394. 2025-03-04 08:03:19 +00:00
Thomas Adam
94783addfc Merge branch 'obsd-master' 2025-02-26 10:01:14 +00:00
nicm
91c0de60b4 Also need the implied meta paste keys in the list for output. 2025-02-26 09:02:00 +00:00
nicm
21f7db4c4d Do not allow meta prefix on paste start and end sequences, GitHub issue 4387. 2025-02-26 08:55:27 +00:00
nicm
f224d61f37 Document the use of ';' as a modifier separator, from Matt Liggett in
GitHub issue 4384.
2025-02-26 07:50:36 +00:00
nicm
d938ab5dd7 If command parsing fails in the client, report the error rather than
trying to send the command to the server. GitHub issue 4372 from Nikola
Tadic.
2025-02-26 07:47:46 +00:00
nicm
9a8f46e554 Fix colouring of pane border when scrollbars are enabled, GitHub issue
4378 from Michael Grant.
2025-02-26 07:42:52 +00:00
nicm
27ee0c9c3b Add the width of the scrollbars to the calculation of the width of the
window panes when finding the adjacent panes, GitHub issue 4370 from
Michael Grant.
2025-02-26 07:39:50 +00:00
Thomas Adam
251a87e2d2 Merge branch 'obsd-master' 2025-02-20 16:01:14 +00:00
nicm
084e6ee9ec Add a -M flag to capture-pane to use the copy mode screen, GitHub issue
4358.
2025-02-20 13:39:58 +00:00
nicm
18331e39bf Reset overlay_resize pointer when clearing overlay. 2025-02-20 13:32:07 +00:00
Nicholas Marriott
250c88efdc Add .swp, from Nikola Tadic. 2025-02-20 13:31:07 +00:00
Thomas Adam
9a377485be Merge branch 'obsd-master' 2025-02-13 18:01:08 +00:00
nicm
47a56c11f2 Add a note about C-r and C-s behaviour, GitHub issue 4309.
Also add a missing word, from jmc@.
2025-02-13 16:31:25 +00:00
Nicholas Marriott
c4b9716873 Look for imsg_add not _init now. 2025-02-13 16:24:33 +00:00
Thomas Adam
ec119b2f9e Merge branch 'obsd-master' 2025-02-10 10:01:11 +00:00
nicm
5d1a6acc84 Align index numbers in trees, from David Mandelberg, GitHub issue 4360. 2025-02-10 08:18:23 +00:00
nicm
80eb460fc9 Add display-message -C flag to update pane while message is displayed,
GitHub issue 4363 from Vitaly Ostrosablin.
2025-02-10 08:14:32 +00:00
Thomas Adam
ef68debc8d Merge branch 'obsd-master' 2025-01-27 12:01:13 +00:00
nicm
4c12ac9fb8 Make list-commands command show only one command if an argument is
given, from Ilya Grigoriev in GitHub issue 4352.
2025-01-27 09:16:05 +00:00
nicm
244bb726e2 Add some missing spaces, from Ilya Grigoriev. 2025-01-27 09:05:22 +00:00
Nicholas Marriott
6ab268c7bb Remove old issue template. 2025-01-20 13:06:03 +00:00
Nicholas Marriott
0c9f8ff189 Update issue templates 2025-01-20 13:02:24 +00:00
Nicholas Marriott
dd7d04be95 Update issue templates 2025-01-20 13:00:07 +00:00
Thomas Adam
58392d29da Merge branch 'obsd-master' 2025-01-17 18:01:08 +00:00
nicm
9260f5dc96 Do not update focus on client's without a session. 2025-01-17 15:53:01 +00:00
Thomas Adam
62a6c16b43 Merge branch 'obsd-master' 2025-01-13 12:01:11 +00:00
nicm
31e8d4676a Count line numbers correctly inside strings, reported by Pedro Navarro
in GitHub issue 4325.
2025-01-13 08:58:34 +00:00
Thomas Adam
d3cbe00f78 Merge branch 'obsd-master' 2025-01-12 16:01:11 +00:00
nicm
97fe3563fa Do not crash if moving popup that has exited to a pane, from Michael
Grant in GitHub issue 4312.
2025-01-12 14:36:28 +00:00
nicm
37ad1e2f6d Map bright black (colour 8) to white (7) if the background is black on
terminals with only eight colours so the text is not invisible. From
Dmytro Bagrii in GitHub issue 4322.
2025-01-12 14:20:49 +00:00
Thomas Adam
00894d188d Merge branch 'obsd-master' 2025-01-02 12:01:10 +00:00
nicm
2a5eba7899 Check backspace against VERASE earlier before it is translated to an
internal key and do not go through the mapping on output. Fixes problems
reported by Ben Price in GitHub issue 4284 and by tb@.
2025-01-02 10:34:45 +00:00
Thomas Adam
bf30492d57 Merge branch 'obsd-master' 2025-01-01 18:01:10 +00:00
nicm
e00853ee82 Add an option allowing users to override the width of individual Unicode
codepoints (overriding tmux's default list).
2025-01-01 15:17:36 +00:00
Thomas Adam
e75f3a0060 Merge branch 'obsd-master' 2024-12-20 10:01:10 +00:00
nicm
eece41547e Only map S-Tag in mode 2, not mode 1. GitHub issue 4304. 2024-12-20 07:10:51 +00:00
Thomas Adam
c311202b73 Merge branch 'obsd-master' 2024-12-17 10:01:11 +00:00
nicm
e149d29803 Assign excess space more evenly when spreading out cells, from Torbjorn
Lonnemark.
2024-12-17 08:40:24 +00:00
Thomas Adam
190ddda572 Merge branch 'obsd-master' 2024-12-16 12:01:12 +00:00
Thomas Adam
6b470c5222 Merge branch 'obsd-master' 2024-12-16 10:01:11 +00:00
nicm
6b32d195e8 Add a nesting limit to source-file, from Fadi Afani in GitHub issue
4223.
2024-12-16 09:13:09 +00:00
nicm
bec6ce54c1 Memory leak, GitHub issue 4298. 2024-12-16 08:54:34 +00:00
nicm
106d1c3538 Missing main-vertical-mirrored from layout list, from charlotte at
lottia dot net.
2024-12-16 08:51:41 +00:00
Thomas Adam
ae8f2208c9 Merge branch 'obsd-master' 2024-12-06 12:01:11 +00:00
nicm
5c3cf2f08b Preserve modifiers on backspace. 2024-12-06 09:07:40 +00:00
nicm
102f34090d Do not write bracketed paste keys themselves if the pane has not asked
for them.
2024-12-06 09:06:56 +00:00
Thomas Adam
99790c90aa Merge branch 'obsd-master' 2024-12-04 22:01:08 +00:00
nicm
6d792e4123 Fix backspace option for new key format, GitHub issue 4284. 2024-12-04 19:11:15 +00:00
Thomas Adam
98e322d5a5 Merge branch 'obsd-master' 2024-12-03 14:01:12 +00:00
jmc
01edce40f3 M-1 to M-7 for 7 preset layouts; from bunkmate
ok nicm
2024-12-03 11:18:34 +00:00
Thomas Adam
0549f87ccc Merge branch 'obsd-master' 2024-11-28 10:01:11 +00:00
nicm
72cd0eac29 Add extended keys flag for foot terminal. 2024-11-28 08:49:14 +00:00
Thomas Adam
f0c68533c4 Merge branch 'obsd-master' 2024-11-27 12:01:08 +00:00
Nicholas Marriott
9e4a9c51cc Use evbuffer_readline if readln is not available. It doesn't work properly but
at least it builds.
2024-11-27 10:37:45 +00:00
Nicholas Marriott
6874ec0dcc Remove endian.h. 2024-11-27 10:31:51 +00:00
Nicholas Marriott
252f41818e Update imsg and remove workaround. 2024-11-27 10:30:52 +00:00
nicm
d361f21065 Do not check for latest client in callback since the type may be latest
but with no window, fixes new-session -x and -y with another attached
client. GitHub issue 4268.
2024-11-27 10:12:20 +00:00
nicm
feb090abb3 Do not stop drag on double or triple click, GitHub issue 4272. 2024-11-27 10:10:20 +00:00
Thomas Adam
db771ec6e3 Merge branch 'obsd-master' 2024-11-26 18:01:10 +00:00
nicm
6f7db82b18 Add copy-mode-position-style and copy-mode-selection-style for copy
mode (they default to mode-style as before).
2024-11-26 15:52:41 +00:00
nicm
67cc7f6dc6 Enter is now sent from single prompt as \r not empty string. 2024-11-26 15:51:48 +00:00
Thomas Adam
db978db271 Merge branch 'obsd-master' 2024-11-25 14:01:12 +00:00
nicm
363f35f076 Do not try to terminate an empty buffer. 2024-11-25 12:32:24 +00:00
Thomas Adam
49b7276f2a Merge branch 'obsd-master' 2024-11-25 10:01:11 +00:00
nicm
f57131e11b Use cursor style from global options instead of default for popups, from
Alexander Arch.
2024-11-25 08:36:46 +00:00
nicm
420af9e108 Do not rely on window reference count for linked formats because they
are also used for notifications, GitHub issue 4258.
2024-11-25 08:34:01 +00:00
Thomas Adam
ceaf9b7452 Merge branch 'obsd-master' 2024-11-22 14:01:11 +00:00
nicm
5fd45b3892 Do not strvis output to terminal from commands. 2024-11-22 12:58:05 +00:00
nicm
64d82d5988 Document command prompt escape sequence, from Von Welch. 2024-11-22 12:36:13 +00:00
Nicholas Marriott
0f308bd18f imsg no longer associates file descriptors with the imsg they were sent with,
work around this for the moment (it is not clear if this is intentional).
2024-11-22 09:58:47 +00:00
Nicholas Marriott
1365f1ce52 Add an __unused. 2024-11-22 08:07:21 +00:00
Nicholas Marriott
7fc4e54efe And arpa/inet.h needs to stay... 2024-11-22 08:06:30 +00:00
Nicholas Marriott
c98c08c232 Need to remove endian.h as well. 2024-11-22 08:05:02 +00:00
Nicholas Marriott
990c724bd8 Bring in updated imsg. 2024-11-22 08:03:55 +00:00
Thomas Adam
b82c7b40b0 Merge branch 'obsd-master' 2024-11-21 16:01:11 +00:00
claudio
70299c6646 Convert sbin and usr.bin to check for imsgbuf_init failure and add
imsgbuf_allow_fdpass where needed.

OK tb@
2024-11-21 13:35:20 +00:00
Thomas Adam
ea6c5053c8 Merge branch 'obsd-master' 2024-11-21 10:01:09 +00:00
nicm
3f4fd45d5c Fix save-buffer documentation - it writes to stdout. From Ilya Grigoriev. 2024-11-21 07:37:21 +00:00
nicm
7c7e88253e When the mouse clicked on the border between two horizontal panes, the
location was not being set properly. Pulled the checking of this into
separate function for clarity. Fixes dragging on horizontal pane border.
From Michael Grant.
2024-11-21 07:34:38 +00:00
Thomas Adam
bb94a5437d Merge branch 'obsd-master' 2024-11-21 00:01:13 +00:00
nicm
273f9b2027 Fix word navigation on lines with tabs, from Alexander Arch. 2024-11-20 20:54:02 +00:00
Nicholas Marriott
b6d0b4be30 Define SD_ID128_UUID_FORMAT_STR if missing, from Marc Reisner. 2024-11-19 17:55:12 +00:00
Thomas Adam
2dce45f427 Merge branch 'obsd-master' 2024-11-18 10:01:12 +00:00
nicm
a3ede3106a Check all %if in the list when deciding whether to process an
assignment, not just the most recent.
2024-11-18 08:29:35 +00:00
Thomas Adam
dd9722184b Merge branch 'obsd-master' 2024-11-16 18:01:08 +00:00
nicm
a854e36f2b Do not call layout_fix_panes if wp is NULL (that is, a popup). 2024-11-16 16:49:50 +00:00
Thomas Adam
be594ff8a4 Merge branch 'obsd-master' 2024-11-15 16:01:11 +00:00
nicm
c66628e52b Add no-detach-on-destroy client option (useful for control mode
clients). From laur dot aliste at gmail dot com, GitHub issue 4242.
2024-11-15 14:09:04 +00:00
nicm
350a151ee4 Add two new style parameters, width and pad, which apply to scrollbars.
From Michael Grant, GitHub issue 4241.
2024-11-15 13:12:20 +00:00
Thomas Adam
d35458e3fe Merge branch 'obsd-master' 2024-11-15 10:01:11 +00:00
nicm
d6883c0266 Turn off scrollbar when pane is in alternate screen, from Michael Grant,
GitHub issue 4231.
2024-11-15 09:01:16 +00:00
Thomas Adam
563ed05353 Merge branch 'obsd-master' 2024-11-12 12:01:10 +00:00
nicm
f527412d9b Tidy up loop, from Alexander Arch. 2024-11-12 10:06:35 +00:00
nicm
713cacab1e Mouse support on the scrollbars, from Michael Grant. 2024-11-12 09:32:56 +00:00
Thomas Adam
846f813565 Merge branch 'obsd-master' 2024-11-11 10:01:08 +00:00
nicm
c26d71d3e9 Add an option to control the input buffer size, from Ken Lau. 2024-11-11 08:41:05 +00:00
Thomas Adam
c8677d3272 Merge branch 'obsd-master' 2024-11-08 10:01:08 +00:00
nicm
596ea62dc3 Some fixes for searching for tabs, from Alexander Arch. 2024-11-08 08:51:36 +00:00
Thomas Adam
1fe30bb2e8 Merge branch 'obsd-master' 2024-11-06 20:33:45 +00:00
nicm
809d659e64 Xr to Ic, from jmc. 2024-11-05 21:07:19 +00:00
nicm
09f4e43189 Add support for a scrollbar at the side of each pane. New options
pane-scrollbars turn them on or off, pane-scrollbars-position sets the
position (left or right), and pane-scrollbars-style to set the colours.
Mouse support will come later. From Michael Grant in GitHub issue 4221.
2024-11-05 09:41:17 +00:00
Thomas Adam
679bbdcf8f Merge branch 'obsd-master' 2024-11-04 10:01:09 +00:00
nicm
a0c79aa87b Do not make padding cells extended (there can be a lot of them), from
Alexander Arch .
2024-11-04 08:52:13 +00:00
Nicholas Marriott
46f3846659 Add -Wno-macro-redefined for macOS. 2024-10-31 12:57:40 +00:00
Thomas Adam
bbc3cc558c Merge branch 'obsd-master' 2024-10-28 10:01:08 +00:00
nicm
c8bd42de16 Match tab cells when searching, from Alexander Arch in GitHub issue
4201.
2024-10-28 08:16:51 +00:00
nicm
62e15e905b Treat tabs as a word separator, from Alexander Arch in GitHub issue
4201.
2024-10-28 08:16:06 +00:00
nicm
125a7b9177 Fix issues in the command prompt: set PROMPT_QUOTENEXT after quoting
than before, meaning that accidentally scrolling the mouse wheel doesn't
break quoting; and move the cursor correctly over wide characters. From
Alexander Arch in GitHub issue 4212.
2024-10-28 08:11:59 +00:00
Thomas Adam
895044c52b Merge branch 'obsd-master' 2024-10-25 22:01:10 +01:00
nicm
40c01c2d37 Allow tabs even on terminals without UTF-8, reported by jmc. 2024-10-25 19:36:38 +00:00
Thomas Adam
911d768b71 Merge branch 'obsd-master' 2024-10-25 18:01:11 +01:00
nicm
71a503e40c Allow control characters to be entered at the command prompt prefixed
with with C-v, from  Alexander Arch in GitHub issue 4206.
2024-10-25 15:32:51 +00:00
nicm
487b0ee124 Do not attempt to search for zero length strings, from Alexander Arch in
GitHub issue 4209.
2024-10-25 15:19:15 +00:00
nicm
eaec0a48f4 Do not stop stop at first padding in format_grid_line and handle tabs. 2024-10-25 15:13:10 +00:00
nicm
fdbc6cdea5 Flag tabs if possible in the grid cell so they can be preserved on
copying and capture-pane. From Alexander Arch in GitHub issue 4201.
2024-10-25 15:00:18 +00:00
Thomas Adam
9623ec3ee4 Merge branch 'obsd-master' 2024-10-25 10:01:07 +01:00
nicm
63582c154c Add a helper function for cell data comparison, from Alexander Arch. 2024-10-25 07:57:49 +00:00
Thomas Adam
914815e70f Merge branch 'obsd-master' 2024-10-21 16:01:07 +01:00
nicm
354926a956 Bump up the maximum for repeat-time (some people want it to be
effectively infinite).
2024-10-21 12:42:06 +00:00
nicm
df303770ea Add function to get current offset from copy mode, from Michael Grant. 2024-10-21 12:39:49 +00:00
Thomas Adam
a269c33a54 Merge branch 'obsd-master' 2024-10-21 10:01:08 +01:00
nicm
eb04f9314b Adjust how Ctrl and Meta keys are sent to use standard representation if
available in mode 1, from Stanislav Kljuhhin, GitHub issue 4188.
2024-10-21 07:38:06 +00:00
Thomas Adam
a9d0461839 Merge branch 'obsd-master' 2024-10-17 20:01:07 +01:00
nicm
71aa4fe767 Switch pane-colors and cursor-style options to be in alphabetical order,
from Teubel Gyorgy, GitHub issue 4191.
2024-10-17 17:22:01 +00:00
nicm
cfd3c02306 Allow attributes in menu style, from Japin Li in GitHub issue 4194. 2024-10-17 17:10:41 +00:00
Thomas Adam
35104059ed Merge branch 'obsd-master' 2024-10-15 00:01:10 +01:00
nicm
934035db71 Set ACS flag for REP. Reported by Romain Francoise, GitHub issue 4182. 2024-10-14 20:26:45 +00:00
Nicholas Marriott
8ff65230e1 Optimize sixel_print. Previously, the algorithm scanned each pixel several
times; once to find out which colors are active, and then once for every single
active color to actually construct the output string.

Now it constructs the compressed sixel patterns in the first pass (now x * 12
iters), so we can reduce the second pass (the really expensive part, at active
colors * x * 6 iters) to just appending these to the output buffer.

From nincsnevem662 at gmail dot com in GitHub issue 4184.
2024-10-14 11:05:59 +01:00
Thomas Adam
cb00e869ea Merge branch 'obsd-master' 2024-10-12 12:01:09 +01:00
nicm
f8b56fdc3f Call realpath on the source file to match -f on the command line, GitHub
issue 4180.
2024-10-12 08:20:32 +00:00
nicm
2d8b6fcf23 Do not rename a buffer to itself, GitHub issue 4181. 2024-10-12 08:13:52 +00:00
Nicholas Marriott
6ddee22f81 Portable tmux needs to check ENABLE_SIXEL. 2024-10-10 12:07:29 +01:00
Thomas Adam
66c738bc10 Merge branch 'obsd-master' 2024-10-10 12:01:08 +01:00
nicm
bcc47add38 Add a sixel_support format variable which is 1 if SIXEL is supported
(always 0 on OpenBSD), requested by Misaki Masa in GitHub issue 4177
2024-10-10 10:41:33 +00:00
Thomas Adam
d326c51d79 Merge branch 'obsd-master' 2024-10-08 12:01:09 +01:00
nicm
940fdf7ed0 Draw pane status line on the correct line when at the bottom, fixes
issues if the window size is smaller than the entire terminal. GitHub
issue 3943.
2024-10-08 09:40:50 +00:00
Thomas Adam
4f5d6d97d3 Merge branch 'obsd-master' 2024-10-08 10:01:08 +01:00
nicm
00412b570b Put the prompt cursor attributes into the status line screen and use
that rather than updating the current screen (since there might not be
one).
2024-10-08 06:29:44 +00:00
Thomas Adam
6a35b8ad07 Merge branch 'obsd-master' 2024-10-07 16:01:08 +01:00
nicm
735082b7c2 Add prompt-cursor-colour and prompt-cursor-style to set the style of the
cursor in the command prompt and remove the emulated cursor, from
Alexander Arch in GitHub issue 4170.
2024-10-07 12:58:36 +00:00
Thomas Adam
5b7bdc82c8 Merge branch 'obsd-master' 2024-10-07 12:01:08 +01:00
nicm
a3dea81b49 Add initial-repeat-time option to allow the first repeat time to be
increased and later reduced, from David le Blanc in GitHub issue 4164.
2024-10-07 08:50:47 +00:00
Thomas Adam
486221f206 Merge branch 'obsd-master' 2024-10-06 12:01:07 +01:00
nicm
9528d7470b Try to stay near the same line in modes if the current one is removed. 2024-10-06 09:30:22 +00:00
Thomas Adam
933d6b90ed Merge branch 'obsd-master' 2024-10-05 16:01:07 +01:00
nicm
a869693405 Send focus events to pane when entering or leaving popup, GitHub issue
3991.
2024-10-05 12:10:16 +00:00
Nicholas Marriott
549c35b061 Update regression tests. 2024-10-05 07:01:16 +01:00
Nicholas Marriott
42959db0a4 Update CHANGES. 2024-10-05 07:00:22 +01:00
Thomas Adam
114977dd25 Merge branch 'obsd-master' 2024-10-05 04:01:08 +01:00
Thomas Adam
aba8ffbe7b Merge branch 'obsd-master' 2024-10-05 02:01:07 +01:00
nicm
455a2b3705 Remove some debugging left behind. 2024-10-05 00:35:35 +00:00
nicm
e0638c48cd Add copy-mode-position-format to configure the position indicator. 2024-10-05 00:32:55 +00:00
nicm
41f6b691e3 Document missing window-layout-changed hook. 2024-10-04 22:36:11 +00:00
Nicholas Marriott
0f30fa34e3 Add install step. 2024-10-04 23:29:36 +01:00
Thomas Adam
e486f5ffdc Merge branch 'obsd-master' 2024-10-04 22:01:09 +01:00
nicm
5a68730e2f Add -y flag to disable confirmation prompts in modes, GitHub issue 4152. 2024-10-04 19:16:13 +00:00
Nicholas Marriott
7997fd3152 Missing define. 2024-10-04 18:59:53 +01:00
nicm
048db82041 Do not translate BSpace as Unicode, GitHub issue 4156. 2024-10-04 18:45:36 +01:00
Thomas Adam
16b44d2a5b Merge branch 'obsd-master' 2024-10-04 18:01:07 +01:00
nicm
34775fc235 Do not translate BSpace as Unicode, GitHub issue 4156. 2024-10-04 14:55:17 +00:00
Thomas Adam
f2fd8c854a Merge branch 'obsd-master' 2024-10-04 10:01:08 +01:00
nicm
9f2a853d87 Rework of copy mode commands ("send-keys -X") to parse the arguments so
that flags may be detected propertly rather than just looking for
strings ("-O" and so on). Also add -C and -P flags to the copy commands:
-C prevents the commands from sending the text to the clipboard and -P
prevents them from adding the text as a paste buffer.

Note some of the default key bindings change to add "--" and any similar
custom key bindings using "send-keys -X" may need a similar change.

GitHub issue 4153.
2024-10-04 07:03:08 +00:00
Nicholas Marriott
7a78cdf78c Three SIXEL improvements from nincsnevem662 at gmail dot com in GitHub issue
4149:

- Pass P2 on the received image through when forwarding the image to the
  outside terminal to preserve transparency;

- Preserve raster attribute dimensions which may be smaller than the actual
  image (used to crop out parts of the image to work around the limitation that
  rows must come in groups of six);

- To avoid collapsing empty sixel lines, no longer ignore duplicate "-" new
  line specifiers.
2024-10-04 07:49:07 +01:00
nicm
356887bca2 Improve fix for shifted keys so it works for all the keys it should,
Stanislav Kljuhhin in GitHub issue 4146.
2024-10-03 08:14:32 +01:00
Thomas Adam
2df15ad08c Merge branch 'obsd-master' 2024-10-03 08:01:07 +01:00
nicm
780a87be9a Improve fix for shifted keys so it works for all the keys it should,
Stanislav Kljuhhin in GitHub issue 4146.
2024-10-03 05:41:59 +00:00
Nicholas Marriott
1e303b6a9d Bypass permission check for Cygwin, based on a different change by Yuya Adachi
via Rafael Kitover; GitHub issue 4148.
2024-10-02 14:04:06 +01:00
Thomas Adam
8d2aee34ab Merge branch 'obsd-master' 2024-10-02 14:01:10 +01:00
nicm
05116cefe6 Add MSYSTEM to default update-environment. 2024-10-02 11:51:15 +00:00
nicm
ce4be58eef Add a define for the socket permissions check so it can be overridden
more easily (for Cgywin).
2024-10-02 11:48:16 +00:00
Nicholas Marriott
8d6eb4be40 Set client stdout file descriptor also for Cgywin, from Michael Wild via Rafael
Kitover in GitHub issue 4148.
2024-10-02 12:16:24 +01:00
nicm
834ec91b44 Report shifted keys like S-A as A not as S-A in mode 1 extended keys,
from Stanislav Kljuhhin.
2024-10-02 11:06:53 +01:00
Thomas Adam
38c38f1c64 Merge branch 'obsd-master' 2024-10-02 10:01:08 +01:00
nicm
ddf6af79e3 Report shifted keys like S-A as A not as S-A in mode 1 extended keys,
from Stanislav Kljuhhin.
2024-10-02 08:06:45 +00:00
Thomas Adam
826ba515be Merge branch 'obsd-master' 2024-10-01 14:01:08 +01:00
Nicholas Marriott
729bded8bf Fix up regression tests. 2024-10-01 13:40:42 +01:00
nicm
b95a06d2d4 Add a way to make the preview larger in tree mode, GitHub issue 4124. 2024-10-01 10:10:29 +00:00
Thomas Adam
157d748949 Merge branch 'obsd-master' 2024-10-01 10:01:10 +01:00
nicm
1c1f4c1219 Use global cursor style and colour options for modes instead of default,
GitHub issue 4117.
2024-10-01 08:01:19 +00:00
nicm
17bab32794 Change pasting to bypass the output key processing entirely and write
what was originally received. Fixes problems with pasted text being
interpreted as extended keys reported by Mark Kelly.
2024-10-01 06:15:47 +00:00
Nicholas Marriott
868ddf5d38 3.5a bits. 2024-09-30 13:09:50 +01:00
nicm
a1b7a3bcb3 Only use default-shell for popups, return to /bin/sh for run-shell,
if-shell and #() - these have been documented as using /bin/sh for a
long time and scripts rely on it. Pointed out by Gregory Pakosz.
2024-09-30 13:07:46 +01:00
nicm
cbd419c483 Fix grey colour, from Magnus Gross. 2024-09-30 13:06:55 +01:00
Thomas Adam
25b1cc1e8e Merge branch 'obsd-master' 2024-09-30 12:01:11 +01:00
nicm
89adec0ca5 On some Windows terminals, if TIOCWINSZ does not return xpixel and
ypixel (they are zero), if this is the case then try the query escape
sequences. From Dmitry Galchinsky in GitHub issue 4099.
2024-09-30 08:10:20 +00:00
nicm
f95d055e04 Only use default-shell for popups, return to /bin/sh for run-shell,
if-shell and #() - these have been documented as using /bin/sh for a
long time and scripts rely on it. Pointed out by Gregory Pakosz.
2024-09-30 07:54:51 +00:00
Thomas Adam
7b148f7b5b Merge branch 'obsd-master' 2024-09-29 22:01:09 +01:00
nicm
9bd039d1bc Fix grey colour, from Magnus Gross. 2024-09-29 20:05:42 +00:00
Nicholas Marriott
7c30056d96 Bump to 3.6. 2024-09-27 08:58:00 +01:00
Nicholas Marriott
ac44566c9c tmux 3.5. 2024-09-27 08:56:39 +01:00
Thomas Adam
64f1076d97 Merge branch 'obsd-master' 2024-09-17 00:01:09 +01:00
nicm
489c69f5ed Add copy mode commands which were missed when descriptions were added,
from Julian Prein, GitHub issue 4121.
2024-09-16 20:46:58 +00:00
nicm
f897049935 Change the behaviour of extended-keys always slightly so that
applications can still enter mode 2 if they want, they just cannot turn
extended keys off entirely. From Stanislav Kljuhhin.
2024-09-16 20:38:48 +00:00
nicm
d8b66110f7 Add a prefix timeout option, from Conor Taylor in GitHub issue 4108. 2024-09-16 20:28:22 +00:00
Nicholas Marriott
8b1a3bb2e5 Only default --enable-debug if the user has not turned it off, from Ken Lau. 2024-09-16 21:19:43 +01:00
Thomas Adam
37771a5a8d Merge branch 'obsd-master' 2024-09-11 22:01:10 +01:00
nicm
3a8a31d8d2 Mouse move keys are not useful as key bindings because we do not turn
them on unless the application requests them. Ignore them so they do not
cause the prefix to be canceled, GitHub issue 4111.
2024-09-11 19:12:33 +00:00
Nicholas Marriott
c36ffcbe56 Typo from Bastian Venthur. 2024-09-06 13:09:43 +01:00
Thomas Adam
3d8ead8a97 Merge branch 'obsd-master' 2024-08-28 10:27:21 +01:00
nicm
141cd78407 Display hyperlinks in copy mode and add copy_cursor_hyperlink format to
get the hyperlink under the cursor.
2024-08-27 07:49:07 +00:00
nicm
d0c8124661 Add search_count and search_count_partial formats in copy mode, GitHub
issue 4091.
2024-08-27 07:31:26 +00:00
nicm
2917bc5274 Do not reset mouse pane if clicked on status line, it may have been set
by a range.
2024-08-27 07:25:27 +00:00
nicm
2d1e93447e Use strtonum instead of atoi. 2024-08-26 13:02:15 +00:00
nicm
31b6c9356c C-h should not be treated specially and represented internally as \b but
as C-h like the other Ctrl keys. Backspace is already handled separately
if it VERASE.
2024-08-26 07:45:05 +00:00
nicm
9e2a7c28f5 Pass the screen_redraw_ctx struct into more functions instead of
individual arguments (for example for the pane status), from Michael
Grant.
2024-08-26 07:34:40 +00:00
nicm
73b2277af8 Client flags was changed to uint64_t a while ago, fix a few cases where
it is still int (do not matter now but will with some new flags). From
Michael Grant.
2024-08-26 07:30:46 +00:00
nicm
a84c109604 Add window_pane_mode helper function to tell if a pane is in copy mode,
from Michael Grant.
2024-08-26 07:14:40 +00:00
nicm
4823acca8f Add copy-mode -d flag to scroll a page down if in copy mode already,
from Michael Grant.
2024-08-26 07:09:34 +00:00
Thomas Adam
34807388b0 Merge branch 'obsd-master' 2024-08-23 16:01:10 +01:00
nicm
08be883297 Ignore internal function keys if they have not got an entry in the key
table.
2024-08-23 13:25:39 +00:00
Thomas Adam
13bd0e46db Merge branch 'obsd-master' 2024-08-23 14:01:08 +01:00
nicm
6e9a914014 Check for exact match for layout name before looking for a prefix match. 2024-08-23 10:19:06 +00:00
Thomas Adam
7990e5fa8f Merge branch 'obsd-master' 2024-08-22 12:01:07 +01:00
nicm
4860a58d07 Clear overlay when command prompt is entered. Also fix some spacing in
man page pointed out by jmc.
2024-08-22 09:05:51 +00:00
Thomas Adam
99af9f23bd Merge branch 'obsd-master' 2024-08-22 08:01:08 +01:00
nicm
9ebbe2cca7 Short Ctrl keys like ^A need to be converted to lowercase so they end up
as 'a'|KEYC_CTRL to match the new internal representation. Problem
reported by naddy@.
2024-08-22 05:39:55 +00:00
Thomas Adam
692bae9ea6 Merge branch 'obsd-master' 2024-08-21 08:01:09 +01:00
nicm
a6645c4de4 Mention that load- and save-buffer can use stdin, from Ramon Fischer. 2024-08-21 05:06:45 +00:00
nicm
06292baadc Add mirrored versions of the main-horizontal and main-vertical layouts where
the main pane is bottom or right instead of top or left, from Sherwyn Sen.
2024-08-21 05:03:13 +00:00
nicm
ceda0a68ae C-Space and Meta keys should not be translated in mode 1 extended keys. 2024-08-21 04:55:57 +00:00
nicm
4fa90c9acf Set the default for extended-keys back to off because it appears emacs turns
the keys on but does not correctly handle them except in xterm (!). Also fix so
that off takes effect as expected.
2024-08-21 04:37:42 +00:00
nicm
c7e61a01e5 Revamp extended keys support to more closely match xterm and support
mode 2 as well as mode 1. From Stanislav Kljuhhin (GitHub issue 4038).

This changes tmux to always request mode 2 from parent terminal, change
to an unambiguous internal representation of keys, and adds an option
(extended-keys-format) to control the format similar to the xterm(1)
formatOtherKeys resource.
2024-08-21 04:17:09 +00:00
Thomas Adam
963e824f5f Merge branch 'obsd-master' 2024-08-19 12:01:09 +01:00
nicm
de6bce057a Allow REP to work with Unicode characters, GitHub issue 3687. 2024-08-19 08:31:36 +00:00
nicm
937ba1d8dd Both terminators \007 and \033\\ leave the index pointing to the final
character of the terminator, so correct the size calculation to always
add one. GitHub issue 4082.
2024-08-19 08:29:16 +00:00
Thomas Adam
651891c3bd Merge branch 'obsd-master' 2024-08-04 12:01:09 +01:00
nicm
4008e2ff6d Make a little effort to treate CRLF as LF in config files. GitHub issue
3720.
2024-08-04 09:42:23 +00:00
nicm
b88130d24b The Linux console has some bugs with bright colours. It seems likely
that it is emulating them by setting a bright (or bold) flag; however,
when the colour is changed from a bright colour (say SGR 96) to a
non-bright (say SGR 36), the flag is not reset, so the new colour
remains as bright. SGR 39 (default colour) also does not reset, so you
end up with the bright default colour. Work around this by sending SGR 0
when switching away from a bright colour, and disable AX for TERM=linux.
Also make the check for AX simpler and do not check for the op
capability is not actually used. GitHub issue 3976.
2024-08-04 09:35:30 +00:00
nicm
fc7ee7efc7 -l should be before -r, pointed out by jmc a while ago. 2024-08-04 09:01:18 +00:00
Nicholas Marriott
d0eb3fe543 Use terminal-features instead of terminal-overrides to enable truecolor support
in example_tmux.conf, from Simon Hengel.
2024-08-04 09:59:18 +01:00
Nicholas Marriott
2ac0faf119 If built with systemd, remove some environment variables it uses. From Ciprian
Dorin Craciun, GitHub issue 4035.
2024-08-04 09:58:13 +01:00
Nicholas Marriott
775789fbd5 Rename header guards on compat/queue.h to avoid it conflicting if the system
header is included first. This matters on some platforms (macOS) where queue.h
is old. From Saagar Jha in GitHub issue 4041.
2024-08-04 09:57:26 +01:00
nicm
7b6fbe7262 Adjust the logic when deleting last buffer to better preserve the
selection: if selecting the element below the deleted one fails (because
as the last one), select the one above it instead. From Daniel Mueller,
GitHub issue 4043.
2024-08-04 08:53:43 +00:00
Thomas Adam
109d2bda1a Merge branch 'obsd-master' 2024-07-22 18:01:09 +01:00
nicm
ddd4e57c65 Expand full array option values if no index is provided, GitHub issue
4051.
2024-07-22 15:27:42 +00:00
Nicholas Marriott
3c2621b41b Support building with jemalloc memory allocator, from Romain Francoise. 2024-07-15 11:25:15 +01:00
Nicholas Marriott
5039be657c utf8proc bits for utf8_fromwc. 2024-07-12 14:30:56 +01:00
Thomas Adam
d02254f754 Merge branch 'obsd-master' 2024-07-12 14:01:09 +01:00
nicm
aa1353947e UTF-8 keys now contain the internal representation and not the Unicode
codepoint, so convert extended keys properly. From Stanislav Kljuhhin.
2024-07-12 11:21:18 +00:00
Nicholas Marriott
171004dfd0 Use mdoc on Illumos which uses mandoc, from Andy Fiddaman. 2024-07-08 10:57:32 +01:00
Thomas Adam
c773fe89e7 Merge branch 'obsd-master' 2024-06-24 12:01:10 +01:00
nicm
093b5a5518 Add a way (refresh-client -r) for control mode clients to provide OSC 10
and 11 responses to tmux so they can set the default foreground and
background colours, from George Nachman in GitHub issue 4014.
2024-06-24 08:30:50 +00:00
nicm
db1665868f Check the underline style colour against the correct default value again
(it was changed from 0 to 8), from Romain Francoise.
2024-06-24 08:11:46 +00:00
Thomas Adam
c07e856d24 Merge branch 'obsd-master' 2024-05-24 16:01:08 +01:00
nicm
692ce59bce Do not escape $ unless DQ is set, that is the only case where we need to
escape it.
2024-05-24 12:41:24 +00:00
nicm
9e7c1aee48 Add N to search backwards in tree modes, from Fadi Afani in GitHub issue
3982.
2024-05-24 12:39:06 +00:00
Thomas Adam
4c2eedca5a Merge branch 'obsd-master' 2024-05-19 06:01:09 +01:00
jsg
ac6c1e9589 remove prototype with no matching function 2024-05-19 03:27:58 +00:00
Thomas Adam
0903790b00 Merge branch 'obsd-master' 2024-05-18 12:01:09 +01:00
jsg
03de52653e remove prototypes with no matching function; ok nicm@ 2024-05-18 08:51:26 +00:00
jsg
da06719309 remove externs with no matching var; ok nicm@ 2024-05-18 08:50:11 +00:00
Thomas Adam
fc84097379 Merge branch 'obsd-master' 2024-05-15 14:01:09 +01:00
Thomas Adam
4fd725c6e1 Merge branch 'obsd-master' 2024-05-15 12:01:10 +01:00
nicm
d39dcea30a Use default-shell for command prompt #() and popups as well 2024-05-15 09:59:12 +00:00
nicm
bfd65398a9 Fix memory leaks reported by Lu Ming Yin. 2024-05-15 08:39:30 +00:00
Thomas Adam
452d987e0e Merge branch 'obsd-master' 2024-05-14 14:01:10 +01:00
Thomas Adam
8ef899f315 Merge branch 'obsd-master' 2024-05-14 12:01:09 +01:00
nicm
a18d1146aa Add missing time.h to tty.c (from Ismail Donmez), also remove some stray
spaces.
2024-05-14 10:11:09 +00:00
nicm
5b5004e5ac Revert part of the change for GitHub issue 3675 because it does not work
correctly, it was intended to skip lines that are already being searched
as part of a previous wrapped line but in fact is skipping all lines
except the last in wrapped lines.

Also revert the search-wrapped-lines option (I didn't realize it was
intended to work around this).
2024-05-14 09:32:37 +00:00
Thomas Adam
6ff8f8fbf9 Merge branch 'obsd-master' 2024-05-14 10:01:10 +01:00
nicm
c9616700ca Add a command-error hook when a command fails, from Hugh Davenport in
GitHub issue 3973.
2024-05-14 07:52:19 +00:00
nicm
4c928dce74 Add an option to disable unwrapping lines for searching, from
meanderingprogrammer at gmail dot com, GitHub issue 3975.
2024-05-14 07:40:39 +00:00
nicm
fb37d52dde Restore previous behaviour or writing to stdout if available. 2024-05-14 07:33:01 +00:00
Thomas Adam
363d9c401e Merge branch 'obsd-master' 2024-05-13 14:01:10 +01:00
nicm
8643ece345 Fix memory leak, from Fadi Afani. 2024-05-13 11:45:05 +00:00
Nicholas Marriott
9ba433e521 Use printf not echo -e, from Joyce Lin. 2024-05-13 12:42:14 +01:00
Nicholas Marriott
3823fa2c57 Send SIGHUP since some programs ignore SIGTERM, from Eduardo Grajeda in GitHub
issue 3958.
2024-04-30 12:38:58 +01:00
Thomas Adam
0a8571b6fe Merge branch 'obsd-master' 2024-04-23 16:09:50 +01:00
jsg
ea9f416c99 correct indentation; no functional change
ok tb@
2024-04-23 13:34:51 +00:00
Thomas Adam
036d8993e6 Merge branch 'obsd-master' 2024-04-15 12:01:11 +01:00
nicm
e8530c9fee Fixes for memory leaks reported by Lu Ming Yin, fixes from Howard Chu. 2024-04-15 08:19:55 +00:00
Nicholas Marriott
dd4c0109a9 Missing headers for Android, from Biswapriyo Nath. 2024-04-15 09:07:41 +01:00
Thomas Adam
43530d4397 Merge branch 'obsd-master' 2024-04-10 10:01:13 +01:00
nicm
553d4cba79 Add an option allow-set-title to forbid applications from changing the
pane title, from someone in GitHub issue 3930.
2024-04-10 07:36:25 +00:00
nicm
c62a9ca16b Correct handling of mouse up events (don't ignore all but the last
released button), and always process down event for double click. From
Rudy Dellomas III in GitHub issue 3919.
2024-04-10 07:29:15 +00:00
nicm
424f13fe13 Do not get muddled and crash if focusing a pane that is exiting,
reported by Saul Nogueras in GitHub issue 3776.
2024-04-10 07:15:21 +00:00
Thomas Adam
4bb6da75ba Merge branch 'obsd-master' 2024-04-05 02:01:09 +01:00
nicm
a28175dbfd Pick newest session as documented, not oldest, from Magnus Gross. 2024-04-04 22:44:40 +00:00
Thomas Adam
fc204bb5e5 Merge branch 'obsd-master' 2024-03-26 12:01:11 +00:00
nicm
6207a45139 Fix selection present check, reported by M Kelly. 2024-03-26 10:20:20 +00:00
Thomas Adam
3c3643f580 Merge branch 'obsd-master' 2024-03-21 14:01:10 +00:00
nicm
89c1c43ef9 Write padding character into the right position. 2024-03-21 12:10:57 +00:00
nicm
2e9d7ebf15 Reduce escape-time default to 10 milliseconds, 500 is far too long for
modern terminals and networks. Case made by Kurtis Rader in GitHub issue
3844.
2024-03-21 11:53:11 +00:00
nicm
d8ddeec7db Add -M to always turn mouse on in a menu, GitHub issue 3779. 2024-03-21 11:51:32 +00:00
nicm
6f0254e6a8 Look for feature code 21 for DECSLRM and 28 for DECFRA in the device
attributes and also accept level 1 (there is no hardware with this but
some emulators may use it). Pointed out by James Holderness.
2024-03-21 11:47:55 +00:00
Nicholas Marriott
aa17f0e0c1 Fix crash if SIXEL colour register is invalid and remove SIXEL images before
reflow to avoid a different crash, from Anindya Mukherjee.
2024-03-21 11:37:09 +00:00
nicm
0ae8b681b2 Use -p for default paste-buffer command in buffer mode, it will only do
anything if the application asked for it. From Gregory Anders.
2024-03-21 11:32:49 +00:00
nicm
6c0067c103 Do not notify window-layout-changed if the window is about to be
destroyed (since it may have been freed by the time the notify happens),
from Romain Francoise in GitHub issue 3860.
2024-03-21 11:30:42 +00:00
nicm
5458cb2850 Revert detach-client part of last, did not intend this to go in. 2024-03-21 11:27:18 +00:00
nicm
0c374868ca Do not consider a selection present if it is empty, from Michael Grant
(GitHub issue 3869). Also a typo fix from GitHub issue 3877.
2024-03-21 11:26:28 +00:00
Nicholas Marriott
bf5d3f2e26 Typo, GitHub issue 3877. 2024-03-21 11:19:59 +00:00
Nicholas Marriott
d5ef837f63 Remove duplicate .tmux.conf mention, from Valentin Rylenko. 2024-03-21 11:18:49 +00:00
Thomas Adam
b79e28b2c3 Merge branch 'obsd-master' 2024-03-13 14:01:09 +00:00
nicm
8ffd5458ff Make the attach-session description clearer - do not mention creating a
client which is not important, explicitly say the session must exist,
and mention new-session and new-session -A. Prompted by Theo.
2024-03-13 11:25:50 +00:00
Thomas Adam
b54e1fc4f7 Merge branch 'obsd-master' 2024-03-07 00:01:10 +00:00
Nicholas Marriott
bdb6321229 Update lock.yml. 2024-03-06 21:45:26 +00:00
nicm
bd29a48b56 Check for the right flag to fix split-window -p, from Bryan Childs. 2024-03-06 21:32:39 +00:00
Nicholas Marriott
f3f1c3db58 Add missing headers, from Marvin Schmidt. 2024-03-06 21:29:28 +00:00
Nicholas Marriott
608d113486 next-3.5 2024-02-13 10:20:18 +00:00
165 changed files with 15618 additions and 4947 deletions

View File

@@ -77,3 +77,8 @@ The log files are:
- `tmux-out*.log`: output log file.
Please attach the log files to your issue.
## What does it mean if an issue is closed?
All it means is that work on the issue is not planned for the near future. See
the issue's comments to find out if contributions would be welcome.

1
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1 @@
blank_issues_enabled: false

View File

@@ -1,3 +1,12 @@
---
name: Use this issue template
about: Please read https://github.com/tmux/tmux/blob/master/.github/CONTRIBUTING.md
title: ''
labels: ''
assignees: ''
---
### Issue description
Please read https://github.com/tmux/tmux/blob/master/.github/CONTRIBUTING.md
@@ -14,9 +23,11 @@ built from the latest code in Git.
### Required information
Please provide the following information:
Please provide the following information. These are **required**. Note that bug reports without logs may be ignored or closed without comment.
* tmux version (`tmux -V`).
* Platform (`uname -sp`).
* $TERM inside and outside of tmux (`echo $TERM`).
* Terminal in use (xterm, rxvt, etc).
* $TERM *inside* tmux (`echo $TERM`).
* $TERM *outside* tmux (`echo $TERM`).
* Logs from tmux (`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/imomaliev/tmux-bash-completion
https://github.com/scop/bash-completion/blob/main/completions-core/tmux.bash
For debugging, run tmux with `-v` or `-vv` to generate server and client log
files in the current directory.

View File

@@ -3,27 +3,32 @@ name: 'Lock Threads'
on:
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
permissions:
contents: read
issues: write
pull-requests: write
discussions: write
concurrency:
group: lock-threads
jobs:
lock:
permissions:
issues: write # for dessant/lock-threads to lock issues
pull-requests: write # for dessant/lock-threads to lock PRs
action:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v2
- uses: dessant/lock-threads@v5
with:
github-token: ${{ github.token }}
issue-lock-inactive-days: '30'
pr-lock-inactive-days: '60'
issue-lock-comment: >
issue-inactive-days: '30'
issue-comment: >
This issue has been automatically locked since there
has not been any recent activity after it was closed.
Please open a new issue for related bugs.
pr-lock-comment: >
pr-inactive-days: '60'
pr-comment: >
This pull request has been automatically locked since there
has not been any recent activity after it was closed.
Please open a new issue for related bugs.
discussion-inactive-days: '60'
discussion-comment: >
This discussion has been automatically locked since there
has not been any recent activity after it was closed.

35
.gitignore vendored
View File

@@ -1,23 +1,24 @@
*.o
*~
*.diff
*.patch
*.core
core
tags
*.dSYM
*.diff
*.o
*.patch
*.swp
*~
.deps/
compat/.dirstamp
aclocal.m4
autom4te.cache/
config.log
config.status
etc/
tmux
.dirstamp
Makefile
Makefile.in
configure
tmux.1.*
*.dSYM
aclocal.m4
autom4te.cache/
cmd-parse.c
compat/.dirstamp
config.log
config.status
configure
core
etc/
fuzz/*-fuzzer
.dirstamp
tags
tmux
tmux.1.*

View File

@@ -29,6 +29,7 @@ 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>

334
CHANGES
View File

@@ -1,4 +1,332 @@
CHANGES FROM 3.3a to 3.4
CHANGES FROM 3.6 TO 3.6a
* Fix a buffer overread and an infinite loop in format processing (reported by
Giorgi Kobakhia, issue 4735).
* Allow drag in alternate screen again (issue 4743 reported by Brad King).
* Fix y offset of mouse if status at top (issue 4738 from Michael Grant).
* Add a missing skin tone (from Jake Stewart, issue 4736).
* Allow characters to be combined in either order (issue 4726, reported by Jake
Stewart).
* Fix horizontal mouse resizing when pane status lines are on (from Michael
Grant, issue 4720).
* Fix noattr so it does not delete attributes set in the style itself (issue
4713).
* Newer libevents do not allow event_del on a zero'd event (issue 4706).
* Place cursor on correct line if message-line is not 0 (issue 4707).
* Fix compile error on FreeBSD (from Yasuhiro Kimura, issue 4701).
CHANGES FROM 3.5a TO 3.6
* Add seconds options for clock mode (issue 4697).
* Add a resize callback for menus so that they are correctly moved on resize
(issue 4696).
* Make -v to source-file pass through to subsequent source-file commands (issue
4216).
* If display-popup is used inside a popup, modify that popup (issue 4678).
* Add selection-mode command to expilcitly set the selection mode in copy mode
(issue 3842).
* Save and restore images in alternate screen (issue 3732).
* Ignore Hangul filler character (issue 3998).
* Improve handling of regional indicators and emoji modifiers (issue 3998).
* Preserve marked pane with swap-window and move-window (issue 3443).
* Set and check COLORTERM as a hint for RGB colour.
* If tmux receives a palette request (OSC 4) in a pane and the palette entry
has not been set, send a request to the most recently used client and
forward any response instead (based on change from Tim Culverhouse, issue
4665).
* Add -l flag to command-prompt to disable splitting into multiple prompts
(issue 4483).
* Don't enter copy mode on mouse wheel in alternate screen (issue 3705).
* Add commands to centre the cursor in copy mode (issue 4662).
* Support case insensitive search in modes in the same way as copy mode
(like emacs, so all-lowercase means case insensitive) (issue 4396).
* Fix the logic of the no-detached case for the detach-on-destroy option (from
Martin Louazel, issue 4649).
* Add buffer_full format variable (from Mohammad AlSaleh, issue 4630).
* Introduce a new window option, tiled-layout-max-columns, which configures the
maximum number of columns in the tiled layout.
* Add support for DECRQSS SP q (report cursor style), DECRQM ?12 (report cursor
blink state) and DECRQM ?2004, ?1004, ?1006 (report mouse state) ( rom
Andrea Alberti, issue 4618).
* Fix missing argument from OSC 4 reply (issue 4596).
* Add -k flag to display-popup which allows any key to dismiss the popup once
the command has exited (from Meriel Luna Mittelbach, issue 4612).
* Add nicer default second and third status lines (from Michael Grant, issue
4490).
* Add a pane-border-lines "spaces" value to use spaces for pane borders (issue
4587).
* Replace invalid UTF-8 characters with the placeholder instead of ignoring
them (issue 4514).
* Fix incorrect handling of Korean Hangul Jamo characters (from Roy Jung, issue
4546).
* Allow uppercase letters in gray/grey color names (from Pavel Roskin, issue
4560).
* Add sorting to W, P, L loop operators (from Michael Grant, issue 4516).
* Detect support for OSC 52 using the device attributes report (from James
Holderness, issue 4539).
* Add noattr for styles and use in mode-style to allow whether attributes are
ignored or used to be configured (issue 4498).
* Add a set-default style attribute which replaces the current default colours
and attributes completely.
* Add -E to run-shell to forward stderr as well as stdout (issue 4246).
* Add an option variation-selector-always-wide to instruct tmux not to
always interpret VS16 as a wide character and assume the terminal does
likewise.
* Switch to getopt_long from OpenSSH (from Koichi Murase, issue 4492).
* Add more features for boolean expressions in formats: 1) extend && and || to
support arbitrarily many arguments and 2) add ! and !! for not and not-not
(from David Mandelberg).
* Do not mistake other DCS sequences for SIXEL sequences (from James
Holderness, issue 4488).
* Improve #? conditional expression in formats: add support for else if and
default empty string if no else value (from David Mandelberg, issue 4451).
* Add default-client-command to set the command used if tmux is run without a
command; the default stays new-session (from David Mandelberg, issue
4422).
* Add S-Up and S-Down to move windows in tree mode (from David Mandelberg,
issue 4415).
* Add mode 2031 support to automatically report dark or light theme. tmux will
guess the theme from the background colour on terminals which do not
themselves support the escape sequence (from Jonathan Slenders, issue 4353).
* Add -M flag to capture-pane to use the copy mode screen (issue 4358).
* Align index numbers in trees (from David Mandelberg, issue 4360).
* Add display-message -C flag to update pane while message is displayed (from
Vitaly Ostrosablin, issue 4363).
* Make list-commands command show only one command if an argument is given
(from Ilya Grigoriev, issue 4352).
* Count line numbers correctly inside strings in configuration files (reported
by Pedro Navarro, issue 4325).
* Map bright black (colour 8) to white (7) if the background is black on
terminals with only eight colours so the text is not invisible (from Dmytro
Bagrii, issue 4322).
* New codepoint-widths option allowing users to override the width of
individual Unicode codepoints.
* Add a nesting limit to source-file (from Fadi Afani, issue 4223).
* Add copy-mode-position-style and copy-mode-selection-style options for copy
mode.
* Add no-detach-on-destroy client option (issue 4242).
* Add input-buffer-size option (from Ken Lau).
* Add support for a scrollbar at the side of each pane. New options
pane-scrollbars turn them on or off, pane-scrollbars-position sets the
position (left or right), and pane-scrollbars-style to set the colours (from
Michael Grant, issue 4221).
* Allow control characters to be entered at the command prompt by prefixing
with C-v (from Alexander Arch, issue 4206).
* Do not attempt to search for zero length strings (from Alexander Arch, issue
4209).
* Preserve tabs for copying and capture-pane (from Alexander Arch, issue
4201).
* Increase the maximum for repeat-time.
* Adjust how Ctrl and Meta keys are sent to use standard representation if
available in mode 1 (from Stanislav Kljuhhin, issue 4188).
* Allow attributes in menu style (from Japin Li, issue 4194).
* Add a sixel_support format variable which is 1 if SIXEL is supported, always
0 on OpenBSD (requested by Misaki Masa, issue 4177).
* Add prompt-cursor-colour and prompt-cursor-style to set the style of the
cursor in the command prompt and remove the emulated cursor (from Alexander
Arch, issue 4170).
* Add initial-repeat-time option to allow the first repeat time to be increased
and later reduced (from David le Blanc, issue 4164).
* Send focus events to pane when entering or leaving popup (issue 3991).
* Add copy-mode-position-format to configure the position indicator.
* Add -y flag to disable confirmation prompts in modes (issue 4152).
* Add -C and -P flags to the copy commands in copy mode: -C prevents the
commands from sending the text to the clipboard and -P prevents them from
adding the text as a paste buffer (issue 4153).
* Preserve transparency and raster attribute dimensions when sending a SIXEL
image, and avoid collapsing empty lines (issue 4149).
* Bypass permission check for Cygwin (based on a change by Yuya Adachi via
Rafael Kitover, issue 4148).
* Add MSYSTEM to default update-environment (for Cgywin).
* Set client stdout file descriptor also for Cgywin (from Michael Wild via Rafael
Kitover, issue 4148).
* Use global cursor style and colour options for modes instead of default
(issue 4117).
* Fix pasting so it does not interpret keys but instead copies the input
without interpretation (reported by Mark Kelly).
* Try to query window pixel size from the outside terminal if the values
returned by TIOCGWINSZ are zero (Dmitry Galchinsky, issue 4099).
CHANGES FROM 3.5 TO 3.5a
* Do not translate BSpace as Unicode with extended keys.
* Fix so that keys with Shift are represented correctly with extended keys.
* Revert to using /bin/sh for #() and run-shell and if-shell; the change to use
default-shell only applies now to popups.
* Fix grey colour without a number suffix in styles.
CHANGES FROM 3.4 TO 3.5
* Revamp extended keys support to more closely match xterm and support mode 2
as well as mode 1. This is a substantial change to key handling which changes
tmux to always request mode 2 from parent terminal, changes to an unambiguous
internal representation of keys, and adds an option (extended-keys-format) to
control the format similar to the xterm(1) formatOtherKeys resource.
* Clear an overlay (popup or menu) when command prompt is entered.
* Add copy-mode -d flag to scroll a page down if in copy mode already (matching
-e).
* Display hyperlinks in copy mode and add copy_cursor_hyperlink format to get
the hyperlink under the cursor.
* Add a prefix timeout option.
* Mouse move keys are not useful as key bindings because we do not turn them on
unless the application requests them. Ignore them so they do not cause the
prefix to be canceled
* Add search_count and search_count_partial formats in copy mode.
* Do not reset mouse pane if clicked on status line,
* Add mirrored versions of the main-horizontal and main-vertical layouts where
the main pane is bottom or right instead of top or left.
* Allow REP to work with Unicode characters.
* Fix size calculation of terminators for clipboard escape sequences.
* Treat CRLF as LF in config files where it is easy to do so.
* The Linux console has some bugs with bright colours, so add some workarounds
for it.
* If built with systemd, remove some environment variables it uses.
* Adjust the logic when deleting last buffer to better preserve the selection:
if selecting the element below the deleted one fails (because as the last
one), select the one above it instead.
* Add --enable-jemalloc to build with jemalloc memory allocator (since glibc
malloc is so poor).
* Add a way (refresh-client -r) for control mode clients to provide OSC 10 and
11 responses to tmux so they can set the default foreground and background
colours.
* Add N to search backwards in tree modes.
* Use default-shell for command prompt, #() and popups.
* Revert part of a change intended to improve search performance by skipping
parts of lines already searched, but which in fact skipped the ends of lines
altogether.
* Add a command-error hook when a command fails.
* Add an option allow-set-title to forbid applications from changing the pane
title.
* Correct handling of mouse up events (don't ignore all but the last released
button), and always process down event for double click.
* Fix a crash if focusing a pane that is exiting.
* Pick newest session (as documented) when looking for next session for
detach-on-destroy.
* Reduce default escape-time to 10 milliseconds.
* Add display-menu -M to always turn mouse on in a menu.
* Look for feature code 21 for DECSLRM and 28 for DECFRA in the device
attributes and also accept level 1.
* Fix crash if built with SIXEL and the SIXEL colour register is invalid; also
remove SIXEL images before reflow.
* Do not notify window-layout-changed if the window is about to be destroyed.
* Do not consider a selection present if it is empty for the selection_active
and selection_present format variables.
* Fix split-window -p.
CHANGES FROM 3.3a TO 3.4
* Add options keep-last and keep-group to destroy-unattached to keep the last
session whether in a group.
@@ -84,7 +412,7 @@ CHANGES FROM 3.3a to 3.4
* Add message-line option to control where message and prompt go.
* Notification when a when a paste buffer is deleted.
* Notification when a paste buffer is deleted.
* Add a Nobr terminfo(5) capability to tell tmux the terminal does not use bright
colours for bold.
@@ -300,7 +628,7 @@ CHANGES FROM 3.2 TO 3.2a
* Do not expand the filename given to -f so it can contain colons.
* Fixes for problems with extended keys and modifiers, scroll region,
source-file, crosscompiling, format modifiers and other minor issues.
source-file, cross compiling, format modifiers and other minor issues.
CHANGES FROM 3.1c TO 3.2

View File

@@ -20,17 +20,23 @@ LDADD = $(LIBOBJS)
# Set flags for gcc.
if IS_GCC
AM_CFLAGS += -std=gnu99 -O2
AM_CFLAGS += -std=gnu99
if IS_OPTIMIZED
AM_CFLAGS += -O2
else
AM_CFLAGS += -O0
endif
if IS_DEBUG
AM_CFLAGS += -g
AM_CFLAGS += -Wno-long-long -Wall -W -Wformat=2
AM_CFLAGS += -Wno-long-long -Wall -W -Wformat=2 -Wno-use-after-free
AM_CFLAGS += -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations
AM_CFLAGS += -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare
AM_CFLAGS += -Wundef -Wbad-function-cast -Winline -Wcast-align
AM_CFLAGS += -Wundef -Wbad-function-cast -Winline -Wno-cast-align
AM_CFLAGS += -Wdeclaration-after-statement -Wno-pointer-sign -Wno-attributes
AM_CFLAGS += -Wno-unused-result -Wno-format-y2k
AM_CFLAGS += -Wno-unused-result -Wno-format-y2k -Wno-unknown-warning-option
AM_CFLAGS += -Wno-maybe-uninitialized
if IS_DARWIN
AM_CFLAGS += -Wno-deprecated-declarations -Wno-cast-align
AM_CFLAGS += -Wno-deprecated-declarations -Wno-macro-redefined
endif
AM_CPPFLAGS += -DDEBUG
endif
@@ -66,6 +72,11 @@ if IS_HAIKU
AM_CPPFLAGS += -D_BSD_SOURCE
endif
# Set flags for Cygwin.
if IS_CYGWIN
AM_CPPFLAGS += -DTMUX_SOCK_PERM=0
endif
# List of sources.
dist_tmux_SOURCES = \
alerts.c \
@@ -95,6 +106,7 @@ 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 \
@@ -178,6 +190,7 @@ dist_tmux_SOURCES = \
server-fn.c \
server.c \
session.c \
sort.c \
spawn.c \
status.c \
style.c \
@@ -185,6 +198,7 @@ dist_tmux_SOURCES = \
tmux.h \
tmux-protocol.h \
tty-acs.c \
tty-draw.c \
tty-features.c \
tty-keys.c \
tty-term.c \
@@ -218,8 +232,18 @@ nodist_tmux_SOURCES += compat/utf8proc.c
endif
# Enable sixel support.
if ENABLE_SIXEL
if ENABLE_SIXEL_IMAGES
dist_tmux_SOURCES += image.c image-sixel.c
else
# If not sixel, still need image.c for kitty.
if ENABLE_KITTY_IMAGES
dist_tmux_SOURCES += image.c
endif
endif
# Enable kitty graphics protocol support.
if ENABLE_KITTY_IMAGES
dist_tmux_SOURCES += image-kitty.c
endif
if NEED_FUZZING

3
README
View File

@@ -36,6 +36,7 @@ autoconf, automake and pkg-config:
$ cd tmux
$ sh autogen.sh
$ ./configure && make
$ sudo make install
* Contributing
@@ -65,7 +66,7 @@ Also see the tmux FAQ at:
A bash(1) completion file is at:
https://github.com/imomaliev/tmux-bash-completion
https://github.com/scop/bash-completion/blob/main/completions/tmux
For debugging, run tmux with -v and -vv to generate server and client log files
in the current directory.

View File

@@ -38,7 +38,7 @@ tmuxのドキュメントについてはtmux.1マニュアルをご覧くださ
サンプル設定は本リポジトリのexample_tmux.confに
また、bash-completionファイルは下記にあります。
https://github.com/imomaliev/tmux-bash-completion
https://github.com/scop/bash-completion/blob/main/completions/tmux
「-v」や「-vv」を指定することでデバッグモードでの起動が可能です。カレントディレクトリにサーバーやクライアントのログファイルが生成されます。

32
SYNCING
View File

@@ -1,17 +1,17 @@
Preamble
========
Tmux portable relies on repositories "tmux" and "tmux-openbsd".
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-openbsd" is the version of tmux in OpenBSD base system which provides
* "tmux-obsd" is the version of tmux in OpenBSD base system which provides
the basis of the portable tmux version.
Note: The "tmux-openbsd" repository is actually handled by "git cvsimport"
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
@@ -34,11 +34,11 @@ this information has ever been set before.
Cloning repositories
====================
This involves having both tmux and tmux-openbsd cloned, as in:
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-openbsd.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
@@ -47,30 +47,30 @@ these checkouts existing, skip that.
Adding in git-remotes
=====================
Because the portable "tmux" git repository and the "tmux-openbsd"
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-openbsd" repositories for syncing. To do this, we must reference the
clone of the "tmux-openbsd" repository from the "tmux" repository, as
"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-openbsd
% 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-openbsd" repository, but from the context of the
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-openbsd" can be found from within
"tmux", we have to ensure the "master" branch from "tmux-openbsd" is
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-openbsd
% cd /path/to/tmux-obsd
% git checkout master
% git pull
@@ -82,9 +82,9 @@ Then back in "tmux":
Creating the necessary branches
===============================
Now that "tmux" can see commits and branches from "tmux-openbsd" by way
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-openbsd" in the "tmux" repository:
"tmux-obsd" in the "tmux" repository:
% git checkout -b obsd-master obsd-tmux/master
@@ -92,7 +92,7 @@ Adding in the fake history points
=================================
To tie both the "master" branch from "tmux" and the "obsd-master"
branch from "tmux-openbsd" together, the fake history points added to the
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:

View File

@@ -315,11 +315,11 @@ alerts_set_message(struct winlink *wl, const char *type, const char *option)
if (visual == VISUAL_OFF)
continue;
if (c->session->curw == wl) {
status_message_set(c, -1, 1, 0, "%s in current window",
type);
status_message_set(c, -1, 1, 0, 0,
"%s in current window", type);
} else {
status_message_set(c, -1, 1, 0, "%s in window %d", type,
wl->idx);
status_message_set(c, -1, 1, 0, 0,
"%s in window %d", type, wl->idx);
}
}
}

View File

@@ -164,10 +164,14 @@ args_parse_flag_argument(struct args_value *values, u_int count, char **cause,
argument = &values[*i];
if (argument->type != ARGS_STRING) {
xasprintf(cause, "-%c argument must be a string", flag);
args_free_value(new);
free(new);
return (-1);
}
}
if (argument == NULL) {
args_free_value(new);
free(new);
if (optional_argument) {
log_debug("%s: -%c (optional)", __func__, flag);
args_set(args, flag, NULL, ARGS_ENTRY_OPTIONAL_VALUE);
@@ -662,6 +666,8 @@ args_set(struct args *args, u_char flag, struct args_value *value, int flags)
entry->count++;
if (value != NULL && value->type != ARGS_NONE)
TAILQ_INSERT_TAIL(&entry->values, value, entry);
else
free(value);
}
/* Get argument value. Will be NULL if it isn't present. */

View File

@@ -31,7 +31,7 @@ attributes_tostring(int attr)
if (attr == 0)
return ("none");
len = xsnprintf(buf, sizeof buf, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
len = xsnprintf(buf, sizeof buf, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
(attr & GRID_ATTR_CHARSET) ? "acs," : "",
(attr & GRID_ATTR_BRIGHT) ? "bright," : "",
(attr & GRID_ATTR_DIM) ? "dim," : "",
@@ -45,7 +45,8 @@ attributes_tostring(int attr)
(attr & GRID_ATTR_UNDERSCORE_3) ? "curly-underscore," : "",
(attr & GRID_ATTR_UNDERSCORE_4) ? "dotted-underscore," : "",
(attr & GRID_ATTR_UNDERSCORE_5) ? "dashed-underscore," : "",
(attr & GRID_ATTR_OVERLINE) ? "overline," : "");
(attr & GRID_ATTR_OVERLINE) ? "overline," : "",
(attr & GRID_ATTR_NOATTR) ? "noattr," : "");
if (len > 0)
buf[len - 1] = '\0';

8
cfg.c
View File

@@ -221,10 +221,14 @@ cfg_add_cause(const char *fmt, ...)
void
cfg_print_causes(struct cmdq_item *item)
{
u_int i;
struct client *c = cmdq_get_client(item);
u_int i;
for (i = 0; i < cfg_ncauses; i++) {
cmdq_print(item, "%s", cfg_causes[i]);
if (c != NULL && (c->flags & CLIENT_CONTROL))
control_write(c, "%%config-error %s", cfg_causes[i]);
else
cmdq_print(item, "%s", cfg_causes[i]);
free(cfg_causes[i]);
}

View File

@@ -452,11 +452,12 @@ client_send_identify(const char *ttynam, const char *termname, char **caps,
{
char **ss;
size_t sslen;
int fd, flags = client_flags;
int fd;
uint64_t flags = client_flags;
pid_t pid;
u_int i;
proc_send(client_peer, MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags);
proc_send(client_peer, MSG_IDENTIFY_LONGFLAGS, -1, &flags, sizeof flags);
proc_send(client_peer, MSG_IDENTIFY_LONGFLAGS, -1, &client_flags,
sizeof client_flags);
@@ -497,20 +498,10 @@ client_send_identify(const char *ttynam, const char *termname, char **caps,
static __dead void
client_exec(const char *shell, const char *shellcmd)
{
const char *name, *ptr;
char *argv0;
char *argv0;
log_debug("shell %s, command %s", shell, shellcmd);
ptr = strrchr(shell, '/');
if (ptr != NULL && *(ptr + 1) != '\0')
name = ptr + 1;
else
name = shell;
if (client_flags & CLIENT_LOGIN)
xasprintf(&argv0, "-%s", name);
else
xasprintf(&argv0, "%s", name);
argv0 = shell_argv0(shell, !!(client_flags & CLIENT_LOGIN));
setenv("SHELL", shell, 1);
proc_clear_signals(client_proc, 1);

View File

@@ -38,7 +38,7 @@ const struct cmd_entry cmd_bind_key_entry = {
.args = { "nrN:T:", 1, -1, cmd_bind_key_args_parse },
.usage = "[-nr] [-T key-table] [-N note] key "
"[command [arguments]]",
"[command [argument ...]]",
.flags = CMD_AFTERHOOK,
.exec = cmd_bind_key_exec

View File

@@ -85,7 +85,10 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
options_set_number(w->options, "automatic-rename", 0);
}
server_unlink_window(src_s, wl);
return (CMD_RETURN_NORMAL);
wl = winlink_find_by_window(&dst_s->windows, w);
if (wl == NULL)
return (CMD_RETURN_ERROR);
goto out;
}
if (idx != -1 && winlink_find_by_index(&dst_s->windows, idx) != NULL) {
cmdq_error(item, "index in use: %d", idx);
@@ -99,7 +102,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
w = wp->window = window_create(w->sx, w->sy, w->xpixel, w->ypixel);
options_set_parent(wp->options, w->options);
wp->flags |= PANE_STYLECHANGED;
wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
TAILQ_INSERT_HEAD(&w->panes, wp, entry);
w->active = wp;
w->latest = tc;
@@ -132,6 +135,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
if (src_s != dst_s)
server_status_session_group(dst_s);
out:
if (args_has(args, 'P')) {
if ((template = args_get(args, 'F')) == NULL)
template = BREAK_PANE_TEMPLATE;

View File

@@ -39,8 +39,8 @@ const struct cmd_entry cmd_capture_pane_entry = {
.name = "capture-pane",
.alias = "capturep",
.args = { "ab:CeE:JNpPqS:Tt:", 0, 0, NULL },
.usage = "[-aCeJNpPqT] " 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 },
@@ -107,14 +107,16 @@ static char *
cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
struct window_pane *wp, size_t *len)
{
struct grid *gd;
const struct grid_line *gl;
struct grid_cell *gc = NULL;
int n, join_lines, flags = 0;
u_int i, sx, top, bottom, tmp;
char *cause, *buf, *line;
const char *Sflag, *Eflag;
size_t linelen;
struct grid *gd;
const struct grid_line *gl;
struct screen *s;
struct grid_cell *gc = NULL;
struct window_mode_entry *wme;
int n, join_lines, flags = 0;
u_int i, sx, top, bottom, tmp;
char *cause, *buf, *line;
const char *Sflag, *Eflag;
size_t linelen;
sx = screen_size_x(&wp->base);
if (args_has(args, 'a')) {
@@ -126,8 +128,20 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
}
return (xstrdup(""));
}
} else
s = &wp->base;
} else if (args_has(args, 'M')) {
wme = TAILQ_FIRST(&wp->modes);
if (wme != NULL && wme->mode->get_screen != NULL) {
s = wme->mode->get_screen (wme);
gd = s->grid;
} else {
s = &wp->base;
gd = wp->base.grid;
}
} else {
s = &wp->base;
gd = wp->base.grid;
}
Sflag = args_get(args, 'S');
if (Sflag != NULL && strcmp(Sflag, "-") == 0)
@@ -181,7 +195,7 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
buf = NULL;
for (i = top; i <= bottom; i++) {
line = grid_string_cells(gd, 0, i, sx, &gc, flags, wp->screen);
line = grid_string_cells(gd, 0, i, sx, &gc, flags, s);
linelen = strlen(line);
buf = cmd_capture_pane_append(buf, len, line, linelen);
@@ -234,8 +248,8 @@ cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
}
file_print_buffer(c, buf, len);
file_print(c, "\n");
free(buf);
}
free(buf);
} else {
bufname = NULL;
if (args_has(args, 'b'))

View File

@@ -33,7 +33,7 @@ const struct cmd_entry cmd_choose_tree_entry = {
.name = "choose-tree",
.alias = NULL,
.args = { "F:f:GK:NO:rst:wZ", 0, 1, cmd_choose_tree_args_parse },
.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]",
@@ -47,7 +47,7 @@ const struct cmd_entry cmd_choose_client_entry = {
.name = "choose-client",
.alias = NULL,
.args = { "F:f:K:NO:rt:Z", 0, 1, cmd_choose_tree_args_parse },
.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]",
@@ -61,7 +61,7 @@ const struct cmd_entry cmd_choose_buffer_entry = {
.name = "choose-buffer",
.alias = NULL,
.args = { "F:f:K:NO:rt:Z", 0, 1, cmd_choose_tree_args_parse },
.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]",
@@ -75,7 +75,7 @@ const struct cmd_entry cmd_customize_mode_entry = {
.name = "customize-mode",
.alias = NULL,
.args = { "F:f:Nt:Z", 0, 0, NULL },
.args = { "F:f:Nt:yZ", 0, 0, NULL },
.usage = "[-NZ] [-F format] [-f filter] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 },
@@ -98,6 +98,13 @@ 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())

View File

@@ -42,9 +42,9 @@ const struct cmd_entry cmd_command_prompt_entry = {
.name = "command-prompt",
.alias = NULL,
.args = { "1bFkiI:Np:t:T:", 0, 1, cmd_command_prompt_args_parse },
.usage = "[-1bFkiN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE
" [-T type] [template]",
.args = { "1beFiklI:Np:t:T:", 0, 1, cmd_command_prompt_args_parse },
.usage = "[-1beFiklN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE
" [-T prompt-type] [template]",
.flags = CMD_CLIENT_TFLAG,
.exec = cmd_command_prompt_exec
@@ -84,7 +84,7 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
struct client *tc = cmdq_get_target_client(item);
struct cmd_find_state *target = cmdq_get_target(item);
const char *type, *s, *input;
struct cmd_command_prompt_cdata *cdata;
struct cmd_command_prompt_cdata *cdata;
char *tmp, *prompts, *prompt, *next_prompt;
char *inputs = NULL, *next_input;
u_int count = args_count(args);
@@ -117,32 +117,39 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
next_input = inputs = xstrdup(s);
else
next_input = NULL;
while ((prompt = strsep(&next_prompt, ",")) != NULL) {
cdata->prompts = xreallocarray(cdata->prompts, cdata->count + 1,
sizeof *cdata->prompts);
if (!space)
tmp = xstrdup(prompt);
else
xasprintf(&tmp, "%s ", prompt);
cdata->prompts[cdata->count].prompt = tmp;
if (args_has(args, 'l')) {
cdata->prompts = xcalloc(1, sizeof *cdata->prompts);
cdata->prompts[0].prompt = prompts;
cdata->prompts[0].input = inputs;
cdata->count = 1;
} else {
while ((prompt = strsep(&next_prompt, ",")) != NULL) {
cdata->prompts = xreallocarray(cdata->prompts,
cdata->count + 1, sizeof *cdata->prompts);
if (!space)
tmp = xstrdup(prompt);
else
xasprintf(&tmp, "%s ", prompt);
cdata->prompts[cdata->count].prompt = tmp;
if (next_input != NULL) {
input = strsep(&next_input, ",");
if (input == NULL)
if (next_input != NULL) {
input = strsep(&next_input, ",");
if (input == NULL)
input = "";
} else
input = "";
} else
input = "";
cdata->prompts[cdata->count].input = xstrdup(input);
cdata->count++;
cdata->prompts[cdata->count].input = xstrdup(input);
cdata->count++;
}
free(inputs);
free(prompts);
}
free(inputs);
free(prompts);
if ((type = args_get(args, 'T')) != NULL) {
cdata->prompt_type = status_prompt_type(type);
if (cdata->prompt_type == PROMPT_TYPE_INVALID) {
cmdq_error(item, "unknown type: %s", type);
cmd_command_prompt_free(cdata);
return (CMD_RETURN_ERROR);
}
} else
@@ -156,6 +163,8 @@ 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;
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);
@@ -227,7 +236,7 @@ out:
static void
cmd_command_prompt_free(void *data)
{
struct cmd_command_prompt_cdata *cdata = data;
struct cmd_command_prompt_cdata *cdata = data;
u_int i;
for (i = 0; i < cdata->count; i++) {

View File

@@ -42,7 +42,7 @@ const struct cmd_entry cmd_confirm_before_entry = {
.alias = "confirm",
.args = { "bc:p:t:y", 1, 1, cmd_confirm_before_args_parse },
.usage = "[-by] [-c confirm_key] [-p prompt] " CMD_TARGET_CLIENT_USAGE
.usage = "[-by] [-c confirm-key] [-p prompt] " CMD_TARGET_CLIENT_USAGE
" command",
.flags = CMD_CLIENT_TFLAG,
@@ -76,8 +76,10 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item)
cdata = xcalloc(1, sizeof *cdata);
cdata->cmdlist = args_make_commands_now(self, item, 0, 1);
if (cdata->cmdlist == NULL)
if (cdata->cmdlist == NULL) {
free(cdata);
return (CMD_RETURN_ERROR);
}
if (wait)
cdata->item = item;
@@ -90,6 +92,8 @@ 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_list_free(cdata->cmdlist);
free(cdata);
return (CMD_RETURN_ERROR);
}
}
@@ -100,8 +104,8 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item)
xasprintf(&new_prompt, "%s ", prompt);
else {
cmd = cmd_get_entry(cmd_list_first(cdata->cmdlist))->name;
xasprintf(&new_prompt, "Confirm '%s'? (%c/n) ",
cmd, cdata->confirm_key);
xasprintf(&new_prompt, "Confirm '%s'? (%c/n) ", cmd,
cdata->confirm_key);
}
status_prompt_set(tc, target, new_prompt, NULL,
@@ -127,7 +131,7 @@ cmd_confirm_before_callback(struct client *c, void *data, const char *s,
if (s == NULL)
goto out;
if (s[0] != cdata->confirm_key && (s[0] != '\0' || !cdata->default_yes))
if (s[0] != cdata->confirm_key && (s[0] != '\r' || !cdata->default_yes))
goto out;
retcode = 0;

View File

@@ -30,13 +30,13 @@ const struct cmd_entry cmd_copy_mode_entry = {
.name = "copy-mode",
.alias = NULL,
.args = { "eHMs:t:uq", 0, 0, NULL },
.usage = "[-eHMuq] [-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,
.flags = CMD_AFTERHOOK|CMD_READONLY,
.exec = cmd_copy_mode_exec
};
@@ -91,6 +91,13 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
}
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')) {
window_copy_scroll(wp, c->tty.mouse_slider_mpos, event->m.y,
args_has(args, 'e'));
return (CMD_RETURN_NORMAL);
}
return (CMD_RETURN_NORMAL);
}

View File

@@ -38,11 +38,11 @@ const struct cmd_entry cmd_display_menu_entry = {
.name = "display-menu",
.alias = "menu",
.args = { "b:c:C:H:s:S:Ot:T:x:y:", 1, -1, cmd_display_menu_args_parse },
.usage = "[-O] [-b border-lines] [-c target-client] "
.args = { "b:c:C:H:s:S:MOt:T:x:y:", 1, -1, cmd_display_menu_args_parse },
.usage = "[-MO] [-b border-lines] [-c target-client] "
"[-C starting-choice] [-H selected-style] [-s style] "
"[-S border-style] " CMD_TARGET_PANE_USAGE "[-T title] "
"[-x position] [-y position] name key command ...",
"[-S border-style] " CMD_TARGET_PANE_USAGE " [-T title] "
"[-x position] [-y position] name [key] [command] ...",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -54,12 +54,12 @@ const struct cmd_entry cmd_display_popup_entry = {
.name = "display-popup",
.alias = "popup",
.args = { "Bb:Cc:d:e:Eh:s:S:t:T:w:x:y:", 0, -1, NULL },
.usage = "[-BCE] [-b border-lines] [-c target-client] "
.args = { "Bb:Cc:d:e:Eh:kNs:S:t:T:w:x:y:", 0, -1, NULL },
.usage = "[-BCEkN] [-b border-lines] [-c target-client] "
"[-d start-directory] [-e environment] [-h height] "
"[-s style] [-S border-style] " CMD_TARGET_PANE_USAGE
"[-T title] [-w width] [-x position] [-y position] "
"[shell-command]",
" [-T title] [-w width] [-x position] [-y position] "
"[shell-command [argument ...]]",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -92,7 +92,7 @@ cmd_display_menu_args_parse(struct args *args, u_int idx, __unused char **cause)
}
static int
cmd_display_menu_get_position(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;
@@ -294,7 +294,7 @@ 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;
char *title, *cause = NULL;
int flags = 0, starting_choice = 0;
u_int px, py, i, count = args_count(args);
struct options *o = target->s->curw->window->options;
@@ -312,8 +312,7 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
&cause);
if (cause != NULL) {
cmdq_error(item, "starting choice %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
goto fail;
}
}
}
@@ -334,8 +333,7 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
if (count - i < 2) {
cmdq_error(item, "not enough arguments");
menu_free(menu);
return (CMD_RETURN_ERROR);
goto fail;
}
key = args_string(args, i++);
@@ -347,17 +345,13 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
}
if (menu == NULL) {
cmdq_error(item, "invalid menu arguments");
return (CMD_RETURN_ERROR);
}
if (menu->count == 0) {
menu_free(menu);
return (CMD_RETURN_NORMAL);
}
if (!cmd_display_menu_get_position(tc, item, args, &px, &py,
menu->width + 4, menu->count + 2)) {
menu_free(menu);
return (CMD_RETURN_NORMAL);
goto fail;
}
if (menu->count == 0)
goto out;
if (!cmd_display_menu_get_pos(tc, item, args, &px, &py, menu->width + 4,
menu->count + 2))
goto out;
value = args_get(args, 'b');
if (value != NULL) {
@@ -366,19 +360,27 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
&cause);
if (lines == -1) {
cmdq_error(item, "menu-border-lines %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
goto fail;
}
}
if (args_has(args, 'O'))
flags |= MENU_STAYOPEN;
if (!event->m.valid)
if (!event->m.valid && !args_has(args, 'M'))
flags |= MENU_NOMOUSE;
if (menu_display(menu, flags, starting_choice, item, px, py, tc, lines,
style, selected_style, border_style, target, NULL, NULL) != 0)
return (CMD_RETURN_NORMAL);
return (CMD_RETURN_WAIT);
out:
menu_free(menu);
return (CMD_RETURN_NORMAL);
fail:
free(cause);
menu_free(menu);
return (CMD_RETURN_ERROR);
}
static enum cmd_retval
@@ -392,8 +394,10 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
const char *value, *shell, *shellcmd = NULL;
const char *style = args_get(args, 's');
const char *border_style = args_get(args, 'S');
char *cwd, *cause = NULL, **argv = NULL, *title;
int flags = 0, argc = 0;
char *cwd = NULL, *cause = NULL, **argv = NULL;
char *title = NULL;
int modify = popup_present(tc);
int flags = -1, argc = 0;
enum box_lines lines = BOX_LINES_DEFAULT;
u_int px, py, w, h, count = args_count(args);
struct args_value *av;
@@ -405,36 +409,66 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
server_client_clear_overlay(tc);
return (CMD_RETURN_NORMAL);
}
if (tc->overlay_draw != NULL)
if (!modify && tc->overlay_draw != NULL)
return (CMD_RETURN_NORMAL);
h = tty->sy / 2;
if (args_has(args, 'h')) {
h = args_percentage(args, 'h', 1, tty->sy, tty->sy, &cause);
if (cause != NULL) {
cmdq_error(item, "height %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
if (!modify) {
h = tty->sy / 2;
if (args_has(args, 'h')) {
h = args_percentage(args, 'h', 1, tty->sy, tty->sy,
&cause);
if (cause != NULL) {
cmdq_error(item, "height %s", cause);
goto fail;
}
}
w = tty->sx / 2;
if (args_has(args, 'w')) {
w = args_percentage(args, 'w', 1, tty->sx, tty->sx,
&cause);
if (cause != NULL) {
cmdq_error(item, "width %s", cause);
goto fail;
}
}
if (w > tty->sx)
w = tty->sx;
if (h > tty->sy)
h = tty->sy;
if (!cmd_display_menu_get_pos(tc, item, args, &px, &py, w, h))
goto out;
value = args_get(args, 'd');
if (value != NULL)
cwd = format_single_from_target(item, value);
else
cwd = xstrdup(server_client_get_cwd(tc, s));
if (count == 0) {
shellcmd = options_get_string(s->options,
"default-command");
} else if (count == 1)
shellcmd = args_string(args, 0);
if (count <= 1 && (shellcmd == NULL || *shellcmd == '\0')) {
shellcmd = NULL;
shell = options_get_string(s->options, "default-shell");
if (!checkshell(shell))
shell = _PATH_BSHELL;
cmd_append_argv(&argc, &argv, shell);
} else
args_to_vector(args, &argc, &argv);
if (args_has(args, 'e') >= 1) {
env = environ_create();
av = args_first_value(args, 'e');
while (av != NULL) {
environ_put(env, av->string, 0);
av = args_next_value(av);
}
}
}
w = tty->sx / 2;
if (args_has(args, 'w')) {
w = args_percentage(args, 'w', 1, tty->sx, tty->sx, &cause);
if (cause != NULL) {
cmdq_error(item, "width %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
}
if (w > tty->sx)
w = tty->sx;
if (h > tty->sy)
h = tty->sy;
if (!cmd_display_menu_get_position(tc, item, args, &px, &py, w, h))
return (CMD_RETURN_NORMAL);
value = args_get(args, 'b');
if (args_has(args, 'B'))
lines = BOX_LINES_NONE;
@@ -444,35 +478,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
&cause);
if (cause != NULL) {
cmdq_error(item, "popup-border-lines %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
}
value = args_get(args, 'd');
if (value != NULL)
cwd = format_single_from_target(item, value);
else
cwd = xstrdup(server_client_get_cwd(tc, s));
if (count == 0)
shellcmd = options_get_string(s->options, "default-command");
else if (count == 1)
shellcmd = args_string(args, 0);
if (count <= 1 && (shellcmd == NULL || *shellcmd == '\0')) {
shellcmd = NULL;
shell = options_get_string(s->options, "default-shell");
if (!checkshell(shell))
shell = _PATH_BSHELL;
cmd_append_argv(&argc, &argv, shell);
} else
args_to_vector(args, &argc, &argv);
if (args_has(args, 'e') >= 1) {
env = environ_create();
av = args_first_value(args, 'e');
while (av != NULL) {
environ_put(env, av->string, 0);
av = args_next_value(av);
goto fail;
}
}
@@ -480,23 +486,49 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
title = format_single_from_target(item, args_get(args, 'T'));
else
title = xstrdup("");
if (args_has(args, 'E') > 1)
if (args_has(args, 'N') || !modify)
flags = 0;
if (args_has(args, 'E') > 1) {
if (flags == -1)
flags = 0;
flags |= POPUP_CLOSEEXITZERO;
else if (args_has(args, 'E'))
} else if (args_has(args, 'E')) {
if (flags == -1)
flags = 0;
flags |= POPUP_CLOSEEXIT;
if (popup_display(flags, lines, item, px, py, w, h, env, shellcmd, argc,
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);
if (args_has(args, 'k')) {
if (flags == -1)
flags = 0;
flags |= POPUP_CLOSEANYKEY;
}
if (modify) {
popup_modify(tc, title, style, border_style, lines, flags);
goto out;
}
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;
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

@@ -39,8 +39,8 @@ const struct cmd_entry cmd_display_message_entry = {
.name = "display-message",
.alias = "display",
.args = { "ac:d:lINpt:F:v", 0, 1, NULL },
.usage = "[-aIlNpv] [-c target-client] [-d delay] [-F format] "
.args = { "aCc:d:lINpt:F:v", 0, 1, NULL },
.usage = "[-aCIlNpv] [-c target-client] [-d delay] [-F format] "
CMD_TARGET_PANE_USAGE " [message]",
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
@@ -69,6 +69,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
const char *template;
char *msg, *cause;
int delay = -1, flags, Nflag = args_has(args, 'N');
int Cflag = args_has(args, 'C');
struct format_tree *ft;
u_int count = args_count(args);
struct evbuffer *evb;
@@ -130,6 +131,7 @@ 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);
}
@@ -150,10 +152,9 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
server_client_print(tc, 0, evb);
evbuffer_free(evb);
} else if (tc != NULL)
status_message_set(tc, delay, 0, Nflag, "%s", msg);
status_message_set(tc, delay, 0, Nflag, Cflag, "%s", msg);
free(msg);
format_free(ft);
return (CMD_RETURN_NORMAL);
}

View File

@@ -246,7 +246,7 @@ cmd_display_panes_key(struct client *c, void *data, struct key_event *event)
wp = window_pane_at_index(w, index);
if (wp == NULL)
return (1);
window_unzoom(w);
window_unzoom(w, 1);
xasprintf(&expanded, "%%%u", wp->id);

View File

@@ -924,6 +924,7 @@ 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;
@@ -990,6 +991,20 @@ 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) {
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

@@ -124,7 +124,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
-1) == NULL) {
cmdq_error(item, "failed to run command: %s", shellcmd);
free(shellcmd);
free(cdata);
cmd_if_shell_free(cdata);
return (CMD_RETURN_ERROR);
}
free(shellcmd);
@@ -157,7 +157,7 @@ cmd_if_shell_callback(struct job *job)
if (cmdlist == NULL) {
if (cdata->item == NULL) {
*error = toupper((u_char)*error);
status_message_set(c, -1, 1, 0, "%s", error);
status_message_set(c, -1, 1, 0, 0, "%s", error);
} else
cmdq_error(cdata->item, "%s", error);
free(error);

View File

@@ -149,7 +149,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
src_wp->window = dst_w;
options_set_parent(src_wp->options, dst_w->options);
src_wp->flags |= PANE_STYLECHANGED;
src_wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
if (flags & SPAWN_BEFORE)
TAILQ_INSERT_BEFORE(dst_wp, src_wp, entry);
else

View File

@@ -36,8 +36,8 @@ const struct cmd_entry cmd_list_buffers_entry = {
.name = "list-buffers",
.alias = "lsb",
.args = { "F:f:", 0, 0, NULL },
.usage = "[-F format] [-f filter]",
.args = { "F:f:O:r", 0, 0, NULL },
.usage = "[-F format] [-f filter] [-O order]",
.flags = CMD_AFTERHOOK,
.exec = cmd_list_buffers_exec
@@ -46,21 +46,30 @@ const struct cmd_entry cmd_list_buffers_entry = {
static enum cmd_retval
cmd_list_buffers_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct paste_buffer *pb;
struct format_tree *ft;
const char *template, *filter;
char *line, *expanded;
int flag;
struct args *args = cmd_get_args(self);
struct paste_buffer **l;
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');
pb = NULL;
while ((pb = paste_walk(pb)) != NULL) {
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++) {
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_defaults_paste_buffer(ft, pb);
format_defaults_paste_buffer(ft, l[i]);
if (filter != NULL) {
expanded = format_expand(ft, filter);

View File

@@ -41,8 +41,8 @@ const struct cmd_entry cmd_list_clients_entry = {
.name = "list-clients",
.alias = "lsc",
.args = { "F:f:t:", 0, 0, NULL },
.usage = "[-F format] [-f filter] " CMD_TARGET_SESSION_USAGE,
.args = { "F:f:O:rt:", 0, 0, NULL },
.usage = "[-F format] [-f filter] [-O order]" CMD_TARGET_SESSION_USAGE,
.target = { 't', CMD_FIND_SESSION, 0 },
@@ -53,15 +53,16 @@ const struct cmd_entry cmd_list_clients_entry = {
static enum cmd_retval
cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct client *c;
struct session *s;
struct format_tree *ft;
const char *template, *filter;
u_int idx;
char *line, *expanded;
int flag;
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct client **l;
struct session *s;
struct format_tree *ft;
const char *template, *filter;
u_int i, n;
char *line, *expanded;
int flag;
struct sort_criteria sort_crit;
if (args_has(args, 't'))
s = target->s;
@@ -72,14 +73,21 @@ cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item)
template = LIST_CLIENTS_TEMPLATE;
filter = args_get(args, 'f');
idx = 0;
TAILQ_FOREACH(c, &clients, entry) {
if (c->session == NULL || (s != NULL && s != c->session))
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))
continue;
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_add(ft, "line", "%u", idx);
format_defaults(ft, c, NULL, NULL, NULL);
format_add(ft, "line", "%u", i);
format_defaults(ft, l[i], NULL, NULL, NULL);
if (filter != NULL) {
expanded = format_expand(ft, filter);
@@ -94,8 +102,6 @@ cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item)
}
format_free(ft);
idx++;
}
return (CMD_RETURN_NORMAL);

107
cmd-list-commands.c Normal file
View File

@@ -0,0 +1,107 @@
/* $OpenBSD$ */
/*
* 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

@@ -27,121 +27,140 @@
* List key bindings.
*/
static enum cmd_retval cmd_list_keys_exec(struct cmd *, struct cmdq_item *);
#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}:key_string} " \
"#{key_command}}"
static enum cmd_retval cmd_list_keys_commands(struct cmd *,
struct cmdq_item *);
static enum cmd_retval cmd_list_keys_exec(struct cmd *, struct cmdq_item *);
const struct cmd_entry cmd_list_keys_entry = {
.name = "list-keys",
.alias = "lsk",
.args = { "1aNP:T:", 0, 1, NULL },
.usage = "[-1aN] [-P prefix-string] [-T key-table] [key]",
.args = { "1aF:NO:P:rT:", 0, 1, NULL },
.usage = "[-1aNr] [-F format] [-O order] [-P prefix-string]"
"[-T key-table] [key]",
.flags = CMD_STARTSERVER|CMD_AFTERHOOK,
.exec = cmd_list_keys_exec
};
const struct cmd_entry cmd_list_commands_entry = {
.name = "list-commands",
.alias = "lscm",
static char *
cmd_list_keys_get_prefix(struct args *args)
{
key_code prefix;
.args = { "F:", 0, 1, NULL },
.usage = "[-F format] [command]",
if (args_has(args, 'P'))
return (xstrdup(args_get(args, 'P')));
.flags = CMD_STARTSERVER|CMD_AFTERHOOK,
.exec = cmd_list_keys_exec
};
prefix = options_get_number(global_s_options, "prefix");
if (prefix == KEYC_NONE)
return (xstrdup(""));
return (xstrdup(key_string_lookup_key(prefix, 0)));
}
static u_int
cmd_list_keys_get_width(const char *tablename, key_code only)
cmd_list_keys_get_width(struct key_binding **l, u_int n)
{
struct key_table *table;
struct key_binding *bd;
u_int width, keywidth = 0;
u_int i, width, keywidth = 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));
for (i = 0; i < n; i++) {
width = utf8_cstrwidth(key_string_lookup_key(l[i]->key, 0));
if (width > keywidth)
keywidth = width;
bd = key_bindings_next(table, bd);
}
return (keywidth);
}
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)
static u_int
cmd_list_keys_get_table_width(struct key_binding **l, u_int n)
{
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;
u_int i, width, tablewidth = 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') &&
!args_has(args, 'a'))) {
bd = key_bindings_next(table, bd);
continue;
}
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, "%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);
for (i = 0; i < n; i++) {
width = utf8_cstrwidth(l[i]->tablename);
if (width > tablewidth)
tablewidth = width;
}
return (found);
return (tablewidth);
}
static char *
cmd_list_keys_get_prefix(struct args *args, key_code *prefix)
static struct key_binding **
cmd_get_root_and_prefix(u_int *n, struct sort_criteria *sort_crit)
{
char *s;
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;
*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
s = xstrdup("");
} else
s = xstrdup(args_get(args, 'P'));
return (s);
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_filter_key_list(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)
continue;
if (filter_notes && l[i]->note == NULL)
continue;
l[j++] = l[i];
}
*n = j;
}
static void
cmd_format_add_key_binding(struct format_tree *ft,
const struct key_binding *bd, const char *prefix)
{
const char *s;
if (bd->flags & KEY_BINDING_REPEAT)
format_add(ft, "key_repeat", "1");
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);
s = key_string_lookup_key(bd->key, 0);
format_add(ft, "key_string", "%s", s);
s = cmd_list_print(bd->cmdlist,
CMD_LIST_PRINT_ESCAPED|CMD_LIST_PRINT_NO_GROUPS);
format_add(ft, "key_command", "%s", s);
}
static enum cmd_retval
@@ -149,16 +168,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 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));
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;
struct sort_criteria sort_crit;
if ((keystr = args_string(args, 0)) != NULL) {
only = key_string_lookup_string(keystr);
@@ -169,204 +188,64 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
only &= (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS);
}
tablename = args_get(args, 'T');
if (tablename != NULL && key_bindings_get_table(tablename, 0) == NULL) {
cmdq_error(item, "table %s doesn't exist", tablename);
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')) {
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);
prefix = cmd_list_keys_get_prefix(args);
single = args_has(args, '1');
notes_only = args_has(args, 'N');
if ((tablename = args_get(args, 'T')) != NULL) {
table = key_bindings_get_table(tablename, 0);
if (table == NULL) {
cmdq_error(item, "table %s doesn't exist", tablename);
return (CMD_RETURN_ERROR);
}
free(start);
goto out;
}
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)
template = LIST_KEYS_TEMPLATE;
if (bd->flags & KEY_BINDING_REPEAT)
repeat = 1;
if (table)
l = sort_get_key_bindings_table(table, &n, &sort_crit);
else if (notes_only)
l = cmd_get_root_and_prefix(&n, &sort_crit);
else
l = sort_get_key_bindings(&n, &sort_crit);
width = utf8_cstrwidth(table->name);
if (width > tablewidth)
tablewidth = width;
width = utf8_cstrwidth(key);
if (width > keywidth)
keywidth = width;
free(key);
bd = key_bindings_next(table, bd);
}
table = key_bindings_next_table(table);
}
tmpsize = 256;
tmp = xmalloc(tmpsize);
table = key_bindings_first_table();
while (table != NULL) {
if (tablename != NULL && strcmp(table->name, tablename) != 0) {
table = key_bindings_next_table(table);
continue;
}
bd = key_bindings_first(table);
while (bd != NULL) {
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, "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);
}
return (CMD_RETURN_NORMAL);
}
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, *s, *command;
char *line;
if ((template = args_get(args, 'F')) == NULL) {
template = "#{command_list_name}"
"#{?command_list_alias, (#{command_list_alias}),} "
"#{command_list_usage}";
}
filter_notes = notes_only && !args_has(args, 'a');
filter_key = only != KEYC_UNKNOWN;
if (filter_notes || filter_key)
cmd_filter_key_list(filter_notes, filter_key, only, l, &n);
if (single)
n = 1;
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_defaults(ft, NULL, NULL, NULL, NULL);
command = args_string(args, 0);
for (entryp = cmd_table; *entryp != NULL; entryp++) {
entry = *entryp;
if (command != NULL &&
(strcmp(entry->name, command) != 0 &&
(entry->alias == NULL ||
strcmp(entry->alias, command) != 0)))
continue;
format_add(ft, "command_list_name", "%s", entry->name);
if (entry->alias != NULL)
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);
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_format_add_key_binding(ft, l[i], prefix);
line = format_expand(ft, template);
if (*line != '\0')
if ((single && tc != NULL) || n == 1)
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);
}

View File

@@ -38,8 +38,9 @@ const struct cmd_entry cmd_list_panes_entry = {
.name = "list-panes",
.alias = "lsp",
.args = { "asF:f:t:", 0, 0, NULL },
.usage = "[-as] [-F format] [-f filter] " CMD_TARGET_WINDOW_USAGE,
.args = { "aF:f:O:rst:", 0, 0, NULL },
.usage = "[-asr] [-F format] [-f filter] [-O order]"
CMD_TARGET_WINDOW_USAGE,
.target = { 't', CMD_FIND_WINDOW, 0 },
@@ -54,6 +55,13 @@ 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);
@@ -89,12 +97,13 @@ 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;
u_int n;
struct window_pane *wp, **l;
u_int i, 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) {
@@ -124,8 +133,12 @@ cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl,
}
filter = args_get(args, 'f');
n = 0;
TAILQ_FOREACH(wp, &wl->window->panes, entry) {
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];
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_add(ft, "line", "%u", n);
format_defaults(ft, NULL, s, wl, wp);
@@ -143,6 +156,5 @@ cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl,
}
format_free(ft);
n++;
}
}

View File

@@ -42,8 +42,8 @@ const struct cmd_entry cmd_list_sessions_entry = {
.name = "list-sessions",
.alias = "ls",
.args = { "F:f:", 0, 0, NULL },
.usage = "[-F format] [-f filter]",
.args = { "F:f:O:r", 0, 0, NULL },
.usage = "[-r] [-F format] [-f filter] [-O order]",
.flags = CMD_AFTERHOOK,
.exec = cmd_list_sessions_exec
@@ -52,23 +52,31 @@ const struct cmd_entry cmd_list_sessions_entry = {
static enum cmd_retval
cmd_list_sessions_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct session *s;
u_int n;
struct format_tree *ft;
const char *template, *filter;
char *line, *expanded;
int flag;
struct args *args = cmd_get_args(self);
struct session **l;
u_int n, i;
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');
n = 0;
RB_FOREACH(s, sessions, &sessions) {
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++) {
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_add(ft, "line", "%u", n);
format_defaults(ft, NULL, s, NULL, NULL);
format_add(ft, "line", "%u", i);
format_defaults(ft, NULL, l[i], NULL, NULL);
if (filter != NULL) {
expanded = format_expand(ft, filter);
@@ -83,7 +91,6 @@ cmd_list_sessions_exec(struct cmd *self, struct cmdq_item *item)
}
format_free(ft);
n++;
}
return (CMD_RETURN_NORMAL);

View File

@@ -41,16 +41,13 @@
static enum cmd_retval cmd_list_windows_exec(struct cmd *, struct cmdq_item *);
static void cmd_list_windows_server(struct cmd *, struct cmdq_item *);
static void cmd_list_windows_session(struct cmd *, struct session *,
struct cmdq_item *, int);
const struct cmd_entry cmd_list_windows_entry = {
.name = "list-windows",
.alias = "lsw",
.args = { "F:f:at:", 0, 0, NULL },
.usage = "[-a] [-F format] [-f filter] " CMD_TARGET_SESSION_USAGE,
.args = { "aF:f:O:rt:", 0, 0, NULL },
.usage = "[-ar] [-F format] [-f filter] [-O order]"
CMD_TARGET_SESSION_USAGE,
.target = { 't', CMD_FIND_SESSION, 0 },
@@ -63,51 +60,38 @@ 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);
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;
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 winlink *wl, **l;
struct session *s;
u_int i, 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');
n = 0;
RB_FOREACH(wl, winlinks, &s->windows) {
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;
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_add(ft, "line", "%u", n);
format_defaults(ft, NULL, s, wl, NULL);
@@ -125,6 +109,7 @@ cmd_list_windows_session(struct cmd *self, struct session *s,
}
format_free(ft);
n++;
}
return (CMD_RETURN_NORMAL);
}

View File

@@ -66,7 +66,7 @@ cmd_load_buffer_done(__unused struct client *c, const char *path, int error,
return;
if (error != 0)
cmdq_error(item, "%s: %s", path, strerror(error));
cmdq_error(item, "%s: %s", strerror(error), path);
else if (bsize != 0) {
copy = xmalloc(bsize);
memcpy(copy, bdata, bsize);

View File

@@ -43,7 +43,7 @@ const struct cmd_entry cmd_new_session_entry = {
.usage = "[-AdDEPX] [-c start-directory] [-e environment] [-F format] "
"[-f flags] [-n window-name] [-s session-name] "
CMD_TARGET_SESSION_USAGE " [-x width] [-y height] "
"[shell-command]",
"[shell-command [argument ...]]",
.target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL },
@@ -117,8 +117,9 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
as = target->s;
if (as != NULL) {
retval = cmd_attach_session(item, as->name,
args_has(args, 'D'), args_has(args, 'X'), 0, NULL,
args_has(args, 'E'), args_get(args, 'f'));
args_has(args, 'D'), args_has(args, 'X'), 0,
args_get(args, 'c'), args_has(args, 'E'),
args_get(args, 'f'));
free(newname);
return (retval);
}

View File

@@ -40,7 +40,8 @@ const struct cmd_entry cmd_new_window_entry = {
.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]",
"[-n window-name] " CMD_TARGET_WINDOW_USAGE
" [shell-command [argument ...]]",
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX },

View File

@@ -32,7 +32,7 @@
static int yylex(void);
static int yyparse(void);
static int printflike(1,2) yyerror(const char *, ...);
static void printflike(1,2) yyerror(const char *, ...);
static char *yylex_token(int);
static char *yylex_format(void);
@@ -223,9 +223,16 @@ assignment : EQUALS
{
struct cmd_parse_state *ps = &parse_state;
int flags = ps->input->flags;
int flag = 1;
struct cmd_parse_scope *scope;
if ((~flags & CMD_PARSE_PARSEONLY) &&
(ps->scope == NULL || ps->scope->flag))
if (ps->scope != NULL) {
flag = ps->scope->flag;
TAILQ_FOREACH(scope, &ps->stack, entry)
flag = flag && scope->flag;
}
if ((~flags & CMD_PARSE_PARSEONLY) && flag)
environ_put(global_environ, $1, 0);
free($1);
}
@@ -234,9 +241,16 @@ hidden_assignment : HIDDEN EQUALS
{
struct cmd_parse_state *ps = &parse_state;
int flags = ps->input->flags;
int flag = 1;
struct cmd_parse_scope *scope;
if ((~flags & CMD_PARSE_PARSEONLY) &&
(ps->scope == NULL || ps->scope->flag))
if (ps->scope != NULL) {
flag = ps->scope->flag;
TAILQ_FOREACH(scope, &ps->stack, entry)
flag = flag && scope->flag;
}
if ((~flags & CMD_PARSE_PARSEONLY) && flag)
environ_put(global_environ, $2, ENVIRON_HIDDEN);
free($2);
}
@@ -744,7 +758,7 @@ static int
cmd_parse_expand_alias(struct cmd_parse_command *cmd,
struct cmd_parse_input *pi, struct cmd_parse_result *pr)
{
struct cmd_parse_argument *arg, *arg1, *first;
struct cmd_parse_argument *first;
struct cmd_parse_commands *cmds;
struct cmd_parse_command *last;
char *alias, *name, *cause;
@@ -784,10 +798,7 @@ cmd_parse_expand_alias(struct cmd_parse_command *cmd,
TAILQ_REMOVE(&cmd->arguments, first, entry);
cmd_parse_free_argument(first);
TAILQ_FOREACH_SAFE(arg, &cmd->arguments, entry, arg1) {
TAILQ_REMOVE(&cmd->arguments, arg, entry);
TAILQ_INSERT_TAIL(&last->arguments, arg, entry);
}
TAILQ_CONCAT(&last->arguments, &cmd->arguments, entry);
cmd_parse_log_commands(cmds, __func__);
pi->flags |= CMD_PARSE_NOALIAS;
@@ -835,7 +846,7 @@ cmd_parse_build_command(struct cmd_parse_command *cmd,
count++;
}
add = cmd_parse(values, count, pi->file, pi->line, &cause);
add = cmd_parse(values, count, pi->file, pi->line, pi->flags, &cause);
if (add == NULL) {
pr->status = CMD_PARSE_ERROR;
pr->error = cmd_parse_get_error(pi->file, pi->line, cause);
@@ -1113,7 +1124,7 @@ cmd_parse_from_arguments(struct args_value *values, u_int count,
return (&pr);
}
static int printflike(1, 2)
static void printflike(1, 2)
yyerror(const char *fmt, ...)
{
struct cmd_parse_state *ps = &parse_state;
@@ -1122,7 +1133,7 @@ yyerror(const char *fmt, ...)
char *error;
if (ps->error != NULL)
return (0);
return;
va_start(ap, fmt);
xvasprintf(&error, fmt, ap);
@@ -1130,7 +1141,6 @@ yyerror(const char *fmt, ...)
ps->error = cmd_parse_get_error(pi->file, pi->line, error);
free(error);
return (0);
}
static int
@@ -1273,6 +1283,16 @@ yylex(void)
continue;
}
if (ch == '\r') {
/*
* Treat \r\n as \n.
*/
ch = yylex_getc();
if (ch != '\n') {
yylex_ungetc(ch);
ch = '\r';
}
}
if (ch == '\n') {
/*
* End of line. Update the line number.
@@ -1603,6 +1623,7 @@ yylex_token_tilde(char **buf, size_t *len)
static char *
yylex_token(int ch)
{
struct cmd_parse_state *ps = &parse_state;
char *buf;
size_t len;
enum { START,
@@ -1619,9 +1640,19 @@ yylex_token(int ch)
log_debug("%s: end at EOF", __func__);
break;
}
if (state == NONE && ch == '\n') {
log_debug("%s: end at EOL", __func__);
break;
if (state == NONE && ch == '\r') {
ch = yylex_getc();
if (ch != '\n') {
yylex_ungetc(ch);
ch = '\r';
}
}
if (ch == '\n') {
if (state == NONE) {
log_debug("%s: end at EOL", __func__);
break;
}
ps->input->line++;
}
/* Whitespace or ; or } ends a token unless inside quotes. */

View File

@@ -33,8 +33,8 @@ const struct cmd_entry cmd_paste_buffer_entry = {
.name = "paste-buffer",
.alias = "pasteb",
.args = { "db:prs:t:", 0, 0, NULL },
.usage = "[-dpr] [-s separator] " CMD_BUFFER_USAGE " "
.args = { "db:prSs:t:", 0, 0, NULL },
.usage = "[-dprS] [-s separator] " CMD_BUFFER_USAGE " "
CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 },
@@ -43,6 +43,17 @@ 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)
{
@@ -51,7 +62,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;
size_t seplen, bufsize, len;
int bracket = args_has(args, 'p');
if (window_pane_exited(wp)) {
@@ -93,14 +104,22 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item)
line = memchr(bufdata, '\n', bufend - bufdata);
if (line == NULL)
break;
bufferevent_write(wp->event, bufdata, line - bufdata);
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, sepstr, seplen);
bufdata = line + 1;
}
if (bufdata != bufend)
bufferevent_write(wp->event, bufdata, bufend - bufdata);
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 (bracket && (wp->screen->mode & MODE_BRACKETPASTE))
bufferevent_write(wp->event, "\033[201~", 6);

View File

@@ -123,7 +123,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
/* Fork the child. */
sigfillset(&set);
sigprocmask(SIG_BLOCK, &set, &oldset);
switch (fork()) {
switch ((wp->pipe_pid = fork())) {
case -1:
sigprocmask(SIG_SETMASK, &oldset, NULL);
cmdq_error(item, "fork error: %s", strerror(errno));
@@ -136,6 +136,9 @@ 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)

View File

@@ -664,9 +664,18 @@ cmdq_fire_command(struct cmdq_item *item)
out:
item->client = saved;
if (retval == CMD_RETURN_ERROR)
if (retval == CMD_RETURN_ERROR) {
fsp = NULL;
if (cmd_find_valid_state(&item->target))
fsp = &item->target;
else if (cmd_find_valid_state(&item->state->current))
fsp = &item->state->current;
else if (cmd_find_from_client(&fs, item->client, 0) == 0)
fsp = &fs;
cmdq_insert_hook(fsp != NULL ? fsp->s : NULL, item, fsp,
"command-error");
cmdq_guard(item, "error", flags);
else
} else
cmdq_guard(item, "end", flags);
return (retval);
}
@@ -805,10 +814,10 @@ cmdq_running(struct client *c)
struct cmdq_list *queue = cmdq_get(c);
if (queue->item == NULL)
return (NULL);
if (queue->item->flags & CMDQ_WAITING)
return (NULL);
return (queue->item);
return (NULL);
if (queue->item->flags & CMDQ_WAITING)
return (NULL);
return (queue->item);
}
/* Print a guard line. */
@@ -825,9 +834,9 @@ cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
/* Show message from command. */
void
cmdq_print_data(struct cmdq_item *item, int parse, struct evbuffer *evb)
cmdq_print_data(struct cmdq_item *item, struct evbuffer *evb)
{
server_client_print(item->client, parse, evb);
server_client_print(item->client, 1, evb);
}
/* Show message from command. */
@@ -845,7 +854,7 @@ cmdq_print(struct cmdq_item *item, const char *fmt, ...)
evbuffer_add_vprintf(evb, fmt, ap);
va_end(ap);
cmdq_print_data(item, 0, evb);
cmdq_print_data(item, evb);
evbuffer_free(evb);
}
@@ -883,7 +892,7 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...)
c->retval = 1;
} else {
*msg = toupper((u_char) *msg);
status_message_set(c, -1, 1, 0, "%s", msg);
status_message_set(c, -1, 1, 0, 0, "%s", msg);
}
free(msg);

View File

@@ -34,9 +34,10 @@ const struct cmd_entry cmd_refresh_client_entry = {
.name = "refresh-client",
.alias = "refresh",
.args = { "A:B:cC:Df:F:l::LRSt:U", 0, 1, NULL },
.args = { "A:B:cC:Df:r:F:lLRSt:U", 0, 1, NULL },
.usage = "[-cDlLRSU] [-A pane:state] [-B name:what:format] "
"[-C XxY] [-f flags] " CMD_TARGET_CLIENT_USAGE " [adjustment]",
"[-C XxY] [-f flags] [-r pane:report] " CMD_TARGET_CLIENT_USAGE
" [adjustment]",
.flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG,
.exec = cmd_refresh_client_exec
@@ -162,35 +163,32 @@ out:
free(copy);
}
static enum cmd_retval
cmd_refresh_client_clipboard(struct cmd *self, struct cmdq_item *item)
static void
cmd_refresh_report(struct tty *tty, const char *value)
{
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;
struct window_pane *wp;
u_int pane;
size_t size = 0;
char *copy, *split;
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);
if (*value != '%')
return;
copy = xstrdup(value);
if ((split = strchr(copy, ':')) == NULL)
goto out;
*split++ = '\0';
if (sscanf(copy, "%%%u", &pane) != 1)
goto out;
wp = window_pane_find_by_id(pane);
if (wp == NULL)
goto out;
tty_keys_colours(tty, split, strlen(split), &size, &wp->control_fg,
&wp->control_bg);
out:
free(copy);
}
static enum cmd_retval
@@ -255,13 +253,17 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'l'))
return (cmd_refresh_client_clipboard(self, item));
if (args_has(args, 'l')) {
tty_clipboard_query(&tc->tty);
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'F')) /* -F is an alias for -f */
server_client_set_flags(tc, args_get(args, 'F'));
if (args_has(args, 'f'))
server_client_set_flags(tc, args_get(args, 'f'));
if (args_has(args, 'r'))
cmd_refresh_report(tty, args_get(args, 'r'));
if (args_has(args, 'A')) {
if (~tc->flags & CLIENT_CONTROL)

View File

@@ -87,7 +87,7 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'Z')) {
if (w->flags & WINDOW_ZOOMED)
window_unzoom(w);
window_unzoom(w, 1);
else
window_zoom(wp);
server_redraw_window(w);

View File

@@ -36,7 +36,7 @@ const struct cmd_entry cmd_respawn_pane_entry = {
.args = { "c:e:kt:", 0, -1, NULL },
.usage = "[-k] [-c start-directory] [-e environment] "
CMD_TARGET_PANE_USAGE " [shell-command]",
CMD_TARGET_PANE_USAGE " [shell-command [argument ...]]",
.target = { 't', CMD_FIND_PANE, 0 },

View File

@@ -36,7 +36,7 @@ const struct cmd_entry cmd_respawn_window_entry = {
.args = { "c:e:kt:", 0, -1, NULL },
.usage = "[-k] [-c start-directory] [-e environment] "
CMD_TARGET_WINDOW_USAGE " [shell-command]",
CMD_TARGET_WINDOW_USAGE " [shell-command [argument ...]]",
.target = { 't', CMD_FIND_WINDOW, 0 },

View File

@@ -44,8 +44,8 @@ const struct cmd_entry cmd_run_shell_entry = {
.name = "run-shell",
.alias = "run",
.args = { "bd:Ct:c:", 0, 2, cmd_run_shell_args_parse },
.usage = "[-bC] [-c start-directory] [-d delay] " CMD_TARGET_PANE_USAGE
.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]",
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
@@ -85,12 +85,18 @@ cmd_run_shell_print(struct job *job, const char *msg)
if (cdata->wp_id != -1)
wp = window_pane_find_by_id(cdata->wp_id);
if (wp == NULL && 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)
return;
if (wp == NULL) {
if (cdata->item != NULL) {
cmdq_print(cdata->item, "%s", msg);
return;
}
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)
return;
}
wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode != &window_view_mode)
@@ -152,6 +158,9 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
else
cdata->cwd = xstrdup(server_client_get_cwd(c, s));
if (args_has(args, 'E'))
cdata->flags |= JOB_SHOWSTDERR;
cdata->s = s;
if (s != NULL)
session_add_ref(s, __func__);
@@ -198,7 +207,7 @@ cmd_run_shell_timer(__unused int fd, __unused short events, void* arg)
if (cmdlist == NULL) {
if (cdata->item == NULL) {
*error = toupper((u_char)*error);
status_message_set(c, -1, 1, 0, "%s", error);
status_message_set(c, -1, 1, 0, 0, "%s", error);
} else
cmdq_error(cdata->item, "%s", error);
free(error);

View File

@@ -65,7 +65,7 @@ cmd_save_buffer_done(__unused struct client *c, const char *path, int error,
return;
if (error != 0)
cmdq_error(item, "%s: %s", path, strerror(error));
cmdq_error(item, "%s: %s", strerror(error), path);
cmdq_continue(item);
}
@@ -101,7 +101,7 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
if (evb == NULL)
fatalx("out of memory");
evbuffer_add(evb, bufdata, bufsize);
cmdq_print_data(item, 1, evb);
cmdq_print_data(item, evb);
evbuffer_free(evb);
return (CMD_RETURN_NORMAL);
}

View File

@@ -149,12 +149,14 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
markedwp = marked_pane.wp;
if (lastwp != NULL) {
lastwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
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);
markedwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|
PANE_THEMECHANGED);
server_redraw_window_borders(markedwp->window);
server_status_window(markedwp->window);
}
@@ -169,7 +171,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR);
}
options_set_string(oo, "window-active-style", 0, "%s", style);
wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|PANE_THEMECHANGED);
}
if (args_has(args, 'g')) {
cmdq_print(item, "%s", options_get_string(oo, "window-style"));

View File

@@ -35,11 +35,12 @@ const struct cmd_entry cmd_send_keys_entry = {
.args = { "c:FHKlMN:Rt:X", 0, -1, NULL },
.usage = "[-FHKlMRX] [-c target-client] [-N repeat-count] "
CMD_TARGET_PANE_USAGE " key ...",
CMD_TARGET_PANE_USAGE " [key ...]",
.target = { 't', CMD_FIND_PANE, 0 },
.flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG|CMD_CLIENT_CANFAIL,
.flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG|CMD_CLIENT_CANFAIL|
CMD_READONLY,
.exec = cmd_send_keys_exec
};
@@ -73,11 +74,13 @@ cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after,
if (args_has(args, 'K')) {
if (tc == NULL)
return (item);
event = xmalloc(sizeof *event);
event = xcalloc(1, sizeof *event);
event->key = key|KEYC_SENT;
memset(&event->m, 0, sizeof event->m);
if (server_client_handle_key(tc, event) == 0)
if (server_client_handle_key(tc, event) == 0) {
free(event->buf);
free(event);
}
return (item);
}
@@ -165,6 +168,11 @@ 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);
@@ -215,7 +223,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'R')) {
colour_palette_clear(&wp->palette);
input_reset(wp->ictx, 1);
wp->flags |= (PANE_STYLECHANGED|PANE_REDRAW);
wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED|PANE_REDRAW);
}
if (count == 0) {

View File

@@ -90,6 +90,7 @@ cmd_server_access_exec(struct cmd *self, struct cmdq_item *item)
pw = getpwnam(name);
if (pw == NULL) {
cmdq_error(item, "unknown user: %s", name);
free(name);
return (CMD_RETURN_ERROR);
}
free(name);

View File

@@ -35,7 +35,7 @@ const struct cmd_entry cmd_set_buffer_entry = {
.args = { "ab:t:n:w", 0, 1, NULL },
.usage = "[-aw] " CMD_BUFFER_USAGE " [-n new-buffer-name] "
CMD_TARGET_CLIENT_USAGE " data",
CMD_TARGET_CLIENT_USAGE " [data]",
.flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG|CMD_CLIENT_CANFAIL,
.exec = cmd_set_buffer_exec
@@ -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;
char *bufdata, *cause;
const char *bufname, *olddata;
size_t bufsize, newsize;
struct paste_buffer *pb = NULL;
char *bufname = NULL, *bufdata = NULL, *cause = NULL;
const char *olddata;
size_t bufsize = 0, newsize;
bufname = args_get(args, 'b');
if (bufname == NULL)
pb = NULL;
else
if (args_get(args, 'b') != NULL) {
bufname = xstrdup(args_get(args, 'b'));
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);
return (CMD_RETURN_ERROR);
goto fail;
}
pb = paste_get_top(&bufname);
}
if (pb == NULL) {
cmdq_error(item, "no buffer");
return (CMD_RETURN_ERROR);
goto fail;
}
paste_free(pb);
free(bufname);
return (CMD_RETURN_NORMAL);
}
@@ -88,32 +88,28 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
if (pb == NULL) {
if (bufname != NULL) {
cmdq_error(item, "unknown buffer: %s", bufname);
return (CMD_RETURN_ERROR);
goto fail;
}
pb = paste_get_top(&bufname);
}
if (pb == NULL) {
cmdq_error(item, "no buffer");
return (CMD_RETURN_ERROR);
goto fail;
}
if (paste_rename(bufname, args_get(args, 'n'), &cause) != 0) {
cmdq_error(item, "%s", cause);
free(cause);
return (CMD_RETURN_ERROR);
goto fail;
}
return (CMD_RETURN_NORMAL);
}
if (args_count(args) != 1) {
cmdq_error(item, "no data specified");
return (CMD_RETURN_ERROR);
goto fail;
}
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);
bufdata = xmalloc(bufsize);
@@ -126,12 +122,16 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
if (paste_set(bufdata, bufsize, bufname, &cause) != 0) {
cmdq_error(item, "%s", cause);
free(bufdata);
free(cause);
return (CMD_RETURN_ERROR);
goto fail;
}
if (args_has(args, 'w') && tc != NULL)
tty_set_selection(&tc->tty, "", bufdata, bufsize);
return (CMD_RETURN_NORMAL);
fail:
free(bufdata);
free(bufname);
free(cause);
return (CMD_RETURN_ERROR);
}

View File

@@ -35,7 +35,7 @@ const struct cmd_entry cmd_set_environment_entry = {
.alias = "setenv",
.args = { "Fhgrt:u", 1, 2, NULL },
.usage = "[-Fhgru] " CMD_TARGET_SESSION_USAGE " name [value]",
.usage = "[-Fhgru] " CMD_TARGET_SESSION_USAGE " variable [value]",
.target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL },

View File

@@ -39,7 +39,7 @@ const struct cmd_entry cmd_show_environment_entry = {
.alias = "showenv",
.args = { "hgst:", 0, 1, NULL },
.usage = "[-hgs] " CMD_TARGET_SESSION_USAGE " [name]",
.usage = "[-hgs] " CMD_TARGET_SESSION_USAGE " [variable]",
.target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL },

View File

@@ -41,7 +41,7 @@ const struct cmd_entry cmd_show_messages_entry = {
.args = { "JTt:", 0, 0, NULL },
.usage = "[-JT] " CMD_TARGET_CLIENT_USAGE,
.flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG,
.flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG|CMD_CLIENT_CANFAIL,
.exec = cmd_show_messages_exec
};
@@ -55,7 +55,7 @@ cmd_show_messages_terminals(struct cmd *self, struct cmdq_item *item, int blank)
n = 0;
LIST_FOREACH(term, &tty_terms, entry) {
if (args_has(args, 't') && term != tc->tty.term)
if (args_has(args, 't') && tc != NULL && term != tc->tty.term)
continue;
if (blank) {
cmdq_print(item, "%s", "");

View File

@@ -65,7 +65,7 @@ const struct cmd_entry cmd_show_hooks_entry = {
.alias = NULL,
.args = { "gpt:w", 0, 1, NULL },
.usage = "[-gpw] " CMD_TARGET_PANE_USAGE,
.usage = "[-gpw] " CMD_TARGET_PANE_USAGE " [hook]",
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },

View File

@@ -32,7 +32,7 @@ const struct cmd_entry cmd_show_prompt_history_entry = {
.alias = "showphist",
.args = { "T:", 0, 0, NULL },
.usage = "[-T type]",
.usage = "[-T prompt-type]",
.flags = CMD_AFTERHOOK,
.exec = cmd_show_prompt_history_exec
@@ -43,7 +43,7 @@ const struct cmd_entry cmd_clear_prompt_history_entry = {
.alias = "clearphist",
.args = { "T:", 0, 0, NULL },
.usage = "[-T type]",
.usage = "[-T prompt-type]",
.flags = CMD_AFTERHOOK,
.exec = cmd_show_prompt_history_exec
@@ -60,6 +60,9 @@ cmd_show_prompt_history_exec(struct cmd *self, struct cmdq_item *item)
if (cmd_get_entry(self) == &cmd_clear_prompt_history_entry) {
if (typestr == NULL) {
for (tidx = 0; tidx < PROMPT_NTYPES; tidx++) {
for (hidx = 0; hidx < status_prompt_hsize[tidx];
hidx++)
free(status_prompt_hlist[tidx][hidx]);
free(status_prompt_hlist[tidx]);
status_prompt_hlist[tidx] = NULL;
status_prompt_hsize[tidx] = 0;
@@ -70,6 +73,8 @@ cmd_show_prompt_history_exec(struct cmd *self, struct cmdq_item *item)
cmdq_error(item, "invalid type: %s", typestr);
return (CMD_RETURN_ERROR);
}
for (hidx = 0; hidx < status_prompt_hsize[type]; hidx++)
free(status_prompt_hlist[type][hidx]);
free(status_prompt_hlist[type]);
status_prompt_hlist[type] = NULL;
status_prompt_hsize[type] = 0;

View File

@@ -18,6 +18,7 @@
#include <sys/types.h>
#include <ctype.h>
#include <errno.h>
#include <glob.h>
#include <stdlib.h>
@@ -29,6 +30,9 @@
* Sources a configuration file.
*/
#define CMD_SOURCE_FILE_DEPTH_LIMIT 50
static u_int cmd_source_file_depth;
static enum cmd_retval cmd_source_file_exec(struct cmd *, struct cmdq_item *);
const struct cmd_entry cmd_source_file_entry = {
@@ -59,6 +63,16 @@ struct cmd_source_file_data {
static enum cmd_retval
cmd_source_file_complete_cb(struct cmdq_item *item, __unused void *data)
{
struct client *c = cmdq_get_client(item);
if (c == NULL) {
cmd_source_file_depth--;
log_debug("%s: depth now %u", __func__, cmd_source_file_depth);
} else {
c->source_file_depth--;
log_debug("%s: depth now %u", __func__, c->source_file_depth);
}
cfg_print_causes(item);
return (CMD_RETURN_NORMAL);
}
@@ -100,7 +114,7 @@ cmd_source_file_done(struct client *c, const char *path, int error,
return;
if (error != 0)
cmdq_error(item, "%s: %s", path, strerror(error));
cmdq_error(item, "%s: %s", strerror(error), path);
else if (bsize != 0) {
if (load_cfg_from_buffer(bdata, bsize, path, c, cdata->after,
target, cdata->flags, &new_item) < 0)
@@ -127,6 +141,21 @@ cmd_source_file_add(struct cmd_source_file_data *cdata, const char *path)
cdata->files[cdata->nfiles++] = xstrdup(path);
}
static char *
cmd_source_file_quote_for_glob(const char *path)
{
char *quoted = xmalloc(2 * strlen(path) + 1), *q = quoted;
const char *p = path;
while (*p != '\0') {
if ((u_char)*p < 128 && !isalnum((u_char)*p) && *p != '/')
*q++ = '\\';
*q++ = *p++;
}
*q = '\0';
return (quoted);
}
static enum cmd_retval
cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
{
@@ -137,9 +166,25 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
char *pattern, *cwd, *expanded = NULL;
const char *path, *error;
glob_t g;
int result;
int result, parse_flags;
u_int i, j;
if (c == NULL) {
if (cmd_source_file_depth >= CMD_SOURCE_FILE_DEPTH_LIMIT) {
cmdq_error(item, "too many nested files");
return (CMD_RETURN_ERROR);
}
cmd_source_file_depth++;
log_debug("%s: depth now %u", __func__, cmd_source_file_depth);
} else {
if (c->source_file_depth >= CMD_SOURCE_FILE_DEPTH_LIMIT) {
cmdq_error(item, "too many nested files");
return (CMD_RETURN_ERROR);
}
c->source_file_depth++;
log_debug("%s: depth now %u", __func__, c->source_file_depth);
}
cdata = xcalloc(1, sizeof *cdata);
cdata->item = item;
@@ -147,10 +192,13 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
cdata->flags |= CMD_PARSE_QUIET;
if (args_has(args, 'n'))
cdata->flags |= CMD_PARSE_PARSEONLY;
if (args_has(args, 'v'))
cdata->flags |= CMD_PARSE_VERBOSE;
if (c == NULL || ~c->flags & CLIENT_CONTROL) {
parse_flags = cmd_get_parse_flags(self);
if (args_has(args, 'v') || (parse_flags & CMD_PARSE_VERBOSE))
cdata->flags |= CMD_PARSE_VERBOSE;
}
utf8_stravis(&cwd, server_client_get_cwd(c, NULL), VIS_GLOB);
cwd = cmd_source_file_quote_for_glob(server_client_get_cwd(c, NULL));
for (i = 0; i < args_count(args); i++) {
path = args_string(args, i);
@@ -179,7 +227,7 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
error = strerror(ENOMEM);
else
error = strerror(EINVAL);
cmdq_error(item, "%s: %s", path, error);
cmdq_error(item, "%s: %s", error, path);
retval = CMD_RETURN_ERROR;
}
globfree(&g);

View File

@@ -42,7 +42,7 @@ const struct cmd_entry cmd_split_window_entry = {
.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]",
" [shell-command [argument ...]]",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -93,10 +93,10 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
size = -1;
if (args_has(args, 'l')) {
size = args_percentage_and_expand(args, 'l', 0, INT_MAX, curval,
item, &cause);
item, &cause);
} else if (args_has(args, 'p')) {
size = args_strtonum_and_expand(args, 'l', 0, 100, item,
&cause);
size = args_strtonum_and_expand(args, 'p', 0, 100, item,
&cause);
if (cause == NULL)
size = curval * size / 100;
}

View File

@@ -101,10 +101,10 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
src_wp->window = dst_w;
options_set_parent(src_wp->options, dst_w->options);
src_wp->flags |= PANE_STYLECHANGED;
src_wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
dst_wp->window = src_w;
options_set_parent(dst_wp->options, src_w->options);
dst_wp->flags |= PANE_STYLECHANGED;
dst_wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
sx = src_wp->sx; sy = src_wp->sy;
xoff = src_wp->xoff; yoff = src_wp->yoff;
@@ -132,9 +132,12 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
window_pane_stack_remove(&dst_w->last_panes, dst_wp);
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);
server_redraw_window(src_w);
}
server_redraw_window(src_w);
layout_fix_panes(dst_w, NULL);
server_redraw_window(dst_w);
notify_window("window-layout-changed", src_w);
if (src_w != dst_w)
notify_window("window-layout-changed", dst_w);

View File

@@ -77,6 +77,8 @@ cmd_swap_window_exec(struct cmd *self, struct cmdq_item *item)
wl_src->window = w_dst;
TAILQ_INSERT_TAIL(&w_dst->winlinks, wl_src, wentry);
if (marked_pane.wl == wl_src)
marked_pane.wl = wl_dst;
if (args_has(args, 'd')) {
session_select(dst, wl_dst->idx);
if (src != dst)

View File

@@ -34,9 +34,9 @@ const struct cmd_entry cmd_switch_client_entry = {
.name = "switch-client",
.alias = "switchc",
.args = { "lc:EFnpt:rT:Z", 0, 0, NULL },
.args = { "c:EFlnO:pt:rT:Z", 0, 0, NULL },
.usage = "[-ElnprZ] [-c target-client] [-t target-session] "
"[-T key-table]",
"[-T key-table] [-O order]",
/* -t is special */
@@ -60,8 +60,10 @@ 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;
if (tflag != NULL && tflag[strcspn(tflag, ":.%")] != '\0') {
if (tflag != NULL &&
(tflag[strcspn(tflag, ":.%")] != '\0' || strcmp(tflag, "=") == 0)) {
type = CMD_FIND_PANE;
flags = 0;
} else {
@@ -94,13 +96,22 @@ 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')) {
if ((s = session_next_session(tc->session)) == NULL) {
s = session_next_session(tc->session, &sort_crit);
if (s == NULL) {
cmdq_error(item, "can't find next session");
return (CMD_RETURN_ERROR);
}
} else if (args_has(args, 'p')) {
if ((s = session_previous_session(tc->session)) == NULL) {
s = session_previous_session(tc->session, &sort_crit);
if (s == NULL) {
cmdq_error(item, "can't find previous session");
return (CMD_RETURN_ERROR);
}

40
cmd.c
View File

@@ -47,7 +47,6 @@ extern const struct cmd_entry cmd_display_menu_entry;
extern const struct cmd_entry cmd_display_message_entry;
extern const struct cmd_entry cmd_display_popup_entry;
extern const struct cmd_entry cmd_display_panes_entry;
extern const struct cmd_entry cmd_down_pane_entry;
extern const struct cmd_entry cmd_find_window_entry;
extern const struct cmd_entry cmd_has_session_entry;
extern const struct cmd_entry cmd_if_shell_entry;
@@ -117,7 +116,6 @@ extern const struct cmd_entry cmd_swap_window_entry;
extern const struct cmd_entry cmd_switch_client_entry;
extern const struct cmd_entry cmd_unbind_key_entry;
extern const struct cmd_entry cmd_unlink_window_entry;
extern const struct cmd_entry cmd_up_pane_entry;
extern const struct cmd_entry cmd_wait_for_entry;
const struct cmd_entry *cmd_table[] = {
@@ -222,6 +220,7 @@ struct cmd {
char *file;
u_int line;
int parse_flags;
TAILQ_ENTRY(cmd) qentry;
};
@@ -414,6 +413,13 @@ cmd_get_source(struct cmd *cmd, const char **file, u_int *line)
*line = cmd->line;
}
/* Get parse flags for command. */
int
cmd_get_parse_flags(struct cmd *cmd)
{
return (cmd->parse_flags);
}
/* Look for an alias for a command. */
char *
cmd_get_alias(const char *name)
@@ -446,7 +452,7 @@ cmd_get_alias(const char *name)
}
/* Look up a command entry by name. */
static const struct cmd_entry *
const struct cmd_entry *
cmd_find(const char *name, char **cause)
{
const struct cmd_entry **loop, *entry, *found = NULL;
@@ -498,7 +504,7 @@ ambiguous:
/* Parse a single command from an argument vector. */
struct cmd *
cmd_parse(struct args_value *values, u_int count, const char *file, u_int line,
char **cause)
int parse_flags, char **cause)
{
const struct cmd_entry *entry;
struct cmd *cmd;
@@ -527,6 +533,7 @@ cmd_parse(struct args_value *values, u_int count, const char *file, u_int line,
cmd = xcalloc(1, sizeof *cmd);
cmd->entry = entry;
cmd->args = args;
cmd->parse_flags = parse_flags;
if (file != NULL)
cmd->file = xstrdup(file);
@@ -638,7 +645,7 @@ cmd_list_free(struct cmd_list *cmdlist)
/* Copy a command list, expanding %s in arguments. */
struct cmd_list *
cmd_list_copy(struct cmd_list *cmdlist, int argc, char **argv)
cmd_list_copy(const struct cmd_list *cmdlist, int argc, char **argv)
{
struct cmd *cmd;
struct cmd_list *new_cmdlist;
@@ -669,11 +676,16 @@ cmd_list_copy(struct cmd_list *cmdlist, int argc, char **argv)
/* Get a command list as a string. */
char *
cmd_list_print(struct cmd_list *cmdlist, int escaped)
cmd_list_print(const struct cmd_list *cmdlist, int flags)
{
struct cmd *cmd, *next;
char *buf, *this;
size_t len;
const char *separator;
int escaped = flags & CMD_LIST_PRINT_ESCAPED;
int no_groups = flags & CMD_LIST_PRINT_NO_GROUPS;
const char *single_separator = escaped ? " \\; " : " ; ";
const char *double_separator = escaped ? " \\;\\; " : " ;; ";
len = 1;
buf = xcalloc(1, len);
@@ -688,17 +700,11 @@ cmd_list_print(struct cmd_list *cmdlist, int escaped)
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);
}
if (!no_groups && cmd->group != next->group)
separator = double_separator;
else
separator = single_separator;
strlcat(buf, separator, len);
}
free(this);

View File

@@ -182,6 +182,46 @@ colour_tostring(int c)
return ("invalid");
}
/* Convert background colour to theme. */
enum client_theme
colour_totheme(int c)
{
int r, g, b, brightness;
if (c == -1)
return (THEME_UNKNOWN);
if (c & COLOUR_FLAG_RGB) {
r = (c >> 16) & 0xff;
g = (c >> 8) & 0xff;
b = (c >> 0) & 0xff;
brightness = r + g + b;
if (brightness > 382)
return (THEME_LIGHT);
return (THEME_DARK);
}
if (c & COLOUR_FLAG_256)
return (colour_totheme(colour_256toRGB(c)));
switch (c) {
case 0:
case 90:
return (THEME_DARK);
case 7:
case 97:
return (THEME_LIGHT);
default:
if (c >= 0 && c <= 7)
return (colour_totheme(colour_256toRGB(c)));
if (c >= 90 && c <= 97)
return (colour_totheme(colour_256toRGB(8 + c - 90)));
break;
}
return (THEME_UNKNOWN);
}
/* Convert colour from string. */
int
colour_fromstring(const char *s)
@@ -942,13 +982,18 @@ colour_byname(const char *name)
{ "yellow3", 0xcdcd00 },
{ "yellow4", 0x8b8b00 }
};
u_int i;
int c;
u_int i;
int c;
const char *errstr;
if (strncmp(name, "grey", 4) == 0 || strncmp(name, "gray", 4) == 0) {
if (!isdigit((u_char)name[4]))
if (strncasecmp(name, "grey", 4) == 0 ||
strncasecmp(name, "gray", 4) == 0) {
if (name[4] == '\0')
return (0xbebebe|COLOUR_FLAG_RGB);
c = round(2.55 * atoi(name + 4));
c = strtonum(name + 4, 0, 100, &errstr);
if (errstr != NULL)
return (-1);
c = round(2.55 * c);
if (c < 0 || c > 255)
return (-1);
return (colour_join_rgb(c, c, c));
@@ -1037,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 c)
colour_palette_get(struct colour_palette *p, int n)
{
if (p == NULL)
return (-1);
if (c >= 90 && c <= 97)
c = 8 + c - 90;
else if (c & COLOUR_FLAG_256)
c &= ~COLOUR_FLAG_256;
else if (c >= 8)
if (n >= 90 && n <= 97)
n = 8 + n - 90;
else if (n & COLOUR_FLAG_256)
n &= ~COLOUR_FLAG_256;
else if (n >= 8)
return (-1);
if (p->palette != NULL && p->palette[c] != -1)
return (p->palette[c]);
if (p->default_palette != NULL && p->default_palette[c] != -1)
return (p->default_palette[c]);
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]);
return (-1);
}
@@ -1062,15 +1107,14 @@ colour_palette_set(struct colour_palette *p, int n, int c)
{
u_int i;
if (p == NULL || n > 255)
if (p == NULL || n < 0 || n > 255)
return (0);
if (c == -1 && p->palette == NULL)
return (0);
if (c != -1 && p->palette == NULL) {
if (p->palette == NULL)
p->palette = xcalloc(256, sizeof *p->palette);
if (p->palette == NULL) {
p->palette = xcalloc(256, sizeof *p->palette);
for (i = 0; i < 256; i++)
p->palette[i] = -1;
}

View File

@@ -38,6 +38,14 @@
#include <event2/bufferevent_compat.h>
#else
#include <event.h>
#ifndef EVBUFFER_EOL_LF
/*
* This doesn't really work because evbuffer_readline is broken, but gets us to
* build with very old (older than 1.4.14) libevent.
*/
#define EVBUFFER_EOL_LF
#define evbuffer_readln(a, b, c) evbuffer_readline(a)
#endif
#endif
#ifdef HAVE_MALLOC_TRIM
@@ -289,6 +297,11 @@ void explicit_bzero(void *, size_t);
int getdtablecount(void);
#endif
#ifndef HAVE_GETDTABLESIZE
/* getdtablesize.c */
int getdtablesize(void);
#endif
#ifndef HAVE_CLOSEFROM
/* closefrom.c */
void closefrom(int);
@@ -375,7 +388,7 @@ int clock_gettime(int, struct timespec *);
/* base64.c */
#undef b64_ntop
#undef b64_pton
int b64_ntop(const char *, size_t, char *, size_t);
int b64_ntop(const u_char *, size_t, char *, size_t);
int b64_pton(const char *, u_char *, size_t);
#endif
@@ -437,7 +450,7 @@ void *recallocarray(void *, size_t, size_t, size_t);
/* systemd.c */
int systemd_activated(void);
int systemd_create_socket(int, char **);
int systemd_move_pid_to_new_cgroup(pid_t, char **);
int systemd_move_to_new_cgroup(char **);
#endif
#ifdef HAVE_UTF8PROC
@@ -453,11 +466,11 @@ int utf8proc_wctomb(char *, wchar_t);
#endif
/* getopt.c */
extern int BSDopterr;
extern int BSDoptind;
extern int BSDoptopt;
extern int BSDoptreset;
extern char *BSDoptarg;
extern int BSDopterr;
extern int BSDoptind;
extern int BSDoptopt;
extern int BSDoptreset;
extern char *BSDoptarg;
int BSDgetopt(int, char *const *, const char *);
#define getopt(ac, av, o) BSDgetopt(ac, av, o)
#define opterr BSDopterr

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: base64.c,v 1.8 2015/01/16 16:48:51 deraadt Exp $ */
/* $OpenBSD: base64.c,v 1.15 2021/10/25 14:41:09 jca 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 = '=';
@@ -107,9 +107,9 @@ static const char Pad64 = '=';
end of the data is performed using the '=' character.
Since all base64 input is an integral number of octets, only the
-------------------------------------------------
-------------------------------------------------
following cases can arise:
(1) the final quantum of encoding input is an integral
multiple of 24 bits; here, the final unit of encoded
output will be an integral multiple of 4 characters
@@ -123,15 +123,12 @@ static const char Pad64 = '=';
*/
int
b64_ntop(src, srclength, target, targsize)
u_char const *src;
size_t srclength;
char *target;
size_t targsize;
b64_ntop(unsigned char const *src, size_t srclength, char *target,
size_t targsize)
{
size_t datalength = 0;
u_char input[3];
u_char output[4];
unsigned char input[3];
unsigned char output[4];
int i;
while (2 < srclength) {
@@ -152,14 +149,14 @@ b64_ntop(src, srclength, target, targsize)
target[datalength++] = Base64[output[2]];
target[datalength++] = Base64[output[3]];
}
/* Now we worry about padding. */
if (0 != srclength) {
/* Get what's left. */
input[0] = input[1] = input[2] = '\0';
for (i = 0; i < srclength; i++)
input[i] = *src++;
output[0] = input[0] >> 2;
output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
@@ -187,13 +184,10 @@ b64_ntop(src, srclength, target, targsize)
*/
int
b64_pton(src, target, targsize)
char const *src;
u_char *target;
size_t targsize;
b64_pton(char const *src, unsigned char *target, size_t targsize)
{
int tarindex, state, ch;
u_char nextbyte;
unsigned char nextbyte;
char *pos;
state = 0;
@@ -207,7 +201,7 @@ b64_pton(src, target, targsize)
break;
pos = strchr(Base64, ch);
if (pos == 0) /* A non-base64 character. */
if (pos == 0) /* A non-base64 character. */
return (-1);
switch (state) {

View File

@@ -27,7 +27,7 @@
#endif
int
clock_gettime(int clock, struct timespec *ts)
clock_gettime(__unused int clock, struct timespec *ts)
{
struct timeval tv;

View File

@@ -49,12 +49,12 @@
#include <mach/mach.h>
#include <Availability.h>
#include <AvailabilityMacros.h>
#include <unistd.h>
void daemon_darwin(void);
#ifdef __MAC_10_10
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
extern kern_return_t bootstrap_look_up_per_user(mach_port_t, const char *,
uid_t, mach_port_t *);

View File

@@ -18,13 +18,28 @@
#include <glob.h>
#include <unistd.h>
#if defined(HAVE_LIBPROC_H)
#include <libproc.h>
#endif
#include "compat.h"
void fatal(const char *, ...);
void fatalx(const char *, ...);
#ifdef HAVE_PROC_PID
#if defined(HAVE_LIBPROC_H) && defined(HAVE_PROC_PIDINFO)
int
getdtablecount(void)
{
int sz;
pid_t pid = getpid();
sz = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
if (sz == -1)
return (0);
return (sz / PROC_PIDLISTFD_SIZE);
}
#elif defined(HAVE_PROC_PID)
int
getdtablecount(void)
{

View File

@@ -1,115 +0,0 @@
/*
* Copyright (c) 1987, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/* OPENBSD ORIGINAL: lib/libc/stdlib/getopt.c */
#include "compat.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int BSDopterr = 1, /* if error message should be printed */
BSDoptind = 1, /* index into parent argv vector */
BSDoptopt, /* character checked for validity */
BSDoptreset; /* reset getopt */
char *BSDoptarg; /* argument associated with option */
#define BADCH (int)'?'
#define BADARG (int)':'
#define EMSG ""
/*
* getopt --
* Parse argc/argv argument vector.
*/
int
BSDgetopt(int nargc, char *const *nargv, const char *ostr)
{
static const char *place = EMSG; /* option letter processing */
char *oli; /* option letter list index */
if (ostr == NULL)
return (-1);
if (BSDoptreset || !*place) { /* update scanning pointer */
BSDoptreset = 0;
if (BSDoptind >= nargc || *(place = nargv[BSDoptind]) != '-') {
place = EMSG;
return (-1);
}
if (place[1] && *++place == '-') { /* found "--" */
if (place[1])
return (BADCH);
++BSDoptind;
place = EMSG;
return (-1);
}
} /* option letter okay? */
if ((BSDoptopt = (int)*place++) == (int)':' ||
!(oli = strchr(ostr, BSDoptopt))) {
/*
* if the user didn't specify '-' as an option,
* assume it means -1.
*/
if (BSDoptopt == (int)'-')
return (-1);
if (!*place)
++BSDoptind;
if (BSDopterr && *ostr != ':')
(void)fprintf(stderr,
"%s: unknown option -- %c\n", getprogname(),
BSDoptopt);
return (BADCH);
}
if (*++oli != ':') { /* don't need argument */
BSDoptarg = NULL;
if (!*place)
++BSDoptind;
}
else { /* need an argument */
if (*place) /* no white space */
BSDoptarg = (char *)place;
else if (nargc <= ++BSDoptind) { /* no arg */
place = EMSG;
if (*ostr == ':')
return (BADARG);
if (BSDopterr)
(void)fprintf(stderr,
"%s: option requires an argument -- %c\n",
getprogname(), BSDoptopt);
return (BADCH);
}
else /* white space */
BSDoptarg = nargv[BSDoptind];
place = EMSG;
++BSDoptind;
}
return (BSDoptopt); /* dump back option letter */
}

577
compat/getopt_long.c Normal file
View File

@@ -0,0 +1,577 @@
/* This file is obtained from OpenSSH:
* Repository: https://github.com/openssh/openssh-portable
* Commit: b5b405fee7f3e79d44e2d2971a4b6b4cc53f112e
* File: /openbsd-compat/getopt_long.c */
/*
* Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.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.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Dieter Baron and Thomas Klausner.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/* OPENBSD ORIGINAL: lib/libc/stdlib/getopt_long.c */
#include "compat.h"
/* The following macro constants are taken from getopt.h of OpenSSH:
* Repository: https://github.com/openssh/openssh-portable
* Commit: b5b405fee7f3e79d44e2d2971a4b6b4cc53f112e
* File: /openbsd-compat/getopt.h
*
* ---- BEGIN - Copyright notice and license of getopt.h ----
* Copyright (c) 2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Dieter Baron and Thomas Klausner.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* ---- END ----
*/
#define no_argument 0
#define required_argument 1
#define optional_argument 2
#if !defined(HAVE_GETOPT) || !defined(HAVE_GETOPT_OPTRESET)
#if 0
#include <err.h>
#include <getopt.h>
#endif
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
struct option {
/* name of long option */
const char *name;
/*
* one of no_argument, required_argument, and optional_argument:
* whether option takes an argument
*/
int has_arg;
/* if not NULL, set *flag to val when option found */
int *flag;
/* if flag not NULL, value to set *flag to; else return value */
int val;
};
int opterr = 1; /* if error message should be printed */
int optind = 1; /* index into parent argv vector */
int optopt = '?'; /* character checked for validity */
int optreset; /* reset getopt */
char *optarg; /* argument associated with option */
#define PRINT_ERROR ((opterr) && (*options != ':'))
#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
/* return values */
#define BADCH (int)'?'
#define BADARG ((*options == ':') ? (int)':' : (int)'?')
#define INORDER (int)1
#define EMSG (char *)""
static int getopt_internal(int, char * const *, const char *,
const struct option *, int *, int);
static int parse_long_options(char * const *, const char *,
const struct option *, int *, int);
static int gcd(int, int);
static void permute_args(int, int, int, char * const *);
static char *place = EMSG; /* option letter processing */
/* XXX: set optreset to 1 rather than these two */
static int nonopt_start = -1; /* first non option argument (for permute) */
static int nonopt_end = -1; /* first option after non options (for permute) */
/* Error messages */
static const char recargchar[] = "option requires an argument -- %c";
static const char recargstring[] = "option requires an argument -- %s";
static const char ambig[] = "ambiguous option -- %.*s";
static const char noarg[] = "option doesn't take an argument -- %.*s";
static const char illoptchar[] = "unknown option -- %c";
static const char illoptstring[] = "unknown option -- %s";
/*
* Compute the greatest common divisor of a and b.
*/
static int
gcd(int a, int b)
{
int c;
c = a % b;
while (c != 0) {
a = b;
b = c;
c = a % b;
}
return (b);
}
/*
* Exchange the block from nonopt_start to nonopt_end with the block
* from nonopt_end to opt_end (keeping the same order of arguments
* in each block).
*/
static void
permute_args(int panonopt_start, int panonopt_end, int opt_end,
char * const *nargv)
{
int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
char *swap;
/*
* compute lengths of blocks and number and size of cycles
*/
nnonopts = panonopt_end - panonopt_start;
nopts = opt_end - panonopt_end;
ncycle = gcd(nnonopts, nopts);
cyclelen = (opt_end - panonopt_start) / ncycle;
for (i = 0; i < ncycle; i++) {
cstart = panonopt_end+i;
pos = cstart;
for (j = 0; j < cyclelen; j++) {
if (pos >= panonopt_end)
pos -= nnonopts;
else
pos += nopts;
swap = nargv[pos];
/* LINTED const cast */
((char **) nargv)[pos] = nargv[cstart];
/* LINTED const cast */
((char **)nargv)[cstart] = swap;
}
}
}
/*
* parse_long_options --
* Parse long options in argc/argv argument vector.
* Returns -1 if short_too is set and the option does not match long_options.
*/
static int
parse_long_options(char * const *nargv, const char *options,
const struct option *long_options, int *idx, int short_too)
{
char *current_argv, *has_equal;
size_t current_argv_len;
int i, match;
current_argv = place;
match = -1;
optind++;
if ((has_equal = strchr(current_argv, '=')) != NULL) {
/* argument found (--option=arg) */
current_argv_len = has_equal - current_argv;
has_equal++;
} else
current_argv_len = strlen(current_argv);
for (i = 0; long_options[i].name; i++) {
/* find matching long option */
if (strncmp(current_argv, long_options[i].name,
current_argv_len))
continue;
if (strlen(long_options[i].name) == current_argv_len) {
/* exact match */
match = i;
break;
}
/*
* If this is a known short option, don't allow
* a partial match of a single character.
*/
if (short_too && current_argv_len == 1)
continue;
if (match == -1) /* partial match */
match = i;
else {
/* ambiguous abbreviation */
if (PRINT_ERROR)
warnx(ambig, (int)current_argv_len,
current_argv);
optopt = 0;
return (BADCH);
}
}
if (match != -1) { /* option found */
if (long_options[match].has_arg == no_argument
&& has_equal) {
if (PRINT_ERROR)
warnx(noarg, (int)current_argv_len,
current_argv);
/*
* XXX: GNU sets optopt to val regardless of flag
*/
if (long_options[match].flag == NULL)
optopt = long_options[match].val;
else
optopt = 0;
return (BADARG);
}
if (long_options[match].has_arg == required_argument ||
long_options[match].has_arg == optional_argument) {
if (has_equal)
optarg = has_equal;
else if (long_options[match].has_arg ==
required_argument) {
/*
* optional argument doesn't use next nargv
*/
optarg = nargv[optind++];
}
}
if ((long_options[match].has_arg == required_argument)
&& (optarg == NULL)) {
/*
* Missing argument; leading ':' indicates no error
* should be generated.
*/
if (PRINT_ERROR)
warnx(recargstring,
current_argv);
/*
* XXX: GNU sets optopt to val regardless of flag
*/
if (long_options[match].flag == NULL)
optopt = long_options[match].val;
else
optopt = 0;
--optind;
return (BADARG);
}
} else { /* unknown option */
if (short_too) {
--optind;
return (-1);
}
if (PRINT_ERROR)
warnx(illoptstring, current_argv);
optopt = 0;
return (BADCH);
}
if (idx)
*idx = match;
if (long_options[match].flag) {
*long_options[match].flag = long_options[match].val;
return (0);
} else
return (long_options[match].val);
}
/*
* getopt_internal --
* Parse argc/argv argument vector. Called by user level routines.
*/
static int
getopt_internal(int nargc, char * const *nargv, const char *options,
const struct option *long_options, int *idx, int flags)
{
char *oli; /* option letter list index */
int optchar, short_too;
static int posixly_correct = -1;
if (options == NULL)
return (-1);
/*
* XXX Some GNU programs (like cvs) set optind to 0 instead of
* XXX using optreset. Work around this braindamage.
*/
if (optind == 0)
optind = optreset = 1;
/*
* Disable GNU extensions if POSIXLY_CORRECT is set or options
* string begins with a '+'.
*/
if (posixly_correct == -1 || optreset)
posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
if (*options == '-')
flags |= FLAG_ALLARGS;
else if (posixly_correct || *options == '+')
flags &= ~FLAG_PERMUTE;
if (*options == '+' || *options == '-')
options++;
optarg = NULL;
if (optreset)
nonopt_start = nonopt_end = -1;
start:
if (optreset || !*place) { /* update scanning pointer */
optreset = 0;
if (optind >= nargc) { /* end of argument vector */
place = EMSG;
if (nonopt_end != -1) {
/* do permutation, if we have to */
permute_args(nonopt_start, nonopt_end,
optind, nargv);
optind -= nonopt_end - nonopt_start;
}
else if (nonopt_start != -1) {
/*
* If we skipped non-options, set optind
* to the first of them.
*/
optind = nonopt_start;
}
nonopt_start = nonopt_end = -1;
return (-1);
}
if (*(place = nargv[optind]) != '-' ||
(place[1] == '\0' && strchr(options, '-') == NULL)) {
place = EMSG; /* found non-option */
if (flags & FLAG_ALLARGS) {
/*
* GNU extension:
* return non-option as argument to option 1
*/
optarg = nargv[optind++];
return (INORDER);
}
if (!(flags & FLAG_PERMUTE)) {
/*
* If no permutation wanted, stop parsing
* at first non-option.
*/
return (-1);
}
/* do permutation */
if (nonopt_start == -1)
nonopt_start = optind;
else if (nonopt_end != -1) {
permute_args(nonopt_start, nonopt_end,
optind, nargv);
nonopt_start = optind -
(nonopt_end - nonopt_start);
nonopt_end = -1;
}
optind++;
/* process next argument */
goto start;
}
if (nonopt_start != -1 && nonopt_end == -1)
nonopt_end = optind;
/*
* If we have "-" do nothing, if "--" we are done.
*/
if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
optind++;
place = EMSG;
/*
* We found an option (--), so if we skipped
* non-options, we have to permute.
*/
if (nonopt_end != -1) {
permute_args(nonopt_start, nonopt_end,
optind, nargv);
optind -= nonopt_end - nonopt_start;
}
nonopt_start = nonopt_end = -1;
return (-1);
}
}
/*
* Check long options if:
* 1) we were passed some
* 2) the arg is not just "-"
* 3) either the arg starts with -- we are getopt_long_only()
*/
if (long_options != NULL && place != nargv[optind] &&
(*place == '-' || (flags & FLAG_LONGONLY))) {
short_too = 0;
if (*place == '-')
place++; /* --foo long option */
else if (*place != ':' && strchr(options, *place) != NULL)
short_too = 1; /* could be short option too */
optchar = parse_long_options(nargv, options, long_options,
idx, short_too);
if (optchar != -1) {
place = EMSG;
return (optchar);
}
}
if ((optchar = (int)*place++) == (int)':' ||
(optchar == (int)'-' && *place != '\0') ||
(oli = strchr(options, optchar)) == NULL) {
/*
* If the user specified "-" and '-' isn't listed in
* options, return -1 (non-option) as per POSIX.
* Otherwise, it is an unknown option character (or ':').
*/
if (optchar == (int)'-' && *place == '\0')
return (-1);
if (!*place)
++optind;
if (PRINT_ERROR)
warnx(illoptchar, optchar);
optopt = optchar;
return (BADCH);
}
if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
/* -W long-option */
if (*place) /* no space */
/* NOTHING */;
else if (++optind >= nargc) { /* no arg */
place = EMSG;
if (PRINT_ERROR)
warnx(recargchar, optchar);
optopt = optchar;
return (BADARG);
} else /* white space */
place = nargv[optind];
optchar = parse_long_options(nargv, options, long_options,
idx, 0);
place = EMSG;
return (optchar);
}
if (*++oli != ':') { /* doesn't take argument */
if (!*place)
++optind;
} else { /* takes (optional) argument */
optarg = NULL;
if (*place) /* no white space */
optarg = place;
else if (oli[1] != ':') { /* arg not optional */
if (++optind >= nargc) { /* no arg */
place = EMSG;
if (PRINT_ERROR)
warnx(recargchar, optchar);
optopt = optchar;
return (BADARG);
} else
optarg = nargv[optind];
}
place = EMSG;
++optind;
}
/* dump back option letter */
return (optchar);
}
/*
* getopt --
* Parse argc/argv argument vector.
*
* [eventually this will replace the BSD getopt]
*/
int
getopt(int nargc, char * const *nargv, const char *options)
{
/*
* We don't pass FLAG_PERMUTE to getopt_internal() since
* the BSD getopt(3) (unlike GNU) has never done this.
*
* Furthermore, since many privileged programs call getopt()
* before dropping privileges it makes sense to keep things
* as simple (and bug-free) as possible.
*/
return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
}
#if 0
/*
* getopt_long --
* Parse argc/argv argument vector.
*/
int
getopt_long(int nargc, char * const *nargv, const char *options,
const struct option *long_options, int *idx)
{
return (getopt_internal(nargc, nargv, options, long_options, idx,
FLAG_PERMUTE));
}
/*
* getopt_long_only --
* Parse argc/argv argument vector.
*/
int
getopt_long_only(int nargc, char * const *nargv, const char *options,
const struct option *long_options, int *idx)
{
return (getopt_internal(nargc, nargv, options, long_options, idx,
FLAG_PERMUTE|FLAG_LONGONLY));
}
#endif
#endif /* !defined(HAVE_GETOPT) || !defined(HAVE_OPTRESET) */

View File

@@ -14,6 +14,7 @@
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <arpa/inet.h>
#include <sys/types.h>
#include "compat.h"

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: imsg-buffer.c,v 1.18 2023/12/12 15:47:41 claudio Exp $ */
/* $OpenBSD: imsg-buffer.c,v 1.36 2025/08/25 08:29:49 claudio Exp $ */
/*
* Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org>
@@ -45,25 +45,39 @@
#undef be64toh
#define be64toh ntohll
static int ibuf_realloc(struct ibuf *, size_t);
static void ibuf_enqueue(struct msgbuf *, struct ibuf *);
static void ibuf_dequeue(struct msgbuf *, struct ibuf *);
struct ibufqueue {
TAILQ_HEAD(, ibuf) bufs;
uint32_t queued;
};
struct msgbuf {
struct ibufqueue bufs;
struct ibufqueue rbufs;
char *rbuf;
struct ibuf *rpmsg;
struct ibuf *(*readhdr)(struct ibuf *, void *, int *);
void *rarg;
size_t roff;
size_t hdrsize;
};
static void msgbuf_drain(struct msgbuf *, size_t);
static void ibufq_init(struct ibufqueue *);
#define IBUF_FD_MARK_ON_STACK -2
struct ibuf *
ibuf_open(size_t len)
{
struct ibuf *buf;
if (len == 0) {
errno = EINVAL;
return (NULL);
}
if ((buf = calloc(1, sizeof(struct ibuf))) == NULL)
return (NULL);
if ((buf->buf = calloc(len, 1)) == NULL) {
free(buf);
return (NULL);
if (len > 0) {
if ((buf->buf = calloc(len, 1)) == NULL) {
free(buf);
return (NULL);
}
}
buf->size = buf->max = len;
buf->fd = -1;
@@ -96,39 +110,36 @@ ibuf_dynamic(size_t len, size_t max)
return (buf);
}
static int
ibuf_realloc(struct ibuf *buf, size_t len)
{
unsigned char *b;
/* on static buffers max is eq size and so the following fails */
if (len > SIZE_MAX - buf->wpos || buf->wpos + len > buf->max) {
errno = ERANGE;
return (-1);
}
b = recallocarray(buf->buf, buf->size, buf->wpos + len, 1);
if (b == NULL)
return (-1);
buf->buf = b;
buf->size = buf->wpos + len;
return (0);
}
void *
ibuf_reserve(struct ibuf *buf, size_t len)
{
void *b;
if (len > SIZE_MAX - buf->wpos || buf->max == 0) {
if (len > SIZE_MAX - buf->wpos) {
errno = ERANGE;
return (NULL);
}
if (buf->fd == IBUF_FD_MARK_ON_STACK) {
/* can not grow stack buffers */
errno = EINVAL;
return (NULL);
}
if (buf->wpos + len > buf->size)
if (ibuf_realloc(buf, len) == -1)
if (buf->wpos + len > buf->size) {
unsigned char *nb;
/* check if buffer is allowed to grow */
if (buf->wpos + len > buf->max) {
errno = ERANGE;
return (NULL);
}
nb = realloc(buf->buf, buf->wpos + len);
if (nb == NULL)
return (NULL);
memset(nb + buf->size, 0, buf->wpos + len - buf->size);
buf->buf = nb;
buf->size = buf->wpos + len;
}
b = buf->buf + buf->wpos;
buf->wpos += len;
@@ -140,6 +151,9 @@ ibuf_add(struct ibuf *buf, const void *data, size_t len)
{
void *b;
if (len == 0)
return (0);
if ((b = ibuf_reserve(buf, len)) == NULL)
return (-1);
@@ -153,13 +167,6 @@ ibuf_add_ibuf(struct ibuf *buf, const struct ibuf *from)
return ibuf_add(buf, ibuf_data(from), ibuf_size(from));
}
/* remove after tree is converted */
int
ibuf_add_buf(struct ibuf *buf, const struct ibuf *from)
{
return ibuf_add_ibuf(buf, from);
}
int
ibuf_add_n8(struct ibuf *buf, uint64_t value)
{
@@ -243,12 +250,34 @@ ibuf_add_zero(struct ibuf *buf, size_t len)
{
void *b;
if (len == 0)
return (0);
if ((b = ibuf_reserve(buf, len)) == NULL)
return (-1);
memset(b, 0, len);
return (0);
}
int
ibuf_add_strbuf(struct ibuf *buf, const char *str, size_t len)
{
char *b;
size_t n;
if ((b = ibuf_reserve(buf, len)) == NULL)
return (-1);
n = strlcpy(b, str, len);
if (n >= len) {
/* also covers the case where len == 0 */
errno = EOVERFLOW;
return (-1);
}
memset(b + n, 0, len - n);
return (0);
}
void *
ibuf_seek(struct ibuf *buf, size_t pos, size_t len)
{
@@ -270,6 +299,8 @@ ibuf_set(struct ibuf *buf, size_t pos, const void *data, size_t len)
if ((b = ibuf_seek(buf, pos, len)) == NULL)
return (-1);
if (len == 0)
return (0);
memcpy(b, data, len);
return (0);
}
@@ -352,6 +383,22 @@ ibuf_set_h64(struct ibuf *buf, size_t pos, uint64_t value)
return (ibuf_set(buf, pos, &value, sizeof(value)));
}
int
ibuf_set_maxsize(struct ibuf *buf, size_t max)
{
if (buf->fd == IBUF_FD_MARK_ON_STACK) {
/* can't fiddle with stack buffers */
errno = EINVAL;
return (-1);
}
if (max > buf->max) {
errno = ERANGE;
return (-1);
}
buf->max = max;
return (0);
}
void *
ibuf_data(const struct ibuf *buf)
{
@@ -367,7 +414,8 @@ ibuf_size(const struct ibuf *buf)
size_t
ibuf_left(const struct ibuf *buf)
{
if (buf->max == 0)
/* on stack buffers have no space left */
if (buf->fd == IBUF_FD_MARK_ON_STACK)
return (0);
return (buf->max - buf->wpos);
}
@@ -379,8 +427,8 @@ ibuf_truncate(struct ibuf *buf, size_t len)
buf->wpos = buf->rpos + len;
return (0);
}
if (buf->max == 0) {
/* only allow to truncate down */
if (buf->fd == IBUF_FD_MARK_ON_STACK) {
/* only allow to truncate down for stack buffers */
errno = ERANGE;
return (-1);
}
@@ -396,7 +444,7 @@ ibuf_rewind(struct ibuf *buf)
void
ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf)
{
ibuf_enqueue(msgbuf, buf);
ibufq_push(&msgbuf->bufs, buf);
}
void
@@ -405,7 +453,7 @@ ibuf_from_buffer(struct ibuf *buf, void *data, size_t len)
memset(buf, 0, sizeof(*buf));
buf->buf = data;
buf->size = buf->wpos = len;
buf->fd = -1;
buf->fd = IBUF_FD_MARK_ON_STACK;
}
void
@@ -440,6 +488,24 @@ ibuf_get_ibuf(struct ibuf *buf, size_t len, struct ibuf *new)
return (0);
}
int
ibuf_get_h16(struct ibuf *buf, uint16_t *value)
{
return ibuf_get(buf, value, sizeof(*value));
}
int
ibuf_get_h32(struct ibuf *buf, uint32_t *value)
{
return ibuf_get(buf, value, sizeof(*value));
}
int
ibuf_get_h64(struct ibuf *buf, uint64_t *value)
{
return ibuf_get(buf, value, sizeof(*value));
}
int
ibuf_get_n8(struct ibuf *buf, uint8_t *value)
{
@@ -476,22 +542,39 @@ ibuf_get_n64(struct ibuf *buf, uint64_t *value)
return (rv);
}
int
ibuf_get_h16(struct ibuf *buf, uint16_t *value)
char *
ibuf_get_string(struct ibuf *buf, size_t len)
{
return ibuf_get(buf, value, sizeof(*value));
char *str;
if (ibuf_size(buf) < len) {
errno = EBADMSG;
return (NULL);
}
str = strndup(ibuf_data(buf), len);
if (str == NULL)
return (NULL);
buf->rpos += len;
return (str);
}
int
ibuf_get_h32(struct ibuf *buf, uint32_t *value)
ibuf_get_strbuf(struct ibuf *buf, char *str, size_t len)
{
return ibuf_get(buf, value, sizeof(*value));
}
if (len == 0) {
errno = EINVAL;
return (-1);
}
int
ibuf_get_h64(struct ibuf *buf, uint64_t *value)
{
return ibuf_get(buf, value, sizeof(*value));
if (ibuf_get(buf, str, len) == -1)
return -1;
if (str[len - 1] != '\0') {
str[len - 1] = '\0';
errno = EOVERFLOW;
return -1;
}
return 0;
}
int
@@ -509,20 +592,24 @@ ibuf_skip(struct ibuf *buf, size_t len)
void
ibuf_free(struct ibuf *buf)
{
int save_errno = errno;
if (buf == NULL)
return;
if (buf->max == 0) /* if buf lives on the stack */
abort(); /* abort before causing more harm */
if (buf->fd != -1)
/* if buf lives on the stack abort before causing more harm */
if (buf->fd == IBUF_FD_MARK_ON_STACK)
abort();
if (buf->fd >= 0)
close(buf->fd);
freezero(buf->buf, buf->size);
free(buf);
errno = save_errno;
}
int
ibuf_fd_avail(struct ibuf *buf)
{
return (buf->fd != -1);
return (buf->fd >= 0);
}
int
@@ -530,6 +617,9 @@ ibuf_fd_get(struct ibuf *buf)
{
int fd;
/* negative fds are internal use and equivalent to -1 */
if (buf->fd < 0)
return (-1);
fd = buf->fd;
buf->fd = -1;
return (fd);
@@ -538,15 +628,101 @@ ibuf_fd_get(struct ibuf *buf)
void
ibuf_fd_set(struct ibuf *buf, int fd)
{
if (buf->max == 0) /* if buf lives on the stack */
abort(); /* abort before causing more harm */
if (buf->fd != -1)
/* if buf lives on the stack abort before causing more harm */
if (buf->fd == IBUF_FD_MARK_ON_STACK)
abort();
if (buf->fd >= 0)
close(buf->fd);
buf->fd = fd;
buf->fd = -1;
if (fd >= 0)
buf->fd = fd;
}
struct msgbuf *
msgbuf_new(void)
{
struct msgbuf *msgbuf;
if ((msgbuf = calloc(1, sizeof(*msgbuf))) == NULL)
return (NULL);
ibufq_init(&msgbuf->bufs);
ibufq_init(&msgbuf->rbufs);
return msgbuf;
}
struct msgbuf *
msgbuf_new_reader(size_t hdrsz,
struct ibuf *(*readhdr)(struct ibuf *, void *, int *), void *arg)
{
struct msgbuf *msgbuf;
char *buf;
if (hdrsz == 0 || hdrsz > IBUF_READ_SIZE / 2) {
errno = EINVAL;
return (NULL);
}
if ((buf = malloc(IBUF_READ_SIZE)) == NULL)
return (NULL);
msgbuf = msgbuf_new();
if (msgbuf == NULL) {
free(buf);
return (NULL);
}
msgbuf->rbuf = buf;
msgbuf->hdrsize = hdrsz;
msgbuf->readhdr = readhdr;
msgbuf->rarg = arg;
return (msgbuf);
}
void
msgbuf_free(struct msgbuf *msgbuf)
{
if (msgbuf == NULL)
return;
msgbuf_clear(msgbuf);
free(msgbuf->rbuf);
free(msgbuf);
}
uint32_t
msgbuf_queuelen(struct msgbuf *msgbuf)
{
return ibufq_queuelen(&msgbuf->bufs);
}
void
msgbuf_clear(struct msgbuf *msgbuf)
{
/* write side */
ibufq_flush(&msgbuf->bufs);
/* read side */
ibufq_flush(&msgbuf->rbufs);
msgbuf->roff = 0;
ibuf_free(msgbuf->rpmsg);
msgbuf->rpmsg = NULL;
}
struct ibuf *
msgbuf_get(struct msgbuf *msgbuf)
{
return ibufq_pop(&msgbuf->rbufs);
}
void
msgbuf_concat(struct msgbuf *msgbuf, struct ibufqueue *from)
{
ibufq_concat(&msgbuf->bufs, from);
}
int
ibuf_write(struct msgbuf *msgbuf)
ibuf_write(int fd, struct msgbuf *msgbuf)
{
struct iovec iov[IOV_MAX];
struct ibuf *buf;
@@ -554,70 +730,32 @@ ibuf_write(struct msgbuf *msgbuf)
ssize_t n;
memset(&iov, 0, sizeof(iov));
TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
TAILQ_FOREACH(buf, &msgbuf->bufs.bufs, entry) {
if (i >= IOV_MAX)
break;
iov[i].iov_base = ibuf_data(buf);
iov[i].iov_len = ibuf_size(buf);
i++;
}
if (i == 0)
return (0); /* nothing queued */
again:
if ((n = writev(msgbuf->fd, iov, i)) == -1) {
again:
if ((n = writev(fd, iov, i)) == -1) {
if (errno == EINTR)
goto again;
if (errno == ENOBUFS)
errno = EAGAIN;
if (errno == EAGAIN || errno == ENOBUFS)
/* lets retry later again */
return (0);
return (-1);
}
if (n == 0) { /* connection closed */
errno = 0;
return (0);
}
msgbuf_drain(msgbuf, n);
return (1);
}
void
msgbuf_init(struct msgbuf *msgbuf)
{
msgbuf->queued = 0;
msgbuf->fd = -1;
TAILQ_INIT(&msgbuf->bufs);
}
static void
msgbuf_drain(struct msgbuf *msgbuf, size_t n)
{
struct ibuf *buf, *next;
for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0;
buf = next) {
next = TAILQ_NEXT(buf, entry);
if (n >= ibuf_size(buf)) {
n -= ibuf_size(buf);
ibuf_dequeue(msgbuf, buf);
} else {
buf->rpos += n;
n = 0;
}
}
}
void
msgbuf_clear(struct msgbuf *msgbuf)
{
struct ibuf *buf;
while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL)
ibuf_dequeue(msgbuf, buf);
return (0);
}
int
msgbuf_write(struct msgbuf *msgbuf)
msgbuf_write(int fd, struct msgbuf *msgbuf)
{
struct iovec iov[IOV_MAX];
struct ibuf *buf, *buf0 = NULL;
@@ -633,7 +771,7 @@ msgbuf_write(struct msgbuf *msgbuf)
memset(&iov, 0, sizeof(iov));
memset(&msg, 0, sizeof(msg));
memset(&cmsgbuf, 0, sizeof(cmsgbuf));
TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
TAILQ_FOREACH(buf, &msgbuf->bufs.bufs, entry) {
if (i >= IOV_MAX)
break;
if (i > 0 && buf->fd != -1)
@@ -645,6 +783,9 @@ msgbuf_write(struct msgbuf *msgbuf)
buf0 = buf;
}
if (i == 0)
return (0); /* nothing queued */
msg.msg_iov = iov;
msg.msg_iovlen = i;
@@ -658,20 +799,16 @@ msgbuf_write(struct msgbuf *msgbuf)
*(int *)CMSG_DATA(cmsg) = buf0->fd;
}
again:
if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) {
again:
if ((n = sendmsg(fd, &msg, 0)) == -1) {
if (errno == EINTR)
goto again;
if (errno == ENOBUFS)
errno = EAGAIN;
if (errno == EAGAIN || errno == ENOBUFS)
/* lets retry later again */
return (0);
return (-1);
}
if (n == 0) { /* connection closed */
errno = 0;
return (0);
}
/*
* assumption: fd got sent if sendmsg sent anything
* this works because fds are passed one at a time
@@ -683,28 +820,257 @@ again:
msgbuf_drain(msgbuf, n);
return (0);
}
static int
ibuf_read_process(struct msgbuf *msgbuf, int fd)
{
struct ibuf rbuf, msg;
ssize_t sz;
ibuf_from_buffer(&rbuf, msgbuf->rbuf, msgbuf->roff);
do {
if (msgbuf->rpmsg == NULL) {
if (ibuf_size(&rbuf) < msgbuf->hdrsize)
break;
/* get size from header */
ibuf_from_buffer(&msg, ibuf_data(&rbuf),
msgbuf->hdrsize);
if ((msgbuf->rpmsg = msgbuf->readhdr(&msg,
msgbuf->rarg, &fd)) == NULL)
goto fail;
}
if (ibuf_left(msgbuf->rpmsg) <= ibuf_size(&rbuf))
sz = ibuf_left(msgbuf->rpmsg);
else
sz = ibuf_size(&rbuf);
/* neither call below can fail */
if (ibuf_get_ibuf(&rbuf, sz, &msg) == -1 ||
ibuf_add_ibuf(msgbuf->rpmsg, &msg) == -1)
goto fail;
if (ibuf_left(msgbuf->rpmsg) == 0) {
ibufq_push(&msgbuf->rbufs, msgbuf->rpmsg);
msgbuf->rpmsg = NULL;
}
} while (ibuf_size(&rbuf) > 0);
if (ibuf_size(&rbuf) > 0)
memmove(msgbuf->rbuf, ibuf_data(&rbuf), ibuf_size(&rbuf));
msgbuf->roff = ibuf_size(&rbuf);
if (fd != -1)
close(fd);
return (1);
fail:
/* XXX how to properly clean up is unclear */
if (fd != -1)
close(fd);
return (-1);
}
int
ibuf_read(int fd, struct msgbuf *msgbuf)
{
struct iovec iov;
ssize_t n;
if (msgbuf->rbuf == NULL) {
errno = EINVAL;
return (-1);
}
iov.iov_base = msgbuf->rbuf + msgbuf->roff;
iov.iov_len = IBUF_READ_SIZE - msgbuf->roff;
again:
if ((n = readv(fd, &iov, 1)) == -1) {
if (errno == EINTR)
goto again;
if (errno == EAGAIN)
/* lets retry later again */
return (1);
return (-1);
}
if (n == 0) /* connection closed */
return (0);
msgbuf->roff += n;
/* new data arrived, try to process it */
return (ibuf_read_process(msgbuf, -1));
}
int
msgbuf_read(int fd, struct msgbuf *msgbuf)
{
struct msghdr msg;
struct cmsghdr *cmsg;
union {
struct cmsghdr hdr;
char buf[CMSG_SPACE(sizeof(int) * 1)];
} cmsgbuf;
struct iovec iov;
ssize_t n;
int fdpass = -1;
if (msgbuf->rbuf == NULL) {
errno = EINVAL;
return (-1);
}
memset(&msg, 0, sizeof(msg));
memset(&cmsgbuf, 0, sizeof(cmsgbuf));
iov.iov_base = msgbuf->rbuf + msgbuf->roff;
iov.iov_len = IBUF_READ_SIZE - msgbuf->roff;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = &cmsgbuf.buf;
msg.msg_controllen = sizeof(cmsgbuf.buf);
again:
if ((n = recvmsg(fd, &msg, 0)) == -1) {
if (errno == EINTR)
goto again;
if (errno == EMSGSIZE)
/*
* Not enough fd slots: fd passing failed, retry
* to receive the message without fd.
* imsg_get_fd() will return -1 in that case.
*/
goto again;
if (errno == EAGAIN)
/* lets retry later again */
return (1);
return (-1);
}
if (n == 0) /* connection closed */
return (0);
msgbuf->roff += n;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_RIGHTS) {
int i, j, f;
/*
* We only accept one file descriptor. Due to C
* padding rules, our control buffer might contain
* more than one fd, and we must close them.
*/
j = ((char *)cmsg + cmsg->cmsg_len -
(char *)CMSG_DATA(cmsg)) / sizeof(int);
for (i = 0; i < j; i++) {
f = ((int *)CMSG_DATA(cmsg))[i];
if (i == 0)
fdpass = f;
else
close(f);
}
}
/* we do not handle other ctl data level */
}
/* new data arrived, try to process it */
return (ibuf_read_process(msgbuf, fdpass));
}
static void
msgbuf_drain(struct msgbuf *msgbuf, size_t n)
{
struct ibuf *buf;
while ((buf = TAILQ_FIRST(&msgbuf->bufs.bufs)) != NULL) {
if (n >= ibuf_size(buf)) {
n -= ibuf_size(buf);
TAILQ_REMOVE(&msgbuf->bufs.bufs, buf, entry);
msgbuf->bufs.queued--;
ibuf_free(buf);
} else {
buf->rpos += n;
return;
}
}
}
static void
ibufq_init(struct ibufqueue *bufq)
{
TAILQ_INIT(&bufq->bufs);
bufq->queued = 0;
}
struct ibufqueue *
ibufq_new(void)
{
struct ibufqueue *bufq;
if ((bufq = calloc(1, sizeof(*bufq))) == NULL)
return NULL;
ibufq_init(bufq);
return bufq;
}
void
ibufq_free(struct ibufqueue *bufq)
{
if (bufq == NULL)
return;
ibufq_flush(bufq);
free(bufq);
}
struct ibuf *
ibufq_pop(struct ibufqueue *bufq)
{
struct ibuf *buf;
if ((buf = TAILQ_FIRST(&bufq->bufs)) == NULL)
return NULL;
TAILQ_REMOVE(&bufq->bufs, buf, entry);
bufq->queued--;
return buf;
}
void
ibufq_push(struct ibufqueue *bufq, struct ibuf *buf)
{
/* if buf lives on the stack abort before causing more harm */
if (buf->fd == IBUF_FD_MARK_ON_STACK)
abort();
TAILQ_INSERT_TAIL(&bufq->bufs, buf, entry);
bufq->queued++;
}
uint32_t
msgbuf_queuelen(struct msgbuf *msgbuf)
ibufq_queuelen(struct ibufqueue *bufq)
{
return (msgbuf->queued);
return (bufq->queued);
}
static void
ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf)
void
ibufq_concat(struct ibufqueue *to, struct ibufqueue *from)
{
if (buf->max == 0) /* if buf lives on the stack */
abort(); /* abort before causing more harm */
TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry);
msgbuf->queued++;
to->queued += from->queued;
TAILQ_CONCAT(&to->bufs, &from->bufs, entry);
from->queued = 0;
}
static void
ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf)
void
ibufq_flush(struct ibufqueue *bufq)
{
TAILQ_REMOVE(&msgbuf->bufs, buf, entry);
msgbuf->queued--;
ibuf_free(buf);
struct ibuf *buf;
while ((buf = TAILQ_FIRST(&bufq->bufs)) != NULL) {
TAILQ_REMOVE(&bufq->bufs, buf, entry);
ibuf_free(buf);
}
bufq->queued = 0;
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: imsg.c,v 1.23 2023/12/12 15:47:41 claudio Exp $ */
/* $OpenBSD: imsg.c,v 1.42 2025/06/16 13:56:11 claudio Exp $ */
/*
* Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org>
@@ -22,6 +22,7 @@
#include <sys/uio.h>
#include <errno.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@@ -29,158 +30,156 @@
#include "compat.h"
#include "imsg.h"
struct imsg_fd {
TAILQ_ENTRY(imsg_fd) entry;
int fd;
};
#define IMSG_ALLOW_FDPASS 0x01
#define IMSG_FD_MARK 0x80000000U
int imsg_fd_overhead = 0;
static struct ibuf *imsg_parse_hdr(struct ibuf *, void *, int *);
static int imsg_dequeue_fd(struct imsgbuf *);
void
imsg_init(struct imsgbuf *imsgbuf, int fd)
int
imsgbuf_init(struct imsgbuf *imsgbuf, int fd)
{
msgbuf_init(&imsgbuf->w);
memset(&imsgbuf->r, 0, sizeof(imsgbuf->r));
imsgbuf->fd = fd;
imsgbuf->w.fd = fd;
imsgbuf->w = msgbuf_new_reader(IMSG_HEADER_SIZE, imsg_parse_hdr,
imsgbuf);
if (imsgbuf->w == NULL)
return (-1);
imsgbuf->pid = getpid();
TAILQ_INIT(&imsgbuf->fds);
imsgbuf->maxsize = MAX_IMSGSIZE;
imsgbuf->fd = fd;
imsgbuf->flags = 0;
return (0);
}
ssize_t
imsg_read(struct imsgbuf *imsgbuf)
void
imsgbuf_allow_fdpass(struct imsgbuf *imsgbuf)
{
struct msghdr msg;
struct cmsghdr *cmsg;
union {
struct cmsghdr hdr;
char buf[CMSG_SPACE(sizeof(int) * 1)];
} cmsgbuf;
struct iovec iov;
ssize_t n = -1;
int fd;
struct imsg_fd *ifd;
imsgbuf->flags |= IMSG_ALLOW_FDPASS;
}
memset(&msg, 0, sizeof(msg));
memset(&cmsgbuf, 0, sizeof(cmsgbuf));
iov.iov_base = imsgbuf->r.buf + imsgbuf->r.wpos;
iov.iov_len = sizeof(imsgbuf->r.buf) - imsgbuf->r.wpos;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = &cmsgbuf.buf;
msg.msg_controllen = sizeof(cmsgbuf.buf);
if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL)
return (-1);
again:
if (getdtablecount() + imsg_fd_overhead +
(int)((CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int))
>= getdtablesize()) {
errno = EAGAIN;
free(ifd);
int
imsgbuf_set_maxsize(struct imsgbuf *imsgbuf, uint32_t max)
{
if (max > UINT32_MAX - IMSG_HEADER_SIZE) {
errno = ERANGE;
return (-1);
}
if ((n = recvmsg(imsgbuf->fd, &msg, 0)) == -1) {
if (errno == EINTR)
goto again;
goto fail;
max += IMSG_HEADER_SIZE;
if (max & IMSG_FD_MARK) {
errno = EINVAL;
return (-1);
}
imsgbuf->maxsize = max;
return (0);
}
imsgbuf->r.wpos += n;
int
imsgbuf_read(struct imsgbuf *imsgbuf)
{
if (imsgbuf->flags & IMSG_ALLOW_FDPASS)
return msgbuf_read(imsgbuf->fd, imsgbuf->w);
else
return ibuf_read(imsgbuf->fd, imsgbuf->w);
}
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_RIGHTS) {
int i;
int j;
int
imsgbuf_write(struct imsgbuf *imsgbuf)
{
if (imsgbuf->flags & IMSG_ALLOW_FDPASS)
return msgbuf_write(imsgbuf->fd, imsgbuf->w);
else
return ibuf_write(imsgbuf->fd, imsgbuf->w);
}
/*
* We only accept one file descriptor. Due to C
* padding rules, our control buffer might contain
* more than one fd, and we must close them.
*/
j = ((char *)cmsg + cmsg->cmsg_len -
(char *)CMSG_DATA(cmsg)) / sizeof(int);
for (i = 0; i < j; i++) {
fd = ((int *)CMSG_DATA(cmsg))[i];
if (ifd != NULL) {
ifd->fd = fd;
TAILQ_INSERT_TAIL(&imsgbuf->fds, ifd,
entry);
ifd = NULL;
} else
close(fd);
}
}
/* we do not handle other ctl data level */
int
imsgbuf_flush(struct imsgbuf *imsgbuf)
{
while (imsgbuf_queuelen(imsgbuf) > 0) {
if (imsgbuf_write(imsgbuf) == -1)
return (-1);
}
return (0);
}
fail:
free(ifd);
return (n);
void
imsgbuf_clear(struct imsgbuf *imsgbuf)
{
msgbuf_free(imsgbuf->w);
imsgbuf->w = NULL;
}
uint32_t
imsgbuf_queuelen(struct imsgbuf *imsgbuf)
{
return msgbuf_queuelen(imsgbuf->w);
}
int
imsgbuf_get(struct imsgbuf *imsgbuf, struct imsg *imsg)
{
struct imsg m;
struct ibuf *buf;
if ((buf = msgbuf_get(imsgbuf->w)) == NULL)
return (0);
if (ibuf_get(buf, &m.hdr, sizeof(m.hdr)) == -1)
return (-1);
if (ibuf_size(buf))
m.data = ibuf_data(buf);
else
m.data = NULL;
m.buf = buf;
m.hdr.len &= ~IMSG_FD_MARK;
*imsg = m;
return (1);
}
ssize_t
imsg_get(struct imsgbuf *imsgbuf, struct imsg *imsg)
{
struct imsg m;
size_t av, left, datalen;
int rv;
av = imsgbuf->r.wpos;
if ((rv = imsgbuf_get(imsgbuf, imsg)) != 1)
return rv;
return (imsg_get_len(imsg) + IMSG_HEADER_SIZE);
}
if (IMSG_HEADER_SIZE > av)
int
imsg_ibufq_pop(struct ibufqueue *bufq, struct imsg *imsg)
{
struct imsg m;
struct ibuf *buf;
if ((buf = ibufq_pop(bufq)) == NULL)
return (0);
memcpy(&m.hdr, imsgbuf->r.buf, sizeof(m.hdr));
if (m.hdr.len < IMSG_HEADER_SIZE ||
m.hdr.len > MAX_IMSGSIZE) {
errno = ERANGE;
if (ibuf_get(buf, &m.hdr, sizeof(m.hdr)) == -1)
return (-1);
}
if (m.hdr.len > av)
return (0);
m.fd = -1;
m.buf = NULL;
m.data = NULL;
datalen = m.hdr.len - IMSG_HEADER_SIZE;
imsgbuf->r.rptr = imsgbuf->r.buf + IMSG_HEADER_SIZE;
if (datalen != 0) {
if ((m.buf = ibuf_open(datalen)) == NULL)
return (-1);
if (ibuf_add(m.buf, imsgbuf->r.rptr, datalen) == -1) {
/* this should never fail */
ibuf_free(m.buf);
return (-1);
}
m.data = ibuf_data(m.buf);
}
if (m.hdr.flags & IMSGF_HASFD)
m.fd = imsg_dequeue_fd(imsgbuf);
if (m.hdr.len < av) {
left = av - m.hdr.len;
memmove(&imsgbuf->r.buf, imsgbuf->r.buf + m.hdr.len, left);
imsgbuf->r.wpos = left;
} else
imsgbuf->r.wpos = 0;
if (ibuf_size(buf))
m.data = ibuf_data(buf);
else
m.data = NULL;
m.buf = buf;
m.hdr.len &= ~IMSG_FD_MARK;
*imsg = m;
return (datalen + IMSG_HEADER_SIZE);
return (1);
}
void
imsg_ibufq_push(struct ibufqueue *bufq, struct imsg *imsg)
{
ibuf_rewind(imsg->buf);
ibufq_push(bufq, imsg->buf);
memset(imsg, 0, sizeof(*imsg));
}
int
imsg_get_ibuf(struct imsg *imsg, struct ibuf *ibuf)
{
if (imsg->buf == NULL) {
if (ibuf_size(imsg->buf) == 0) {
errno = EBADMSG;
return (-1);
}
@@ -194,20 +193,29 @@ imsg_get_data(struct imsg *imsg, void *data, size_t len)
errno = EINVAL;
return (-1);
}
if (imsg->buf == NULL || ibuf_size(imsg->buf) != len) {
if (ibuf_size(imsg->buf) != len) {
errno = EBADMSG;
return (-1);
}
return ibuf_get(imsg->buf, data, len);
}
int
imsg_get_buf(struct imsg *imsg, void *data, size_t len)
{
return ibuf_get(imsg->buf, data, len);
}
int
imsg_get_strbuf(struct imsg *imsg, char *str, size_t len)
{
return ibuf_get_strbuf(imsg->buf, str, len);
}
int
imsg_get_fd(struct imsg *imsg)
{
int fd = imsg->fd;
imsg->fd = -1;
return fd;
return ibuf_fd_get(imsg->buf);
}
uint32_t
@@ -219,8 +227,6 @@ imsg_get_id(struct imsg *imsg)
size_t
imsg_get_len(struct imsg *imsg)
{
if (imsg->buf == NULL)
return 0;
return ibuf_size(imsg->buf);
}
@@ -243,15 +249,19 @@ imsg_compose(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
struct ibuf *wbuf;
if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL)
return (-1);
goto fail;
if (imsg_add(wbuf, data, datalen) == -1)
return (-1);
if (ibuf_add(wbuf, data, datalen) == -1)
goto fail;
ibuf_fd_set(wbuf, fd);
imsg_close(imsgbuf, wbuf);
return (1);
fail:
ibuf_free(wbuf);
return (-1);
}
int
@@ -266,16 +276,20 @@ imsg_composev(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
datalen += iov[i].iov_len;
if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL)
return (-1);
goto fail;
for (i = 0; i < iovcnt; i++)
if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1)
return (-1);
if (ibuf_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1)
goto fail;
ibuf_fd_set(wbuf, fd);
imsg_close(imsgbuf, wbuf);
return (1);
fail:
ibuf_free(wbuf);
return (-1);
}
/*
@@ -288,34 +302,30 @@ imsg_compose_ibuf(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id,
{
struct ibuf *hdrbuf = NULL;
struct imsg_hdr hdr;
int save_errno;
if (ibuf_size(buf) + IMSG_HEADER_SIZE > MAX_IMSGSIZE) {
if (ibuf_size(buf) + IMSG_HEADER_SIZE > imsgbuf->maxsize) {
errno = ERANGE;
goto fail;
}
hdr.type = type;
hdr.len = ibuf_size(buf) + IMSG_HEADER_SIZE;
hdr.flags = 0;
hdr.peerid = id;
if ((hdr.pid = pid) == 0)
hdr.pid = imsgbuf->pid;
if ((hdrbuf = ibuf_open(IMSG_HEADER_SIZE)) == NULL)
goto fail;
if (imsg_add(hdrbuf, &hdr, sizeof(hdr)) == -1)
if (ibuf_add(hdrbuf, &hdr, sizeof(hdr)) == -1)
goto fail;
ibuf_close(&imsgbuf->w, hdrbuf);
ibuf_close(&imsgbuf->w, buf);
ibuf_close(imsgbuf->w, hdrbuf);
ibuf_close(imsgbuf->w, buf);
return (1);
fail:
save_errno = errno;
ibuf_free(buf);
ibuf_free(hdrbuf);
errno = save_errno;
return (-1);
}
@@ -326,24 +336,18 @@ int
imsg_forward(struct imsgbuf *imsgbuf, struct imsg *msg)
{
struct ibuf *wbuf;
size_t len = 0;
size_t len;
if (msg->fd != -1) {
close(msg->fd);
msg->fd = -1;
}
if (msg->buf != NULL) {
ibuf_rewind(msg->buf);
len = ibuf_size(msg->buf);
}
ibuf_rewind(msg->buf);
ibuf_skip(msg->buf, sizeof(msg->hdr));
len = ibuf_size(msg->buf);
if ((wbuf = imsg_create(imsgbuf, msg->hdr.type, msg->hdr.peerid,
msg->hdr.pid, len)) == NULL)
return (-1);
if (msg->buf != NULL) {
if (ibuf_add_buf(wbuf, msg->buf) == -1) {
if (len != 0) {
if (ibuf_add_ibuf(wbuf, msg->buf) == -1) {
ibuf_free(wbuf);
return (-1);
}
@@ -361,23 +365,26 @@ imsg_create(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
struct imsg_hdr hdr;
datalen += IMSG_HEADER_SIZE;
if (datalen > MAX_IMSGSIZE) {
if (datalen > imsgbuf->maxsize) {
errno = ERANGE;
return (NULL);
}
hdr.len = 0;
hdr.type = type;
hdr.flags = 0;
hdr.peerid = id;
if ((hdr.pid = pid) == 0)
hdr.pid = imsgbuf->pid;
if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) {
return (NULL);
}
if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1)
return (NULL);
if ((wbuf = ibuf_dynamic(datalen, imsgbuf->maxsize)) == NULL)
goto fail;
if (ibuf_add(wbuf, &hdr, sizeof(hdr)) == -1)
goto fail;
return (wbuf);
fail:
ibuf_free(wbuf);
return (NULL);
}
int
@@ -394,16 +401,13 @@ imsg_add(struct ibuf *msg, const void *data, size_t datalen)
void
imsg_close(struct imsgbuf *imsgbuf, struct ibuf *msg)
{
struct imsg_hdr *hdr;
uint32_t len;
hdr = (struct imsg_hdr *)msg->buf;
hdr->flags &= ~IMSGF_HASFD;
len = ibuf_size(msg);
if (ibuf_fd_avail(msg))
hdr->flags |= IMSGF_HASFD;
hdr->len = ibuf_size(msg);
ibuf_close(&imsgbuf->w, msg);
len |= IMSG_FD_MARK;
(void)ibuf_set_h32(msg, offsetof(struct imsg_hdr, len), len);
ibuf_close(imsgbuf->w, msg);
}
void
@@ -412,37 +416,39 @@ imsg_free(struct imsg *imsg)
ibuf_free(imsg->buf);
}
static int
imsg_dequeue_fd(struct imsgbuf *imsgbuf)
{
int fd;
struct imsg_fd *ifd;
if ((ifd = TAILQ_FIRST(&imsgbuf->fds)) == NULL)
return (-1);
fd = ifd->fd;
TAILQ_REMOVE(&imsgbuf->fds, ifd, entry);
free(ifd);
return (fd);
}
int
imsg_flush(struct imsgbuf *imsgbuf)
imsg_set_maxsize(struct ibuf *msg, size_t max)
{
while (imsgbuf->w.queued)
if (msgbuf_write(&imsgbuf->w) <= 0)
return (-1);
return (0);
if (max > UINT32_MAX - IMSG_HEADER_SIZE) {
errno = ERANGE;
return (-1);
}
return ibuf_set_maxsize(msg, max + IMSG_HEADER_SIZE);
}
void
imsg_clear(struct imsgbuf *imsgbuf)
static struct ibuf *
imsg_parse_hdr(struct ibuf *buf, void *arg, int *fd)
{
int fd;
struct imsgbuf *imsgbuf = arg;
struct imsg_hdr hdr;
struct ibuf *b;
uint32_t len;
msgbuf_clear(&imsgbuf->w);
while ((fd = imsg_dequeue_fd(imsgbuf)) != -1)
close(fd);
if (ibuf_get(buf, &hdr, sizeof(hdr)) == -1)
return (NULL);
len = hdr.len & ~IMSG_FD_MARK;
if (len < IMSG_HEADER_SIZE || len > imsgbuf->maxsize) {
errno = ERANGE;
return (NULL);
}
if ((b = ibuf_open(len)) == NULL)
return (NULL);
if (hdr.len & IMSG_FD_MARK) {
ibuf_fd_set(b, *fd);
*fd = -1;
}
return b;
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: imsg.h,v 1.8 2023/12/12 15:47:41 claudio Exp $ */
/* $OpenBSD: imsg.h,v 1.24 2025/06/05 08:55:07 tb Exp $ */
/*
* Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org>
@@ -23,6 +23,7 @@
#define _IMSG_H_
#include <sys/types.h>
#include <stddef.h>
#define IBUF_READ_SIZE 65535
#define IMSG_HEADER_SIZE sizeof(struct imsg_hdr)
@@ -38,40 +39,26 @@ struct ibuf {
int fd;
};
struct msgbuf {
TAILQ_HEAD(, ibuf) bufs;
uint32_t queued;
int fd;
};
struct ibufqueue;
struct msgbuf;
struct ibuf_read {
unsigned char buf[IBUF_READ_SIZE];
unsigned char *rptr;
size_t wpos;
};
struct imsg_fd;
struct imsgbuf {
TAILQ_HEAD(, imsg_fd) fds;
struct ibuf_read r;
struct msgbuf w;
int fd;
struct msgbuf *w;
pid_t pid;
uint32_t maxsize;
int fd;
int flags;
};
#define IMSGF_HASFD 1
struct imsg_hdr {
uint32_t type;
uint16_t len;
uint16_t flags;
uint32_t len;
uint32_t peerid;
uint32_t pid;
};
struct imsg {
struct imsg_hdr hdr;
int fd;
void *data;
struct ibuf *buf;
};
@@ -82,7 +69,6 @@ struct iovec;
struct ibuf *ibuf_open(size_t);
struct ibuf *ibuf_dynamic(size_t, size_t);
int ibuf_add(struct ibuf *, const void *, size_t);
int ibuf_add_buf(struct ibuf *, const struct ibuf *);
int ibuf_add_ibuf(struct ibuf *, const struct ibuf *);
int ibuf_add_zero(struct ibuf *, size_t);
int ibuf_add_n8(struct ibuf *, uint64_t);
@@ -92,6 +78,7 @@ int ibuf_add_n64(struct ibuf *, uint64_t);
int ibuf_add_h16(struct ibuf *, uint64_t);
int ibuf_add_h32(struct ibuf *, uint64_t);
int ibuf_add_h64(struct ibuf *, uint64_t);
int ibuf_add_strbuf(struct ibuf *, const char *, size_t);
void *ibuf_reserve(struct ibuf *, size_t);
void *ibuf_seek(struct ibuf *, size_t, size_t);
int ibuf_set(struct ibuf *, size_t, const void *, size_t);
@@ -102,6 +89,7 @@ int ibuf_set_n64(struct ibuf *, size_t, uint64_t);
int ibuf_set_h16(struct ibuf *, size_t, uint64_t);
int ibuf_set_h32(struct ibuf *, size_t, uint64_t);
int ibuf_set_h64(struct ibuf *, size_t, uint64_t);
int ibuf_set_maxsize(struct ibuf *, size_t);
void *ibuf_data(const struct ibuf *);
size_t ibuf_size(const struct ibuf *);
size_t ibuf_left(const struct ibuf *);
@@ -119,23 +107,51 @@ int ibuf_get_n64(struct ibuf *, uint64_t *);
int ibuf_get_h16(struct ibuf *, uint16_t *);
int ibuf_get_h32(struct ibuf *, uint32_t *);
int ibuf_get_h64(struct ibuf *, uint64_t *);
char *ibuf_get_string(struct ibuf *, size_t);
int ibuf_get_strbuf(struct ibuf *, char *, size_t);
int ibuf_skip(struct ibuf *, size_t);
void ibuf_free(struct ibuf *);
int ibuf_fd_avail(struct ibuf *);
int ibuf_fd_get(struct ibuf *);
void ibuf_fd_set(struct ibuf *, int);
int ibuf_write(struct msgbuf *);
void msgbuf_init(struct msgbuf *);
struct msgbuf *msgbuf_new(void);
struct msgbuf *msgbuf_new_reader(size_t,
struct ibuf *(*)(struct ibuf *, void *, int *), void *);
void msgbuf_free(struct msgbuf *);
void msgbuf_clear(struct msgbuf *);
void msgbuf_concat(struct msgbuf *, struct ibufqueue *);
uint32_t msgbuf_queuelen(struct msgbuf *);
int msgbuf_write(struct msgbuf *);
int ibuf_write(int, struct msgbuf *);
int msgbuf_write(int, struct msgbuf *);
int ibuf_read(int, struct msgbuf *);
int msgbuf_read(int, struct msgbuf *);
struct ibuf *msgbuf_get(struct msgbuf *);
struct ibufqueue *ibufq_new(void);
void ibufq_free(struct ibufqueue *);
struct ibuf *ibufq_pop(struct ibufqueue *bufq);
void ibufq_push(struct ibufqueue *, struct ibuf *);
uint32_t ibufq_queuelen(struct ibufqueue *);
void ibufq_concat(struct ibufqueue *, struct ibufqueue *);
void ibufq_flush(struct ibufqueue *);
/* imsg.c */
void imsg_init(struct imsgbuf *, int);
ssize_t imsg_read(struct imsgbuf *);
int imsgbuf_init(struct imsgbuf *, int);
void imsgbuf_allow_fdpass(struct imsgbuf *imsgbuf);
int imsgbuf_set_maxsize(struct imsgbuf *, uint32_t);
int imsgbuf_read(struct imsgbuf *);
int imsgbuf_write(struct imsgbuf *);
int imsgbuf_flush(struct imsgbuf *);
void imsgbuf_clear(struct imsgbuf *);
uint32_t imsgbuf_queuelen(struct imsgbuf *);
int imsgbuf_get(struct imsgbuf *, struct imsg *);
ssize_t imsg_get(struct imsgbuf *, struct imsg *);
int imsg_ibufq_pop(struct ibufqueue *, struct imsg *);
void imsg_ibufq_push(struct ibufqueue *, struct imsg *);
int imsg_get_ibuf(struct imsg *, struct ibuf *);
int imsg_get_data(struct imsg *, void *, size_t);
int imsg_get_buf(struct imsg *, void *, size_t);
int imsg_get_strbuf(struct imsg *, char *, size_t);
int imsg_get_fd(struct imsg *);
uint32_t imsg_get_id(struct imsg *);
size_t imsg_get_len(struct imsg *);
@@ -152,7 +168,6 @@ struct ibuf *imsg_create(struct imsgbuf *, uint32_t, uint32_t, pid_t, size_t);
int imsg_add(struct ibuf *, const void *, size_t);
void imsg_close(struct imsgbuf *, struct ibuf *);
void imsg_free(struct imsg *);
int imsg_flush(struct imsgbuf *);
void imsg_clear(struct imsgbuf *);
int imsg_set_maxsize(struct ibuf *, size_t);
#endif

View File

@@ -14,6 +14,7 @@
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <arpa/inet.h>
#include <sys/types.h>
#include "compat.h"

View File

@@ -32,8 +32,8 @@
* @(#)queue.h 8.5 (Berkeley) 8/20/94
*/
#ifndef _SYS_QUEUE_H_
#define _SYS_QUEUE_H_
#ifndef _COMPAT_QUEUE_H_
#define _COMPAT_QUEUE_H_
/*
* This file defines five types of data structures: singly-linked lists,
@@ -530,4 +530,4 @@ struct { \
} \
} while (0)
#endif /* !_SYS_QUEUE_H_ */
#endif /* !_COMPAT_QUEUE_H_ */

View File

@@ -17,6 +17,7 @@
#include <sys/types.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "compat.h"

View File

@@ -24,10 +24,17 @@
#include <systemd/sd-login.h>
#include <systemd/sd-id128.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "tmux.h"
#ifndef SD_ID128_UUID_FORMAT_STR
#define SD_ID128_UUID_FORMAT_STR \
"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x"
#endif
int
systemd_activated(void)
{
@@ -68,16 +75,49 @@ fail:
return (-1);
}
int
systemd_move_pid_to_new_cgroup(pid_t pid, char **cause)
struct systemd_job_watch {
const char *path;
int done;
};
static int
job_removed_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus_message *m = NULL, *reply = NULL;
sd_bus *bus = NULL;
char *name, *desc, *slice;
sd_id128_t uuid;
int r;
pid_t parent_pid;
struct systemd_job_watch *watch = userdata;
const char *path = NULL;
uint32_t id;
int r;
/* This handler could be called during the sd_bus_call. */
if (watch->path == NULL)
return 0;
r = sd_bus_message_read(m, "uo", &id, &path);
if (r < 0)
return (r);
if (strcmp(path, watch->path) == 0)
watch->done = 1;
return (0);
}
int
systemd_move_to_new_cgroup(char **cause)
{
sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus_message *m = NULL, *reply = NULL;
sd_bus *bus = NULL;
sd_bus_slot *slot = NULL;
char *name, *desc, *slice;
sd_id128_t uuid;
int r;
uint64_t elapsed_usec;
pid_t pid, parent_pid;
struct timeval start, now;
struct systemd_job_watch watch = {};
gettimeofday(&start, NULL);
/* Connect to the session bus. */
r = sd_bus_default_user(&bus);
@@ -87,6 +127,20 @@ systemd_move_pid_to_new_cgroup(pid_t pid, char **cause)
goto finish;
}
/* Start watching for JobRemoved events */
r = sd_bus_match_signal(bus, &slot,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"JobRemoved",
job_removed_handler,
&watch);
if (r < 0) {
xasprintf(cause, "failed to create match signal: %s",
strerror(-r));
goto finish;
}
/* Start building the method call. */
r = sd_bus_message_new_method_call(bus, &m,
"org.freedesktop.systemd1",
@@ -131,7 +185,8 @@ systemd_move_pid_to_new_cgroup(pid_t pid, char **cause)
goto finish;
}
parent_pid = getpid();
pid = getpid();
parent_pid = getppid();
xasprintf(&desc, "tmux child pane %ld launched by process %ld",
(long)pid, (long)parent_pid);
r = sd_bus_message_append(m, "(sv)", "Description", "s", desc);
@@ -142,6 +197,17 @@ systemd_move_pid_to_new_cgroup(pid_t pid, char **cause)
goto finish;
}
/*
* Make sure that the session shells are terminated with SIGHUP since
* bash and friends tend to ignore SIGTERM.
*/
r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", 1);
if (r < 0) {
xasprintf(cause, "failed to append to properties: %s",
strerror(-r));
goto finish;
}
/*
* Inherit the slice from the parent process, or default to
* "app-tmux.slice" if that fails.
@@ -205,10 +271,55 @@ systemd_move_pid_to_new_cgroup(pid_t pid, char **cause)
goto finish;
}
/* Get the job (object path) from the reply */
r = sd_bus_message_read(reply, "o", &watch.path);
if (r < 0) {
xasprintf(cause, "failed to parse method reply: %s",
strerror(-r));
goto finish;
}
while (!watch.done) {
/* Process events including callbacks. */
r = sd_bus_process(bus, NULL);
if (r < 0) {
xasprintf(cause,
"failed waiting for cgroup allocation: %s",
strerror(-r));
goto finish;
}
/*
* A positive return means we handled an event and should keep
* processing; zero indicates no events available, so wait.
*/
if (r > 0)
continue;
gettimeofday(&now, NULL);
elapsed_usec = (now.tv_sec - start.tv_sec) * 1000000 +
now.tv_usec - start.tv_usec;
if (elapsed_usec >= 1000000) {
xasprintf(cause,
"timeout waiting for cgroup allocation");
goto finish;
}
r = sd_bus_wait(bus, 1000000 - elapsed_usec);
if (r < 0) {
xasprintf(cause,
"failed waiting for cgroup allocation: %s",
strerror(-r));
goto finish;
}
}
finish:
sd_bus_error_free(&error);
sd_bus_message_unref(m);
sd_bus_message_unref(reply);
sd_bus_slot_unref(slot);
sd_bus_unref(bus);
return (r);

View File

@@ -48,7 +48,7 @@ utf8proc_mbtowc(wchar_t *pwc, const char *s, size_t n)
* *pwc == -1 indicates invalid codepoint
* slen < 0 indicates an error
*/
slen = utf8proc_iterate(s, n, pwc);
slen = utf8proc_iterate(s, n, (utf8proc_int32_t*)pwc);
if (*pwc == (wchar_t)-1 || slen < 0)
return (-1);
return (slen);

View File

@@ -1,6 +1,6 @@
# configure.ac
AC_INIT([tmux], 3.4)
AC_INIT([tmux], next-3.7)
AC_PREREQ([2.60])
AC_CONFIG_AUX_DIR(etc)
@@ -56,13 +56,23 @@ AC_USE_SYSTEM_EXTENSIONS
test "$sysconfdir" = '${prefix}/etc' && sysconfdir=/etc
# Is this --enable-debug?
case "x$VERSION" in xnext*) enable_debug=yes;; esac
AC_ARG_ENABLE(
debug,
AS_HELP_STRING(--enable-debug, enable debug build flags),
,
[case "x$VERSION" in xnext*) enable_debug=yes;; esac]
)
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 a static build?
AC_ARG_ENABLE(
static,
@@ -218,7 +228,7 @@ AC_SEARCH_LIBS(clock_gettime, rt)
# musl does not set optarg to NULL for flags without arguments (although it is
# not required to, but it is helpful) 3) there are probably other weird
# implementations.
AC_LIBOBJ(getopt)
AC_LIBOBJ(getopt_long)
# Look for libevent. Try libevent_core or libevent with pkg-config first then
# look for the library.
@@ -453,13 +463,32 @@ fi
# Enable sixel support.
AC_ARG_ENABLE(
sixel,
AS_HELP_STRING(--enable-sixel, enable sixel images)
sixel-images,
AS_HELP_STRING(--enable-sixel-images, enable sixel images)
)
if test "x$enable_sixel" = xyes; then
AC_DEFINE(ENABLE_SIXEL)
AC_ARG_ENABLE(
sixel,
AS_HELP_STRING(--enable-sixel support),
[
enable_sixel_images="$enableval"
]
)
if test "x$enable_sixel_images" = xyes; then
AC_DEFINE(ENABLE_SIXEL_IMAGES)
fi
AM_CONDITIONAL(ENABLE_SIXEL, [test "x$enable_sixel" = xyes])
AM_CONDITIONAL(ENABLE_SIXEL_IMAGES, [test "x$enable_sixel_images" = xyes])
# Enable kitty graphics protocol support.
AC_ARG_ENABLE(
kitty-images,
AS_HELP_STRING(--enable-kitty-images, enable kitty terminal graphics)
)
if test "x$enable_kitty_images" = xyes; then
AC_DEFINE(ENABLE_KITTY_IMAGES)
fi
AM_CONDITIONAL(ENABLE_KITTY_IMAGES, [test "x$enable_kitty_images" = xyes])
# Check for b64_ntop. If we have b64_ntop, we assume b64_pton as well.
AC_MSG_CHECKING(for b64_ntop)
@@ -548,6 +577,24 @@ if test "x$found_malloc_trim" = xyes; then
AC_DEFINE(HAVE_MALLOC_TRIM)
fi
# Build against jemalloc if requested.
AC_ARG_ENABLE(
jemalloc,
AS_HELP_STRING(--enable-jemalloc, use jemalloc if it is installed)
)
if test "x$enable_jemalloc" = xyes; then
PKG_CHECK_MODULES(
JEMALLOC,
jemalloc,
[
AM_CPPFLAGS="$JEMALLOC_CFLAGS $AM_CPPFLAGS"
CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBS $JEMALLOC_LIBS"
],
AC_MSG_ERROR("jemalloc not found")
)
fi
# Check for CMSG_DATA. On some platforms like HP-UX this requires UNIX 95
# (_XOPEN_SOURCE and _XOPEN_SOURCE_EXTENDED) (see xopen_networking(7)). On
# others, UNIX 03 (_XOPEN_SOURCE 600, see standards(7) on Solaris).
@@ -619,9 +666,9 @@ else
AC_LIBOBJ(err)
fi
# Look for imsg_init in libutil.
AC_SEARCH_LIBS(imsg_init, util, found_imsg_init=yes, found_imsg_init=no)
if test "x$found_imsg_init" = xyes; then
# Look for imsg_add in libutil.
AC_SEARCH_LIBS(imsg_add, util, found_imsg_add=yes, found_imsg_add=no)
if test "x$found_imsg_add" = xyes; then
AC_DEFINE(HAVE_IMSG)
else
AC_LIBOBJ(imsg)
@@ -926,8 +973,13 @@ case "$host_os" in
MANFORMAT=mdoc
;;
*)
# Solaris 2.0 to 11.3 use AT&T nroff.
MANFORMAT=man
if test `uname -o 2>/dev/null` = illumos; then
# Illumos uses mandoc.
MANFORMAT=mdoc
else
# Solaris 2.0 to 11.3 use AT&T nroff.
MANFORMAT=man
fi
;;
esac
;;
@@ -958,22 +1010,22 @@ AM_CONDITIONAL(IS_NETBSD, test "x$PLATFORM" = xnetbsd)
AM_CONDITIONAL(IS_OPENBSD, test "x$PLATFORM" = xopenbsd)
AM_CONDITIONAL(IS_SUNOS, test "x$PLATFORM" = xsunos)
AM_CONDITIONAL(IS_HPUX, test "x$PLATFORM" = xhpux)
AM_CONDITIONAL(IS_CYGWIN, test "x$PLATFORM" = xcygwin)
AM_CONDITIONAL(IS_HAIKU, test "x$PLATFORM" = xhaiku)
AM_CONDITIONAL(IS_UNKNOWN, test "x$PLATFORM" = xunknown)
# Set the default lock command
DEFAULT_LOCK_CMD="lock -np"
AC_MSG_CHECKING(lock-command)
if test "x$PLATFORM" = xlinux; then
AC_CHECK_PROG(found_vlock, vlock, yes, no)
if test "x$found_vlock" = xyes; then
DEFAULT_LOCK_CMD="vlock"
fi
fi
AC_MSG_CHECKING(lock-command)
AC_MSG_RESULT($DEFAULT_LOCK_CMD)
AC_SUBST(DEFAULT_LOCK_CMD)
# Save our CFLAGS/CPPFLAGS/LDFLAGS for the Makefile and restore the old user
# variables.
AC_SUBST(AM_CPPFLAGS)

View File

@@ -51,29 +51,24 @@ control_notify_window_layout_changed(struct window *w)
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)
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);
if (winlink_find_by_window_id(&s->windows, w->id) != NULL)
control_write(c, "%s", cp);
free(cp);
}
}
free(cp);
}
void

204
control.c
View File

@@ -610,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;
size_t new_size, start;
u_int i;
if (message == NULL) {
@@ -629,10 +629,16 @@ 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
evbuffer_add_printf(message, "%c", 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);
}
}
window_pane_update_used_data(wp, &cp->offset, size);
return (message);
@@ -840,15 +846,13 @@ control_stop(struct client *c)
/* Check session subscription. */
static void
control_check_subs_session(struct client *c, struct control_sub *csub)
control_check_subs_session(struct client *c, struct control_sub *csub,
struct format_tree *ft)
{
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);
@@ -909,48 +913,38 @@ control_check_subs_pane(struct client *c, struct control_sub *csub)
}
}
/* Check all panes subscription. */
/* Check all-panes subscription for a pane. */
static void
control_check_subs_all_panes(struct client *c, struct control_sub *csub)
control_check_subs_all_panes_one(struct client *c, struct control_sub *csub,
struct format_tree *ft, struct winlink *wl, struct window_pane *wp)
{
struct session *s = c->session;
struct window_pane *wp;
struct window *w;
struct winlink *wl;
struct format_tree *ft;
struct window *w = wl->window;
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);
value = format_expand(ft, csub->format);
find.pane = wp->id;
find.idx = wl->idx;
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;
}
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);
return;
}
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. */
@@ -999,45 +993,38 @@ control_check_subs_window(struct client *c, struct control_sub *csub)
}
}
/* Check all windows subscription. */
/* Check all-windows subscription for a window. */
static void
control_check_subs_all_windows(struct client *c, struct control_sub *csub)
control_check_subs_all_windows_one(struct client *c, struct control_sub *csub,
struct format_tree *ft, struct winlink *wl)
{
struct session *s = c->session;
struct window *w;
struct winlink *wl;
struct format_tree *ft;
struct window *w = wl->window;
char *value;
struct control_sub_window *csw, find;
RB_FOREACH(wl, winlinks, &s->windows) {
w = wl->window;
value = format_expand(ft, csub->format);
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;
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;
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);
return;
}
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. */
@@ -1047,30 +1034,91 @@ 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 session *s = c->session;
struct format_tree *ft;
struct winlink *wl;
struct window_pane *wp;
struct timeval tv = { .tv_sec = 1 };
int have_session = 0, have_all_panes = 0;
int have_all_windows = 0;
log_debug("%s: timer fired", __func__);
evtimer_add(&cs->subs_timer, &tv);
RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1) {
/* Find which subscription types are present. */
RB_FOREACH(csub, control_subs, &cs->subs) {
switch (csub->type) {
case CONTROL_SUB_SESSION:
control_check_subs_session(c, csub);
break;
case CONTROL_SUB_PANE:
control_check_subs_pane(c, csub);
have_session = 1;
break;
case CONTROL_SUB_ALL_PANES:
control_check_subs_all_panes(c, csub);
have_all_panes = 1;
break;
case CONTROL_SUB_ALL_WINDOWS:
have_all_windows = 1;
break;
default:
break;
}
}
/* Check session subscriptions. */
if (have_session) {
ft = format_create_defaults(NULL, c, s, NULL, NULL);
RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1) {
if (csub->type == CONTROL_SUB_SESSION)
control_check_subs_session(c, csub, ft);
}
format_free(ft);
}
/* Check pane and window subscriptions. */
RB_FOREACH_SAFE(csub, control_subs, &cs->subs, csub1) {
switch (csub->type) {
case CONTROL_SUB_PANE:
control_check_subs_pane(c, csub);
break;
case CONTROL_SUB_WINDOW:
control_check_subs_window(c, csub);
break;
case CONTROL_SUB_SESSION:
case CONTROL_SUB_ALL_PANES:
case CONTROL_SUB_ALL_WINDOWS:
control_check_subs_all_windows(c, csub);
break;
}
}
/* Check all-panes subscriptions. */
if (have_all_panes) {
RB_FOREACH(wl, winlinks, &s->windows) {
TAILQ_FOREACH(wp, &wl->window->panes, entry) {
ft = format_create_defaults(NULL, c, s, wl, wp);
RB_FOREACH_SAFE(csub, control_subs, &cs->subs,
csub1) {
if (csub->type != CONTROL_SUB_ALL_PANES)
continue;
control_check_subs_all_panes_one(c,
csub, ft, wl, wp);
}
format_free(ft);
}
}
}
/* Check all-windows subscriptions. */
if (have_all_windows) {
RB_FOREACH(wl, winlinks, &s->windows) {
ft = format_create_defaults(NULL, c, s, wl, NULL);
RB_FOREACH_SAFE(csub, control_subs, &cs->subs,
csub1) {
if (csub->type != CONTROL_SUB_ALL_WINDOWS)
continue;
control_check_subs_all_windows_one(c, csub, ft,
wl);
}
format_free(ft);
}
}
}
/* Add a subscription. */

View File

@@ -57,6 +57,9 @@ 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);
@@ -262,8 +265,15 @@ environ_for_session(struct session *s, int no_TERM)
environ_set(env, "TERM", 0, "%s", value);
environ_set(env, "TERM_PROGRAM", 0, "%s", "tmux");
environ_set(env, "TERM_PROGRAM_VERSION", 0, "%s", getversion());
environ_set(env, "COLORTERM", 0, "truecolor");
}
#ifdef HAVE_SYSTEMD
environ_clear(env, "LISTEN_PID");
environ_clear(env, "LISTEN_FDS");
environ_clear(env, "LISTEN_FDNAMES");
#endif
if (s != NULL)
idx = s->id;
else

View File

@@ -14,7 +14,7 @@ set -g status-bg red
%endif
# Enable RGB colour if running in xterm(1)
set-option -sa terminal-overrides ",xterm*:Tc"
set-option -sa terminal-features ",xterm*:RGB"
# Change the default $TERM to tmux-256color
set -g default-terminal "tmux-256color"

24
file.c
View File

@@ -41,13 +41,22 @@ RB_GENERATE(client_files, client_file, entry, file_cmp);
static char *
file_get_path(struct client *c, const char *file)
{
char *path;
const char *home;
char *path, *full_path;
if (*file == '/')
if (strncmp(file, "~/", 2) != 0)
path = xstrdup(file);
else
xasprintf(&path, "%s/%s", server_client_get_cwd(c, NULL), file);
return (path);
else {
home = find_home();
if (home == NULL)
home = "";
xasprintf(&path, "%s%s", home, file + 1);
}
if (*path == '/')
return (path);
xasprintf(&full_path, "%s/%s", server_client_get_cwd(c, NULL), path);
free(path);
return (full_path);
}
/* Tree comparison function. */
@@ -361,7 +370,7 @@ file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
size_t msglen;
int fd = -1;
u_int stream = file_next_stream++;
FILE *f;
FILE *f = NULL;
size_t size;
char buffer[BUFSIZ];
@@ -401,7 +410,6 @@ file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
cf->error = EIO;
goto done;
}
fclose(f);
goto done;
}
@@ -424,6 +432,8 @@ skip:
return cf;
done:
if (f != NULL)
fclose(f);
file_fire_done(cf);
return NULL;
}

View File

@@ -719,7 +719,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
int focus_start = -1, focus_end = -1;
int list_state = -1, fill = -1, even;
enum style_align list_align = STYLE_ALIGN_DEFAULT;
struct grid_cell gc, current_default;
struct grid_cell gc, current_default, base_default;
struct style sy, saved_sy;
struct utf8_data *ud = &sy.gc.data;
const char *cp, *end;
@@ -729,7 +729,9 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
struct format_ranges frs;
struct style_range *sr;
memcpy(&base_default, base, sizeof base_default);
memcpy(&current_default, base, sizeof current_default);
base = &base_default;
style_set(&sy, &current_default);
TAILQ_INIT(&frs);
log_debug("%s: %s", __func__, expanded);
@@ -847,6 +849,12 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
} else if (sy.default_type == STYLE_DEFAULT_POP) {
memcpy(&current_default, base, sizeof current_default);
sy.default_type = STYLE_DEFAULT_BASE;
} else if (sy.default_type == STYLE_DEFAULT_SET) {
memcpy(&base_default, &saved_sy.gc,
sizeof base_default);
memcpy(&current_default, &saved_sy.gc,
sizeof current_default);
sy.default_type = STYLE_DEFAULT_BASE;
}
/* Check the list state. */
@@ -1096,8 +1104,6 @@ format_width(const char *expanded)
more = utf8_append(&ud, *cp);
if (more == UTF8_DONE)
width += ud.width;
else
cp -= ud.have;
} else if (*cp > 0x1f && *cp < 0x7f) {
width++;
cp++;

838
format.c

File diff suppressed because it is too large Load Diff

View File

@@ -35,7 +35,7 @@ LLVMFuzzerTestOneInput(const u_char *data, size_t size)
int error;
/*
* Since AFL doesn't support -max_len paramenter we have to
* Since AFL doesn't support -max_len parameter we have to
* discard long inputs manually.
*/
if (size > FUZZER_MAXLEN)
@@ -44,7 +44,7 @@ LLVMFuzzerTestOneInput(const u_char *data, size_t size)
w = window_create(PANE_WIDTH, PANE_HEIGHT, 0, 0);
wp = window_add_pane(w, NULL, 0, 0);
bufferevent_pair_new(libevent, BEV_OPT_CLOSE_ON_FREE, vpty);
wp->ictx = input_init(wp, vpty[0], NULL);
wp->ictx = input_init(wp, vpty[0], NULL, NULL);
window_add_ref(w, __func__);
wp->fd = open("/dev/null", O_WRONLY);

View File

@@ -180,19 +180,14 @@ grid_reader_handle_wrap(struct grid_reader *gr, u_int *xx, u_int *yy)
int
grid_reader_in_set(struct grid_reader *gr, const char *set)
{
struct grid_cell gc;
grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
if (gc.flags & GRID_FLAG_PADDING)
return (0);
return (utf8_cstrhas(set, &gc.data));
return (grid_in_set(gr->gd, gr->cx, gr->cy, set));
}
/* Move cursor to the start of the next word. */
void
grid_reader_cursor_next_word(struct grid_reader *gr, const char *separators)
{
u_int xx, yy;
u_int xx, yy, width;
/* Do not break up wrapped words. */
if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED)
@@ -229,8 +224,8 @@ grid_reader_cursor_next_word(struct grid_reader *gr, const char *separators)
}
}
while (grid_reader_handle_wrap(gr, &xx, &yy) &&
grid_reader_in_set(gr, WHITESPACE))
gr->cx++;
(width = grid_reader_in_set(gr, WHITESPACE)))
gr->cx += width;
}
/* Move cursor to the end of the next word. */
@@ -338,6 +333,20 @@ grid_reader_cursor_previous_word(struct grid_reader *gr, const char *separators,
gr->cy = oldy;
}
/* Compare grid cell to UTF-8 data. Return 1 if equal, 0 if not. */
static int
grid_reader_cell_equals_data(const struct grid_cell *gc,
const struct utf8_data *ud)
{
if (gc->flags & GRID_FLAG_PADDING)
return (0);
if (gc->flags & GRID_FLAG_TAB && ud->size == 1 && *ud->data == '\t')
return (1);
if (gc->data.size != ud->size)
return (0);
return (memcmp(gc->data.data, ud->data, gc->data.size) == 0);
}
/* Jump forward to character. */
int
grid_reader_cursor_jump(struct grid_reader *gr, const struct utf8_data *jc)
@@ -352,9 +361,7 @@ grid_reader_cursor_jump(struct grid_reader *gr, const struct utf8_data *jc)
xx = grid_line_length(gr->gd, py);
while (px < xx) {
grid_get_cell(gr->gd, px, py, &gc);
if (!(gc.flags & GRID_FLAG_PADDING) &&
gc.data.size == jc->size &&
memcmp(gc.data.data, jc->data, gc.data.size) == 0) {
if (grid_reader_cell_equals_data(&gc, jc)) {
gr->cx = px;
gr->cy = py;
return (1);
@@ -382,9 +389,7 @@ grid_reader_cursor_jump_back(struct grid_reader *gr, const struct utf8_data *jc)
for (py = gr->cy + 1; py > 0; py--) {
for (px = xx; px > 0; px--) {
grid_get_cell(gr->gd, px - 1, py - 1, &gc);
if (!(gc.flags & GRID_FLAG_PADDING) &&
gc.data.size == jc->size &&
memcmp(gc.data.data, jc->data, gc.data.size) == 0) {
if (grid_reader_cell_equals_data(&gc, jc)) {
gr->cx = px - 1;
gr->cy = py - 1;
return (1);
@@ -415,7 +420,9 @@ grid_reader_cursor_back_to_indentation(struct grid_reader *gr)
xx = grid_line_length(gr->gd, py);
for (px = 0; px < xx; px++) {
grid_get_cell(gr->gd, px, py, &gc);
if (gc.data.size != 1 || *gc.data.data != ' ') {
if ((gc.data.size != 1 || *gc.data.data != ' ') &&
~gc.flags & GRID_FLAG_TAB &&
~gc.flags & GRID_FLAG_PADDING) {
gr->cx = px;
gr->cy = py;
return;

View File

@@ -82,7 +82,7 @@ grid_view_clear_history(struct grid *gd, u_int bg)
/* Scroll the lines into the history. */
for (yy = 0; yy < last; yy++) {
grid_collect_history(gd);
grid_collect_history(gd, 0);
grid_scroll_history(gd, bg);
}
if (last < gd->sy)
@@ -107,7 +107,7 @@ grid_view_scroll_region_up(struct grid *gd, u_int rupper, u_int rlower,
u_int bg)
{
if (gd->flags & GRID_HISTORY) {
grid_collect_history(gd);
grid_collect_history(gd, 0);
if (rupper == 0 && rlower == gd->sy - 1)
grid_scroll_history(gd, bg);
else {
@@ -173,7 +173,7 @@ grid_view_delete_lines(struct grid *gd, u_int py, u_int ny, u_int bg)
sy = grid_view_y(gd, gd->sy);
grid_move_lines(gd, py, py + ny, sy - py - ny, bg);
grid_clear(gd, 0, sy - ny, gd->sx, py + ny - (sy - ny), bg);
grid_clear(gd, 0, sy - ny, gd->sx, ny, bg);
}
/* Delete lines inside scroll region. */
@@ -221,7 +221,7 @@ grid_view_delete_cells(struct grid *gd, u_int px, u_int py, u_int nx, u_int bg)
sx = grid_view_x(gd, gd->sx);
grid_move_cells(gd, px, px + nx, py, sx - px - nx, bg);
grid_clear(gd, sx - nx, py, px + nx - (sx - nx), 1, bg);
grid_clear(gd, sx - nx, py, nx, 1, bg);
}
/* Convert cells into a string. */

116
grid.c
View File

@@ -84,14 +84,16 @@ grid_need_extended_cell(const struct grid_cell_entry *gce,
return (1);
if (gc->attr > 0xff)
return (1);
if (gc->data.size != 1 || gc->data.width != 1)
if (gc->data.size > 1 || gc->data.width > 1)
return (1);
if ((gc->fg & COLOUR_FLAG_RGB) || (gc->bg & COLOUR_FLAG_RGB))
return (1);
if (gc->us != 0) /* only supports 256 or RGB */
if (gc->us != 8) /* only supports 256 or RGB */
return (1);
if (gc->link != 0)
return (1);
if (gc->flags & GRID_FLAG_TAB)
return (1);
return (0);
}
@@ -124,7 +126,10 @@ grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
fatalx("offset too big");
gl->flags |= GRID_LINE_EXTENDED;
utf8_from_data(&gc->data, &uc);
if (gc->flags & GRID_FLAG_TAB)
uc = gc->data.width;
else
utf8_from_data(&gc->data, &uc);
gee = &gl->extddata[gce->offset];
gee->data = uc;
@@ -200,9 +205,17 @@ grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg)
struct grid_line *gl = &gd->linedata[py];
struct grid_cell_entry *gce = &gl->celldata[px];
struct grid_extd_entry *gee;
u_int old_offset = gce->offset;
int had_extd = (gce->flags & GRID_FLAG_EXTENDED);
memcpy(gce, &grid_cleared_entry, sizeof *gce);
if (bg != 8) {
if (had_extd && old_offset < gl->extdsize) {
gce->flags |= GRID_FLAG_EXTENDED;
gce->offset = old_offset;
gee = grid_extended_cell(gl, gce, &grid_cleared_cell);
if (bg != 8)
gee->bg = bg;
} else if (bg != 8) {
if (bg & COLOUR_FLAG_RGB) {
grid_get_extended_cell(gl, gce, gce->flags);
gee = grid_extended_cell(gl, gce, &grid_cleared_cell);
@@ -230,9 +243,13 @@ grid_check_y(struct grid *gd, const char *from, u_int py)
int
grid_cells_look_equal(const struct grid_cell *gc1, const struct grid_cell *gc2)
{
int flags1 = gc1->flags, flags2 = gc2->flags;
if (gc1->fg != gc2->fg || gc1->bg != gc2->bg)
return (0);
if (gc1->attr != gc2->attr || gc1->flags != gc2->flags)
if (gc1->attr != gc2->attr)
return (0);
if ((flags1 & ~GRID_FLAG_CLEARED) != (flags2 & ~GRID_FLAG_CLEARED))
return (0);
if (gc1->link != gc2->link)
return (0);
@@ -252,6 +269,17 @@ grid_cells_equal(const struct grid_cell *gc1, const struct grid_cell *gc2)
return (memcmp(gc1->data.data, gc2->data.data, gc1->data.size) == 0);
}
/* Set grid cell to a tab. */
void
grid_set_tab(struct grid_cell *gc, u_int width)
{
memset(gc->data.data, 0, sizeof gc->data.data);
gc->flags |= GRID_FLAG_TAB;
gc->flags &= ~GRID_FLAG_PADDING;
gc->data.width = gc->data.size = gc->data.have = width;
memset(gc->data.data, ' ', gc->data.size);
}
/* Free one line. */
static void
grid_free_line(struct grid *gd, u_int py)
@@ -341,9 +369,13 @@ grid_compare(struct grid *ga, struct grid *gb)
static void
grid_trim_history(struct grid *gd, u_int ny)
{
u_int remaining;
grid_free_lines(gd, 0, ny);
remaining = gd->hsize + gd->sy - ny;
memmove(&gd->linedata[0], &gd->linedata[ny],
(gd->hsize + gd->sy - ny) * (sizeof *gd->linedata));
remaining * (sizeof *gd->linedata));
memset(&gd->linedata[remaining], 0, ny * (sizeof *gd->linedata));
}
/*
@@ -351,14 +383,17 @@ grid_trim_history(struct grid *gd, u_int ny)
* and shift up.
*/
void
grid_collect_history(struct grid *gd)
grid_collect_history(struct grid *gd, int all)
{
u_int ny;
if (gd->hsize == 0 || gd->hsize < gd->hlimit)
return;
ny = gd->hlimit / 10;
if (all)
ny = gd->hsize - gd->hlimit;
else
ny = gd->hlimit / 10;
if (ny < 1)
ny = 1;
if (ny > gd->hsize)
@@ -460,7 +495,7 @@ static void
grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg)
{
struct grid_line *gl;
u_int xx;
u_int xx, old_cellsize;
gl = &gd->linedata[py];
if (sx <= gl->cellsize)
@@ -473,8 +508,10 @@ grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg)
else if (gd->sx > sx)
sx = gd->sx;
gl->celldata = xreallocarray(gl->celldata, sx, sizeof *gl->celldata);
for (xx = gl->cellsize; xx < sx; xx++)
old_cellsize = gl->cellsize;
gl->celldata = xrecallocarray(gl->celldata, old_cellsize, sx,
sizeof *gl->celldata);
for (xx = old_cellsize; xx < sx; xx++)
grid_clear_cell(gd, xx, py, bg);
gl->cellsize = sx;
}
@@ -515,7 +552,11 @@ grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc)
gc->bg = gee->bg;
gc->us = gee->us;
gc->link = gee->link;
utf8_to_data(gee->data, &gc->data);
if (gc->flags & GRID_FLAG_TAB)
grid_set_tab(gc, gee->data);
else
utf8_to_data(gee->data, &gc->data);
}
return;
}
@@ -1058,12 +1099,16 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
off = 0;
gl = grid_peek_line(gd, py);
if (gl == NULL) {
buf[0] = '\0';
return (buf);
}
if (flags & GRID_STRING_EMPTY_CELLS)
end = gl->cellsize;
else
end = gl->cellused;
for (xx = px; xx < px + nx; xx++) {
if (gl == NULL || xx >= end)
if (xx >= end)
break;
grid_get_cell(gd, xx, py, &gc);
if (gc.flags & GRID_FLAG_PADDING)
@@ -1077,13 +1122,18 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
} else
codelen = 0;
data = gc.data.data;
size = gc.data.size;
if ((flags & GRID_STRING_ESCAPE_SEQUENCES) &&
size == 1 &&
*data == '\\') {
data = "\\\\";
size = 2;
if (gc.flags & GRID_FLAG_TAB) {
data = "\t";
size = 1;
} else {
data = gc.data.data;
size = gc.data.size;
if ((flags & GRID_STRING_ESCAPE_SEQUENCES) &&
size == 1 &&
*data == '\\') {
data = "\\\\";
size = 2;
}
}
while (len < off + size + codelen + 1) {
@@ -1275,7 +1325,7 @@ grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy,
if (!wrapped || want != from->cellused || width == sx)
break;
}
if (lines == 0)
if (lines == 0 || from == NULL)
return;
/*
@@ -1533,3 +1583,27 @@ grid_line_length(struct grid *gd, u_int py)
}
return (px);
}
/* Check if character is in set. */
int
grid_in_set(struct grid *gd, u_int px, u_int py, const char *set)
{
struct grid_cell gc, tmp_gc;
u_int pxx;
grid_get_cell(gd, px, py, &gc);
if (strchr(set, '\t')) {
if (gc.flags & GRID_FLAG_PADDING) {
pxx = px;
do
grid_get_cell(gd, --pxx, py, &tmp_gc);
while (pxx > 0 && tmp_gc.flags & GRID_FLAG_PADDING);
if (tmp_gc.flags & GRID_FLAG_TAB)
return (tmp_gc.data.width - (px - pxx));
} else if (gc.flags & GRID_FLAG_TAB)
return (gc.data.width);
}
if (gc.flags & GRID_FLAG_PADDING)
return (0);
return (utf8_cstrhas(set, &gc.data));
}

View File

@@ -68,6 +68,7 @@ struct hyperlinks {
u_int next_inner;
struct hyperlinks_by_inner_tree by_inner;
struct hyperlinks_by_uri_tree by_uri;
u_int references;
};
static int
@@ -205,6 +206,15 @@ hyperlinks_init(void)
hl->next_inner = 1;
RB_INIT(&hl->by_uri);
RB_INIT(&hl->by_inner);
hl->references = 1;
return (hl);
}
/* Copy hyperlink set. */
struct hyperlinks *
hyperlinks_copy(struct hyperlinks *hl)
{
hl->references++;
return (hl);
}
@@ -222,6 +232,8 @@ hyperlinks_reset(struct hyperlinks *hl)
void
hyperlinks_free(struct hyperlinks *hl)
{
hyperlinks_reset(hl);
free(hl);
if (--hl->references == 0) {
hyperlinks_reset(hl);
free(hl);
}
}

325
image-kitty.c Normal file
View File

@@ -0,0 +1,325 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2026 Thomas Adam <thomas@xteddy.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 MIND, USE, DATA OR PROFITS, WHETHER
* IN 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"
/*
* kitty_image stores the raw decoded pixel data and metadata from a kitty
* graphics protocol APC sequence. It is used to re-emit the sequence to the
* outer terminal on redraw.
*/
struct kitty_image {
/* Control-data fields parsed from the APC sequence. */
char action; /* a=: 'T'=transmit+display, 't', 'p', 'd' */
u_int format; /* f=: 32=RGBA, 24=RGB, 100=PNG */
char medium; /* t=: 'd'=direct, 'f'=file, 't'=tmp, 's'=shm */
u_int pixel_w; /* s=: source image pixel width */
u_int pixel_h; /* v=: source image pixel height */
u_int cols; /* c=: display columns (0=auto) */
u_int rows; /* r=: display rows (0=auto) */
u_int image_id; /* i=: image id (0=unassigned) */
u_int image_num; /* I=: image number */
u_int placement_id; /* p=: placement id */
u_int more; /* m=: 1=more chunks coming, 0=last */
u_int quiet; /* q=: suppress responses */
int z_index; /* z=: z-index */
char compression; /* o=: 'z'=zlib, 0=none */
char delete_what; /* d=: delete target (used with a=d) */
/* Cell size at the time of parsing (from the owning window). */
u_int xpixel;
u_int ypixel;
/* Original base64-encoded payload (concatenated across all chunks). */
char *encoded;
size_t encodedlen;
char *ctrl;
size_t ctrllen;
};
/*
* Parse control-data key=value pairs from a kitty APC sequence.
* Format: key=value,key=value,...
*/
static int
kitty_parse_control(const char *ctrl, size_t ctrllen, struct kitty_image *ki)
{
const char *p = ctrl, *end = ctrl + ctrllen, *errstr;
char key[4], val[32];
size_t klen, vlen;
while (p < end) {
klen = 0;
while (p < end && *p != '=' && klen < sizeof(key) - 1)
key[klen++] = *p++;
key[klen] = '\0';
if (p >= end || *p != '=')
return (-1);
p++;
vlen = 0;
while (p < end && *p != ',' && vlen < sizeof(val) - 1)
val[vlen++] = *p++;
val[vlen] = '\0';
if (p < end && *p == ',')
p++;
if (klen != 1)
continue;
switch (key[0]) {
case 'a':
ki->action = val[0];
break;
case 'f':
ki->format = strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 't':
ki->medium = val[0];
break;
case 's':
ki->pixel_w = strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 'v':
ki->pixel_h = strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 'c':
ki->cols = strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 'r':
ki->rows = strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 'i':
ki->image_id = strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 'I':
ki->image_num = strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 'p':
ki->placement_id = strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 'm':
ki->more = strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 'q':
ki->quiet = strtonum(val, 0, UINT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 'z':
ki->z_index = strtonum(val, INT_MIN, INT_MAX, &errstr);
if (errstr != NULL)
return (-1);
break;
case 'o':
ki->compression = val[0];
break;
case 'd':
ki->delete_what = val[0];
break;
}
}
return (0);
}
/*
* Parse a kitty APC body (after the leading 'G').
* Stores the original control string and base64 payload verbatim for
* pass-through re-emission to the outer terminal.
*/
struct kitty_image *
kitty_parse(const u_char *buf, size_t len, u_int xpixel, u_int ypixel)
{
struct kitty_image *ki;
const u_char *semi;
const char *ctrl;
size_t ctrllen, paylen;
if (len == 0)
return (NULL);
semi = memchr(buf, ';', len);
if (semi != NULL) {
ctrl = (const char *)buf;
ctrllen = semi - buf;
paylen = len - ctrllen - 1;
} else {
ctrl = (const char *)buf;
ctrllen = len;
paylen = 0;
}
ki = xcalloc(1, sizeof *ki);
ki->xpixel = xpixel;
ki->ypixel = ypixel;
ki->action = 'T';
ki->format = 32;
ki->medium = 'd';
if (kitty_parse_control(ctrl, ctrllen, ki) != 0) {
free(ki);
return (NULL);
}
if (paylen > 0) {
ki->encoded = xmalloc(paylen + 1);
memcpy(ki->encoded, semi + 1, paylen);
ki->encoded[paylen] = '\0';
ki->encodedlen = paylen;
}
ki->ctrl = xmalloc(ctrllen + 1);
memcpy(ki->ctrl, ctrl, ctrllen);
ki->ctrl[ctrllen] = '\0';
ki->ctrllen = ctrllen;
return (ki);
}
void
kitty_free(struct kitty_image *ki)
{
if (ki == NULL)
return;
free(ki->encoded);
free(ki->ctrl);
free(ki);
}
/*
* Get the size in cells of a kitty image. If cols/rows are 0 (auto),
* calculate from pixel dimensions. Returns size via sx/sy pointers.
*/
void
kitty_size_in_cells(struct kitty_image *ki, u_int *sx, u_int *sy)
{
*sx = ki->cols;
*sy = ki->rows;
/*
* If cols/rows are 0, they mean "auto" - calculate from
* pixel dimensions.
*/
if (*sx == 0 && ki->pixel_w > 0 && ki->xpixel > 0) {
*sx = (ki->pixel_w + ki->xpixel - 1) / ki->xpixel;
}
if (*sy == 0 && ki->pixel_h > 0 && ki->ypixel > 0) {
*sy = (ki->pixel_h + ki->ypixel - 1) / ki->ypixel;
}
/* If still 0, use a reasonable default */
if (*sx == 0)
*sx = 10;
if (*sy == 0)
*sy = 10;
}
char
kitty_get_action(struct kitty_image *ki)
{
return (ki->action);
}
u_int
kitty_get_image_id(struct kitty_image *ki)
{
return (ki->image_id);
}
u_int
kitty_get_rows(struct kitty_image *ki)
{
return (ki->rows);
}
/*
* Serialize a kitty_image back into an APC escape sequence for transmission
* to the terminal. This recreates the original command that was parsed.
*/
char *
kitty_print(struct kitty_image *ki, size_t *outlen)
{
char *out;
size_t total, pos;
if (ki == NULL || ki->ctrl == NULL)
return (NULL);
/* Calculate total length: ESC _ G + ctrl + ; + encoded + ESC \ */
total = 3 + ki->ctrllen; /* \033_G + ctrl */
if (ki->encoded != NULL && ki->encodedlen > 0) {
total += 1 + ki->encodedlen; /* ; + encoded */
}
total += 2; /* \033\\ */
out = xmalloc(total + 1);
*outlen = total;
/* Build the sequence */
pos = 0;
memcpy(out + pos, "\033_G", 3);
pos += 3;
memcpy(out + pos, ki->ctrl, ki->ctrllen);
pos += ki->ctrllen;
if (ki->encoded != NULL && ki->encodedlen > 0) {
out[pos++] = ';';
memcpy(out + pos, ki->encoded, ki->encodedlen);
pos += ki->encodedlen;
}
memcpy(out + pos, "\033\\", 2);
pos += 2;
out[pos] = '\0';
return (out);
}
char *
kitty_delete_all(size_t *outlen)
{
char *out;
out = xstrdup("\033_Ga=d,d=a\033\\");
*outlen = strlen(out);
return (out);
}

View File

@@ -37,8 +37,14 @@ struct sixel_image {
u_int xpixel;
u_int ypixel;
u_int set_ra;
u_int ra_x;
u_int ra_y;
u_int *colours;
u_int ncolours;
u_int used_colours;
u_int p2;
u_int dx;
u_int dy;
@@ -47,6 +53,19 @@ struct sixel_image {
struct sixel_line *lines;
};
struct sixel_chunk {
u_int next_x;
u_int next_y;
u_int count;
char pattern;
char next_pattern;
size_t len;
size_t used;
char *data;
};
static int
sixel_parse_expand_lines(struct sixel_image *si, u_int y)
{
@@ -166,6 +185,10 @@ sixel_parse_attributes(struct sixel_image *si, const char *cp, const char *end)
si->x = x;
sixel_parse_expand_lines(si, y);
si->set_ra = 1;
si->ra_x = x;
si->ra_y = y;
return (last);
}
@@ -174,7 +197,7 @@ sixel_parse_colour(struct sixel_image *si, const char *cp, const char *end)
{
const char *last;
char *endptr;
u_int c, type, r, g, b;
u_int c, type, c1, c2, c3;
last = cp;
while (last != end) {
@@ -188,6 +211,8 @@ sixel_parse_colour(struct sixel_image *si, const char *cp, const char *end)
log_debug("%s: too many colours", __func__);
return (NULL);
}
if (si->used_colours <= c)
si->used_colours = c + 1;
si->dc = c + 1;
if (endptr == last || *endptr != ';')
return (last);
@@ -197,32 +222,36 @@ sixel_parse_colour(struct sixel_image *si, const char *cp, const char *end)
log_debug("%s: missing ;", __func__);
return (NULL);
}
r = strtoul(endptr + 1, &endptr, 10);
c1 = strtoul(endptr + 1, &endptr, 10);
if (endptr == last || *endptr != ';') {
log_debug("%s: missing ;", __func__);
return (NULL);
}
g = strtoul(endptr + 1, &endptr, 10);
c2 = strtoul(endptr + 1, &endptr, 10);
if (endptr == last || *endptr != ';') {
log_debug("%s: missing ;", __func__);
return (NULL);
}
b = strtoul(endptr + 1, &endptr, 10);
c3 = strtoul(endptr + 1, &endptr, 10);
if (endptr != last) {
log_debug("%s: missing ;", __func__);
return (NULL);
}
if (type != 1 && type != 2) {
log_debug("%s: invalid type %d", __func__, type);
if ((type != 1 && type != 2) ||
(type == 1 && (c1 > 360 || c2 > 100 || c3 > 100)) ||
(type == 2 && (c1 > 100 || c2 > 100 || c3 > 100))) {
log_debug("%s: invalid color %u;%u;%u;%u", __func__, type,
c1, c2, c3);
return (NULL);
}
if (c + 1 > si->ncolours) {
si->colours = xrecallocarray(si->colours, si->ncolours, c + 1,
sizeof *si->colours);
si->ncolours = c + 1;
}
si->colours[c] = (type << 24) | (r << 16) | (g << 8) | b;
si->colours[c] = (type << 25) | (c1 << 16) | (c2 << 8) | c3;
return (last);
}
@@ -268,7 +297,7 @@ sixel_parse_repeat(struct sixel_image *si, const char *cp, const char *end)
}
struct sixel_image *
sixel_parse(const char *buf, size_t len, u_int xpixel, u_int ypixel)
sixel_parse(const char *buf, size_t len, u_int p2, u_int xpixel, u_int ypixel)
{
struct sixel_image *si;
const char *cp = buf, *end = buf + len;
@@ -282,6 +311,7 @@ sixel_parse(const char *buf, size_t len, u_int xpixel, u_int ypixel)
si = xcalloc (1, sizeof *si);
si->xpixel = xpixel;
si->ypixel = ypixel;
si->p2 = p2;
while (cp != end) {
ch = *cp++;
@@ -393,8 +423,7 @@ sixel_scale(struct sixel_image *si, u_int xpixel, u_int ypixel, u_int ox,
/*
* We want to get the section of the image at ox,oy in image cells and
* map it onto the same size in terminal cells, remembering that we
* can only draw vertical sections of six pixels.
* map it onto the same size in terminal cells.
*/
sixel_size_in_cells(si, &cx, &cy);
@@ -418,12 +447,25 @@ sixel_scale(struct sixel_image *si, u_int xpixel, u_int ypixel, u_int ox,
psy = sy * si->ypixel;
tsx = sx * xpixel;
tsy = ((sy * ypixel) / 6) * 6;
tsy = sy * ypixel;
new = xcalloc (1, sizeof *si);
new->xpixel = xpixel;
new->ypixel = ypixel;
new->p2 = si->p2;
new->set_ra = si->set_ra;
/* subtract offset */
new->ra_x = new->ra_x > pox ? new->ra_x - pox : 0;
new->ra_y = new->ra_y > poy ? new->ra_y - poy : 0;
/* clamp to size */
new->ra_x = si->ra_x < psx ? si->ra_x : psx;
new->ra_y = si->ra_y < psy ? si->ra_y : psy;
/* resize */
new->ra_x = new->ra_x * xpixel / si->xpixel;
new->ra_y = new->ra_y * ypixel / si->ypixel;
new->used_colours = si->used_colours;
for (y = 0; y < tsy; y++) {
py = poy + ((double)y * psy / tsy);
for (x = 0; x < tsx; x++) {
@@ -432,7 +474,7 @@ sixel_scale(struct sixel_image *si, u_int xpixel, u_int ypixel, u_int ox,
}
}
if (colours) {
if (colours && si->ncolours != 0) {
new->colours = xmalloc(si->ncolours * sizeof *new->colours);
for (i = 0; i < si->ncolours; i++)
new->colours[i] = si->colours[i];
@@ -474,13 +516,66 @@ sixel_print_repeat(char **buf, size_t *len, size_t *used, u_int count, char ch)
}
}
static void
sixel_print_compress_colors(struct sixel_image *si, struct sixel_chunk *chunks,
u_int y, u_int *active, u_int *nactive)
{
u_int i, x, c, dx, colors[6];
struct sixel_chunk *chunk = NULL;
struct sixel_line *sl;
for (x = 0; x < si->x; x++) {
for (i = 0; i < 6; i++) {
colors[i] = 0;
if (y + i < si->y) {
sl = &si->lines[y + i];
if (x < sl->x && sl->data[x] != 0) {
colors[i] = sl->data[x];
c = sl->data[x] - 1;
chunks[c].next_pattern |= 1 << i;
}
}
}
for (i = 0; i < 6; i++) {
if (colors[i] == 0)
continue;
c = colors[i] - 1;
chunk = &chunks[c];
if (chunk->next_x == x + 1)
continue;
if (chunk->next_y < y + 1) {
chunk->next_y = y + 1;
active[(*nactive)++] = c;
}
dx = x - chunk->next_x;
if (chunk->pattern != chunk->next_pattern || dx != 0) {
sixel_print_repeat(&chunk->data, &chunk->len,
&chunk->used, chunk->count,
chunk->pattern + 0x3f);
sixel_print_repeat(&chunk->data, &chunk->len,
&chunk->used, dx, '?');
chunk->pattern = chunk->next_pattern;
chunk->count = 0;
}
chunk->count++;
chunk->next_pattern = 0;
chunk->next_x = x + 1;
}
}
}
char *
sixel_print(struct sixel_image *si, struct sixel_image *map, size_t *size)
{
char *buf, tmp[64], *contains, data, last = 0;
char *buf, tmp[64];
size_t len, used = 0, tmplen;
u_int *colours, ncolours, i, c, x, y, count;
struct sixel_line *sl;
u_int *colours, ncolours, used_colours, i, c, y;
u_int *active, nactive;
struct sixel_chunk *chunks, *chunk;
if (map != NULL) {
colours = map->colours;
@@ -489,70 +584,61 @@ sixel_print(struct sixel_image *si, struct sixel_image *map, size_t *size)
colours = si->colours;
ncolours = si->ncolours;
}
contains = xcalloc(1, ncolours);
used_colours = si->used_colours;
if (used_colours == 0)
return (NULL);
len = 8192;
buf = xmalloc(len);
sixel_print_add(&buf, &len, &used, "\033Pq", 3);
tmplen = xsnprintf(tmp, sizeof tmp, "\"1;1;%u;%u", si->x, si->y);
tmplen = xsnprintf(tmp, sizeof tmp, "\033P0;%uq", si->p2);
sixel_print_add(&buf, &len, &used, tmp, tmplen);
if (si->set_ra) {
tmplen = xsnprintf(tmp, sizeof tmp, "\"1;1;%u;%u", si->ra_x,
si->ra_y);
sixel_print_add(&buf, &len, &used, tmp, tmplen);
}
chunks = xcalloc(used_colours, sizeof *chunks);
active = xcalloc(used_colours, sizeof *active);
for (i = 0; i < ncolours; i++) {
c = colours[i];
tmplen = xsnprintf(tmp, sizeof tmp, "#%u;%u;%u;%u;%u",
i, c >> 24, (c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff);
i, c >> 25, (c >> 16) & 0x1ff, (c >> 8) & 0xff, c & 0xff);
sixel_print_add(&buf, &len, &used, tmp, tmplen);
}
for (y = 0; y < si->y; y += 6) {
memset(contains, 0, ncolours);
for (x = 0; x < si->x; x++) {
for (i = 0; i < 6; i++) {
if (y + i >= si->y)
break;
sl = &si->lines[y + i];
if (x < sl->x && sl->data[x] != 0)
contains[sl->data[x] - 1] = 1;
}
}
for (i = 0; i < used_colours; i++) {
chunk = &chunks[i];
chunk->len = 8;
chunk->data = xmalloc(chunk->len);
}
for (c = 0; c < ncolours; c++) {
if (!contains[c])
continue;
for (y = 0; y < si->y; y += 6) {
nactive = 0;
sixel_print_compress_colors(si, chunks, y, active, &nactive);
for (i = 0; i < nactive; i++) {
c = active[i];
chunk = &chunks[c];
tmplen = xsnprintf(tmp, sizeof tmp, "#%u", c);
sixel_print_add(&buf, &len, &used, tmp, tmplen);
count = 0;
for (x = 0; x < si->x; x++) {
data = 0;
for (i = 0; i < 6; i++) {
if (y + i >= si->y)
break;
sl = &si->lines[y + i];
if (x < sl->x && sl->data[x] == c + 1)
data |= (1 << i);
}
data += 0x3f;
if (data != last) {
sixel_print_repeat(&buf, &len, &used,
count, last);
last = data;
count = 1;
} else
count++;
}
sixel_print_repeat(&buf, &len, &used, count, data);
sixel_print_add(&buf, &len, &used, chunk->data,
chunk->used);
sixel_print_repeat(&buf, &len, &used, chunk->count,
chunk->pattern + 0x3f);
sixel_print_add(&buf, &len, &used, "$", 1);
chunk->used = chunk->next_x = chunk->count = 0;
}
if (buf[used - 1] == '$')
used--;
if (buf[used - 1] != '-')
sixel_print_add(&buf, &len, &used, "-", 1);
sixel_print_add(&buf, &len, &used, "-", 1);
}
if (buf[used - 1] == '$' || buf[used - 1] == '-')
if (buf[used - 1] == '-')
used--;
sixel_print_add(&buf, &len, &used, "\033\\", 2);
@@ -561,7 +647,11 @@ sixel_print(struct sixel_image *si, struct sixel_image *map, size_t *size)
if (size != NULL)
*size = used;
free(contains);
for (i = 0; i < used_colours; i++)
free(chunks[i].data);
free(active);
free(chunks);
return (buf);
}

167
image.c
View File

@@ -25,17 +25,58 @@
static struct images all_images = TAILQ_HEAD_INITIALIZER(all_images);
static u_int all_images_count;
#define MAX_IMAGE_COUNT 20
static void printflike(3, 4)
image_log(struct image *im, const char* from, const char* fmt, ...)
{
va_list ap;
char s[128];
if (log_get_level() == 0)
return;
if (fmt == NULL) {
log_debug("%s: %p (%ux%u %u,%u)", from, im, im->sx, im->sy,
im->px, im->py);
return;
}
va_start(ap, fmt);
vsnprintf(s, sizeof s, fmt, ap);
va_end(ap);
log_debug("%s: %p (%ux%u %u,%u): %s", from, im, im->sx, im->sy,
im->px, im->py, s);
}
static void
image_free(struct image *im)
{
struct screen *s = im->s;
image_log(im, __func__, NULL);
TAILQ_REMOVE(&all_images, im, all_entry);
all_images_count--;
TAILQ_REMOVE(&s->images, im, entry);
sixel_free(im->data);
switch (im->type) {
#ifdef ENABLE_SIXEL_IMAGES
case IMAGE_SIXEL:
sixel_free(im->data.sixel);
break;
#endif
#ifdef ENABLE_KITTY_IMAGES
case IMAGE_KITTY:
kitty_free(im->data.kitty);
break;
#endif
default:
break;
}
free(im->fallback);
free(im);
}
@@ -46,6 +87,8 @@ image_free_all(struct screen *s)
struct image *im, *im1;
int redraw = !TAILQ_EMPTY(&s->images);
if (redraw)
log_debug ("%s", __func__);
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1)
image_free(im);
return (redraw);
@@ -53,13 +96,30 @@ image_free_all(struct screen *s)
/* Create text placeholder for an image. */
static void
image_fallback(char **ret, u_int sx, u_int sy)
image_fallback(char **ret, enum image_type type, u_int sx, u_int sy)
{
char *buf, *label;
u_int py, size, lsize;
const char *type_name;
switch (type) {
#ifdef ENABLE_SIXEL_IMAGES
case IMAGE_SIXEL:
type_name = "SIXEL";
break;
#endif
#ifdef ENABLE_KITTY_IMAGES
case IMAGE_KITTY:
type_name = "KITTY";
break;
#endif
default:
type_name = "UNKNOWN";
break;
}
/* Allocate first line. */
lsize = xasprintf(&label, "SIXEL IMAGE (%ux%u)\r\n", sx, sy) + 1;
lsize = xasprintf(&label, "%s IMAGE (%ux%u)\r\n", type_name, sx, sy) + 1;
if (sx < lsize - 3)
size = lsize - 1;
else
@@ -94,24 +154,42 @@ image_fallback(char **ret, u_int sx, u_int sy)
}
struct image*
image_store(struct screen *s, struct sixel_image *si)
image_store(struct screen *s, enum image_type type, void *data)
{
struct image *im;
im = xcalloc(1, sizeof *im);
im->type = type;
im->s = s;
im->data = si;
im->px = s->cx;
im->py = s->cy;
sixel_size_in_cells(si, &im->sx, &im->sy);
image_fallback(&im->fallback, im->sx, im->sy);
switch (type) {
#ifdef ENABLE_SIXEL_IMAGES
case IMAGE_SIXEL:
im->data.sixel = data;
sixel_size_in_cells(im->data.sixel, &im->sx, &im->sy);
break;
#endif
#ifdef ENABLE_KITTY_IMAGES
case IMAGE_KITTY:
im->data.kitty = data;
kitty_size_in_cells(im->data.kitty, &im->sx, &im->sy);
break;
#endif
default:
break;
}
image_fallback(&im->fallback, type, im->sx, im->sy);
image_log(im, __func__, NULL);
TAILQ_INSERT_TAIL(&s->images, im, entry);
TAILQ_INSERT_TAIL(&all_images, im, all_entry);
if (++all_images_count == 10/*XXX*/)
if (++all_images_count == MAX_IMAGE_COUNT)
image_free(TAILQ_FIRST(&all_images));
return (im);
@@ -121,10 +199,12 @@ int
image_check_line(struct screen *s, u_int py, u_int ny)
{
struct image *im, *im1;
int redraw = 0;
int redraw = 0, in;
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
if (py + ny > im->py && py < im->py + im->sy) {
in = (py + ny > im->py && py < im->py + im->sy);
image_log(im, __func__, "py=%u, ny=%u, in=%d", py, ny, in);
if (in) {
image_free(im);
redraw = 1;
}
@@ -136,15 +216,18 @@ int
image_check_area(struct screen *s, u_int px, u_int py, u_int nx, u_int ny)
{
struct image *im, *im1;
int redraw = 0;
int redraw = 0, in;
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
if (py + ny <= im->py || py >= im->py + im->sy)
continue;
if (px + nx <= im->px || px >= im->px + im->sx)
continue;
image_free(im);
redraw = 1;
in = (py < im->py + im->sy &&
py + ny > im->py &&
px < im->px + im->sx &&
px + nx > im->px);
image_log(im, __func__, "py=%u, ny=%u, in=%d", py, ny, in);
if (in) {
image_free(im);
redraw = 1;
}
}
return (redraw);
}
@@ -154,33 +237,61 @@ image_scroll_up(struct screen *s, u_int lines)
{
struct image *im, *im1;
int redraw = 0;
u_int sx, sy;
#ifdef ENABLE_SIXEL_IMAGES
struct sixel_image *new;
u_int sx, sy;
#endif
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
if (im->py >= lines) {
image_log(im, __func__, "1, lines=%u", lines);
im->py -= lines;
redraw = 1;
continue;
}
if (im->py + im->sy <= lines) {
image_log(im, __func__, "2, lines=%u", lines);
image_free(im);
redraw = 1;
continue;
}
sx = im->sx;
sy = (im->py + im->sy) - lines;
new = sixel_scale(im->data, 0, 0, 0, im->sy - sy, sx, sy, 1);
sixel_free(im->data);
im->data = new;
/* Image is partially scrolled off - need to crop it */
switch (im->type) {
#ifdef ENABLE_SIXEL_IMAGES
case IMAGE_SIXEL:
sx = im->sx;
sy = (im->py + im->sy) - lines;
image_log(im, __func__, "sixel, lines=%u, sy=%u",
lines, sy);
im->py = 0;
sixel_size_in_cells(im->data, &im->sx, &im->sy);
new = sixel_scale(im->data.sixel, 0, 0, 0, im->sy - sy,
sx, sy, 1);
sixel_free(im->data.sixel);
im->data.sixel = new;
free(im->fallback);
image_fallback(&im->fallback, im->sx, im->sy);
redraw = 1;
im->py = 0;
sixel_size_in_cells(im->data.sixel, &im->sx, &im->sy);
free(im->fallback);
image_fallback(&im->fallback, im->type, im->sx, im->sy);
redraw = 1;
break;
#endif
#ifdef ENABLE_KITTY_IMAGES
case IMAGE_KITTY:
/*
* For kitty images, we can't rescale - the terminal
* owns the placement. Just adjust position and let
* the terminal handle clipping.
*/
im->py = 0;
redraw = 1;
break;
#endif
default:
break;
}
}
return (redraw);
}

View File

@@ -53,9 +53,15 @@ static struct input_key_entry input_key_defaults[] = {
{ .key = KEYC_PASTE_START,
.data = "\033[200~"
},
{ .key = KEYC_PASTE_START|KEYC_IMPLIED_META,
.data = "\033[200~"
},
{ .key = KEYC_PASTE_END,
.data = "\033[201~"
},
{ .key = KEYC_PASTE_END|KEYC_IMPLIED_META,
.data = "\033[201~"
},
/* Function keys. */
{ .key = KEYC_F1,
@@ -307,20 +313,6 @@ static struct input_key_entry input_key_defaults[] = {
{ .key = KEYC_DC|KEYC_BUILD_MODIFIERS,
.data = "\033[3;_~"
},
/* Tab and modifiers. */
{ .key = '\011'|KEYC_CTRL,
.data = "\011"
},
{ .key = '\011'|KEYC_CTRL|KEYC_EXTENDED,
.data = "\033[9;5u"
},
{ .key = '\011'|KEYC_CTRL|KEYC_SHIFT,
.data = "\033[Z"
},
{ .key = '\011'|KEYC_CTRL|KEYC_SHIFT|KEYC_EXTENDED,
.data = "\033[1;5Z"
}
};
static const key_code input_key_modifiers[] = {
0,
@@ -426,126 +418,18 @@ input_key_write(const char *from, struct bufferevent *bev, const char *data,
bufferevent_write(bev, data, size);
}
/* Translate a key code into an output key sequence. */
int
input_key(struct screen *s, struct bufferevent *bev, key_code key)
/*
* Encode and write an extended key escape sequence in one of the two
* possible formats, depending on the configured output mode.
*/
static int
input_key_extended(struct bufferevent *bev, key_code key)
{
struct input_key_entry *ike = NULL;
key_code justkey, newkey, outkey, modifiers;
struct utf8_data ud;
char tmp[64], modifier;
char tmp[64], modifier;
struct utf8_data ud;
wchar_t wc;
/* Mouse keys need a pane. */
if (KEYC_IS_MOUSE(key))
return (0);
/* Literal keys go as themselves (can't be more than eight bits). */
if (key & KEYC_LITERAL) {
ud.data[0] = (u_char)key;
input_key_write(__func__, bev, &ud.data[0], 1);
return (0);
}
/* Is this backspace? */
if ((key & KEYC_MASK_KEY) == KEYC_BSPACE) {
newkey = options_get_number(global_options, "backspace");
if (newkey >= 0x7f)
newkey = '\177';
key = newkey|(key & (KEYC_MASK_MODIFIERS|KEYC_MASK_FLAGS));
}
/*
* If this is a normal 7-bit key, just send it, with a leading escape
* if necessary. If it is a UTF-8 key, split it and send it.
*/
justkey = (key & ~(KEYC_META|KEYC_IMPLIED_META));
if (justkey <= 0x7f) {
if (key & KEYC_META)
input_key_write(__func__, bev, "\033", 1);
ud.data[0] = justkey;
input_key_write(__func__, bev, &ud.data[0], 1);
return (0);
}
if (KEYC_IS_UNICODE(justkey)) {
if (key & KEYC_META)
input_key_write(__func__, bev, "\033", 1);
utf8_to_data(justkey, &ud);
input_key_write(__func__, bev, ud.data, ud.size);
return (0);
}
/*
* Look up in the tree. If not in application keypad or cursor mode,
* remove the flags from the key.
*/
if (~s->mode & MODE_KKEYPAD)
key &= ~KEYC_KEYPAD;
if (~s->mode & MODE_KCURSOR)
key &= ~KEYC_CURSOR;
if (s->mode & MODE_KEXTENDED)
ike = input_key_get(key|KEYC_EXTENDED);
if (ike == NULL)
ike = input_key_get(key);
if (ike == NULL && (key & KEYC_META) && (~key & KEYC_IMPLIED_META))
ike = input_key_get(key & ~KEYC_META);
if (ike == NULL && (key & KEYC_CURSOR))
ike = input_key_get(key & ~KEYC_CURSOR);
if (ike == NULL && (key & KEYC_KEYPAD))
ike = input_key_get(key & ~KEYC_KEYPAD);
if (ike == NULL && (key & KEYC_EXTENDED))
ike = input_key_get(key & ~KEYC_EXTENDED);
if (ike != NULL) {
log_debug("found key 0x%llx: \"%s\"", key, ike->data);
if ((key == KEYC_PASTE_START || key == KEYC_PASTE_END) &&
(~s->mode & MODE_BRACKETPASTE))
return (0);
if ((key & KEYC_META) && (~key & KEYC_IMPLIED_META))
input_key_write(__func__, bev, "\033", 1);
input_key_write(__func__, bev, ike->data, strlen(ike->data));
return (0);
}
/* No builtin key sequence; construct an extended key sequence. */
if (~s->mode & MODE_KEXTENDED) {
if ((key & KEYC_MASK_MODIFIERS) != KEYC_CTRL)
goto missing;
justkey = (key & KEYC_MASK_KEY);
switch (justkey) {
case ' ':
case '2':
key = 0|(key & ~KEYC_MASK_KEY);
break;
case '|':
key = 28|(key & ~KEYC_MASK_KEY);
break;
case '6':
key = 30|(key & ~KEYC_MASK_KEY);
break;
case '-':
case '/':
key = 31|(key & ~KEYC_MASK_KEY);
break;
case '?':
key = 127|(key & ~KEYC_MASK_KEY);
break;
default:
if (justkey >= 'A' && justkey <= '_')
key = (justkey - 'A')|(key & ~KEYC_MASK_KEY);
else if (justkey >= 'a' && justkey <= '~')
key = (justkey - 96)|(key & ~KEYC_MASK_KEY);
else
return (0);
break;
}
return (input_key(s, bev, key & ~KEYC_CTRL));
}
outkey = (key & KEYC_MASK_KEY);
modifiers = (key & KEYC_MASK_MODIFIERS);
if (outkey < 32 && outkey != 9 && outkey != 13 && outkey != 27) {
outkey = 64 + outkey;
modifiers |= KEYC_CTRL;
}
switch (modifiers) {
switch (key & KEYC_MASK_MODIFIERS) {
case KEYC_SHIFT:
modifier = '2';
break;
@@ -568,17 +452,263 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key)
modifier = '8';
break;
default:
goto missing;
return (-1);
}
xsnprintf(tmp, sizeof tmp, "\033[%llu;%cu", outkey, modifier);
if (KEYC_IS_UNICODE(key)) {
utf8_to_data(key & KEYC_MASK_KEY, &ud);
if (utf8_towc(&ud, &wc) == UTF8_DONE)
key = wc;
else
return (-1);
} else
key &= KEYC_MASK_KEY;
if (options_get_number(global_options, "extended-keys-format") == 1)
xsnprintf(tmp, sizeof tmp, "\033[27;%c;%llu~", modifier, key);
else
xsnprintf(tmp, sizeof tmp, "\033[%llu;%cu", key, modifier);
input_key_write(__func__, bev, tmp, strlen(tmp));
return (0);
}
/*
* Outputs the key in the "standard" mode. This is by far the most
* complicated output mode, with a lot of remapping in order to
* emulate quirks of terminals that today can be only found in museums.
*/
static int
input_key_vt10x(struct bufferevent *bev, key_code key)
{
struct utf8_data ud;
key_code onlykey;
char *p;
static const char *standard_map[2] = {
"1!9(0)=+;:'\",<.>/-8? 2",
"119900=+;;'',,..\x1f\x1f\x7f\x7f\0\0",
};
log_debug("%s: key in %llx", __func__, key);
if (key & KEYC_META)
input_key_write(__func__, bev, "\033", 1);
/*
* There's no way to report modifiers for unicode keys in standard mode
* so lose the modifiers.
*/
if (KEYC_IS_UNICODE(key)) {
utf8_to_data(key, &ud);
input_key_write(__func__, bev, ud.data, ud.size);
return (0);
}
/*
* Prevent TAB, CR and LF from being swallowed by the C0 remapping
* logic.
*/
onlykey = key & KEYC_MASK_KEY;
if (onlykey == '\r' || onlykey == '\n' || onlykey == '\t')
key &= ~KEYC_CTRL;
/*
* Convert keys with Ctrl modifier into corresponding C0 control codes,
* with the exception of *some* keys, which are remapped into printable
* ASCII characters.
*
* There is no special handling for Shift modifier, which is pretty
* much redundant anyway, as no terminal will send <base key>|SHIFT,
* but only <shifted key>|SHIFT.
*/
if (key & KEYC_CTRL) {
p = strchr(standard_map[0], onlykey);
if (p != NULL)
key = standard_map[1][p - standard_map[0]];
else if (onlykey >= '3' && onlykey <= '7')
key = onlykey - '\030';
else if (onlykey >= '@' && onlykey <= '~')
key = onlykey & 0x1f;
else
return (-1);
}
log_debug("%s: key out %llx", __func__, key);
ud.data[0] = key & 0x7f;
input_key_write(__func__, bev, &ud.data[0], 1);
return (0);
}
/* Pick keys that are reported as vt10x keys in modifyOtherKeys=1 mode. */
static int
input_key_mode1(struct bufferevent *bev, key_code key)
{
key_code onlykey;
log_debug("%s: key in %llx", __func__, key);
/* A regular or shifted key + Meta. */
if ((key & (KEYC_CTRL | KEYC_META)) == KEYC_META)
return (input_key_vt10x(bev, key));
/*
* As per
* https://invisible-island.net/xterm/modified-keys-us-pc105.html.
*/
onlykey = key & KEYC_MASK_KEY;
if ((key & KEYC_CTRL) &&
(onlykey == ' ' ||
onlykey == '/' ||
onlykey == '@' ||
onlykey == '^' ||
(onlykey >= '2' && onlykey <= '8') ||
(onlykey >= '@' && onlykey <= '~')))
return (input_key_vt10x(bev, key));
missing:
log_debug("key 0x%llx missing", key);
return (-1);
}
/* Translate a key code into an output key sequence. */
int
input_key(struct screen *s, struct bufferevent *bev, key_code key)
{
struct input_key_entry *ike = NULL;
key_code newkey;
struct utf8_data ud;
/* Mouse keys need a pane. */
if (KEYC_IS_MOUSE(key))
return (0);
/* Literal keys go as themselves (can't be more than eight bits). */
if (key & KEYC_LITERAL) {
ud.data[0] = (u_char)key;
input_key_write(__func__, bev, &ud.data[0], 1);
return (0);
}
/* Is this backspace? */
if ((key & KEYC_MASK_KEY) == KEYC_BSPACE) {
newkey = options_get_number(global_options, "backspace");
log_debug("%s: key 0x%llx is backspace -> 0x%llx", __func__,
key, newkey);
if ((key & KEYC_MASK_MODIFIERS) == 0) {
ud.data[0] = 255;
if ((newkey & KEYC_MASK_MODIFIERS) == 0)
ud.data[0] = newkey;
else if ((newkey & KEYC_MASK_MODIFIERS) == KEYC_CTRL) {
newkey &= KEYC_MASK_KEY;
if (newkey == '?')
ud.data[0] = 0x7f;
else if (newkey >= '@' && newkey <= '_')
ud.data[0] = newkey - 0x40;
else if (newkey >= 'a' && newkey <= 'z')
ud.data[0] = newkey - 0x60;
}
if (ud.data[0] != 255)
input_key_write(__func__, bev, &ud.data[0], 1);
return (0);
}
key = newkey|(key & (KEYC_MASK_FLAGS|KEYC_MASK_MODIFIERS));
}
/* Is this backtab? */
if ((key & KEYC_MASK_KEY) == KEYC_BTAB) {
if (s->mode & MODE_KEYS_EXTENDED_2) {
/* When in xterm extended mode, remap into S-Tab. */
key = '\011' | (key & ~KEYC_MASK_KEY) | KEYC_SHIFT;
} else {
/* Otherwise clear modifiers. */
key &= ~KEYC_MASK_MODIFIERS;
}
}
/*
* A trivial case, that is a 7-bit key, excluding C0 control characters
* that can't be entered from the keyboard, and no modifiers; or a UTF-8
* key and no modifiers.
*/
if (!(key & ~KEYC_MASK_KEY)) {
if (key == C0_HT ||
key == C0_CR ||
key == C0_ESC ||
(key >= 0x20 && key <= 0x7f)) {
ud.data[0] = key;
input_key_write(__func__, bev, &ud.data[0], 1);
return (0);
}
if (KEYC_IS_UNICODE(key)) {
utf8_to_data(key, &ud);
input_key_write(__func__, bev, ud.data, ud.size);
return (0);
}
}
/*
* Look up the standard VT10x keys in the tree. If not in application
* keypad or cursor mode, remove the respective flags from the key.
*/
if (~s->mode & MODE_KKEYPAD)
key &= ~KEYC_KEYPAD;
if (~s->mode & MODE_KCURSOR)
key &= ~KEYC_CURSOR;
if (ike == NULL)
ike = input_key_get(key);
if (ike == NULL && (key & KEYC_META) && (~key & KEYC_IMPLIED_META))
ike = input_key_get(key & ~KEYC_META);
if (ike == NULL && (key & KEYC_CURSOR))
ike = input_key_get(key & ~KEYC_CURSOR);
if (ike == NULL && (key & KEYC_KEYPAD))
ike = input_key_get(key & ~KEYC_KEYPAD);
if (ike != NULL) {
log_debug("%s: found key 0x%llx: \"%s\"", __func__, key,
ike->data);
if (KEYC_IS_PASTE(key) && (~s->mode & MODE_BRACKETPASTE))
return (0);
if ((key & KEYC_META) && (~key & KEYC_IMPLIED_META))
input_key_write(__func__, bev, "\033", 1);
input_key_write(__func__, bev, ike->data, strlen(ike->data));
return (0);
}
/* Ignore internal function key codes. */
if ((key >= KEYC_BASE && key < KEYC_BASE_END) ||
(key >= KEYC_USER && key < KEYC_USER_END)) {
log_debug("%s: ignoring key 0x%llx", __func__, key);
return (0);
}
/*
* No builtin key sequence; construct an extended key sequence
* depending on the client mode.
*
* If something invalid reaches here, an invalid output may be
* produced. For example Ctrl-Shift-2 is invalid (as there's
* no way to enter it). The correct form is Ctrl-Shift-@, at
* least in US English keyboard layout.
*/
switch (s->mode & EXTENDED_KEY_MODES) {
case MODE_KEYS_EXTENDED_2:
/*
* The simplest mode to handle - *all* modified keys are
* reported in the extended form.
*/
return (input_key_extended(bev, key));
case MODE_KEYS_EXTENDED:
/*
* Some keys are still reported in standard mode, to maintain
* compatibility with applications unaware of extended keys.
*/
if (input_key_mode1(bev, key) == -1)
return (input_key_extended(bev, key));
return (0);
default:
/* The standard mode. */
return (input_key_vt10x(bev, key));
}
}
/* Get mouse event string. */
int
input_key_get_mouse(struct screen *s, struct mouse_event *m, u_int x, u_int y,

1132
input.c

File diff suppressed because it is too large Load Diff

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