1014 Commits
3.1b ... 3.2

Author SHA1 Message Date
nicm
bc4681c83d Move mode set/reset after sync so cursor doesn't flicker, from Avi
Halachmi.
2021-04-13 08:37:57 +01:00
nicm
f29d3c7f74 Handle C-Tab correctly with extended keys, GitHub issue 2642. 2021-04-13 08:37:57 +01:00
Nicholas Marriott
30cf20d615 Update CHANGES. 2021-04-13 06:28:16 +01:00
Nicholas Marriott
c2048c5c65 Merge branch 'master' into 3.2-rc 2021-04-13 06:25:59 +01:00
Thomas Adam
bedf2bd437 Merge branch 'obsd-master' into master 2021-04-12 12:01:32 +01:00
nicm
e6abe55134 Add a flag to disable keys to close a message, GitHub issue 2625. 2021-04-12 09:36:12 +00:00
Thomas Adam
83cd593b9c Merge branch 'obsd-master' into master 2021-04-12 10:01:27 +01:00
nicm
cd208c9d72 Permit shortcut keys in buffer, client, tree modes to be configured with
a format; the default remains the line number. GitHub issue 2636.
2021-04-12 06:50:25 +00:00
Thomas Adam
7579097db6 Merge branch 'obsd-master' into master 2021-04-09 10:01:19 +01:00
nicm
73cbe46f8d Change a type to fix a warning with some compilers. 2021-04-09 07:02:00 +00:00
Thomas Adam
caf7aeb3a9 Merge branch 'obsd-master' into master 2021-04-08 16:01:17 +01:00
nicm
30fb628388 Log the key written to the terminal as well as tmux's idea of what it
is.
2021-04-08 14:16:12 +00:00
Thomas Adam
a57c2bf97e Merge branch 'obsd-master' into master 2021-04-07 18:01:20 +01:00
nicm
efb5e58c38 Restore previous behaviour so that C-X remains the same as C-x. Instead,
translate incoming extended keys so that they are consistent.
2021-04-07 15:46:12 +00:00
Thomas Adam
3cd63cc9e0 Merge branch 'obsd-master' into master 2021-04-07 16:01:21 +01:00
nicm
71fc9f3ee8 Add a current_file format for the config file being parsed. Originally
suggested by kn@, also GitHub issue 2638.
2021-04-07 12:50:12 +00:00
nicm
1ac47400d2 When display-message used in config file, show the message after the
config file finishes. GitHub issue 2637.
2021-04-07 12:49:33 +00:00
Thomas Adam
998a6b0aea Merge branch 'obsd-master' into master 2021-04-07 10:01:19 +01:00
nicm
ba99996676 Fixes for extended keys: 1) allow C-x and C-X to be bound separately
since some terminals report them differently 2) use the "backspace"
option to translate backspace 3) map ctrl which are have the ctrl
implied (such as C-x) properly when the terminal reports both the key
and the modifier.

Note that any key bindings for C-X where C-x is meant must now be
changed.
2021-04-07 07:30:02 +00:00
Thomas Adam
db97bef60b Merge branch 'obsd-master' into master 2021-04-05 16:01:23 +01:00
nicm
10470cea67 Move client-detached into server_client_lost so it is fired even if a
client is closed unexpectedly.
2021-04-05 14:11:05 +00:00
Thomas Adam
22ccae1c9d Merge branch 'obsd-master' into master 2021-04-05 10:01:19 +01:00
nicm
5900b164a4 Fix a couple of edge cases with the jump-back-xxx commands, and also
update back-to-indentation to use grid_reader, thereby fixing line
wrapping issues. From Anindya Mukherjee, GitHub issue 2633.
2021-04-05 08:43:48 +00:00
Thomas Adam
b307fc8cd5 Merge branch 'obsd-master' into master 2021-04-01 12:01:25 +01:00
nicm
28cd956729 Change search-again with vi keys to work like actual vi(1), also some
other fixes. From Aaron Jensen with help from Anindya Mukherjee.
2021-04-01 06:46:12 +00:00
nicm
6c98f222e9 Missing commas, from Vipul Kumar. 2021-04-01 06:37:46 +00:00
Thomas Adam
8f5ff4bf66 Merge branch 'obsd-master' into master 2021-03-31 10:01:16 +01:00
nicm
a4b9b5a1e5 Do not exit if cannot write to normal log file, GitHub issue 2630. 2021-03-31 08:37:48 +00:00
Nicholas Marriott
4208641de7 Remove queue.h, from Simon Holesch. 2021-03-28 10:16:17 +01:00
Thomas Adam
be568ea3b2 Merge branch 'obsd-master' into master 2021-03-16 10:01:20 +00:00
nicm
8b800b41c9 Add client-detached notification in control mode, from Mohsin Kaleem. 2021-03-16 09:14:58 +00:00
Thomas Adam
60bb8fa86d Merge branch 'obsd-master' into master 2021-03-15 14:01:17 +00:00
nicm
9017af2355 Do not crash if there is no item to show command error, from Anindya
Mukherjee.
2021-03-15 13:06:33 +00:00
Thomas Adam
7019937b52 Merge branch 'obsd-master' into master 2021-03-12 10:01:18 +00:00
nicm
e8224fb0d1 Fix so tmux correctly sends the cvvis (cursor very visible) capability
rather than sending it and then immediately undoing it with cnorm. Also
turn it off when the cursor shape is changed like xterm.
2021-03-12 08:39:17 +00:00
Nicholas Marriott
46cbbe3d45 Merge branch 'master' into 3.2-rc 2021-03-11 08:41:19 +00:00
Nicholas Marriott
ef9700816f malloc_trim is itself very poor and gets slower and slower as the heap becomes
more fragmented. Run it only once an hour. GitHub issue 2551.
2021-03-11 08:39:41 +00:00
Thomas Adam
12cfd0d22b Merge branch 'obsd-master' into master 2021-03-11 08:01:29 +00:00
nicm
ee0df1b8f8 Tidy old jobs every hour instead of every 30 seconds. 2021-03-11 07:08:18 +00:00
nicm
3eb91efba1 Add an "absolute-centre" alignment to use the centre of the total space
instead of only the available space. From Magnus Gross in GitHub issue 2578.
2021-03-11 06:41:04 +00:00
nicm
d98f9f7fe5 Add split-window -Z to start the pane zoomed, GitHub issue 2591. 2021-03-11 06:31:05 +00:00
Thomas Adam
541872bc57 Merge branch 'obsd-master' into master 2021-03-09 14:01:24 +00:00
nicm
7f87280cd5 Allow cursor to be just after match if copying, GitHub issue 2602. 2021-03-09 13:07:50 +00:00
Thomas Adam
ff77658f27 Merge branch 'obsd-master' into master 2021-03-09 10:01:19 +00:00
nicm
81e5736510 Copy mode improvements from Anindya Mukherjee:
- Fix word and word-end for wrapped lines.
- Fix copying of selection end on wrapped lines.
- Fix wrapped word selection edge case.
- Update select-line to respect wrapped lines.
- Update window_copy_..._pos() functions to use grid_reader.

GitHub issue 2605.
2021-03-09 08:24:09 +00:00
Nicholas Marriott
7bef887fd1 Update version. 2021-03-08 08:59:58 +00:00
Nicholas Marriott
8995d571d8 Merge branch 'master' into 3.2-rc 2021-03-08 08:59:40 +00:00
Nicholas Marriott
0792b65863 Remove compat.h again. 2021-03-08 08:59:13 +00:00
Nicholas Marriott
de4ac37baa 3.3. 2021-03-02 12:37:52 +00:00
Nicholas Marriott
b243f1b2ee No sys/queue.h. 2021-03-02 12:37:23 +00:00
Nicholas Marriott
d06c7197d4 Merge branch 'master' into 3.2-rc 2021-03-02 12:10:14 +00:00
Nicholas Marriott
fb039d5b82 paths.h is compat. 2021-03-02 12:09:59 +00:00
Nicholas Marriott
c01251d023 Merge branch 'master' into 3.2-rc 2021-03-02 12:08:34 +00:00
Nicholas Marriott
1466b570ee Update CHANGES. 2021-03-02 12:05:41 +00:00
Thomas Adam
2301bee87d Merge branch 'obsd-master' into master 2021-03-02 12:01:25 +00:00
nicm
81f9a23d25 Do not use NULL active window; also do not leak window name. GitHub
issue 2590 from Chester Liu.
2021-03-02 11:00:38 +00:00
nicm
c44750792a Drop support for popups where the content is provided directly to tmux
(which does not have many practical uses) and only support running a
program in the popup. display-popup is now simpler and can accept
multiple arguments to avoid escaping problems (like the other commands).
2021-03-02 10:56:45 +00:00
Thomas Adam
48131c6d02 Merge branch 'obsd-master' into master 2021-03-01 20:01:20 +00:00
Thomas Adam
9d99dad9e8 Merge branch 'obsd-master' into master 2021-03-01 17:54:47 +00:00
jmc
de3a898e8a escape quotes and remove some unneccessary Pp; ok nicm 2021-03-01 17:49:08 +00:00
Nicholas Marriott
9cd45ddad3 Reinstate del_curterm ifdef bits. 2021-03-01 10:51:24 +00:00
nicm
8a4a2153fd There is no need to call del_curterm in the server anymore. 2021-03-01 10:50:14 +00:00
nicm
b6dfb9996a Add some text with examples of ; as a separator, GitHub issues 2522 and
2580.
2021-03-01 10:44:38 +00:00
Thomas Adam
ba9f89c44e Merge branch 'obsd-master' into master 2021-02-27 08:01:20 +00:00
nicm
40ad11b2b5 Handle NULL term_type. 2021-02-27 06:28:16 +00:00
Thomas Adam
a7d4703bfe Merge branch 'obsd-master' into master 2021-02-27 00:01:19 +00:00
nicm
583aaebc0a Check session, window, pane in the right order when working out format type. 2021-02-26 21:53:41 +00:00
Thomas Adam
9c6502fcc9 Merge branch 'obsd-master' into master 2021-02-26 10:01:22 +00:00
nicm
dd7006c850 Add a couple of format variables for active and last window index. 2021-02-26 07:53:26 +00:00
Thomas Adam
9710ec5244 Merge branch 'obsd-master' into master 2021-02-24 10:01:19 +00:00
nicm
6aaef3e705 Correct client_prefix so it returns 1 if in prefix, not 0. 2021-02-24 09:22:15 +00:00
Thomas Adam
0982a1a975 Merge branch 'obsd-master' into master 2021-02-22 12:01:18 +00:00
nicm
6d8efe9319 expand_paths needs the global environment to be set up, do that first. 2021-02-22 11:42:50 +00:00
Thomas Adam
0cd5ed9e9b Merge branch 'obsd-master' into master 2021-02-22 10:01:22 +00:00
nicm
5f425ee318 Fix regex searching with wrapped lines, from Anindya Mukherjee; GitHub
issue 2570.
2021-02-22 08:31:19 +00:00
nicm
6876381276 Move config file path expansion much earlier, keep the list of paths
around rather than freeing later, and add a config_files format variable
containing it. Suggested by kn@ a while back.
2021-02-22 08:18:13 +00:00
Thomas Adam
cb7e6698f3 Merge branch 'obsd-master' into master 2021-02-22 08:01:22 +00:00
nicm
e858270006 There are many format variables now so allocating all the default ones
each time a tree is created is too expensive. Instead, convert them all
into callbacks and put them in a static table so they only allocate on
demand. The tree remains for the moment for extra (non-default)
variables added by for example copy mode or popups. Also reduce
expensive calls to localtime_r/strftime. GitHub issue 2253.
2021-02-22 07:09:06 +00:00
nicm
8986c8dfcd Move jump commands to grid reader, make them UTF-8 aware, and tidy up,
from Anindya Mukherjee.
2021-02-22 06:53:04 +00:00
Thomas Adam
742e670805 Merge branch 'obsd-master' into master 2021-02-19 10:01:19 +00:00
nicm
b04f8acb70 Check return value of chdir() to stop a silly warning with some
compilers, GitHub issue 2573.
2021-02-19 09:09:16 +00:00
Thomas Adam
11e404ca3c Merge branch 'obsd-master' into master 2021-02-18 16:01:18 +00:00
nicm
fb42ae3071 Reduce len when moving past spaces in OSC 11 parameter. 2021-02-18 13:30:24 +00:00
Thomas Adam
3a76a56824 Merge branch 'obsd-master' into master 2021-02-17 09:58:51 +00:00
Thomas Adam
ce5de76592 Merge branch 'obsd-master' into master 2021-02-17 09:58:12 +00:00
nicm
af3ffa9c41 Move the call to setupterm() into the client and have it pass the
results to the server over imsg, means the server does not need to enter
ncurses or read terminfo db. Old clients will not work with a new
server.
2021-02-17 07:18:36 +00:00
nicm
5c275c2a1a Log missing keys when extended keys is on rather than fatal(). 2021-02-16 09:41:55 +00:00
nicm
5df9b3650a In the end UTF-8 did not become a terminal feature, should not be listed
in man page.
2021-02-16 09:40:00 +00:00
nicm
d768fc2553 Make SGR 6 (rapid blink) the same as SGR 5 (blink) and make SGR 21 to
the same as SGR 4:2, it is an old alternative. GitHub issue 2567.
2021-02-15 14:22:35 +00:00
Nicholas Marriott
0526d074d0 OSC 11 test. 2021-02-15 09:40:50 +00:00
nicm
6642706f7b Support X11 colour names and some other variations for OSC 10/11, also
add OSC 110 and 111. GitHub issue 2567.
2021-02-15 09:39:37 +00:00
nicm
632636dba5 Do not care about the server socket closing if exiting anyway. 2021-02-12 06:52:48 +00:00
nicm
2b58c226db Add a couple of helper functions, and flush imsgs on exit. 2021-02-11 09:39:29 +00:00
nicm
79e1984962 O_TRUNC is needed in case file exists. 2021-02-11 09:03:38 +00:00
nicm
e40831a002 Move file handling protocol stuff all into file.c so it can be reused
more easily.
2021-02-11 08:28:45 +00:00
Nicholas Marriott
679b2288e8 Restore utf8proc bits that went missing, GitHub issue 2564. 2021-02-10 17:18:37 +00:00
Thomas Adam
5b6d4c4fd1 Merge branch 'obsd-master' into master 2021-02-10 08:01:20 +00:00
nicm
32186950f5 Use ~/.tmux.conf as an example rather than /etc/passwd, suggested by
deraadt@.
2021-02-10 07:17:07 +00:00
Thomas Adam
c6215b55e0 Merge branch 'obsd-master' into master 2021-02-09 16:01:18 +00:00
nicm
1492ae11a5 Do not expand times and #() inside #(). 2021-02-09 14:25:40 +00:00
Thomas Adam
8d7f341a85 Merge branch 'obsd-master' into master 2021-02-08 16:01:21 +00:00
nicm
e3005e5ec4 Add "pipe" variants of the "copy-pipe" commands which do not copy, from
Christian Zangl.
2021-02-08 14:46:53 +00:00
Thomas Adam
fe3ab51b78 Merge branch 'obsd-master' into master 2021-02-08 10:01:20 +00:00
nicm
c579be1f2a Include "focused" in client flags, from Dan Aloni in GitHub issue 2558. 2021-02-08 08:33:54 +00:00
Thomas Adam
03430887be Merge branch 'obsd-master' into master 2021-02-06 14:01:20 +00:00
nicm
1e29ebd412 In the end UTF-8 did not become a terminal feature, should not be listed
in man page.
2021-02-06 13:02:52 +00:00
Thomas Adam
39904f7fc6 Merge branch 'obsd-master' into master 2021-02-05 14:01:23 +00:00
nicm
3dddc11603 Send Unicode directional isolate characters around horizontal pane
borders if the terminal support UTF-8 and an extension terminfo(5)
capability "Bidi" is present. On terminals with BiDi support (ie, VTE)
this seems to be enough to display right-to-left text acceptably enough
to be usable (with some caveats about the mouse position). Requested by
and with help from Mahmoud Elagdar in GitHub issue 2425.
2021-02-05 12:29:18 +00:00
nicm
be471c328e Add a -S flag to new-window to make it select the existing window if one
with the given name already exists rather than failing with an error.
Also add a format to check if a window or session name exists which
allows the same with other commands. Requested by and discussed with
kn@.
2021-02-05 12:23:49 +00:00
Nicholas Marriott
e3d71d9bdf Add compat clock_gettime for older macOS. GitHub issue 2555. 2021-02-05 11:01:21 +00:00
Thomas Adam
3dd2e85075 Merge branch 'obsd-master' into master 2021-02-04 16:01:18 +00:00
nicm
c13f2e1135 Redraw status line and borders on pane enable/disable, GitHub issue 2554. 2021-02-04 14:02:24 +00:00
Thomas Adam
86955dbfe1 Merge branch 'obsd-master' into master 2021-02-02 14:01:19 +00:00
nicm
f0546b0ff8 Fix popup mouse position. 2021-02-02 13:03:03 +00:00
Thomas Adam
a5d69ab4b0 Merge branch 'obsd-master' into master 2021-02-02 08:01:19 +00:00
jmc
5c48086e5c article fixes; from eddie youseph 2021-02-02 07:33:29 +00:00
Thomas Adam
0242513ce7 Merge branch 'obsd-master' into master 2021-02-01 10:01:20 +00:00
nicm
509221520c Add a no-detached choice to detach-on-destroy which detaches only if
there are no other detached sessions to switch to, from Sencer Selcuk in
GitHub issue 2553.
2021-02-01 08:01:14 +00:00
Thomas Adam
545a610c6b Merge branch 'obsd-master' into master 2021-01-29 12:01:21 +00:00
nicm
255802d8d7 Trim output overwritten by later text or clears completely rather than
only in a few cases. This means we can better track when a line should
wrap. GitHub issue 2537.
2021-01-29 09:48:43 +00:00
Thomas Adam
ffc159a456 Merge branch 'obsd-master' into master 2021-01-27 12:01:21 +00:00
nicm
8156d9ba41 Flush pending output before entering or exiting alternate screen rather
than leaking it, oss-fuzz issue 29959.
2021-01-27 10:42:52 +00:00
Thomas Adam
70a6af6287 Merge branch 'obsd-master' into master 2021-01-26 10:01:18 +00:00
nicm
d6542c333d Always resize the original screen before copying when exiting the
alternate screen, GitHub issue 2536.
2021-01-26 09:32:52 +00:00
Thomas Adam
3b51abcf22 Merge branch 'obsd-master' into master 2021-01-22 14:01:18 +00:00
Thomas Adam
0898a868c6 Merge branch 'obsd-master' into master 2021-01-22 12:01:21 +00:00
nicm
9fcf413d87 Revert clear changes to writing as they don't work properly, better
change to come.
2021-01-22 11:28:33 +00:00
nicm
bba71f696f Add rectangle-on and rectangle-off copy mode commands, GitHub isse 2546
from author at will dot party.
2021-01-22 10:24:52 +00:00
nicm
8d185395e4 Fix some cursor movement commands, from Anindya Mukherjee. 2021-01-22 10:21:24 +00:00
Thomas Adam
9ff017e908 Merge branch 'obsd-master' into master 2021-01-20 08:01:22 +00:00
nicm
fb774b77d0 Change so that window_flags escapes # automatically which means configs
will not have to change. A new format window_raw_flags contains the old
unescaped version.
2021-01-20 07:16:54 +00:00
Thomas Adam
82423975df Merge branch 'obsd-master' into master 2021-01-18 14:01:21 +00:00
Thomas Adam
66da51b631 Merge branch 'obsd-master' into master 2021-01-18 12:01:22 +00:00
Nicholas Marriott
3c86fa2ad0 Add -Wno-format-y2k. 2021-01-18 11:14:37 +00:00
nicm
0730dce5ab Hide some warnings on newer GCC versions, GitHUb issue 2525. 2021-01-18 11:14:23 +00:00
Nicholas Marriott
63f4a3c4e5 Extra result message. 2021-01-18 10:48:49 +00:00
nicm
91d112bf12 There is no need to clear every line entirely before drawing to it, this
means moving the cursor and messes up wrapping. Better to just clear the
sections that aren't written over. GitHub issue 2537.
2021-01-18 10:27:54 +00:00
Nicholas Marriott
4148417a2a PKG_CHECK_MODULES needs to be separate. 2021-01-17 19:03:18 +00:00
Nicholas Marriott
607594f6e5 Show config.log on failure. 2021-01-17 18:47:14 +00:00
Nicholas Marriott
b18834be8a Revert "Set CFLAGS also."
This reverts commit 032723c874.
2021-01-17 18:24:52 +00:00
Nicholas Marriott
032723c874 Set CFLAGS also. 2021-01-17 18:21:54 +00:00
Nicholas Marriott
c6bcf3dba5 Fix yes/no for b64_ntop check. 2021-01-17 18:20:15 +00:00
Thomas Adam
24c15eda79 Merge branch 'obsd-master' into master 2021-01-17 18:01:21 +00:00
Nicholas Marriott
d4866d5fe6 Fix SEARCH_LIBS. 2021-01-17 17:55:14 +00:00
Nicholas Marriott
603280cb28 +compat.h 2021-01-17 17:52:10 +00:00
Nicholas Marriott
a3011be0d2 Look for libevent2 differently from libevent for platforms with both. 2021-01-17 17:21:51 +00:00
nicm
71c590a37f Add -N flag to never start server even if command would normally do so,
GitHub issue 2523.
2021-01-17 16:17:41 +00:00
Thomas Adam
dc1e1125a5 Merge branch 'obsd-master' into master 2021-01-08 12:01:21 +00:00
nicm
a75aca4d6a Missed from last commit. 2021-01-08 10:09:44 +00:00
Thomas Adam
9df33bc536 Merge branch 'obsd-master' into master 2021-01-08 10:01:20 +00:00
nicm
b96c5e3687 With incremental search, start empty and only repeat the previous search
if the user tries to search again with an empty prompt. This matches
emacs behaviour more closely.
2021-01-08 08:22:10 +00:00
Thomas Adam
fc28e2065a Merge branch 'obsd-master' into master 2021-01-06 10:01:22 +00:00
nicm
199689954b Insert joined pane before the target pane with -b, like for split. From
Takeshi Banse.
2021-01-06 07:32:23 +00:00
nicm
ccb8b9eb2a Remove unused variable, from Ben Boeckel. 2021-01-06 07:29:49 +00:00
Thomas Adam
5a2db4c7e8 Merge branch 'obsd-master' into master 2021-01-04 10:01:20 +00:00
nicm
bd0fb22f0a Add a variant of remain-on-exit that only keeps the pane if the program
failed, GitHub issue 2513.
2021-01-04 08:43:16 +00:00
Thomas Adam
f04cc39976 Merge branch 'obsd-master' into master 2021-01-01 10:01:21 +00:00
nicm
606bd5f8c6 Add a -C flag to run-shell to use a tmux command rather than a shell command. 2021-01-01 08:36:51 +00:00
Thomas Adam
f72deb092a Merge branch 'obsd-master' into master 2020-12-30 22:01:23 +00:00
nicm
f97305af31 Use right format for session loop, GitHub issue 2519. 2020-12-30 18:29:40 +00:00
Thomas Adam
3cbe186efb Merge branch 'obsd-master' into master 2020-12-28 12:01:21 +00:00
nicm
a98ee00dd9 Do not list user options with show-hooks. 2020-12-28 09:40:27 +00:00
nicm
c68baaad98 Remove current match indicator which can't work anymore since we only
search the visible region. From Anindya Mukherjee, GitHub issue 2508.
2020-12-28 09:36:26 +00:00
Thomas Adam
d936fde7ef Makefile.am: add grid-reader.c
Add grid-reader.c to Makefile.am so it's included for compilation.
2020-12-24 22:22:10 +00:00
Thomas Adam
70a0eb3a22 Merge branch 'obsd-master' into master 2020-12-24 22:21:21 +00:00
nicm
c43f2dce1b Break cursor movement in grid into a common set of functions that can
handle line wrapping and so on in one place and use them for the obvious
copy mode commands. From Anindya Mukherjee.
2020-12-22 09:22:14 +00:00
Thomas Adam
950e982001 Merge branch 'obsd-master' into master 2020-12-15 10:01:22 +00:00
nicm
8bd29a30bf Make synchronize-panes a pane option and add -U flag to set-option to
unset an option on all panes. GitHub issue 2491 from Rostislav Nesin.
2020-12-15 08:31:50 +00:00
Nicholas Marriott
681c0d2bfb Include compat.h after system headers, GitHub issue 2492. 2020-12-07 12:13:20 +00:00
Thomas Adam
caf096394b Merge branch 'obsd-master' into master 2020-12-07 10:01:20 +00:00
nicm
f6095cad99 Do not include the status line size when working out the character for
the pane status line. GitHub issue 2493.
2020-12-07 09:46:58 +00:00
nicm
ed786309cc Do not clear the wrapped flag on linefeeds if it is already set - this
does not appear to be what applications want. GitHub issue 2478 and 2414.
2020-12-07 09:23:57 +00:00
Thomas Adam
e288ea153c Merge branch 'obsd-master' into master 2020-12-03 08:01:22 +00:00
nicm
fd451aa796 Redraw any visible modes when status line changes so that formats like
the pane title are updated. GitHub issue 2487. Also a man page fix from
jmc.
2020-12-03 07:12:11 +00:00
Thomas Adam
4724702d4e Merge branch 'obsd-master' into master 2020-12-01 12:01:20 +00:00
nicm
f0c1233d4f Leave newlines inside multiline quotes. 2020-12-01 10:48:03 +00:00
Thomas Adam
27634645e9 Merge branch 'obsd-master' into master 2020-12-01 10:01:22 +00:00
nicm
9a74bba007 Make replacement of ##s consistent when drawing formats, whether
followed by [ or not. Add a flag (e) to the q: format modifier to double
up #s and use it for the window_flags format variable so it doesn't end
up escaping any following text. GitHub issue 2485.
2020-12-01 08:12:58 +00:00
Thomas Adam
70a5207bd1 Merge branch 'obsd-master' into master 2020-11-30 16:01:22 +00:00
nicm
33046ecee2 Ignore running command when checking for no-hooks flag if it is blocked.
GitHub issue 2483.
2020-11-30 13:37:45 +00:00
Thomas Adam
bf8aa9804b Merge branch 'obsd-master' into master 2020-11-26 14:01:19 +00:00
nicm
fd5c3e6122 Fix check for vertical centre. 2020-11-26 13:06:21 +00:00
Thomas Adam
82fbff4e08 Merge branch 'obsd-master' into master 2020-11-26 10:01:21 +00:00
nicm
76cfb5f471 Add -N flag to display-panes to ignore keys, GitHub issue 2473. 2020-11-26 09:19:10 +00:00
Nicholas Marriott
2f1578ef83 Update closefrom from OpenSSH for macOS code which is now needed. 2020-11-20 09:05:32 +00:00
Nicholas Marriott
bfdc4373d7 Update closefrom from OpenSSH for macOS code which is now needed. 2020-11-17 17:57:21 +00:00
Thomas Adam
3ee1addbb9 Merge branch 'obsd-master' into master 2020-11-17 10:01:22 +00:00
nicm
0d28ee9274 Log missing keys when extended keys is on rather than fatal(). 2020-11-17 08:13:35 +00:00
Thomas Adam
e94bd5ccff Merge branch 'obsd-master' 2020-11-10 10:01:22 +00:00
nicm
bbab5b7a30 Allow previous-word to scroll onto the first line, noticed by Anindya
Mukherjee.
2020-11-10 08:16:52 +00:00
Nicholas Marriott
3eb1519bd7 Scaffold for oss-fuzz, from Sergey Nizovtsev. 2020-11-09 16:44:39 +00:00
Thomas Adam
0dcb6e5eb4 Merge branch 'obsd-master' 2020-11-09 12:01:21 +00:00
nicm
f1193b4891 If mouse bits change, clear them all and set again to avoid problems
with some bits implying others. GitHub issue 2458.
2020-11-09 10:54:28 +00:00
Thomas Adam
5ddbf0c918 Merge branch 'obsd-master' 2020-11-09 10:01:27 +00:00
nicm
61e55fa50d Change how escaping is processed for formats so that ## and # can be
used in styles. Also add a 'w' format modifier for the width. From Chas
J Owens IV in GitHub issue 2389.
2020-11-09 09:10:10 +00:00
Nicholas Marriott
72c46aa15e Add support for Haiku, from David Carlier. GitHub issue 2453. 2020-11-09 09:00:41 +00:00
nicm
1326529f99 Remove some old debug logging. 2020-11-09 08:42:43 +00:00
Thomas Adam
dac285c92a Merge branch 'obsd-master' 2020-11-03 10:01:21 +00:00
Nicholas Marriott
572a6b21b5 Back to 3.3. 2020-11-03 08:41:24 +00:00
Nicholas Marriott
5306bb0db7 Update to 3.2-rc3, bring in all the changes from master. 2020-11-03 08:37:08 +00:00
Nicholas Marriott
ba9962b568 Merge branch 'master' into 3.2-rc 2020-11-03 08:35:52 +00:00
nicm
9d83c5e948 Expand menu and popup -x and -y as a format, from teo_paul1 at yahoo dot
com in GitHub issue 2442.
2020-11-03 08:09:35 +00:00
Thomas Adam
ff53eed402 Merge branch 'obsd-master' 2020-11-02 10:01:20 +00:00
nicm
ac5045a00f Add numeric comparisons for formats, from teo_paul1 at yahoo dot com in
GitHub issue 2442.
2020-11-02 08:21:30 +00:00
Thomas Adam
ffe39edf2f Merge branch 'obsd-master' 2020-10-30 20:01:20 +00:00
nicm
95841ba16a With csh, a tmux client gets SIGTERM before SIGCONT when killed with
"kill %%", so when the client tells the server it got SIGCONT, don't use
bits that may already have been freed when it got SIGTERM. Also don't
print anything on exit if we get SIGTERM while suspended. Reported by
Theo.
2020-10-30 18:54:23 +00:00
Thomas Adam
d064060904 Merge branch 'obsd-master' 2020-10-30 14:01:20 +00:00
Nicholas Marriott
0b8ae4de5c Update CHANGES. 2020-10-30 12:29:40 +00:00
nicm
9726c4454e Do not allow disabled items to be selected. 2020-10-30 12:00:01 +00:00
nicm
8e1d28453d Limit range of repeat to avoid silly high numbers causing delays, from
Sergey Nizovtsev.
2020-10-30 11:34:13 +00:00
nicm
02197f20d0 Do not leak path when freeing screen, from Sergey Nizovtsev. 2020-10-30 11:33:41 +00:00
Thomas Adam
970e8f734c Merge branch 'obsd-master' 2020-10-30 10:01:22 +00:00
Nicholas Marriott
ce2b6ff40e Style trim test (currently failing). 2020-10-30 09:25:41 +00:00
nicm
910457f68d There is no reason not to fire focus events when a pane is in a mode,
GitHub issue 2372.
2020-10-30 09:00:07 +00:00
nicm
649e5970e9 Add a -O flag to display-menu to change the mouse behaviour and not
close the menu when the mouse is released, from teo_paul1 at yahoo dot
com.
2020-10-30 08:55:56 +00:00
nicm
733abfcfc5 Do not write after the end of the array and overwrite the stack when
colon-separated SGR sequences contain empty arguments. Reported by Sergey
Nizovtsev.
2020-10-30 08:17:38 +00:00
Thomas Adam
7f321a4cc6 Merge branch 'obsd-master' 2020-10-29 18:01:21 +00:00
nicm
a868bacb46 Do not write after the end of the array and overwrite the stack when
colon-separated SGR sequences contain empty arguments. Reported by Sergey
Nizovtsev.
2020-10-29 16:33:01 +00:00
nicm
7a4aa14618 Do not require that there be no other clients before loading the config,
being the first client is enough. GitHub issue 2438.
2020-10-29 14:10:24 +00:00
Thomas Adam
d5fac75667 Merge branch 'obsd-master' 2020-10-29 14:05:21 +00:00
nicm
977cf3cf69 Set RGB flag if capabilities are present, GitHub issue 2418. 2020-10-29 13:48:03 +00:00
nicm
07ffed8b6f Fix note for "previous-window" default key binding, from Sebastian
Falbesoner.
2020-10-29 13:47:50 +00:00
nicm
7ffb414299 Client could be NULL in select-window (for example in .tmux.conf), do
not set latest session if so. GitHub issue 2429 from Han Boetes.
2020-10-29 13:47:40 +00:00
nicm
3c298b98ce SIGQUIT handler needs to be cleared before fork like the others,
reported by Simon Andersson.
2020-10-29 13:47:24 +00:00
nicm
b33a302235 Do not require that there be no other clients before loading the config,
being the first client is enough. GitHub issue 2438.
2020-10-28 10:09:10 +00:00
Thomas Adam
20e89605c9 Merge branch 'obsd-master' 2020-10-26 20:01:18 +00:00
nicm
31ed29e551 SIGQUIT handler needs to be cleared before fork like the others,
reported by Simon Andersson.
2020-10-26 19:00:37 +00:00
Thomas Adam
9a5b2c93a7 Merge branch 'obsd-master' 2020-10-19 10:01:18 +01:00
nicm
d8cda9286f Client could be NULL in select-window (for example in .tmux.conf), do
not set latest session if so. GitHub issue 2429 from Han Boetes.
2020-10-19 06:39:28 +00:00
Thomas Adam
6125800876 Merge branch 'obsd-master' 2020-10-13 12:01:20 +01:00
nicm
4c8706d399 Fix note for "previous-window" default key binding, from Sebastian
Falbesoner.
2020-10-13 10:15:23 +00:00
Thomas Adam
9ab81e1879 Merge branch 'obsd-master' 2020-10-13 10:01:19 +01:00
nicm
d603dbdef0 Set RGB flag if capabilities are present, GitHub issue 2418. 2020-10-13 07:29:24 +00:00
Thomas Adam
30601d11a8 Merge branch 'obsd-master' 2020-10-09 22:01:22 +01:00
tim
4dc76e084b Escape ! in Ql
OK jmc@ nicm@, agreement from schwarze@
2020-10-09 19:12:36 +00:00
Thomas Adam
7ca0b9ddfa Merge branch 'obsd-master' 2020-10-07 10:01:19 +01:00
Nicholas Marriott
991d5a9c74 Add compat for getdtablesize, GitHub issue 2406. 2020-10-07 09:39:43 +01:00
nicm
3afcc6faac Allow fnmatch(3) wildcards in update-environment, GitHub issue 2397. 2020-10-07 08:23:55 +00:00
Thomas Adam
57168f3f59 Merge branch 'obsd-master' 2020-10-06 10:01:20 +01:00
nicm
7e319756d2 Fix a last minute change in previous. 2020-10-06 07:36:42 +00:00
nicm
e369f64669 Add a state struct to store working state during format expansion
instead of modiyfing the format tree.

Use this to disable nested job expansion so that the result of #() is
not expanded again. Reported by Chas J Owens IV, GitHub issue 2390.
2020-10-06 07:36:05 +00:00
Nicholas Marriott
680e7a382f glibc's malloc is very bad about returning memory from the kernel, add a call
to its malloc_trim to prompt it to do so. Reported by Sarunas Valaskevicius.
2020-10-06 08:18:42 +01:00
Thomas Adam
847a061e31 Merge branch 'obsd-master' 2020-10-05 14:01:19 +01:00
nicm
8d9ea1b97c Trim "s from process names; also fix a default format in man page. 2020-10-05 11:04:40 +00:00
Thomas Adam
f5b7ebc540 Merge branch 'obsd-master' 2020-10-05 12:01:23 +01:00
nicm
1479e32e1a Tidy the resize code, merge some common bits and add some comments. From
"Mike" in GitHub issue 2392.
2020-10-05 10:00:51 +00:00
nicm
c8f3736b07 Use the setal capability as well as (tmux's) Setulc. 2020-10-05 09:53:01 +00:00
Nicholas Marriott
92a2e7411f Link to install wiki page. 2020-10-01 09:01:42 +01:00
Nicholas Marriott
9ec68db74f Correct break-pane default format, from Gregory Pakosz. 2020-09-30 14:17:27 +01:00
Nicholas Marriott
f43e3e5b4f Next version. 2020-09-30 13:36:58 +01:00
Nicholas Marriott
86433c6fb5 Merge tag '3.2-rc' into master
3.2-rc
2020-09-30 13:36:26 +01:00
Nicholas Marriott
cf8ef63c4a Fix some warnings, GitHub issue 2382. 2020-09-30 13:35:51 +01:00
nicm
ec9e03d09a Old Terminal.app versions do not respond correctly to secondary DA,
instead responding with the primary DA response. Ignore it. Reported by
Dave Vandervies.
2020-09-30 13:33:02 +01:00
Nicholas Marriott
3bece648bd Trim "s from process names, from Gregory Pakosz. 2020-09-30 13:27:50 +01:00
nicm
68c2d5c48d Escape+Up and the other arrow keys should be kept as Escape+Up and not
converted to M-Up. Do not give them the implied meta flag so they don't
match the M-Up entry in the output key tree. Fixes problem with vi
reported by jsing@.
2020-09-30 13:27:43 +01:00
Nicholas Marriott
f70eda3817 Check if UNIX 03 is needed for CMSG_DATA, for newer Solaris. From Eric N Vander
Weele.
2020-09-30 13:27:35 +01:00
Nicholas Marriott
a880237141 Also pkg-config. 2020-09-30 13:27:25 +01:00
Nicholas Marriott
9d597390ca Mention build dependencies, based on a change from Mateusz Urbanek. 2020-09-30 13:27:21 +01:00
Nicholas Marriott
5f50e7d942 Trim "s from process names, from Gregory Pakosz. 2020-09-30 13:26:31 +01:00
Nicholas Marriott
4f638c0e31 Check if UNIX 03 is needed for CMSG_DATA, for newer Solaris. From Eric N Vander
Weele.
2020-09-25 10:04:52 +01:00
Nicholas Marriott
e2e5169f84 Also pkg-config. 2020-09-25 09:43:35 +01:00
Nicholas Marriott
ebf27f6900 Mention build dependencies, based on a change from Mateusz Urbanek. 2020-09-25 09:41:41 +01:00
Thomas Adam
476c185997 Merge branch 'obsd-master' 2020-09-23 18:01:17 +01:00
nicm
f2dfc2759e Escape+Up and the other arrow keys should be kept as Escape+Up and not
converted to M-Up. Do not give them the implied meta flag so they don't
match the M-Up entry in the output key tree. Fixes problem with vi
reported by jsing@.
2020-09-23 14:57:33 +00:00
Thomas Adam
dc0c3aa391 Merge branch 'obsd-master' 2020-09-22 18:01:18 +01:00
nicm
d6680b9474 Move a sentence to the right command. 2020-09-22 15:45:20 +00:00
Thomas Adam
859a4c3460 Merge branch 'obsd-master' 2020-09-22 10:01:17 +01:00
nicm
b9392d5cb1 Do not wrap at end of text when positioning at end of match because the
length may include trailing spaces.
2020-09-22 08:41:27 +00:00
Thomas Adam
a34ceb1074 Merge branch 'obsd-master' 2020-09-22 08:01:18 +01:00
nicm
51909a107f Resize screen to the correct size (borders need to be taken off). 2020-09-22 06:44:52 +00:00
nicm
86d6ac2f06 Fix warnings on some platforms with %llx and add a new message to handle
64-bit client flags.
2020-09-22 05:23:34 +00:00
Thomas Adam
ca5afb34bf Merge branch 'obsd-master' 2020-09-18 14:01:19 +01:00
nicm
88b66e9e28 Free buffer earlier to avoid confusing some compilers, GitHub issue
2382.
2020-09-18 11:23:29 +00:00
nicm
ed946dccc7 Some other warnings, GitHub issue 2382. 2020-09-18 11:20:59 +00:00
Thomas Adam
eea85fb4c7 Merge branch 'obsd-master' 2020-09-16 22:01:18 +01:00
nicm
3206869ea5 Add -q flag to unbind-key to hide errors, GitHub issue 2381. 2020-09-16 19:12:59 +00:00
Thomas Adam
c3e1b841f9 Merge branch 'obsd-master' 2020-09-16 20:01:19 +01:00
nicm
869c0e860f Fix some warnings, GitHub issue 2382. 2020-09-16 18:37:55 +00:00
Thomas Adam
3c1f34c208 Merge branch 'obsd-master' 2020-09-08 12:01:17 +01:00
nicm
1fed7e84a3 Allow -N without a command to change or add a note to an existing key. 2020-09-08 10:19:19 +00:00
Thomas Adam
634a2bb647 Merge branch 'obsd-master' 2020-09-04 14:01:18 +01:00
nicm
9b45ba82fd calloc cb data so the client is NULL. 2020-09-04 12:24:25 +00:00
Nicholas Marriott
233d14f4da Hide warnings due to Apple's stupidity with __dead, reported by Kurtis Rader. 2020-09-04 08:37:11 +01:00
Thomas Adam
2e931d4994 Merge branch 'obsd-master' 2020-09-03 14:01:18 +01:00
nicm
eadf18b9fa Do not free old session working directory until after expanding the new
one because it may be needed.
2020-09-03 12:47:33 +00:00
Thomas Adam
cbbf5febff Merge branch 'obsd-master' 2020-09-02 20:01:17 +01:00
nicm
e538bef757 Check started flag before looking for capability. 2020-09-02 17:19:58 +00:00
Thomas Adam
fc9ecdc665 Merge branch 'obsd-master' 2020-09-02 16:01:20 +01:00
nicm
37b1600d9c Add a -w flag to set- and load-buffer to send to clipboard using OSC 52.
GitHub issue 2363.
2020-09-02 13:46:35 +00:00
Thomas Adam
a0fee328bf Merge branch 'obsd-master' 2020-09-01 12:01:20 +01:00
nicm
60860aced8 Add -F to set-environment and source-file; GitHub issue 2359. 2020-09-01 09:19:01 +00:00
nicm
b2a262e353 Only print below number when there is enough space. 2020-09-01 08:50:14 +00:00
Thomas Adam
b895ffbf37 Merge branch 'obsd-master' 2020-08-27 10:01:20 +01:00
nicm
2ab289980a Add pane_last format, GitHub issue 2353. 2020-08-27 06:55:54 +00:00
Thomas Adam
655134f77c Merge branch 'obsd-master' 2020-08-25 14:01:19 +01:00
nicm
20fcdcfea1 Allow colour to be spelt as color, from Boris Verkhovsky. GitHub issue
2317.
2020-08-25 11:35:32 +00:00
Thomas Adam
aa084bb49e Merge branch 'obsd-master' 2020-08-24 08:01:17 +01:00
nicm
e4a4fcfc90 Old Terminal.app versions do not respond correctly to secondary DA,
instead responding with the primary DA response. Ignore it. Reported by
Dave Vandervies.
2020-08-24 05:23:30 +00:00
nicm
43e3e53908 Do not run off end of string when stripping delays, reported by Dave
Vandervies.
2020-08-24 05:22:28 +00:00
Thomas Adam
769ae10658 Merge branch 'obsd-master' 2020-08-20 20:01:19 +01:00
nicm
d0957529ed Add n: modifier to get length of a format, also automatically expand
variable name arguments again if they contain a #{.
2020-08-20 16:57:40 +00:00
Thomas Adam
5a55d1390a Merge branch 'obsd-master' 2020-08-19 10:01:19 +01:00
nicm
d8b6560cbf Set alert flag for the current window if the session is unattached.
GitHub issues 1182 and 2299. From Eric Garver.
2020-08-19 07:15:42 +00:00
nicm
f08bfa7cd1 Respond to colour requests if a colour is available, from Michal Goral. 2020-08-19 06:37:23 +00:00
Thomas Adam
93eb2c8c53 Merge branch 'obsd-master' 2020-08-07 10:01:19 +01:00
nicm
212c0c1f72 Do not force line width to grid width because it may need to be larger
to accomodate a wide character. GitHub issue 2336.
2020-08-07 07:02:57 +00:00
Thomas Adam
9f0973b711 Merge branch 'obsd-master' 2020-08-05 12:01:21 +01:00
nicm
df7fbcd7a5 Change searching to behave more like emacs and so that regex searching
doesn't overlap when searching forwards.
2020-08-05 09:11:09 +00:00
Thomas Adam
8d7e127b17 Merge branch 'obsd-master' 2020-08-04 12:01:18 +01:00
nicm
82c65e3f37 Also ignore SIGQUIT so it can't be used to kill the client when locked. 2020-08-04 08:50:01 +00:00
Thomas Adam
98aa835079 Merge branch 'obsd-master' 2020-07-30 10:01:18 +01:00
nicm
944177eec3 Trim newline from ctime, from Thomas Adam. 2020-07-30 07:32:52 +00:00
Thomas Adam
2fdd5fa507 Merge branch 'obsd-master' 2020-07-27 10:01:21 +01:00
nicm
40e65c5115 Add a -d option to display-message to set delay, from theonekeyg at
gmail dot com in GitHub issue 2322.
2020-07-27 08:03:10 +00:00
Thomas Adam
90158b5977 Merge branch 'obsd-master' 2020-07-24 10:01:19 +01:00
Nicholas Marriott
31bc4c4346 3.2-rc version. 2020-07-24 08:38:34 +01:00
nicm
d329b035ce Add a hook when the pane title changed. 2020-07-24 07:05:37 +00:00
Thomas Adam
1f19355fb0 Merge branch 'obsd-master' 2020-07-23 16:01:17 +01:00
nicm
112b0f417c Check all lists if option not found already. 2020-07-23 14:17:56 +00:00
Thomas Adam
e0d984597b Merge branch 'obsd-master' 2020-07-22 08:01:18 +01:00
nicm
1f5e520def Correct checks for window borders. 2020-07-22 06:21:46 +00:00
Thomas Adam
90de0c1a9b Merge branch 'obsd-master' 2020-07-21 08:01:19 +01:00
nicm
743ab5728d Fix show-buffer when run from inside tmux, GitHub issue 2314. 2020-07-21 05:24:33 +00:00
Thomas Adam
70a674dde1 Merge branch 'obsd-master' 2020-07-18 06:01:20 +01:00
daniel
3b089fc69f Properly escape a backslash.
Found by CompCert which notes that \E is not a valid escape sequence.

ok nicm@
2020-07-18 02:53:47 +00:00
Thomas Adam
ab8d685302 Merge branch 'obsd-master' 2020-07-15 14:01:18 +01:00
nicm
5e008eefaa Renumber after killing windows for choose-tree. 2020-07-15 11:03:17 +00:00
Thomas Adam
c973801f92 Merge branch 'obsd-master' 2020-07-15 12:01:19 +01:00
nicm
8f1179d656 Handle padding cells correctly when searching, GitHub issue 2301. 2020-07-15 10:09:54 +00:00
Thomas Adam
405b71e016 Merge branch 'obsd-master' 2020-07-13 12:01:17 +01:00
nicm
468be2a37f Do not dereference NULL environment variable value, GitHub issue 2304. 2020-07-13 10:10:10 +00:00
Thomas Adam
7d6723b5f7 Merge branch 'obsd-master' 2020-07-13 10:01:18 +01:00
nicm
f26b7b7788 Clarify /tmp permissions and use, GitHub issue 2300. 2020-07-13 07:04:17 +00:00
Nicholas Marriott
a5f99e14c6 Update version. 2020-07-06 14:07:11 +01:00
Nicholas Marriott
b30989a964 Pull 3.2-rc up to master. 2020-07-06 14:03:33 +01:00
Thomas Adam
aa4f3a9b3d Merge branch 'obsd-master' 2020-07-06 12:01:22 +01:00
nicm
2aa177d102 Do not eliminate redundant clears, the code is wrong and doing it
correctly wouldn't be worth it. GitHub issue 2298.
2020-07-06 10:07:02 +00:00
nicm
66d5e5de7a Add a way for control mode clients to subscribe to a format and be
notified of changes rather than having to poll. GitHub issue 2242.
2020-07-06 09:14:20 +00:00
Thomas Adam
af82094439 Merge branch 'obsd-master' 2020-07-06 10:01:19 +01:00
nicm
2bf612a806 Always send xterm-style keys for M-Left and M-Right. GitHub issue 2296. 2020-07-06 07:27:39 +00:00
Thomas Adam
c0d9eaff9b Merge branch 'obsd-master' 2020-07-04 16:01:20 +01:00
nicm
1e42689661 kill-window -a cannot just walk the list of windows because if
renumber-windows is on, the window it wants to keep could be moved.
Change to renumber afterwards and also behave better if the window is
linked into the session twice. GitHub issue 2287.
2020-07-04 14:24:02 +00:00
Nicholas Marriott
a109e839d1 Fix version. 2020-07-03 12:03:25 +01:00
Nicholas Marriott
6b01eac774 Merge branch '3.2-rc' 2020-07-03 12:03:07 +01:00
nicm
5661346c41 Missing word, from annihilannic at hotmail dot com, GitHub issue 2288. 2020-07-03 12:02:52 +01:00
nicm
0d0fc13aaa Check if client is NULL before using it, GitHub issue 2295. 2020-07-03 12:02:46 +01:00
bket
83868ceb1a Replace TAILQ concatenation loop with TAILQ_CONCAT
As a result remove unneeded variables

OK @nicm
2020-07-03 12:02:37 +01:00
Thomas Adam
0fa306d73c Merge branch 'obsd-master' 2020-07-03 10:01:20 +01:00
nicm
43e1577b5d Missing word, from annihilannic at hotmail dot com, GitHub issue 2288. 2020-07-03 07:07:50 +00:00
nicm
2b1e8d06e1 Check if client is NULL before using it, GitHub issue 2295. 2020-07-03 07:00:12 +00:00
Thomas Adam
a284664e71 Merge branch 'obsd-master' 2020-06-29 18:01:18 +01:00
bket
2a9bdb700d Replace TAILQ concatenation loop with TAILQ_CONCAT
As a result remove unneeded variables

OK @nicm
2020-06-29 15:53:28 +00:00
nicm
629ba1b838 Check for no pane border status line separately from top/bottom. 2020-06-29 09:20:39 +01:00
nicm
c9b4d5a4a5 Fix 0x Unicode character parsing, GitHub issue 2286. 2020-06-29 09:20:32 +01:00
nicm
6cacaa94a5 Silently ignore -a or -b if the window index doesn't exist and create
using that index (this is how it used to work), reported by Romain
Francoise.
2020-06-29 09:20:25 +01:00
Thomas Adam
332aca754b Merge branch 'obsd-master' 2020-06-27 12:01:18 +01:00
nicm
b6aeb86c20 Check for no pane border status line separately from top/bottom. 2020-06-27 10:23:10 +00:00
nicm
74df7071ad Fix 0x Unicode character parsing, GitHub issue 2286. 2020-06-27 10:19:59 +00:00
Thomas Adam
04a1a84bb8 Merge branch 'obsd-master' 2020-06-25 12:01:17 +01:00
nicm
f69bdda950 Silently ignore -a or -b if the window index doesn't exist and create
using that index (this is how it used to work), reported by Romain
Francoise.
2020-06-25 08:56:02 +00:00
nicm
43295bd4a5 Correctly redraw pane border bottom line when the status line is on and
at the bottom, reported by Kaushal Modi.
2020-06-23 16:32:40 +01:00
nicm
e215a566a4 Use xvasprintf not vasprintf. 2020-06-23 16:32:40 +01:00
Thomas Adam
2a2ebf315f Merge branch 'obsd-master' 2020-06-23 16:01:18 +01:00
nicm
5340bf556e Correctly redraw pane border bottom line when the status line is on and
at the bottom, reported by Kaushal Modi.
2020-06-23 14:10:43 +00:00
Thomas Adam
1f515663d1 Merge branch 'obsd-master' 2020-06-23 08:01:18 +01:00
nicm
2964dde903 Use xvasprintf not vasprintf. 2020-06-23 05:23:26 +00:00
Nicholas Marriott
e450416c93 3.2-rc. 2020-06-22 12:55:10 +01:00
Nicholas Marriott
b730083d7a Add to CHANGES. 2020-06-22 12:53:43 +01:00
Nicholas Marriott
3a1fc7315c Add getline compat. 2020-06-18 21:01:55 +01:00
Nicholas Marriott
3df68d6b00 Fix regress test for am. 2020-06-18 21:01:45 +01:00
Thomas Adam
6c437d45ac Merge branch 'obsd-master' 2020-06-18 12:01:22 +01:00
nicm
068b92b051 The redraw callback could be fired with a NULL pane if it updates while
in a mode, problem reported by Martin Vahlensieck.
2020-06-18 08:41:56 +00:00
nicm
2372b0fdc6 Add a flag to make a client wait for an empty line before exiting in
control mode to avoid stray commands ending up in the shell.
2020-06-18 08:34:22 +00:00
Thomas Adam
eb448daa1a Merge branch 'obsd-master' 2020-06-16 10:01:21 +01:00
nicm
1bf9555e4f d and D keys to reset to default in customize mode. 2020-06-16 08:18:34 +00:00
nicm
afe4ea4250 Correctly move to previous line when looking for previous word, from
Derry Jing.
2020-06-16 07:28:57 +00:00
Thomas Adam
824efe7be4 Merge branch 'obsd-master' 2020-06-13 12:01:20 +01:00
nicm
1c78155e70 Add -b flags to insert a window before (like the existing -a for after)
to break-pane, move-window, new-window. GitHub issue 2261.
2020-06-13 09:05:53 +00:00
Thomas Adam
4000052d92 Merge branch 'obsd-master' 2020-06-12 12:01:17 +01:00
nicm
d52ac7d027 Do not wait on shutdown for commands started with run -b. 2020-06-12 10:31:12 +00:00
Thomas Adam
bd3fb2fb10 Merge branch 'obsd-master' 2020-06-12 10:01:19 +01:00
nicm
d8d7769104 Check if a pane needs to be paused when output is written rather than
just when it is queued.
2020-06-12 08:35:01 +00:00
nicm
4c3bdc5a36 move-pane also defaults to marked pane now, reported by Ben Challenor. 2020-06-12 07:52:38 +00:00
nicm
cf63465eb0 Fix quoting with newlines and single quotes. 2020-06-12 07:10:43 +00:00
Thomas Adam
b5c86fdc0c Merge branch 'obsd-master' 2020-06-11 22:01:23 +01:00
nicm
63c2ed1483 Add some formats for search in copy mode (search_present, search_match).
GitHub issue 2268.
2020-06-11 19:43:34 +00:00
Thomas Adam
2e5b496053 Merge branch 'obsd-master' 2020-06-11 14:01:19 +01:00
Thomas Adam
e8c99496cd Merge branch 'obsd-master' 2020-06-11 12:01:19 +01:00
nicm
cf13d1e110 Fix a crash when completing sessions, from Anindya Mukherjee. 2020-06-11 10:56:19 +00:00
nicm
50ee41423f Add a -A option to pause a pane manually. 2020-06-11 09:55:47 +00:00
Thomas Adam
ea4425b9bd Merge branch 'obsd-master' 2020-06-10 10:01:20 +01:00
nicm
23d79cfda8 Instead of a buffer size limit on each pane, set a limit of 300 seconds
of data for each client in control mode.
2020-06-10 07:27:10 +00:00
Thomas Adam
8b673cc4f2 Merge branch 'obsd-master' 2020-06-10 08:01:19 +01:00
nicm
fddcad6957 When the pause-after flag is set, send an alternative %extended-output
form instead of %output with the age of the output.
2020-06-10 06:23:43 +00:00
Thomas Adam
208d9449b7 Merge branch 'obsd-master' 2020-06-09 12:01:18 +01:00
nicm
fee585ea14 Include width in error message. 2020-06-09 10:37:00 +00:00
Thomas Adam
30eaf885c4 Merge branch 'obsd-master' 2020-06-09 10:01:17 +01:00
nicm
c60389acbf It is not sensible to store pointers into an array we are going to
realloc (duh), use two trees instead.
2020-06-09 08:34:33 +00:00
Thomas Adam
9ffdcc7656 Merge branch 'obsd-master' 2020-06-06 14:01:17 +01:00
nicm
a4a3d89598 Use bitshifts instead of a union for encoding UTF-8 into 32 bits, which
is more friendly to GCC3.

Reported by and ok aoyama@.
2020-06-06 12:38:32 +00:00
Nicholas Marriott
0d8ba2e57f Add to CHANGES. 2020-06-05 14:56:55 +01:00
Thomas Adam
92c8cc17c3 Merge branch 'obsd-master' 2020-06-05 14:01:19 +01:00
nicm
d919fa1ed0 Change how panes are resized so that the code is clearer and if the pane
is resized multiple times during one event loop, it is forced to resize
at the end. Also don't zoom/unzoom in switch-client if the pane hasn't
changed. GitHub issue 2260.
2020-06-05 11:20:51 +00:00
Thomas Adam
47a5afe5bf Merge branch 'obsd-master' 2020-06-05 12:01:18 +01:00
nicm
03b2998abe Do not take the address of a potentially unaligned member. 2020-06-05 09:35:41 +00:00
nicm
c908d2039f Fix various confusion about am vs xenl. 2020-06-05 09:32:15 +00:00
Thomas Adam
a06cf900c7 Merge branch 'obsd-master' 2020-06-05 10:01:22 +01:00
nicm
4e5e2c19d0 Now that we mostly only search visible text, the rate limit on repeating
search does not seem to be necessary, remove it for the moment.
2020-06-05 07:44:42 +00:00
nicm
c586208991 Add support for pausing a pane when the output buffered for a control
mode client gets too far behind. The pause-after flag with a time is set
on the pane with refresh-client -f and a paused pane may be resumed with
refresh-client -A. GitHub issue 2217.
2020-06-05 07:33:57 +00:00
Thomas Adam
976cf6c60f Merge branch 'obsd-master' 2020-06-05 00:01:19 +01:00
nicm
d9cd493d09 Reset wrapped flag when clearing or moving lines, GitHub issue 2215. 2020-06-04 21:41:31 +00:00
nicm
2154e1f4fb Search marks outside the visible text are not useful, so there is no
point in allocating a big buffer to store them - just allocate the
visible text size, and ignore any outside.
2020-06-04 21:40:27 +00:00
Thomas Adam
8b43fcd82b Merge branch 'obsd-master' 2020-06-04 22:01:17 +01:00
nicm
c4732af006 Some improvements to performance of searching:
- Do not allow searches to be repeated at intervals of less than 50
  milliseconds, to prevent a huge queue of repeat key presses blocking
  up everything for ages.

- If the search text hasn't changed, the match count can't have changed
  and there is no need to do a full search, so only search the visible
  text. This includes both scrolling and repeating the search.

- Do not redraw twice when jumping to the search location.

GitHub issue 2258.
2020-06-04 20:41:57 +00:00
Thomas Adam
c8bddfba2b Merge branch 'obsd-master' 2020-06-04 18:01:17 +01:00
nicm
4403afe29c A } can go on the same line as a command. 2020-06-04 16:06:01 +00:00
Thomas Adam
81ba6477ff Merge branch 'obsd-master' 2020-06-04 12:01:20 +01:00
Nicholas Marriott
23a3742dc8 Update CHANGES. 2020-06-04 11:43:11 +01:00
nicm
4ea3370316 Shorten some long lines. 2020-06-04 10:36:28 +00:00
nicm
a9bf5367da Correct respawn-* - they don't always use the creation command. 2020-06-04 10:34:40 +00:00
nicm
dc74d2e054 Make the -no-clear command variants not clear the search marks either. 2020-06-04 10:24:14 +00:00
Thomas Adam
b0a6025897 Merge branch 'obsd-master' 2020-06-04 10:01:20 +01:00
nicm
d3c5202f50 Allow strings to span multiple lines - newlines and any leading
whitespace are removed, as well as any following comments that couldn't
be part of a format. This allows long formats or other strings to be
annotated and indented.
2020-06-04 08:30:44 +00:00
nicm
b3782d2dc8 Instead of using a custom parse function to process {}, treat it as a
set of statements and parse with yacc, then convert back to a string as
the last step. This means the rules are consistent inside and outside
{}, %if and friends work at the right time, and the final result isn't
littered with unnecessary newlines.
2020-06-04 07:12:05 +00:00
Thomas Adam
f837dcdd58 Merge branch 'obsd-master' 2020-06-03 18:01:17 +01:00
nicm
3f6af4156f Make paste -p the default for ], GitHub issue 2248. 2020-06-03 16:35:40 +00:00
Thomas Adam
62c0617d79 Merge branch 'obsd-master' 2020-06-03 00:01:20 +01:00
Thomas Adam
3d5decb305 Merge branch 'obsd-master' 2020-06-02 22:01:20 +01:00
nicm
4694e9a2b6 Move the code to set up a padding cell into grid.c. 2020-06-02 20:51:46 +00:00
nicm
2a4d4bda2b Allow UTF-8 characters of width 0 to be stored, it is useful to be able
to put padding cells in as width 0.
2020-06-02 20:10:23 +00:00
nicm
f5366ff828 Missing ; in previous. 2020-06-02 19:16:46 +00:00
nicm
5fbae8c8c6 Fire copy-pipe command even if there is no text, means it works if it
has side effects.
2020-06-02 19:10:26 +00:00
Thomas Adam
ebe866c378 Merge branch 'obsd-master' 2020-06-02 20:01:18 +01:00
nicm
7e501f1993 UTF-8 keys need to be big endian so the size bits are at the top. 2020-06-02 17:17:44 +00:00
Thomas Adam
049ccb4a4b Merge branch 'obsd-master' 2020-06-02 14:01:17 +01:00
nicm
822ee4e0a6 Fail rather than fatal on UTF-8 width 0. 2020-06-02 11:29:00 +00:00
Thomas Adam
31a51cae1d Merge branch 'obsd-master' 2020-06-02 11:29:48 +01:00
nicm
f3931497f8 Use CLOCK_MONOTONIC for timer measurement and add a timestamp to control
mode %output blocks.
2020-06-02 08:17:27 +00:00
nicm
563b7331da Remove blocks from queue when pane disappears. 2020-06-01 21:08:05 +00:00
nicm
8339702d47 Check the right thing for maximum client buffer size. 2020-06-01 20:58:42 +00:00
nicm
9819470058 Change format callback to return value rather than storing it in the entry. 2020-06-01 19:39:25 +00:00
Thomas Adam
191a836560 Merge branch 'obsd-master' 2020-06-01 18:01:18 +01:00
nicm
674ec410b7 Try without cursor/keypad flags if a key doesn't exist, and limit ctrl
key translation to ASCII keys. Fixes send-keys, GitHub issue 2247.
2020-06-01 16:09:35 +00:00
Thomas Adam
91e40de2da Merge branch 'obsd-master' 2020-06-01 12:01:20 +01:00
nicm
a54a88edd6 Instead of sending all data to control mode clients as fast as possible,
add a limit of how much data will be sent to the client and try to use
it for panes with some degree of fairness. GitHub issue 2217, with
George Nachman.
2020-06-01 09:43:00 +00:00
Thomas Adam
5ef790a6c4 Merge branch 'obsd-master' 2020-05-29 16:01:19 +01:00
nicm
175e45005f Add -i to find-window to ignore case. 2020-05-29 13:42:13 +00:00
Nicholas Marriott
ce6b3a539d utf8proc_unicode_version is too new. 2020-05-28 08:41:56 +01:00
Thomas Adam
86159fca82 Merge branch 'obsd-master' 2020-05-27 16:01:18 +01:00
nicm
bda2a0282a Fix ASCII keys with send-keys -l. 2020-05-27 14:45:35 +00:00
Thomas Adam
38ce6adab6 Merge branch 'obsd-master' 2020-05-27 08:01:19 +01:00
nicm
f336599a3a Make padding cell a valid character. 2020-05-27 06:23:23 +00:00
Thomas Adam
fd8652ceda Merge branch 'obsd-master' 2020-05-26 16:01:19 +01:00
nicm
2ced370bee Tweak some out of date bits, reported by bcgraham. 2020-05-26 13:19:21 +00:00
Thomas Adam
a4cb700269 Merge branch 'obsd-master' 2020-05-26 14:02:15 +01:00
nicm
ff6f2ff6d9 Return new character properly when converting to data. 2020-05-26 12:50:03 +00:00
nicm
392b381d1c Apply -n when only one pane in the window. 2020-05-26 09:01:03 +00:00
nicm
370f0bb98d Remove leftover debug logging and fix comparison. 2020-05-26 08:56:48 +00:00
nicm
ca0166f26f Do not try to use the last marked pane if it is invalid. 2020-05-26 08:47:50 +00:00
nicm
ea610a3119 Pass the stdout file descriptor from the client as well as stdin and use
them for control clients directly instead of passing everything via the
client.
2020-05-26 08:41:47 +00:00
Nicholas Marriott
bc2e0cf7ff Remove bad merge. 2020-05-26 08:54:05 +01:00
Nicholas Marriott
d73fcfc176 Put the fix back for wcwidth() failing. 2020-05-26 08:49:36 +01:00
Thomas Adam
967e5f8be3 Merge branch 'obsd-master' 2020-05-26 08:42:55 +01:00
Nicholas Marriott
fd4d3e8793 Set IUTF8 again when it exists. 2020-05-26 06:15:57 +01:00
nicm
6f03e49e68 Use the internal representation for UTF-8 keys instead of wchar_t and
drop some code only needed for that.
2020-05-25 18:57:24 +00:00
nicm
35779d655d Fix definition of padding cells so they are not extended cells. 2020-05-25 18:55:36 +00:00
nicm
49ec074271 Tidy up new UTF-8 code and make it more generic. 2020-05-25 18:19:29 +00:00
nicm
dc893405e1 Fix some error strings, from Kris Katterjohn. 2020-05-25 18:17:14 +00:00
Thomas Adam
b34af611a5 Merge branch 'obsd-master' 2020-05-25 18:01:20 +01:00
nicm
bbfb44e9b2 Make some data types consistent. 2020-05-25 15:02:25 +00:00
Thomas Adam
32ce468b22 Merge branch 'obsd-master' 2020-05-25 14:01:19 +01:00
nicm
4589297e43 Do not attempt to divide by zero when working out copy position. 2020-05-25 12:12:58 +00:00
nicm
26e8e467e8 Include title for the width of the menu. 2020-05-25 11:59:50 +00:00
Thomas Adam
bb5a1925ab Merge branch 'obsd-master' 2020-05-25 12:01:19 +01:00
nicm
3a5219c6d0 Instead of storing all UTF-8 characters in the extended cell which means
that 14 bytes are wasted for each character in the BMP, only store
characters of three bytes or less in the cell itself and store others
(outside the BMP or with combining characters) in a separate global
tree. Can reduce grid memory use for heavy Unicode users by around 30%.
2020-05-25 09:32:10 +00:00
Thomas Adam
e16191dbfc Merge branch 'obsd-master' 2020-05-24 16:01:20 +01:00
nicm
14a9fd58d5 Remove leftover call to control_free_offsets and do not use for
non-control clients.
2020-05-24 14:45:00 +00:00
Thomas Adam
aeefd585a0 Merge branch 'obsd-master' 2020-05-24 12:01:21 +01:00
nicm
18aab90959 Give control code its own state struct. 2020-05-24 09:40:17 +00:00
nicm
6c82982711 Now the tty has a pointer back to the client there is no point (and a
bit confusing) in it keeping a copy of the fd as well. Remove it.
2020-05-24 09:13:06 +00:00
Thomas Adam
e71c5efd58 Merge branch 'obsd-master' 2020-05-22 18:01:20 +01:00
Nicholas Marriott
2ac6cc2633 Put headers back how they were. 2020-05-22 17:34:30 +01:00
Nicholas Marriott
a48cc458a6 Maybe this is better. 2020-05-22 17:27:07 +01:00
Nicholas Marriott
1fdacba111 Think Solaris needs term.h here. 2020-05-22 17:24:42 +01:00
Nicholas Marriott
b3e5a99c8f And tweak again. 2020-05-22 17:22:03 +01:00
Nicholas Marriott
6ae26a6b54 Fix utf8proc version logging. 2020-05-22 17:20:35 +01:00
Nicholas Marriott
87a59efc94 Log ncurses and utf8proc versions. 2020-05-22 17:14:35 +01:00
nicm
033d6472cb FocusIn keys can also update the latest client, like normal keys. 2020-05-22 15:43:38 +00:00
nicm
a06a0e1392 xterm* can have focus too. 2020-05-22 15:08:38 +00:00
Thomas Adam
d48f0e114e Merge branch 'obsd-master' 2020-05-22 13:40:33 +01:00
nicm
9a0763c3a0 Move client offset stuff into control.c since only control clients will
need it.
2020-05-22 11:07:04 +00:00
Thomas Adam
2420bd8584 spawn.c: fix up bad merge 2020-05-21 09:02:36 +01:00
Thomas Adam
40126ee96c Merge branch 'obsd-master' 2020-05-21 08:55:31 +01:00
nicm
31e3f2d530 Support code for control mode flow control: allow clients to have
separate offsets (used and acknowleged) into the pane buffers; turn off
reading from panes when no clients can accept the data; and add a -A
flag to refresh-client to let clients turn receiving a pane on and off.
2020-05-21 07:24:13 +00:00
Nicholas Marriott
98a18d064a Fix a regression test, size is not updated until end of event loop. 2020-05-21 08:20:37 +01:00
Thomas Adam
5ac5cd995e Merge branch 'obsd-master' 2020-05-20 10:01:19 +01:00
nicm
6bde1c1837 Fix a couple more places where the key flags need to be masked off. 2020-05-20 07:11:45 +00:00
Thomas Adam
fa835339fd Merge branch 'obsd-master' 2020-05-20 08:01:18 +01:00
nicm
b53e60f4c6 Remove a redundant if statement. 2020-05-20 06:18:22 +00:00
nicm
2a9e2b556a Key strings need to include the cursor and keypad flags now since the
output key lookup expects them already set.
2020-05-20 06:13:09 +00:00
Thomas Adam
fb9e53ba19 Merge branch 'obsd-master' 2020-05-19 14:01:17 +01:00
Nicholas Marriott
e10f5a72ce Add FreeBSD CI, from Jan Beich. 2020-05-19 12:34:34 +01:00
nicm
8425084b8a Some other ctrl keys need to be translated with extended keys on. 2020-05-19 10:59:09 +00:00
Nicholas Marriott
dc56b3cd32 No paths.h. 2020-05-16 20:27:00 +01:00
Thomas Adam
2bc05db54f remove vis.h: portable doesn't need this 2020-05-16 18:42:53 +01:00
Thomas Adam
bd87f6bf00 Merge branch 'obsd-master' 2020-05-16 18:36:35 +01:00
nicm
574a9e4b6c Move lazy resize from the pane to the window, there is no point in
resizing the window unless it is the current window, and if we do and
don't resize the pane until later there are problems if the size changes
from A to B then back to A.
2020-05-16 16:50:55 +00:00
nicm
844b363baf On select-window, make this client the latest client for the window. 2020-05-16 16:45:55 +00:00
nicm
0ab82d9531 Add a terminal feature for enable/disable extended keys (supported by
xterm and mintty) and add an option to make tmux send it. Only forward
extended keys if the application has requested them, even though we use
the CSI u sequence and xterm uses CSI 27 ~ - this is what mintty does as
well.
2020-05-16 16:44:54 +00:00
nicm
292b335ca5 Separate key flags and modifiers, log key flags, make the "xterm" flag
more explicit and fix M- keys with a leading escape.
2020-05-16 16:35:13 +00:00
nicm
e2a26740b9 Add an option to set the pane border lines style from a choice of single
lines (ACS or UTF-8), double or heavy (UTF-8), simple (plain ASCII) or
number (the pane numbers). Lines that won't work on a non-UTF-8 terminal
are translated back into ACS when they are output.
2020-05-16 16:26:34 +00:00
nicm
ecbdcc256f Add screen write flags instead of individual bits and fix line length
calculation with padding.
2020-05-16 16:22:01 +00:00
nicm
303d342d5f Add a client flag 'active-pane' which stores the active pane in the
client and allows it to be changed independently from the real active
pane stored in the window. This is can be used with session groups which
allow an independent current window (although it would be nice to have a
flag for this too and remove session groups). The client active pane is
only really useful interactively, many things (hooks, window-style,
zooming) still use the window active pane.
2020-05-16 16:20:59 +00:00
nicm
c914abfa19 Expand target from client and use it to expand the prompt. 2020-05-16 16:16:07 +00:00
nicm
72984c4834 Move editor stuff to common code in popup.c. 2020-05-16 16:13:09 +00:00
nicm
ff8dd150e0 Add a mark in copy mode. Set with set-mark command (bound to 'X') by
default and the mark and cursor position are swapped with 'jump-to-mark'
(bound to M-x). The line containing the mark is shown in
copy-mode-mark-style with the horizontal position in reverse.

From Anindya Mukherjee in GitHub issue 2209.
2020-05-16 16:10:28 +00:00
nicm
dceb6a15d0 Add a -D flag to ask tmux not to daemonize, useful both for running a
debugger (lldb does not have follow-fork-mode) and for running with a
managed supervisor init system. GitHub issue 2190.
2020-05-16 16:07:55 +00:00
nicm
126bacb473 Do not loop forever when search finds an empty match, GitHub issue 2203. 2020-05-16 16:03:57 +00:00
nicm
592f141dee Fix next-matching-bracket logic, from Chris Barber. 2020-05-16 16:03:30 +00:00
nicm
d67245c734 Add a customize mode where keys and options may be browsed and changed,
includes adding a brief description of each option. Bound to "C" by
default.
2020-05-16 16:02:24 +00:00
nicm
472d77fd0f Support embedded styles in the display-message message, GitHub issue
2206.
2020-05-16 15:54:20 +00:00
nicm
6ea6d46d0a Store and restore cursor position when copy mode is resized, from
Anindya Mukherjee.
2020-05-16 15:49:20 +00:00
nicm
daa95810b5 Allow a custom time format to be given to the t format modifier. 2020-05-16 15:48:35 +00:00
nicm
367b4e4e0f Change message log to be per server rather than per client and include
every command that is run.
2020-05-16 15:47:22 +00:00
nicm
4de0bd4c5c Add M-+ and M-- to expand and collapse all items in tree mode. 2020-05-16 15:46:01 +00:00
nicm
cf9baddd6f Change the existing client flags for control mode to apply for any
client, use the same mechanism for the read-only flag and add an
ignore-size flag.

refresh-client -F has become -f (-F stays for backwards compatibility)
and attach-session and switch-client now have -f flags also. A new
format "client_flags" lists the flags and is shown by list-clients by
default.

This separates the read-only flag from "ignore size" behaviour (new
ignore-size) flag - both behaviours are useful in different
circumstances.

attach -r and switchc -r remain and set or toggle both flags together.
2020-05-16 15:45:29 +00:00
nicm
469eda7e44 Only redraw popup on the client it belongs to. 2020-05-16 15:41:54 +00:00
nicm
4e053685df Export TERM_PROGRAM and TERM_PROGRAM_VERSION like various other
terminals.
2020-05-16 15:40:44 +00:00
nicm
beb214bcb3 Add formats for after hook command arguments. 2020-05-16 15:40:04 +00:00
nicm
d056144aa1 Try to search the entire history first for up to 200 ms so a search
count can be shown. If it takes too long, search the visible text only.
2020-05-16 15:38:14 +00:00
nicm
3fb4d4df43 Do not need to work out status line offset, we already have it. 2020-05-16 15:36:57 +00:00
nicm
2df75aa117 Use VIS_CSTYLE for paste buffers also. 2020-05-16 15:35:19 +00:00
nicm
9605b080f6 Do not hoke into struct window_pane from the tty code and instead set
everything up in tty_ctx. Provide a way to initialize the tty_ctx from a
callback and use it to let popups draw directly through input_parse in
the same way as panes do, rather than forcing a full redraw on every
change.
2020-05-16 15:34:08 +00:00
nicm
379ca54c80 Rename and tidy some stuff in struct tty_ctx. 2020-05-16 15:27:08 +00:00
nicm
edeb81ba9e Add -e for new-session to set environment variables. 2020-05-16 15:25:24 +00:00
nicm
78595457f9 Add 'e' key in buffer mode to open the buffer in an editor. 2020-05-16 15:24:28 +00:00
nicm
a3cbc014c3 Use formats for status-style and message-style. 2020-05-16 15:19:04 +00:00
nicm
58fb81d19a Complete partial window indexes properly. 2020-05-16 15:18:17 +00:00
nicm
463864f5a2 Add -W and -T flags to command-prompt to only complete a window and a
target, also complete aliases.
2020-05-16 15:16:36 +00:00
nicm
2391fe23ab Copy mode search improvements:
- Add styles for the search marking styles (copy-mode-match-style and
  copy-mode-current-match-style).

- Show the current match (the one with the cursor on it) in a different style.

- Copying without a selection will copy the current match if there is one.
2020-05-16 15:11:52 +00:00
nicm
80e52545a0 Improve command prompt completion:
- Show a menu with completions if there are multiple.

- Don't complete argument stuff (options, layouts) at start of text.

- For -t and -s, if there is no : then complete sessions but if there is
  a :, show a menu of all windows in the session rather than trying to
  complete the window name which is a bit useless if there are
  duplicates.
2020-05-16 15:06:03 +00:00
nicm
f03b61131b Drop having a separate type for style options and make them all strings,
which allows formats to be expanded. Any styles without a '#{' are still
validated when they are set but any with a '#{' are not. Formats are not
expanded usefully in many cases yet, that will be changed later.

To make this work, a few other changes:

- set-option -a with a style option automatically appends a ",".

- OSC 10 and 11 don't set the window-style option anymore, instead the
  fg and bg are stored in the pane struct and act as the defaults that
  can be overridden by window-style.

- status-fg and -bg now override status-style instead of trying to keep
  them in sync.
2020-05-16 15:01:30 +00:00
nicm
0487029fc5 Call format_defaults_window for panes as well. 2020-05-16 14:55:38 +00:00
nicm
5bf96c2f2c Use a grid cell not a style for the pane style. 2020-05-16 14:53:23 +00:00
nicm
428137d876 Instead of forbidding invalid session names, sanitize them like window
names.
2020-05-16 14:49:50 +00:00
nicm
7dbe623156 Instead of having a default set of terminals in terminal-overrides that
get XT added and using that as a marker for xterm(1)-like, assume that
if the terminfo(5) entry already has XT or the clear capability starts
with CSI then the terminal is VT100-like and it should be safe to send
DA requests. The DA responses trigger additional features being added.
2020-05-16 14:46:14 +00:00
nicm
21a39c997b Do not redraw or update mode if nothing has changed. 2020-05-16 14:42:06 +00:00
nicm
aebeeec1e9 Add feature and capabilities for focus reporting. Also document AX and
XT even though they aren't tmux's, and add some bits for rxvt.
2020-05-16 14:39:40 +00:00
nicm
26312a7774 Move terminal features into a single file. 2020-05-16 14:30:17 +00:00
nicm
a29196ca6a Build list of paths and weed out duplicates before loading configs, and
add TMUX_SOCK like TMUX_PATH for the socket directory.
2020-05-16 14:26:33 +00:00
nicm
4e0a718666 Add extension terminfo(5) capabilities for margins. 2020-05-16 14:22:51 +00:00
nicm
41dec585df Response is iTerm2 not not ITerm2. 2020-05-16 14:18:39 +00:00
nicm
9dd58470e4 Remove support for iTerm2's DSR 1337 extension and use the CSI > q
extension now supported by a few different terminals.
2020-05-16 14:16:25 +00:00
nicm
471f697423 Add an attribute for ACS. 2020-05-16 14:13:37 +00:00
nicm
0dd1944206 Tweak the default choose modes formats:
- Only show pane title if it is not default and not empty.
- Add a prettier time format and use that instead of long ctime().
- Remove clutter and change the order.
2020-05-16 14:10:29 +00:00
Nicholas Marriott
57fe03dc5a Move lazy resize from the pane to the window, there is no point in resizing the
window unless it is the current window, and if we do and don't resize the pane
until later there are problems if the size changes from A to B then back to A.
2020-05-16 14:57:36 +01:00
Nicholas Marriott
53c84fd4aa If the application has not requested extended keys, then C-1 sends 1 not
nothing.
2020-05-16 07:39:22 +01:00
Nicholas Marriott
740f047a85 Need to update features after all the sequences come in. 2020-05-16 07:32:46 +01:00
Nicholas Marriott
c2167c5ee8 On select-window, make this client the latest client for the window. 2020-05-15 22:52:55 +01:00
Nicholas Marriott
e8ca5a4c7d List needs to be sorted. 2020-05-15 19:17:56 +01:00
Nicholas Marriott
67090dd91d XTerm not xterm. 2020-05-15 19:10:06 +01:00
Nicholas Marriott
401f197750 Er, misread this and it is not needed. 2020-05-15 18:58:13 +01:00
Nicholas Marriott
ca60aabab5 Translate special CSI u keys on input. 2020-05-15 18:25:44 +01:00
Nicholas Marriott
7317a0865c Get == and != the right way round. 2020-05-15 17:49:58 +01:00
Nicholas Marriott
0b828b91a5 Only send XDA on 0. 2020-05-15 17:49:07 +01:00
Nicholas Marriott
dcf537519f Fix default values for new escape sequences. 2020-05-15 17:48:21 +01:00
Nicholas Marriott
c364a7142c Only forward extended keys if the application has requested them, even though
we use the CSI u sequence and xterm uses CSI 27 ~ - this is what mintty does as
well.
2020-05-15 17:40:24 +01:00
Nicholas Marriott
3a4f3ee087 Mask off flags bits in menu keys. 2020-05-15 16:17:20 +01:00
Nicholas Marriott
6d92b99dbc Add a terminal feature for enable/disable extended keys (supported by xterm and
mintty) and add an option to make tmux send it.
2020-05-15 16:15:24 +01:00
Nicholas Marriott
e6b17e77db C-M-S keys need the implied flag also. 2020-05-15 15:11:08 +01:00
Nicholas Marriott
e23c73457a Stop at end of buffer. 2020-05-15 15:05:49 +01:00
Nicholas Marriott
3e60ab1caf Send conventional \033 sequences for keys with just Meta even if they came in
as an extended CSI u key sequence. It is much more useful for applications that
don't understand CSI u to receive \033> for M-S-. rather than \033[62;3u.
2020-05-15 13:15:12 +01:00
Nicholas Marriott
7501e297dd Send CSI u sequences for any keys that do not have a defined sequence already -
this should only be similar sequences sent by the terminal outside tmux if
enabled.
2020-05-15 12:39:37 +01:00
Nicholas Marriott
340fd691cb Separate key flags and modifiers, log key flags, make the "xterm" flag more
explicit and fix M- keys with a leading escape.
2020-05-15 12:16:41 +01:00
Nicholas Marriott
031d4864a9 Rename KEYC_ESCAPE to KEYC_META. 2020-05-15 11:24:30 +01:00
Nicholas Marriott
2cf967ee67 Always set xterm flag. 2020-05-15 11:20:12 +01:00
Nicholas Marriott
e95b644139 Recognise extended key sequences on input (both the forms xterm offers). 2020-05-15 11:09:32 +01:00
Nicholas Marriott
5ee4d991b6 xterm-keys has been on by default for 5 years and all other modern terminals
use these key sequences by default. Merge the code into the main tty and input
tree processing (convering the latter to use a tree rather than a table at the
same time) and make the option a no-op.
2020-05-15 10:31:54 +01:00
Nicholas Marriott
c4d8100b2f Draw outside correctly with pane numbers. 2020-05-14 16:58:14 +01:00
Nicholas Marriott
bef70132ac Check if outside the window before checking if on the border so that cells that
are outside in one direction but not the other are not given the wrong type.
2020-05-14 16:53:04 +01:00
Nicholas Marriott
31621036ad Add an option to set the pane border lines style from a choice of single lines
(ACS or UTF-8), double or heavy (UTF-8), simple (plain ASCII) or number (the
pane numbers). Lines that won't work on a non-UTF-8 terminal are translated
back into ACS when they are output.
2020-05-14 16:49:08 +01:00
Nicholas Marriott
97c8374855 Tweak CHANGES. 2020-05-14 14:07:26 +01:00
Nicholas Marriott
4dc0f3ee6b Update CHANGES. 2020-05-14 14:01:32 +01:00
Nicholas Marriott
09a66451ce Add screen write flags instead of individual bits and fix line length
calculation with padding.
2020-05-14 13:18:05 +01:00
Nicholas Marriott
0bdbf47ef9 Add a client flag 'active-pane' which stores the active pane in the client and
allows it to be changed independently from the real active pane stored in the
window. This is can be used with session groups which allow an independent
current window (although it would be nice to have a flag for this too and
remove session groups). The client active pane is only really useful
interactively, many things (hooks, window-style, zooming) still use the window
active pane.
2020-05-14 11:18:19 +01:00
Nicholas Marriott
12eceaf2b3 Expand target from client and use it to expand the prompt. 2020-05-14 10:35:26 +01:00
Nicholas Marriott
09a2246b00 Use safe loop for freeing client files. 2020-05-13 21:11:46 +01:00
Nicholas Marriott
e6d9f3f90c Add -Z to customize-mode binding. 2020-05-13 20:58:42 +01:00
Nicholas Marriott
3f55d05386 Tidy up border redrawing, fix some errors in how the window border connects
with panes.
2020-05-13 17:49:37 +01:00
Nicholas Marriott
4cb1d3d7a9 Move editor stuff to common code in popup.c. 2020-05-13 06:58:07 +01:00
Nicholas Marriott
6214cd0726 Add a mark in copy mode. Set with set-mark command (bound to 'X') by default
and the mark and cursor position are swapped with 'jump-to-mark' (bound to
M-x). The line containing the mark is shown in copy-mode-mark-style with the
horizontal position in reverse.

From Anindya Mukherjee in GitHub issue 2209.
2020-05-13 06:29:57 +01:00
Thomas Adam
9d44df9da9 Merge branch 'master' of github.com:tmux/tmux 2020-05-13 01:15:31 +01:00
Thomas Adam
212cf53ea9 Merge branch 'obsd-master' 2020-05-13 01:05:04 +01:00
Nicholas Marriott
ba20e46bdc Do not log NULL text. 2020-05-12 10:36:31 +01:00
Nicholas Marriott
8d238491d0 Show default key bindings in customize mode. 2020-05-12 10:22:38 +01:00
Nicholas Marriott
5a34f51d33 Include key bindings in customize mode. 2020-05-12 08:57:55 +01:00
Nicholas Marriott
c489bf0a1e Support embedded styles in the display-message message, GitHub issue 2206. 2020-05-11 09:18:35 +01:00
Nicholas Marriott
8502517d30 Add to CHANGES. 2020-05-10 17:06:31 +01:00
Nicholas Marriott
0070313e28 Fix comparison of tty name. 2020-05-10 16:57:33 +01:00
Nicholas Marriott
d01e7aac89 Add a -D flag to ask tmux not to daemonize, useful both for running a debugger
(lldb does not have follow-fork-mode) and for running with a managed supervisor
init system. GitHub issue 2190.
2020-05-10 16:52:46 +01:00
Nicholas Marriott
5fa377d927 Do not loop forever when search finds an empty match, GitHub issue 2203. 2020-05-10 10:01:20 +01:00
Nicholas Marriott
79a9a7b931 Fix next-matching-bracket logic, from Chris Barber. 2020-05-09 16:15:08 +01:00
Nicholas Marriott
532d06c399 Initialize return code in case something mysterious happens. 2020-05-09 16:08:13 +01:00
Nicholas Marriott
013d857ef8 Wrap all lines in customize mode, not just the description. 2020-05-09 15:29:14 +01:00
Nicholas Marriott
690d72adb3 Remove unused variables. 2020-05-09 14:38:00 +01:00
Nicholas Marriott
ca18990826 Fix some warnings, from Jan Polensky. 2020-05-09 14:33:25 +01:00
Nicholas Marriott
5e97d79eb1 Fix some customize mode drawing nits. 2020-05-08 22:09:31 +01:00
Nicholas Marriott
a61cbf1c33 Add a customize mode where options may be browsed and changed, includes adding
a brief description of each option. Bound to "C" by default.
2020-05-08 19:10:09 +01:00
nicm
aa7dccf8e1 imsg.h needs uio.h, pointed out by deraadt 2020-05-08 14:15:11 +00:00
Nicholas Marriott
708e9bc072 Allow a custom time format to be given to the t format modifier. 2020-05-07 11:04:43 +01:00
Nicholas Marriott
b0fa36734e Fix pretty time function to actually work and allow time format to be applied
to any string that is suitable.
2020-05-07 09:55:06 +01:00
Nicholas Marriott
63e17d8cad Do not use client if NULL, from Thomas Adam. 2020-05-07 05:55:42 +01:00
Nicholas Marriott
66ecb1dff4 Update CHANGES. 2020-05-06 15:48:27 +01:00
Nicholas Marriott
7a95e9bf7e Change message log to be per server rather than per client and include every
command that is run.
2020-05-06 13:43:22 +01:00
Nicholas Marriott
c80fc6bf9e Add M-+ and M-- to expand and collapse all items in tree mode. 2020-05-05 16:33:58 +01:00
Nicholas Marriott
2f89d2e7d8 Change the existing client flags for control mode to apply for any client, use
the same mechanism for the read-only flag and add an ignore-size flag.

refresh-client -F has become -f (-F stays for backwards compatibility) and
attach-session and switch-client now have -f flags also. A new format
"client_flags" lists the flags and is shown by list-clients by default.

This separates the read-only flag from "ignore size" behaviour (new
ignore-size) flag - both behaviours are useful in different circumstances.

attach -r and switchc -r remain and set or toggle both flags together.
2020-05-05 15:42:20 +01:00
Nicholas Marriott
deacfedc65 Remove an extra space in clients output. 2020-05-05 13:34:53 +01:00
Nicholas Marriott
32c134f5a9 Wrap a line. 2020-05-05 12:39:20 +01:00
Nicholas Marriott
23b4e1b9d8 pane_path is not #T, from Chris Rawnsley. 2020-05-05 11:35:33 +01:00
Nicholas Marriott
e810f15272 Store and restore cursor position when copy mode is resized, from Anindya
Mukherjee.
2020-05-05 10:20:57 +01:00
Nicholas Marriott
1f6c00f8ef Only redraw popup on the client it belongs to. 2020-05-05 10:02:47 +01:00
Nicholas Marriott
63390d2dd6 Export TERM_PROGRAM and TERM_PROGRAM_VERSION like various other terminals. 2020-05-05 06:31:14 +01:00
Nicholas Marriott
9991a14e81 Add formats for after hook command arguments. 2020-05-05 06:19:29 +01:00
Nicholas Marriott
fc13e9bc2b Turn off cursor in menus again. 2020-05-04 17:37:03 +01:00
Nicholas Marriott
a08f1c8c59 Merge branch '3.1b-rc' 2020-05-04 09:14:36 +01:00
Nicholas Marriott
a10c4c60cb Add to CHANGES. 2020-05-04 09:06:57 +01:00
Nicholas Marriott
c89ed7c092 Try to search the entire history first for up to 200 ms so a search count can
be shown. If it takes too long, search the visible text only.
2020-05-03 15:58:29 +01:00
Nicholas Marriott
e7aeb77bd9 Use the cursor position not the current position when working out which marks
are current.
2020-05-03 15:44:38 +01:00
Nicholas Marriott
cb09705df3 Need musl-gcc for static also. 2020-05-03 12:37:38 +01:00
Nicholas Marriott
4fcbd6700f Add musl builds. 2020-05-03 12:33:47 +01:00
Nicholas Marriott
ff5e3d1a88 Ugh, enable not with. 2020-05-03 12:20:08 +01:00
Nicholas Marriott
fbc8fca067 Use termcap instead. 2020-05-03 12:18:04 +01:00
Nicholas Marriott
1cfa6b0d5c Try w/o database. 2020-05-03 12:12:47 +01:00
Nicholas Marriott
b203f7f19f Better ncurses URL and some other fixes. 2020-05-03 12:03:50 +01:00
Nicholas Marriott
97d490204b Add build of everything. 2020-05-03 11:47:28 +01:00
Nicholas Marriott
55901367d0 Add more stuff. 2020-05-03 11:16:30 +01:00
Nicholas Marriott
ff250aa30e Fix exclude. 2020-05-03 11:12:51 +01:00
Nicholas Marriott
955d9d22b9 Add static build. 2020-05-03 11:09:36 +01:00
Nicholas Marriott
daef6f6b67 Merge branch '3.1b-rc' 2020-05-03 10:49:35 +01:00
Nicholas Marriott
846b99e0cb Portable does not need sys/queue.h. 2020-05-03 10:49:16 +01:00
Nicholas Marriott
d9a4449307 Do not need to work out status line offset, we already have it. 2020-05-02 16:44:31 +01:00
Nicholas Marriott
af69289e0e Clamping to area needs to use the offset without the status line, since that is
where the window offsets are based.
2020-05-02 16:17:44 +01:00
Nicholas Marriott
cb1131a294 menu_mode_cb needs to return a screen also. 2020-05-02 15:15:52 +01:00
Nicholas Marriott
e078f975c5 Update CHANGES. 2020-05-01 18:24:20 +01:00
Nicholas Marriott
bf84359dfb Use VIS_CSTYLE for paste buffers also. 2020-05-01 17:59:13 +01:00
Nicholas Marriott
1fa9bcc183 Turn off overlay check when we know we are inside. 2020-05-01 17:55:08 +01:00
Nicholas Marriott
3f1fc9cde3 Get the whole overlay screen not just the mode so cursor changes are included. 2020-05-01 17:30:28 +01:00
Nicholas Marriott
8110c7a25f Do not hoke into struct window_pane from the tty code and instead set
everything up in tty_ctx. Provide a way to initialize the tty_ctx from a
callback and use it to let popups draw directly through input_parse in the same
way as panes do, rather than forcing a full redraw on every change.
2020-05-01 17:01:36 +01:00
Nicholas Marriott
dbebdb2d36 Rename tty_pane_full_width to tty_full_width. 2020-05-01 13:22:08 +01:00
Nicholas Marriott
93dca5ab3f Move size to tty_ctx. 2020-05-01 13:19:05 +01:00
Nicholas Marriott
5ce194f15d Rename some tty_ctx members. 2020-05-01 13:01:55 +01:00
Nicholas Marriott
af21e76fdb Add -e for new-session. 2020-05-01 12:01:58 +01:00
Nicholas Marriott
ec61aa3025 Solaris at least does not have _PATH_VI. 2020-05-01 09:11:56 +01:00
Nicholas Marriott
5c888e168e Typo in man page. 2020-05-01 09:05:56 +01:00
Nicholas Marriott
cc19203be2 Add 'e' key in buffer mode to open the buffer in an editor. 2020-05-01 09:02:44 +01:00
Thomas Adam
7af5817245 Merge branch 'obsd-master' 2020-04-30 16:01:27 +01:00
Nicholas Marriott
3d33a19102 Merge tag '3.1b'
3.1b
2020-04-30 15:21:47 +01:00
Nicholas Marriott
8e01221d02 Allow formats for message-style. 2020-04-30 14:59:58 +01:00
Nicholas Marriott
272f3dbf2e Use format for status-style. 2020-04-30 14:56:46 +01:00
nicm
048f1ff18a Do not remove the automatic-rename option from the global set, only from
the window (it must stay in the global set or tmux will crash). GitHub
issue 2188.
2020-04-30 13:31:22 +00:00
Nicholas Marriott
66bab1f6cf Complete partial window indexes properly. 2020-04-30 13:05:21 +01:00
Nicholas Marriott
25487757bc Add -W and -T flags to command-prompt to only complete a window and a target. 2020-04-30 12:02:21 +01:00
Nicholas Marriott
5af6943940 Complete aliases as well as commands. 2020-04-29 19:55:20 +01:00
Nicholas Marriott
7324442b42 Add to CHANGES. 2020-04-29 19:48:26 +01:00
Nicholas Marriott
6f700904a9 Copy mode search improvements:
- Add styles for the search marking styles (copy-mode-match-style and
  copy-mode-current-match-style).

- Show the current match (the one with the cursor on it) in a different style.

- Copying without a selection will copy the current match if there is one.
2020-04-29 18:08:21 +01:00
Nicholas Marriott
881b8e9bb5 Handle cells outside any pane correctly. 2020-04-29 16:50:20 +01:00
Nicholas Marriott
a9743fa047 Did not mean to commit this bit. 2020-04-29 15:27:38 +01:00
Nicholas Marriott
04033add19 Close menu on backspace with TAB flag. 2020-04-29 15:26:49 +01:00
Nicholas Marriott
d9fa122fd2 Do not want -O0 by default. 2020-04-29 15:21:15 +01:00
Nicholas Marriott
fe601e5417 Update CHANGES. 2020-04-29 15:19:39 +01:00
Nicholas Marriott
b06235c345 Improve command prompt completion:
- Show a menu with completions if there are multiple.

- Don't complete argument stuff (options, layouts) at start of text.

- For -t and -s, if there is no : then complete sessions but if there is a :,
  show a menu of all windows in the session rather than trying to complete the
  window name which is a bit useless if there are duplicates.

Lots of scope for being more sophisticated left here.
2020-04-29 13:56:10 +01:00
Nicholas Marriott
7c52d702e4 Remove an unnecessary comma. 2020-04-29 08:59:20 +01:00
Nicholas Marriott
3d76748161 Need to redraw borders now when some things change. Also change default so that
the active border colour is different in a mode or with synchronize-panes on.
2020-04-29 08:55:21 +01:00
Nicholas Marriott
2d151d8ca5 Apply format to pane status line also. 2020-04-29 08:24:09 +01:00
Nicholas Marriott
24316bed49 Apply a format when redrawing pane borders. 2020-04-29 08:21:29 +01:00
Nicholas Marriott
c1acfb4341 Start with style initialized to default. 2020-04-28 17:27:07 +01:00
Nicholas Marriott
1f8256fc50 Drop having a separate type for style options and make them all strings, which
allows formats to be expanded. Any styles without a '#{' are still validated
when they are set but any with a '#{' are not. Formats are not expanded
usefully in many cases yet, that will be changed later.

To make this work, a few other changes:

- set-option -a with a style option automatically appends a ",".

- OSC 10 and 11 don't set the window-style option anymore, instead the fg and
  bg are stored in the pane struct and act as the defaults that can be
  overridden by window-style.

- status-fg and -bg now override status-style instead of trying to keep them in
  sync.
2020-04-28 13:50:07 +01:00
Nicholas Marriott
a43a156846 Call format_defaults_window for panes as well. 2020-04-28 10:53:35 +01:00
Nicholas Marriott
79b4d83952 Use a grid cell not a style for the pane style. 2020-04-27 15:15:12 +01:00
Nicholas Marriott
c30e765c7b Add some additional format helper functions. 2020-04-27 14:33:17 +01:00
Thomas Adam
e62db55713 Merge branch 'obsd-master' 2020-04-27 10:01:27 +01:00
Nicholas Marriott
266bbba484 Merge branch '3.1a-rc' 2020-04-27 09:39:17 +01:00
nicm
1574126e8a Do not close the stdout file descriptor in control mode as it will be
needed for printing the exit messages. Fixes a bug when detaching with
iTerm2.
2020-04-27 08:35:09 +00:00
Nicholas Marriott
5811dd7ceb Do not close stdout file descriptor in control mode since it will be needed for
printing the exit messages.
2020-04-27 09:33:46 +01:00
Nicholas Marriott
f3d6d4e802 CUD is not a requirement and tweak some comments. 2020-04-24 16:47:38 +01:00
Nicholas Marriott
9b571dacee Instead of forbidding invalid session names, sanitize them. 2020-04-24 16:40:10 +01:00
Nicholas Marriott
527f66ed23 Instead of having a default set of terminals in terminal-overrides that get XT
added and using that as a marker for xterm(1)-like, assume that if the
terminfo(5) entry already has XT or the clear capability starts with CSI then
the terminal is VT100-like and it should be safe to send DA requests. The DA
responses trigger additional features being added.

This is all to detect extensions if terminfo(5) is wrong or inadequate. If it
fails, tmux will just fall back to using the capabilities in the terminfo(5)
entry alone.
2020-04-24 15:52:44 +01:00
Nicholas Marriott
e67d65064e rxvt needs XT also for the moment. 2020-04-24 14:20:33 +01:00
Nicholas Marriott
c107708bcc Focus reporting no longer under XT. 2020-04-24 14:20:17 +01:00
Nicholas Marriott
a477c03ad5 Do not update mode until actually drawing something. 2020-04-24 12:14:53 +01:00
Nicholas Marriott
ae73fd363b Do not redraw at all if nothing has changed. 2020-04-24 11:56:44 +01:00
Nicholas Marriott
bb107d2979 All of this stuff can be const. 2020-04-24 07:47:16 +01:00
Nicholas Marriott
61550ac2e0 Add feature and capabilities for focus reporting. Also document AX and XT even
though they aren't tmux's.
2020-04-24 07:37:11 +01:00
Nicholas Marriott
5d69b9c4a7 Add a feature for bracketed paste. 2020-04-24 07:13:02 +01:00
Nicholas Marriott
2d8fd35de2 Add a feature for strikethrough. 2020-04-24 06:51:15 +01:00
Nicholas Marriott
8650f44340 Move terminal features into a single file. 2020-04-24 06:40:51 +01:00
Thomas Adam
ca13208b6b Merge branch 'obsd-master' 2020-04-24 00:01:27 +01:00
jmc
18886cb510 ce examples of "Ar arg Ar arg" with "Ar arg arg" and stop the spread; 2020-04-23 21:28:09 +00:00
Nicholas Marriott
d1c1e05ea7 Update CHANGES. 2020-04-23 18:27:27 +01:00
Nicholas Marriott
d53e1fedd5 Add TMUX_SOCK like TMUX_PATH for the socket directory. 2020-04-23 18:15:02 +01:00
Nicholas Marriott
0d3fdae7b6 Build list of paths and weed out duplicates before loading configs. 2020-04-23 17:56:45 +01:00
Nicholas Marriott
f87be8d052 Add XDG_CONFIG home to the configuration search paths. 2020-04-23 17:27:39 +01:00
Nicholas Marriott
351c5423f0 time.h is needed. 2020-04-23 16:55:20 +01:00
Nicholas Marriott
ac91635f82 Add extension terminfo(5) capabilities for margins. 2020-04-23 12:12:02 +01:00
Nicholas Marriott
0c73dbb7e1 Response is iTerm2 not not ITerm2. 2020-04-23 12:12:02 +01:00
Thomas Adam
5653bc8287 Merge branch 'obsd-master' 2020-04-23 12:01:26 +01:00
nicm
766b425d05 Overrides need to be applied both before and after features in case they
change flags used to detect a feature.
2020-04-23 10:22:53 +00:00
Nicholas Marriott
c74572da92 Remove support for iTerm2's DSR 1337 extension and use the CSI > q extension
now supported by a few different terminals.
2020-04-23 10:29:03 +01:00
Thomas Adam
e94a15b3d6 Merge branch 'obsd-master' 2020-04-23 10:01:26 +01:00
Nicholas Marriott
1a612a5936 Add an attribute for ACS. 2020-04-23 07:15:17 +01:00
nicm
e25fa4ba1b Fix a couple of memory leaks, one when creating a new pane and one when
adding formats onto the queue item.
2020-04-23 05:48:42 +00:00
nicm
906dfe9f5c Fix a couple of memory leaks, one when creating a new pane and one when
adding formats onto the queue item.
2020-04-23 05:48:42 +00:00
Nicholas Marriott
106e5d07be Tweak the default choose modes formats:
- Only show pane title if it is not default and not empty.
- Add a prettier time format and use that instead of long ctime().
- Remove clutter and change the order.
2020-04-23 06:30:15 +01:00
Thomas Adam
63f2034f29 Merge branch 'obsd-master' 2020-04-23 04:01:30 +01:00
Thomas Adam
cf5f93b2b3 Merge branch 'obsd-master' 2020-04-23 02:01:33 +01:00
nicm
e46cf86d30 Improve join-pane, move-pane and break-pane:
- There is no need for join-pane and move-pane to be different.
- break-pane can just behave like move-window if the source has only one
  pane, instead of failing.
- Add -a to break-pane like move-window.

Also add missing man page bits for previous window-tree.c changes.

GitHub issue 2176.
2020-04-22 21:15:33 +00:00
nicm
950af33636 Improve join-pane, move-pane and break-pane:
- There is no need for join-pane and move-pane to be different.
- break-pane can just behave like move-window if the source has only one
  pane, instead of failing.
- Add -a to break-pane like move-window.

Also add missing man page bits for previous window-tree.c changes.

GitHub issue 2176.
2020-04-22 21:15:33 +00:00
Thomas Adam
4a31eedc26 Merge branch 'obsd-master' 2020-04-22 22:01:35 +01:00
nicm
4b21fd2ed1 Indicate the marked pane in choose mode in reverse and add key to set
and clear it (m and M) and a key to jump to the starting pane (H).
2020-04-22 21:01:28 +00:00
nicm
899b3d2436 Indicate the marked pane in choose mode in reverse and add key to set
and clear it (m and M) and a key to jump to the starting pane (H).
2020-04-22 21:01:28 +00:00
nicm
662728d6c7 Add a session_marked format like window_marked. 2020-04-22 20:47:00 +00:00
Thomas Adam
b4a21b5d80 Merge branch 'obsd-master' 2020-04-22 14:01:33 +01:00
Nicholas Marriott
bb31776dd3 Merge branch '3.1-rc' 2020-04-22 13:00:43 +01:00
Nicholas Marriott
ecb6db6b6a Update CHANGES. 2020-04-22 12:59:10 +01:00
Nicholas Marriott
d4826aa1aa Nope, OS X kqueue is still broken...
This reverts commit 94c90385d2.
2020-04-22 12:18:11 +01:00
Nicholas Marriott
df1bce40f0 Call the event_init wrapper again. 2020-04-22 12:09:25 +01:00
Thomas Adam
8ae3915cc2 Merge branch 'obsd-master' 2020-04-22 12:01:38 +01:00
nicm
4b5a16567a Update the cursor position when deleting lines from screens without
history, GitHub issue 2173.
2020-04-22 08:48:44 +00:00
nicm
b72498c4ff Update the cursor position when deleting lines from screens without
history, GitHub issue 2173.
2020-04-22 08:48:44 +00:00
nicm
5935100181 Change so main-pane-width and height can be given as a percentage. 2020-04-22 06:57:13 +00:00
nicm
de5163a634 Change so main-pane-width and height can be given as a percentage. 2020-04-22 06:57:13 +00:00
Thomas Adam
dd5299841a Merge branch 'obsd-master' 2020-04-21 16:01:30 +01:00
nicm
445dfa8512 Move the background colour to clear with (if any) up as well as the data
when scrolling, redraw problem reported by sthen@.
2020-04-21 13:48:56 +00:00
Thomas Adam
291b85746f Merge branch 'obsd-master' 2020-04-21 14:01:25 +01:00
Thomas Adam
2cbca7ce16 Merge branch 'obsd-master' 2020-04-21 12:01:35 +01:00
nicm
9a60d41db4 256 and RGB features can imply AX (for aixterm colours). 2020-04-21 10:37:11 +00:00
nicm
bd91015b13 256 and RGB features can imply AX (for aixterm colours). 2020-04-21 10:37:11 +00:00
Thomas Adam
985bc0ee4a Merge branch 'obsd-master' 2020-04-21 08:01:30 +01:00
nicm
18671a27b6 Turn off the block flag to reset the state or the cursor will not be
moved back to the right place.
2020-04-21 06:34:13 +00:00
nicm
57bd6e0447 Turn off the block flag to reset the state or the cursor will not be
moved back to the right place.
2020-04-21 06:34:13 +00:00
nicm
d524cb64e7 Do not clear client pane redraw flags until the redraw actually happens. 2020-04-21 06:32:40 +00:00
nicm
d0b8f5340e Do not clear client pane redraw flags until the redraw actually happens. 2020-04-21 06:32:40 +00:00
nicm
1cabccbb2b xterm* needs XT also. 2020-04-21 05:26:13 +00:00
Thomas Adam
32c3fe40eb Merge branch 'obsd-master' 2020-04-20 17:43:20 +01:00
Thomas Adam
c706aadf52 Merge branch 'obsd-master' 2020-04-20 17:42:29 +01:00
nicm
4a5182e665 Always start sync for output in panes that are not the active pane. 2020-04-20 15:49:05 +00:00
nicm
117ec1b2e6 Apply terminal-overrides after terminal detection, it always takes
precedence.
2020-04-20 15:37:32 +00:00
nicm
2083a6ea20 Change how sync works to always send the end sequence after all output
is done when we are returning to the event loop (since we always move
the cursor at that point). Also a man fix from jmc.
2020-04-20 14:59:31 +00:00
nicm
135bb1edee Change the Sync capability to be a string instead of a flag. 2020-04-20 13:38:48 +00:00
nicm
c91b4b2e14 Tidy up the terminal detection and feature code and add named sets of
terminal features, each of which are defined in one place and map to a
builtin set of terminfo(5) capabilities. Features can be specified based
on TERM with a new terminal-features option or with the -T flag when
running tmux. tmux will also detect a few common terminals from the DA
and DSR responses.

This is intended to make it easier to configure tmux's use of
terminfo(5) even in the presence of outdated ncurses(3) or terminfo(5)
databases or for features which do not yet have a terminfo(5) entry.
Instead of having to grok terminfo(5) capability names and what they
should be set to in the terminal-overrides option, the user can
hopefully just give tmux a feature name and let it do the right thing.

The terminal-overrides option remains both for backwards compatibility
and to allow tweaks of individual capabilities.

tmux already did much of this already, this makes it tidier and simpler
to configure.
2020-04-20 13:25:36 +00:00
Thomas Adam
3898d4e7c8 Merge branch 'obsd-master' 2020-04-20 12:01:32 +01:00
nicm
86862c976a Also redraw panes which aren't pane 0. Problem reported by tb@. 2020-04-20 09:07:55 +00:00
Thomas Adam
b1be668a3e Merge branch 'obsd-master' 2020-04-20 08:01:31 +01:00
nicm
b846ec2665 Only trim blank lines when the source pane is not the target pane,
otherwise the cursor moves which is a bit strange.
2020-04-20 06:08:37 +00:00
nicm
4bc0a83d51 Need to check for pane redrawing even if just the window flag is set
(the pane flag may not have been previously set to avoid looping the
windows).
2020-04-20 06:07:39 +00:00
Thomas Adam
8c9bbc3749 Merge branch 'obsd-master' 2020-04-19 00:01:31 +01:00
nicm
62ff5e4b01 The PANE_REDRAW flag bit might be needed by other panes so we can't
clear it on the first redraw, and it can't be set when we are finished
or they would be redrawn again, so if the redraw is deferred for a
client, copy the redraw flag into a separate set of bits just for that
client.
2020-04-18 21:35:32 +00:00
Thomas Adam
7da5418758 Merge branch 'obsd-master' 2020-04-18 20:01:29 +01:00
nicm
100db552d1 A resize can realloc so cannot cache the value of the list pointer. 2020-04-18 17:20:25 +00:00
Thomas Adam
2b83ee5557 Merge branch 'obsd-master' 2020-04-18 18:01:28 +01:00
nicm
deffef6f13 Reset background colour on scrolled line. 2020-04-18 15:22:05 +00:00
nicm
ea5fdd5331 There is no point in keeping a bunch of different text buffers for each
line when writing, we only need one as big as the line width - there
can't be any more text than that since newer will overwrite older.
2020-04-18 15:12:28 +00:00
Thomas Adam
2ccf15e5d8 Merge branch 'obsd-master' 2020-04-18 16:01:34 +01:00
nicm
4a93294152 Use size_t not u_int for the bytes counters and fix a const missing. 2020-04-18 14:21:39 +00:00
Thomas Adam
d55510ebc8 Merge branch 'obsd-master' 2020-04-18 12:01:31 +01:00
Nicholas Marriott
94c90385d2 Apple appear to have fixed kqueue in some OS X version (will wonder never
cease!) so use it since it appears to be faster.
2020-04-18 11:45:49 +01:00
Thomas Adam
e1799ed7c8 Merge branch 'obsd-master' 2020-04-18 10:01:31 +01:00
nicm
c87595326c Use peek line function instead of hoking in the array directly. 2020-04-18 09:00:31 +00:00
nicm
b0a37e7514 Bring back previons fix to only redraw panes that need it after a redraw
is deferred, but clear the pane flags when they are actually redrawn
rather than every time.
2020-04-18 07:32:53 +00:00
nicm
e153b928ff Add formats for pane written/skipped bytes for debugging. 2020-04-18 07:19:28 +00:00
Thomas Adam
349617a818 Merge branch 'obsd-master' 2020-04-18 08:01:37 +01:00
nicm
baf1fca273 Only update mode when actually going to redraw something. 2020-04-18 06:52:36 +00:00
nicm
1d2bd864f2 Add a flag to protect against nested syncs and add some extra logging to
redrawing.
2020-04-18 06:20:50 +00:00
nicm
d94bdf7420 Revert previous, there is still a problem. 2020-04-18 06:15:07 +00:00
nicm
5289d4ed13 When a redraw is deferred because the terminal hasn't finished reading
the data from the last one, other panes could update while waiting, so
we set the flag to redraw them all when the new redraw actually
happened. But this means a lot of redrawing panes unnecessarily if they
haven't changed - so instead set a flag to say "at least one pane needs
to be redrawed" then look at the invidual pane flags to see which ones
need it.
2020-04-18 06:10:15 +00:00
Thomas Adam
87d79e6d36 Merge branch 'obsd-master' 2020-04-18 00:01:30 +01:00
nicm
a7a9460d27 Set mode properly before and after redrawing, and don't bother
calculating cursor position if it won't be used.
2020-04-17 22:16:28 +00:00
nicm
a877a5d8c9 Do not move the cursor to the existing y position if it is invalid, go
home instead.
2020-04-17 21:33:18 +00:00
Thomas Adam
857fca1095 Merge branch 'obsd-master' 2020-04-17 18:01:30 +01:00
nicm
bbd6e899a8 There is no point allocating a new item and putting it on the list when
the whole line is cleared line, there is never any point in doing it
more than once. Instead store the background colour alone.
2020-04-17 15:44:58 +00:00
Thomas Adam
7a21e911e1 Merge branch 'obsd-master' 2020-04-17 16:01:33 +01:00
nicm
282a7a8d96 Make sure the cursor position is still on screen after we have trimmed
empty lines. Also improve some log messages.
2020-04-17 14:06:42 +00:00
Thomas Adam
abeb31dd3d Merge branch 'obsd-master' 2020-04-17 12:01:30 +01:00
nicm
7f2925a01d Support the application escape sequence mintty (means tmux doesn't have
to delay to wait for Escape).
2020-04-17 09:06:10 +00:00
Thomas Adam
413c4cfd1b Merge branch 'obsd-master' 2020-04-17 10:01:38 +01:00
nicm
5aba26f2cb Add a copy-command option and change copy-pipe and friends to pipe to it
if used without arguments, allows all copy key bindings to be changed to
pipe with one option.
2020-04-17 08:03:22 +00:00
Thomas Adam
2846be326a Merge branch 'obsd-master' 2020-04-17 00:01:35 +01:00
nicm
5f18844b32 Return to sending sync around clears. 2020-04-16 21:46:43 +00:00
nicm
d8433add47 Do not need to set up a tty context for clearing lines now. 2020-04-16 21:16:24 +00:00
nicm
d90ca7ecd6 Collect up line clears like text within the available data so we don't
need to flush everything.
2020-04-16 20:32:51 +00:00
Thomas Adam
9edef17698 Merge branch 'obsd-master' 2020-04-16 20:01:31 +01:00
nicm
c1b015f24e Log what caused a flush for better visibility on what could be improved. 2020-04-16 17:24:28 +00:00
nicm
9311ed049b Start menu with top item selected if no mouse, GitHub issue 2169. 2020-04-16 17:20:23 +00:00
Thomas Adam
21eb2ba419 Merge branch 'obsd-master' 2020-04-16 18:01:32 +01:00
nicm
2e347d6a38 Only start and stop sync for operations like clear and scroll where
there is a better chance more data will be on the way.
2020-04-16 16:13:56 +00:00
nicm
363d950ac0 Send secondary device attributes instead of primary which gives us a bit
more useful information on some terminals.
2020-04-16 15:14:25 +00:00
Thomas Adam
5e38d26257 Merge branch 'obsd-master' 2020-04-16 16:01:35 +01:00
nicm
5ec80bd249 Move the UTF-8 flag to terminal flags. 2020-04-16 14:25:35 +00:00
nicm
4744aa43af Add a helper function to get the terminal flags. 2020-04-16 14:03:51 +00:00
nicm
b2443aa2f9 Add support for the iTerm2 sychronized updates escape sequence which
drastically reduces flickering.
2020-04-16 13:35:24 +00:00
Nicholas Marriott
b3cadf8260 Fix sys_signame check. 2020-04-16 10:15:33 +01:00
Nicholas Marriott
1aa2845026 Check for sys_signame. 2020-04-16 10:08:16 +01:00
Thomas Adam
dd66ede38b Merge branch 'obsd-master' 2020-04-16 10:01:33 +01:00
nicm
a2e47b5279 Show signal name when process exits rather than number. 2020-04-16 07:28:36 +00:00
Thomas Adam
3aa1e5810c Merge branch 'obsd-master' 2020-04-16 08:01:26 +01:00
nicm
b6dfca9b4d Don't miss the last line off the screen when writing after resize, from
Anindya Mukherjee.
2020-04-16 05:22:08 +00:00
Thomas Adam
8dc06446ec Merge branch 'obsd-master' 2020-04-15 22:01:31 +01:00
nicm
fc1855f514 Clear the selection and repeat the search on refresh same as resize. 2020-04-15 19:06:49 +00:00
Thomas Adam
6d6309014e Merge branch 'obsd-master' 2020-04-15 20:01:27 +01:00
nicm
53a29a2ffa Instead of fixing with the cursor position when the copied screen is
created, resize it and let the resize/reflow code fix it up and return
it. Solves various problems with cursor position and resizing when in
copy mode. With Anindya Mukherjee.
2020-04-15 17:50:02 +00:00
Thomas Adam
ebeb457385 Merge branch 'obsd-master' 2020-04-15 18:01:27 +01:00
nicm
1e72f5ea43 Use mode-style for selected items, like choose modes. GitHub issue 2166. 2020-04-15 16:11:23 +00:00
Thomas Adam
fe1778e377 Merge branch 'obsd-master' 2020-04-15 16:01:27 +01:00
nicm
c7883d5c87 Use grid_empty_line rather than memset when adding new lines on resize.
Also remove some old test code.
2020-04-15 12:59:20 +00:00
Nicholas Marriott
c2c9b77f14 Do not use the command if the kernel didn't return the full size. 2020-04-15 13:04:53 +01:00
Thomas Adam
533c5ee7ad Merge branch 'obsd-master' 2020-04-14 22:01:27 +01:00
nicm
b9a00cbe8a Leave the cursor above empty lines. 2020-04-14 19:07:10 +00:00
nicm
e11295f42d Adjust cursor and scroll positions when entering copy mode so that the
cursor line is still visible even if the source and target panes are
different heights.
2020-04-14 18:33:01 +00:00
Thomas Adam
c68291f627 Merge branch 'obsd-master' 2020-04-14 16:01:27 +01:00
nicm
1ef9a69f4f Send keys when they are complete not before (!= vs ==). 2020-04-14 13:22:05 +00:00
Thomas Adam
6d9b3704f5 Merge branch 'obsd-master' 2020-04-14 08:01:33 +01:00
nicm
63ec791854 Provide an accessor for the running queue item and use it to not let
hooks recurse.
2020-04-14 06:00:52 +00:00
Thomas Adam
653a159225 Merge branch 'obsd-master' 2020-04-14 00:01:41 +01:00
Thomas Adam
2159ff3256 Merge branch 'obsd-master' 2020-04-13 22:01:30 +01:00
nicm
fc83517913 Missed a few warnings in previous. 2020-04-13 20:54:15 +00:00
nicm
3f7f9a0e20 Make client -c and -t handling common in cmd-queue.c and try to be
clearer about whether the client is the target client (must have a
session) or not.
2020-04-13 20:51:57 +00:00
nicm
187277eaad Add helpers for the simple case of parse string and add to command queue. 2020-04-13 18:59:41 +00:00
Thomas Adam
acc00cd13a Merge branch 'obsd-master' 2020-04-13 18:01:43 +01:00
nicm
34804f2709 When parsing strings, put all commands in one group even if there are
newlines. This means that for example bind q { a \n b } and bind q "a ;
b" are the same. Also log commands in different groups separated by ;;
rather than ; (a command list like this should never be user visible).
2020-04-13 16:19:37 +00:00
nicm
3f86d6d460 When adding a list of commands to the queue, instead of automatically
creating a new state for each group of commands, require the caller to
create one and use it for all the commands in the list. This means the
current target works even with list with multiple groups (which can
happen if they are defined with newlines).
2020-04-13 15:55:51 +00:00
Thomas Adam
0a11f1607b Merge branch 'obsd-master' 2020-04-13 16:01:46 +01:00
nicm
adb76fd1ce Move cmdq_state into cmd-queue.c. 2020-04-13 14:46:04 +00:00
nicm
9a65102bfc Rename cmdq_shared to cmdq_state which will better reflect what it is
(going to be) used for.
2020-04-13 14:04:25 +00:00
nicm
77d5b0cc53 Store a key event not a mouse event in the shared data. 2020-04-13 13:42:35 +00:00
nicm
53d6b94e8a Move the NOHOOKS flag into the shared flags. 2020-04-13 13:32:09 +00:00
Thomas Adam
8f2b5d714a Merge branch 'obsd-master' 2020-04-13 14:01:45 +01:00
nicm
04cdd03525 Also move cmdq_item and cmdq_list into cmd-queue.c (this is to make its
use more clearly defined and preparation for some future work).
2020-04-13 10:59:58 +00:00
Thomas Adam
b117c3b812 Merge branch 'obsd-master' 2020-04-13 10:30:00 +01:00
nicm
c20eb0c0ae Make struct cmd local to cmd.c and move it out of tmux.h. 2020-04-13 08:26:27 +00:00
nicm
9cbe9675ea Change so that the appropriate hooks for windows and panes belong to
pane/window options rather than all being session options. This is
useful for example to create a pane that is automatically closed on some
condition. From Anindya Mukherjee.
2020-04-13 07:25:33 +00:00
nicm
ad38ef6ff4 Print empty arguments properly. 2020-04-12 20:54:28 +00:00
nicm
de6b30a51c Mention RGB, pointed out by Jody Frankowski. 2020-04-12 20:16:36 +00:00
nicm
da4034944d Add a -f filter argument to the list commands like choose-tree. 2020-04-12 08:36:18 +00:00
nicm
756591b4ca Add a -f filter argument to the list commands like choose-tree. 2020-04-12 08:36:18 +00:00
nicm
70534cfde6 Clarify a couple of style options. 2020-04-12 08:13:41 +00:00
nicm
1c433f1354 Remove unused define, also a man fix from jmc. 2020-04-10 20:53:54 +00:00
nicm
c0602f357d Now that copy mode copies the pane content rather than keeping a
reference to it, it isn't necessary that the pane in copy mode is the
same as the one copying from. Add a -s flag to copy-mode to specify a
different pane for the source content. This means it is possible to view
two places in a pane's history at the same time in different panes, or
copy from a pane's history into an editor or shell in the same pane.

From Anindya Mukherjee.
2020-04-10 07:44:26 +00:00
nicm
a1fc8f8b23 More style nits. 2020-04-09 15:35:27 +00:00
Thomas Adam
52e3d960e7 Merge branch 'obsd-master' 2020-04-09 16:01:45 +01:00
nicm
26f5dfbe46 Fix history-bottom to use the right line when working out the length. 2020-04-09 14:30:28 +00:00
nicm
e9e5facb0e Some minor style nits. 2020-04-09 14:23:34 +00:00
nicm
b0b07fb585 Tweak how the default size is worked out so it is more obvious. 2020-04-09 13:57:18 +00:00
nicm
886fdb1f7e A couple of other redundant checks/assignments. 2020-04-09 13:56:46 +00:00
nicm
5288801d3e Do not try to use the client if the item containing it is NULL. 2020-04-09 13:54:38 +00:00
nicm
315961faec Some more, and use of wp->window before wp NULL check in format.c. 2020-04-09 13:53:50 +00:00
nicm
b96ac80901 Some unnecessary assignments and unused variables. 2020-04-09 13:52:31 +00:00
nicm
c4d0089edb Pass correct flags to fnmatch. 2020-04-09 13:49:21 +00:00
Thomas Adam
916c3787d7 Merge branch 'obsd-master' 2020-04-09 14:01:32 +01:00
Thomas Adam
9e0e860031 Merge branch 'obsd-master' 2020-04-09 13:41:59 +01:00
nicm
0e8710f507 Wait until the initial command sequence is done before sending a device
attributes request and other bits that prompt a reply from the terminal.
This means that stray relies are not left on the terminal if the command
has attached and then immediately detached and tmux will not be around
to receive them. Prompted by a problem report from espie@.
2020-04-09 12:16:16 +00:00
nicm
ff135b34a4 Mention paste at same place as copy, suggested by John Boyle. 2020-04-09 06:28:55 +00:00
nicm
5d0eb619f1 Restore pane_current_path format from portable tmux, it is no longer
used by default and is very useful.
2020-04-08 11:26:07 +00:00
nicm
d388dbdea9 Pass the cmd item to format expansion so that mouse formats work. 2020-04-08 10:58:09 +00:00
Thomas Adam
6a2f32b4fd Merge branch 'obsd-master' 2020-04-07 16:01:29 +01:00
nicm
1c8f7c1f7a Do not restore history flag if it was never set. 2020-04-07 13:55:24 +00:00
nicm
eff881b15a Do not send mouse events if the program has not requested them. 2020-04-07 13:38:30 +00:00
nicm
a2efdb21a8 Limit size to 1x1 (total size 3x3). 2020-04-07 13:33:00 +00:00
Thomas Adam
8fa0b0cd26 Merge branch 'obsd-master' 2020-04-06 20:01:33 +01:00
nicm
77b827f879 Change copy mode to make copy of the pane history so it does not need to
freeze updates (which does not play nicely with some applications, a
longstanding problem) and will allow some other changes later. From
Anindya Mukherjee.
2020-04-06 17:51:34 +00:00
Nicholas Marriott
0953b994ff Merge branch '3.1-rc' 2020-04-06 16:17:34 +01:00
Thomas Adam
71ab50eddd Merge branch 'obsd-master' 2020-04-06 04:01:42 +01:00
nicm
fccce69cf0 Add an argument to list-commands to show only a single command. 2020-04-05 08:40:31 +00:00
nicm
832b8a8cf5 Use new window and new pane as well for -P to new-session or new-window. 2020-04-03 13:54:31 +00:00
nicm
c9b9b0c7c3 Stop logging the entire command queue every time we add something,
spotted by tb & sthen.
2020-04-03 12:59:22 +00:00
nicm
b65eab5505 Check previous line rather than an extra line, from Anindya Mukherjee. 2020-04-03 05:18:02 +00:00
Thomas Adam
450315aa74 Merge branch 'obsd-master' 2020-04-02 20:01:26 +01:00
nicm
a20d96000e Only search the visible part of the history when marking (highlighting)
search terms, much faster than searching the whole history.
2020-04-02 17:03:10 +00:00
Thomas Adam
9bcf5c0b90 Merge branch 'obsd-master' 2020-04-02 08:01:29 +01:00
nicm
90f4e149c1 Add a W position to display-menu -y to use the line above (or below) the
status line containing the window list. Leave S meaning above (or below)
all status lines. GitHub issue 2145.
2020-04-02 05:35:15 +00:00
Thomas Adam
7e6db00dd2 Merge branch 'obsd-master' 2020-04-01 14:01:25 +01:00
nicm
05a15215c5 Do not ignore triple-click and send to pane. 2020-04-01 11:47:44 +00:00
Thomas Adam
66db12db31 Merge branch 'obsd-master' 2020-04-01 12:01:27 +01:00
nicm
567b27e10a Add a 10 second timeout to prevent searches taking too much time, from
Anindya Mukherjee.
2020-04-01 09:36:37 +00:00
Nicholas Marriott
faf2ed48fb Merge branch '3.1-rc' 2020-04-01 10:10:48 +01:00
nicm
dd2fdcda79 Support mouse in popups. 2020-04-01 09:05:27 +00:00
Thomas Adam
e54d4e7fe0 Merge branch 'obsd-master' 2020-04-01 10:01:30 +01:00
Nicholas Marriott
0ced25ce50 Fix configure.ac. 2020-04-01 09:30:29 +01:00
Nicholas Marriott
1d4cdbc227 Merge branch '3.1-rc' 2020-04-01 09:30:12 +01:00
Nicholas Marriott
b8356c650a Update CHANGES. 2020-04-01 09:29:02 +01:00
nicm
cd30633d10 Do not go down the regex search path (which is expensive because we need
to convert the grid data into a string for regexec and reverse it to
find the grid position) if the search string does not contain any regex
special characters.
2020-04-01 08:07:05 +00:00
nicm
b66d62d2d0 Do not go down the regex search path (which is expensive because we need
to convert the grid data into a string for regexec and reverse it to
find the grid position) if the search string does not contain any regex
special characters.
2020-04-01 08:07:05 +00:00
nicm
c129ed3233 Use a comparison to check for wrap and avoid an expensive modulus. 2020-04-01 07:52:07 +00:00
nicm
46092f2760 Use a comparison to check for wrap and avoid an expensive modulus. 2020-04-01 07:52:07 +00:00
nicm
89d2a20e56 Performance improvements for regex searching, most notably:
- Use the grid data directly instead of copying it.

- Special case the most typical one byte character cells and use memcmp
  for multiple bytes instead of a handrolled loop.

- Hoist regcomp out of the loop into the calling functions.

GitHub issue 2143.

Also a man page from from jmc@.
2020-04-01 07:35:10 +00:00
nicm
46ed81fc45 Performance improvements for regex searching, most notably:
- Use the grid data directly instead of copying it.

- Special case the most typical one byte character cells and use memcmp
  for multiple bytes instead of a handrolled loop.

- Hoist regcomp out of the loop into the calling functions.

GitHub issue 2143.

Also a man page from from jmc@.
2020-04-01 07:35:10 +00:00
Thomas Adam
bb6630af31 Merge branch 'obsd-master' 2020-04-01 00:01:25 +01:00
Thomas Adam
dfd29977e0 Merge branch 'obsd-master' 2020-03-31 20:01:34 +01:00
nicm
38f1546a66 Add a way to mark environment variables as "hidden" so they can be used
by tmux but are not passed into the environment of new panes.
2020-03-31 17:14:40 +00:00
nicm
cc8b41f294 Add a way to mark environment variables as "hidden" so they can be used
by tmux but are not passed into the environment of new panes.
2020-03-31 17:14:40 +00:00
nicm
e221ef203c Add a -T flag to resize-pane to trim lines below the cursor, moving
lines out of the history. GitHub issue 2134.
2020-03-31 17:13:20 +00:00
nicm
e6cddcf752 Add a -T flag to resize-pane to trim lines below the cursor, moving
lines out of the history. GitHub issue 2134.
2020-03-31 17:13:20 +00:00
nicm
e6d1b6770c Add non-regex search variants to avoid the performance cost for people
with large histories or long lines.
2020-03-31 16:53:23 +00:00
nicm
2624edde46 Add non-regex search variants to avoid the performance cost for people
with large histories or long lines.
2020-03-31 16:53:23 +00:00
Thomas Adam
0bb1a50b88 Merge branch 'obsd-master' 2020-03-31 16:01:29 +01:00
nicm
2ca95840d1 Add session_path from Chris Ruegge in GitHub issue 2142. 2020-03-31 11:58:05 +00:00
nicm
2a4714e76b Add session_path from Chris Ruegge in GitHub issue 2142. 2020-03-31 11:58:05 +00:00
nicm
0dd4977d5c Add a "second click" key type which is fired for the second click of a
double click, even if the timer hasn't expired to confirm it isn't
actually a triple click. Provides a way for people who don't care about
triple clicks or can make their commands have no side effects to avoid
the double click timer delay.
2020-03-31 11:38:35 +00:00
nicm
01b3bb8e2c Add a "second click" key type which is fired for the second click of a
double click, even if the timer hasn't expired to confirm it isn't
actually a triple click. Provides a way for people who don't care about
triple clicks or can make their commands have no side effects to avoid
the double click timer delay.
2020-03-31 11:38:35 +00:00
Thomas Adam
c9cd8f9b5d Merge branch 'obsd-master' 2020-03-31 10:01:29 +01:00
nicm
3bbd66c013 Move alternate screen into the screen rather than the pane. 2020-03-31 07:00:34 +00:00
nicm
eedf059d00 Detach reply escape sequences from the pane so they work in popups. 2020-03-31 06:35:38 +00:00
Thomas Adam
588865152a Merge branch 'obsd-master' 2020-03-30 18:01:28 +01:00
nicm
1fb504d0d5 Tweak key numbers to avoid some special keys crossing over with modifier bits. 2020-03-30 16:16:48 +00:00
nicm
34de379c7d Add to rather than replace flags with -c. 2020-03-30 15:49:23 +00:00
Nicholas Marriott
a46916b452 Tweak text. 2020-03-30 14:18:29 +01:00
Nicholas Marriott
df633c527d Add to CHANGES. 2020-03-30 14:17:58 +01:00
Thomas Adam
aa264ae568 Merge branch 'obsd-master' 2020-03-30 12:01:25 +01:00
Thomas Adam
ecde339f59 Merge branch 'obsd-master' 2020-03-30 10:01:27 +01:00
nicm
c713b65b9e Do not check flags after the popup struct has been freed. 2020-03-30 07:42:44 +00:00
nicm
586cafff0f Do not check flags after the popup struct has been freed. 2020-03-30 07:42:44 +00:00
Thomas Adam
5aa9e425b4 Merge branch 'obsd-master' 2020-03-28 16:01:24 +00:00
Thomas Adam
374f5ea60c Merge branch 'obsd-master' 2020-03-28 14:01:28 +00:00
Thomas Adam
88ca500546 Merge branch 'obsd-master' 2020-03-28 10:01:27 +00:00
nicm
6d0376a679 Change default position for menu and popup to centre rather than top left. 2020-03-28 09:55:30 +00:00
nicm
8036d0f834 Change default position for menu and popup to centre rather than top left. 2020-03-28 09:55:30 +00:00
nicm
852a2f2e1f Make two -E only close popup automatically if the command exited with 0. 2020-03-28 09:51:12 +00:00
nicm
593fddf84b Make two -E only close popup automatically if the command exited with 0. 2020-03-28 09:51:12 +00:00
nicm
4346098e97 Fix how popup height is calculated to take embedded newlines into account. 2020-03-28 09:39:44 +00:00
nicm
e0b17e796b Add formats for top paste buffer by default. Also a tmux.1 fix from jmc. 2020-03-28 09:39:27 +00:00
Thomas Adam
f986539e3c Merge branch 'master' of github.com:tmux/tmux 2020-03-26 11:12:18 +00:00
Thomas Adam
8a57d14f4b Merge branch 'obsd-master' 2020-03-26 11:11:37 +00:00
Nicholas Marriott
75a93207d4 Update capture-pane test, from Johannes Altmanninger. 2020-03-26 10:16:05 +00:00
nicm
55b14cdc6a Add support for overlay popup boxes to show text or output temporarily
above the normal layout. These work similarly to menus and are created
with the display-popup command.
2020-03-24 08:09:43 +00:00
nicm
8a838b0372 Add support for overlay popup boxes to show text or output temporarily
above the normal layout. These work similarly to menus and are created
with the display-popup command.
2020-03-24 08:09:43 +00:00
Thomas Adam
f652d777a6 Merge branch 'obsd-master' 2020-03-21 16:01:25 +00:00
Thomas Adam
5123bb7db4 Merge branch 'obsd-master' 2020-03-21 14:01:27 +00:00
nicm
edca27ae45 AIX colours are always stored as 90-97, not 100-107. From Johannes
Altmanninger.
2020-03-21 13:51:30 +00:00
nicm
af6ae35900 Set end position correctly, GitHub issue 2129 from Anindya Mukherjee. 2020-03-21 13:19:56 +00:00
nicm
8828b958f0 Break code to convert an argument as a percentage into a common function. 2020-03-21 13:16:15 +00:00
nicm
5aeab5ab40 Preserve exit status from run-shell and pass to the client. 2020-03-21 13:15:38 +00:00
Thomas Adam
c6d4baa4e5 Merge branch 'obsd-master' 2020-03-20 22:01:26 +00:00
nicm
9a55f65702 Fix select-word when not on a word, from Anindya Mukherjee. 2020-03-20 20:12:39 +00:00
Thomas Adam
e4efd59fe0 Merge branch 'obsd-master' 2020-03-20 20:01:34 +00:00
nicm
1a4e64ba69 Apply same menu items to view mode like copy mode. 2020-03-20 18:35:53 +00:00
nicm
7c25f22074 Similarly, disable zoom if only one pane. 2020-03-20 18:22:37 +00:00
nicm
b66501df0c Put swap down back in the right place. 2020-03-20 18:20:58 +00:00
nicm
4d6805284b Disable swap entries if nothing to swap with. 2020-03-20 18:19:22 +00:00
nicm
68cf61aa46 Still want the per-mode menus outside copy mode. 2020-03-20 18:11:56 +00:00
nicm
005cd48620 Oops, typo in key binding. 2020-03-20 18:05:22 +00:00
Thomas Adam
9894e50c42 Merge branch 'obsd-master' 2020-03-20 18:01:25 +00:00
nicm
06c3079d66 Make the mouse_word and mouse_line formats work in copy mode and enable
the default pane menu in copy mode.
2020-03-20 17:59:39 +00:00
nicm
7bbca49395 Fix positioning of menu in choose modes and a couple of keys in tree mode. 2020-03-20 17:26:14 +00:00
Thomas Adam
5b71943f89 Merge branch 'obsd-master' 2020-03-20 14:01:26 +00:00
nicm
a3ff5a9e25 select_word_end needs to forward no_reset flag or select-word selects
too much.
2020-03-20 13:12:04 +00:00
Thomas Adam
159e648ccb Merge branch 'obsd-master' 2020-03-20 08:01:26 +00:00
nicm
c3e96cce4e Another fix to make other-end forget the selection mode, from Anindya Mukherjee. 2020-03-20 06:09:19 +00:00
Thomas Adam
35c4897d8f Merge branch 'obsd-master' 2020-03-19 16:01:29 +00:00
nicm
74ed17d41b Little bit of tidying. 2020-03-19 14:23:58 +00:00
Nicholas Marriott
c15396459b No util.h. 2020-03-19 14:06:37 +00:00
nicm
de34436d4c Change input path so it doesn't require a pane. 2020-03-19 14:03:48 +00:00
Thomas Adam
17e4f2394a Merge branch 'obsd-master' 2020-03-19 14:01:31 +00:00
nicm
ce61bf931b Do not set the history flag if there is no history. 2020-03-19 13:46:10 +00:00
nicm
e8273a993e Add a flag to run a background process in a pty as well, not used for
anything yet.
2020-03-19 13:43:18 +00:00
nicm
581ed718e7 Add C position for terminal centre with display-menu -x and -y. 2020-03-19 13:32:49 +00:00
nicm
2cd8ea7680 Various fixes to copying with select-word and select-line, including
making it consistent with keys and with the mouse, and using other-end.
From Anindya Mukherjee.
2020-03-19 13:28:52 +00:00
Thomas Adam
7595b22e72 Merge branch 'obsd-master' 2020-03-18 10:01:26 +00:00
nicm
7b0e688a96 Break position calculation into a helper function. 2020-03-18 09:13:49 +00:00
Thomas Adam
18cfd9befa Merge branch 'obsd-master' 2020-03-17 18:01:27 +00:00
Nicholas Marriott
76f373ab35 Merge branch '3.1-rc' 2020-03-17 16:17:09 +00:00
Nicholas Marriott
80f20b8e4e getopt varies too much between platforms, and we already use compat/getopt.c
for Linux so just use it everywhere.
2020-03-17 16:07:06 +00:00
nicm
bd0342b0a7 getopt is not required to set optarg to NULL when there is no argument
and some do not, so set it explicitly each time.
2020-03-17 16:02:38 +00:00
Thomas Adam
5717633cf5 Merge branch 'obsd-master' 2020-03-17 14:01:25 +00:00
nicm
1ddc128860 Do not return early if no bits changed because may still need to change the style. 2020-03-17 12:20:12 +00:00
Thomas Adam
0610f66fa9 Merge branch 'obsd-master' 2020-03-17 12:01:28 +00:00
nicm
115bb33257 Ignore default-shell (and use /bin/sh) if it invalid not just if it is
tmux itself, also refuse to set the option to something invalid in the
first place. GitHub issue 2120.
2020-03-17 11:10:12 +00:00
Thomas Adam
71eb965dd9 Merge branch 'obsd-master' 2020-03-16 20:01:24 +00:00
nicm
fb396286ff Do not attempt to close a NULL pane when failing to create a new one. 2020-03-16 18:08:39 +00:00
Thomas Adam
c18a46cf56 Merge branch 'obsd-master' 2020-03-16 16:01:26 +00:00
Nicholas Marriott
62c646ac32 Add couple of CHANGES tweaks. 2020-03-16 15:13:35 +00:00
Nicholas Marriott
372841f70a Add to CHANGES. 2020-03-16 15:12:20 +00:00
Nicholas Marriott
69eff51538 Add. 2020-03-16 15:11:34 +00:00
nicm
7021757c9d Adjust selection correctly when scrolling, from Anindya Mukherjee. 2020-03-16 14:17:56 +00:00
Thomas Adam
cae2dfc95a Merge branch 'obsd-master' 2020-03-16 12:01:26 +00:00
nicm
37b7a29cca VTE treats each mouse mode bit as independent, so turning off 1000
doesn't also turn off 1001, so don't rely on that behaviour. GitHub
issue 2116.
2020-03-16 10:49:06 +00:00
Thomas Adam
45be93f604 Merge branch 'obsd-master' 2020-03-16 10:01:27 +00:00
nicm
7cae4e8e89 Turn off mouse mode 1003 as well as the rest when exiting. 2020-03-16 09:18:47 +00:00
nicm
7815b30c7d Terminate the output buffer for control mode output - it is now used as
a string. GitHub issue 2114.
2020-03-16 09:12:44 +00:00
nicm
9abeff7f0b FIx type for %u, from Thomas Adam. 2020-03-16 08:23:24 +00:00
Thomas Adam
f584fe1b00 Merge branch 'obsd-master' 2020-03-16 08:01:27 +00:00
nicm
d162ff48f3 Send mouse down event immediately rather than waiting for double click
to finish which would now mean it was out of order. Reported by Mark
Kelly.
2020-03-16 06:12:42 +00:00
Thomas Adam
55aeaffb1a Merge branch 'obsd-master' 2020-03-15 22:01:27 +00:00
nicm
882d0b785d Reset selection flag when clearing or stopping selection, from Mark
Kelly.
2020-03-15 20:44:19 +00:00
nicm
fa3871b1be Fix C-Space key string. 2020-03-15 20:35:52 +00:00
Thomas Adam
c859748210 Merge branch 'obsd-master' 2020-03-13 08:01:24 +00:00
nicm
fa36e9bc88 Do not add a reference to the session if no session is present. 2020-03-13 06:19:33 +00:00
Thomas Adam
c91a0948e3 Merge branch 'obsd-master' 2020-03-12 16:01:28 +00:00
Thomas Adam
6385bd1e08 Merge branch 'obsd-master' 2020-03-12 14:01:29 +00:00
nicm
6571dd50f8 Tidy up the default mouse key bindings and:
- Add double and triple click bindings to copy a word or line outside
  copy mode. The text is selected for a short period to show what has
  been copied. This is in line with the existing mouse selection where
  the text is copied and the selection is cleared when the mouse button
  is released.

- Change the existing double and triple click bindings in copy mode to
  behave in the same way.

- Add a button 2 binding to paste the top buffer.
2020-03-12 13:48:32 +00:00
nicm
516f6099fc Add a -d flag to run-shell to wait for delay before running the command,
also allow run-shell to accept no command to just delay.
2020-03-12 13:25:45 +00:00
nicm
7863445e5d Add a copy-mode -H flag to hide the position marker in the top right. 2020-03-12 13:19:20 +00:00
nicm
f7bc753442 Change how double and triple clicks works so that one or the other is
fired - a double click is no longer triggered on the way to a triple
click.
2020-03-12 13:16:16 +00:00
Thomas Adam
5625c62044 Merge branch 'obsd-master' 2020-03-12 12:01:25 +00:00
Thomas Adam
1a2e66f345 Merge branch 'obsd-master' 2020-03-12 10:01:25 +00:00
nicm
b8b48e2e37 Add C-g to cancel command prompt with vi(1) keys as well as emacs, and q
in command mode.
2020-03-12 09:49:43 +00:00
nicm
2a5702a936 When the server socket is given by the user with -S, create it with
umask 177 instead of 117 because it may not be in a safe directory like
the default directory in /tmp. The user can chmod it more open after it
is created if they want.
2020-03-12 09:26:34 +00:00
Thomas Adam
39dc809751 Merge branch 'obsd-master' 2020-03-11 20:01:26 +00:00
nicm
4eba98313c Start a new selection if outside the existing selection after a word has
been selected. From Anindya Mukherjee.
2020-03-11 18:46:42 +00:00
Nicholas Marriott
e4898de98d Only need one lm. 2020-03-11 18:41:14 +00:00
Thomas Adam
256f7e8f38 Merge branch 'master' of github.com:tmux/tmux 2020-03-11 17:24:42 +00:00
Thomas Adam
e512a3642a Merge branch 'obsd-master'
Also add a check for -lm via AC_SEARCH_LIBS in configure.ac for
portablility fixes.
2020-03-11 17:23:38 +00:00
Nicholas Marriott
6d9beccb41 Will need fmod. 2020-03-11 16:33:55 +00:00
Nicholas Marriott
8d1d7fd775 Lock much more quickly. 2020-03-11 14:41:25 +00:00
nicm
c820585dd0 Add some number operators for formats, from Tyler Culp. 2020-03-11 14:17:55 +00:00
Nicholas Marriott
07bf5cbd27 3.2 next. 2020-03-11 06:41:13 +00:00
165 changed files with 23409 additions and 8305 deletions

12
.github/README.md vendored
View File

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

2
.github/lock.yml vendored
View File

@@ -1,4 +1,4 @@
daysUntilLock: 180 daysUntilLock: 30
skipCreatedBefore: false skipCreatedBefore: false
exemptLabels: [] exemptLabels: []
lockLabel: false lockLabel: false

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

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

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

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

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

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

2
.gitignore vendored
View File

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

View File

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

377
CHANGES
View File

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

View File

@@ -12,8 +12,8 @@ dist_EXTRA_tmux_SOURCES = compat/*.[ch]
# Preprocessor flags. # Preprocessor flags.
AM_CPPFLAGS += @XOPEN_DEFINES@ \ AM_CPPFLAGS += @XOPEN_DEFINES@ \
-DTMUX_VERSION="\"@VERSION@\"" \ -DTMUX_VERSION='"@VERSION@"' \
-DTMUX_CONF="\"$(sysconfdir)/tmux.conf:~/.tmux.conf:~/.config/tmux/tmux.conf\"" -DTMUX_CONF='"$(sysconfdir)/tmux.conf:~/.tmux.conf:$$XDG_CONFIG_HOME/tmux/tmux.conf:~/.config/tmux/tmux.conf"'
# Additional object files. # Additional object files.
LDADD = $(LIBOBJS) LDADD = $(LIBOBJS)
@@ -28,7 +28,7 @@ AM_CFLAGS += -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations
AM_CFLAGS += -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare 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 -Wcast-align
AM_CFLAGS += -Wdeclaration-after-statement -Wno-pointer-sign -Wno-attributes AM_CFLAGS += -Wdeclaration-after-statement -Wno-pointer-sign -Wno-attributes
AM_CFLAGS += -Wno-unused-result AM_CFLAGS += -Wno-unused-result -Wno-format-y2k
AM_CPPFLAGS += -DDEBUG AM_CPPFLAGS += -DDEBUG
endif endif
AM_CPPFLAGS += -iquote. AM_CPPFLAGS += -iquote.
@@ -58,6 +58,11 @@ if IS_NETBSD
AM_CPPFLAGS += -D_OPENBSD_SOURCE AM_CPPFLAGS += -D_OPENBSD_SOURCE
endif endif
# Set flags for Haiku.
if IS_HAIKU
AM_CPPFLAGS += -D_BSD_SOURCE
endif
# List of sources. # List of sources.
dist_tmux_SOURCES = \ dist_tmux_SOURCES = \
alerts.c \ alerts.c \
@@ -136,6 +141,7 @@ dist_tmux_SOURCES = \
file.c \ file.c \
format.c \ format.c \
format-draw.c \ format-draw.c \
grid-reader.c \
grid-view.c \ grid-view.c \
grid.c \ grid.c \
input-keys.c \ input-keys.c \
@@ -154,6 +160,7 @@ dist_tmux_SOURCES = \
options-table.c \ options-table.c \
options.c \ options.c \
paste.c \ paste.c \
popup.c \
proc.c \ proc.c \
regsub.c \ regsub.c \
resize.c \ resize.c \
@@ -170,6 +177,7 @@ dist_tmux_SOURCES = \
tmux.c \ tmux.c \
tmux.h \ tmux.h \
tty-acs.c \ tty-acs.c \
tty-features.c \
tty-keys.c \ tty-keys.c \
tty-term.c \ tty-term.c \
tty.c \ tty.c \
@@ -178,11 +186,11 @@ dist_tmux_SOURCES = \
window-client.c \ window-client.c \
window-clock.c \ window-clock.c \
window-copy.c \ window-copy.c \
window-customize.c \
window-tree.c \ window-tree.c \
window.c \ window.c \
xmalloc.c \ xmalloc.c \
xmalloc.h \ xmalloc.h
xterm-keys.c
nodist_tmux_SOURCES = osdep-@PLATFORM@.c nodist_tmux_SOURCES = osdep-@PLATFORM@.c
# Add compat file for forkpty. # Add compat file for forkpty.
@@ -195,6 +203,12 @@ if HAVE_UTF8PROC
nodist_tmux_SOURCES += compat/utf8proc.c nodist_tmux_SOURCES += compat/utf8proc.c
endif endif
if NEED_FUZZING
check_PROGRAMS = fuzz/input-fuzzer
fuzz_input_fuzzer_LDFLAGS = $(FUZZING_LIBS)
fuzz_input_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS)
endif
# Install tmux.1 in the right format. # Install tmux.1 in the right format.
install-exec-hook: install-exec-hook:
if test x@MANFORMAT@ = xmdoc; then \ if test x@MANFORMAT@ = xmdoc; then \

3
README
View File

@@ -16,6 +16,9 @@ It also depends on ncurses, available from:
https://invisible-mirror.net/archives/ncurses/ https://invisible-mirror.net/archives/ncurses/
To build tmux, a C compiler (for example gcc or clang), make, pkg-config and a
suitable yacc (yacc or bison) are needed.
* Installation * Installation
To build and install tmux from a release tarball, use: To build and install tmux from a release tarball, use:

View File

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

View File

@@ -55,11 +55,11 @@ args_cmp(struct args_entry *a1, struct args_entry *a2)
/* Find a flag in the arguments tree. */ /* Find a flag in the arguments tree. */
static struct args_entry * static struct args_entry *
args_find(struct args *args, u_char ch) args_find(struct args *args, u_char flag)
{ {
struct args_entry entry; struct args_entry entry;
entry.flag = ch; entry.flag = flag;
return (RB_FIND(args_tree, &args->tree, &entry)); return (RB_FIND(args_tree, &args->tree, &entry));
} }
@@ -211,25 +211,35 @@ args_print(struct args *args)
char * char *
args_escape(const char *s) args_escape(const char *s)
{ {
static const char quoted[] = " #\"';${}"; static const char dquoted[] = " #';${}";
static const char squoted[] = " \"";
char *escaped, *result; char *escaped, *result;
int flags; int flags, quotes = 0;
if (*s == '\0') {
xasprintf(&result, "''");
return (result);
}
if (s[strcspn(s, dquoted)] != '\0')
quotes = '"';
else if (s[strcspn(s, squoted)] != '\0')
quotes = '\'';
if (*s == '\0')
return (xstrdup(s));
if (s[0] != ' ' && if (s[0] != ' ' &&
(strchr(quoted, s[0]) != NULL || s[0] == '~') && s[1] == '\0' &&
s[1] == '\0') { (quotes != 0 || s[0] == '~')) {
xasprintf(&escaped, "\\%c", s[0]); xasprintf(&escaped, "\\%c", s[0]);
return (escaped); return (escaped);
} }
flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL; flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL;
if (s[strcspn(s, quoted)] != '\0') if (quotes == '"')
flags |= VIS_DQ; flags |= VIS_DQ;
utf8_stravis(&escaped, s, flags); utf8_stravis(&escaped, s, flags);
if (flags & VIS_DQ) { if (quotes == '\'')
xasprintf(&result, "'%s'", escaped);
else if (quotes == '"') {
if (*escaped == '~') if (*escaped == '~')
xasprintf(&result, "\"\\%s\"", escaped); xasprintf(&result, "\"\\%s\"", escaped);
else else
@@ -246,11 +256,11 @@ args_escape(const char *s)
/* Return if an argument is present. */ /* Return if an argument is present. */
int int
args_has(struct args *args, u_char ch) args_has(struct args *args, u_char flag)
{ {
struct args_entry *entry; struct args_entry *entry;
entry = args_find(args, ch); entry = args_find(args, flag);
if (entry == NULL) if (entry == NULL)
return (0); return (0);
return (entry->count); return (entry->count);
@@ -258,15 +268,15 @@ args_has(struct args *args, u_char ch)
/* Set argument value in the arguments tree. */ /* Set argument value in the arguments tree. */
void void
args_set(struct args *args, u_char ch, const char *s) args_set(struct args *args, u_char flag, const char *s)
{ {
struct args_entry *entry; struct args_entry *entry;
struct args_value *value; struct args_value *value;
entry = args_find(args, ch); entry = args_find(args, flag);
if (entry == NULL) { if (entry == NULL) {
entry = xcalloc(1, sizeof *entry); entry = xcalloc(1, sizeof *entry);
entry->flag = ch; entry->flag = flag;
entry->count = 1; entry->count = 1;
TAILQ_INIT(&entry->values); TAILQ_INIT(&entry->values);
RB_INSERT(args_tree, &args->tree, entry); RB_INSERT(args_tree, &args->tree, entry);
@@ -282,22 +292,44 @@ args_set(struct args *args, u_char ch, const char *s)
/* Get argument value. Will be NULL if it isn't present. */ /* Get argument value. Will be NULL if it isn't present. */
const char * const char *
args_get(struct args *args, u_char ch) args_get(struct args *args, u_char flag)
{ {
struct args_entry *entry; struct args_entry *entry;
if ((entry = args_find(args, ch)) == NULL) if ((entry = args_find(args, flag)) == NULL)
return (NULL);
if (TAILQ_EMPTY(&entry->values))
return (NULL); return (NULL);
return (TAILQ_LAST(&entry->values, args_values)->value); return (TAILQ_LAST(&entry->values, args_values)->value);
} }
/* Get first argument. */
u_char
args_first(struct args *args, struct args_entry **entry)
{
*entry = RB_MIN(args_tree, &args->tree);
if (*entry == NULL)
return (0);
return ((*entry)->flag);
}
/* Get next argument. */
u_char
args_next(struct args_entry **entry)
{
*entry = RB_NEXT(args_tree, &args->tree, *entry);
if (*entry == NULL)
return (0);
return ((*entry)->flag);
}
/* Get first value in argument. */ /* Get first value in argument. */
const char * const char *
args_first_value(struct args *args, u_char ch, struct args_value **value) args_first_value(struct args *args, u_char flag, struct args_value **value)
{ {
struct args_entry *entry; struct args_entry *entry;
if ((entry = args_find(args, ch)) == NULL) if ((entry = args_find(args, flag)) == NULL)
return (NULL); return (NULL);
*value = TAILQ_FIRST(&entry->values); *value = TAILQ_FIRST(&entry->values);
@@ -320,15 +352,15 @@ args_next_value(struct args_value **value)
/* Convert an argument value to a number. */ /* Convert an argument value to a number. */
long long long long
args_strtonum(struct args *args, u_char ch, long long minval, long long maxval, args_strtonum(struct args *args, u_char flag, long long minval,
char **cause) long long maxval, char **cause)
{ {
const char *errstr; const char *errstr;
long long ll; long long ll;
struct args_entry *entry; struct args_entry *entry;
struct args_value *value; struct args_value *value;
if ((entry = args_find(args, ch)) == NULL) { if ((entry = args_find(args, flag)) == NULL) {
*cause = xstrdup("missing"); *cause = xstrdup("missing");
return (0); return (0);
} }
@@ -343,3 +375,60 @@ args_strtonum(struct args *args, u_char ch, long long minval, long long maxval,
*cause = NULL; *cause = NULL;
return (ll); return (ll);
} }
/* Convert an argument to a number which may be a percentage. */
long long
args_percentage(struct args *args, u_char flag, long long minval,
long long maxval, long long curval, char **cause)
{
const char *value;
struct args_entry *entry;
if ((entry = args_find(args, flag)) == NULL) {
*cause = xstrdup("missing");
return (0);
}
value = TAILQ_LAST(&entry->values, args_values)->value;
return (args_string_percentage(value, minval, maxval, curval, cause));
}
/* Convert a string to a number which may be a percentage. */
long long
args_string_percentage(const char *value, long long minval, long long maxval,
long long curval, char **cause)
{
const char *errstr;
long long ll;
size_t valuelen = strlen(value);
char *copy;
if (value[valuelen - 1] == '%') {
copy = xstrdup(value);
copy[valuelen - 1] = '\0';
ll = strtonum(copy, 0, 100, &errstr);
free(copy);
if (errstr != NULL) {
*cause = xstrdup(errstr);
return (0);
}
ll = (curval * ll) / 100;
if (ll < minval) {
*cause = xstrdup("too small");
return (0);
}
if (ll > maxval) {
*cause = xstrdup("too large");
return (0);
}
} else {
ll = strtonum(value, minval, maxval, &errstr);
if (errstr != NULL) {
*cause = xstrdup(errstr);
return (0);
}
}
*cause = NULL;
return (ll);
}

View File

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

93
cfg.c
View File

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

482
client.c
View File

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

View File

@@ -37,8 +37,9 @@ const struct cmd_entry cmd_attach_session_entry = {
.name = "attach-session", .name = "attach-session",
.alias = "attach", .alias = "attach",
.args = { "c:dErt:x", 0, 0 }, .args = { "c:dEf:rt:x", 0, 0 },
.usage = "[-dErx] [-c working-directory] " CMD_TARGET_SESSION_USAGE, .usage = "[-dErx] [-c working-directory] [-f flags] "
CMD_TARGET_SESSION_USAGE,
/* -t is special */ /* -t is special */
@@ -48,16 +49,17 @@ const struct cmd_entry cmd_attach_session_entry = {
enum cmd_retval enum cmd_retval
cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag, cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
int xflag, int rflag, const char *cflag, int Eflag) int xflag, int rflag, const char *cflag, int Eflag, const char *fflag)
{ {
struct cmd_find_state *current = &item->shared->current; struct cmd_find_state *current = cmdq_get_current(item);
struct cmd_find_state target;
enum cmd_find_type type; enum cmd_find_type type;
int flags; int flags;
struct client *c = item->client, *c_loop; struct client *c = cmdq_get_client(item), *c_loop;
struct session *s; struct session *s;
struct winlink *wl; struct winlink *wl;
struct window_pane *wp; struct window_pane *wp;
char *cause; char *cwd, *cause;
enum msgtype msgtype; enum msgtype msgtype;
if (RB_EMPTY(&sessions)) { if (RB_EMPTY(&sessions)) {
@@ -80,11 +82,11 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
type = CMD_FIND_SESSION; type = CMD_FIND_SESSION;
flags = CMD_FIND_PREFER_UNATTACHED; flags = CMD_FIND_PREFER_UNATTACHED;
} }
if (cmd_find_target(&item->target, item, tflag, type, flags) != 0) if (cmd_find_target(&target, item, tflag, type, flags) != 0)
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
s = item->target.s; s = target.s;
wl = item->target.wl; wl = target.wl;
wp = item->target.wp; wp = target.wp;
if (wl != NULL) { if (wl != NULL) {
if (wp != NULL) if (wp != NULL)
@@ -97,9 +99,14 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
} }
if (cflag != NULL) { if (cflag != NULL) {
cwd = format_single(item, cflag, c, s, wl, wp);
free((void *)s->cwd); free((void *)s->cwd);
s->cwd = format_single(item, cflag, c, s, wl, wp); s->cwd = cwd;
} }
if (fflag)
server_client_set_flags(c, fflag);
if (rflag)
c->flags |= (CLIENT_READONLY|CLIENT_IGNORESIZE);
c->last_session = c->session; c->last_session = c->session;
if (c->session != NULL) { if (c->session != NULL) {
@@ -118,7 +125,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
environ_update(s->options, c->environ, s->environ); environ_update(s->options, c->environ, s->environ);
c->session = s; c->session = s;
if (~item->shared->flags & CMDQ_SHARED_REPEAT) if (~cmdq_get_flags(item) & CMDQ_STATE_REPEAT)
server_client_set_key_table(c, NULL); server_client_set_key_table(c, NULL);
tty_update_client_offset(c); tty_update_client_offset(c);
status_timer_start(c); status_timer_start(c);
@@ -134,8 +141,6 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
free(cause); free(cause);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
if (rflag)
c->flags |= CLIENT_READONLY;
if (dflag || xflag) { if (dflag || xflag) {
if (xflag) if (xflag)
@@ -177,9 +182,9 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
static enum cmd_retval static enum cmd_retval
cmd_attach_session_exec(struct cmd *self, struct cmdq_item *item) cmd_attach_session_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = cmd_get_args(self);
return (cmd_attach_session(item, args_get(args, 't'), return (cmd_attach_session(item, args_get(args, 't'),
args_has(args, 'd'), args_has(args, 'x'), args_has(args, 'r'), args_has(args, 'd'), args_has(args, 'x'), args_has(args, 'r'),
args_get(args, 'c'), args_has(args, 'E'))); args_get(args, 'c'), args_has(args, 'E'), args_get(args, 'f')));
} }

View File

@@ -33,9 +33,9 @@ const struct cmd_entry cmd_bind_key_entry = {
.name = "bind-key", .name = "bind-key",
.alias = "bind", .alias = "bind",
.args = { "nrN:T:", 2, -1 }, .args = { "nrN:T:", 1, -1 },
.usage = "[-nr] [-T key-table] [-N note] key " .usage = "[-nr] [-T key-table] [-N note] key "
"command [arguments]", "[command [arguments]]",
.flags = CMD_AFTERHOOK, .flags = CMD_AFTERHOOK,
.exec = cmd_bind_key_exec .exec = cmd_bind_key_exec
@@ -44,9 +44,9 @@ const struct cmd_entry cmd_bind_key_entry = {
static enum cmd_retval static enum cmd_retval
cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item) cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = cmd_get_args(self);
key_code key; key_code key;
const char *tablename, *note; const char *tablename, *note = args_get(args, 'N');
struct cmd_parse_result *pr; struct cmd_parse_result *pr;
char **argv = args->argv; char **argv = args->argv;
int argc = args->argc, repeat; int argc = args->argc, repeat;
@@ -65,22 +65,24 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item)
tablename = "prefix"; tablename = "prefix";
repeat = args_has(args, 'r'); repeat = args_has(args, 'r');
if (argc == 2) if (argc != 1) {
pr = cmd_parse_from_string(argv[1], NULL); if (argc == 2)
else pr = cmd_parse_from_string(argv[1], NULL);
pr = cmd_parse_from_arguments(argc - 1, argv + 1, NULL); else
switch (pr->status) { pr = cmd_parse_from_arguments(argc - 1, argv + 1, NULL);
case CMD_PARSE_EMPTY: switch (pr->status) {
cmdq_error(item, "empty command"); case CMD_PARSE_EMPTY:
return (CMD_RETURN_ERROR); cmdq_error(item, "empty command");
case CMD_PARSE_ERROR: return (CMD_RETURN_ERROR);
cmdq_error(item, "%s", pr->error); case CMD_PARSE_ERROR:
free(pr->error); cmdq_error(item, "%s", pr->error);
return (CMD_RETURN_ERROR); free(pr->error);
case CMD_PARSE_SUCCESS: return (CMD_RETURN_ERROR);
break; case CMD_PARSE_SUCCESS:
} break;
note = args_get(args, 'N'); }
key_bindings_add(tablename, key, note, repeat, pr->cmdlist); key_bindings_add(tablename, key, note, repeat, pr->cmdlist);
} else
key_bindings_add(tablename, key, note, repeat, NULL);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

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

View File

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

View File

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

View File

@@ -40,11 +40,11 @@ const struct cmd_entry cmd_command_prompt_entry = {
.name = "command-prompt", .name = "command-prompt",
.alias = NULL, .alias = NULL,
.args = { "1kiI:Np:t:", 0, 1 }, .args = { "1kiI:Np:Tt:W", 0, 1 },
.usage = "[-1kiN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " " .usage = "[-1kiNTW] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " "
"[template]", "[template]",
.flags = 0, .flags = CMD_CLIENT_TFLAG,
.exec = cmd_command_prompt_exec .exec = cmd_command_prompt_exec
}; };
@@ -64,17 +64,15 @@ struct cmd_command_prompt_cdata {
static enum cmd_retval static enum cmd_retval
cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = cmd_get_args(self);
struct client *tc = cmdq_get_target_client(item);
struct cmd_find_state *target = cmdq_get_target(item);
const char *inputs, *prompts; const char *inputs, *prompts;
struct cmd_command_prompt_cdata *cdata; struct cmd_command_prompt_cdata *cdata;
struct client *c;
char *prompt, *ptr, *input = NULL; char *prompt, *ptr, *input = NULL;
size_t n; size_t n;
if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL) if (tc->prompt_string != NULL)
return (CMD_RETURN_ERROR);
if (c->prompt_string != NULL)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
cdata = xcalloc(1, sizeof *cdata); cdata = xcalloc(1, sizeof *cdata);
@@ -124,8 +122,13 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
cdata->flags |= PROMPT_INCREMENTAL; cdata->flags |= PROMPT_INCREMENTAL;
else if (args_has(args, 'k')) else if (args_has(args, 'k'))
cdata->flags |= PROMPT_KEY; cdata->flags |= PROMPT_KEY;
status_prompt_set(c, prompt, input, cmd_command_prompt_callback, else if (args_has(args, 'W'))
cmd_command_prompt_free, cdata, cdata->flags); cdata->flags |= PROMPT_WINDOW;
else if (args_has(args, 'T'))
cdata->flags |= PROMPT_TARGET;
status_prompt_set(tc, target, prompt, input,
cmd_command_prompt_callback, cmd_command_prompt_free, cdata,
cdata->flags);
free(prompt); free(prompt);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
@@ -136,10 +139,9 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s,
int done) int done)
{ {
struct cmd_command_prompt_cdata *cdata = data; struct cmd_command_prompt_cdata *cdata = data;
struct cmdq_item *new_item; char *new_template, *prompt, *ptr, *error;
char *new_template, *prompt, *ptr;
char *input = NULL; char *input = NULL;
struct cmd_parse_result *pr; enum cmd_parse_status status;
if (s == NULL) if (s == NULL)
return (0); return (0);
@@ -166,21 +168,10 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s,
return (1); return (1);
} }
pr = cmd_parse_from_string(new_template, NULL); status = cmd_parse_and_append(new_template, NULL, c, NULL, &error);
switch (pr->status) { if (status == CMD_PARSE_ERROR) {
case CMD_PARSE_EMPTY: cmdq_append(c, cmdq_get_error(error));
new_item = NULL; free(error);
break;
case CMD_PARSE_ERROR:
new_item = cmdq_get_error(pr->error);
free(pr->error);
cmdq_append(c, new_item);
break;
case CMD_PARSE_SUCCESS:
new_item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
cmd_list_free(pr->cmdlist);
cmdq_append(c, new_item);
break;
} }
if (!done) if (!done)

View File

@@ -42,7 +42,7 @@ const struct cmd_entry cmd_confirm_before_entry = {
.args = { "p:t:", 1, 1 }, .args = { "p:t:", 1, 1 },
.usage = "[-p prompt] " CMD_TARGET_CLIENT_USAGE " command", .usage = "[-p prompt] " CMD_TARGET_CLIENT_USAGE " command",
.flags = 0, .flags = CMD_CLIENT_TFLAG,
.exec = cmd_confirm_before_exec .exec = cmd_confirm_before_exec
}; };
@@ -53,15 +53,13 @@ struct cmd_confirm_before_data {
static enum cmd_retval static enum cmd_retval
cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item) cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = cmd_get_args(self);
struct cmd_confirm_before_data *cdata; struct cmd_confirm_before_data *cdata;
struct client *c; struct client *tc = cmdq_get_target_client(item);
struct cmd_find_state *target = cmdq_get_target(item);
char *cmd, *copy, *new_prompt, *ptr; char *cmd, *copy, *new_prompt, *ptr;
const char *prompt; const char *prompt;
if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL)
return (CMD_RETURN_ERROR);
if ((prompt = args_get(args, 'p')) != NULL) if ((prompt = args_get(args, 'p')) != NULL)
xasprintf(&new_prompt, "%s ", prompt); xasprintf(&new_prompt, "%s ", prompt);
else { else {
@@ -74,7 +72,7 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item)
cdata = xmalloc(sizeof *cdata); cdata = xmalloc(sizeof *cdata);
cdata->cmd = xstrdup(args->argv[0]); cdata->cmd = xstrdup(args->argv[0]);
status_prompt_set(c, new_prompt, NULL, status_prompt_set(tc, target, new_prompt, NULL,
cmd_confirm_before_callback, cmd_confirm_before_free, cdata, cmd_confirm_before_callback, cmd_confirm_before_free, cdata,
PROMPT_SINGLE); PROMPT_SINGLE);
@@ -87,8 +85,8 @@ cmd_confirm_before_callback(struct client *c, void *data, const char *s,
__unused int done) __unused int done)
{ {
struct cmd_confirm_before_data *cdata = data; struct cmd_confirm_before_data *cdata = data;
struct cmdq_item *new_item; char *error;
struct cmd_parse_result *pr; enum cmd_parse_status status;
if (c->flags & CLIENT_DEAD) if (c->flags & CLIENT_DEAD)
return (0); return (0);
@@ -98,21 +96,10 @@ cmd_confirm_before_callback(struct client *c, void *data, const char *s,
if (tolower((u_char)s[0]) != 'y' || s[1] != '\0') if (tolower((u_char)s[0]) != 'y' || s[1] != '\0')
return (0); return (0);
pr = cmd_parse_from_string(cdata->cmd, NULL); status = cmd_parse_and_append(cdata->cmd, NULL, c, NULL, &error);
switch (pr->status) { if (status == CMD_PARSE_ERROR) {
case CMD_PARSE_EMPTY: cmdq_append(c, cmdq_get_error(error));
new_item = NULL; free(error);
break;
case CMD_PARSE_ERROR:
new_item = cmdq_get_error(pr->error);
free(pr->error);
cmdq_append(c, new_item);
break;
case CMD_PARSE_SUCCESS:
new_item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
cmd_list_free(pr->cmdlist);
cmdq_append(c, new_item);
break;
} }
return (0); return (0);

View File

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

View File

@@ -39,7 +39,7 @@ const struct cmd_entry cmd_detach_client_entry = {
.source = { 's', CMD_FIND_SESSION, CMD_FIND_CANFAIL }, .source = { 's', CMD_FIND_SESSION, CMD_FIND_CANFAIL },
.flags = CMD_READONLY, .flags = CMD_READONLY|CMD_CLIENT_TFLAG,
.exec = cmd_detach_client_exec .exec = cmd_detach_client_exec
}; };
@@ -50,24 +50,22 @@ const struct cmd_entry cmd_suspend_client_entry = {
.args = { "t:", 0, 0 }, .args = { "t:", 0, 0 },
.usage = CMD_TARGET_CLIENT_USAGE, .usage = CMD_TARGET_CLIENT_USAGE,
.flags = 0, .flags = CMD_CLIENT_TFLAG,
.exec = cmd_detach_client_exec .exec = cmd_detach_client_exec
}; };
static enum cmd_retval static enum cmd_retval
cmd_detach_client_exec(struct cmd *self, struct cmdq_item *item) cmd_detach_client_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = cmd_get_args(self);
struct client *c, *cloop; struct cmd_find_state *source = cmdq_get_source(item);
struct session *s; struct client *tc = cmdq_get_target_client(item), *loop;
enum msgtype msgtype; struct session *s;
const char *cmd = args_get(args, 'E'); enum msgtype msgtype;
const char *cmd = args_get(args, 'E');
if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL) if (cmd_get_entry(self) == &cmd_suspend_client_entry) {
return (CMD_RETURN_ERROR); server_client_suspend(tc);
if (self->entry == &cmd_suspend_client_entry) {
server_client_suspend(c);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
@@ -77,35 +75,35 @@ cmd_detach_client_exec(struct cmd *self, struct cmdq_item *item)
msgtype = MSG_DETACH; msgtype = MSG_DETACH;
if (args_has(args, 's')) { if (args_has(args, 's')) {
s = item->source.s; s = source->s;
if (s == NULL) if (s == NULL)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
TAILQ_FOREACH(cloop, &clients, entry) { TAILQ_FOREACH(loop, &clients, entry) {
if (cloop->session == s) { if (loop->session == s) {
if (cmd != NULL) if (cmd != NULL)
server_client_exec(cloop, cmd); server_client_exec(loop, cmd);
else else
server_client_detach(cloop, msgtype); server_client_detach(loop, msgtype);
} }
} }
return (CMD_RETURN_STOP); return (CMD_RETURN_STOP);
} }
if (args_has(args, 'a')) { if (args_has(args, 'a')) {
TAILQ_FOREACH(cloop, &clients, entry) { TAILQ_FOREACH(loop, &clients, entry) {
if (cloop->session != NULL && cloop != c) { if (loop->session != NULL && loop != tc) {
if (cmd != NULL) if (cmd != NULL)
server_client_exec(cloop, cmd); server_client_exec(loop, cmd);
else else
server_client_detach(cloop, msgtype); server_client_detach(loop, msgtype);
} }
} }
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
if (cmd != NULL) if (cmd != NULL)
server_client_exec(c, cmd); server_client_exec(tc, cmd);
else else
server_client_detach(c, msgtype); server_client_detach(tc, msgtype);
return (CMD_RETURN_STOP); return (CMD_RETURN_STOP);
} }

View File

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

View File

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

View File

@@ -34,10 +34,10 @@ const struct cmd_entry cmd_display_panes_entry = {
.name = "display-panes", .name = "display-panes",
.alias = "displayp", .alias = "displayp",
.args = { "bd:t:", 0, 1 }, .args = { "bd:Nt:", 0, 1 },
.usage = "[-b] [-d duration] " CMD_TARGET_CLIENT_USAGE " [template]", .usage = "[-bN] [-d duration] " CMD_TARGET_CLIENT_USAGE " [template]",
.flags = CMD_AFTERHOOK, .flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG,
.exec = cmd_display_panes_exec .exec = cmd_display_panes_exec
}; };
@@ -55,11 +55,11 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
struct session *s = c->session; struct session *s = c->session;
struct options *oo = s->options; struct options *oo = s->options;
struct window *w = wp->window; struct window *w = wp->window;
struct grid_cell gc; struct grid_cell fgc, bgc;
u_int idx, px, py, i, j, xoff, yoff, sx, sy; u_int pane, idx, px, py, i, j, xoff, yoff, sx, sy;
int colour, active_colour; int colour, active_colour;
char buf[16], *ptr; char buf[16], lbuf[16], rbuf[16], *ptr;
size_t len; size_t len, llen, rlen;
if (wp->xoff + wp->sx <= ctx->ox || if (wp->xoff + wp->sx <= ctx->ox ||
wp->xoff >= ctx->ox + ctx->sx || wp->xoff >= ctx->ox + ctx->sx ||
@@ -109,31 +109,50 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
px = sx / 2; px = sx / 2;
py = sy / 2; py = sy / 2;
if (window_pane_index(wp, &idx) != 0) if (window_pane_index(wp, &pane) != 0)
fatalx("index not found"); fatalx("index not found");
len = xsnprintf(buf, sizeof buf, "%u", idx); len = xsnprintf(buf, sizeof buf, "%u", pane);
if (sx < len) if (sx < len)
return; return;
colour = options_get_number(oo, "display-panes-colour"); colour = options_get_number(oo, "display-panes-colour");
active_colour = options_get_number(oo, "display-panes-active-colour"); active_colour = options_get_number(oo, "display-panes-active-colour");
memcpy(&fgc, &grid_default_cell, sizeof fgc);
memcpy(&bgc, &grid_default_cell, sizeof bgc);
if (w->active == wp) {
fgc.fg = active_colour;
bgc.bg = active_colour;
} else {
fgc.fg = colour;
bgc.bg = colour;
}
rlen = xsnprintf(rbuf, sizeof rbuf, "%ux%u", wp->sx, wp->sy);
if (pane > 9 && pane < 35)
llen = xsnprintf(lbuf, sizeof lbuf, "%c", 'a' + (pane - 10));
else
llen = 0;
if (sx < len * 6 || sy < 5) { if (sx < len * 6 || sy < 5) {
tty_cursor(tty, xoff + px - len / 2, yoff + py); tty_attributes(tty, &fgc, &grid_default_cell, NULL);
goto draw_text; if (sx >= len + llen + 1) {
len += llen + 1;
tty_cursor(tty, xoff + px - len / 2, yoff + py);
tty_putn(tty, buf, len, len);
tty_putn(tty, " ", 1, 1);
tty_putn(tty, lbuf, llen, llen);
} else {
tty_cursor(tty, xoff + px - len / 2, yoff + py);
tty_putn(tty, buf, len, len);
}
goto out;
} }
px -= len * 3; px -= len * 3;
py -= 2; py -= 2;
memcpy(&gc, &grid_default_cell, sizeof gc); tty_attributes(tty, &bgc, &grid_default_cell, NULL);
if (w->active == wp)
gc.bg = active_colour;
else
gc.bg = colour;
gc.flags |= GRID_FLAG_NOPALETTE;
tty_attributes(tty, &gc, wp);
for (ptr = buf; *ptr != '\0'; ptr++) { for (ptr = buf; *ptr != '\0'; ptr++) {
if (*ptr < '0' || *ptr > '9') if (*ptr < '0' || *ptr > '9')
continue; continue;
@@ -149,22 +168,20 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
px += 6; px += 6;
} }
len = xsnprintf(buf, sizeof buf, "%ux%u", wp->sx, wp->sy); if (sy <= 6)
if (sx < len || sy < 6) goto out;
return; tty_attributes(tty, &fgc, &grid_default_cell, NULL);
tty_cursor(tty, xoff + sx - len, yoff); if (rlen != 0 && sx >= rlen) {
tty_cursor(tty, xoff + sx - rlen, yoff);
draw_text: tty_putn(tty, rbuf, rlen, rlen);
memcpy(&gc, &grid_default_cell, sizeof gc); }
if (w->active == wp) if (llen != 0) {
gc.fg = active_colour; tty_cursor(tty, xoff + sx / 2 + len * 3 - llen - 1,
else yoff + py + 5);
gc.fg = colour; tty_putn(tty, lbuf, llen, llen);
gc.flags |= GRID_FLAG_NOPALETTE; }
tty_attributes(tty, &gc, wp);
tty_puts(tty, buf);
out:
tty_cursor(tty, 0, 0); tty_cursor(tty, 0, 0);
} }
@@ -197,16 +214,25 @@ static int
cmd_display_panes_key(struct client *c, struct key_event *event) cmd_display_panes_key(struct client *c, struct key_event *event)
{ {
struct cmd_display_panes_data *cdata = c->overlay_data; struct cmd_display_panes_data *cdata = c->overlay_data;
struct cmdq_item *new_item; char *cmd, *expanded, *error;
char *cmd, *expanded;
struct window *w = c->session->curw->window; struct window *w = c->session->curw->window;
struct window_pane *wp; struct window_pane *wp;
struct cmd_parse_result *pr; enum cmd_parse_status status;
u_int index;
key_code key;
if (event->key < '0' || event->key > '9') if (event->key >= '0' && event->key <= '9')
index = event->key - '0';
else if ((event->key & KEYC_MASK_MODIFIERS) == 0) {
key = (event->key & KEYC_MASK_KEY);
if (key >= 'a' && key <= 'z')
index = 10 + (key - 'a');
else
return (-1);
} else
return (-1); return (-1);
wp = window_pane_at_index(w, event->key - '0'); wp = window_pane_at_index(w, index);
if (wp == NULL) if (wp == NULL)
return (1); return (1);
window_unzoom(w); window_unzoom(w);
@@ -214,21 +240,10 @@ cmd_display_panes_key(struct client *c, struct key_event *event)
xasprintf(&expanded, "%%%u", wp->id); xasprintf(&expanded, "%%%u", wp->id);
cmd = cmd_template_replace(cdata->command, expanded, 1); cmd = cmd_template_replace(cdata->command, expanded, 1);
pr = cmd_parse_from_string(cmd, NULL); status = cmd_parse_and_append(cmd, NULL, c, NULL, &error);
switch (pr->status) { if (status == CMD_PARSE_ERROR) {
case CMD_PARSE_EMPTY: cmdq_append(c, cmdq_get_error(error));
new_item = NULL; free(error);
break;
case CMD_PARSE_ERROR:
new_item = cmdq_get_error(pr->error);
free(pr->error);
cmdq_append(c, new_item);
break;
case CMD_PARSE_SUCCESS:
new_item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
cmd_list_free(pr->cmdlist);
cmdq_append(c, new_item);
break;
} }
free(cmd); free(cmd);
@@ -239,18 +254,14 @@ cmd_display_panes_key(struct client *c, struct key_event *event)
static enum cmd_retval static enum cmd_retval
cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item) cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = cmd_get_args(self);
struct client *c; struct client *tc = cmdq_get_target_client(item);
struct session *s; struct session *s = tc->session;
u_int delay; u_int delay;
char *cause; char *cause;
struct cmd_display_panes_data *cdata; struct cmd_display_panes_data *cdata;
if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL) if (tc->overlay_draw != NULL)
return (CMD_RETURN_ERROR);
s = c->session;
if (c->overlay_draw != NULL)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
if (args_has(args, 'd')) { if (args_has(args, 'd')) {
@@ -273,8 +284,15 @@ cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item)
else else
cdata->item = item; cdata->item = item;
server_client_set_overlay(c, delay, cmd_display_panes_draw, if (args_has(args, 'N')) {
cmd_display_panes_key, cmd_display_panes_free, cdata); server_client_set_overlay(tc, delay, NULL, NULL,
cmd_display_panes_draw, NULL, cmd_display_panes_free,
cdata);
} else {
server_client_set_overlay(tc, delay, NULL, NULL,
cmd_display_panes_draw, cmd_display_panes_key,
cmd_display_panes_free, cdata);
}
if (args_has(args, 'b')) if (args_has(args, 'b'))
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);

View File

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

View File

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

View File

@@ -56,26 +56,23 @@ struct cmd_if_shell_data {
struct client *client; struct client *client;
struct cmdq_item *item; struct cmdq_item *item;
struct mouse_event mouse;
}; };
static enum cmd_retval static enum cmd_retval
cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = cmd_get_args(self);
struct mouse_event *m = &item->shared->mouse; struct cmd_find_state *target = cmdq_get_target(item);
struct cmdq_state *state = cmdq_get_state(item);
struct cmd_if_shell_data *cdata; struct cmd_if_shell_data *cdata;
char *shellcmd, *cmd; char *shellcmd, *cmd, *error;
struct cmdq_item *new_item; const char *file;
struct cmd_find_state *fs = &item->target; struct client *tc = cmdq_get_target_client(item);
struct client *c = cmd_find_client(item, NULL, 1); struct session *s = target->s;
struct session *s = fs->s;
struct winlink *wl = fs->wl;
struct window_pane *wp = fs->wp;
struct cmd_parse_input pi; struct cmd_parse_input pi;
struct cmd_parse_result *pr; enum cmd_parse_status status;
shellcmd = format_single(item, args->argv[0], c, s, wl, wp); shellcmd = format_single_from_target(item, args->argv[0]);
if (args_has(args, 'F')) { if (args_has(args, 'F')) {
if (*shellcmd != '0' && *shellcmd != '\0') if (*shellcmd != '0' && *shellcmd != '\0')
cmd = args->argv[1]; cmd = args->argv[1];
@@ -88,26 +85,16 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
memset(&pi, 0, sizeof pi); memset(&pi, 0, sizeof pi);
if (self->file != NULL) cmd_get_source(self, &pi.file, &pi.line);
pi.file = self->file;
pi.line = self->line;
pi.item = item; pi.item = item;
pi.c = c; pi.c = tc;
cmd_find_copy_state(&pi.fs, fs); cmd_find_copy_state(&pi.fs, target);
pr = cmd_parse_from_string(cmd, &pi); status = cmd_parse_and_insert(cmd, &pi, item, state, &error);
switch (pr->status) { if (status == CMD_PARSE_ERROR) {
case CMD_PARSE_EMPTY: cmdq_error(item, "%s", error);
break; free(error);
case CMD_PARSE_ERROR:
cmdq_error(item, "%s", pr->error);
free(pr->error);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
case CMD_PARSE_SUCCESS:
new_item = cmdq_get_command(pr->cmdlist, fs, m, 0);
cmdq_insert_after(item, new_item);
cmd_list_free(pr->cmdlist);
break;
} }
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
@@ -119,12 +106,11 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
cdata->cmd_else = xstrdup(args->argv[2]); cdata->cmd_else = xstrdup(args->argv[2]);
else else
cdata->cmd_else = NULL; cdata->cmd_else = NULL;
memcpy(&cdata->mouse, m, sizeof cdata->mouse);
if (!args_has(args, 'b')) if (!args_has(args, 'b'))
cdata->client = item->client; cdata->client = cmdq_get_client(item);
else else
cdata->client = c; cdata->client = tc;
if (cdata->client != NULL) if (cdata->client != NULL)
cdata->client->references++; cdata->client->references++;
@@ -134,17 +120,18 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
cdata->item = NULL; cdata->item = NULL;
memset(&cdata->input, 0, sizeof cdata->input); memset(&cdata->input, 0, sizeof cdata->input);
if (self->file != NULL) cmd_get_source(self, &file, &cdata->input.line);
cdata->input.file = xstrdup(self->file); if (file != NULL)
cdata->input.line = self->line; cdata->input.file = xstrdup(file);
cdata->input.item = cdata->item; cdata->input.c = tc;
cdata->input.c = c;
if (cdata->input.c != NULL) if (cdata->input.c != NULL)
cdata->input.c->references++; cdata->input.c->references++;
cmd_find_copy_state(&cdata->input.fs, fs); cmd_find_copy_state(&cdata->input.fs, target);
if (job_run(shellcmd, s, server_client_get_cwd(item->client, s), NULL, if (job_run(shellcmd, 0, NULL, s,
cmd_if_shell_callback, cmd_if_shell_free, cdata, 0) == NULL) { server_client_get_cwd(cmdq_get_client(item), s), NULL,
cmd_if_shell_callback, cmd_if_shell_free, cdata, 0, -1,
-1) == NULL) {
cmdq_error(item, "failed to run command: %s", shellcmd); cmdq_error(item, "failed to run command: %s", shellcmd);
free(shellcmd); free(shellcmd);
free(cdata); free(cdata);
@@ -162,8 +149,8 @@ cmd_if_shell_callback(struct job *job)
{ {
struct cmd_if_shell_data *cdata = job_get_data(job); struct cmd_if_shell_data *cdata = job_get_data(job);
struct client *c = cdata->client; struct client *c = cdata->client;
struct mouse_event *m = &cdata->mouse;
struct cmdq_item *new_item = NULL; struct cmdq_item *new_item = NULL;
struct cmdq_state *new_state = NULL;
char *cmd; char *cmd;
int status; int status;
struct cmd_parse_result *pr; struct cmd_parse_result *pr;
@@ -186,7 +173,13 @@ cmd_if_shell_callback(struct job *job)
free(pr->error); free(pr->error);
break; break;
case CMD_PARSE_SUCCESS: case CMD_PARSE_SUCCESS:
new_item = cmdq_get_command(pr->cmdlist, NULL, m, 0); if (cdata->item == NULL)
new_state = cmdq_new_state(NULL, NULL, 0);
else
new_state = cmdq_get_state(cdata->item);
new_item = cmdq_get_command(pr->cmdlist, new_state);
if (cdata->item == NULL)
cmdq_free_state(new_state);
cmd_list_free(pr->cmdlist); cmd_list_free(pr->cmdlist);
break; break;
} }

View File

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

View File

@@ -44,14 +44,17 @@ const struct cmd_entry cmd_kill_pane_entry = {
static enum cmd_retval static enum cmd_retval
cmd_kill_pane_exec(struct cmd *self, struct cmdq_item *item) cmd_kill_pane_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct winlink *wl = item->target.wl; struct args *args = cmd_get_args(self);
struct window_pane *loopwp, *tmpwp, *wp = item->target.wp; struct cmd_find_state *target = cmdq_get_target(item);
struct winlink *wl = target->wl;
struct window_pane *loopwp, *tmpwp, *wp = target->wp;
if (args_has(self->args, 'a')) { if (args_has(args, 'a')) {
server_unzoom_window(wl->window); server_unzoom_window(wl->window);
TAILQ_FOREACH_SAFE(loopwp, &wl->window->panes, entry, tmpwp) { TAILQ_FOREACH_SAFE(loopwp, &wl->window->panes, entry, tmpwp) {
if (loopwp == wp) if (loopwp == wp)
continue; continue;
server_client_remove_pane(loopwp);
layout_close_pane(loopwp); layout_close_pane(loopwp);
window_remove_pane(wl->window, loopwp); window_remove_pane(wl->window, loopwp);
} }

View File

@@ -54,7 +54,7 @@ const struct cmd_entry cmd_start_server_entry = {
static enum cmd_retval static enum cmd_retval
cmd_kill_server_exec(struct cmd *self, __unused struct cmdq_item *item) cmd_kill_server_exec(struct cmd *self, __unused struct cmdq_item *item)
{ {
if (self->entry == &cmd_kill_server_entry) if (cmd_get_entry(self) == &cmd_kill_server_entry)
kill(getpid(), SIGTERM); kill(getpid(), SIGTERM);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);

View File

@@ -45,11 +45,10 @@ const struct cmd_entry cmd_kill_session_entry = {
static enum cmd_retval static enum cmd_retval
cmd_kill_session_exec(struct cmd *self, struct cmdq_item *item) cmd_kill_session_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = cmd_get_args(self);
struct session *s, *sloop, *stmp; struct cmd_find_state *target = cmdq_get_target(item);
struct winlink *wl; struct session *s = target->s, *sloop, *stmp;
struct winlink *wl;
s = item->target.s;
if (args_has(args, 'C')) { if (args_has(args, 'C')) {
RB_FOREACH(wl, winlinks, &s->windows) { RB_FOREACH(wl, winlinks, &s->windows) {

View File

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

View File

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

View File

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

View File

@@ -68,11 +68,12 @@ cmd_list_keys_get_width(const char *tablename, key_code only)
while (bd != NULL) { while (bd != NULL) {
if ((only != KEYC_UNKNOWN && bd->key != only) || if ((only != KEYC_UNKNOWN && bd->key != only) ||
KEYC_IS_MOUSE(bd->key) || KEYC_IS_MOUSE(bd->key) ||
bd->note == NULL) { bd->note == NULL ||
*bd->note == '\0') {
bd = key_bindings_next(table, bd); bd = key_bindings_next(table, bd);
continue; continue;
} }
width = utf8_cstrwidth(key_string_lookup_key(bd->key)); width = utf8_cstrwidth(key_string_lookup_key(bd->key, 0));
if (width > keywidth) if (width > keywidth)
keywidth = width; keywidth = width;
@@ -85,7 +86,7 @@ static int
cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args, cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args,
const char *tablename, u_int keywidth, key_code only, const char *prefix) const char *tablename, u_int keywidth, key_code only, const char *prefix)
{ {
struct client *c = cmd_find_client(item, NULL, 1); struct client *tc = cmdq_get_target_client(item);
struct key_table *table; struct key_table *table;
struct key_binding *bd; struct key_binding *bd;
const char *key; const char *key;
@@ -99,21 +100,23 @@ cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args,
while (bd != NULL) { while (bd != NULL) {
if ((only != KEYC_UNKNOWN && bd->key != only) || if ((only != KEYC_UNKNOWN && bd->key != only) ||
KEYC_IS_MOUSE(bd->key) || KEYC_IS_MOUSE(bd->key) ||
(bd->note == NULL && !args_has(args, 'a'))) { ((bd->note == NULL || *bd->note == '\0') &&
!args_has(args, 'a'))) {
bd = key_bindings_next(table, bd); bd = key_bindings_next(table, bd);
continue; continue;
} }
found = 1; found = 1;
key = key_string_lookup_key(bd->key); key = key_string_lookup_key(bd->key, 0);
if (bd->note == NULL) if (bd->note == NULL || *bd->note == '\0')
note = cmd_list_print(bd->cmdlist, 1); note = cmd_list_print(bd->cmdlist, 1);
else else
note = xstrdup(bd->note); note = xstrdup(bd->note);
tmp = utf8_padcstr(key, keywidth + 1); tmp = utf8_padcstr(key, keywidth + 1);
if (args_has(args, '1') && c != NULL) if (args_has(args, '1') && tc != NULL) {
status_message_set(c, "%s%s%s", prefix, tmp, note); status_message_set(tc, -1, 1, 0, "%s%s%s", prefix, tmp,
else note);
} else
cmdq_print(item, "%s%s%s", prefix, tmp, note); cmdq_print(item, "%s%s%s", prefix, tmp, note);
free(tmp); free(tmp);
free(note); free(note);
@@ -133,7 +136,7 @@ cmd_list_keys_get_prefix(struct args *args, key_code *prefix)
*prefix = options_get_number(global_s_options, "prefix"); *prefix = options_get_number(global_s_options, "prefix");
if (!args_has(args, 'P')) { if (!args_has(args, 'P')) {
if (*prefix != KEYC_NONE) if (*prefix != KEYC_NONE)
xasprintf(&s, "%s ", key_string_lookup_key(*prefix)); xasprintf(&s, "%s ", key_string_lookup_key(*prefix, 0));
else else
s = xstrdup(""); s = xstrdup("");
} else } else
@@ -144,7 +147,7 @@ cmd_list_keys_get_prefix(struct args *args, key_code *prefix)
static enum cmd_retval static enum cmd_retval
cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = cmd_get_args(self);
struct key_table *table; struct key_table *table;
struct key_binding *bd; struct key_binding *bd;
const char *tablename, *r; const char *tablename, *r;
@@ -153,7 +156,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
int repeat, width, tablewidth, keywidth, found = 0; int repeat, width, tablewidth, keywidth, found = 0;
size_t tmpsize, tmpused, cplen; size_t tmpsize, tmpused, cplen;
if (self->entry == &cmd_list_commands_entry) if (cmd_get_entry(self) == &cmd_list_commands_entry)
return (cmd_list_keys_commands(self, item)); return (cmd_list_keys_commands(self, item));
if (args->argc != 0) { if (args->argc != 0) {
@@ -162,6 +165,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
cmdq_error(item, "invalid key: %s", args->argv[0]); cmdq_error(item, "invalid key: %s", args->argv[0]);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
only &= KEYC_MASK_KEY;
} }
tablename = args_get(args, 'T'); tablename = args_get(args, 'T');
@@ -219,7 +223,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
bd = key_bindings_next(table, bd); bd = key_bindings_next(table, bd);
continue; continue;
} }
key = args_escape(key_string_lookup_key(bd->key)); key = args_escape(key_string_lookup_key(bd->key, 0));
if (bd->flags & KEY_BINDING_REPEAT) if (bd->flags & KEY_BINDING_REPEAT)
repeat = 1; repeat = 1;
@@ -253,7 +257,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
continue; continue;
} }
found = 1; found = 1;
key = args_escape(key_string_lookup_key(bd->key)); key = args_escape(key_string_lookup_key(bd->key, 0));
if (!repeat) if (!repeat)
r = ""; r = "";
@@ -269,7 +273,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
tmpsize *= 2; tmpsize *= 2;
tmp = xrealloc(tmp, tmpsize); tmp = xrealloc(tmp, tmpsize);
} }
tmpused = strlcat(tmp, cp, tmpsize); strlcat(tmp, cp, tmpsize);
tmpused = strlcat(tmp, " ", tmpsize); tmpused = strlcat(tmp, " ", tmpsize);
free(cp); free(cp);
@@ -279,7 +283,7 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
tmpsize *= 2; tmpsize *= 2;
tmp = xrealloc(tmp, tmpsize); tmp = xrealloc(tmp, tmpsize);
} }
tmpused = strlcat(tmp, cp, tmpsize); strlcat(tmp, cp, tmpsize);
tmpused = strlcat(tmp, " ", tmpsize); tmpused = strlcat(tmp, " ", tmpsize);
free(cp); free(cp);
@@ -313,7 +317,7 @@ out:
static enum cmd_retval static enum cmd_retval
cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item) cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = cmd_get_args(self);
const struct cmd_entry **entryp; const struct cmd_entry **entryp;
const struct cmd_entry *entry; const struct cmd_entry *entry;
struct format_tree *ft; struct format_tree *ft;
@@ -329,7 +333,7 @@ cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
"#{command_list_usage}"; "#{command_list_usage}";
} }
ft = format_create(item->client, item, FORMAT_NONE, 0); ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_defaults(ft, NULL, NULL, NULL, NULL); format_defaults(ft, NULL, NULL, NULL, NULL);
for (entryp = cmd_table; *entryp != NULL; entryp++) { for (entryp = cmd_table; *entryp != NULL; entryp++) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -39,10 +39,10 @@ const struct cmd_entry cmd_new_session_entry = {
.name = "new-session", .name = "new-session",
.alias = "new", .alias = "new",
.args = { "Ac:dDEF:n:Ps:t:x:Xy:", 0, -1 }, .args = { "Ac:dDe:EF:f:n:Ps:t:x:Xy:", 0, -1 },
.usage = "[-AdDEPX] [-c start-directory] [-F format] [-n window-name] " .usage = "[-AdDEPX] [-c start-directory] [-e environment] [-F format] "
"[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] " "[-f flags] [-n window-name] [-s session-name] "
"[-y height] [command]", CMD_TARGET_SESSION_USAGE " [-x width] [-y height] [command]",
.target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL }, .target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL },
@@ -66,22 +66,26 @@ const struct cmd_entry cmd_has_session_entry = {
static enum cmd_retval static enum cmd_retval
cmd_new_session_exec(struct cmd *self, struct cmdq_item *item) cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = cmd_get_args(self);
struct client *c = item->client; struct cmd_find_state *current = cmdq_get_current(item);
struct session *s, *as, *groupwith; struct cmd_find_state *target = cmdq_get_target(item);
struct client *c = cmdq_get_client(item);
struct session *s, *as, *groupwith = NULL;
struct environ *env; struct environ *env;
struct options *oo; struct options *oo;
struct termios tio, *tiop; struct termios tio, *tiop;
struct session_group *sg; struct session_group *sg = NULL;
const char *errstr, *template, *group, *prefix, *tmp; const char *errstr, *template, *group, *tmp, *add;
char *cause, *cwd = NULL, *cp, *newname = NULL; char *cause, *cwd = NULL, *cp, *newname = NULL;
char *name, *prefix = NULL;
int detached, already_attached, is_control = 0; int detached, already_attached, is_control = 0;
u_int sx, sy, dsx, dsy; u_int sx, sy, dsx, dsy;
struct spawn_context sc; struct spawn_context sc;
enum cmd_retval retval; enum cmd_retval retval;
struct cmd_find_state fs; struct cmd_find_state fs;
struct args_value *value;
if (self->entry == &cmd_has_session_entry) { if (cmd_get_entry(self) == &cmd_has_session_entry) {
/* /*
* cmd_find_target() will fail if the session cannot be found, * cmd_find_target() will fail if the session cannot be found,
* so always return success here. * so always return success here.
@@ -96,21 +100,19 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
tmp = args_get(args, 's'); tmp = args_get(args, 's');
if (tmp != NULL) { if (tmp != NULL) {
newname = format_single(item, tmp, c, NULL, NULL, NULL); name = format_single(item, tmp, c, NULL, NULL, NULL);
if (!session_check_name(newname)) { newname = session_check_name(name);
cmdq_error(item, "bad session name: %s", newname); free(name);
goto fail;
}
} }
if (args_has(args, 'A')) { if (args_has(args, 'A')) {
if (newname != NULL) if (newname != NULL)
as = session_find(newname); as = session_find(newname);
else else
as = item->target.s; as = target->s;
if (as != NULL) { if (as != NULL) {
retval = cmd_attach_session(item, as->name, retval = cmd_attach_session(item, as->name,
args_has(args, 'D'), args_has(args, 'X'), 0, NULL, args_has(args, 'D'), args_has(args, 'X'), 0, NULL,
args_has(args, 'E')); args_has(args, 'E'), args_get(args, 'f'));
free(newname); free(newname);
return (retval); return (retval);
} }
@@ -123,25 +125,17 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
/* Is this going to be part of a session group? */ /* Is this going to be part of a session group? */
group = args_get(args, 't'); group = args_get(args, 't');
if (group != NULL) { if (group != NULL) {
groupwith = item->target.s; groupwith = target->s;
if (groupwith == NULL) { if (groupwith == NULL)
if (!session_check_name(group)) {
cmdq_error(item, "bad group name: %s", group);
goto fail;
}
sg = session_group_find(group); sg = session_group_find(group);
} else else
sg = session_group_contains(groupwith); sg = session_group_contains(groupwith);
if (sg != NULL) if (sg != NULL)
prefix = sg->name; prefix = xstrdup(sg->name);
else if (groupwith != NULL) else if (groupwith != NULL)
prefix = groupwith->name; prefix = xstrdup(groupwith->name);
else else
prefix = group; prefix = session_check_name(group);
} else {
groupwith = NULL;
sg = NULL;
prefix = NULL;
} }
/* Set -d if no client. */ /* Set -d if no client. */
@@ -171,13 +165,16 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
* the terminal as that calls tcsetattr() to prepare for tmux taking * the terminal as that calls tcsetattr() to prepare for tmux taking
* over. * over.
*/ */
if (!detached && !already_attached && c->tty.fd != -1) { if (!detached &&
if (server_client_check_nested(item->client)) { !already_attached &&
c->fd != -1 &&
(~c->flags & CLIENT_CONTROL)) {
if (server_client_check_nested(cmdq_get_client(item))) {
cmdq_error(item, "sessions should be nested with care, " cmdq_error(item, "sessions should be nested with care, "
"unset $TMUX to force"); "unset $TMUX to force");
goto fail; goto fail;
} }
if (tcgetattr(c->tty.fd, &tio) != 0) if (tcgetattr(c->fd, &tio) != 0)
fatal("tcgetattr failed"); fatal("tcgetattr failed");
tiop = &tio; tiop = &tio;
} else } else
@@ -207,7 +204,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
goto fail; goto fail;
} }
} }
} } else
dsx = 80;
if (args_has(args, 'y')) { if (args_has(args, 'y')) {
tmp = args_get(args, 'y'); tmp = args_get(args, 'y');
if (strcmp(tmp, "-") == 0) { if (strcmp(tmp, "-") == 0) {
@@ -222,7 +220,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
goto fail; goto fail;
} }
} }
} } else
dsy = 24;
/* Find new session size. */ /* Find new session size. */
if (!detached && !is_control) { if (!detached && !is_control) {
@@ -233,13 +232,14 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
} else { } else {
tmp = options_get_string(global_s_options, "default-size"); tmp = options_get_string(global_s_options, "default-size");
if (sscanf(tmp, "%ux%u", &sx, &sy) != 2) { if (sscanf(tmp, "%ux%u", &sx, &sy) != 2) {
sx = 80;
sy = 24;
}
if (args_has(args, 'x'))
sx = dsx; sx = dsx;
if (args_has(args, 'y'))
sy = dsy; sy = dsy;
} else {
if (args_has(args, 'x'))
sx = dsx;
if (args_has(args, 'y'))
sy = dsy;
}
} }
if (sx == 0) if (sx == 0)
sx = 1; sx = 1;
@@ -258,13 +258,17 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
env = environ_create(); env = environ_create();
if (c != NULL && !args_has(args, 'E')) if (c != NULL && !args_has(args, 'E'))
environ_update(global_s_options, c->environ, env); environ_update(global_s_options, c->environ, env);
add = args_first_value(args, 'e', &value);
while (add != NULL) {
environ_put(env, add, 0);
add = args_next_value(&value);
}
s = session_create(prefix, newname, cwd, env, oo, tiop); s = session_create(prefix, newname, cwd, env, oo, tiop);
/* Spawn the initial window. */ /* Spawn the initial window. */
memset(&sc, 0, sizeof sc); memset(&sc, 0, sizeof sc);
sc.item = item; sc.item = item;
sc.s = s; sc.s = s;
sc.c = c;
sc.name = args_get(args, 'n'); sc.name = args_get(args, 'n');
sc.argc = args->argc; sc.argc = args->argc;
@@ -305,13 +309,15 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
* taking this session and needs to get MSG_READY and stay around. * taking this session and needs to get MSG_READY and stay around.
*/ */
if (!detached) { if (!detached) {
if (args_has(args, 'f'))
server_client_set_flags(c, args_get(args, 'f'));
if (!already_attached) { if (!already_attached) {
if (~c->flags & CLIENT_CONTROL) if (~c->flags & CLIENT_CONTROL)
proc_send(c->peer, MSG_READY, -1, NULL, 0); proc_send(c->peer, MSG_READY, -1, NULL, 0);
} else if (c->session != NULL) } else if (c->session != NULL)
c->last_session = c->session; c->last_session = c->session;
c->session = s; c->session = s;
if (~item->shared->flags & CMDQ_SHARED_REPEAT) if (~cmdq_get_flags(item) & CMDQ_STATE_REPEAT)
server_client_set_key_table(c, NULL); server_client_set_key_table(c, NULL);
tty_update_client_offset(c); tty_update_client_offset(c);
status_timer_start(c); status_timer_start(c);
@@ -339,20 +345,22 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
free(cp); free(cp);
} }
if (!detached) { if (!detached)
c->flags |= CLIENT_ATTACHED; c->flags |= CLIENT_ATTACHED;
cmd_find_from_session(&item->shared->current, s, 0); if (!args_has(args, 'd'))
} cmd_find_from_session(current, s, 0);
cmd_find_from_session(&fs, s, 0); cmd_find_from_session(&fs, s, 0);
cmdq_insert_hook(s, item, &fs, "after-new-session"); cmdq_insert_hook(s, item, &fs, "after-new-session");
free(cwd); free(cwd);
free(newname); free(newname);
free(prefix);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
fail: fail:
free(cwd); free(cwd);
free(newname); free(newname);
free(prefix);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }

View File

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

View File

@@ -26,6 +26,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <wchar.h>
#include "tmux.h" #include "tmux.h"
@@ -42,7 +43,6 @@ struct cmd_parse_scope {
}; };
struct cmd_parse_command { struct cmd_parse_command {
char *name;
u_int line; u_int line;
int argc; int argc;
@@ -77,6 +77,7 @@ static char *cmd_parse_get_error(const char *, u_int, const char *);
static void cmd_parse_free_command(struct cmd_parse_command *); static void cmd_parse_free_command(struct cmd_parse_command *);
static struct cmd_parse_commands *cmd_parse_new_commands(void); static struct cmd_parse_commands *cmd_parse_new_commands(void);
static void cmd_parse_free_commands(struct cmd_parse_commands *); static void cmd_parse_free_commands(struct cmd_parse_commands *);
static char *cmd_parse_commands_to_string(struct cmd_parse_commands *);
static void cmd_parse_print_commands(struct cmd_parse_input *, u_int, static void cmd_parse_print_commands(struct cmd_parse_input *, u_int,
struct cmd_list *); struct cmd_list *);
@@ -99,6 +100,7 @@ static void cmd_parse_print_commands(struct cmd_parse_input *, u_int,
} }
%token ERROR %token ERROR
%token HIDDEN
%token IF %token IF
%token ELSE %token ELSE
%token ELIF %token ELIF
@@ -109,7 +111,8 @@ static void cmd_parse_print_commands(struct cmd_parse_input *, u_int,
%type <arguments> arguments %type <arguments> arguments
%type <flag> if_open if_elif %type <flag> if_open if_elif
%type <elif> elif elif1 %type <elif> elif elif1
%type <commands> statements statement commands condition condition1 %type <commands> argument_statements statements statement
%type <commands> commands condition condition1
%type <command> command %type <command> command
%% %%
@@ -134,6 +137,11 @@ statements : statement '\n'
} }
statement : /* empty */ statement : /* empty */
{
$$ = xmalloc (sizeof *$$);
TAILQ_INIT($$);
}
| hidden_assignment
{ {
$$ = xmalloc (sizeof *$$); $$ = xmalloc (sizeof *$$);
TAILQ_INIT($$); TAILQ_INIT($$);
@@ -204,10 +212,21 @@ assignment : EQUALS
if ((~flags & CMD_PARSE_PARSEONLY) && if ((~flags & CMD_PARSE_PARSEONLY) &&
(ps->scope == NULL || ps->scope->flag)) (ps->scope == NULL || ps->scope->flag))
environ_put(global_environ, $1); environ_put(global_environ, $1, 0);
free($1); free($1);
} }
hidden_assignment : HIDDEN EQUALS
{
struct cmd_parse_state *ps = &parse_state;
int flags = ps->input->flags;
if ((~flags & CMD_PARSE_PARSEONLY) &&
(ps->scope == NULL || ps->scope->flag))
environ_put(global_environ, $2, ENVIRON_HIDDEN);
free($2);
}
if_open : IF expanded if_open : IF expanded
{ {
struct cmd_parse_state *ps = &parse_state; struct cmd_parse_state *ps = &parse_state;
@@ -341,7 +360,7 @@ commands : command
struct cmd_parse_state *ps = &parse_state; struct cmd_parse_state *ps = &parse_state;
$$ = cmd_parse_new_commands(); $$ = cmd_parse_new_commands();
if ($1->name != NULL && if ($1->argc != 0 &&
(ps->scope == NULL || ps->scope->flag)) (ps->scope == NULL || ps->scope->flag))
TAILQ_INSERT_TAIL($$, $1, entry); TAILQ_INSERT_TAIL($$, $1, entry);
else else
@@ -361,7 +380,7 @@ commands : command
{ {
struct cmd_parse_state *ps = &parse_state; struct cmd_parse_state *ps = &parse_state;
if ($3->name != NULL && if ($3->argc != 0 &&
(ps->scope == NULL || ps->scope->flag)) { (ps->scope == NULL || ps->scope->flag)) {
$$ = $1; $$ = $1;
TAILQ_INSERT_TAIL($$, $3, entry); TAILQ_INSERT_TAIL($$, $3, entry);
@@ -381,7 +400,6 @@ command : assignment
struct cmd_parse_state *ps = &parse_state; struct cmd_parse_state *ps = &parse_state;
$$ = xcalloc(1, sizeof *$$); $$ = xcalloc(1, sizeof *$$);
$$->name = NULL;
$$->line = ps->input->line; $$->line = ps->input->line;
} }
| optional_assignment TOKEN | optional_assignment TOKEN
@@ -389,20 +407,21 @@ command : assignment
struct cmd_parse_state *ps = &parse_state; struct cmd_parse_state *ps = &parse_state;
$$ = xcalloc(1, sizeof *$$); $$ = xcalloc(1, sizeof *$$);
$$->name = $2;
$$->line = ps->input->line; $$->line = ps->input->line;
cmd_prepend_argv(&$$->argc, &$$->argv, $2);
} }
| optional_assignment TOKEN arguments | optional_assignment TOKEN arguments
{ {
struct cmd_parse_state *ps = &parse_state; struct cmd_parse_state *ps = &parse_state;
$$ = xcalloc(1, sizeof *$$); $$ = xcalloc(1, sizeof *$$);
$$->name = $2;
$$->line = ps->input->line; $$->line = ps->input->line;
$$->argc = $3.argc; $$->argc = $3.argc;
$$->argv = $3.argv; $$->argv = $3.argv;
cmd_prepend_argv(&$$->argc, &$$->argv, $2);
} }
condition1 : if_open commands if_close condition1 : if_open commands if_close
@@ -506,6 +525,22 @@ argument : TOKEN
{ {
$$ = $1; $$ = $1;
} }
| '{' argument_statements
{
$$ = cmd_parse_commands_to_string($2);
cmd_parse_free_commands($2);
}
argument_statements : statement '}'
{
$$ = $1;
}
| statements statement '}'
{
$$ = $1;
TAILQ_CONCAT($$, $2, entry);
free($2);
}
%% %%
@@ -540,7 +575,6 @@ cmd_parse_print_commands(struct cmd_parse_input *pi, u_int line,
static void static void
cmd_parse_free_command(struct cmd_parse_command *cmd) cmd_parse_free_command(struct cmd_parse_command *cmd)
{ {
free(cmd->name);
cmd_free_argv(cmd->argc, cmd->argv); cmd_free_argv(cmd->argc, cmd->argv);
free(cmd); free(cmd);
} }
@@ -567,6 +601,30 @@ cmd_parse_free_commands(struct cmd_parse_commands *cmds)
free(cmds); free(cmds);
} }
static char *
cmd_parse_commands_to_string(struct cmd_parse_commands *cmds)
{
struct cmd_parse_command *cmd;
char *string = NULL, *s, *line;
TAILQ_FOREACH(cmd, cmds, entry) {
line = cmd_stringify_argv(cmd->argc, cmd->argv);
if (string == NULL)
s = line;
else {
xasprintf(&s, "%s ; %s", s, line);
free(line);
}
free(string);
string = s;
}
if (string == NULL)
string = xstrdup("");
log_debug("%s: %s", __func__, string);
return (string);
}
static struct cmd_parse_commands * static struct cmd_parse_commands *
cmd_parse_run_parser(char **cause) cmd_parse_run_parser(char **cause)
{ {
@@ -627,7 +685,7 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds,
int i; int i;
struct cmd_list *cmdlist = NULL, *result; struct cmd_list *cmdlist = NULL, *result;
struct cmd *add; struct cmd *add;
char *alias, *cause, *s; char *name, *alias, *cause, *s;
/* Check for an empty list. */ /* Check for an empty list. */
if (TAILQ_EMPTY(cmds)) { if (TAILQ_EMPTY(cmds)) {
@@ -643,12 +701,14 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds,
* command list. * command list.
*/ */
TAILQ_FOREACH_SAFE(cmd, cmds, entry, next) { TAILQ_FOREACH_SAFE(cmd, cmds, entry, next) {
alias = cmd_get_alias(cmd->name); name = cmd->argv[0];
alias = cmd_get_alias(name);
if (alias == NULL) if (alias == NULL)
continue; continue;
line = cmd->line; line = cmd->line;
log_debug("%s: %u %s = %s", __func__, line, cmd->name, alias); log_debug("%s: %u %s = %s", __func__, line, name, alias);
pi->line = line; pi->line = line;
cmds2 = cmd_parse_do_buffer(alias, strlen(alias), pi, &cause); cmds2 = cmd_parse_do_buffer(alias, strlen(alias), pi, &cause);
@@ -665,7 +725,7 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds,
cmd_parse_free_command(cmd); cmd_parse_free_command(cmd);
continue; continue;
} }
for (i = 0; i < cmd->argc; i++) for (i = 1; i < cmd->argc; i++)
cmd_append_argv(&cmd2->argc, &cmd2->argv, cmd->argv[i]); cmd_append_argv(&cmd2->argc, &cmd2->argv, cmd->argv[i]);
after = cmd; after = cmd;
@@ -683,15 +743,18 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds,
/* /*
* Parse each command into a command list. Create a new command list * Parse each command into a command list. Create a new command list
* for each line so they get a new group (so the queue knows which ones * for each line (unless the flag is set) so they get a new group (so
* to remove if a command fails when executed). * the queue knows which ones to remove if a command fails when
* executed).
*/ */
result = cmd_list_new(); result = cmd_list_new();
TAILQ_FOREACH(cmd, cmds, entry) { TAILQ_FOREACH(cmd, cmds, entry) {
log_debug("%s: %u %s", __func__, cmd->line, cmd->name); name = cmd->argv[0];
log_debug("%s: %u %s", __func__, cmd->line, name);
cmd_log_argv(cmd->argc, cmd->argv, __func__); cmd_log_argv(cmd->argc, cmd->argv, __func__);
if (cmdlist == NULL || cmd->line != line) { if (cmdlist == NULL ||
((~pi->flags & CMD_PARSE_ONEGROUP) && cmd->line != line)) {
if (cmdlist != NULL) { if (cmdlist != NULL) {
cmd_parse_print_commands(pi, line, cmdlist); cmd_parse_print_commands(pi, line, cmdlist);
cmd_list_move(result, cmdlist); cmd_list_move(result, cmdlist);
@@ -701,7 +764,6 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds,
} }
line = cmd->line; line = cmd->line;
cmd_prepend_argv(&cmd->argc, &cmd->argv, cmd->name);
add = cmd_parse(cmd->argc, cmd->argv, pi->file, line, &cause); add = cmd_parse(cmd->argc, cmd->argv, pi->file, line, &cause);
if (add == NULL) { if (add == NULL) {
cmd_list_free(result); cmd_list_free(result);
@@ -758,9 +820,74 @@ cmd_parse_from_file(FILE *f, struct cmd_parse_input *pi)
struct cmd_parse_result * struct cmd_parse_result *
cmd_parse_from_string(const char *s, struct cmd_parse_input *pi) cmd_parse_from_string(const char *s, struct cmd_parse_input *pi)
{ {
struct cmd_parse_input input;
if (pi == NULL) {
memset(&input, 0, sizeof input);
pi = &input;
}
/*
* When parsing a string, put commands in one group even if there are
* multiple lines. This means { a \n b } is identical to "a ; b" when
* given as an argument to another command.
*/
pi->flags |= CMD_PARSE_ONEGROUP;
return (cmd_parse_from_buffer(s, strlen(s), pi)); return (cmd_parse_from_buffer(s, strlen(s), pi));
} }
enum cmd_parse_status
cmd_parse_and_insert(const char *s, struct cmd_parse_input *pi,
struct cmdq_item *after, struct cmdq_state *state, char **error)
{
struct cmd_parse_result *pr;
struct cmdq_item *item;
pr = cmd_parse_from_string(s, pi);
switch (pr->status) {
case CMD_PARSE_EMPTY:
break;
case CMD_PARSE_ERROR:
if (error != NULL)
*error = pr->error;
else
free(pr->error);
break;
case CMD_PARSE_SUCCESS:
item = cmdq_get_command(pr->cmdlist, state);
cmdq_insert_after(after, item);
cmd_list_free(pr->cmdlist);
break;
}
return (pr->status);
}
enum cmd_parse_status
cmd_parse_and_append(const char *s, struct cmd_parse_input *pi,
struct client *c, struct cmdq_state *state, char **error)
{
struct cmd_parse_result *pr;
struct cmdq_item *item;
pr = cmd_parse_from_string(s, pi);
switch (pr->status) {
case CMD_PARSE_EMPTY:
break;
case CMD_PARSE_ERROR:
if (error != NULL)
*error = pr->error;
else
free(pr->error);
break;
case CMD_PARSE_SUCCESS:
item = cmdq_get_command(pr->cmdlist, state);
cmdq_append(c, item);
cmd_list_free(pr->cmdlist);
break;
}
return (pr->status);
}
struct cmd_parse_result * struct cmd_parse_result *
cmd_parse_from_buffer(const void *buf, size_t len, struct cmd_parse_input *pi) cmd_parse_from_buffer(const void *buf, size_t len, struct cmd_parse_input *pi)
{ {
@@ -836,11 +963,10 @@ cmd_parse_from_arguments(int argc, char **argv, struct cmd_parse_input *pi)
i); i);
cmd = xcalloc(1, sizeof *cmd); cmd = xcalloc(1, sizeof *cmd);
cmd->name = xstrdup(new_argv[0]);
cmd->line = pi->line; cmd->line = pi->line;
cmd->argc = new_argc - 1; cmd->argc = new_argc;
cmd->argv = cmd_copy_argv(new_argc - 1, new_argv + 1); cmd->argv = cmd_copy_argv(new_argc, new_argv);
TAILQ_INSERT_TAIL(cmds, cmd, entry); TAILQ_INSERT_TAIL(cmds, cmd, entry);
} }
@@ -856,11 +982,10 @@ cmd_parse_from_arguments(int argc, char **argv, struct cmd_parse_input *pi)
last); last);
cmd = xcalloc(1, sizeof *cmd); cmd = xcalloc(1, sizeof *cmd);
cmd->name = xstrdup(new_argv[0]);
cmd->line = pi->line; cmd->line = pi->line;
cmd->argc = new_argc - 1; cmd->argc = new_argc;
cmd->argv = cmd_copy_argv(new_argc - 1, new_argv + 1); cmd->argv = cmd_copy_argv(new_argc, new_argv);
TAILQ_INSERT_TAIL(cmds, cmd, entry); TAILQ_INSERT_TAIL(cmds, cmd, entry);
} }
@@ -1038,11 +1163,11 @@ yylex(void)
return ('\n'); return ('\n');
} }
if (ch == ';') { if (ch == ';' || ch == '{' || ch == '}') {
/* /*
* A semicolon is itself. * A semicolon or { or } is itself.
*/ */
return (';'); return (ch);
} }
if (ch == '#') { if (ch == '#') {
@@ -1079,6 +1204,10 @@ yylex(void)
if (*cp == '\0') if (*cp == '\0')
return (TOKEN); return (TOKEN);
ps->condition = 1; ps->condition = 1;
if (strcmp(yylval.token, "%hidden") == 0) {
free(yylval.token);
return (HIDDEN);
}
if (strcmp(yylval.token, "%if") == 0) { if (strcmp(yylval.token, "%if") == 0) {
free(yylval.token); free(yylval.token);
return (IF); return (IF);
@@ -1163,10 +1292,9 @@ error:
static int static int
yylex_token_escape(char **buf, size_t *len) yylex_token_escape(char **buf, size_t *len)
{ {
int ch, type, o2, o3; int ch, type, o2, o3, mlen;
u_int size, i, tmp; u_int size, i, tmp;
char s[9]; char s[9], m[MB_LEN_MAX];
struct utf8_data ud;
ch = yylex_getc(); ch = yylex_getc();
@@ -1251,11 +1379,12 @@ unicode:
yyerror("invalid \\%c argument", type); yyerror("invalid \\%c argument", type);
return (0); return (0);
} }
if (utf8_split(tmp, &ud) != UTF8_DONE) { mlen = wctomb(m, tmp);
if (mlen <= 0 || mlen > (int)sizeof m) {
yyerror("invalid \\%c argument", type); yyerror("invalid \\%c argument", type);
return (0); return (0);
} }
yylex_append(buf, len, ud.data, ud.size); yylex_append(buf, len, m, mlen);
return (1); return (1);
} }
@@ -1303,7 +1432,7 @@ yylex_token_variable(char **buf, size_t *len)
name[namelen] = '\0'; name[namelen] = '\0';
envent = environ_find(global_environ, name); envent = environ_find(global_environ, name);
if (envent != NULL) { if (envent != NULL && envent->value != NULL) {
value = envent->value; value = envent->value;
log_debug("%s: %s -> %s", __func__, name, value); log_debug("%s: %s -> %s", __func__, name, value);
yylex_append(buf, len, value, strlen(value)); yylex_append(buf, len, value, strlen(value));
@@ -1353,119 +1482,6 @@ yylex_token_tilde(char **buf, size_t *len)
return (1); return (1);
} }
static int
yylex_token_brace(char **buf, size_t *len)
{
struct cmd_parse_state *ps = &parse_state;
int ch, lines = 0, nesting = 1, escape = 0;
int quote = '\0', token = 0;
/*
* Extract a string up to the matching unquoted '}', including newlines
* and handling nested braces.
*
* To detect the final and intermediate braces which affect the nesting
* depth, we scan the input as if it was a tmux config file, and ignore
* braces which would be considered quoted, escaped, or in a comment.
*
* We update the token state after every character because '#' begins a
* comment only when it begins a token. For simplicity, we treat an
* unquoted directive format as comment.
*
* The result is verbatim copy of the input excluding the final brace.
*/
for (ch = yylex_getc1(); ch != EOF; ch = yylex_getc1()) {
yylex_append1(buf, len, ch);
if (ch == '\n')
lines++;
/*
* If the previous character was a backslash (escape is set),
* escape anything if unquoted or in double quotes, otherwise
* escape only '\n' and '\\'.
*/
if (escape &&
(quote == '\0' ||
quote == '"' ||
ch == '\n' ||
ch == '\\')) {
escape = 0;
if (ch != '\n')
token = 1;
continue;
}
/*
* The character is not escaped. If it is a backslash, set the
* escape flag.
*/
if (ch == '\\') {
escape = 1;
continue;
}
escape = 0;
/* A newline always resets to unquoted. */
if (ch == '\n') {
quote = token = 0;
continue;
}
if (quote) {
/*
* Inside quotes or comment. Check if this is the
* closing quote.
*/
if (ch == quote && quote != '#')
quote = 0;
token = 1; /* token continues regardless */
} else {
/* Not inside quotes or comment. */
switch (ch) {
case '"':
case '\'':
case '#':
/* Beginning of quote or maybe comment. */
if (ch != '#' || !token)
quote = ch;
token = 1;
break;
case ' ':
case '\t':
case ';':
/* Delimiter - token resets. */
token = 0;
break;
case '{':
nesting++;
token = 0; /* new commands set - token resets */
break;
case '}':
nesting--;
token = 1; /* same as after quotes */
if (nesting == 0) {
(*len)--; /* remove closing } */
ps->input->line += lines;
return (1);
}
break;
default:
token = 1;
break;
}
}
}
/*
* Update line count after error as reporting the opening line is more
* useful than EOF.
*/
yyerror("unterminated brace string");
ps->input->line += lines;
return (0);
}
static char * static char *
yylex_token(int ch) yylex_token(int ch)
{ {
@@ -1480,23 +1496,37 @@ yylex_token(int ch)
buf = xmalloc(1); buf = xmalloc(1);
for (;;) { for (;;) {
/* /* EOF or \n are always the end of the token. */
* EOF or \n are always the end of the token. If inside quotes if (ch == EOF || (state == NONE && ch == '\n'))
* they are an error.
*/
if (ch == EOF || ch == '\n') {
if (state != NONE)
goto error;
break; break;
/* Whitespace or ; or } ends a token unless inside quotes. */
if ((ch == ' ' || ch == '\t' || ch == ';' || ch == '}') &&
state == NONE)
break;
/*
* Spaces and comments inside quotes after \n are removed but
* the \n is left.
*/
if (ch == '\n' && state != NONE) {
yylex_append1(&buf, &len, '\n');
while ((ch = yylex_getc()) == ' ' || ch == '\t')
/* nothing */;
if (ch != '#')
continue;
ch = yylex_getc();
if (strchr(",#{}:", ch) != NULL) {
yylex_ungetc(ch);
ch = '#';
} else {
while ((ch = yylex_getc()) != '\n' && ch != EOF)
/* nothing */;
}
continue;
} }
/* Whitespace or ; ends a token unless inside quotes. */ /* \ ~ and $ are expanded except in single quotes. */
if ((ch == ' ' || ch == '\t' || ch == ';') && state == NONE)
break;
/*
* \ ~ and $ are expanded except in single quotes.
*/
if (ch == '\\' && state != SINGLE_QUOTES) { if (ch == '\\' && state != SINGLE_QUOTES) {
if (!yylex_token_escape(&buf, &len)) if (!yylex_token_escape(&buf, &len))
goto error; goto error;
@@ -1512,17 +1542,10 @@ yylex_token(int ch)
goto error; goto error;
goto skip; goto skip;
} }
if (ch == '{' && state == NONE) {
if (!yylex_token_brace(&buf, &len))
goto error;
goto skip;
}
if (ch == '}' && state == NONE) if (ch == '}' && state == NONE)
goto error; /* unmatched (matched ones were handled) */ goto error; /* unmatched (matched ones were handled) */
/* /* ' and " starts or end quotes (and is consumed). */
* ' and " starts or end quotes (and is consumed).
*/
if (ch == '\'') { if (ch == '\'') {
if (state == NONE) { if (state == NONE) {
state = SINGLE_QUOTES; state = SINGLE_QUOTES;
@@ -1544,9 +1567,7 @@ yylex_token(int ch)
} }
} }
/* /* Otherwise add the character to the buffer. */
* Otherwise add the character to the buffer.
*/
yylex_append1(&buf, &len, ch); yylex_append1(&buf, &len, ch);
skip: skip:

View File

@@ -46,8 +46,9 @@ const struct cmd_entry cmd_paste_buffer_entry = {
static enum cmd_retval static enum cmd_retval
cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item) cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = cmd_get_args(self);
struct window_pane *wp = item->target.wp; struct cmd_find_state *target = cmdq_get_target(item);
struct window_pane *wp = target->wp;
struct paste_buffer *pb; struct paste_buffer *pb;
const char *sepstr, *bufname, *bufdata, *bufend, *line; const char *sepstr, *bufname, *bufdata, *bufend, *line;
size_t seplen, bufsize; size_t seplen, bufsize;

View File

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

View File

@@ -25,8 +25,69 @@
#include "tmux.h" #include "tmux.h"
/* Global command queue. */ /* Command queue flags. */
static struct cmdq_list global_queue = TAILQ_HEAD_INITIALIZER(global_queue); #define CMDQ_FIRED 0x1
#define CMDQ_WAITING 0x2
/* Command queue item type. */
enum cmdq_type {
CMDQ_COMMAND,
CMDQ_CALLBACK,
};
/* Command queue item. */
struct cmdq_item {
char *name;
struct cmdq_list *queue;
struct cmdq_item *next;
struct client *client;
struct client *target_client;
enum cmdq_type type;
u_int group;
u_int number;
time_t time;
int flags;
struct cmdq_state *state;
struct cmd_find_state source;
struct cmd_find_state target;
struct cmd_list *cmdlist;
struct cmd *cmd;
cmdq_cb cb;
void *data;
TAILQ_ENTRY(cmdq_item) entry;
};
TAILQ_HEAD(cmdq_item_list, cmdq_item);
/*
* Command queue state. This is the context for commands on the command queue.
* It holds information about how the commands were fired (the key and flags),
* any additional formats for the commands, and the current default target.
* Multiple commands can share the same state and a command may update the
* default target.
*/
struct cmdq_state {
int references;
int flags;
struct format_tree *formats;
struct key_event event;
struct cmd_find_state current;
};
/* Command queue. */
struct cmdq_list {
struct cmdq_item *item;
struct cmdq_item_list list;
};
/* Get command queue name. */ /* Get command queue name. */
static const char * static const char *
@@ -47,9 +108,179 @@ cmdq_name(struct client *c)
static struct cmdq_list * static struct cmdq_list *
cmdq_get(struct client *c) cmdq_get(struct client *c)
{ {
if (c == NULL) static struct cmdq_list *global_queue;
return (&global_queue);
return (&c->queue); if (c == NULL) {
if (global_queue == NULL)
global_queue = cmdq_new();
return (global_queue);
}
return (c->queue);
}
/* Create a queue. */
struct cmdq_list *
cmdq_new(void)
{
struct cmdq_list *queue;
queue = xcalloc (1, sizeof *queue);
TAILQ_INIT (&queue->list);
return (queue);
}
/* Free a queue. */
void
cmdq_free(struct cmdq_list *queue)
{
if (!TAILQ_EMPTY(&queue->list))
fatalx("queue not empty");
free(queue);
}
/* Get item name. */
const char *
cmdq_get_name(struct cmdq_item *item)
{
return (item->name);
}
/* Get item client. */
struct client *
cmdq_get_client(struct cmdq_item *item)
{
return (item->client);
}
/* Get item target client. */
struct client *
cmdq_get_target_client(struct cmdq_item *item)
{
return (item->target_client);
}
/* Get item state. */
struct cmdq_state *
cmdq_get_state(struct cmdq_item *item)
{
return (item->state);
}
/* Get item target. */
struct cmd_find_state *
cmdq_get_target(struct cmdq_item *item)
{
return (&item->target);
}
/* Get item source. */
struct cmd_find_state *
cmdq_get_source(struct cmdq_item *item)
{
return (&item->source);
}
/* Get state event. */
struct key_event *
cmdq_get_event(struct cmdq_item *item)
{
return (&item->state->event);
}
/* Get state current target. */
struct cmd_find_state *
cmdq_get_current(struct cmdq_item *item)
{
return (&item->state->current);
}
/* Get state flags. */
int
cmdq_get_flags(struct cmdq_item *item)
{
return (item->state->flags);
}
/* Create a new state. */
struct cmdq_state *
cmdq_new_state(struct cmd_find_state *current, struct key_event *event,
int flags)
{
struct cmdq_state *state;
state = xcalloc(1, sizeof *state);
state->references = 1;
state->flags = flags;
if (event != NULL)
memcpy(&state->event, event, sizeof state->event);
else
state->event.key = KEYC_NONE;
if (current != NULL && cmd_find_valid_state(current))
cmd_find_copy_state(&state->current, current);
else
cmd_find_clear_state(&state->current, 0);
return (state);
}
/* Add a reference to a state. */
struct cmdq_state *
cmdq_link_state(struct cmdq_state *state)
{
state->references++;
return (state);
}
/* Make a copy of a state. */
struct cmdq_state *
cmdq_copy_state(struct cmdq_state *state)
{
return (cmdq_new_state(&state->current, &state->event, state->flags));
}
/* Free a state. */
void
cmdq_free_state(struct cmdq_state *state)
{
if (--state->references != 0)
return;
if (state->formats != NULL)
format_free(state->formats);
free(state);
}
/* Add a format to command queue. */
void
cmdq_add_format(struct cmdq_state *state, const char *key, const char *fmt, ...)
{
va_list ap;
char *value;
va_start(ap, fmt);
xvasprintf(&value, fmt, ap);
va_end(ap);
if (state->formats == NULL)
state->formats = format_create(NULL, NULL, FORMAT_NONE, 0);
format_add(state->formats, key, "%s", value);
free(value);
}
/* Merge formats from item. */
void
cmdq_merge_formats(struct cmdq_item *item, struct format_tree *ft)
{
const struct cmd_entry *entry;
if (item->cmd != NULL) {
entry = cmd_get_entry(item->cmd);
format_add(ft, "command", "%s", entry->name);
}
if (item->state->formats != NULL)
format_merge(ft, item->state->formats);
} }
/* Append an item. */ /* Append an item. */
@@ -68,12 +299,12 @@ cmdq_append(struct client *c, struct cmdq_item *item)
item->client = c; item->client = c;
item->queue = queue; item->queue = queue;
TAILQ_INSERT_TAIL(queue, item, entry); TAILQ_INSERT_TAIL(&queue->list, item, entry);
log_debug("%s %s: %s", __func__, cmdq_name(c), item->name); log_debug("%s %s: %s", __func__, cmdq_name(c), item->name);
item = next; item = next;
} while (item != NULL); } while (item != NULL);
return (TAILQ_LAST(queue, cmdq_list)); return (TAILQ_LAST(&queue->list, cmdq_item_list));
} }
/* Insert an item. */ /* Insert an item. */
@@ -94,7 +325,7 @@ cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item)
item->client = c; item->client = c;
item->queue = queue; item->queue = queue;
TAILQ_INSERT_AFTER(queue, after, item, entry); TAILQ_INSERT_AFTER(&queue->list, after, item, entry);
log_debug("%s %s: %s after %s", __func__, cmdq_name(c), log_debug("%s %s: %s after %s", __func__, cmdq_name(c),
item->name, after->name); item->name, after->name);
@@ -107,17 +338,25 @@ cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item)
/* Insert a hook. */ /* Insert a hook. */
void void
cmdq_insert_hook(struct session *s, struct cmdq_item *item, cmdq_insert_hook(struct session *s, struct cmdq_item *item,
struct cmd_find_state *fs, const char *fmt, ...) struct cmd_find_state *current, const char *fmt, ...)
{ {
struct cmdq_state *state = item->state;
struct cmd *cmd = item->cmd;
struct args *args = cmd_get_args(cmd);
struct args_entry *entryp;
struct args_value *valuep;
struct options *oo; struct options *oo;
va_list ap; va_list ap;
char *name; char *name, tmp[32], flag, *arguments;
int i;
const char *value;
struct cmdq_item *new_item; struct cmdq_item *new_item;
struct cmdq_state *new_state;
struct options_entry *o; struct options_entry *o;
struct options_array_item *a; struct options_array_item *a;
struct cmd_list *cmdlist; struct cmd_list *cmdlist;
if (item->flags & CMDQ_NOHOOKS) if (item->state->flags & CMDQ_STATE_NOHOOKS)
return; return;
if (s == NULL) if (s == NULL)
oo = global_s_options; oo = global_s_options;
@@ -135,24 +374,58 @@ cmdq_insert_hook(struct session *s, struct cmdq_item *item,
} }
log_debug("running hook %s (parent %p)", name, item); log_debug("running hook %s (parent %p)", name, item);
/*
* The hooks get a new state because they should not update the current
* target or formats for any subsequent commands.
*/
new_state = cmdq_new_state(current, &state->event, CMDQ_STATE_NOHOOKS);
cmdq_add_format(new_state, "hook", "%s", name);
arguments = args_print(args);
cmdq_add_format(new_state, "hook_arguments", "%s", arguments);
free(arguments);
for (i = 0; i < args->argc; i++) {
xsnprintf(tmp, sizeof tmp, "hook_argument_%d", i);
cmdq_add_format(new_state, tmp, "%s", args->argv[i]);
}
flag = args_first(args, &entryp);
while (flag != 0) {
value = args_get(args, flag);
if (value == NULL) {
xsnprintf(tmp, sizeof tmp, "hook_flag_%c", flag);
cmdq_add_format(new_state, tmp, "1");
} else {
xsnprintf(tmp, sizeof tmp, "hook_flag_%c", flag);
cmdq_add_format(new_state, tmp, "%s", value);
}
i = 0;
value = args_first_value(args, flag, &valuep);
while (value != NULL) {
xsnprintf(tmp, sizeof tmp, "hook_flag_%c_%d", flag, i);
cmdq_add_format(new_state, tmp, "%s", value);
i++;
value = args_next_value(&valuep);
}
flag = args_next(&entryp);
}
a = options_array_first(o); a = options_array_first(o);
while (a != NULL) { while (a != NULL) {
cmdlist = options_array_item_value(a)->cmdlist; cmdlist = options_array_item_value(a)->cmdlist;
if (cmdlist == NULL) { if (cmdlist != NULL) {
a = options_array_next(a); new_item = cmdq_get_command(cmdlist, new_state);
continue; if (item != NULL)
item = cmdq_insert_after(item, new_item);
else
item = cmdq_append(NULL, new_item);
} }
new_item = cmdq_get_command(cmdlist, fs, NULL, CMDQ_NOHOOKS);
cmdq_format(new_item, "hook", "%s", name);
if (item != NULL)
item = cmdq_insert_after(item, new_item);
else
item = cmdq_append(NULL, new_item);
a = options_array_next(a); a = options_array_next(a);
} }
cmdq_free_state(new_state);
free(name); free(name);
} }
@@ -167,19 +440,13 @@ cmdq_continue(struct cmdq_item *item)
static void static void
cmdq_remove(struct cmdq_item *item) cmdq_remove(struct cmdq_item *item)
{ {
if (item->shared != NULL && --item->shared->references == 0) {
if (item->shared->formats != NULL)
format_free(item->shared->formats);
free(item->shared);
}
if (item->client != NULL) if (item->client != NULL)
server_client_unref(item->client); server_client_unref(item->client);
if (item->cmdlist != NULL) if (item->cmdlist != NULL)
cmd_list_free(item->cmdlist); cmd_list_free(item->cmdlist);
cmdq_free_state(item->state);
TAILQ_REMOVE(item->queue, item, entry); TAILQ_REMOVE(&item->queue->list, item, entry);
free(item->name); free(item->name);
free(item); free(item);
@@ -204,48 +471,46 @@ cmdq_remove_group(struct cmdq_item *item)
/* Get a command for the command queue. */ /* Get a command for the command queue. */
struct cmdq_item * struct cmdq_item *
cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current, cmdq_get_command(struct cmd_list *cmdlist, struct cmdq_state *state)
struct mouse_event *m, int flags)
{ {
struct cmdq_item *item, *first = NULL, *last = NULL; struct cmdq_item *item, *first = NULL, *last = NULL;
struct cmd *cmd; struct cmd *cmd;
struct cmdq_shared *shared = NULL; const struct cmd_entry *entry;
u_int group = 0; int created = 0;
TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { if (state == NULL) {
if (cmd->group != group) { state = cmdq_new_state(NULL, NULL, 0);
shared = xcalloc(1, sizeof *shared); created = 1;
if (current != NULL) }
cmd_find_copy_state(&shared->current, current);
else cmd = cmd_list_first(cmdlist);
cmd_find_clear_state(&shared->current, 0); while (cmd != NULL) {
if (m != NULL) entry = cmd_get_entry(cmd);
memcpy(&shared->mouse, m, sizeof shared->mouse);
group = cmd->group;
}
item = xcalloc(1, sizeof *item); item = xcalloc(1, sizeof *item);
xasprintf(&item->name, "[%s/%p]", cmd->entry->name, item); xasprintf(&item->name, "[%s/%p]", entry->name, item);
item->type = CMDQ_COMMAND; item->type = CMDQ_COMMAND;
item->group = cmd->group; item->group = cmd_get_group(cmd);
item->flags = flags; item->state = cmdq_link_state(state);
item->shared = shared;
item->cmdlist = cmdlist; item->cmdlist = cmdlist;
item->cmd = cmd; item->cmd = cmd;
log_debug("%s: %s group %u", __func__, item->name, item->group);
shared->references++;
cmdlist->references++; cmdlist->references++;
log_debug("%s: %s group %u", __func__, item->name, item->group);
if (first == NULL) if (first == NULL)
first = item; first = item;
if (last != NULL) if (last != NULL)
last->next = item; last->next = item;
last = item; last = item;
cmd = cmd_list_next(cmd);
} }
if (created)
cmdq_free_state(state);
return (first); return (first);
} }
@@ -257,11 +522,11 @@ cmdq_find_flag(struct cmdq_item *item, struct cmd_find_state *fs,
const char *value; const char *value;
if (flag->flag == 0) { if (flag->flag == 0) {
cmd_find_clear_state(fs, 0); cmd_find_from_client(fs, item->target_client, 0);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
value = args_get(item->cmd->args, flag->flag); value = args_get(cmd_get_args(item->cmd), flag->flag);
if (cmd_find_target(fs, item, value, flag->type, flag->flags) != 0) { if (cmd_find_target(fs, item, value, flag->type, flag->flags) != 0) {
cmd_find_clear_state(fs, 0); cmd_find_clear_state(fs, 0);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
@@ -269,31 +534,75 @@ cmdq_find_flag(struct cmdq_item *item, struct cmd_find_state *fs,
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
/* Add message with command. */
static void
cmdq_add_message(struct cmdq_item *item)
{
struct client *c = item->client;
struct cmdq_state *state = item->state;
const char *name, *key;
char *tmp;
tmp = cmd_print(item->cmd);
if (c != NULL) {
name = c->name;
if (c->session != NULL && state->event.key != KEYC_NONE) {
key = key_string_lookup_key(state->event.key, 0);
server_add_message("%s key %s: %s", name, key, tmp);
} else
server_add_message("%s command: %s", name, tmp);
} else
server_add_message("command: %s", tmp);
free(tmp);
}
/* Fire command on command queue. */ /* Fire command on command queue. */
static enum cmd_retval static enum cmd_retval
cmdq_fire_command(struct cmdq_item *item) cmdq_fire_command(struct cmdq_item *item)
{ {
struct client *c = item->client; const char *name = cmdq_name(item->client);
const char *name = cmdq_name(c); struct cmdq_state *state = item->state;
struct cmdq_shared *shared = item->shared;
struct cmd *cmd = item->cmd; struct cmd *cmd = item->cmd;
const struct cmd_entry *entry = cmd->entry; struct args *args = cmd_get_args(cmd);
const struct cmd_entry *entry = cmd_get_entry(cmd);
struct client *tc, *saved = item->client;
enum cmd_retval retval; enum cmd_retval retval;
struct cmd_find_state *fsp, fs; struct cmd_find_state *fsp, fs;
int flags; int flags, quiet = 0;
char *tmp; char *tmp;
if (cfg_finished)
cmdq_add_message(item);
if (log_get_level() > 1) { if (log_get_level() > 1) {
tmp = cmd_print(cmd); tmp = cmd_print(cmd);
log_debug("%s %s: (%u) %s", __func__, name, item->group, tmp); log_debug("%s %s: (%u) %s", __func__, name, item->group, tmp);
free(tmp); free(tmp);
} }
flags = !!(shared->flags & CMDQ_SHARED_CONTROL); flags = !!(state->flags & CMDQ_STATE_CONTROL);
cmdq_guard(item, "begin", flags); cmdq_guard(item, "begin", flags);
if (item->client == NULL) if (item->client == NULL)
item->client = cmd_find_client(item, NULL, 1); item->client = cmd_find_client(item, NULL, 1);
if (entry->flags & CMD_CLIENT_CANFAIL)
quiet = 1;
if (entry->flags & CMD_CLIENT_CFLAG) {
tc = cmd_find_client(item, args_get(args, 'c'), quiet);
if (tc == NULL && !quiet) {
retval = CMD_RETURN_ERROR;
goto out;
}
} else if (entry->flags & CMD_CLIENT_TFLAG) {
tc = cmd_find_client(item, args_get(args, 't'), quiet);
if (tc == NULL && !quiet) {
retval = CMD_RETURN_ERROR;
goto out;
}
} else
tc = cmd_find_client(item, NULL, 1);
item->target_client = tc;
retval = cmdq_find_flag(item, &item->source, &entry->source); retval = cmdq_find_flag(item, &item->source, &entry->source);
if (retval == CMD_RETURN_ERROR) if (retval == CMD_RETURN_ERROR)
goto out; goto out;
@@ -308,8 +617,8 @@ cmdq_fire_command(struct cmdq_item *item)
if (entry->flags & CMD_AFTERHOOK) { if (entry->flags & CMD_AFTERHOOK) {
if (cmd_find_valid_state(&item->target)) if (cmd_find_valid_state(&item->target))
fsp = &item->target; fsp = &item->target;
else if (cmd_find_valid_state(&item->shared->current)) else if (cmd_find_valid_state(&item->state->current))
fsp = &item->shared->current; fsp = &item->state->current;
else if (cmd_find_from_client(&fs, item->client, 0) == 0) else if (cmd_find_from_client(&fs, item->client, 0) == 0)
fsp = &fs; fsp = &fs;
else else
@@ -318,7 +627,7 @@ cmdq_fire_command(struct cmdq_item *item)
} }
out: out:
item->client = c; item->client = saved;
if (retval == CMD_RETURN_ERROR) if (retval == CMD_RETURN_ERROR)
cmdq_guard(item, "error", flags); cmdq_guard(item, "error", flags);
else else
@@ -337,7 +646,7 @@ cmdq_get_callback1(const char *name, cmdq_cb cb, void *data)
item->type = CMDQ_CALLBACK; item->type = CMDQ_CALLBACK;
item->group = 0; item->group = 0;
item->flags = 0; item->state = cmdq_new_state(NULL, NULL, 0);
item->cb = cb; item->cb = cb;
item->data = data; item->data = data;
@@ -371,25 +680,6 @@ cmdq_fire_callback(struct cmdq_item *item)
return (item->cb(item, item->data)); return (item->cb(item, item->data));
} }
/* Add a format to command queue. */
void
cmdq_format(struct cmdq_item *item, const char *key, const char *fmt, ...)
{
struct cmdq_shared *shared = item->shared;
va_list ap;
char *value;
va_start(ap, fmt);
xvasprintf(&value, fmt, ap);
va_end(ap);
if (shared->formats == NULL)
shared->formats = format_create(NULL, NULL, FORMAT_NONE, 0);
format_add(shared->formats, key, "%s", value);
free(value);
}
/* Process next item on command queue. */ /* Process next item on command queue. */
u_int u_int
cmdq_next(struct client *c) cmdq_next(struct client *c)
@@ -401,18 +691,18 @@ cmdq_next(struct client *c)
u_int items = 0; u_int items = 0;
static u_int number; static u_int number;
if (TAILQ_EMPTY(queue)) { if (TAILQ_EMPTY(&queue->list)) {
log_debug("%s %s: empty", __func__, name); log_debug("%s %s: empty", __func__, name);
return (0); return (0);
} }
if (TAILQ_FIRST(queue)->flags & CMDQ_WAITING) { if (TAILQ_FIRST(&queue->list)->flags & CMDQ_WAITING) {
log_debug("%s %s: waiting", __func__, name); log_debug("%s %s: waiting", __func__, name);
return (0); return (0);
} }
log_debug("%s %s: enter", __func__, name); log_debug("%s %s: enter", __func__, name);
for (;;) { for (;;) {
item = TAILQ_FIRST(queue); item = queue->item = TAILQ_FIRST(&queue->list);
if (item == NULL) if (item == NULL)
break; break;
log_debug("%s %s: %s (%d), flags %x", __func__, name, log_debug("%s %s: %s (%d), flags %x", __func__, name,
@@ -462,6 +752,7 @@ cmdq_next(struct client *c)
} }
cmdq_remove(item); cmdq_remove(item);
} }
queue->item = NULL;
log_debug("%s %s: exit (empty)", __func__, name); log_debug("%s %s: exit (empty)", __func__, name);
return (items); return (items);
@@ -471,6 +762,19 @@ waiting:
return (items); return (items);
} }
/* Get running item if any. */
struct cmdq_item *
cmdq_running(struct client *c)
{
struct cmdq_list *queue = cmdq_get(c);
if (queue->item == NULL)
return (NULL);
if (queue->item->flags & CMDQ_WAITING)
return (NULL);
return (queue->item);
}
/* Print a guard line. */ /* Print a guard line. */
void void
cmdq_guard(struct cmdq_item *item, const char *guard, int flags) cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
@@ -480,7 +784,7 @@ cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
u_int number = item->number; u_int number = item->number;
if (c != NULL && (c->flags & CLIENT_CONTROL)) if (c != NULL && (c->flags & CLIENT_CONTROL))
file_print(c, "%%%s %ld %u %d\n", guard, t, number, flags); control_write(c, "%%%s %ld %u %d", guard, t, number, flags);
} }
/* Show message from command. */ /* Show message from command. */
@@ -507,12 +811,17 @@ cmdq_print(struct cmdq_item *item, const char *fmt, ...)
msg = utf8_sanitize(tmp); msg = utf8_sanitize(tmp);
free(tmp); free(tmp);
} }
file_print(c, "%s\n", msg); if (c->flags & CLIENT_CONTROL)
control_write(c, "%s", msg);
else
file_print(c, "%s\n", msg);
} else { } else {
wp = c->session->curw->window->active; wp = server_client_get_pane(c);
wme = TAILQ_FIRST(&wp->modes); wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode != &window_view_mode) if (wme == NULL || wme->mode != &window_view_mode) {
window_pane_set_mode(wp, &window_view_mode, NULL, NULL); window_pane_set_mode(wp, NULL, &window_view_mode, NULL,
NULL);
}
window_copy_add(wp, "%s", msg); window_copy_add(wp, "%s", msg);
} }
@@ -526,8 +835,9 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...)
struct client *c = item->client; struct client *c = item->client;
struct cmd *cmd = item->cmd; struct cmd *cmd = item->cmd;
va_list ap; va_list ap;
char *msg; char *msg, *tmp;
char *tmp; const char *file;
u_int line;
va_start(ap, fmt); va_start(ap, fmt);
xvasprintf(&msg, fmt, ap); xvasprintf(&msg, fmt, ap);
@@ -535,22 +845,24 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...)
log_debug("%s: %s", __func__, msg); log_debug("%s: %s", __func__, msg);
if (c == NULL) if (c == NULL) {
cfg_add_cause("%s:%u: %s", cmd->file, cmd->line, msg); cmd_get_source(cmd, &file, &line);
else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) { cfg_add_cause("%s:%u: %s", file, line, msg);
} else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
server_add_message("%s message: %s", c->name, msg);
if (~c->flags & CLIENT_UTF8) { if (~c->flags & CLIENT_UTF8) {
tmp = msg; tmp = msg;
msg = utf8_sanitize(tmp); msg = utf8_sanitize(tmp);
free(tmp); free(tmp);
} }
if (c->flags & CLIENT_CONTROL) if (c->flags & CLIENT_CONTROL)
file_print(c, "%s\n", msg); control_write(c, "%s", msg);
else else
file_error(c, "%s\n", msg); file_error(c, "%s\n", msg);
c->retval = 1; c->retval = 1;
} else { } else {
*msg = toupper((u_char) *msg); *msg = toupper((u_char) *msg);
status_message_set(c, "%s", msg); status_message_set(c, -1, 1, 0, "%s", msg);
} }
free(msg); free(msg);

View File

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

View File

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

View File

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

View File

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

View File

@@ -46,10 +46,11 @@ const struct cmd_entry cmd_resize_window_entry = {
static enum cmd_retval static enum cmd_retval
cmd_resize_window_exec(struct cmd *self, struct cmdq_item *item) cmd_resize_window_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = cmd_get_args(self);
struct winlink *wl = item->target.wl; struct cmd_find_state *target = cmdq_get_target(item);
struct winlink *wl = target->wl;
struct window *w = wl->window; struct window *w = wl->window;
struct session *s = item->target.s; struct session *s = target->s;
const char *errstr; const char *errstr;
char *cause; char *cause;
u_int adjust, sx, sy; u_int adjust, sx, sy;

View File

@@ -47,11 +47,12 @@ const struct cmd_entry cmd_respawn_pane_entry = {
static enum cmd_retval static enum cmd_retval
cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item) cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct spawn_context sc; struct spawn_context sc;
struct session *s = item->target.s; struct session *s = target->s;
struct winlink *wl = item->target.wl; struct winlink *wl = target->wl;
struct window_pane *wp = item->target.wp; struct window_pane *wp = target->wp;
char *cause = NULL; char *cause = NULL;
const char *add; const char *add;
struct args_value *value; struct args_value *value;
@@ -71,7 +72,7 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item)
add = args_first_value(args, 'e', &value); add = args_first_value(args, 'e', &value);
while (add != NULL) { while (add != NULL) {
environ_put(sc.environ, add); environ_put(sc.environ, add, 0);
add = args_next_value(&value); add = args_next_value(&value);
} }
@@ -89,6 +90,7 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item)
} }
wp->flags |= PANE_REDRAW; wp->flags |= PANE_REDRAW;
server_redraw_window_borders(wp->window);
server_status_window(wp->window); server_status_window(wp->window);
environ_free(sc.environ); environ_free(sc.environ);

View File

@@ -47,10 +47,12 @@ const struct cmd_entry cmd_respawn_window_entry = {
static enum cmd_retval static enum cmd_retval
cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item) cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct spawn_context sc; struct spawn_context sc;
struct session *s = item->target.s; struct client *tc = cmdq_get_target_client(item);
struct winlink *wl = item->target.wl; struct session *s = target->s;
struct winlink *wl = target->wl;
char *cause = NULL; char *cause = NULL;
const char *add; const char *add;
struct args_value *value; struct args_value *value;
@@ -59,7 +61,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item)
sc.item = item; sc.item = item;
sc.s = s; sc.s = s;
sc.wl = wl; sc.wl = wl;
sc.c = cmd_find_client(item, NULL, 1); sc.tc = tc;
sc.name = NULL; sc.name = NULL;
sc.argc = args->argc; sc.argc = args->argc;
@@ -68,7 +70,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item)
add = args_first_value(args, 'e', &value); add = args_first_value(args, 'e', &value);
while (add != NULL) { while (add != NULL) {
environ_put(sc.environ, add); environ_put(sc.environ, add, 0);
add = args_next_value(&value); add = args_next_value(&value);
} }

View File

@@ -43,16 +43,18 @@ const struct cmd_entry cmd_rotate_window_entry = {
static enum cmd_retval static enum cmd_retval
cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item) cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct cmd_find_state *current = &item->shared->current; struct args *args = cmd_get_args(self);
struct winlink *wl = item->target.wl; struct cmd_find_state *current = cmdq_get_current(item);
struct cmd_find_state *target = cmdq_get_target(item);
struct winlink *wl = target->wl;
struct window *w = wl->window; struct window *w = wl->window;
struct window_pane *wp, *wp2; struct window_pane *wp, *wp2;
struct layout_cell *lc; struct layout_cell *lc;
u_int sx, sy, xoff, yoff; u_int sx, sy, xoff, yoff;
window_push_zoom(w, args_has(self->args, 'Z')); window_push_zoom(w, 0, args_has(args, 'Z'));
if (args_has(self->args, 'D')) { if (args_has(args, 'D')) {
wp = TAILQ_LAST(&w->panes, window_panes); wp = TAILQ_LAST(&w->panes, window_panes);
TAILQ_REMOVE(&w->panes, wp, entry); TAILQ_REMOVE(&w->panes, wp, entry);
TAILQ_INSERT_HEAD(&w->panes, wp, entry); TAILQ_INSERT_HEAD(&w->panes, wp, entry);

View File

@@ -20,6 +20,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <ctype.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -31,6 +32,7 @@
static enum cmd_retval cmd_run_shell_exec(struct cmd *, struct cmdq_item *); static enum cmd_retval cmd_run_shell_exec(struct cmd *, struct cmdq_item *);
static void cmd_run_shell_timer(int, short, void *);
static void cmd_run_shell_callback(struct job *); static void cmd_run_shell_callback(struct job *);
static void cmd_run_shell_free(void *); static void cmd_run_shell_free(void *);
static void cmd_run_shell_print(struct job *, const char *); static void cmd_run_shell_print(struct job *, const char *);
@@ -39,8 +41,8 @@ const struct cmd_entry cmd_run_shell_entry = {
.name = "run-shell", .name = "run-shell",
.alias = "run", .alias = "run",
.args = { "bt:", 1, 1 }, .args = { "bd:Ct:", 0, 1 },
.usage = "[-b] " CMD_TARGET_PANE_USAGE " shell-command", .usage = "[-bC] [-d delay] " CMD_TARGET_PANE_USAGE " [shell-command]",
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
@@ -49,9 +51,16 @@ const struct cmd_entry cmd_run_shell_entry = {
}; };
struct cmd_run_shell_data { struct cmd_run_shell_data {
struct client *client;
char *cmd; char *cmd;
int shell;
char *cwd;
struct cmdq_item *item; struct cmdq_item *item;
struct session *s;
int wp_id; int wp_id;
struct event timer;
int flags;
struct cmd_parse_input pi;
}; };
static void static void
@@ -78,48 +87,130 @@ cmd_run_shell_print(struct job *job, const char *msg)
wme = TAILQ_FIRST(&wp->modes); wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode != &window_view_mode) if (wme == NULL || wme->mode != &window_view_mode)
window_pane_set_mode(wp, &window_view_mode, NULL, NULL); window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL);
window_copy_add(wp, "%s", msg); window_copy_add(wp, "%s", msg);
} }
static enum cmd_retval static enum cmd_retval
cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct cmd_run_shell_data *cdata; struct cmd_run_shell_data *cdata;
struct client *c = cmd_find_client(item, NULL, 1); struct client *tc = cmdq_get_target_client(item);
struct session *s = item->target.s; struct session *s = target->s;
struct winlink *wl = item->target.wl; struct window_pane *wp = target->wp;
struct window_pane *wp = item->target.wp; const char *delay;
double d;
struct timeval tv;
char *end;
int wait = !args_has(args, 'b');
if ((delay = args_get(args, 'd')) != NULL) {
d = strtod(delay, &end);
if (*end != '\0') {
cmdq_error(item, "invalid delay time: %s", delay);
return (CMD_RETURN_ERROR);
}
} else if (args->argc == 0)
return (CMD_RETURN_NORMAL);
cdata = xcalloc(1, sizeof *cdata); cdata = xcalloc(1, sizeof *cdata);
cdata->cmd = format_single(item, args->argv[0], c, s, wl, wp); if (args->argc != 0)
cdata->cmd = format_single_from_target(item, args->argv[0]);
cdata->shell = !args_has(args, 'C');
if (!cdata->shell) {
memset(&cdata->pi, 0, sizeof cdata->pi);
cmd_get_source(self, &cdata->pi.file, &cdata->pi.line);
if (wait)
cdata->pi.item = item;
cdata->pi.c = tc;
cmd_find_copy_state(&cdata->pi.fs, target);
}
if (args_has(args, 't') && wp != NULL) if (args_has(args, 't') && wp != NULL)
cdata->wp_id = wp->id; cdata->wp_id = wp->id;
else else
cdata->wp_id = -1; cdata->wp_id = -1;
if (!args_has(args, 'b')) if (wait) {
cdata->client = cmdq_get_client(item);
cdata->item = item; cdata->item = item;
} else {
if (job_run(cdata->cmd, s, server_client_get_cwd(item->client, s), NULL, cdata->client = tc;
cmd_run_shell_callback, cmd_run_shell_free, cdata, 0) == NULL) { cdata->flags |= JOB_NOWAIT;
cmdq_error(item, "failed to run command: %s", cdata->cmd);
free(cdata);
return (CMD_RETURN_ERROR);
} }
if (cdata->client != NULL)
cdata->client->references++;
if (args_has(args, 'b')) cdata->cwd = xstrdup(server_client_get_cwd(cmdq_get_client(item), s));
cdata->s = s;
if (s != NULL)
session_add_ref(s, __func__);
evtimer_set(&cdata->timer, cmd_run_shell_timer, cdata);
if (delay != NULL) {
timerclear(&tv);
tv.tv_sec = (time_t)d;
tv.tv_usec = (d - (double)tv.tv_sec) * 1000000U;
evtimer_add(&cdata->timer, &tv);
} else
event_active(&cdata->timer, EV_TIMEOUT, 1);
if (!wait)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
return (CMD_RETURN_WAIT); return (CMD_RETURN_WAIT);
} }
static void
cmd_run_shell_timer(__unused int fd, __unused short events, void* arg)
{
struct cmd_run_shell_data *cdata = arg;
struct client *c = cdata->client;
const char *cmd = cdata->cmd;
char *error;
struct cmdq_item *item = cdata->item;
enum cmd_parse_status status;
if (cmd != NULL && cdata->shell) {
if (job_run(cmd, 0, NULL, cdata->s, cdata->cwd, NULL,
cmd_run_shell_callback, cmd_run_shell_free, cdata,
cdata->flags, -1, -1) == NULL)
cmd_run_shell_free(cdata);
return;
}
if (cmd != NULL) {
if (item != NULL) {
status = cmd_parse_and_insert(cmd, &cdata->pi, item,
cmdq_get_state(item), &error);
} else {
status = cmd_parse_and_append(cmd, &cdata->pi, c, NULL,
&error);
}
if (status == CMD_PARSE_ERROR) {
if (cdata->item == NULL) {
*error = toupper((u_char)*error);
status_message_set(c, -1, 1, 0, "%s", error);
} else
cmdq_error(cdata->item, "%s", error);
free(error);
}
}
if (cdata->item != NULL)
cmdq_continue(cdata->item);
cmd_run_shell_free(cdata);
}
static void static void
cmd_run_shell_callback(struct job *job) cmd_run_shell_callback(struct job *job)
{ {
struct cmd_run_shell_data *cdata = job_get_data(job); struct cmd_run_shell_data *cdata = job_get_data(job);
struct bufferevent *event = job_get_event(job); struct bufferevent *event = job_get_event(job);
struct cmdq_item *item = cdata->item;
char *cmd = cdata->cmd, *msg = NULL, *line; char *cmd = cdata->cmd, *msg = NULL, *line;
size_t size; size_t size;
int retcode, status; int retcode, status;
@@ -149,13 +240,19 @@ cmd_run_shell_callback(struct job *job)
} else if (WIFSIGNALED(status)) { } else if (WIFSIGNALED(status)) {
retcode = WTERMSIG(status); retcode = WTERMSIG(status);
xasprintf(&msg, "'%s' terminated by signal %d", cmd, retcode); xasprintf(&msg, "'%s' terminated by signal %d", cmd, retcode);
} retcode += 128;
} else
retcode = 0;
if (msg != NULL) if (msg != NULL)
cmd_run_shell_print(job, msg); cmd_run_shell_print(job, msg);
free(msg); free(msg);
if (cdata->item != NULL) if (item != NULL) {
cmdq_continue(cdata->item); if (cmdq_get_client(item) != NULL &&
cmdq_get_client(item)->session == NULL)
cmdq_get_client(item)->retval = retcode;
cmdq_continue(item);
}
} }
static void static void
@@ -163,6 +260,12 @@ cmd_run_shell_free(void *data)
{ {
struct cmd_run_shell_data *cdata = data; struct cmd_run_shell_data *cdata = data;
evtimer_del(&cdata->timer);
if (cdata->s != NULL)
session_remove_ref(cdata->s, __func__);
if (cdata->client != NULL)
server_client_unref(cdata->client);
free(cdata->cwd);
free(cdata->cmd); free(cdata->cmd);
free(cdata); free(cdata);
} }

View File

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

View File

@@ -71,20 +71,21 @@ const struct cmd_entry cmd_previous_layout_entry = {
static enum cmd_retval static enum cmd_retval
cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item) cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = cmd_get_args(self);
struct winlink *wl = item->target.wl; struct cmd_find_state *target = cmdq_get_target(item);
struct winlink *wl = target->wl;
struct window *w = wl->window; struct window *w = wl->window;
struct window_pane *wp = item->target.wp; struct window_pane *wp = target->wp;
const char *layoutname; const char *layoutname;
char *oldlayout; char *oldlayout;
int next, previous, layout; int next, previous, layout;
server_unzoom_window(w); server_unzoom_window(w);
next = self->entry == &cmd_next_layout_entry; next = (cmd_get_entry(self) == &cmd_next_layout_entry);
if (args_has(args, 'n')) if (args_has(args, 'n'))
next = 1; next = 1;
previous = self->entry == &cmd_previous_layout_entry; previous = (cmd_get_entry(self) == &cmd_previous_layout_entry);
if (args_has(args, 'p')) if (args_has(args, 'p'))
previous = 1; previous = 1;

View File

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

View File

@@ -84,23 +84,26 @@ const struct cmd_entry cmd_last_window_entry = {
static enum cmd_retval static enum cmd_retval
cmd_select_window_exec(struct cmd *self, struct cmdq_item *item) cmd_select_window_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct cmd_find_state *current = &item->shared->current; struct args *args = cmd_get_args(self);
struct winlink *wl = item->target.wl; struct client *c = cmdq_get_client(item);
struct session *s = item->target.s; struct cmd_find_state *current = cmdq_get_current(item);
struct cmd_find_state *target = cmdq_get_target(item);
struct winlink *wl = target->wl;
struct session *s = target->s;
int next, previous, last, activity; int next, previous, last, activity;
next = self->entry == &cmd_next_window_entry; next = (cmd_get_entry(self) == &cmd_next_window_entry);
if (args_has(self->args, 'n')) if (args_has(args, 'n'))
next = 1; next = 1;
previous = self->entry == &cmd_previous_window_entry; previous = (cmd_get_entry(self) == &cmd_previous_window_entry);
if (args_has(self->args, 'p')) if (args_has(args, 'p'))
previous = 1; previous = 1;
last = self->entry == &cmd_last_window_entry; last = (cmd_get_entry(self) == &cmd_last_window_entry);
if (args_has(self->args, 'l')) if (args_has(args, 'l'))
last = 1; last = 1;
if (next || previous || last) { if (next || previous || last) {
activity = args_has(self->args, 'a'); activity = args_has(args, 'a');
if (next) { if (next) {
if (session_next(s, activity) != 0) { if (session_next(s, activity) != 0) {
cmdq_error(item, "no next window"); cmdq_error(item, "no next window");
@@ -125,7 +128,7 @@ cmd_select_window_exec(struct cmd *self, struct cmdq_item *item)
* If -T and select-window is invoked on same window as * If -T and select-window is invoked on same window as
* current, switch to previous window. * current, switch to previous window.
*/ */
if (args_has(self->args, 'T') && wl == s->curw) { if (args_has(args, 'T') && wl == s->curw) {
if (session_last(s) != 0) { if (session_last(s) != 0) {
cmdq_error(item, "no last window"); cmdq_error(item, "no last window");
return (-1); return (-1);
@@ -139,6 +142,8 @@ cmd_select_window_exec(struct cmd *self, struct cmdq_item *item)
} }
cmdq_insert_hook(s, item, current, "after-select-window"); cmdq_insert_hook(s, item, current, "after-select-window");
} }
if (c != NULL && c->session != NULL)
s->curw->window->latest = c;
recalculate_sizes(); recalculate_sizes();
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);

View File

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

View File

@@ -33,10 +33,11 @@ const struct cmd_entry cmd_set_buffer_entry = {
.name = "set-buffer", .name = "set-buffer",
.alias = "setb", .alias = "setb",
.args = { "ab:n:", 0, 1 }, .args = { "ab:t:n:w", 0, 1 },
.usage = "[-a] " CMD_BUFFER_USAGE " [-n new-buffer-name] data", .usage = "[-aw] " CMD_BUFFER_USAGE " [-n new-buffer-name] "
CMD_TARGET_CLIENT_USAGE " data",
.flags = CMD_AFTERHOOK, .flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG|CMD_CLIENT_CANFAIL,
.exec = cmd_set_buffer_exec .exec = cmd_set_buffer_exec
}; };
@@ -54,7 +55,8 @@ const struct cmd_entry cmd_delete_buffer_entry = {
static enum cmd_retval static enum cmd_retval
cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item) cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = cmd_get_args(self);
struct client *tc = cmdq_get_target_client(item);
struct paste_buffer *pb; struct paste_buffer *pb;
char *bufdata, *cause; char *bufdata, *cause;
const char *bufname, *olddata; const char *bufname, *olddata;
@@ -66,7 +68,7 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
else else
pb = paste_get_name(bufname); pb = paste_get_name(bufname);
if (self->entry == &cmd_delete_buffer_entry) { if (cmd_get_entry(self) == &cmd_delete_buffer_entry) {
if (pb == NULL) if (pb == NULL)
pb = paste_get_top(&bufname); pb = paste_get_top(&bufname);
if (pb == NULL) { if (pb == NULL) {
@@ -118,6 +120,8 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
free(cause); free(cause);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
if (args_has(args, 'w') && tc != NULL)
tty_set_selection(&tc->tty, bufdata, bufsize);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@@ -34,8 +34,8 @@ const struct cmd_entry cmd_set_environment_entry = {
.name = "set-environment", .name = "set-environment",
.alias = "setenv", .alias = "setenv",
.args = { "grt:u", 1, 2 }, .args = { "Fhgrt:u", 1, 2 },
.usage = "[-gru] " CMD_TARGET_SESSION_USAGE " name [value]", .usage = "[-Fhgru] " CMD_TARGET_SESSION_USAGE " name [value]",
.target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL }, .target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL },
@@ -46,9 +46,12 @@ const struct cmd_entry cmd_set_environment_entry = {
static enum cmd_retval static enum cmd_retval
cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item) cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = cmd_get_args(self);
struct environ *env; struct cmd_find_state *target = cmdq_get_target(item);
const char *name, *value, *target; struct environ *env;
const char *name, *value, *tflag;
char *expand = NULL;
enum cmd_retval retval = CMD_RETURN_NORMAL;
name = args->argv[0]; name = args->argv[0];
if (*name == '\0') { if (*name == '\0') {
@@ -62,42 +65,54 @@ cmd_set_environment_exec(struct cmd *self, struct cmdq_item *item)
if (args->argc < 2) if (args->argc < 2)
value = NULL; value = NULL;
else if (args_has(args, 'F'))
value = expand = format_single_from_target(item, args->argv[1]);
else else
value = args->argv[1]; value = args->argv[1];
if (args_has(self->args, 'g')) if (args_has(args, 'g'))
env = global_environ; env = global_environ;
else { else {
if (item->target.s == NULL) { if (target->s == NULL) {
target = args_get(args, 't'); tflag = args_get(args, 't');
if (target != NULL) if (tflag != NULL)
cmdq_error(item, "no such session: %s", target); cmdq_error(item, "no such session: %s", tflag);
else else
cmdq_error(item, "no current session"); cmdq_error(item, "no current session");
return (CMD_RETURN_ERROR); retval = CMD_RETURN_ERROR;
goto out;
} }
env = item->target.s->environ; env = target->s->environ;
} }
if (args_has(self->args, 'u')) { if (args_has(args, 'u')) {
if (value != NULL) { if (value != NULL) {
cmdq_error(item, "can't specify a value with -u"); cmdq_error(item, "can't specify a value with -u");
return (CMD_RETURN_ERROR); retval = CMD_RETURN_ERROR;
goto out;
} }
environ_unset(env, name); environ_unset(env, name);
} else if (args_has(self->args, 'r')) { } else if (args_has(args, 'r')) {
if (value != NULL) { if (value != NULL) {
cmdq_error(item, "can't specify a value with -r"); cmdq_error(item, "can't specify a value with -r");
return (CMD_RETURN_ERROR); retval = CMD_RETURN_ERROR;
goto out;
} }
environ_clear(env, name); environ_clear(env, name);
} else { } else {
if (value == NULL) { if (value == NULL) {
cmdq_error(item, "no value specified"); cmdq_error(item, "no value specified");
return (CMD_RETURN_ERROR); retval = CMD_RETURN_ERROR;
goto out;
} }
environ_set(env, name, "%s", value);
if (args_has(args, 'h'))
environ_set(env, name, ENVIRON_HIDDEN, "%s", value);
else
environ_set(env, name, 0, "%s", value);
} }
return (CMD_RETURN_NORMAL); out:
free(expand);
return (retval);
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -35,8 +35,8 @@ const struct cmd_entry cmd_source_file_entry = {
.name = "source-file", .name = "source-file",
.alias = "source", .alias = "source",
.args = { "nqv", 1, -1 }, .args = { "Fnqv", 1, -1 },
.usage = "[-nqv] path ...", .usage = "[-Fnqv] path ...",
.flags = 0, .flags = 0,
.exec = cmd_source_file_exec .exec = cmd_source_file_exec
@@ -122,11 +122,11 @@ cmd_source_file_add(struct cmd_source_file_data *cdata, const char *path)
static enum cmd_retval static enum cmd_retval
cmd_source_file_exec(struct cmd *self, struct cmdq_item *item) cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = cmd_get_args(self);
struct cmd_source_file_data *cdata; struct cmd_source_file_data *cdata;
struct client *c = item->client; struct client *c = cmdq_get_client(item);
enum cmd_retval retval = CMD_RETURN_NORMAL; enum cmd_retval retval = CMD_RETURN_NORMAL;
char *pattern, *cwd; char *pattern, *cwd, *expand = NULL;
const char *path, *error; const char *path, *error;
glob_t g; glob_t g;
int i, result; int i, result;
@@ -145,7 +145,12 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
utf8_stravis(&cwd, server_client_get_cwd(c, NULL), VIS_GLOB); utf8_stravis(&cwd, server_client_get_cwd(c, NULL), VIS_GLOB);
for (i = 0; i < args->argc; i++) { for (i = 0; i < args->argc; i++) {
path = args->argv[i]; if (args_has(args, 'F')) {
free(expand);
expand = format_single_from_target(item, args->argv[i]);
path = expand;
} else
path = args->argv[i];
if (strcmp(path, "-") == 0) { if (strcmp(path, "-") == 0) {
cmd_source_file_add(cdata, "-"); cmd_source_file_add(cdata, "-");
continue; continue;
@@ -172,6 +177,7 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
free(pattern); free(pattern);
continue; continue;
} }
free(expand);
free(pattern); free(pattern);
for (j = 0; j < g.gl_pathc; j++) for (j = 0; j < g.gl_pathc; j++)

View File

@@ -39,8 +39,8 @@ const struct cmd_entry cmd_split_window_entry = {
.name = "split-window", .name = "split-window",
.alias = "splitw", .alias = "splitw",
.args = { "bc:de:fF:hIl:p:Pt:v", 0, -1 }, .args = { "bc:de:fF:hIl:p:Pt:vZ", 0, -1 },
.usage = "[-bdefhIPv] [-c start-directory] [-e environment] " .usage = "[-bdefhIPvZ] [-c start-directory] [-e environment] "
"[-F format] [-l size] " CMD_TARGET_PANE_USAGE " [command]", "[-F format] [-l size] " CMD_TARGET_PANE_USAGE " [command]",
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -52,13 +52,14 @@ const struct cmd_entry cmd_split_window_entry = {
static enum cmd_retval static enum cmd_retval
cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = cmd_get_args(self);
struct cmd_find_state *current = &item->shared->current; struct cmd_find_state *current = cmdq_get_current(item);
struct cmd_find_state *target = cmdq_get_target(item);
struct spawn_context sc; struct spawn_context sc;
struct client *c = cmd_find_client(item, NULL, 1); struct client *tc = cmdq_get_target_client(item);
struct session *s = item->target.s; struct session *s = target->s;
struct winlink *wl = item->target.wl; struct winlink *wl = target->wl;
struct window_pane *wp = item->target.wp, *new_wp; struct window_pane *wp = target->wp, *new_wp;
enum layout_type type; enum layout_type type;
struct layout_cell *lc; struct layout_cell *lc;
struct cmd_find_state fs; struct cmd_find_state fs;
@@ -109,7 +110,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
} else } else
size = -1; size = -1;
server_unzoom_window(wp->window); window_push_zoom(wp->window, 1, args_has(args, 'Z'));
input = (args_has(args, 'I') && args->argc == 0); input = (args_has(args, 'I') && args->argc == 0);
flags = 0; flags = 0;
@@ -141,7 +142,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
add = args_first_value(args, 'e', &value); add = args_first_value(args, 'e', &value);
while (add != NULL) { while (add != NULL) {
environ_put(sc.environ, add); environ_put(sc.environ, add, 0);
add = args_next_value(&value); add = args_next_value(&value);
} }
@@ -151,6 +152,8 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
sc.flags = flags; sc.flags = flags;
if (args_has(args, 'd')) if (args_has(args, 'd'))
sc.flags |= SPAWN_DETACHED; sc.flags |= SPAWN_DETACHED;
if (args_has(args, 'Z'))
sc.flags |= SPAWN_ZOOM;
if ((new_wp = spawn_pane(&sc, &cause)) == NULL) { if ((new_wp = spawn_pane(&sc, &cause)) == NULL) {
cmdq_error(item, "create pane failed: %s", cause); cmdq_error(item, "create pane failed: %s", cause);
@@ -158,6 +161,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
if (input && window_pane_start_input(new_wp, item, &cause) != 0) { if (input && window_pane_start_input(new_wp, item, &cause) != 0) {
server_client_remove_pane(new_wp);
layout_close_pane(new_wp); layout_close_pane(new_wp);
window_remove_pane(wp->window, new_wp); window_remove_pane(wp->window, new_wp);
cmdq_error(item, "%s", cause); cmdq_error(item, "%s", cause);
@@ -166,13 +170,14 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
} }
if (!args_has(args, 'd')) if (!args_has(args, 'd'))
cmd_find_from_winlink_pane(current, wl, new_wp, 0); cmd_find_from_winlink_pane(current, wl, new_wp, 0);
window_pop_zoom(wp->window);
server_redraw_window(wp->window); server_redraw_window(wp->window);
server_status_session(s); server_status_session(s);
if (args_has(args, 'P')) { if (args_has(args, 'P')) {
if ((template = args_get(args, 'F')) == NULL) if ((template = args_get(args, 'F')) == NULL)
template = SPLIT_WINDOW_TEMPLATE; template = SPLIT_WINDOW_TEMPLATE;
cp = format_single(item, template, c, s, wl, new_wp); cp = format_single(item, template, tc, s, wl, new_wp);
cmdq_print(item, "%s", cp); cmdq_print(item, "%s", cp);
free(cp); free(cp);
} }

View File

@@ -45,18 +45,20 @@ const struct cmd_entry cmd_swap_pane_entry = {
static enum cmd_retval static enum cmd_retval
cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = cmd_get_args(self);
struct cmd_find_state *source = cmdq_get_source(item);
struct cmd_find_state *target = cmdq_get_target(item);
struct window *src_w, *dst_w; struct window *src_w, *dst_w;
struct window_pane *tmp_wp, *src_wp, *dst_wp; struct window_pane *tmp_wp, *src_wp, *dst_wp;
struct layout_cell *src_lc, *dst_lc; struct layout_cell *src_lc, *dst_lc;
u_int sx, sy, xoff, yoff; u_int sx, sy, xoff, yoff;
dst_w = item->target.wl->window; dst_w = target->wl->window;
dst_wp = item->target.wp; dst_wp = target->wp;
src_w = item->source.wl->window; src_w = source->wl->window;
src_wp = item->source.wp; src_wp = source->wp;
if (window_push_zoom(dst_w, args_has(args, 'Z'))) if (window_push_zoom(dst_w, 0, args_has(args, 'Z')))
server_redraw_window(dst_w); server_redraw_window(dst_w);
if (args_has(args, 'D')) { if (args_has(args, 'D')) {
@@ -71,12 +73,15 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
src_wp = TAILQ_LAST(&dst_w->panes, window_panes); src_wp = TAILQ_LAST(&dst_w->panes, window_panes);
} }
if (src_w != dst_w && window_push_zoom(src_w, args_has(args, 'Z'))) if (src_w != dst_w && window_push_zoom(src_w, 0, args_has(args, 'Z')))
server_redraw_window(src_w); server_redraw_window(src_w);
if (src_wp == dst_wp) if (src_wp == dst_wp)
goto out; goto out;
server_client_remove_pane(src_wp);
server_client_remove_pane(dst_wp);
tmp_wp = TAILQ_PREV(dst_wp, window_panes, entry); tmp_wp = TAILQ_PREV(dst_wp, window_panes, entry);
TAILQ_REMOVE(&dst_w->panes, dst_wp, entry); TAILQ_REMOVE(&dst_w->panes, dst_wp, entry);
TAILQ_REPLACE(&src_w->panes, src_wp, dst_wp, entry); TAILQ_REPLACE(&src_w->panes, src_wp, dst_wp, entry);

View File

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

View File

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

View File

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

View File

@@ -120,7 +120,7 @@ cmd_wait_for_remove(struct wait_channel *wc)
static enum cmd_retval static enum cmd_retval
cmd_wait_for_exec(struct cmd *self, struct cmdq_item *item) cmd_wait_for_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = self->args; struct args *args = cmd_get_args(self);
const char *name = args->argv[0]; const char *name = args->argv[0];
struct wait_channel *wc, wc0; struct wait_channel *wc, wc0;
@@ -167,7 +167,7 @@ static enum cmd_retval
cmd_wait_for_wait(struct cmdq_item *item, const char *name, cmd_wait_for_wait(struct cmdq_item *item, const char *name,
struct wait_channel *wc) struct wait_channel *wc)
{ {
struct client *c = item->client; struct client *c = cmdq_get_client(item);
struct wait_item *wi; struct wait_item *wi;
if (c == NULL) { if (c == NULL) {
@@ -198,7 +198,7 @@ cmd_wait_for_lock(struct cmdq_item *item, const char *name,
{ {
struct wait_item *wi; struct wait_item *wi;
if (item->client == NULL) { if (cmdq_get_client(item) == NULL) {
cmdq_error(item, "not able to lock"); cmdq_error(item, "not able to lock");
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }

170
cmd.c
View File

@@ -39,10 +39,12 @@ extern const struct cmd_entry cmd_clock_mode_entry;
extern const struct cmd_entry cmd_command_prompt_entry; extern const struct cmd_entry cmd_command_prompt_entry;
extern const struct cmd_entry cmd_confirm_before_entry; extern const struct cmd_entry cmd_confirm_before_entry;
extern const struct cmd_entry cmd_copy_mode_entry; extern const struct cmd_entry cmd_copy_mode_entry;
extern const struct cmd_entry cmd_customize_mode_entry;
extern const struct cmd_entry cmd_delete_buffer_entry; extern const struct cmd_entry cmd_delete_buffer_entry;
extern const struct cmd_entry cmd_detach_client_entry; extern const struct cmd_entry cmd_detach_client_entry;
extern const struct cmd_entry cmd_display_menu_entry; extern const struct cmd_entry cmd_display_menu_entry;
extern const struct cmd_entry cmd_display_message_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_display_panes_entry;
extern const struct cmd_entry cmd_down_pane_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_find_window_entry;
@@ -128,10 +130,12 @@ const struct cmd_entry *cmd_table[] = {
&cmd_command_prompt_entry, &cmd_command_prompt_entry,
&cmd_confirm_before_entry, &cmd_confirm_before_entry,
&cmd_copy_mode_entry, &cmd_copy_mode_entry,
&cmd_customize_mode_entry,
&cmd_delete_buffer_entry, &cmd_delete_buffer_entry,
&cmd_detach_client_entry, &cmd_detach_client_entry,
&cmd_display_menu_entry, &cmd_display_menu_entry,
&cmd_display_message_entry, &cmd_display_message_entry,
&cmd_display_popup_entry,
&cmd_display_panes_entry, &cmd_display_panes_entry,
&cmd_find_window_entry, &cmd_find_window_entry,
&cmd_has_session_entry, &cmd_has_session_entry,
@@ -204,8 +208,27 @@ const struct cmd_entry *cmd_table[] = {
NULL NULL
}; };
/* Instance of a command. */
struct cmd {
const struct cmd_entry *entry;
struct args *args;
u_int group;
char *file;
u_int line;
char *alias;
int argc;
char **argv;
TAILQ_ENTRY(cmd) qentry;
};
TAILQ_HEAD(cmds, cmd);
/* Next group number for new command list. */
static u_int cmd_list_next_group = 1; static u_int cmd_list_next_group = 1;
/* Log an argument vector. */
void printflike(3, 4) void printflike(3, 4)
cmd_log_argv(int argc, char **argv, const char *fmt, ...) cmd_log_argv(int argc, char **argv, const char *fmt, ...)
{ {
@@ -222,6 +245,7 @@ cmd_log_argv(int argc, char **argv, const char *fmt, ...)
free(prefix); free(prefix);
} }
/* Prepend to an argument vector. */
void void
cmd_prepend_argv(int *argc, char ***argv, char *arg) cmd_prepend_argv(int *argc, char ***argv, char *arg)
{ {
@@ -238,6 +262,7 @@ cmd_prepend_argv(int *argc, char ***argv, char *arg)
(*argc)++; (*argc)++;
} }
/* Append to an argument vector. */
void void
cmd_append_argv(int *argc, char ***argv, char *arg) cmd_append_argv(int *argc, char ***argv, char *arg)
{ {
@@ -245,6 +270,7 @@ cmd_append_argv(int *argc, char ***argv, char *arg)
(*argv)[(*argc)++] = xstrdup(arg); (*argv)[(*argc)++] = xstrdup(arg);
} }
/* Pack an argument vector up into a buffer. */
int int
cmd_pack_argv(int argc, char **argv, char *buf, size_t len) cmd_pack_argv(int argc, char **argv, char *buf, size_t len)
{ {
@@ -267,6 +293,7 @@ cmd_pack_argv(int argc, char **argv, char *buf, size_t len)
return (0); return (0);
} }
/* Unpack an argument vector from a packed buffer. */
int int
cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv) cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv)
{ {
@@ -295,6 +322,7 @@ cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv)
return (0); return (0);
} }
/* Copy an argument vector, ensuring it is terminated by NULL. */
char ** char **
cmd_copy_argv(int argc, char **argv) cmd_copy_argv(int argc, char **argv)
{ {
@@ -311,6 +339,7 @@ cmd_copy_argv(int argc, char **argv)
return (new_argv); return (new_argv);
} }
/* Free an argument vector. */
void void
cmd_free_argv(int argc, char **argv) cmd_free_argv(int argc, char **argv)
{ {
@@ -323,32 +352,67 @@ cmd_free_argv(int argc, char **argv)
free(argv); free(argv);
} }
/* Convert argument vector to a string. */
char * char *
cmd_stringify_argv(int argc, char **argv) cmd_stringify_argv(int argc, char **argv)
{ {
char *buf; char *buf = NULL, *s;
size_t len = 0;
int i; int i;
size_t len;
if (argc == 0) if (argc == 0)
return (xstrdup("")); return (xstrdup(""));
len = 0;
buf = NULL;
for (i = 0; i < argc; i++) { for (i = 0; i < argc; i++) {
len += strlen(argv[i]) + 1; s = args_escape(argv[i]);
log_debug("%s: %u %s = %s", __func__, i, argv[i], s);
len += strlen(s) + 1;
buf = xrealloc(buf, len); buf = xrealloc(buf, len);
if (i == 0) if (i == 0)
*buf = '\0'; *buf = '\0';
else else
strlcat(buf, " ", len); strlcat(buf, " ", len);
strlcat(buf, argv[i], len); strlcat(buf, s, len);
free(s);
} }
return (buf); return (buf);
} }
/* Get entry for command. */
const struct cmd_entry *
cmd_get_entry(struct cmd *cmd)
{
return (cmd->entry);
}
/* Get arguments for command. */
struct args *
cmd_get_args(struct cmd *cmd)
{
return (cmd->args);
}
/* Get group for command. */
u_int
cmd_get_group(struct cmd *cmd)
{
return (cmd->group);
}
/* Get file and line for command. */
void
cmd_get_source(struct cmd *cmd, const char **file, u_int *line)
{
if (file != NULL)
*file = cmd->file;
if (line != NULL)
*line = cmd->line;
}
/* Look for an alias for a command. */
char * char *
cmd_get_alias(const char *name) cmd_get_alias(const char *name)
{ {
@@ -379,6 +443,7 @@ cmd_get_alias(const char *name)
return (NULL); return (NULL);
} }
/* Look up a command entry by name. */
static const struct cmd_entry * static const struct cmd_entry *
cmd_find(const char *name, char **cause) cmd_find(const char *name, char **cause)
{ {
@@ -428,6 +493,7 @@ ambiguous:
return (NULL); return (NULL);
} }
/* Parse a single command from an argument vector. */
struct cmd * struct cmd *
cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause) cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause)
{ {
@@ -476,6 +542,7 @@ usage:
return (NULL); return (NULL);
} }
/* Free a command. */
void void
cmd_free(struct cmd *cmd) cmd_free(struct cmd *cmd)
{ {
@@ -488,6 +555,7 @@ cmd_free(struct cmd *cmd)
free(cmd); free(cmd);
} }
/* Get a command as a string. */
char * char *
cmd_print(struct cmd *cmd) cmd_print(struct cmd *cmd)
{ {
@@ -503,6 +571,7 @@ cmd_print(struct cmd *cmd)
return (out); return (out);
} }
/* Create a new command list. */
struct cmd_list * struct cmd_list *
cmd_list_new(void) cmd_list_new(void)
{ {
@@ -511,29 +580,28 @@ cmd_list_new(void)
cmdlist = xcalloc(1, sizeof *cmdlist); cmdlist = xcalloc(1, sizeof *cmdlist);
cmdlist->references = 1; cmdlist->references = 1;
cmdlist->group = cmd_list_next_group++; cmdlist->group = cmd_list_next_group++;
TAILQ_INIT(&cmdlist->list); cmdlist->list = xcalloc(1, sizeof *cmdlist->list);
TAILQ_INIT(cmdlist->list);
return (cmdlist); return (cmdlist);
} }
/* Append a command to a command list. */
void void
cmd_list_append(struct cmd_list *cmdlist, struct cmd *cmd) cmd_list_append(struct cmd_list *cmdlist, struct cmd *cmd)
{ {
cmd->group = cmdlist->group; cmd->group = cmdlist->group;
TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry); TAILQ_INSERT_TAIL(cmdlist->list, cmd, qentry);
} }
/* Move all commands from one command list to another */
void void
cmd_list_move(struct cmd_list *cmdlist, struct cmd_list *from) cmd_list_move(struct cmd_list *cmdlist, struct cmd_list *from)
{ {
struct cmd *cmd, *cmd1; TAILQ_CONCAT(cmdlist->list, from->list, qentry);
TAILQ_FOREACH_SAFE(cmd, &from->list, qentry, cmd1) {
TAILQ_REMOVE(&from->list, cmd, qentry);
TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry);
}
cmdlist->group = cmd_list_next_group++; cmdlist->group = cmd_list_next_group++;
} }
/* Free a command list. */
void void
cmd_list_free(struct cmd_list *cmdlist) cmd_list_free(struct cmd_list *cmdlist)
{ {
@@ -542,36 +610,46 @@ cmd_list_free(struct cmd_list *cmdlist)
if (--cmdlist->references != 0) if (--cmdlist->references != 0)
return; return;
TAILQ_FOREACH_SAFE(cmd, &cmdlist->list, qentry, cmd1) { TAILQ_FOREACH_SAFE(cmd, cmdlist->list, qentry, cmd1) {
TAILQ_REMOVE(&cmdlist->list, cmd, qentry); TAILQ_REMOVE(cmdlist->list, cmd, qentry);
cmd_free(cmd); cmd_free(cmd);
} }
free(cmdlist->list);
free(cmdlist); free(cmdlist);
} }
/* Get a command list as a string. */
char * char *
cmd_list_print(struct cmd_list *cmdlist, int escaped) cmd_list_print(struct cmd_list *cmdlist, int escaped)
{ {
struct cmd *cmd; struct cmd *cmd, *next;
char *buf, *this; char *buf, *this;
size_t len; size_t len;
len = 1; len = 1;
buf = xcalloc(1, len); buf = xcalloc(1, len);
TAILQ_FOREACH(cmd, &cmdlist->list, qentry) { TAILQ_FOREACH(cmd, cmdlist->list, qentry) {
this = cmd_print(cmd); this = cmd_print(cmd);
len += strlen(this) + 4; len += strlen(this) + 6;
buf = xrealloc(buf, len); buf = xrealloc(buf, len);
strlcat(buf, this, len); strlcat(buf, this, len);
if (TAILQ_NEXT(cmd, qentry) != NULL) {
if (escaped) next = TAILQ_NEXT(cmd, qentry);
strlcat(buf, " \\; ", len); if (next != NULL) {
else if (cmd->group != next->group) {
strlcat(buf, " ; ", len); if (escaped)
strlcat(buf, " \\;\\; ", len);
else
strlcat(buf, " ;; ", len);
} else {
if (escaped)
strlcat(buf, " \\; ", len);
else
strlcat(buf, " ; ", len);
}
} }
free(this); free(this);
@@ -580,6 +658,46 @@ cmd_list_print(struct cmd_list *cmdlist, int escaped)
return (buf); return (buf);
} }
/* Get first command in list. */
struct cmd *
cmd_list_first(struct cmd_list *cmdlist)
{
return (TAILQ_FIRST(cmdlist->list));
}
/* Get next command in list. */
struct cmd *
cmd_list_next(struct cmd *cmd)
{
return (TAILQ_NEXT(cmd, qentry));
}
/* Do all of the commands in this command list have this flag? */
int
cmd_list_all_have(struct cmd_list *cmdlist, int flag)
{
struct cmd *cmd;
TAILQ_FOREACH(cmd, cmdlist->list, qentry) {
if (~cmd->entry->flags & flag)
return (0);
}
return (1);
}
/* Do any of the commands in this command list have this flag? */
int
cmd_list_any_have(struct cmd_list *cmdlist, int flag)
{
struct cmd *cmd;
TAILQ_FOREACH(cmd, cmdlist->list, qentry) {
if (cmd->entry->flags & flag)
return (1);
}
return (0);
}
/* Adjust current mouse position for a pane. */ /* Adjust current mouse position for a pane. */
int int
cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp, cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp,

617
colour.c
View File

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

View File

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

37
compat/clock_gettime.c Normal file
View File

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

View File

@@ -44,6 +44,9 @@
# include <ndir.h> # include <ndir.h>
# endif # endif
#endif #endif
#if defined(HAVE_LIBPROC_H)
# include <libproc.h>
#endif
#include "compat.h" #include "compat.h"
@@ -55,39 +58,15 @@
__unused static const char rcsid[] = "$Sudo: closefrom.c,v 1.11 2006/08/17 15:26:54 millert Exp $"; __unused static const char rcsid[] = "$Sudo: closefrom.c,v 1.11 2006/08/17 15:26:54 millert Exp $";
#endif /* lint */ #endif /* lint */
#ifndef HAVE_FCNTL_CLOSEM
/* /*
* Close all file descriptors greater than or equal to lowfd. * Close all file descriptors greater than or equal to lowfd.
*/ */
#ifdef HAVE_FCNTL_CLOSEM static void
void closefrom_fallback(int lowfd)
closefrom(int lowfd)
{ {
(void) fcntl(lowfd, F_CLOSEM, 0); long fd, maxfd;
}
#else
void
closefrom(int lowfd)
{
long fd, maxfd;
#if defined(HAVE_DIRFD) && defined(HAVE_PROC_PID)
char fdpath[PATH_MAX], *endp;
struct dirent *dent;
DIR *dirp;
int len;
/* Check for a /proc/$$/fd directory. */
len = snprintf(fdpath, sizeof(fdpath), "/proc/%ld/fd", (long)getpid());
if (len > 0 && (size_t)len <= sizeof(fdpath) && (dirp = opendir(fdpath))) {
while ((dent = readdir(dirp)) != NULL) {
fd = strtol(dent->d_name, &endp, 10);
if (dent->d_name != endp && *endp == '\0' &&
fd >= 0 && fd < INT_MAX && fd >= lowfd && fd != dirfd(dirp))
(void) close((int) fd);
}
(void) closedir(dirp);
} else
#endif
{
/* /*
* Fall back on sysconf() or getdtablesize(). We avoid checking * Fall back on sysconf() or getdtablesize(). We avoid checking
* resource limits since it is possible to open a file descriptor * resource limits since it is possible to open a file descriptor
@@ -99,11 +78,78 @@ closefrom(int lowfd)
maxfd = getdtablesize(); maxfd = getdtablesize();
#endif /* HAVE_SYSCONF */ #endif /* HAVE_SYSCONF */
if (maxfd < 0) if (maxfd < 0)
maxfd = OPEN_MAX; maxfd = OPEN_MAX;
for (fd = lowfd; fd < maxfd; fd++) for (fd = lowfd; fd < maxfd; fd++)
(void) close((int) fd); (void) close((int) fd);
}
#endif /* HAVE_FCNTL_CLOSEM */
#ifdef HAVE_FCNTL_CLOSEM
void
closefrom(int lowfd)
{
(void) fcntl(lowfd, F_CLOSEM, 0);
}
#elif defined(HAVE_LIBPROC_H) && defined(HAVE_PROC_PIDINFO)
void
closefrom(int lowfd)
{
int i, r, sz;
pid_t pid = getpid();
struct proc_fdinfo *fdinfo_buf = NULL;
sz = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
if (sz == 0)
return; /* no fds, really? */
else if (sz == -1)
goto fallback;
if ((fdinfo_buf = malloc(sz)) == NULL)
goto fallback;
r = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fdinfo_buf, sz);
if (r < 0 || r > sz)
goto fallback;
for (i = 0; i < r / (int)PROC_PIDLISTFD_SIZE; i++) {
if (fdinfo_buf[i].proc_fd >= lowfd)
close(fdinfo_buf[i].proc_fd);
}
free(fdinfo_buf);
return;
fallback:
free(fdinfo_buf);
closefrom_fallback(lowfd);
return;
}
#elif defined(HAVE_DIRFD) && defined(HAVE_PROC_PID)
void
closefrom(int lowfd)
{
long fd;
char fdpath[PATH_MAX], *endp;
struct dirent *dent;
DIR *dirp;
int len;
/* Check for a /proc/$$/fd directory. */
len = snprintf(fdpath, sizeof(fdpath), "/proc/%ld/fd", (long)getpid());
if (len > 0 && (size_t)len < sizeof(fdpath) && (dirp = opendir(fdpath))) {
while ((dent = readdir(dirp)) != NULL) {
fd = strtol(dent->d_name, &endp, 10);
if (dent->d_name != endp && *endp == '\0' &&
fd >= 0 && fd < INT_MAX && fd >= lowfd && fd != dirfd(dirp))
(void) close((int) fd);
}
(void) closedir(dirp);
return;
} }
/* /proc/$$/fd strategy failed, fall back to brute force closure */
closefrom_fallback(lowfd);
}
#else
void
closefrom(int lowfd)
{
closefrom_fallback(lowfd);
} }
#endif /* !HAVE_FCNTL_CLOSEM */ #endif /* !HAVE_FCNTL_CLOSEM */
#endif /* HAVE_CLOSEFROM */ #endif /* HAVE_CLOSEFROM */

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

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

29
compat/getdtablesize.c Normal file
View File

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

93
compat/getline.c Normal file
View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
# configure.ac # configure.ac
AC_INIT([tmux], 3.1b) AC_INIT([tmux], 3.2)
AC_PREREQ([2.60]) AC_PREREQ([2.60])
AC_CONFIG_AUX_DIR(etc) AC_CONFIG_AUX_DIR(etc)
@@ -21,6 +21,26 @@ SAVED_CFLAGS="$CFLAGS"
SAVED_CPPFLAGS="$CPPFLAGS" SAVED_CPPFLAGS="$CPPFLAGS"
SAVED_LDFLAGS="$LDFLAGS" SAVED_LDFLAGS="$LDFLAGS"
# Is this oss-fuzz build?
AC_ARG_ENABLE(
fuzzing,
AC_HELP_STRING(--enable-fuzzing, build fuzzers)
)
AC_ARG_VAR(
FUZZING_LIBS,
AC_HELP_STRING(libraries to link fuzzing targets with)
)
# Set up convenient fuzzing defaults before initializing compiler.
if test "x$enable_fuzzing" = xyes; then
AC_DEFINE(NEED_FUZZING)
test "x$CC" == x && CC=clang
test "x$FUZZING_LIBS" == x && \
FUZZING_LIBS="-fsanitize=fuzzer"
test "x$SAVED_CFLAGS" == x && \
AM_CFLAGS="-g -fsanitize=fuzzer-no-link,address"
fi
# Set up the compiler in two different ways and say yes we may want to install. # Set up the compiler in two different ways and say yes we may want to install.
AC_PROG_CC AC_PROG_CC
AM_PROG_CC_C_O AM_PROG_CC_C_O
@@ -54,8 +74,11 @@ if test "x$enable_static" = xyes; then
LDFLAGS="$AM_LDFLAGS $SAVED_LDFLAGS" LDFLAGS="$AM_LDFLAGS $SAVED_LDFLAGS"
fi fi
# Do we need fuzzers?
AM_CONDITIONAL(NEED_FUZZING, test "x$enable_fuzzing" = xyes)
# Is this gcc? # Is this gcc?
AM_CONDITIONAL(IS_GCC, test "x$GCC" = xyes) AM_CONDITIONAL(IS_GCC, test "x$GCC" = xyes -a "x$enable_fuzzing" != xyes)
# Is this Sun CC? # Is this Sun CC?
AC_EGREP_CPP( AC_EGREP_CPP(
@@ -76,6 +99,7 @@ AC_CHECK_HEADERS([ \
dirent.h \ dirent.h \
fcntl.h \ fcntl.h \
inttypes.h \ inttypes.h \
libproc.h \
libutil.h \ libutil.h \
ndir.h \ ndir.h \
paths.h \ paths.h \
@@ -87,6 +111,12 @@ AC_CHECK_HEADERS([ \
util.h \ util.h \
]) ])
# Look for sys_signame.
AC_SEARCH_LIBS(sys_signame, , AC_DEFINE(HAVE_SYS_SIGNAME))
# Look for fmod.
AC_CHECK_LIB(m, fmod)
# Look for library needed for flock. # Look for library needed for flock.
AC_SEARCH_LIBS(flock, bsd) AC_SEARCH_LIBS(flock, bsd)
@@ -95,22 +125,24 @@ AC_CHECK_FUNCS([ \
dirfd \ dirfd \
flock \ flock \
prctl \ prctl \
sysconf \ proc_pidinfo \
sysconf
]) ])
# Check for functions with a compatibility implementation. # Check for functions with a compatibility implementation.
AC_REPLACE_FUNCS([ \ AC_REPLACE_FUNCS([ \
asprintf \ asprintf \
cfmakeraw \ cfmakeraw \
clock_gettime \
closefrom \ closefrom \
explicit_bzero \ explicit_bzero \
fgetln \ fgetln \
freezero \ freezero \
getdtablecount \ getdtablecount \
getdtablesize \
getline \
getprogname \ getprogname \
memmem \ memmem \
recallocarray \
reallocarray \
setenv \ setenv \
setproctitle \ setproctitle \
strcasestr \ strcasestr \
@@ -122,6 +154,26 @@ AC_REPLACE_FUNCS([ \
]) ])
AC_FUNC_STRNLEN AC_FUNC_STRNLEN
# Clang sanitizers wrap reallocarray even if it isn't available on the target
# system. When compiled it always returns NULL and crashes the program. To
# detect this we need a more complicated test.
AC_MSG_CHECKING([for working reallocarray])
AC_RUN_IFELSE([AC_LANG_PROGRAM(
[#include <stdlib.h>],
[return (reallocarray(NULL, 1, 1) == NULL);]
)],
AC_MSG_RESULT(yes),
[AC_LIBOBJ(reallocarray) AC_MSG_RESULT([no])]
)
AC_MSG_CHECKING([for working recallocarray])
AC_RUN_IFELSE([AC_LANG_PROGRAM(
[#include <stdlib.h>],
[return (recallocarray(NULL, 1, 1, 1) == NULL);]
)],
AC_MSG_RESULT(yes),
[AC_LIBOBJ(recallocarray) AC_MSG_RESULT([no])]
)
# Look for clock_gettime. Must come before event_init. # Look for clock_gettime. Must come before event_init.
AC_SEARCH_LIBS(clock_gettime, rt) AC_SEARCH_LIBS(clock_gettime, rt)
@@ -131,88 +183,112 @@ AC_SEARCH_LIBS(clock_gettime, rt)
# implementations. # implementations.
AC_LIBOBJ(getopt) AC_LIBOBJ(getopt)
# Look for libevent. # Look for libevent. Try libevent_core or libevent with pkg-config first then
# look for the library.
PKG_CHECK_MODULES( PKG_CHECK_MODULES(
LIBEVENT, LIBEVENT_CORE,
libevent, [libevent_core >= 2],
[ [
AM_CFLAGS="$LIBEVENT_CFLAGS $AM_CFLAGS" AM_CPPFLAGS="$LIBEVENT_CORE_CFLAGS $AM_CPPFLAGS"
CFLAGS="$AM_CFLAGS $SAVED_CFLAGS" CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBEVENT_LIBS $LIBS" LIBS="$LIBEVENT_CORE_LIBS $LIBS"
found_libevent=yes found_libevent=yes
], ],
found_libevent=no
)
if test x$found_libevent = xno; then
PKG_CHECK_MODULES(
LIBEVENT,
[libevent >= 2],
[
AM_CPPFLAGS="$LIBEVENT_CFLAGS $AM_CPPFLAGS"
CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBEVENT_LIBS $LIBS"
found_libevent=yes
],
found_libevent=no
)
fi
if test x$found_libevent = xno; then
AC_SEARCH_LIBS(
event_init,
[event_core event event-1.4],
found_libevent=yes,
found_libevent=no
)
fi
AC_CHECK_HEADER(
event2/event.h,
AC_DEFINE(HAVE_EVENT2_EVENT_H),
[ [
AC_SEARCH_LIBS( AC_CHECK_HEADER(
event_init, event.h,
[event event-1.4 event2], AC_DEFINE(HAVE_EVENT_H),
found_libevent=yes,
found_libevent=no found_libevent=no
) )
] ]
) )
AC_CHECK_HEADER(
event.h,
,
found_libevent=no
)
if test "x$found_libevent" = xno; then if test "x$found_libevent" = xno; then
AC_MSG_ERROR("libevent not found") AC_MSG_ERROR("libevent not found")
fi fi
# Look for ncurses. # Look for ncurses or curses. Try pkg-config first then directly for the
# library.
PKG_CHECK_MODULES( PKG_CHECK_MODULES(
LIBTINFO, LIBTINFO,
tinfo, tinfo,
found_ncurses=yes, [
AM_CPPFLAGS="$LIBTINFO_CFLAGS $AM_CPPFLAGS"
CPPFLAGS="$LIBTINFO_CFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBTINFO_LIBS $LIBS"
found_ncurses=yes
],
found_ncurses=no found_ncurses=no
) )
if test "x$found_ncurses" = xno; then if test "x$found_ncurses" = xno; then
PKG_CHECK_MODULES( PKG_CHECK_MODULES(
LIBNCURSES, LIBNCURSES,
ncurses, ncurses,
found_ncurses=yes, [
AM_CPPFLAGS="$LIBNCURSES_CFLAGS $AM_CPPFLAGS"
CPPFLAGS="$LIBNCURSES_CFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBNCURSES_LIBS $LIBS"
found_ncurses=yes
],
found_ncurses=no found_ncurses=no
) )
fi fi
if test "x$found_ncurses" = xno; then if test "x$found_ncurses" = xno; then
PKG_CHECK_MODULES( PKG_CHECK_MODULES(
LIBNCURSES, LIBNCURSESW,
ncursesw, ncursesw,
found_ncurses=yes, [
AM_CPPFLAGS="$LIBNCURSESW_CFLAGS $AM_CPPFLAGS"
CPPFLAGS="$LIBNCURSESW_CFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBNCURSESW_LIBS $LIBS"
found_ncurses=yes
],
found_ncurses=no found_ncurses=no
) )
fi fi
if test "x$found_ncurses" = xyes; then if test "x$found_ncurses" = xno; then
AM_CFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $AM_CFLAGS" AC_SEARCH_LIBS(
CFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $CFLAGS"
LIBS="$LIBNCURSES_LIBS $LIBTINFO_LIBS $LIBS"
else
# pkg-config didn't work, try ncurses.
AC_CHECK_LIB(
tinfo,
setupterm, setupterm,
[tinfo ncurses ncursesw],
found_ncurses=yes, found_ncurses=yes,
found_ncurses=no found_ncurses=no
) )
if test "x$found_ncurses" = xno; then
AC_CHECK_LIB(
ncurses,
setupterm,
found_ncurses=yes,
found_ncurses=no
)
fi
if test "x$found_ncurses" = xyes; then if test "x$found_ncurses" = xyes; then
AC_CHECK_HEADER( AC_CHECK_HEADER(
ncurses.h, ncurses.h,
LIBS="$LIBS -lncurses", LIBS="$LIBS -lncurses",
found_ncurses=no) found_ncurses=no
)
fi fi
fi fi
if test "x$found_ncurses" = xyes; then if test "x$found_ncurses" = xyes; then
AC_DEFINE(HAVE_NCURSES_H) AC_DEFINE(HAVE_NCURSES_H)
else else
# No ncurses, try curses.
AC_CHECK_LIB( AC_CHECK_LIB(
curses, curses,
setupterm, setupterm,
@@ -222,7 +298,8 @@ else
AC_CHECK_HEADER( AC_CHECK_HEADER(
curses.h, curses.h,
, ,
found_curses=no) found_curses=no
)
if test "x$found_curses" = xyes; then if test "x$found_curses" = xyes; then
LIBS="$LIBS -lcurses" LIBS="$LIBS -lcurses"
AC_DEFINE(HAVE_CURSES_H) AC_DEFINE(HAVE_CURSES_H)
@@ -288,12 +365,11 @@ AC_TRY_LINK(
found_b64_ntop=yes, found_b64_ntop=yes,
found_b64_ntop=no found_b64_ntop=no
) )
AC_MSG_RESULT($found_b64_ntop)
OLD_LIBS="$LIBS"
if test "x$found_b64_ntop" = xno; then if test "x$found_b64_ntop" = xno; then
AC_MSG_RESULT(no)
AC_MSG_CHECKING(for b64_ntop with -lresolv) AC_MSG_CHECKING(for b64_ntop with -lresolv)
OLD_LIBS="$LIBS" LIBS="$OLD_LIBS -lresolv"
LIBS="$LIBS -lresolv"
AC_TRY_LINK( AC_TRY_LINK(
[ [
#include <sys/types.h> #include <sys/types.h>
@@ -304,15 +380,27 @@ if test "x$found_b64_ntop" = xno; then
found_b64_ntop=yes, found_b64_ntop=yes,
found_b64_ntop=no found_b64_ntop=no
) )
if test "x$found_b64_ntop" = xno; then AC_MSG_RESULT($found_b64_ntop)
LIBS="$OLD_LIBS" fi
AC_MSG_RESULT(no) if test "x$found_b64_ntop" = xno; then
fi AC_MSG_CHECKING(for b64_ntop with -lnetwork)
LIBS="$OLD_LIBS -lnetwork"
AC_TRY_LINK(
[
#include <sys/types.h>
#include <netinet/in.h>
#include <resolv.h>
],
[b64_ntop(NULL, 0, NULL, 0);],
found_b64_ntop=yes,
found_b64_ntop=no
)
AC_MSG_RESULT($found_b64_ntop)
fi fi
if test "x$found_b64_ntop" = xyes; then if test "x$found_b64_ntop" = xyes; then
AC_DEFINE(HAVE_B64_NTOP) AC_DEFINE(HAVE_B64_NTOP)
AC_MSG_RESULT(yes)
else else
LIBS="$OLD_LIBS"
AC_LIBOBJ(base64) AC_LIBOBJ(base64)
fi fi
@@ -321,8 +409,34 @@ AC_SEARCH_LIBS(inet_ntoa, nsl)
AC_SEARCH_LIBS(socket, socket) AC_SEARCH_LIBS(socket, socket)
AC_CHECK_LIB(xnet, socket) AC_CHECK_LIB(xnet, socket)
# Check for CMSG_DATA. Some platforms require _XOPEN_SOURCE_EXTENDED (for # Check if using glibc and have malloc_trim(3). The glibc free(3) is pretty bad
# example see xopen_networking(7) on HP-UX). # about returning memory to the kernel unless the application tells it when to
# with malloc_trim(3).
AC_MSG_CHECKING(if free doesn't work very well)
AC_LINK_IFELSE([AC_LANG_SOURCE(
[
#include <stdlib.h>
#ifdef __GLIBC__
#include <malloc.h>
int main(void) {
malloc_trim (0);
exit(0);
}
#else
no
#endif
])],
found_malloc_trim=yes,
found_malloc_trim=no
)
AC_MSG_RESULT($found_malloc_trim)
if test "x$found_malloc_trim" = xyes; then
AC_DEFINE(HAVE_MALLOC_TRIM)
fi
# Check for CMSG_DATA. On some platforms like HP-UX this requires UNIX 95
# (_XOPEN_SOURCE and _XOPEN_SOURCE_EXTENDED) (see xopen_networking(7)). On
# others, UNIX 03 (_XOPEN_SOURCE 600, see standards(7) on Solaris).
XOPEN_DEFINES= XOPEN_DEFINES=
AC_MSG_CHECKING(for CMSG_DATA) AC_MSG_CHECKING(for CMSG_DATA)
AC_EGREP_CPP( AC_EGREP_CPP(
@@ -355,6 +469,25 @@ if test "x$found_cmsg_data" = xno; then
AC_MSG_RESULT($found_cmsg_data) AC_MSG_RESULT($found_cmsg_data)
if test "x$found_cmsg_data" = xyes; then if test "x$found_cmsg_data" = xyes; then
XOPEN_DEFINES="-D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED" XOPEN_DEFINES="-D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED"
fi
fi
if test "x$found_cmsg_data" = xno; then
AC_MSG_CHECKING(if CMSG_DATA needs _XOPEN_SOURCE 600)
AC_EGREP_CPP(
yes,
[
#define _XOPEN_SOURCE 600
#include <sys/socket.h>
#ifdef CMSG_DATA
yes
#endif
],
found_cmsg_data=yes,
found_cmsg_data=no
)
AC_MSG_RESULT($found_cmsg_data)
if test "x$found_cmsg_data" = xyes; then
XOPEN_DEFINES="-D_XOPEN_SOURCE=600"
else else
AC_MSG_ERROR("CMSG_DATA not found") AC_MSG_ERROR("CMSG_DATA not found")
fi fi
@@ -543,6 +676,12 @@ case "$host_os" in
AC_MSG_RESULT(darwin) AC_MSG_RESULT(darwin)
PLATFORM=darwin PLATFORM=darwin
# #
# OS X uses __dead2 instead of __dead, like FreeBSD. But it
# defines __dead away so it needs to be removed before we can
# replace it.
#
AC_DEFINE(BROKEN___DEAD)
#
# OS X CMSG_FIRSTHDR is broken, so redefine it with a working # OS X CMSG_FIRSTHDR is broken, so redefine it with a working
# one. daemon works but has some stupid side effects, so use # one. daemon works but has some stupid side effects, so use
# our internal version which has a workaround. # our internal version which has a workaround.
@@ -597,6 +736,10 @@ case "$host_os" in
AC_MSG_RESULT(cygwin) AC_MSG_RESULT(cygwin)
PLATFORM=cygwin PLATFORM=cygwin
;; ;;
*haiku*)
AC_MSG_RESULT(haiku)
PLATFORM=haiku
;;
*) *)
AC_MSG_RESULT(unknown) AC_MSG_RESULT(unknown)
PLATFORM=unknown PLATFORM=unknown
@@ -612,6 +755,7 @@ AM_CONDITIONAL(IS_NETBSD, test "x$PLATFORM" = xnetbsd)
AM_CONDITIONAL(IS_OPENBSD, test "x$PLATFORM" = xopenbsd) AM_CONDITIONAL(IS_OPENBSD, test "x$PLATFORM" = xopenbsd)
AM_CONDITIONAL(IS_SUNOS, test "x$PLATFORM" = xsunos) AM_CONDITIONAL(IS_SUNOS, test "x$PLATFORM" = xsunos)
AM_CONDITIONAL(IS_HPUX, test "x$PLATFORM" = xhpux) AM_CONDITIONAL(IS_HPUX, test "x$PLATFORM" = xhpux)
AM_CONDITIONAL(IS_HAIKU, test "x$PLATFORM" = xhaiku)
AM_CONDITIONAL(IS_UNKNOWN, test "x$PLATFORM" = xunknown) AM_CONDITIONAL(IS_UNKNOWN, test "x$PLATFORM" = xunknown)
# Save our CFLAGS/CPPFLAGS/LDFLAGS for the Makefile and restore the old user # Save our CFLAGS/CPPFLAGS/LDFLAGS for the Makefile and restore the old user

View File

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

1067
control.c

File diff suppressed because it is too large Load Diff

View File

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

463
file.c
View File

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

View File

@@ -157,13 +157,14 @@ format_draw_put_list(struct screen_write_ctx *octx,
static void static void
format_draw_none(struct screen_write_ctx *octx, u_int available, u_int ocx, format_draw_none(struct screen_write_ctx *octx, u_int available, u_int ocx,
u_int ocy, struct screen *left, struct screen *centre, struct screen *right, u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
struct format_ranges *frs) struct screen *abs_centre, struct format_ranges *frs)
{ {
u_int width_left, width_centre, width_right; u_int width_left, width_centre, width_right, width_abs_centre;
width_left = left->cx; width_left = left->cx;
width_centre = centre->cx; width_centre = centre->cx;
width_right = right->cx; width_right = right->cx;
width_abs_centre = abs_centre->cx;
/* /*
* Try to keep as much of the left and right as possible at the expense * Try to keep as much of the left and right as possible at the expense
@@ -199,23 +200,34 @@ format_draw_none(struct screen_write_ctx *octx, u_int available, u_int ocx,
- width_centre / 2, - width_centre / 2,
centre->cx / 2 - width_centre / 2, centre->cx / 2 - width_centre / 2,
width_centre); width_centre);
/*
* Write abs_centre in the perfect centre of all horizontal space.
*/
if (width_abs_centre > available)
width_abs_centre = available;
format_draw_put(octx, ocx, ocy, abs_centre, frs,
(available - width_abs_centre) / 2,
0,
width_abs_centre);
} }
/* Draw format with list on the left. */ /* Draw format with list on the left. */
static void static void
format_draw_left(struct screen_write_ctx *octx, u_int available, u_int ocx, format_draw_left(struct screen_write_ctx *octx, u_int available, u_int ocx,
u_int ocy, struct screen *left, struct screen *centre, struct screen *right, u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
struct screen *list, struct screen *list_left, struct screen *list_right, struct screen *abs_centre, struct screen *list, struct screen *list_left,
struct screen *after, int focus_start, int focus_end, struct screen *list_right, struct screen *after, int focus_start,
struct format_ranges *frs) int focus_end, struct format_ranges *frs)
{ {
u_int width_left, width_centre, width_right; u_int width_left, width_centre, width_right;
u_int width_list, width_after; u_int width_list, width_after, width_abs_centre;
struct screen_write_ctx ctx; struct screen_write_ctx ctx;
width_left = left->cx; width_left = left->cx;
width_centre = centre->cx; width_centre = centre->cx;
width_right = right->cx; width_right = right->cx;
width_abs_centre = abs_centre->cx;
width_list = list->cx; width_list = list->cx;
width_after = after->cx; width_after = after->cx;
@@ -242,12 +254,12 @@ format_draw_left(struct screen_write_ctx *octx, u_int available, u_int ocx,
/* If there is no list left, pass off to the no list function. */ /* If there is no list left, pass off to the no list function. */
if (width_list == 0) { if (width_list == 0) {
screen_write_start(&ctx, NULL, left); screen_write_start(&ctx, left);
screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1); screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
screen_write_stop(&ctx); screen_write_stop(&ctx);
format_draw_none(octx, available, ocx, ocy, left, centre, format_draw_none(octx, available, ocx, ocy, left, centre,
right, frs); right, abs_centre, frs);
return; return;
} }
@@ -291,23 +303,34 @@ format_draw_left(struct screen_write_ctx *octx, u_int available, u_int ocx,
focus_start = focus_end = 0; focus_start = focus_end = 0;
format_draw_put_list(octx, ocx, ocy, width_left, width_list, list, format_draw_put_list(octx, ocx, ocy, width_left, width_list, list,
list_left, list_right, focus_start, focus_end, frs); list_left, list_right, focus_start, focus_end, frs);
/*
* Write abs_centre in the perfect centre of all horizontal space.
*/
if (width_abs_centre > available)
width_abs_centre = available;
format_draw_put(octx, ocx, ocy, abs_centre, frs,
(available - width_abs_centre) / 2,
0,
width_abs_centre);
} }
/* Draw format with list in the centre. */ /* Draw format with list in the centre. */
static void static void
format_draw_centre(struct screen_write_ctx *octx, u_int available, u_int ocx, format_draw_centre(struct screen_write_ctx *octx, u_int available, u_int ocx,
u_int ocy, struct screen *left, struct screen *centre, struct screen *right, u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
struct screen *list, struct screen *list_left, struct screen *list_right, struct screen *abs_centre, struct screen *list, struct screen *list_left,
struct screen *after, int focus_start, int focus_end, struct screen *list_right, struct screen *after, int focus_start,
struct format_ranges *frs) int focus_end, struct format_ranges *frs)
{ {
u_int width_left, width_centre, width_right; u_int width_left, width_centre, width_right, middle;
u_int width_list, width_after, middle; u_int width_list, width_after, width_abs_centre;
struct screen_write_ctx ctx; struct screen_write_ctx ctx;
width_left = left->cx; width_left = left->cx;
width_centre = centre->cx; width_centre = centre->cx;
width_right = right->cx; width_right = right->cx;
width_abs_centre = abs_centre->cx;
width_list = list->cx; width_list = list->cx;
width_after = after->cx; width_after = after->cx;
@@ -334,12 +357,12 @@ format_draw_centre(struct screen_write_ctx *octx, u_int available, u_int ocx,
/* If there is no list left, pass off to the no list function. */ /* If there is no list left, pass off to the no list function. */
if (width_list == 0) { if (width_list == 0) {
screen_write_start(&ctx, NULL, centre); screen_write_start(&ctx, centre);
screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1); screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
screen_write_stop(&ctx); screen_write_stop(&ctx);
format_draw_none(octx, available, ocx, ocy, left, centre, format_draw_none(octx, available, ocx, ocy, left, centre,
right, frs); right, abs_centre, frs);
return; return;
} }
@@ -388,23 +411,34 @@ format_draw_centre(struct screen_write_ctx *octx, u_int available, u_int ocx,
format_draw_put_list(octx, ocx, ocy, middle - width_list / 2, format_draw_put_list(octx, ocx, ocy, middle - width_list / 2,
width_list, list, list_left, list_right, focus_start, focus_end, width_list, list, list_left, list_right, focus_start, focus_end,
frs); frs);
/*
* Write abs_centre in the perfect centre of all horizontal space.
*/
if (width_abs_centre > available)
width_abs_centre = available;
format_draw_put(octx, ocx, ocy, abs_centre, frs,
(available - width_abs_centre) / 2,
0,
width_abs_centre);
} }
/* Draw format with list on the right. */ /* Draw format with list on the right. */
static void static void
format_draw_right(struct screen_write_ctx *octx, u_int available, u_int ocx, format_draw_right(struct screen_write_ctx *octx, u_int available, u_int ocx,
u_int ocy, struct screen *left, struct screen *centre, struct screen *right, u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
struct screen *list, struct screen *list_left, struct screen *list_right, struct screen *abs_centre, struct screen *list,
struct screen *after, int focus_start, int focus_end, struct screen *list_left, struct screen *list_right, struct screen *after,
struct format_ranges *frs) int focus_start, int focus_end, struct format_ranges *frs)
{ {
u_int width_left, width_centre, width_right; u_int width_left, width_centre, width_right;
u_int width_list, width_after; u_int width_list, width_after, width_abs_centre;
struct screen_write_ctx ctx; struct screen_write_ctx ctx;
width_left = left->cx; width_left = left->cx;
width_centre = centre->cx; width_centre = centre->cx;
width_right = right->cx; width_right = right->cx;
width_abs_centre = abs_centre->cx;
width_list = list->cx; width_list = list->cx;
width_after = after->cx; width_after = after->cx;
@@ -431,12 +465,12 @@ format_draw_right(struct screen_write_ctx *octx, u_int available, u_int ocx,
/* If there is no list left, pass off to the no list function. */ /* If there is no list left, pass off to the no list function. */
if (width_list == 0) { if (width_list == 0) {
screen_write_start(&ctx, NULL, right); screen_write_start(&ctx, right);
screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1); screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
screen_write_stop(&ctx); screen_write_stop(&ctx);
format_draw_none(octx, available, ocx, ocy, left, centre, format_draw_none(octx, available, ocx, ocy, left, centre,
right, frs); right, abs_centre, frs);
return; return;
} }
@@ -484,6 +518,130 @@ format_draw_right(struct screen_write_ctx *octx, u_int available, u_int ocx,
format_draw_put_list(octx, ocx, ocy, available - width_list - format_draw_put_list(octx, ocx, ocy, available - width_list -
width_after, width_list, list, list_left, list_right, focus_start, width_after, width_list, list, list_left, list_right, focus_start,
focus_end, frs); focus_end, frs);
/*
* Write abs_centre in the perfect centre of all horizontal space.
*/
if (width_abs_centre > available)
width_abs_centre = available;
format_draw_put(octx, ocx, ocy, abs_centre, frs,
(available - width_abs_centre) / 2,
0,
width_abs_centre);
}
static void
format_draw_absolute_centre(struct screen_write_ctx *octx, u_int available,
u_int ocx, u_int ocy, struct screen *left, struct screen *centre,
struct screen *right, struct screen *abs_centre, struct screen *list,
struct screen *list_left, struct screen *list_right, struct screen *after,
int focus_start, int focus_end, struct format_ranges *frs)
{
u_int width_left, width_centre, width_right, width_abs_centre;
u_int width_list, width_after, middle, abs_centre_offset;
width_left = left->cx;
width_centre = centre->cx;
width_right = right->cx;
width_abs_centre = abs_centre->cx;
width_list = list->cx;
width_after = after->cx;
/*
* Trim first centre, then the right, then the left.
*/
while (width_left +
width_centre +
width_right > available) {
if (width_centre > 0)
width_centre--;
else if (width_right > 0)
width_right--;
else
width_left--;
}
/*
* We trim list after and abs_centre independently, as we are drawing
* them over the rest. Trim first the list, then after the list, then
* abs_centre.
*/
while (width_list + width_after + width_abs_centre > available) {
if (width_list > 0)
width_list--;
else if (width_after > 0)
width_after--;
else
width_abs_centre--;
}
/* Write left at 0. */
format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
/* Write right at available - width_right. */
format_draw_put(octx, ocx, ocy, right, frs,
available - width_right,
right->cx - width_right,
width_right);
/*
* Keep writing centre at the relative centre. Only the list is written
* in the absolute centre of the horizontal space.
*/
middle = (width_left + ((available - width_right) - width_left) / 2);
/*
* Write centre at
* middle - width_centre.
*/
format_draw_put(octx, ocx, ocy, centre, frs,
middle - width_centre,
0,
width_centre);
/*
* If there is no focus given, keep the centre in focus.
*/
if (focus_start == -1 || focus_end == -1)
focus_start = focus_end = list->cx / 2;
/*
* We centre abs_centre and the list together, so their shared centre is
* in the perfect centre of horizontal space.
*/
abs_centre_offset = (available - width_list - width_abs_centre) / 2;
/*
* Write abs_centre before the list.
*/
format_draw_put(octx, ocx, ocy, abs_centre, frs, abs_centre_offset,
0, width_abs_centre);
abs_centre_offset += width_abs_centre;
/*
* Draw the list in the absolute centre
*/
format_draw_put_list(octx, ocx, ocy, abs_centre_offset, width_list,
list, list_left, list_right, focus_start, focus_end, frs);
abs_centre_offset += width_list;
/*
* Write after at the end of the centre
*/
format_draw_put(octx, ocx, ocy, after, frs, abs_centre_offset, 0,
width_after);
}
/* Draw multiple characters. */
static void
format_draw_many(struct screen_write_ctx *ctx, struct style *sy, char ch,
u_int n)
{
u_int i;
utf8_set(&sy->gc.data, ch);
for (i = 0; i < n; i++)
screen_write_cell(ctx, &sy->gc);
} }
/* Draw a format to a screen. */ /* Draw a format to a screen. */
@@ -494,6 +652,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
enum { LEFT, enum { LEFT,
CENTRE, CENTRE,
RIGHT, RIGHT,
ABSOLUTE_CENTRE,
LIST, LIST,
LIST_LEFT, LIST_LEFT,
LIST_RIGHT, LIST_RIGHT,
@@ -502,6 +661,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
const char *names[] = { "LEFT", const char *names[] = { "LEFT",
"CENTRE", "CENTRE",
"RIGHT", "RIGHT",
"ABSOLUTE_CENTRE",
"LIST", "LIST",
"LIST_LEFT", "LIST_LEFT",
"LIST_RIGHT", "LIST_RIGHT",
@@ -509,10 +669,14 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
size_t size = strlen(expanded); size_t size = strlen(expanded);
struct screen *os = octx->s, s[TOTAL]; struct screen *os = octx->s, s[TOTAL];
struct screen_write_ctx ctx[TOTAL]; struct screen_write_ctx ctx[TOTAL];
u_int ocx = os->cx, ocy = os->cy, i, width[TOTAL]; u_int ocx = os->cx, ocy = os->cy, n, i, width[TOTAL];
u_int map[] = { LEFT, LEFT, CENTRE, RIGHT }; u_int map[] = { LEFT,
LEFT,
CENTRE,
RIGHT,
ABSOLUTE_CENTRE };
int focus_start = -1, focus_end = -1; int focus_start = -1, focus_end = -1;
int list_state = -1, fill = -1; int list_state = -1, fill = -1, even;
enum style_align list_align = STYLE_ALIGN_DEFAULT; enum style_align list_align = STYLE_ALIGN_DEFAULT;
struct grid_cell gc, current_default; struct grid_cell gc, current_default;
struct style sy, saved_sy; struct style sy, saved_sy;
@@ -536,7 +700,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
*/ */
for (i = 0; i < TOTAL; i++) { for (i = 0; i < TOTAL; i++) {
screen_init(&s[i], size, 1, 0); screen_init(&s[i], size, 1, 0);
screen_write_start(&ctx[i], NULL, &s[i]); screen_write_start(&ctx[i], &s[i]);
screen_write_clearendofline(&ctx[i], current_default.bg); screen_write_clearendofline(&ctx[i], current_default.bg);
width[i] = 0; width[i] = 0;
} }
@@ -547,7 +711,39 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
*/ */
cp = expanded; cp = expanded;
while (*cp != '\0') { while (*cp != '\0') {
if (cp[0] != '#' || cp[1] != '[') { /* Handle sequences of #. */
if (cp[0] == '#' && cp[1] != '[' && cp[1] != '\0') {
for (n = 1; cp[n] == '#'; n++)
/* nothing */;
even = ((n % 2) == 0);
if (cp[n] != '[') {
cp += n;
if (even)
n = (n / 2);
else
n = (n / 2) + 1;
width[current] += n;
format_draw_many(&ctx[current], &sy, '#', n);
continue;
}
if (even)
cp += (n + 1);
else
cp += (n - 1);
if (sy.ignore)
continue;
format_draw_many(&ctx[current], &sy, '#', n / 2);
width[current] += (n / 2);
if (even) {
utf8_set(ud, '[');
screen_write_cell(&ctx[current], &sy.gc);
width[current]++;
}
continue;
}
/* Is this not a style? */
if (cp[0] != '#' || cp[1] != '[' || sy.ignore) {
/* See if this is a UTF-8 character. */ /* See if this is a UTF-8 character. */
if ((more = utf8_open(ud, *cp)) == UTF8_MORE) { if ((more = utf8_open(ud, *cp)) == UTF8_MORE) {
while (*++cp != '\0' && more == UTF8_MORE) while (*++cp != '\0' && more == UTF8_MORE)
@@ -600,7 +796,8 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
/* If this style pushed or popped the default, update it. */ /* If this style pushed or popped the default, update it. */
if (sy.default_type == STYLE_DEFAULT_PUSH) { if (sy.default_type == STYLE_DEFAULT_PUSH) {
memcpy(&current_default, &saved_sy.gc, sizeof current_default); memcpy(&current_default, &saved_sy.gc,
sizeof current_default);
sy.default_type = STYLE_DEFAULT_BASE; sy.default_type = STYLE_DEFAULT_BASE;
} else if (sy.default_type == STYLE_DEFAULT_POP) { } else if (sy.default_type == STYLE_DEFAULT_POP) {
memcpy(&current_default, base, sizeof current_default); memcpy(&current_default, base, sizeof current_default);
@@ -738,31 +935,41 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
/* /*
* Draw the screens. How they are arranged depends on where the list * Draw the screens. How they are arranged depends on where the list
* appearsq. * appears.
*/ */
switch (list_align) { switch (list_align) {
case STYLE_ALIGN_DEFAULT: case STYLE_ALIGN_DEFAULT:
/* No list. */ /* No list. */
format_draw_none(octx, available, ocx, ocy, &s[LEFT], format_draw_none(octx, available, ocx, ocy, &s[LEFT],
&s[CENTRE], &s[RIGHT], &frs); &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &frs);
break; break;
case STYLE_ALIGN_LEFT: case STYLE_ALIGN_LEFT:
/* List is part of the left. */ /* List is part of the left. */
format_draw_left(octx, available, ocx, ocy, &s[LEFT], format_draw_left(octx, available, ocx, ocy, &s[LEFT],
&s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT], &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
&s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs); &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
focus_start, focus_end, &frs);
break; break;
case STYLE_ALIGN_CENTRE: case STYLE_ALIGN_CENTRE:
/* List is part of the centre. */ /* List is part of the centre. */
format_draw_centre(octx, available, ocx, ocy, &s[LEFT], format_draw_centre(octx, available, ocx, ocy, &s[LEFT],
&s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT], &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
&s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs); &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
focus_start, focus_end, &frs);
break; break;
case STYLE_ALIGN_RIGHT: case STYLE_ALIGN_RIGHT:
/* List is part of the right. */ /* List is part of the right. */
format_draw_right(octx, available, ocx, ocy, &s[LEFT], format_draw_right(octx, available, ocx, ocy, &s[LEFT],
&s[CENTRE], &s[RIGHT], &s[LIST], &s[LIST_LEFT], &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
&s[LIST_RIGHT], &s[AFTER], focus_start, focus_end, &frs); &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
focus_start, focus_end, &frs);
break;
case STYLE_ALIGN_ABSOLUTE_CENTRE:
/* List is in the centre of the entire horizontal space. */
format_draw_absolute_centre(octx, available, ocx, ocy, &s[LEFT],
&s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
&s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
focus_start, focus_end, &frs);
break; break;
} }
@@ -795,16 +1002,36 @@ u_int
format_width(const char *expanded) format_width(const char *expanded)
{ {
const char *cp, *end; const char *cp, *end;
u_int width = 0; u_int n, width = 0;
struct utf8_data ud; struct utf8_data ud;
enum utf8_state more; enum utf8_state more;
cp = expanded; cp = expanded;
while (*cp != '\0') { while (*cp != '\0') {
if (cp[0] == '#' && cp[1] == '[') { if (*cp == '#') {
for (n = 1; cp[n] == '#'; n++)
/* nothing */;
if (cp[n] != '[') {
width += n;
cp += n;
continue;
}
width += (n / 2); /* one for each ## */
if ((n % 2) == 0) {
/*
* An even number of #s means that all #s are
* escaped, so not a style.
*/
width++; /* one for the [ */
cp += (n + 1);
continue;
}
cp += (n - 1); /* point to the [ */
end = format_skip(cp + 2, "]"); end = format_skip(cp + 2, "]");
if (end == NULL) if (end == NULL)
return 0; return (0);
cp = end + 1; cp = end + 1;
} else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) { } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
while (*++cp != '\0' && more == UTF8_MORE) while (*++cp != '\0' && more == UTF8_MORE)
@@ -822,19 +1049,57 @@ format_width(const char *expanded)
return (width); return (width);
} }
/* Trim on the left, taking #[] into account. */ /*
* Trim on the left, taking #[] into account. Note, we copy the whole set of
* unescaped #s, but only add their escaped size to width. This is because the
* format_draw function will actually do the escaping when it runs
*/
char * char *
format_trim_left(const char *expanded, u_int limit) format_trim_left(const char *expanded, u_int limit)
{ {
char *copy, *out; char *copy, *out;
const char *cp = expanded, *end; const char *cp = expanded, *end;
u_int width = 0; u_int even, n, width = 0;
struct utf8_data ud; struct utf8_data ud;
enum utf8_state more; enum utf8_state more;
out = copy = xmalloc(strlen(expanded) + 1); out = copy = xcalloc(1, strlen(expanded) + 1);
while (*cp != '\0') { while (*cp != '\0') {
if (cp[0] == '#' && cp[1] == '[') { if (width >= limit)
break;
if (*cp == '#') {
for (end = cp + 1; *end == '#'; end++)
/* nothing */;
n = end - cp;
if (*end != '[') {
if (n > limit - width)
n = limit - width;
memcpy(out, cp, n);
out += n;
width += n;
cp = end;
continue;
}
even = ((n % 2) == 0);
n /= 2;
if (n > limit - width)
n = limit - width;
width += n;
n *= 2;
memcpy(out, cp, n);
out += n;
if (even) {
if (width + 1 <= limit) {
*out++ = '[';
width++;
}
cp = end + 1;
continue;
}
cp = end - 1;
end = format_skip(cp + 2, "]"); end = format_skip(cp + 2, "]");
if (end == NULL) if (end == NULL)
break; break;
@@ -872,7 +1137,7 @@ format_trim_right(const char *expanded, u_int limit)
{ {
char *copy, *out; char *copy, *out;
const char *cp = expanded, *end; const char *cp = expanded, *end;
u_int width = 0, total_width, skip; u_int width = 0, total_width, skip, old_n, even, n;
struct utf8_data ud; struct utf8_data ud;
enum utf8_state more; enum utf8_state more;
@@ -881,12 +1146,64 @@ format_trim_right(const char *expanded, u_int limit)
return (xstrdup(expanded)); return (xstrdup(expanded));
skip = total_width - limit; skip = total_width - limit;
out = copy = xmalloc(strlen(expanded) + 1); out = copy = xcalloc(1, strlen(expanded) + 1);
while (*cp != '\0') { while (*cp != '\0') {
if (cp[0] == '#' && cp[1] == '[') { if (*cp == '#') {
for (end = cp + 1; *end == '#'; end++)
/* nothing */;
old_n = n = end - cp;
if (*end != '[') {
if (width <= skip) {
if (skip - width >= n)
n = 0;
else
n -= (skip - width);
}
if (n != 0) {
memcpy(out, cp, n);
out += n;
}
/*
* The width always increases by the full
* amount even if we can't copy anything yet.
*/
width += old_n;
cp = end;
continue;
}
even = ((n % 2) == 0);
n /= 2;
if (width <= skip) {
if (skip - width >= n)
n = 0;
else
n -= (skip - width);
}
if (n != 0) {
/*
* Copy the full amount because it hasn't been
* escaped yet.
*/
memcpy(out, cp, old_n);
out += old_n;
}
cp += old_n;
width += (old_n / 2) - even;
if (even) {
if (width > skip)
*out++ = '[';
width++;
continue;
}
cp = end - 1;
end = format_skip(cp + 2, "]"); end = format_skip(cp + 2, "]");
if (end == NULL) if (end == NULL) {
break; break;
}
memcpy(out, cp, end + 1 - cp); memcpy(out, cp, end + 1 - cp);
out += (end + 1 - cp); out += (end + 1 - cp);
cp = end + 1; cp = end + 1;

3844
format.c

File diff suppressed because it is too large Load Diff

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

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

8
fuzz/input-fuzzer.dict Normal file
View File

@@ -0,0 +1,8 @@
"\x1b["
"1000"
"2004"
"1049"
"38;2"
"100;"
"tmux;"
"rgb:00/00/00"

View File

@@ -0,0 +1,2 @@
[libfuzzer]
max_len = 512

389
grid-reader.c Normal file
View File

@@ -0,0 +1,389 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2020 Anindya Mukherjee <anindya49@hotmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "tmux.h"
#include <string.h>
/* Initialise virtual cursor. */
void
grid_reader_start(struct grid_reader *gr, struct grid *gd, u_int cx, u_int cy)
{
gr->gd = gd;
gr->cx = cx;
gr->cy = cy;
}
/* Get cursor position from reader. */
void
grid_reader_get_cursor(struct grid_reader *gr, u_int *cx, u_int *cy)
{
*cx = gr->cx;
*cy = gr->cy;
}
/* Get length of line containing the cursor. */
u_int
grid_reader_line_length(struct grid_reader *gr)
{
return (grid_line_length(gr->gd, gr->cy));
}
/* Move cursor forward one position. */
void
grid_reader_cursor_right(struct grid_reader *gr, int wrap, int all)
{
u_int px;
struct grid_cell gc;
if (all)
px = gr->gd->sx;
else
px = grid_reader_line_length(gr);
if (wrap && gr->cx >= px && gr->cy < gr->gd->hsize + gr->gd->sy - 1) {
grid_reader_cursor_start_of_line(gr, 0);
grid_reader_cursor_down(gr);
} else if (gr->cx < px) {
gr->cx++;
while (gr->cx < px) {
grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
if (~gc.flags & GRID_FLAG_PADDING)
break;
gr->cx++;
}
}
}
/* Move cursor back one position. */
void
grid_reader_cursor_left(struct grid_reader *gr, int wrap)
{
struct grid_cell gc;
while (gr->cx > 0) {
grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
if (~gc.flags & GRID_FLAG_PADDING)
break;
gr->cx--;
}
if (gr->cx == 0 && gr->cy > 0 &&
(wrap ||
grid_get_line(gr->gd, gr->cy - 1)->flags & GRID_LINE_WRAPPED)) {
grid_reader_cursor_up(gr);
grid_reader_cursor_end_of_line(gr, 0, 0);
} else if (gr->cx > 0)
gr->cx--;
}
/* Move cursor down one line. */
void
grid_reader_cursor_down(struct grid_reader *gr)
{
struct grid_cell gc;
if (gr->cy < gr->gd->hsize + gr->gd->sy - 1)
gr->cy++;
while (gr->cx > 0) {
grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
if (~gc.flags & GRID_FLAG_PADDING)
break;
gr->cx--;
}
}
/* Move cursor up one line. */
void
grid_reader_cursor_up(struct grid_reader *gr)
{
struct grid_cell gc;
if (gr->cy > 0)
gr->cy--;
while (gr->cx > 0) {
grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
if (~gc.flags & GRID_FLAG_PADDING)
break;
gr->cx--;
}
}
/* Move cursor to the start of the line. */
void
grid_reader_cursor_start_of_line(struct grid_reader *gr, int wrap)
{
if (wrap) {
while (gr->cy > 0 &&
grid_get_line(gr->gd, gr->cy - 1)->flags &
GRID_LINE_WRAPPED)
gr->cy--;
}
gr->cx = 0;
}
/* Move cursor to the end of the line. */
void
grid_reader_cursor_end_of_line(struct grid_reader *gr, int wrap, int all)
{
u_int yy;
if (wrap) {
yy = gr->gd->hsize + gr->gd->sy - 1;
while (gr->cy < yy && grid_get_line(gr->gd, gr->cy)->flags &
GRID_LINE_WRAPPED)
gr->cy++;
}
if (all)
gr->cx = gr->gd->sx;
else
gr->cx = grid_reader_line_length(gr);
}
/* Check if character under cursor is in set. */
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));
}
/* 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;
int expected = 0;
/* Do not break up wrapped words. */
if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED)
xx = gr->gd->sx - 1;
else
xx = grid_reader_line_length(gr);
yy = gr->gd->hsize + gr->gd->sy - 1;
/*
* If we started inside a word, skip over word characters. Then skip
* over separators till the next word.
*
* expected is initially set to 0 for the former and then 1 for the
* latter. It is finally set to 0 when the beginning of the next word is
* found.
*/
do {
while (gr->cx > xx ||
grid_reader_in_set(gr, separators) == expected) {
/* Move down if we are past the end of the line. */
if (gr->cx > xx) {
if (gr->cy == yy)
return;
grid_reader_cursor_start_of_line(gr, 0);
grid_reader_cursor_down(gr);
if (grid_get_line(gr->gd, gr->cy)->flags &
GRID_LINE_WRAPPED)
xx = gr->gd->sx - 1;
else
xx = grid_reader_line_length(gr);
} else
gr->cx++;
}
expected = !expected;
} while (expected == 1);
}
/* Move cursor to the end of the next word. */
void
grid_reader_cursor_next_word_end(struct grid_reader *gr, const char *separators)
{
u_int xx, yy;
int expected = 1;
/* Do not break up wrapped words. */
if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED)
xx = gr->gd->sx - 1;
else
xx = grid_reader_line_length(gr);
yy = gr->gd->hsize + gr->gd->sy - 1;
/*
* If we started on a separator, skip over separators. Then skip over
* word characters till the next separator.
*
* expected is initially set to 1 for the former and then 1 for the
* latter. It is finally set to 1 when the end of the next word is
* found.
*/
do {
while (gr->cx > xx ||
grid_reader_in_set(gr, separators) == expected) {
/* Move down if we are past the end of the line. */
if (gr->cx > xx) {
if (gr->cy == yy)
return;
grid_reader_cursor_start_of_line(gr, 0);
grid_reader_cursor_down(gr);
if (grid_get_line(gr->gd, gr->cy)->flags &
GRID_LINE_WRAPPED)
xx = gr->gd->sx - 1;
else
xx = grid_reader_line_length(gr);
} else
gr->cx++;
}
expected = !expected;
} while (expected == 0);
}
/* Move to the previous place where a word begins. */
void
grid_reader_cursor_previous_word(struct grid_reader *gr, const char *separators,
int already)
{
int oldx, oldy, r;
/* Move back to the previous word character. */
if (already || grid_reader_in_set(gr, separators)) {
for (;;) {
if (gr->cx > 0) {
gr->cx--;
if (!grid_reader_in_set(gr, separators))
break;
} else {
if (gr->cy == 0)
return;
grid_reader_cursor_up(gr);
grid_reader_cursor_end_of_line(gr, 0, 0);
/* Stop if separator at EOL. */
if (gr->cx > 0) {
oldx = gr->cx;
gr->cx--;
r = grid_reader_in_set(gr, separators);
gr->cx = oldx;
if (r)
break;
}
}
}
}
/* Move back to the beginning of this word. */
do {
oldx = gr->cx;
oldy = gr->cy;
if (gr->cx == 0) {
if (gr->cy == 0 ||
~grid_get_line(gr->gd, gr->cy - 1)->flags &
GRID_LINE_WRAPPED)
break;
grid_reader_cursor_up(gr);
grid_reader_cursor_end_of_line(gr, 0, 1);
}
if (gr->cx > 0)
gr->cx--;
} while (!grid_reader_in_set(gr, separators));
gr->cx = oldx;
gr->cy = oldy;
}
/* Jump forward to character. */
int
grid_reader_cursor_jump(struct grid_reader *gr, const struct utf8_data *jc)
{
struct grid_cell gc;
u_int px, py, xx, yy;
px = gr->cx;
yy = gr->gd->hsize + gr->gd->sy - 1;
for (py = gr->cy; py <= yy; py++) {
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) {
gr->cx = px;
gr->cy = py;
return 1;
}
px++;
}
if (py == yy ||
!(grid_get_line(gr->gd, py)->flags & GRID_LINE_WRAPPED))
return 0;
px = 0;
}
return 0;
}
/* Jump back to character. */
int
grid_reader_cursor_jump_back(struct grid_reader *gr, const struct utf8_data *jc)
{
struct grid_cell gc;
u_int px, py, xx;
xx = gr->cx + 1;
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) {
gr->cx = px - 1;
gr->cy = py - 1;
return 1;
}
}
if (py == 1 ||
!(grid_get_line(gr->gd, py - 2)->flags & GRID_LINE_WRAPPED))
return 0;
xx = grid_line_length(gr->gd, py - 2);
}
return 0;
}
/* Jump back to the first non-blank character of the line. */
void
grid_reader_cursor_back_to_indentation(struct grid_reader *gr)
{
struct grid_cell gc;
u_int px, py, xx, yy;
yy = gr->gd->hsize + gr->gd->sy - 1;
grid_reader_cursor_start_of_line(gr, 1);
for (py = gr->cy; py <= yy; py++) {
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 != ' ')
break;
}
if (~grid_get_line(gr->gd, py)->flags & GRID_LINE_WRAPPED)
break;
}
}

View File

@@ -45,6 +45,13 @@ grid_view_set_cell(struct grid *gd, u_int px, u_int py,
grid_set_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py), gc); grid_set_cell(gd, grid_view_x(gd, px), grid_view_y(gd, py), gc);
} }
/* Set padding. */
void
grid_view_set_padding(struct grid *gd, u_int px, u_int py)
{
grid_set_padding(gd, grid_view_x(gd, px), grid_view_y(gd, py));
}
/* Set cells. */ /* Set cells. */
void void
grid_view_set_cells(struct grid *gd, u_int px, u_int py, grid_view_set_cells(struct grid *gd, u_int px, u_int py,

181
grid.c
View File

@@ -40,16 +40,22 @@ const struct grid_cell grid_default_cell = {
{ { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0 { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0
}; };
/*
* Padding grid cell data. Padding cells are the only zero width cell that
* appears in the grid - because of this, they are always extended cells.
*/
static const struct grid_cell grid_padding_cell = {
{ { '!' }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 0
};
/* Cleared grid cell data. */ /* Cleared grid cell data. */
const struct grid_cell grid_cleared_cell = { static const struct grid_cell grid_cleared_cell = {
{ { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 0 { { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 0
}; };
static const struct grid_cell_entry grid_cleared_entry = { static const struct grid_cell_entry grid_cleared_entry = {
GRID_FLAG_CLEARED, { .data = { 0, 8, 8, ' ' } } GRID_FLAG_CLEARED, { .data = { 0, 8, 8, ' ' } }
}; };
static void grid_empty_line(struct grid *, u_int, u_int);
/* Store cell in entry. */ /* Store cell in entry. */
static void static void
grid_store_cell(struct grid_cell_entry *gce, const struct grid_cell *gc, grid_store_cell(struct grid_cell_entry *gce, const struct grid_cell *gc,
@@ -102,12 +108,13 @@ grid_get_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
} }
/* Set cell as extended. */ /* Set cell as extended. */
static struct grid_cell * static struct grid_extd_entry *
grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce, grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
const struct grid_cell *gc) const struct grid_cell *gc)
{ {
struct grid_cell *gcp; struct grid_extd_entry *gee;
int flags = (gc->flags & ~GRID_FLAG_CLEARED); int flags = (gc->flags & ~GRID_FLAG_CLEARED);
utf8_char uc;
if (~gce->flags & GRID_FLAG_EXTENDED) if (~gce->flags & GRID_FLAG_EXTENDED)
grid_get_extended_cell(gl, gce, flags); grid_get_extended_cell(gl, gce, flags);
@@ -115,10 +122,16 @@ grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
fatalx("offset too big"); fatalx("offset too big");
gl->flags |= GRID_LINE_EXTENDED; gl->flags |= GRID_LINE_EXTENDED;
gcp = &gl->extddata[gce->offset]; utf8_from_data(&gc->data, &uc);
memcpy(gcp, gc, sizeof *gcp);
gcp->flags = flags; gee = &gl->extddata[gce->offset];
return (gcp); gee->data = uc;
gee->attr = gc->attr;
gee->flags = flags;
gee->fg = gc->fg;
gee->bg = gc->bg;
gee->us = gc->us;
return (gee);
} }
/* Free up unused extended cells. */ /* Free up unused extended cells. */
@@ -126,9 +139,9 @@ static void
grid_compact_line(struct grid_line *gl) grid_compact_line(struct grid_line *gl)
{ {
int new_extdsize = 0; int new_extdsize = 0;
struct grid_cell *new_extddata; struct grid_extd_entry *new_extddata;
struct grid_cell_entry *gce; struct grid_cell_entry *gce;
struct grid_cell *gc; struct grid_extd_entry *gee;
u_int px, idx; u_int px, idx;
if (gl->extdsize == 0) if (gl->extdsize == 0)
@@ -152,8 +165,8 @@ grid_compact_line(struct grid_line *gl)
for (px = 0; px < gl->cellsize; px++) { for (px = 0; px < gl->cellsize; px++) {
gce = &gl->celldata[px]; gce = &gl->celldata[px];
if (gce->flags & GRID_FLAG_EXTENDED) { if (gce->flags & GRID_FLAG_EXTENDED) {
gc = &gl->extddata[gce->offset]; gee = &gl->extddata[gce->offset];
memcpy(&new_extddata[idx], gc, sizeof *gc); memcpy(&new_extddata[idx], gee, sizeof *gee);
gce->offset = idx++; gce->offset = idx++;
} }
} }
@@ -183,17 +196,14 @@ grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg)
{ {
struct grid_line *gl = &gd->linedata[py]; struct grid_line *gl = &gd->linedata[py];
struct grid_cell_entry *gce = &gl->celldata[px]; struct grid_cell_entry *gce = &gl->celldata[px];
struct grid_cell *gc; struct grid_extd_entry *gee;
memcpy(gce, &grid_cleared_entry, sizeof *gce); memcpy(gce, &grid_cleared_entry, sizeof *gce);
if (bg != 8) { if (bg != 8) {
if (bg & COLOUR_FLAG_RGB) { if (bg & COLOUR_FLAG_RGB) {
grid_get_extended_cell(gl, gce, gce->flags); grid_get_extended_cell(gl, gce, gce->flags);
gl->flags |= GRID_LINE_EXTENDED; gee = grid_extended_cell(gl, gce, &grid_cleared_cell);
gee->bg = bg;
gc = &gl->extddata[gce->offset];
memcpy(gc, &grid_cleared_cell, sizeof *gc);
gc->bg = bg;
} else { } else {
if (bg & COLOUR_FLAG_256) if (bg & COLOUR_FLAG_256)
gce->flags |= GRID_FLAG_BG256; gce->flags |= GRID_FLAG_BG256;
@@ -213,19 +223,28 @@ grid_check_y(struct grid *gd, const char *from, u_int py)
return (0); return (0);
} }
/* Check if two styles are (visibly) the same. */
int
grid_cells_look_equal(const struct grid_cell *gc1, const struct grid_cell *gc2)
{
if (gc1->fg != gc2->fg || gc1->bg != gc2->bg)
return (0);
if (gc1->attr != gc2->attr || gc1->flags != gc2->flags)
return (0);
return (1);
}
/* Compare grid cells. Return 1 if equal, 0 if not. */ /* Compare grid cells. Return 1 if equal, 0 if not. */
int int
grid_cells_equal(const struct grid_cell *gca, const struct grid_cell *gcb) grid_cells_equal(const struct grid_cell *gc1, const struct grid_cell *gc2)
{ {
if (gca->fg != gcb->fg || gca->bg != gcb->bg) if (!grid_cells_look_equal(gc1, gc2))
return (0); return (0);
if (gca->attr != gcb->attr || gca->flags != gcb->flags) if (gc1->data.width != gc2->data.width)
return (0); return (0);
if (gca->data.width != gcb->data.width) if (gc1->data.size != gc2->data.size)
return (0); return (0);
if (gca->data.size != gcb->data.size) return (memcmp(gc1->data.data, gc2->data.data, gc1->data.size) == 0);
return (0);
return (memcmp(gca->data.data, gcb->data.data, gca->data.size) == 0);
} }
/* Free one line. */ /* Free one line. */
@@ -258,7 +277,10 @@ grid_create(u_int sx, u_int sy, u_int hlimit)
gd->sx = sx; gd->sx = sx;
gd->sy = sy; gd->sy = sy;
gd->flags = GRID_HISTORY; if (hlimit != 0)
gd->flags = GRID_HISTORY;
else
gd->flags = 0;
gd->hscrolled = 0; gd->hscrolled = 0;
gd->hsize = 0; gd->hsize = 0;
@@ -348,6 +370,19 @@ grid_collect_history(struct grid *gd)
gd->hscrolled = gd->hsize; gd->hscrolled = gd->hsize;
} }
/* Remove lines from the bottom of the history. */
void
grid_remove_history(struct grid *gd, u_int ny)
{
u_int yy;
if (ny > gd->hsize)
return;
for (yy = 0; yy < ny; yy++)
grid_free_line(gd, gd->hsize + gd->sy - 1 - yy);
gd->hsize -= ny;
}
/* /*
* Scroll the entire visible screen, moving one line into the history. Just * Scroll the entire visible screen, moving one line into the history. Just
* allocate a new line at the bottom and move the history size indicator. * allocate a new line at the bottom and move the history size indicator.
@@ -428,7 +463,7 @@ grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg)
sx = gd->sx / 4; sx = gd->sx / 4;
else if (sx < gd->sx / 2) else if (sx < gd->sx / 2)
sx = gd->sx / 2; sx = gd->sx / 2;
else else if (gd->sx > sx)
sx = gd->sx; sx = gd->sx;
gl->celldata = xreallocarray(gl->celldata, sx, sizeof *gl->celldata); gl->celldata = xreallocarray(gl->celldata, sx, sizeof *gl->celldata);
@@ -438,7 +473,7 @@ grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg)
} }
/* Empty a line and set background colour if needed. */ /* Empty a line and set background colour if needed. */
static void void
grid_empty_line(struct grid *gd, u_int py, u_int bg) grid_empty_line(struct grid *gd, u_int py, u_int bg)
{ {
memset(&gd->linedata[py], 0, sizeof gd->linedata[py]); memset(&gd->linedata[py], 0, sizeof gd->linedata[py]);
@@ -460,12 +495,20 @@ static void
grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc) grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc)
{ {
struct grid_cell_entry *gce = &gl->celldata[px]; struct grid_cell_entry *gce = &gl->celldata[px];
struct grid_extd_entry *gee;
if (gce->flags & GRID_FLAG_EXTENDED) { if (gce->flags & GRID_FLAG_EXTENDED) {
if (gce->offset >= gl->extdsize) if (gce->offset >= gl->extdsize)
memcpy(gc, &grid_default_cell, sizeof *gc); memcpy(gc, &grid_default_cell, sizeof *gc);
else else {
memcpy(gc, &gl->extddata[gce->offset], sizeof *gc); gee = &gl->extddata[gce->offset];
gc->flags = gee->flags;
gc->attr = gee->attr;
gc->fg = gee->fg;
gc->bg = gee->bg;
gc->us = gee->us;
utf8_to_data(gee->data, &gc->data);
}
return; return;
} }
@@ -492,7 +535,7 @@ grid_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc)
grid_get_cell1(&gd->linedata[py], px, gc); grid_get_cell1(&gd->linedata[py], px, gc);
} }
/* Set cell at relative position. */ /* Set cell at position. */
void void
grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc)
{ {
@@ -515,14 +558,21 @@ grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc)
grid_store_cell(gce, gc, gc->data.data[0]); grid_store_cell(gce, gc, gc->data.data[0]);
} }
/* Set cells at relative position. */ /* Set padding at position. */
void
grid_set_padding(struct grid *gd, u_int px, u_int py)
{
grid_set_cell(gd, px, py, &grid_padding_cell);
}
/* Set cells at position. */
void void
grid_set_cells(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc, grid_set_cells(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc,
const char *s, size_t slen) const char *s, size_t slen)
{ {
struct grid_line *gl; struct grid_line *gl;
struct grid_cell_entry *gce; struct grid_cell_entry *gce;
struct grid_cell *gcp; struct grid_extd_entry *gee;
u_int i; u_int i;
if (grid_check_y(gd, __func__, py) != 0) if (grid_check_y(gd, __func__, py) != 0)
@@ -537,8 +587,8 @@ grid_set_cells(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc,
for (i = 0; i < slen; i++) { for (i = 0; i < slen; i++) {
gce = &gl->celldata[px + i]; gce = &gl->celldata[px + i];
if (grid_need_extended_cell(gce, gc)) { if (grid_need_extended_cell(gce, gc)) {
gcp = grid_extended_cell(gl, gce, gc); gee = grid_extended_cell(gl, gce, gc);
utf8_set(&gcp->data, s[i]); gee->data = utf8_build_one(s[i]);
} else } else
grid_store_cell(gce, gc, s[i]); grid_store_cell(gce, gc, s[i]);
} }
@@ -602,6 +652,8 @@ grid_clear_lines(struct grid *gd, u_int py, u_int ny, u_int bg)
grid_free_line(gd, yy); grid_free_line(gd, yy);
grid_empty_line(gd, yy, bg); grid_empty_line(gd, yy, bg);
} }
if (py != 0)
gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED;
} }
/* Move a group of lines. */ /* Move a group of lines. */
@@ -628,6 +680,8 @@ grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny, u_int bg)
continue; continue;
grid_free_line(gd, yy); grid_free_line(gd, yy);
} }
if (dy != 0)
gd->linedata[dy - 1].flags &= ~GRID_LINE_WRAPPED;
memmove(&gd->linedata[dy], &gd->linedata[py], memmove(&gd->linedata[dy], &gd->linedata[py],
ny * (sizeof *gd->linedata)); ny * (sizeof *gd->linedata));
@@ -640,6 +694,8 @@ grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny, u_int bg)
if (yy < dy || yy >= dy + ny) if (yy < dy || yy >= dy + ny)
grid_empty_line(gd, yy, bg); grid_empty_line(gd, yy, bg);
} }
if (py != 0 && (py < dy || py >= dy + ny))
gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED;
} }
/* Move a group of cells. */ /* Move a group of cells. */
@@ -755,15 +811,15 @@ grid_string_cells_bg(const struct grid_cell *gc, int *values)
case 8: case 8:
values[n++] = 49; values[n++] = 49;
break; break;
case 100: case 90:
case 101: case 91:
case 102: case 92:
case 103: case 93:
case 104: case 94:
case 105: case 95:
case 106: case 96:
case 107: case 97:
values[n++] = gc->bg - 10; values[n++] = gc->bg + 10;
break; break;
} }
} }
@@ -989,14 +1045,14 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
srcl->cellsize * sizeof *dstl->celldata); srcl->cellsize * sizeof *dstl->celldata);
} else } else
dstl->celldata = NULL; dstl->celldata = NULL;
if (srcl->extdsize != 0) { if (srcl->extdsize != 0) {
dstl->extdsize = srcl->extdsize; dstl->extdsize = srcl->extdsize;
dstl->extddata = xreallocarray(NULL, dstl->extdsize, dstl->extddata = xreallocarray(NULL, dstl->extdsize,
sizeof *dstl->extddata); sizeof *dstl->extddata);
memcpy(dstl->extddata, srcl->extddata, dstl->extdsize * memcpy(dstl->extddata, srcl->extddata, dstl->extdsize *
sizeof *dstl->extddata); sizeof *dstl->extddata);
} } else
dstl->extddata = NULL;
sy++; sy++;
dy++; dy++;
@@ -1220,7 +1276,7 @@ grid_reflow(struct grid *gd, u_int sx)
struct grid *target; struct grid *target;
struct grid_line *gl; struct grid_line *gl;
struct grid_cell gc; struct grid_cell gc;
u_int yy, width, i, at, first; u_int yy, width, i, at;
/* /*
* Create a destination grid. This is just used as a container for the * Create a destination grid. This is just used as a container for the
@@ -1237,13 +1293,12 @@ grid_reflow(struct grid *gd, u_int sx)
continue; continue;
/* /*
* Work out the width of this line. first is the width of the * Work out the width of this line. at is the point at which
* first character, at is the point at which the available * the available width is hit, and width is the full line
* width is hit, and width is the full line width. * width.
*/ */
first = at = width = 0; at = width = 0;
if (~gl->flags & GRID_LINE_EXTENDED) { if (~gl->flags & GRID_LINE_EXTENDED) {
first = 1;
width = gl->cellused; width = gl->cellused;
if (width > sx) if (width > sx)
at = sx; at = sx;
@@ -1252,8 +1307,6 @@ grid_reflow(struct grid *gd, u_int sx)
} else { } else {
for (i = 0; i < gl->cellused; i++) { for (i = 0; i < gl->cellused; i++) {
grid_get_cell1(gl, i, &gc); grid_get_cell1(gl, i, &gc);
if (i == 0)
first = gc.data.width;
if (at == 0 && width + gc.data.width > sx) if (at == 0 && width + gc.data.width > sx)
at = i; at = i;
width += gc.data.width; width += gc.data.width;
@@ -1261,10 +1314,10 @@ grid_reflow(struct grid *gd, u_int sx)
} }
/* /*
* If the line is exactly right or the first character is wider * If the line is exactly right, just move it across
* than the targe width, just move it across unchanged. * unchanged.
*/ */
if (width == sx || first > sx) { if (width == sx) {
grid_reflow_move(target, gl); grid_reflow_move(target, gl);
continue; continue;
} }
@@ -1327,17 +1380,13 @@ grid_wrap_position(struct grid *gd, u_int px, u_int py, u_int *wx, u_int *wy)
void void
grid_unwrap_position(struct grid *gd, u_int *px, u_int *py, u_int wx, u_int wy) grid_unwrap_position(struct grid *gd, u_int *px, u_int *py, u_int wx, u_int wy)
{ {
u_int yy, ax = 0, ay = 0; u_int yy, ay = 0;
for (yy = 0; yy < gd->hsize + gd->sy - 1; yy++) { for (yy = 0; yy < gd->hsize + gd->sy - 1; yy++) {
if (ay == wy) if (ay == wy)
break; break;
if (gd->linedata[yy].flags & GRID_LINE_WRAPPED) if (~gd->linedata[yy].flags & GRID_LINE_WRAPPED)
ax += gd->linedata[yy].cellused;
else {
ax = 0;
ay++; ay++;
}
} }
/* /*
@@ -1372,7 +1421,9 @@ grid_line_length(struct grid *gd, u_int py)
px = gd->sx; px = gd->sx;
while (px > 0) { while (px > 0) {
grid_get_cell(gd, px - 1, py, &gc); grid_get_cell(gd, px - 1, py, &gc);
if (gc.data.size != 1 || *gc.data.data != ' ') if ((gc.flags & GRID_FLAG_PADDING) ||
gc.data.size != 1 ||
*gc.data.data != ' ')
break; break;
px--; px--;
} }

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