843 Commits
2.9a ... 3.1c

Author SHA1 Message Date
Nicholas Marriott
25cae5d86f 3.1c. 2020-10-30 12:11:02 +00:00
nicm
d0ad34e94d 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 12:09:52 +00:00
Nicholas Marriott
a10c4c60cb Add to CHANGES. 2020-05-04 09:06:57 +01:00
Nicholas Marriott
846b99e0cb Portable does not need sys/queue.h. 2020-05-03 10:49:16 +01:00
Nicholas Marriott
6a33a12798 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 15:20:08 +01:00
Nicholas Marriott
d0fa520788 Update CHANGES. 2020-04-27 09:38:04 +01:00
Nicholas Marriott
646bfe403e Do not close stdout file descriptor in control mode since it will be needed for
printing the exit messages.
2020-04-27 09:36:30 +01:00
Nicholas Marriott
650d38962f tmux 3.1. 2020-04-24 09:57:49 +01:00
Nicholas Marriott
ccd7368cc5 Update CHANGES. 2020-04-22 12:59:50 +01:00
Nicholas Marriott
9077b212c3 job_run needs fewer arguments. 2020-04-06 16:14:09 +01:00
Nicholas Marriott
a4e19bcd80 Various fixes for copy mode from master. 2020-04-06 16:09:49 +01:00
nicm
bc36b473f1 Check previous line rather than an extra line, from Anindya Mukherjee. 2020-04-06 16:07:20 +01:00
nicm
10975961de Only search the visible part of the history when marking (highlighting)
search terms, much faster than searching the whole history.
2020-04-06 16:06:14 +01:00
nicm
8d2af4fb54 Add a 10 second timeout to prevent searches taking too much time, from
Anindya Mukherjee.
2020-04-06 16:04:51 +01:00
nicm
ac050b2583 Stop logging the entire command queue every time we add something,
spotted by tb & sthen.
2020-04-06 16:04:10 +01:00
nicm
3234017260 Add an argument to list-commands to show only a single command. 2020-04-06 16:03:39 +01:00
nicm
938ad5a98c Use new window and new pane as well for -P to new-session or new-window. 2020-04-06 16:03:33 +01:00
Nicholas Marriott
9f378a163f 3.1-rc4. 2020-04-01 10:09:49 +01:00
nicm
a5922546ac 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 10:09:03 +01:00
nicm
3476eccf48 Use a comparison to check for wrap and avoid an expensive modulus. 2020-04-01 10:08:54 +01:00
nicm
0dbf414578 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 10:08:39 +01:00
nicm
8dedccaa20 Add non-regex search variants to avoid the performance cost for people
with large histories or long lines.
2020-04-01 10:08:09 +01:00
Nicholas Marriott
e5fd85415d Update CHANGES. 2020-04-01 09:29:44 +01:00
Nicholas Marriott
af4b62d10b 3.1-rc3. 2020-03-18 07:55:33 +00:00
nicm
0c06409c9d 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-18 07:54:37 +00:00
nicm
617136c234 Turn off mouse mode 1003 as well as the rest when exiting. 2020-03-17 16:16:23 +00:00
nicm
f16085a362 Fix C-Space key string. 2020-03-17 16:16:09 +00:00
nicm
4ffbebedce Terminate the output buffer for control mode output - it is now used as
a string. GitHub issue 2114.
2020-03-17 16:14:51 +00:00
nicm
c0d74661b7 Do not attempt to close a NULL pane when failing to create a new one. 2020-03-17 16:14:25 +00:00
Nicholas Marriott
b21a9b1c4e getopt varies too much between platforms, and we already use compat/getopt.c
for Linux so just use it everywhere.
2020-03-17 16:14:12 +00:00
Nicholas Marriott
444e9f3c58 Bump 3.1-rc up to master. 2020-03-11 06:38:43 +00:00
Thomas Adam
59cb022c42 Merge branch 'obsd-master' 2020-03-07 12:01:25 +00:00
nicm
2991f4aad0 Use correct width of right marker so it doesn't draw over status right
when more than one character. Reported by Tyler Culp.
2020-03-07 10:58:32 +00:00
Thomas Adam
9e4d0b2b6d Merge branch 'obsd-master' 2020-03-06 18:01:26 +00:00
nicm
add75a06cd Update latest client for target session on switch-client. 2020-03-06 15:35:03 +00:00
Thomas Adam
ccd24c9cb2 Merge branch 'obsd-master' 2020-03-02 23:09:48 +00:00
nicm
8be179de46 Use current session for cwd of new sessions, not the new session which
doesn't have one yet. GitHub issue 2091.
2020-03-02 08:30:30 +00:00
nicm
f65b9c0d36 Change mouse selection so that after selecting a word, dragging selects
only words and similar for lines. From Anindya Mukherjee.
2020-02-24 09:53:59 +00:00
Nicholas Marriott
549b3599ef Update CHANGES. 2020-02-20 20:42:26 +00:00
Thomas Adam
4694afbed4 Merge branch 'obsd-master' 2020-02-20 10:01:29 +00:00
nicm
229be034fb Add selection_active format for when the selection is present but not
moving with the cursor, from Mark Kelly.
2020-02-20 07:34:57 +00:00
Thomas Adam
ba542e42b7 Merge branch 'obsd-master' 2020-02-19 16:01:27 +00:00
nicm
b20753f2a3 A few fixes to make modifier keys and dragging work - need to remove the
modifiers before checking for the dragging marker key, and apply them
before looking up the end key. Also fix key-to-string with modifiers for
special keys.
2020-02-19 14:25:00 +00:00
Nicholas Marriott
22e9cf04ca Add GitHub. 2020-02-19 06:01:54 +00:00
Nicholas Marriott
37919a6b6a This site is too stupid. 2020-02-17 12:20:53 +00:00
Nicholas Marriott
fdbc1116ef Add to FUNDING.yml. 2020-02-17 12:19:04 +00:00
Thomas Adam
0c6c8c4efc Merge branch 'obsd-master' 2020-02-15 16:01:25 +00:00
nicm
a1f6bd55b6 Add -a to list-keys to also list keys without notes with -N, suggested
by Shehu Dikko.
2020-02-15 15:08:08 +00:00
Thomas Adam
c391d50cbb Merge branch 'obsd-master' 2020-02-14 16:01:26 +00:00
nicm
58b47bf01b Fix top/bottom pane calculation with pane border status enabled,
reported by Stanislav Spassov.
2020-02-14 13:57:58 +00:00
Nicholas Marriott
9900ccd04e Change lock.yml options. 2020-02-14 11:43:12 +00:00
Nicholas Marriott
24cd726dae Add lock.yml file. 2020-02-14 11:40:32 +00:00
Thomas Adam
6c28d0dd06 Merge branch 'obsd-master' 2020-02-13 10:01:27 +00:00
nicm
f48b041cf2 Do not jump to next word end if already on a word end when selecting a
word. Fixes select-word with single character words and vi(1) keys. From
Mark Kelly.
2020-02-13 09:02:07 +00:00
Thomas Adam
a4d8437bc2 Merge branch 'obsd-master' 2020-02-11 08:01:30 +00:00
nicm
dc882adb2e Remove unused variables from Ben Boeckel, and a Pp from jmc. 2020-02-11 07:01:08 +00:00
Nicholas Marriott
470cba356d Merge branch '3.1-rc' 2020-02-07 16:43:41 +00:00
Nicholas Marriott
ae9ca620bd Remove duplicates, from Thomas Sattler. 2020-02-07 16:42:10 +00:00
Nicholas Marriott
400750bb26 Merge branch '3.1-rc' 2020-02-06 12:49:50 +00:00
nicm
096f0d35a6 Make list-keys description clearer in tmux.1 and remove an unused variable. 2020-02-06 12:49:02 +00:00
Thomas Adam
54553903de Merge branch 'obsd-master' 2020-02-05 14:01:26 +00:00
nicm
fb29242168 Make list-keys description clearer in tmux.1 and remove an unused variable. 2020-02-05 13:06:49 +00:00
Nicholas Marriott
c915cfc7e4 Merge branch '3.1-rc' 2020-02-04 07:46:59 +00:00
Nicholas Marriott
3ad4a7a571 Style nit in CHANGES. 2020-02-04 07:46:26 +00:00
Nicholas Marriott
47174f5130 Next is 3.2. 2020-02-04 07:45:29 +00:00
Nicholas Marriott
4822130b3c Merge branch '3.1-rc' 2020-02-04 07:44:49 +00:00
Nicholas Marriott
0bf153daa6 Update CONTRIBUTING.md 2020-02-04 07:43:34 +00:00
Nicholas Marriott
63a69fe085 3.1-rc. 2020-02-04 07:21:04 +00:00
Nicholas Marriott
43b36752ce Remove a duplicate entry. 2020-02-03 20:26:03 +00:00
Thomas Adam
19d5f4a0bd Merge branch 'obsd-master' 2020-02-03 14:01:25 +00:00
nicm
265164d251 Instead of passing titles through vis() which doubles backslashes, just
ignore any containing control characters or invalid UTF-8. GitHub issue 2070.
2020-02-03 13:46:27 +00:00
Thomas Adam
6f0241e645 Merge branch 'obsd-master' 2020-01-30 10:01:24 +00:00
nicm
87bcc0c7e0 Remove bind-key -c which doesn't do anything and is undocumented. 2020-01-30 08:02:25 +00:00
Thomas Adam
32be954bdd Merge branch 'obsd-master' 2020-01-29 18:01:24 +00:00
nicm
44dad918f8 Warn if a message type that is no longer used is received. 2020-01-29 16:22:32 +00:00
Thomas Adam
bc36700d05 Merge branch 'obsd-master' 2020-01-29 16:01:24 +00:00
nicm
531daba584 Do not send DA and DSR again if already have a response. 2020-01-29 15:07:49 +00:00
Thomas Adam
8b22da69b6 Merge branch 'obsd-master' 2020-01-29 10:01:25 +00:00
nicm
7a15d10bf4 Remove extra Pp (from jmc) and add a missing word. 2020-01-29 08:28:17 +00:00
Nicholas Marriott
7f3feb1896 Add to CHANGES. 2020-01-28 15:52:04 +00:00
Thomas Adam
7eada28f96 Merge branch 'obsd-master' 2020-01-28 14:01:25 +00:00
nicm
b905c5d455 If ALL clients are readonly, allow them to affect the size, suggested by Thomas Sattler. 2020-01-28 13:23:24 +00:00
nicm
e388702260 Ignore empty commands rather than adding them to the command list rather
than trying to skip them later, fixes problem reported by M Kelly.
2020-01-28 13:10:14 +00:00
Thomas Adam
60ab714451 Merge branch 'obsd-master' 2020-01-28 12:01:28 +00:00
nicm
a6129e9974 If we can identify the terminal as iTerm2 or as tmux, we can be sure
they support 256 and RGB colours, so set those flags too.
2020-01-28 11:39:51 +00:00
nicm
84995ae172 -V also needs to go in usage. 2020-01-28 11:31:31 +00:00
Nicholas Marriott
685eb381de Fix for version changes. 2020-01-28 11:28:30 +00:00
Thomas Adam
ee3d3db364 Merge branch 'obsd-master' 2020-01-28 11:17:08 +00:00
nicm
90e962fff8 Add support for the iTerm2 DSR 1337 sequence to get the terminal version. 2020-01-28 10:59:29 +00:00
nicm
f165221dc4 Reduce a difference with portable tmux by adding the -V flag and
#{version} format; on OpenBSD these just report the OpenBSD version.
2020-01-28 10:44:30 +00:00
nicm
32816eaebd Set up working directory before killing the existing pane on respawn. 2020-01-28 10:21:21 +00:00
Thomas Adam
7cdf5ee9bc Merge branch 'obsd-master' 2020-01-28 10:01:25 +00:00
nicm
24350879cd Add a define for flags meaning a client is not attached, and fix
unattached counter, reported by Thomas Sattler.
2020-01-28 08:06:11 +00:00
Thomas Adam
f3ea318a04 Merge branch 'obsd-master' 2020-01-27 10:01:27 +00:00
nicm
2c38e01b54 Expand description of start-server. 2020-01-27 09:04:47 +00:00
nicm
d0b8d036be Add support for adding a note to a key binding (with bind-key -N) and
use this to add descriptions to the default key bindings. A new -N flag
to list-keys shows key bindings with notes rather than the default
bind-key command used to create them. Change the default ? binding to
use this to show a readable summary of keys.

Also extend command-prompt to return the name of the key pressed and add
a default binding (/) to show the note for the next key pressed

Suggested by Alex Tremblay in GitHub issue 2000.
2020-01-27 08:53:13 +00:00
nicm
2e39b621c9 Change so that assignments may be specified alone - a command isn't
required. GitHub issue 2062.
2020-01-27 08:23:42 +00:00
Thomas Adam
0eb7b54731 Merge branch 'obsd-master' 2020-01-25 18:01:26 +00:00
Nicholas Marriott
74b424075c Use FNM_IGNORECASE if present, from Eric N Vander Weele in GitHub issue 2067. 2020-01-25 16:41:49 +00:00
nicm
9169ee0e87 Mention swap-window -d, GitHub issue 2068. 2020-01-25 16:40:32 +00:00
Nicholas Marriott
cdf138372c Add to CHANGES. 2020-01-14 16:02:22 +00:00
Thomas Adam
a01c9ffc6c Merge branch 'obsd-master' 2020-01-13 14:01:25 +00:00
nicm
da515570dc Stop handling DA and DSR after a second (they should be the first thing
sent) so this should be plenty.
2020-01-13 11:59:21 +00:00
Thomas Adam
8457f54edc Merge branch 'obsd-master' 2020-01-13 10:01:27 +00:00
nicm
835a6c0cf0 Be more specific in the DSR we are looking for so it doesn't get
confused with mouse sequences. Also set a flag and don't bother checking
for it if we have already seen it (same for DA), and don't check if we
never asked for it.
2020-01-13 08:12:53 +00:00
nicm
04eee2410d Treat plausible but invalid keys (like C-BSpace) as literal like any
other unrecognised string passed to send-keys. Reported by Anthony
Sottile in GitHub issue 2049.
2020-01-13 07:51:54 +00:00
Thomas Adam
3e701309a4 Merge branch 'obsd-master' 2020-01-13 00:01:24 +00:00
Thomas Adam
e9b1294331 Merge branch 'obsd-master' 2020-01-12 22:01:26 +00:00
nicm
381333c4a9 Detect iTerm2 and enable DECSLRM. 2020-01-12 22:00:20 +00:00
nicm
193e637de0 The terminal type was never as much use as I expected so remove it in
favour of a couple of flags for the features used (DECSLRM and DECFRA).
Also rename the flag for no xenl to be more obvious while here.
2020-01-12 21:07:07 +00:00
nicm
deb734c7f6 Loop over all DA features, don't skip the first. 2020-01-12 20:20:20 +00:00
Thomas Adam
61b075a263 Merge branch 'obsd-master' 2020-01-08 16:01:23 +00:00
nicm
36eb16ce7d Do not hang in format_trim_* on invalid UTF-8 characters. 2020-01-08 14:40:52 +00:00
Thomas Adam
ed16f51e26 Merge branch 'obsd-master' 2020-01-08 08:01:24 +00:00
nicm
6628e542b5 Add -Z to default switch-client command in tree mode, matches previous
behaviour.
2020-01-08 06:38:55 +00:00
Thomas Adam
507816b1d6 Merge branch 'obsd-master' 2020-01-05 22:01:24 +00:00
nicm
73b8c2ef3c Common function to free key bindings. 2020-01-05 20:39:25 +00:00
Thomas Adam
36169d8a68 Merge branch 'obsd-master' 2020-01-05 14:01:23 +00:00
nicm
7c6c66cc3c Send errors to stdout in control mode so they don't get reordered with
other output, reported by George Nachman in GitHub issue 2048.
2020-01-05 12:51:43 +00:00
Thomas Adam
6d3d47c25b Merge branch 'obsd-master' 2020-01-04 20:01:26 +00:00
nicm
1870cc70ef Add ~ to quoted characters for %%%, reported by tb@. 2020-01-04 18:01:56 +00:00
Thomas Adam
6b0091e185 Merge branch 'obsd-master' 2020-01-02 14:01:26 +00:00
nicm
a770a3bf7e Add CMD_FIND_DEFAULT_MARKED to join-pane like move-pane, from
davidegirardi in GitHub issue 2046.
2020-01-02 13:44:17 +00:00
Thomas Adam
7110226b96 Merge branch 'obsd-master' 2020-01-02 00:01:29 +00:00
nicm
ac85a3e0d3 Document client exit messages. 2020-01-01 22:12:05 +00:00
nicm
9cc603cbad Fix format expansion in window names, reported by Suraj N Kurapati. 2020-01-01 21:51:33 +00:00
Thomas Adam
566ab9aa28 Merge branch 'obsd-master' 2019-12-30 22:01:25 +00:00
nicm
206d878127 Do not let readonly clients limit the size, suggested by Max Barraclough
in GitHub issue 2042.
2019-12-30 21:24:55 +00:00
Thomas Adam
47d06cb023 Merge branch 'obsd-master' 2019-12-27 20:01:24 +00:00
nicm
4ea07716de Support regex search in copy mode, from Anindya Mukherjee in GitHub
issue 2038.
2019-12-27 18:42:49 +00:00
Thomas Adam
0aa6c6f647 Merge branch 'obsd-master' 2019-12-26 16:01:25 +00:00
nicm
88ee5b1a73 Pass correct value into iterator callback for time formats. 2019-12-26 14:48:29 +00:00
Thomas Adam
b931bbb319 Merge branch 'obsd-master' 2019-12-26 12:01:26 +00:00
nicm
817d199cbb Add a number of new formats to inspect what sessions and clients a
window is present or active in. From Tyler Culp in GitHub issue 2034.
2019-12-26 11:04:58 +00:00
Thomas Adam
67d2408279 Merge branch 'obsd-master' 2019-12-24 12:01:25 +00:00
nicm
07e37479c2 Fix name of option, GitHub issue 2030. 2019-12-24 09:57:11 +00:00
Thomas Adam
a6b1cbba02 Merge branch 'obsd-master' 2019-12-21 18:01:24 +00:00
tim
5cd00eda0b Restore source-file -q behaviour, broken in r1.42; OK nicm@ 2019-12-21 17:30:48 +00:00
Thomas Adam
745233d6a1 Merge branch 'obsd-master' 2019-12-19 10:01:26 +00:00
nicm
1764f66b7d When adding a list with multiple commands to the queue, the next item to
insert after needs to be the last one added, not the first. Reported by
Jason Kim in GitHub issue 2023.
2019-12-19 09:22:33 +00:00
Nicholas Marriott
54efe33799 Add back utempter code, reported by Peter Schellenbach. 2019-12-18 15:58:06 +00:00
Thomas Adam
d0cd68d5e4 Merge branch 'obsd-master' 2019-12-18 08:01:23 +00:00
nicm
ef54a08080 Do not rely on errno after glob(3) fails. 2019-12-18 07:48:56 +00:00
Thomas Adam
4223293ed8 Merge branch 'obsd-master' 2019-12-17 12:01:24 +00:00
nicm
f8cb759bdb Use the message that has already been built rather than the va_list. 2019-12-17 11:43:23 +00:00
Nicholas Marriott
3879509426 Define FNM_CASEFOLD to 0 for AIX, from Eric N Vander Weele. 2019-12-16 21:34:36 +00:00
Nicholas Marriott
479d411dda Remove imsg.h. 2019-12-16 20:01:26 +00:00
Thomas Adam
52b6ca5706 Merge branch 'obsd-master' 2019-12-16 18:01:31 +00:00
nicm
1bdd4828bd If /dev/fd/X is a symlink and realpath() expands symlinks, /dev/fd/X
ends up pointing to the wrong place before it is passed to the client.
The path is only used internally so there is no real need for
realpath(), remove it and move the get_path function to file.c where all
the callers are.
2019-12-16 16:39:03 +00:00
nicm
b4520aaf2c Need to include message size in the maximum buffer calculation. 2019-12-16 16:09:28 +00:00
nicm
eaa58d28dc Instead of using large buffers in imsgs, add the data or path onto the end. 2019-12-16 15:48:50 +00:00
Nicholas Marriott
e6b02dec19 Add to CHANGES. 2019-12-13 11:31:53 +00:00
Thomas Adam
00723f1f5c Merge branch 'obsd-master' 2019-12-13 10:01:24 +00:00
nicm
21f9b39f06 Show UTF-8 in choose-buffer mode. From KOIE Hidetaka. 2019-12-13 09:15:13 +00:00
Thomas Adam
e24e9867ec Merge branch 'obsd-master' 2019-12-13 08:01:24 +00:00
nicm
6ce943f4d9 Need to check in the error callback also. 2019-12-13 07:00:22 +00:00
nicm
828001ecc5 Do not spin waiting for exit, instead check in the write callback. 2019-12-13 06:55:12 +00:00
Thomas Adam
58908b045b Merge branch 'obsd-master' 2019-12-12 15:38:33 +00:00
nicm
dcf41ec927 Do not crash in tree modes if the pane is only 1 in width, reported by
KOIE Hidetaka in GitHub issue 2015.
2019-12-12 15:03:13 +00:00
nicm
2b2b193791 Add function to the right file. 2019-12-12 15:01:54 +00:00
Thomas Adam
7922f4ee7b Merge branch 'obsd-master' 2019-12-12 14:33:47 +00:00
nicm
5134666702 Change source-file to use new file code which allows it to read from
stdin.
2019-12-12 12:49:36 +00:00
nicm
268f2b047a Do not check if client is dead if it is NULL. 2019-12-12 11:51:32 +00:00
nicm
c284ebe0ad Rewrite the code for reading and writing files. Now, if the client is
not attached, the server process asks it to open the file, similar to
how works for stdin, stdout, stderr. This makes special files like
/dev/fd/X work (used by some shells). stdin, stdout and stderr and
control mode are now just special cases of the same mechanism. This will
also make it easier to use for other commands that read files such as
source-file.
2019-12-12 11:39:56 +00:00
Thomas Adam
0d99519c3d Merge branch 'obsd-master' 2019-12-11 20:01:25 +00:00
nicm
64fb7e472a Tweak previous to check the wrapped flag and stop if not set. 2019-12-11 18:30:29 +00:00
nicm
ab630f72ed Allow search across wrapped lines and fix some inconsistencies in how th
position is represented, GitHub issue 2014 from Anindya Mukherjee.
2019-12-11 18:23:34 +00:00
Thomas Adam
96abf400a5 Merge branch 'obsd-master' 2019-12-11 14:01:25 +00:00
nicm
f733d3f3eb Do not set cursor colour to default unless it has been changed, GitHub
issue 2013.
2019-12-11 12:13:37 +00:00
Nicholas Marriott
6aeb679066 Add to CHANGES. 2019-12-10 19:02:27 +00:00
Nicholas Marriott
15d7e564dd Add ~/.config/tmux/tmux.conf to the default search path for configuration files
(in Makefile.am, so portable tmux only).
2019-12-10 16:31:01 +00:00
Thomas Adam
5b2048fbb9 Merge branch 'obsd-master' 2019-12-10 16:01:25 +00:00
nicm
55eb3e4773 Make TMUX_CONF a list of files and expand leading $FOO or ~. 2019-12-10 14:22:15 +00:00
Nicholas Marriott
92ecd611f6 Check each _PATH_* define individually (Solaris has paths.h but not all of the
defines). From Eric N Vander Weele.
2019-12-03 18:53:23 +00:00
Thomas Adam
875139f5fa Merge branch 'obsd-master' 2019-12-03 12:01:26 +00:00
nicm
7826d40ff9 Style nits in function arguments. 2019-12-03 10:47:22 +00:00
Thomas Adam
edf96b06a5 Merge branch 'obsd-master' 2019-12-02 20:01:24 +00:00
nicm
ec1b8e5f05 Remove client menu, I don't think it adds anything. 2019-12-02 19:25:52 +00:00
Nicholas Marriott
2173365f4f Fix keys in CHANGES. 2019-12-01 21:15:21 +00:00
Nicholas Marriott
7836779e21 Merge branch '3.0a-rc' 2019-12-01 09:00:46 +00:00
Nicholas Marriott
25ae028695 Add to CHANGES. 2019-12-01 08:53:10 +00:00
nicm
1b0cc4503e REG_STARTEND is not portable, but it turns out we don't actually need
it. From Evan Green, GitHub issue 1982.
2019-12-01 08:52:47 +00:00
Nicholas Marriott
a52fe9cf7f Add to CHANGES. 2019-12-01 08:50:45 +00:00
Thomas Adam
866b053f25 Merge branch 'obsd-master' 2019-11-29 18:01:24 +00:00
nicm
48897fbc44 Fix respawn-pane/window if default-command is set, reported by Janos Barbero. 2019-11-29 17:21:32 +00:00
nicm
67d995d100 If a window appears in only one attached session, there is no point in
worrying about which is the latest client (there is only one).
2019-11-29 16:04:07 +00:00
nicm
ad98fad9a3 Do not crash when a format doesn't exist, reported by Thomas Sattler. 2019-11-28 22:23:40 +00:00
Thomas Adam
34084fe666 Merge branch 'obsd-master' 2019-11-28 22:01:24 +00:00
nicm
c5d74b1deb Do not crash when a format doesn't exist, reported by Thomas Sattler. 2019-11-28 21:18:38 +00:00
Nicholas Marriott
3bb11ec484 Revert "add missing definition"
This reverts commit 743939ec84.
2019-11-28 14:36:32 +00:00
Thomas Adam
743939ec84 add missing definition 2019-11-28 12:31:43 +00:00
Nicholas Marriott
e00730d149 Fix bad merge. 2019-11-28 12:30:43 +00:00
nicm
640149337f Missing after-kill-pane option. 2019-11-28 12:22:32 +00:00
Thomas Adam
5f5f029e3b Merge branch 'obsd-master' 2019-11-28 12:18:41 +00:00
nicm
fa409194d3 Missing after-kill-pane option. 2019-11-28 10:55:45 +00:00
nicm
08e2828592 Parse out DA features. 2019-11-28 10:17:22 +00:00
nicm
bc5881c4d2 Long lines and spacing fixes. 2019-11-28 09:56:25 +00:00
nicm
c416fe0da4 Add xrecallocarray. 2019-11-28 09:51:58 +00:00
nicm
9ea05b2fb3 Bump the escape sequence timeout to five seconds to allow for longer
legitimate sequences.
2019-11-28 09:50:09 +00:00
nicm
2349b1dbef Make a best effort to set xpixel and ypixel for each pane and add
formats for them.
2019-11-28 09:45:15 +00:00
nicm
067604bf8c Store xpixel/ypixel from TIOCGWINSZ and add formats. 2019-11-28 09:05:34 +00:00
nicm
7fb8eec8f1 status-left and status-right need push-default also, reported by Eric
Pruitt in GitHub issue 1989.
2019-11-28 08:38:04 +00:00
Thomas Adam
c13838436e Merge branch 'obsd-master' 2019-11-27 22:01:24 +00:00
Nicholas Marriott
7a30e6b941 Merge branch '3.0-rc' into 3.0a-rc 2019-11-27 20:55:19 +00:00
nicm
eb4d60b1ce REG_STARTEND is not portable, but it turns out we don't actually need
it. From Evan Green, GitHub issue 1982.
2019-11-27 20:54:30 +00:00
Nicholas Marriott
0134574a67 3.0a. 2019-11-27 20:49:59 +00:00
Nicholas Marriott
c942f11ba8 Use a malloc'd buffer for lsk since commands can be very long, from Gregory Pakosz. 2019-11-27 20:48:30 +00:00
nicm
5afe7eb850 Do not lazily use BUFSIZ for "I don't care what size" when building
strings because it is only guaranteed to be 256 bytes and even the
default 1024 is not always enough. Reported by Gregory Pakosz.
2019-11-27 20:48:03 +00:00
nicm
b2fd161b07 Do not crash trying to fix layout size if only one cell, from Azat Khuzhin. 2019-11-27 20:47:27 +00:00
Thomas Adam
d0655f321f Merge branch 'obsd-master' 2019-11-26 16:01:25 +00:00
nicm
fef8ee23c0 Add default # and * binding with vi(1) keys. 2019-11-26 15:35:56 +00:00
Nicholas Marriott
c16faa4fed Fixes to CHANGES. 2019-11-26 14:12:05 +00:00
Nicholas Marriott
bbcb199174 3.0 version. 2019-11-26 13:16:51 +00:00
nicm
47c0405b33 Some old tmux versions can sometimes generate layout strings which have
the incorrect size for the top cell. Previously tmux didn't care but now
that panes can be partly hidden, the size matters and is checked more
strictly. So add some code to fix up the most common problem and a
sanity check to reject layouts with any other size problems. Reported by
Aleksandrs Ledovskis in GitHub issue 1930.
2019-11-26 13:14:00 +00:00
Thomas Adam
f378a0b24d Merge branch 'obsd-master' 2019-11-26 00:01:25 +00:00
nicm
58f870ef6e Don't use motion flag uninitialized. 2019-11-25 22:38:36 +00:00
Thomas Adam
eaf526b1ea Merge branch 'obsd-master' 2019-11-25 22:01:56 +00:00
nicm
87a11a9214 Fix a warning in previous. 2019-11-25 20:43:32 +00:00
nicm
c2fde58701 Do not clear search marks on cursor movement with vi(1) keys, from Eric
Pruitt in GitHub issue 1985.
2019-11-25 20:42:18 +00:00
Thomas Adam
daa93b3fdc Merge branch 'obsd-master' 2019-11-25 16:01:27 +00:00
nicm
1ebd8c1234 Add p format modifier for padding to width. 2019-11-25 15:04:15 +00:00
nicm
5d0504ee11 Allow multiple substitutions in a single format. 2019-11-25 15:02:48 +00:00
Thomas Adam
81d4f95c2f Merge branch 'obsd-master' 2019-11-24 20:01:24 +00:00
nicm
20c1f1aec6 Only substitute patterns starting with ^ once. 2019-11-24 18:37:23 +00:00
Thomas Adam
82d0d85675 Merge branch 'obsd-master' 2019-11-20 12:01:23 +00:00
nicm
3c312a9150 Do not check the client readonly flag when there is no client, GitHub issue 1980. 2019-11-20 11:42:51 +00:00
Nicholas Marriott
0ed96c4609 Update CHANGES. 2019-11-18 12:56:24 +00:00
Thomas Adam
0d3f306c8e Merge branch 'obsd-master' 2019-11-18 10:01:25 +00:00
nicm
350a434939 Add -f for full size to join-pane (like split-window), from Theo Buehler. 2019-11-18 09:43:31 +00:00
nicm
4bc445f080 Keep modifiers on backspace when translating it. 2019-11-18 09:42:09 +00:00
Thomas Adam
dbdff241b2 Merge branch 'obsd-master' 2019-11-15 12:01:27 +00:00
nicm
5dfe9db788 Do not add path if it is NULL, duh. 2019-11-15 11:21:32 +00:00
nicm
f3dc38dcae Handle OSC 7 (a VTE extension) and put the result in a new format (pane_path). 2019-11-15 11:16:53 +00:00
Thomas Adam
48cbbb8757 Merge branch 'obsd-master' 2019-11-14 18:01:24 +00:00
nicm
1f966c495c Change window-size default from smallest to latest. 2019-11-14 16:23:23 +00:00
Thomas Adam
b67fd8f472 Merge branch 'obsd-master' 2019-11-14 16:01:25 +00:00
nicm
16b7719418 Fix parsing of DA with only one argument in the response and add 65 for VT520. 2019-11-14 15:37:19 +00:00
Thomas Adam
518a687886 Merge branch 'obsd-master' 2019-11-14 10:01:29 +00:00
nicm
2dbf062a89 Change new-session -A without a session name (that is, no -s option
also) to attach to the best existing session like attach-session rather
than creating a new one.
2019-11-14 08:00:30 +00:00
nicm
eb399e64d5 CUB and CUF are also limited by the margins so use CUP instead when
margins are enabled (we already do this for linefeed).
2019-11-14 07:56:32 +00:00
nicm
08b07b1a08 Add an option to set the key sent by backspace for those whose system
uses ^H rather than ^?. GitHub issue 1969.
2019-11-14 07:55:01 +00:00
Thomas Adam
eb215d3d3f Merge branch 'obsd-master' 2019-11-07 08:01:26 +00:00
nicm
c225262e13 Add -F flag to send-keys to expand formats in search-backward and
forward copy mode commands, this makes it easier to use the cursor_word
and cursor_line formats. From Anindya Mukherjee in GitHub issue 1964.
2019-11-07 07:11:25 +00:00
Thomas Adam
4408df1e8a Merge branch 'obsd-master' 2019-11-01 22:01:24 +00:00
nicm
bad95db878 Limit lazy resize to panes in attached sessions only - those in
unattached are likely to have been resized by something like
split-window where the user probably wants the resize to happen
immediately. GitHub issue 1963.
2019-11-01 20:26:21 +00:00
Thomas Adam
4fc8741794 Merge branch 'obsd-master' 2019-11-01 10:01:25 +00:00
nicm
d9c95c900c Handle the various different forms of rgb colour strings. 2019-11-01 09:09:53 +00:00
Nicholas Marriott
a1006db91b Create FUNDING.yml 2019-10-29 16:59:15 +00:00
Thomas Adam
57948a0dfc Merge branch 'obsd-master' 2019-10-28 10:01:25 +00:00
nicm
ccdebead79 Start with empty rather than NULL window name to avoid NULL printf if
window_name is evaluated early. Reported by Mikolaj Kucharski.
2019-10-28 09:07:59 +00:00
Thomas Adam
4e5f80dc62 Merge branch 'obsd-master' 2019-10-23 16:01:25 +01:00
nicm
6700018ce5 Document Any key, from Jason Felice. 2019-10-23 14:10:13 +00:00
Thomas Adam
b5de0a20d8 Merge branch 'obsd-master' 2019-10-23 10:01:27 +01:00
nicm
f7fb5df543 Use the existing code in format.c to add foramts for word and line at
cursor position in copy mode, from Anindya Mukherjee.
2019-10-23 07:42:05 +00:00
Thomas Adam
004ee66227 Merge branch 'obsd-master' 2019-10-19 22:01:25 +01:00
nicm
56e5067c46 Add formats for cursor and selection position in copy mode, from Jason Felice. 2019-10-19 19:20:14 +00:00
Thomas Adam
1b96902d73 Merge branch 'obsd-master' 2019-10-19 16:01:24 +01:00
nicm
2cb268d51b Do not crash trying to fix layout size if only one cell, from Azat Khuzhin. 2019-10-19 12:40:42 +00:00
Thomas Adam
fb7ce5b5d5 Merge branch 'obsd-master' 2019-10-15 10:01:28 +01:00
nicm
9fd62efcf0 Rewrite options_array_set to be clearer and remove a spurious warning
with newer GCC. From Ben Boeckel.
2019-10-15 08:30:36 +00:00
nicm
0c5e9c6efa Add support for percentage sizes for resize-pane ("-x 10%"). Also change
split-window and join-pane -l to accept similar percentages and
deprecate -p. From Anindya Mukherjee.
2019-10-15 08:25:37 +00:00
Thomas Adam
eb57cbcc29 Merge branch 'obsd-master' 2019-10-14 12:01:26 +01:00
nicm
b598bbcc2e Do not crash with pane_current_command if the pane is newly created and
has no shell set, from Thomas Adam.
2019-10-14 09:24:06 +00:00
nicm
f18cd5b19c Turn automatic-rename back on if the rename escape sequence is used with
an empty name, GitHub issue 1921.
2019-10-14 09:19:40 +00:00
nicm
bbe8ebf9c2 Some old tmux versions can sometimes generate layout strings which have
the incorrect size for the top cell. Previously tmux didn't care but now
that panes can be partly hidden, the size matters and is checked more
strictly. So add some code to fix up the most common problem and a
sanity check to reject layouts with any other size problems. Reported by
Aleksandrs Ledovskis in GitHub issue 1930.
2019-10-14 09:16:48 +00:00
nicm
68d59a16ce Memory leaks, from Igor Wong in GitHub issue 1934. 2019-10-14 08:38:07 +00:00
Thomas Adam
7323ffeef2 Merge branch 'obsd-master' 2019-10-07 10:01:24 +01:00
nicm
4e2cc0ae2a Fix respawn-pane/window if default-command is set, reported by Janos Barbero. 2019-10-07 07:14:07 +00:00
Thomas Adam
341b330a04 Merge branch 'obsd-master' 2019-10-03 14:01:23 +01:00
Thomas Adam
eeedb43ae8 Merge branch 'obsd-master' 2019-10-03 12:01:25 +01:00
nicm
02253d1e5c Use a malloc'd buffer for lsk since commands can be very long, from Gregory Pakosz. 2019-10-03 10:39:08 +00:00
nicm
f4c7141f5d Do not lazily use BUFSIZ for "I don't care what size" when building
strings because it is only guaranteed to be 256 bytes and even the
default 1024 is not always enough. Reported by Gregory Pakosz.
2019-10-03 10:24:05 +00:00
Thomas Adam
8cf21feefd Merge branch 'obsd-master' 2019-09-25 22:01:24 +01:00
nicm
bbd1032a2a Style and line length nits. 2019-09-25 19:05:59 +00:00
nicm
f0712a7569 Do not set uninitialized signal mask when creating an empty pane. 2019-09-25 15:56:53 +01:00
nicm
9d1cecea8e Up and Down are already used, use < and > instead. 2019-09-25 15:56:39 +01:00
nicm
e6995196f2 Change menu key bindings to Up and Down and also close it on any mouse
press if opened by key.
2019-09-25 15:56:30 +01:00
nicm
a74e37d32d Mouse formats don't work in copy mode so don't try to use them. 2019-09-25 15:54:52 +01:00
Thomas Adam
d89510e1aa Merge branch 'obsd-master' 2019-09-25 00:01:25 +01:00
nicm
e3359f8349 Some minor performance improvements - most notably, don't search the
input state table if the next character matches the same state as before.
2019-09-24 20:44:58 +00:00
Thomas Adam
2534aa4d2d Merge branch 'obsd-master' 2019-09-24 18:01:24 +01:00
nicm
e8adcae0f2 Couple of bits of minor cleanup. 2019-09-24 15:52:14 +00:00
nicm
48c684cbc2 Mouse formats don't work in copy mode so don't try to use them. 2019-09-24 14:50:08 +00:00
nicm
6f8f4bb206 Make select-pane -P set window-active-style also to match previous
behaviour, reported by Thomas Sattler.
2019-09-24 12:20:25 +01:00
Thomas Adam
cb1a626692 Merge branch 'obsd-master' 2019-09-24 12:01:23 +01:00
nicm
232050830b Make select-pane -P set window-active-style also to match previous
behaviour, reported by Thomas Sattler.
2019-09-24 09:58:58 +00:00
Thomas Adam
dd254b90d7 Merge branch 'obsd-master' 2019-09-23 18:01:24 +01:00
nicm
77deef733b Use the correct size for new windows when window-size is latest,
reported by Vamsi Krishna Avula in GitHub issue 1917.
2019-09-23 15:41:11 +00:00
Thomas Adam
24ab1bc714 Merge branch 'obsd-master' 2019-09-19 12:01:30 +01:00
nicm
647887b794 Add a "latest" window-size option which tries to size windows based on
the most recently used client. From Tommie Gannert in GitHub issue 1869
based on earlier changes from me.
2019-09-19 09:02:30 +00:00
nicm
d018477359 Do not use bright when emulating 256 colours on an 8 colour terminal
because it is also bold on some terminals. GitHub issue 1914.
2019-09-19 08:56:37 +00:00
Thomas Adam
827913102e Merge branch 'obsd-master' 2019-09-18 18:01:23 +01:00
nicm
1ee40307b5 Up and Down are already used, use < and > instead. 2019-09-18 15:09:05 +00:00
Thomas Adam
d30b612809 Merge branch 'obsd-master' 2019-09-18 14:01:24 +01:00
nicm
697f938355 Do not set uninitialized signal mask when creating an empty pane. 2019-09-18 11:37:58 +00:00
Thomas Adam
5ae2d421fb Merge branch 'obsd-master' 2019-09-16 16:01:23 +01:00
nicm
83be3afc54 Change menu key bindings to Up and Down and also close it on any mouse
press if opened by key.
2019-09-16 13:27:14 +00:00
Nicholas Marriott
c739772436 3.0-rc5. 2019-09-16 09:01:56 +01:00
Nicholas Marriott
7a1abd66e7 These are in 3.0 now. 2019-09-16 09:01:27 +01:00
Nicholas Marriott
d70d24d360 Merge branch '3.0-rc' 2019-09-16 09:01:09 +01:00
Nicholas Marriott
f27cac585c Add to CHANGES. 2019-09-16 09:00:56 +01:00
nicm
846d57e1b8 Make client exit if pane where input is going is closed. 2019-09-16 09:00:08 +01:00
Nicholas Marriott
54670d898f Missing headers from compat/asprintf.c, from cyyever at outlook dot com. 2019-09-16 08:59:57 +01:00
nicm
c45b255a88 It is not longer necessary to double-escape ; in %%%, problem reported
by Theo Buehler.
2019-09-16 08:59:49 +01:00
nicm
5e4f371408 Fix swap-window -d to work as intended, GitHub issue 1879 from Sam Stuewe. 2019-09-16 08:59:36 +01:00
nicm
7e4439beb7 Remove check for same size - size has already been changed so this
breaks reflow.
2019-09-16 08:58:35 +01:00
Nicholas Marriott
74f150670a In the right place. 2019-09-16 08:57:56 +01:00
Nicholas Marriott
3c355ec3b0 Add to CHANGES. 2019-09-16 08:57:22 +01:00
Thomas Adam
d346d692eb Merge branch 'obsd-master' 2019-09-16 00:01:25 +01:00
nicm
63e07b245f Add push-default and pop-default in styles to change the default colours
and attributes and use them to restore the previous behaviour of
window-status-style being the default for window-status-format in the
status line. From John Drouhard in GitHub issue 1912.
2019-09-15 21:42:57 +00:00
Thomas Adam
a5e36a4bd6 Merge branch 'obsd-master' 2019-09-13 04:01:25 +01:00
Thomas Adam
658ecb0777 Merge branch 'obsd-master' 2019-09-11 16:48:58 +01:00
nicm
a23ce1b45f Add window_marked_flag, GitHub issue 1887. 2019-09-11 06:43:17 +00:00
nicm
0feae4d8ae Make client exit if pane where input is going is closed. 2019-09-10 19:35:34 +00:00
nicm
4b7e97ba53 Set up format tree for %if, GitHub issue 1896. 2019-09-10 07:50:33 +00:00
nicm
b6b7486423 Clarify server options slightly. 2019-09-09 11:47:25 +00:00
Thomas Adam
2e90841f2e Merge branch 'obsd-master' 2019-09-09 12:02:32 +01:00
nicm
b31515fec3 Add cursor-down-and-cancel, from Mark Kelly. 2019-09-09 08:01:21 +00:00
Nicholas Marriott
9a476c5f29 Fix "make ctags", GitHub issue 1888. 2019-09-08 21:42:26 +01:00
Nicholas Marriott
5423bf6db8 Missing headers from compat/asprintf.c, from cyyever at outlook dot com. 2019-09-08 21:29:22 +01:00
Thomas Adam
648471ecee Merge branch 'obsd-master' 2019-08-29 18:02:29 +01:00
nicm
7ce8135138 It is not longer necessary to double-escape ; in %%%, problem reported
by Theo Buehler.
2019-08-29 07:13:48 +00:00
Thomas Adam
4fa1f961f3 Merge branch 'obsd-master' 2019-08-28 20:02:24 +01:00
nicm
df0334d3b3 The resize event was never deciding to actually resize the pane if there
was output in the pane faster than the timer would fire, so change how
it works to only defer the timer again if the pane was actually resized
within the last timer period. Reported by James Tai in GitHub issue
1880.
2019-08-28 07:34:32 +00:00
Thomas Adam
6640790bdc Merge branch 'obsd-master' 2019-08-27 10:02:31 +01:00
nicm
39c55d5b6f Fix swap-window -d to work as intended, GitHub issue 1879 from Sam Stuewe. 2019-08-26 16:35:41 +00:00
nicm
79f09b4d85 Add support for the SD (scroll down) escape sequence, GitHub issue 1861. 2019-08-26 17:28:24 +01:00
nicm
e85ea9f67d grid_view_delete_cells does need to clear, GitHub issue 1871. 2019-08-16 19:34:29 +01:00
Thomas Adam
2db9a18362 Merge branch 'obsd-master' 2019-08-16 17:02:29 +01:00
nicm
37583f0a69 Add a flag to reverse sort in the various choose modes, from Benjamin
Poirier in GitHub issue 1875.
2019-08-16 11:49:12 +00:00
Thomas Adam
06ad86053c Merge branch 'obsd-master' 2019-08-16 11:02:31 +01:00
nicm
5644d37876 grid_view_delete_cells does need to clear, GitHub issue 1871. 2019-08-16 08:52:25 +00:00
Thomas Adam
ed0f2831b4 Merge branch 'obsd-master' 2019-08-15 11:02:48 +01:00
nicm
21fae50089 Default to previous search string for search-forward and
search-backward, from Leah Neukirchen.
2019-08-14 10:02:24 +00:00
nicm
0f243f0388 Add -Z flag to rotate-window, select-pane, swap-pane, switch-client to
preserve zoomed state. GitHub issue 1839.
2019-08-14 09:58:31 +00:00
Thomas Adam
9b3fefc435 Merge branch 'obsd-master' 2019-08-06 07:02:33 +01:00
nicm
45f4ff5485 Add support for the SD (scroll down) escape sequence, GitHub issue 1861. 2019-08-05 06:42:02 +00:00
Nicholas Marriott
eb8eeab05e Merge branch '3.0-rc' 2019-08-01 18:52:44 +01:00
nicm
26f2740110 xterm 348 now disables margins when resized, so send DECLRMM again. 2019-08-01 18:52:33 +01:00
Thomas Adam
a2d7f380b2 Merge branch 'obsd-master' 2019-08-01 17:02:26 +01:00
nicm
c4744620af Correctly wrap search in copy mode even if at the very top left, GitHub
issue 1845.
2019-08-01 14:31:39 +00:00
nicm
3d660b0023 Select the correct word for select-word when already at the start of a
word, GitHub issue 1820.
2019-08-01 14:30:31 +00:00
nicm
49bf7dc77e xterm 348 now disables margins when resized, so send DECLRMM again. 2019-08-01 11:45:34 +00:00
Thomas Adam
11315c589a Merge branch 'obsd-master' 2019-08-01 11:02:26 +01:00
nicm
2db5f9c215 Add -N to capture-pane to preserve trailing spaces, from Leon Winter. 2019-08-01 08:42:34 +00:00
Thomas Adam
cea87758e7 Merge branch 'obsd-master' 2019-08-01 09:02:26 +01:00
nicm
58bbce09e2 Remove check for same size - size has already been changed so this
breaks reflow.
2019-08-01 07:08:13 +00:00
Thomas Adam
900e583c7a Merge branch 'obsd-master' 2019-07-30 13:02:26 +01:00
nicm
e698ee01dd Reorder some text in the windows & panes section and add some better
explanation of modes.
2019-07-30 10:10:02 +00:00
Nicholas Marriott
2dd9a4fb9c Bump version again. 2019-07-29 11:03:15 +01:00
Nicholas Marriott
b8b865fb17 Bump 3.0-rc up to master. 2019-07-29 10:53:04 +01:00
Nicholas Marriott
da552eb73b Merge branch 'master' into 3.0-rc 2019-07-29 10:51:30 +01:00
Thomas Adam
b90a9fcd13 Merge branch 'obsd-master' 2019-07-26 23:02:27 +01:00
Nicholas Marriott
e19622b8db Add to CHANGES. 2019-07-26 21:22:53 +01:00
nicm
8f40f791d9 Change "lost server" message to "server exited unexpectedly", from Neal
McBurnett in GitHub issue 1857.
2019-07-26 20:08:40 +00:00
Thomas Adam
bb9a123ddd Merge branch 'obsd-master' 2019-07-24 23:02:30 +01:00
nicm
90dba3ec66 Mark pane status line jobs with FORMAT_STATUS also so it redraws when
they finish, GitHub issue 1852.
2019-07-24 21:16:17 +00:00
Thomas Adam
47712fc113 Merge branch 'obsd-master' 2019-07-19 09:02:26 +01:00
nicm
bf6d1aeaa4 Add a few vi(1) key aliases, from D Ben Knoble. 2019-07-19 07:20:51 +00:00
nicm
df7b5292ab More man page fixes from lacygoill at lacygoill dot me, GitHub issue
1805.
2019-07-19 07:18:03 +00:00
Thomas Adam
7e7c8faa34 Merge branch 'obsd-master' 2019-07-17 21:02:26 +01:00
nicm
f6a9f6b4ad Use the right client for if -b. 2019-07-17 17:49:23 +00:00
nicm
9e7774bb96 Clear overlay on normal key press. 2019-07-17 17:46:51 +00:00
Nicholas Marriott
7c78ebce45 cc, make, libevent, ncurses are dependencies for all builds so no need to list
them for VCS build.
2019-07-17 08:57:22 +01:00
Nicholas Marriott
80b82c8d14 Mention dependencies earlier. 2019-07-17 08:56:10 +01:00
Thomas Adam
8e33cc61b1 Merge branch 'obsd-master' 2019-07-16 17:02:25 +01:00
nicm
99852f8401 Fix check for wrapping when redrawing entire lines, GitHub issue 1836. 2019-07-16 14:11:52 +00:00
Thomas Adam
b749a39cdb Merge branch 'obsd-master' 2019-07-16 13:02:24 +01:00
nicm
b89f2f28bb Fix grid clear code to correctly clear with the default background
colour rather than ending up with the used count higher than the total
size, GitHub issue 1829.
2019-07-16 10:30:56 +00:00
Thomas Adam
a786a59176 Merge branch 'obsd-master' 2019-07-15 21:02:26 +01:00
nicm
eac055bfaf Simplify code to work out if an extra line is needed in the cell. 2019-07-15 18:43:32 +00:00
nicm
6ceeceab7a Make layout_fix_offsets take a window like layout_fix_panes. 2019-07-15 18:25:07 +00:00
Thomas Adam
c9216493cf Merge branch 'obsd-master' 2019-07-10 17:02:25 +01:00
nicm
91b6145499 The command item changes so can't keep getting the target out of it, need to use
the one from the first item. Fixes crash reported by M Kelly.
2019-07-10 14:33:24 +00:00
Thomas Adam
9cbbdb90bf Merge branch 'obsd-master' 2019-07-10 13:02:26 +01:00
nicm
f4d858e7a0 Add -F to refresh-client to specify flags for control clients - one flag
at the moment, no-output which turns off forwarding pane output. From
Thomas Adam. GitHub issue 1834.
2019-07-10 11:20:10 +00:00
Thomas Adam
98ef369b27 Merge branch 'obsd-master' 2019-07-09 17:02:26 +01:00
nicm
fc2016dbb6 Add a -H flag to send-keys to send literal keys given as hex numbers
(needed for control clients to send mouse sequences). Also add some
format flags for UTF-8 and SGR mouse mode. Requested by Bradley Smith in
GitHub issues 1832 and 1833.
2019-07-09 14:03:12 +00:00
Thomas Adam
177599efb7 Merge branch 'obsd-master' 2019-07-09 15:02:25 +01:00
nicm
ad11d49d64 Do not leak empty lines, GitHub issue 1824. 2019-07-09 13:19:36 +00:00
nicm
b74b8be680 Add j and k for navigation in menus, GitHub issue 1828. 2019-07-09 12:44:47 +00:00
Thomas Adam
ab244cc7ad Merge branch 'obsd-master' 2019-07-08 23:02:26 +01:00
nicm
a4be028b76 Clear search marks before resize, GitHub issue 1823. 2019-07-08 20:29:11 +00:00
Thomas Adam
29b2d07b6b Merge branch 'obsd-master' 2019-07-08 15:02:25 +01:00
nicm
cbe781203f Use the clear history function for the 3J sequence rather than doing it manually. 2019-07-08 11:38:14 +00:00
Thomas Adam
3a4cf62aa9 Merge branch 'obsd-master' 2019-07-06 23:02:26 +01:00
nicm
ddf53d6e4e Correctly adjust mouse position if the status line is at the top and
more than one line. GitHub issue 1822.
2019-07-06 20:56:34 +00:00
nicm
3635b3cd6c Correctly clear underscore colour in grid_get_cell1, also fix struct
grid_cell to avoid padding. Fixes increased memory use reported by Suraj
N Kurapati.
2019-07-06 20:37:29 +00:00
Thomas Adam
bc112a8c89 Merge branch 'obsd-master' 2019-07-05 11:02:26 +01:00
nicm
55c694a467 Do not use uninitialized buffer name. 2019-07-05 07:52:27 +00:00
Thomas Adam
be5af704ad Merge branch 'obsd-master' 2019-07-02 23:02:26 +01:00
nicm
6a489fa7f6 Command prompt key presses need to avoid the command queue, GitHub issue
1817. Also a tmux.1 fix from jmc.
2019-07-02 20:09:19 +00:00
Thomas Adam
b9fbf02ad5 Merge branch 'obsd-master' 2019-07-01 09:02:25 +01:00
nicm
2da050413c Add a "fill" style attribute to clear the entire format drawing area in
a colour, GitHub issue 1815.
2019-07-01 06:56:00 +00:00
Thomas Adam
abcd4bd246 Merge branch 'obsd-master' 2019-06-30 21:02:26 +01:00
nicm
cf30e0f935 Do not double free window if pane fails to start. 2019-06-30 19:21:53 +00:00
Thomas Adam
68c2fc6824 Merge branch 'obsd-master' 2019-06-28 17:02:26 +01:00
deraadt
4ff7bc3eb3 When system calls indicate an error they return -1, not some arbitrary
value < 0.  errno is only updated in this case.  Change all (most?)
callers of syscalls to follow this better, and let's see if this strictness
helps us in the future.
2019-06-28 13:35:05 +00:00
Thomas Adam
b6b4f86cfc Merge branch 'obsd-master' 2019-06-28 09:02:24 +01:00
deraadt
6ce38b7395 asprintf returns -1, not an arbitrary value < 0. Also upon error the
(very sloppy specification) leaves an undefined value in *ret, so it is
wrong to inspect it, the error condition is enough.
discussed a little with nicm, and then much more with millert until we
were exasperated
2019-06-28 05:44:09 +00:00
Thomas Adam
a07df21e79 Merge branch 'obsd-master' 2019-06-27 21:02:25 +01:00
jmc
b434692db2 minor eol issues; 2019-06-27 17:29:16 +00:00
Thomas Adam
e483ce138f Merge branch 'obsd-master' 2019-06-27 17:02:26 +01:00
nicm
dae2868d12 Add support for underscore colours with Setulc capability, mostly from
Kai Moschcau.
2019-06-27 15:17:41 +00:00
nicm
5a501a8ae2 Pass keys that aren't 0-9 on to normal key processing when display-panes
is active (restores previous behaviour).
2019-06-27 15:49:43 +01:00
Thomas Adam
c4a9299956 Merge branch 'obsd-master' 2019-06-26 21:02:26 +01:00
nicm
3a6d90adad Fix a typo in window_pane_find_down (w not wp) and a missing PANE_STATUS_TOP. 2019-06-26 18:44:22 +00:00
nicm
87ea14328c Pass keys that aren't 0-9 on to normal key processing when display-panes
is active (restores previous behaviour).
2019-06-26 18:28:31 +00:00
Thomas Adam
f797ac9ff6 Merge branch 'obsd-master' 2019-06-26 15:02:26 +01:00
nicm
c599ad63f8 Log window and pane resizes. 2019-06-26 13:05:24 +00:00
nicm
80d76612b8 Fix some comments (top/bottom not left/right). 2019-06-26 13:05:06 +00:00
nicm
d83f356218 Add #define for the pane status line option position numbers. 2019-06-26 13:03:47 +00:00
Thomas Adam
a1d4bf20f7 Merge branch 'obsd-master' 2019-06-24 13:02:26 +01:00
nicm
20b938bcb1 Expand arguments to C and s format modifiers (matches m which already expands). 2019-06-24 10:04:29 +00:00
Thomas Adam
c14b0d7c00 Merge branch 'obsd-master' 2019-06-24 11:02:26 +01:00
nicm
bdab595095 Trim trailing spaces when matching. 2019-06-24 08:20:02 +00:00
Thomas Adam
50c4c5917d Merge branch 'obsd-master' 2019-06-23 13:02:26 +01:00
nicm
f1100f97f7 Do not always set scope for panes because the window check might fail,
GitHub issue 1810.
2019-06-23 10:00:29 +00:00
Thomas Adam
69a2f73449 Merge branch 'obsd-master' 2019-06-21 09:02:25 +01:00
nicm
e3c2772d2f Man page fixes from lacygoill at lacygoill dot me. 2019-06-21 07:11:01 +00:00
Thomas Adam
70775b3c28 Merge branch 'obsd-master' 2019-06-20 23:02:28 +01:00
nicm
1d8e545bc1 Add -r to find-window for regex instead of fnmatch. 2019-06-20 20:31:04 +00:00
Thomas Adam
c4a92e5799 Merge branch 'obsd-master' 2019-06-20 21:02:27 +01:00
nicm
97a317a656 Need to always check focus even if not current window. 2019-06-20 19:29:38 +00:00
nicm
cb5e681ef6 FIx return of options_scope_from_name on error. 2019-06-20 18:13:04 +00:00
Thomas Adam
5aae58295e Merge branch 'obsd-master' 2019-06-20 19:02:27 +01:00
Thomas Adam
4a44ae06bf Merge branch 'obsd-master' 2019-06-20 17:02:26 +01:00
nicm
f4e835754c Fix how regex substitution works with empty matches. 2019-06-20 15:40:14 +00:00
Thomas Adam
f8ad72b2ee Merge branch 'obsd-master' 2019-06-20 15:02:26 +01:00
nicm
fc1df91e03 allow-rename and alternate-screen can be pane options. 2019-06-20 13:40:22 +00:00
nicm
c1573727f0 Still need to walk the options tree for user options. 2019-06-20 13:39:17 +00:00
Nicholas Marriott
a0e2c1b4ca Add to CHANGES. 2019-06-20 13:08:22 +01:00
nicm
5f92f92908 Add a per-pane option set. Pane options inherit from window options (so
there should be no change to existing behaviour) and are set and shown
with set-option -p and show-options -p.

Change remain-on-exit and window-style/window-active-style to be pane
options (some others will be changed later).

This makes select-pane -P and -g unnecessary so no longer document them
(they still work) and no longer document set-window-option and
show-window-options in favour of set-option -w and show-options -w.
2019-06-20 11:59:59 +00:00
Thomas Adam
adca856806 Merge branch 'obsd-master' 2019-06-20 11:02:28 +01:00
Thomas Adam
03945276f7 Merge branch 'obsd-master' 2019-06-20 09:02:26 +01:00
nicm
c1ede507d9 Add a helper function to work out option table from name. 2019-06-20 07:41:29 +00:00
nicm
cd1fc42df6 Add a -A flag to show-options to show parent options as well. 2019-06-20 07:10:56 +00:00
nicm
ae541287d3 Expand command formats in %if and move the config file loading later (to
when the first client has identified) so all the client formats are
available, fixes problems reported by Thomas Sattler.
2019-06-20 06:51:36 +00:00
Nicholas Marriott
b3ca410bc3 Use KERN_PROC_CWD on NetBSD, from Leonardo Taccari. 2019-06-20 06:57:37 +01:00
Nicholas Marriott
ec151b79ec Merge branch '3.0-rc' 2019-06-18 19:43:20 +01:00
nicm
797042584e Handle comments more correctly inside {}, from Avi Halachmi. 2019-06-18 19:43:13 +01:00
Nicholas Marriott
df07723e20 Add a config. 2019-06-18 19:42:52 +01:00
Thomas Adam
ee9bc355f5 Merge branch 'obsd-master' 2019-06-18 13:02:28 +01:00
nicm
250fdd08be Handle comments more correctly inside {}, from Avi Halachmi. 2019-06-18 11:17:40 +00:00
nicm
9272fe36e2 Add a cmdq_continue function rather than twiddling the flag directly. 2019-06-18 11:08:42 +00:00
Thomas Adam
5895f1d9f7 Merge branch 'obsd-master' 2019-06-15 09:02:26 +01:00
Nicholas Marriott
fadbf497a4 Merge branch '3.0-rc' 2019-06-15 07:45:58 +01:00
Nicholas Marriott
748633c887 Remove a stray abort. 2019-06-15 07:45:41 +01:00
Nicholas Marriott
d53c2d0ebe Merge branch '3.0-rc' 2019-06-15 07:44:58 +01:00
Nicholas Marriott
08f55414e1 Remove cmd-list.c line in master. 2019-06-15 07:42:49 +01:00
Nicholas Marriott
7a4a286ea2 Merge branch '3.0-rc' 2019-06-15 07:38:01 +01:00
nicm
9ae658983c Do not loop forever if a menu item contains invisible characters,
reported by Thomas Sattler.
2019-06-15 07:37:44 +01:00
nicm
03da0ced46 Use the right format modifier when comparing, and remove a couple of
unused variables.
2019-06-15 06:33:48 +00:00
Thomas Adam
539a6e7fa0 Merge branch 'obsd-master' 2019-06-14 19:02:26 +01:00
nicm
c95cd9ed5e Do not loop forever if a menu item contains invisible characters,
reported by Thomas Sattler.
2019-06-14 15:35:58 +00:00
Nicholas Marriott
c22470bd14 3.0-rc3. 2019-06-14 16:17:34 +01:00
Nicholas Marriott
4c28ed4e4e Merge branch '3.0-rc' 2019-06-14 16:10:38 +01:00
Nicholas Marriott
31cb95893e #{ is no longer special. 2019-06-14 16:10:27 +01:00
Nicholas Marriott
ae83a5b010 Add to CHANGES. 2019-06-14 16:09:49 +01:00
Nicholas Marriott
29b70e2757 Merge branch '3.0-rc' 2019-06-14 16:06:20 +01:00
nicm
52b8274285 Do not crash if the environment variable is present but empty. 2019-06-14 16:06:12 +01:00
Nicholas Marriott
dcb2bb33a2 Merge branch '3.0-rc' 2019-06-14 16:05:26 +01:00
nicm
a924694820 Use the right client when working out where to save or load the buffer,
reported by kn@.
2019-06-14 16:04:57 +01:00
nicm
0a94dbe051 A couple of minor parser changes around conditions: 1) only treat #{
specially after a condition, otherwise as a comment (which is more as
most people expect) 2) allow formats to be quoted after a condition.
2019-06-14 16:04:52 +01:00
Nicholas Marriott
8da756c4f0 Restore missing functions. 2019-06-14 16:02:34 +01:00
Nicholas Marriott
7bb8ab1c0e Add a bit to {}. 2019-06-14 15:54:36 +01:00
Thomas Adam
5e9757b30b Merge branch 'obsd-master' 2019-06-14 15:51:09 +01:00
nicm
d1d3bbb458 Show filename with -v for source-file. 2019-06-14 13:34:45 +00:00
nicm
45203582ff A couple of minor parser changes around conditions: 1) only treat #{
specially after a condition, otherwise as a comment (which is more as
most people expect) 2) allow formats to be quoted after a condition.
2019-06-14 12:04:11 +00:00
Nicholas Marriott
ebc9dcb3bb Add a bit to {}. 2019-06-14 12:40:35 +01:00
Nicholas Marriott
4bbf941436 Merge branch '3.0-rc' 2019-06-14 10:34:05 +01:00
Nicholas Marriott
f31847db62 Improve description of #{. 2019-06-14 10:33:55 +01:00
nicm
f35f15b107 Use the right client when working out where to save or load the buffer,
reported by kn@.
2019-06-13 21:44:13 +00:00
nicm
eef11b64e1 Do not crash if the environment variable is present but empty. 2019-06-13 21:24:09 +00:00
jmc
b8a9c740bb tweak previous; 2019-06-13 21:04:21 +00:00
nicm
26b9a8e49b Set the cursor x at the same time as changing the y or the end of line
marker may not be redrawn.
2019-06-13 20:38:05 +00:00
nicm
3e72e98e3b Add regular expression support for the format search, match and
substitute modifiers.
2019-06-13 19:46:00 +00:00
Thomas Adam
d5902eeae9 Merge branch 'obsd-master' 2019-06-12 11:02:25 +01:00
nicm
7e6a26cc9d Do not use $TMUX to find the session because for windows in multiple
sessions it is wrong as often as it is right, and for windows in one
session it is pointless. Instead check TMUX_PANE to find the pane and
look for the MRU session as usual. GitHub issue 1793.
2019-06-12 09:10:29 +00:00
nicm
b12df01861 Pass target on to new commands with if -F. 2019-06-12 08:47:07 +00:00
nicm
9d42bd328c Do not always resize the window back to its original size after applying
a layout, let the normal window resize process do it. This means windows
are not resized at all if window-size is manual, and are not resized
multiple times if later attached to a different size terminal.
2019-06-12 08:08:33 +00:00
nicm
e337c1ba7d Do not try to parse command when unsetting, GitHub issue 1788. 2019-06-11 15:51:30 +01:00
Thomas Adam
057c04e32a Merge branch 'obsd-master' 2019-06-11 15:02:26 +01:00
Nicholas Marriott
8e971f187a Update README.md 2019-06-11 14:27:40 +01:00
Nicholas Marriott
e8814a7e93 Add to CHANGES. 2019-06-11 14:23:47 +01:00
nicm
1a9f9c09b4 Do not resize panes unless they are in an attached, active window. From
Morten M Neergaard in GitHub issue 1782.
2019-06-11 13:09:00 +00:00
Thomas Adam
d9ad461b5e Merge branch 'obsd-master' 2019-06-09 09:02:26 +01:00
nicm
915097d312 Exiting alternate screen mode should restore cursor position and
attributes even if already outside alternate screen mode. GitHub issue
1789.
2019-06-09 06:50:24 +00:00
Thomas Adam
181841fb67 Merge branch 'obsd-master' 2019-06-09 01:02:26 +01:00
nicm
43796bf131 Do not try to parse command when unsetting, GitHub issue 1788. 2019-06-08 21:48:29 +00:00
Thomas Adam
e13c1e5320 Merge branch 'obsd-master' 2019-06-07 23:02:26 +01:00
nicm
e37f34facc Do not load the config file if the server is exiting because it failed
to start, otherwise commands like lsk which start the server again can
end up looping infinitely. Also make the first client exit
correctly. Problem reported by Wael M Nasreddine.
2019-06-07 20:09:17 +00:00
Thomas Adam
c91680822d Merge branch 'obsd-master' 2019-06-05 23:02:26 +01:00
Nicholas Marriott
37bb993f53 Merge branch '3.0-rc' 2019-06-05 21:06:30 +01:00
nicm
db5a7c1740 Need to increment the argument to skip the prefix earlier, fixes
repeated incremental search in copy mode, reported by Kaushal Modi in
GitHub issue 1780.
2019-06-05 21:06:06 +01:00
Thomas Adam
1ec3fb5f30 Merge branch 'obsd-master' 2019-06-05 21:02:26 +01:00
nicm
8f40796f05 Add a -v flag to source-file to show the commands and line numbers. 2019-06-05 20:00:53 +00:00
nicm
09e90c1645 Need to increment the argument to skip the prefix earlier, fixes
repeated incremental search in copy mode, reported by Kaushal Modi in
GitHub issue 1780.
2019-06-05 19:00:36 +00:00
Nicholas Marriott
f274b1b9d7 Merge branch '3.0-rc' 2019-06-05 15:13:56 +01:00
Nicholas Marriott
53914e50b9 RC2. 2019-06-05 15:09:42 +01:00
Nicholas Marriott
e252984993 If only one of -x or -y is given, use the calculated size for the
other. Also fix some warnings. Pointed out by Ben Boeckel.
2019-06-05 06:44:08 +01:00
Thomas Adam
d3f0c72e20 Merge branch 'obsd-master' 2019-06-03 21:02:26 +01:00
nicm
4ca1de1b8b Add new-session -X and attach-session -x to send SIGHUP to parent when
detaching (like detach-client -P). From Colin Watson in GitHub issue
1773.
2019-06-03 18:28:37 +00:00
nicm
17bc11bd15 yacc(1) copies its union so it is not a good place to store
TAILQ_HEADs. Allocate them instead. Found from a problem reported by
sthen@.
2019-06-02 10:52:32 +01:00
Thomas Adam
f6933e43c2 Merge branch 'obsd-master' 2019-06-02 09:02:27 +01:00
nicm
900238a306 yacc(1) copies its union so it is not a good place to store
TAILQ_HEADs. Allocate them instead. Found from a problem reported by
sthen@.
2019-06-02 07:10:15 +00:00
Thomas Adam
027a7ac829 Merge branch 'obsd-master' 2019-06-01 19:02:26 +01:00
nicm
2c6c3a1d27 If only one of -x or -y is given, use the calculated size for the
other. Pointed out by Ben Boeckel.
2019-06-01 16:21:37 +00:00
Thomas Adam
8382ae65b7 Merge branch 'obsd-master' 2019-06-01 09:02:27 +01:00
Nicholas Marriott
ec690208a3 Move link. 2019-06-01 08:07:22 +01:00
Nicholas Marriott
b28b8312f2 Mention CHANGES. 2019-06-01 08:05:55 +01:00
Nicholas Marriott
c2d79add31 Should be caps. 2019-06-01 07:28:31 +01:00
Nicholas Marriott
feff55b92f Try this instead. 2019-06-01 07:26:39 +01:00
Nicholas Marriott
3d29b97768 Link to file. 2019-06-01 07:24:57 +01:00
nicm
39ea8a2787 Need stdlib.h, from Ben Boeckel. 2019-06-01 06:20:22 +00:00
Thomas Adam
480cdedcac Merge branch 'obsd-master' 2019-06-01 01:02:25 +01:00
Nicholas Marriott
88ee5a1a00 Add a crashing config. 2019-05-31 22:50:30 +01:00
nicm
2c5f3074bc Fix warnings, from Ben Boeckel. 2019-05-31 21:41:17 +00:00
Nicholas Marriott
9d2ef8bf0d Merge branch '3.0-rc' 2019-05-31 15:18:56 +01:00
nicm
1a3a973bd0 Allow % strings that are all numbers or %s, and fix a double free. Both
reported by George Nachman, GitHub issues 1765 and 1766.
2019-05-31 15:18:25 +01:00
Thomas Adam
adf9e77702 Merge branch 'obsd-master' 2019-05-31 15:02:27 +01:00
nicm
82e47403c6 Allow % strings that are all numbers or %s, and fix a double free. Both
reported by George Nachman, GitHub issues 1765 and 1766.
2019-05-31 11:34:09 +00:00
Thomas Adam
41b8bb4eef Merge branch 'obsd-master' 2019-05-30 23:02:27 +01:00
nicm
b26523c26d Remove a leftover abort and some fixes from cppcheck. 2019-05-30 20:54:03 +00:00
Nicholas Marriott
200a1c62c6 Merge branch '3.0-rc' 2019-05-30 13:12:50 +01:00
nicm
e4eec92852 No longer need to reduce line number by one. 2019-05-30 13:12:35 +01:00
Thomas Adam
2df757521a Merge branch 'obsd-master' 2019-05-30 13:02:26 +01:00
nicm
8fb796b5b3 No longer need to reduce line number by one. 2019-05-30 10:04:33 +00:00
Thomas Adam
82b25a9d62 Merge branch 'obsd-master' 2019-05-30 11:02:27 +01:00
nicm
89d2c7eb26 I had hoped that non-xenl terminals had died out, at least in fairly
modern OSs, but no - DragonFly BSD's console returns to haunt us. Fix it
at least somewhat. GitHub issue 1763.
2019-05-30 07:42:41 +00:00
Nicholas Marriott
026450c1a8 Add test for xenl terminals. 2019-05-30 07:52:24 +01:00
Nicholas Marriott
c5e2532b67 Merge branch '3.0-rc' 2019-05-29 23:04:28 +01:00
nicm
75d112c484 The line number needs to be updated only after the \n is processed by
the parser, so store a flag and update it next time around. Also each
new line needs its own shared data.
2019-05-29 23:04:22 +01:00
Thomas Adam
414208aab1 Merge branch 'obsd-master' 2019-05-29 23:02:25 +01:00
nicm
7dced37673 Use VIS_CSTYLE for the arguments and add the missing escapes it can
generate to the parser.
2019-05-29 20:05:14 +00:00
Nicholas Marriott
95d68fcba1 Merge branch '3.0-rc' 2019-05-29 20:43:29 +01:00
Nicholas Marriott
a05c3a7aa6 Add command order test. 2019-05-29 20:43:11 +01:00
nicm
c17edd594e The line number needs to be updated only after the \n is processed by
the parser, so store a flag and update it next time around. Also each
new line needs its own shared data.
2019-05-29 19:34:42 +00:00
Nicholas Marriott
47795d1695 Update .travis.yml from master. 2019-05-29 14:40:41 +01:00
Nicholas Marriott
2ce8e0110c Don't even need this? 2019-05-29 14:32:49 +01:00
Nicholas Marriott
4425c1e1b3 Tweak again. 2019-05-29 14:24:09 +01:00
Nicholas Marriott
409d52ed41 Try OS X. 2019-05-29 14:19:37 +01:00
Thomas Adam
e90b5dcea3 Merge branch 'obsd-master' 2019-05-29 13:02:35 +01:00
Nicholas Marriott
ffcc60211d Merge branch '3.0-rc' 2019-05-29 12:13:55 +01:00
Nicholas Marriott
7cdb1cfc8d as -> and. 2019-05-29 12:13:46 +01:00
Nicholas Marriott
480ba99a16 Merge branch '3.0-rc' 2019-05-29 12:07:05 +01:00
Nicholas Marriott
8827b7f9a6 Tweak {} text. 2019-05-29 11:25:07 +01:00
nicm
a4424fbebf Support \ooo escapes, from Avi Halachmi. 2019-05-29 10:08:36 +00:00
Nicholas Marriott
eefbbfde77 Merge branch '3.0-rc' 2019-05-28 21:05:00 +01:00
nicm
7eeb479705 Do not read past the end of the argument string if it is empty. 2019-05-28 21:04:41 +01:00
nicm
8d137233a9 Redraw status line if size changes, GitHub issue 1762. Also fix length
of target buffer when pasting into status line.
2019-05-28 21:04:35 +01:00
Thomas Adam
c96957583f Merge branch 'obsd-master' 2019-05-28 21:02:28 +01:00
nicm
580cd16f4c Redraw status line if size changes, GitHub issue 1762. Also fix length
of target buffer when pasting into status line.
2019-05-28 18:53:36 +00:00
nicm
4382538e4b Do not read past the end of the argument string if it is empty. 2019-05-28 18:30:30 +00:00
Nicholas Marriott
e7a530fe4c Next will be 3.1. 2019-05-28 15:48:31 +01:00
Nicholas Marriott
05d07413ff 3.0-rc. 2019-05-28 15:46:17 +01:00
Thomas Adam
b8360504f3 Merge branch 'obsd-master' 2019-05-28 15:02:26 +01:00
Nicholas Marriott
a062650d4b Tweak menu option. 2019-05-28 14:49:50 +01:00
Nicholas Marriott
f012db9be9 Add regress for conf files. 2019-05-28 13:21:19 +01:00
nicm
299d4f3aaa Exit 1 correctly if source-file fails. 2019-05-28 12:20:28 +00:00
Thomas Adam
eba6cf61c9 Merge branch 'obsd-master' 2019-05-28 13:02:27 +01:00
nicm
0ec410689c Allow source-file to take multiple arguments. 2019-05-28 11:46:30 +00:00
nicm
e0fd295054 Change the default right click pane to open the menu if not in a mode
and no application mouse.
2019-05-28 10:27:11 +00:00
nicm
99a8469ee4 Add key bindings to open the window and pane menus (C-m and M-m for now). 2019-05-28 10:05:24 +00:00
nicm
12255411f2 Allow menu items to be disabled by putting a - at the start of their
name, rather than just including #[dim] which still allowed them to be
chosen.
2019-05-28 09:50:54 +00:00
Thomas Adam
c0116b2c5b Merge branch 'obsd-master' 2019-05-28 09:02:26 +01:00
nicm
799a154b91 Change display-menu from taking a single string to a set of arguments,
which is much easier to work with. Based on a diff from Avi Halachmi.
2019-05-28 07:18:42 +00:00
Thomas Adam
793f4d89d6 Merge branch 'obsd-master' 2019-05-27 19:02:25 +01:00
nicm
90cd045cf3 Clarify newlines inside {} a little. 2019-05-27 16:22:32 +00:00
nicm
94f6488f0e Go less crazy with horizontal separators on default menus. 2019-05-27 15:29:46 +00:00
Thomas Adam
d4bf4bd7c7 Merge branch 'obsd-master' 2019-05-27 15:02:28 +01:00
Nicholas Marriott
522d1bd309 Add. 2019-05-27 14:39:44 +01:00
Nicholas Marriott
a21de4c483 Mention <><=>=. 2019-05-27 14:39:06 +01:00
Nicholas Marriott
714311a696 Add {}. 2019-05-27 14:37:47 +01:00
Nicholas Marriott
9f0904ce6f Remove unused fparseln compat code. 2019-05-27 14:28:05 +01:00
nicm
ae3eba6e08 Fix crash when killing the current window, reported by Jesus Rafael
Sanchez in GitHub issue 1760.
2019-05-27 12:48:52 +00:00
nicm
6b332127ca Add an additional {} syntax for defining strings in the configuration
file, making it much tidier to define commands that contain other tmux
or shell commands (like if-shell). Also tweak bind-key to expect a
string if it is only given one argument, so {} can be used with it as
well. From Avi Halachmi.
2019-05-27 12:16:27 +00:00
Thomas Adam
bd40d704e2 Merge branch 'obsd-master' 2019-05-27 09:02:26 +01:00
nicm
65e5e14561 Fix the intended ordering of items in buffer mode - it should not always
be tag 0 when the tree is empty. GitHub issue 1759.
2019-05-27 06:50:04 +00:00
Thomas Adam
103e44d936 Merge branch 'obsd-master' 2019-05-26 21:02:26 +01:00
nicm
097973e3d5 Add keys for new menu items. 2019-05-26 18:43:43 +00:00
nicm
5fef946df4 Always redraw overlay if it is on (so status line doesn't redraw over it). 2019-05-26 18:27:52 +00:00
nicm
023c2c5392 Do not accept choice unless mouse has actually moved before. 2019-05-26 18:19:52 +00:00
nicm
e90d4a6021 Add formats for word and line under the mouse and use them to add some
items to the pane menu.
2019-05-26 17:34:45 +00:00
Thomas Adam
2e84d1cf03 Merge branch 'obsd-master' 2019-05-26 15:02:25 +01:00
nicm
6431005169 Add a way to append or prepend to a format if the length has been limited. 2019-05-26 12:02:42 +00:00
Thomas Adam
9beb3eb593 Merge branch 'obsd-master' 2019-05-26 13:02:26 +01:00
nicm
6dee409981 Some other platforms doesn't support fmemopen(3) (not unexpectedly), so
don't use it - since we only use getc/ungetc on the file anyway it is
easy not to.
2019-05-26 10:08:50 +00:00
Thomas Adam
f3fc81b178 Merge branch 'master' of github.com:ThomasAdam/tmux 2019-05-25 18:50:05 +01:00
Thomas Adam
463bd8abb9 Merge branch 'obsd-master' 2019-05-25 18:41:51 +01:00
nicm
a65a6d62d1 Add <, >, <=, >= for formats, GitHub issue 1747. 2019-05-25 16:51:10 +00:00
nicm
207789dc2d Client name can actually be NULL, so use address in that case. 2019-05-25 10:46:55 +00:00
nicm
d7586d3d65 Use client name when logging command queue. 2019-05-25 10:44:09 +00:00
nicm
f8d3d247d8 Merge cmd_list_parse into cmd-parse.y so it can use the new alias
processing code.
2019-05-25 07:18:20 +00:00
nicm
6b0fa14470 Fix error handling in if-shell. 2019-05-25 07:15:53 +00:00
Nicholas Marriott
a69211aff5 Fix up regress test. 2019-05-25 08:09:23 +01:00
nicm
930245d7ff Make cmd_log_argv take a printf-like format for the prefix. 2019-05-25 06:58:10 +00:00
espie
0dc8b7d5d8 unbreak build, okay nicm@ 2019-05-23 21:36:42 +00:00
Thomas Adam
19a3a9ee20 Merge branch 'obsd-master' 2019-05-23 21:02:30 +01:00
nicm
f3e01ecc42 Fix line numbers - commands are added after the line ends so they need to
get line - 1.
2019-05-23 18:39:00 +00:00
nicm
6c260af56d Use the same argument escaping code for options as well. 2019-05-23 18:33:53 +00:00
Nicholas Marriott
e817821104 Mention \. 2019-05-23 19:27:41 +01:00
nicm
f006116bac Environment variables can start with { also. 2019-05-23 18:22:13 +00:00
Thomas Adam
43431e7e84 Merge branch 'obsd-master' 2019-05-23 17:02:25 +01:00
nicm
eb8b51effc Fix drawing of status-right when it is aligned to the centre, GitHub
issue 1754.
2019-05-23 14:44:33 +00:00
Nicholas Marriott
389cf63cbc Tweak text. 2019-05-23 15:18:20 +01:00
Thomas Adam
2148fe33cd CHANGES: remove note to packagers 2019-05-23 15:16:10 +01:00
Thomas Adam
1a6540fea0 CHANGES: remove note to packagers 2019-05-23 15:15:42 +01:00
nicm
7d702f3cef Don't remove group items for group 0 (no group). 2019-05-23 15:15:42 +01:00
nicm
d0c462f718 Fix length calculation for pasting UTF-8 characters in the status line,
GitHub issue 1753.
2019-05-23 15:15:42 +01:00
Thomas Adam
238d2aa870 CHANGES: mention yacc 2019-05-23 15:12:24 +01:00
Thomas Adam
6bb505eb73 CHANGES: mention yacc 2019-05-23 15:11:25 +01:00
Nicholas Marriott
83b9807370 Remove duplicate. 2019-05-23 15:11:25 +01:00
Nicholas Marriott
3e6e533779 Add yacc(1) bits. 2019-05-23 15:11:25 +01:00
nicm
27bfb56ad5 Break the argument escaping code into a separate function and use it to
escape key bindings in list-keys. Also escape ~ and ; and $ properly.
2019-05-23 14:03:44 +00:00
Thomas Adam
c49f2a0365 Merge branch 'obsd-master' 2019-05-23 15:02:28 +01:00
nicm
3e3eb1dd0f Don't remove group items for group 0 (no group). 2019-05-23 13:08:43 +00:00
nicm
a4fe7e81c8 Fix length calculation for pasting UTF-8 characters in the status line,
GitHub issue 1753.
2019-05-23 12:47:52 +00:00
Nicholas Marriott
4ce26b0393 Remove duplicate. 2019-05-23 13:32:18 +01:00
Thomas Adam
82bf0f4d48 configure.ac: add AC_PROC_YACC 2019-05-23 13:12:03 +01:00
Thomas Adam
8590ee65e6 TRAVIS: add bison to build deps 2019-05-23 13:12:03 +01:00
Thomas Adam
d67b99c7e4 configure.ac: add AC_PROC_YACC 2019-05-23 13:09:34 +01:00
Thomas Adam
092c7bfeb8 TRAVIS: add bison to build deps 2019-05-23 13:06:47 +01:00
Nicholas Marriott
bf2cf33fc6 Add yacc(1) bits. 2019-05-23 13:04:41 +01:00
Thomas Adam
75aeb733f2 Merge branch 'obsd-master' 2019-05-23 13:02:27 +01:00
nicm
723010ba72 Replace the split parser code (cfg.c and cmd-string.c) with a single
parser using yacc(1). This is a major change but is clearer and simpler
and allows some edge cases to be made more consistent, as well as
tidying up how aliases are handled. It will also allow some further
improvements later.

Entirely the same parser is now used for parsing the configuration file
and for string commands. This means that constructs previously only
available in .tmux.conf, such as %if, can now be used in string commands
(for example, those given to if-shell - not commands invoked from the
shell, they are still parsed by the shell itself).

The only syntax change I am aware of is that #{} outside quotes or a
comment is now considered a format and not a comment, so #{ is now a
syntax error (notably, if it is at the start of a line).

This also adds two new sections to the man page documenting the syntax
and outlining how parsing and command execution works.

Thanks to everyone who sent me test configs (they still all parse
without errors - but this doesn't mean they still work as intended!).

Thanks to Avi Halachmi for testing and man page improvements, also to
jmc@ for reviewing the man page changes.
2019-05-23 11:13:30 +00:00
Nicholas Marriott
7ca2e2fe88 Add a config. 2019-05-23 11:57:04 +01:00
Thomas Adam
9d450cc6d0 Merge branch 'obsd-master' 2019-05-22 21:02:28 +01:00
nicm
5571d7a21c Fix crash if window doesn't exist, GitHub issue 1751. 2019-05-22 18:58:31 +00:00
Nicholas Marriott
e353d0cab3 Another config. 2019-05-22 19:49:48 +01:00
Nicholas Marriott
282f7fbd37 Add a test config. 2019-05-22 13:31:20 +01:00
Nicholas Marriott
b68fc7f104 Two more configs. 2019-05-22 07:12:38 +01:00
Nicholas Marriott
54dfe36340 Another. 2019-05-21 17:07:08 +01:00
Thomas Adam
c4f4904f9b More configs 2019-05-21 16:43:34 +01:00
Nicholas Marriott
d769fec8d6 More configs. 2019-05-21 14:41:07 +01:00
Nicholas Marriott
f1ce611345 Add another one. 2019-05-21 11:28:12 +01:00
Thomas Adam
3856116069 Merge branch 'obsd-master' 2019-05-21 09:02:37 +01:00
Nicholas Marriott
afe231c94c Add a few test configs thanks to various people. 2019-05-21 08:02:17 +01:00
nicm
ae4cccb4f1 Fix a couple of Ta from Alphonse Mariya. 2019-05-21 07:01:14 +00:00
Thomas Adam
98ee93bde3 Merge branch 'obsd-master' 2019-05-20 15:02:40 +01:00
nicm
87d82170a6 Fix ordering of source-file with multiple files and add flags to load_cfg. 2019-05-20 13:23:32 +00:00
Thomas Adam
3a7e15511b Merge branch 'obsd-master' 2019-05-20 13:02:36 +01:00
nicm
e128c7fcd8 Replace the various identical error callbacks with a single one in cmd-queue.c. 2019-05-20 11:46:06 +00:00
nicm
8db89f8efb Add a helper to allocate a cmd_list. 2019-05-20 11:34:37 +00:00
Thomas Adam
c271cb9ff8 Merge branch 'obsd-master' 2019-05-20 07:02:36 +01:00
nicm
6e0c663a89 Fix the color space parameter in RGB SGR, from Brad Town. 2019-05-20 05:35:46 +00:00
Thomas Adam
56246c2936 README.md: mention TODO list on the tmux wiki 2019-05-19 13:31:09 +01:00
Thomas Adam
54da493476 Merge branch 'obsd-master' 2019-05-18 23:02:36 +01:00
nicm
82ebd98c5f Move the single command flag (CMD_CONTROL) into the shared flags. 2019-05-18 21:14:10 +00:00
Nicholas Marriott
831c67c2d8 Remove TODO, moving onto website. 2019-05-18 14:44:31 +01:00
Nicholas Marriott
d6f7be6345 Add to CHANGES. 2019-05-18 13:29:38 +01:00
Thomas Adam
eb064e8a62 Merge branch 'obsd-master' 2019-05-17 09:02:36 +01:00
nicm
9b83b1daa6 Change a couple of ACS characters to be more sensible and add a few
missing ones, reported by Ricardo Banffy.
2019-05-17 05:48:25 +00:00
nicm
4acd345c6a Initialize default size variables, from Thomas Adam. 2019-05-17 05:47:31 +00:00
Thomas Adam
d9ac0e7576 Merge branch 'obsd-master' 2019-05-15 22:50:42 +01:00
Nicholas Marriott
dcf0bc2cc9 Mention CONTRIBUTING. 2019-05-15 21:42:43 +01:00
nicm
cf4566b47b Fix dragging when in view mode rather than copy mode, GitHub issue 1740
from Brad Town.
2019-05-15 19:25:53 +00:00
Nicholas Marriott
50e77536fe Less headings. 2019-05-15 20:21:38 +01:00
Nicholas Marriott
cd4e467751 Kill tmux for logs. 2019-05-15 20:21:07 +01:00
Thomas Adam
9228fead48 Merge branch 'obsd-master' 2019-05-14 09:02:28 +01:00
nicm
38b8a198ba Fix sizing of main-vertical and main-horizontal layouts, GitHub issue 1736. 2019-05-14 07:37:50 +00:00
Thomas Adam
1eefbd28e8 Merge branch 'obsd-master' 2019-05-13 23:02:26 +01:00
nicm
1ee944a19d Add support for overline (SGR 53), from Ricardo Banffy. 2019-05-13 20:10:23 +00:00
nicm
1b0512aa7e Always include Lock in the menu. 2019-05-13 20:07:02 +00:00
Thomas Adam
b5b5d35eee Merge branch 'obsd-master' 2019-05-13 11:02:31 +01:00
nicm
c5f660e33a Fix column width for copy mode commands. 2019-05-13 08:56:07 +00:00
Nicholas Marriott
e1e520d741 Clarify a little. 2019-05-13 09:51:51 +01:00
Thomas Adam
699d9d2fac Merge branch 'obsd-master' 2019-05-12 21:02:27 +01:00
nicm
c3c3927c2b Oops, removed too much in last change. 2019-05-12 18:18:30 +00:00
nicm
00f19b7f91 Fix some indentation and dead assignments. 2019-05-12 18:16:33 +00:00
Thomas Adam
42da951edf README.md: avaailable -> available
Correct typo.
2019-05-12 18:37:32 +01:00
Thomas Adam
180bbab1fc Merge branch 'obsd-master' 2019-05-12 11:02:25 +01:00
Nicholas Marriott
69c59c52b6 Spelling error. 2019-05-12 10:06:42 +01:00
Nicholas Marriott
19370631ea Tweak text of menu CHANGES entry. 2019-05-12 10:05:10 +01:00
Nicholas Marriott
99c1853792 Scatter some `. 2019-05-12 10:00:31 +01:00
nicm
a131655235 Add simple menus to tree, client, buffer modes. 2019-05-12 08:58:09 +00:00
Thomas Adam
677bb168a9 Merge branch 'obsd-master' 2019-05-12 09:02:28 +01:00
nicm
c91323e4d6 Remove menu_create_from_items, I thought I would use it for some later
work but I don't need it.
2019-05-12 07:27:08 +00:00
Thomas Adam
50d1d04913 Merge branch 'obsd-master' 2019-05-11 09:02:29 +01:00
Nicholas Marriott
67e2f5869a Mention -vv. 2019-05-11 08:34:08 +01:00
Nicholas Marriott
3a9c199ae7 New ISSUE_TEMPLATE from Nicolas CARPi. 2019-05-11 08:23:14 +01:00
Nicholas Marriott
bd6d0b3101 Formatted README.md, from Nicolas CARPi. 2019-05-11 08:19:14 +01:00
Nicholas Marriott
f1dd65cbdf Another tweak. 2019-05-11 08:09:49 +01:00
Nicholas Marriott
c18d7c5fcb Update CONTRIBUTING.md, mostly from Nicolas CARPi. 2019-05-11 08:07:35 +01:00
Nicholas Marriott
0d64531f66 Tweaks to README, some from me, some from Nicolas CARPi. 2019-05-11 07:55:28 +01:00
Nicholas Marriott
f44dafd224 Add to CHANGES. 2019-05-11 07:43:18 +01:00
nicm
198b0a23a2 Don't use arguments with It and -enum, pointed out by jmc. 2019-05-11 06:40:01 +00:00
nicm
ad27b7decd Do not reduce window height by status line height for control mode
clients, from George Nachman.
2019-05-11 06:34:56 +00:00
Thomas Adam
fc00839adc Update CHANGES 2019-05-10 22:27:33 +01:00
Thomas Adam
aa13bd4016 Merge branch 'obsd-master' 2019-05-10 22:10:38 +01:00
nicm
d62fd78655 Fix a typo in previous (, -> :). 2019-05-10 18:09:51 +00:00
nicm
6dcca5fda4 Add support for simple menus usable with mouse or keyboard. New command
display-menu shows a menu (bound to the mouse on status line by default)
and a couple of extra formats for the default menus.
2019-05-10 18:04:06 +00:00
Thomas Adam
c5f6ea5c0d Merge branch 'obsd-master' 2019-05-10 17:02:27 +01:00
nicm
004a9b52f0 Add a function to draw a simple menu onto a screen. 2019-05-10 14:12:47 +00:00
Thomas Adam
e5f06d2cf6 Merge branch 'obsd-master' 2019-05-09 17:02:25 +01:00
nicm
cb10bfb8ef Save mouse buttons as well as position. 2019-05-09 14:09:32 +00:00
Thomas Adam
2e00d775e4 Merge branch 'obsd-master' 2019-05-09 15:02:26 +01:00
nicm
21d9750450 send-keys also needs to insert key commands in the right order. 2019-05-09 13:12:59 +00:00
Thomas Adam
f431e20f3d Merge branch 'obsd-master' 2019-05-09 11:02:25 +01:00
nicm
3c68e51609 Change swap-window -d to be the other way round (stay with src window),
so it works like swap-pane.
2019-05-09 08:39:09 +00:00
nicm
299b7289ea Add formats to show if pane is the marked pane and if any marked pane is set. 2019-05-09 08:38:13 +00:00
Thomas Adam
4bc45fc95a Merge branch 'obsd-master' 2019-05-08 21:02:25 +01:00
nicm
f9682d2e55 Add a flag to redraw only the overlay, and remove the overlay on resize. 2019-05-08 18:07:12 +00:00
nicm
a384245c5a Adjust how mouse targets are found so they always have a session, window
and pane.
2019-05-08 18:05:03 +00:00
Thomas Adam
b24d7d9c95 Merge branch 'obsd-master' 2019-05-07 23:02:30 +01:00
Thomas Adam
453a62c672 Merge branch 'obsd-master' 2019-05-07 21:02:25 +01:00
nicm
89db309e10 Move around the display-panes identify code to make it a bit more
generic and hide the display-panes specific bits into
cmd-display-panes.c.
2019-05-07 20:01:41 +00:00
nicm
d53d3bce59 Adjust the same bit to adjust the selection for history-top and
history-bottom as for cursor-up and cursor-down. GitHub issue 1723.
2019-05-07 19:46:17 +00:00
Thomas Adam
3439c02e9d Merge branch 'obsd-master' 2019-05-07 17:02:25 +01:00
schwarze
fcd0e3082b Rename the ENVIRONMENT section which was squatting on the standard
manual page section and create a new ENVIRONMENT with the expected
content.  Move some information that was misplaced below the -u
flag into that new section.
Feedback and OK nicm@ jmc@ tb@
2019-05-07 14:01:39 +00:00
Thomas Adam
d9767b8112 Merge branch 'obsd-master' 2019-05-07 13:02:27 +01:00
nicm
85a9c2f52b Treat keys in identify mode (display-panes) specially and handle them
immediately rather than queuing them (the command can block the queue
which means they were not being seen until it finished which was too
late). Reported by denis@ and solene@, ok solene@.
2019-05-07 11:24:03 +00:00
nicm
69440d19b7 Do not use evbuffer_add_buffer because it is destructive and doesn't
work in newer libevent.
2019-05-07 10:25:15 +00:00
Nicholas Marriott
eac30a86d7 -attr -> -style. 2019-05-06 21:57:32 +01:00
Thomas Adam
d4177e954c Merge branch 'obsd-master' 2019-05-03 23:02:28 +01:00
nicm
45ae9a8e35 Fix order of insertion in load_cfg. 2019-05-03 21:21:00 +00:00
nicm
9f75635596 Allow panes to be empty (no command), output can be piped to them with
split-window or display-message -I.
2019-05-03 20:44:24 +00:00
Thomas Adam
4d505574dc Merge branch 'obsd-master' 2019-05-03 21:02:26 +01:00
nicm
e8e4f4ec3e Insert after the right element on queue. 2019-05-03 18:59:58 +00:00
nicm
4097257bef Do not store the mouse position we calculate as the start of a drag back
into the mouse event that later code uses, it has been adjusted and they
should use the original position. GitHub issue 1710.
2019-05-03 18:42:40 +00:00
Thomas Adam
a14512e23e Merge branch 'obsd-master' 2019-05-03 19:02:27 +01:00
nicm
84e4652513 Use the right index for user-keys. 2019-05-03 18:00:19 +00:00
nicm
fc3d85e34b Fix mouse positioning when the pane is not entirely visible. 2019-05-03 16:51:29 +00:00
Nicholas Marriott
43656d4ea7 Sync up some tmux.1 bits. 2019-05-03 17:12:30 +01:00
Thomas Adam
fff85d854e Merge branch 'obsd-master' 2019-05-03 17:04:11 +01:00
nicm
cf6075fb29 Correct ordering when adding after an existing item. 2019-05-03 15:43:01 +00:00
nicm
33298d6df6 Instead of processing keys all together, put them up on the client
command queue so they are ordered correctly with the commands that they
execute.
2019-05-03 14:51:30 +00:00
nicm
4bb48998e0 Fix reverse attribute in status line, GitHub issue 1709. 2019-05-03 10:00:48 +00:00
nicm
5d9e591ae8 Fix up some bits about window-size that seem to have got lost. 2019-05-02 20:12:40 +00:00
Thomas Adam
4d16df9312 Merge branch 'obsd-master' 2019-05-01 09:02:27 +01:00
Nicholas Marriott
18e554aa61 Merge tag '2.9a'
2.9a
2019-05-01 07:29:23 +01:00
nicm
c176361788 Remove unused variable from Thomas Adam. 2019-05-01 06:07:14 +00:00
Thomas Adam
dc7e53897a Merge branch 'obsd-master' 2019-04-30 21:02:26 +01:00
nicm
429c4bc51b Fix user options after show-hooks merge, GitHub issue 1704. 2019-04-30 18:02:03 +00:00
Nicholas Marriott
1156d91cf8 Simple -C sanity test. 2019-04-30 18:39:07 +01:00
Thomas Adam
4c5b0fbbcc Merge branch 'obsd-master' 2019-04-30 09:02:29 +01:00
nicm
5a288b1efe Don't redraw control clients, from George Nachman. 2019-04-30 06:21:30 +00:00
nicm
82c789ee58 Fix memory leak in window tree search, from Amos Bird. 2019-04-30 06:19:51 +00:00
Nicholas Marriott
7e0f9ab3eb Don't think I will do this. 2019-04-29 21:48:15 +01:00
Nicholas Marriott
87bd8965c6 Add to CHANGES & remove from TODO. 2019-04-29 20:18:07 +01:00
Thomas Adam
2aa517c805 Merge branch 'obsd-master' 2019-04-29 09:02:25 +01:00
nicm
ec81bd2399 Add support for keys to jump between matching brackets - C-M-f and C-M-b
in emacs, % in vi. Suggested by and help from Chris Barber in GitHub
issue 1666.
2019-04-29 06:55:21 +00:00
Thomas Adam
3ab229da70 Merge branch 'obsd-master' 2019-04-28 23:02:30 +01:00
Nicholas Marriott
2cecabd75e Fix gseq use in RGB colour script. 2019-04-28 22:16:34 +01:00
nicm
c4b0da5513 Support multiple occurances of the same argument. Use this for a new
flag -e to new-window, split-window, respawn-window, respawn-pane to
pass environment variables into the newly created process. From Steffen
Christgau in GitHub issue 1697.
2019-04-28 20:05:50 +00:00
Thomas Adam
5489796737 Merge branch 'obsd-master' 2019-04-27 20:09:07 +01:00
Thomas Adam
85f09f9a4c Merge branch 'obsd-master' 2019-04-26 13:02:37 +01:00
Nicholas Marriott
293fd0d258 Update CHANGES and TODO. 2019-04-26 12:44:25 +01:00
nicm
dfb7bb6830 Merge hooks into options and make each one an array option. This allows
multiple commands to be easily bound to one hook. set-hook and
show-hooks remain but they are now variants of set-option and
show-options. show-options now has a -H flag to show hooks (by default
they are not shown).
2019-04-26 11:38:51 +00:00
nicm
f1e14f86c4 Destroy panes before options to avoid crash when forced into a mode by a
hook.
2019-04-26 10:24:26 +00:00
nicm
6644d209d2 Unbreak main-vertical and main-horizontal layouts. 2019-04-26 10:15:40 +00:00
Thomas Adam
7c4a2253e8 Merge branch 'obsd-master' 2019-04-25 21:02:43 +01:00
nicm
2d65bbd941 options_array_item_value cannot return NULL. 2019-04-25 19:36:59 +00:00
nicm
a609e6361a Need a fallback for -2 for aixterm colours. 2019-04-25 19:03:43 +00:00
nicm
32a81e197b Make options_tostring allocate its result instead of using a stack
buffer (needed for something in the future).
2019-04-25 18:18:55 +00:00
Thomas Adam
f2c0605d6d Merge branch 'obsd-master' 2019-04-25 17:02:37 +01:00
nicm
1677bb0dea Need to escape ]. 2019-04-25 15:35:07 +00:00
Nicholas Marriott
dddc544b8f Update CHANGES. 2019-04-25 13:00:32 +01:00
Thomas Adam
643eecde86 Merge branch 'obsd-master' 2019-04-25 09:02:36 +01:00
nicm
567d3e27ab Automatically scroll if dragging to create a selection with the mouse
and the cursor reaches the top or bottom line.
2019-04-25 06:34:57 +00:00
Nicholas Marriott
a2e08b587a Merge branch '2.9-rc' 2019-04-25 07:29:02 +01:00
Nicholas Marriott
cb75ec25c8 2.9 now. 2019-04-24 22:35:23 +01:00
nicm
7d06216289 Do not loop forever if there is a nonprintable character in the format. 2019-04-24 22:34:56 +01:00
Thomas Adam
4ab208ecc8 Merge branch 'obsd-master' 2019-04-24 22:09:57 +01:00
nicm
6aa0bedad2 Use bg not fg when adjusting for aixterm, from Ailin Nemui. 2019-04-24 20:32:31 +00:00
nicm
c6e39976c6 Do not loop forever if there is a nonprintable character in the format. 2019-04-24 20:27:52 +00:00
Thomas Adam
c869366133 Merge branch 'obsd-master' 2019-04-23 23:02:42 +01:00
nicm
b9022e33ea Somehow missed these bits in last commit. 2019-04-23 20:40:03 +00:00
nicm
772b3b7a06 Indicate an array option with a flag rather than a special type so that
in future will not have to be strings.
2019-04-23 20:36:55 +00:00
Thomas Adam
b0d7623b7e Merge branch 'obsd-master' 2019-04-23 13:02:37 +01:00
Thomas Adam
4a96f599f6 Merge branch 'obsd-master' 2019-04-23 11:02:36 +01:00
nicm
564e44adc6 Add -no-clear variants of copy-selection and copy-pipe which do not
clear the selection after copying. Make copy-pipe clear the selection by
default to be consistent with copy-selection. From Avi Halachmi.
2019-04-23 09:39:07 +00:00
nicm
6752f41c2a Do not try to resize if the parent cell is NULL, problem reported by
Sunil Nimmagadda.
2019-04-23 09:15:24 +00:00
Nicholas Marriott
51c09bf2b4 Merge branch '2.9-rc' 2019-04-18 22:14:55 +01:00
Nicholas Marriott
8d752f5aaa Update CHANGES. 2019-04-18 22:11:46 +01:00
Nicholas Marriott
e7827f8bc2 Remove duplicate entry. 2019-04-18 14:16:19 +01:00
Thomas Adam
f980e868dd Merge branch 'obsd-master' 2019-04-18 14:11:22 +01:00
Thomas Adam
3c1f0cfc34 Merge branch 'obsd-master' 2019-04-18 14:08:13 +01:00
nicm
5b9211d827 Copy the code to infer the option type to show-options and document it. 2019-04-18 12:22:07 +00:00
nicm
3f189945d8 Pass target client and session to load_cfg from source-file so formats
work. Reported by Thomas Sattler.
2019-04-18 11:07:28 +00:00
nicm
f3ab05e7cd Update session activity on focus event, from tafryn at gmail dot com. 2019-04-18 10:11:52 +00:00
jmc
2219f7cc73 mark up punctuation-as-macro-args properly; 2019-04-17 16:34:35 +00:00
nicm
1fd3b9ec1c Fix minimum size check on split and size of first cell on spread out
with a pane status line.
2019-04-17 14:44:33 +00:00
nicm
da31eddadc Rewrite main-vertical and horizontal to use the common spread out code
and to handle the case where the panes won't fit into the existing
window size.
2019-04-17 14:44:14 +00:00
nicm
c660e46149 Set the window size as well as the layout size when using the preset
layouts.
2019-04-17 14:43:49 +00:00
nicm
e3b1358bbc Do not let the size of the pane status screen go negative. 2019-04-17 14:41:08 +00:00
nicm
5943cd1907 Document that switch-client can change all of session,window,pane and
check for % in the target as well as ":.".
2019-04-17 14:39:37 +00:00
nicm
78287e27c8 Break new window and pane creation common code from various commands and
window.c into a separate file spawn.c.
2019-04-17 14:37:48 +00:00
Nicholas Marriott
82bc2c87a9 Add to TODO. 2019-04-17 09:10:23 +01:00
Nicholas Marriott
e6ee3e9504 Warning fixes from Carlo Marcelo Arenas Belón. 2019-04-10 19:27:09 +01:00
Nicholas Marriott
1c1ec84aa3 Fix minimum size check on split and size of first cell on spread out with a
pane status line.
2019-04-10 14:54:02 +01:00
Nicholas Marriott
f3fcf977e4 Use the right winlink pointer in new-window. 2019-04-10 11:40:59 +01:00
Nicholas Marriott
4a3ac3bd26 Remove unused variable. 2019-04-10 07:27:21 +01:00
Nicholas Marriott
3c5d3a3780 Add PATH_DEFPATH to compat.h from Carlo Marcelo Arenas Belón. 2019-04-09 21:31:50 +01:00
Nicholas Marriott
fe44f90e5b Do not let the size of the pane status screen go negative. 2019-04-09 21:30:03 +01:00
Nicholas Marriott
031e9bc854 Rewrite main-vertical and horizontal to use the common spread out code and to
handle the case where the panes won't fit into the existing window size.
2019-04-09 21:15:00 +01:00
Nicholas Marriott
0cbccc90ab Set the window size as well as the layout size when using the preset layouts
and calculate the sizes correctly.
2019-04-09 20:38:43 +01:00
Nicholas Marriott
71d90c11dd Solaris has no paths.h. 2019-04-09 07:52:54 +01:00
Thomas Adam
2546572e8e Merge branch 'obsd-master' 2019-04-08 21:02:37 +01:00
nicm
835ccbac46 select-word was missing from the command list, from pawel-slowik. 2019-04-08 17:55:51 +00:00
Nicholas Marriott
b5b67c5386 Document that switch-client can change all of session,window,pane and check for
% in the target as well as ":.".
2019-04-08 10:38:50 +01:00
Thomas Adam
fbe488e4de Merge branch 'obsd-master' 2019-04-07 23:02:37 +01:00
nicm
bb629f3be7 Current window style also needs to be tested for default. 2019-04-07 20:18:20 +00:00
Thomas Adam
883a428e27 Linux: remove include for util.h
Linux doesn't require this header.
2019-04-07 15:39:58 +01:00
Nicholas Marriott
e44e2c48dd Do not log NULL command. 2019-04-07 13:16:55 +01:00
Nicholas Marriott
ece737274e Forgot to add the file. 2019-04-07 13:11:55 +01:00
Nicholas Marriott
5ece386cdf Break new window and pane creation common code from various commands and
window.c into a separate file spawn.c.
2019-04-07 13:01:03 +01:00
Nicholas Marriott
7653328ce7 Remove from TODO one thing that I don't think is necessary and one that is
done.
2019-04-07 12:18:40 +01:00
Nicholas Marriott
fc111d2b12 Fix a duplicate entry in CHANGES and remove an item from TODO. 2019-04-07 12:07:26 +01:00
Thomas Adam
04402db616 Merge branch 'obsd-master' 2019-04-05 23:02:36 +01:00
nicm
1b5a8a0f09 Fix some warnings, from Thomas Adam. 2019-04-05 20:32:31 +00:00
Nicholas Marriott
e33b623f21 Merge branch '2.9-rc' 2019-04-04 18:31:54 +01:00
Thomas Adam
5a97af7961 Merge branch 'obsd-master' 2019-04-04 13:02:37 +01:00
nicm
f4aefb738e Fix size check for splitw -f and top level pane size for tiled layout,
problems reported by Thomas Sattler.
2019-04-04 10:25:35 +00:00
Thomas Adam
cb039b986e Merge branch 'obsd-master' 2019-04-03 09:02:41 +01:00
nicm
f6c54f3f03 Do not load /etc/tmux.conf if given -f. 2019-04-03 06:43:19 +00:00
nicm
06d58b3b7b screen_write_fast_copy can no longer assume the target screen is default
(it isn't for the pane status lines).
2019-04-03 06:43:04 +00:00
Thomas Adam
c9ef144dca Merge branch 'obsd-master' 2019-04-02 21:02:37 +01:00
nicm
09f71ed1b2 Silence flag should use the same option as activity, reported by Thomas
Sattler.
2019-04-02 18:41:24 +00:00
Nicholas Marriott
f0ddc301b7 Add CHANGES. 2019-04-02 17:02:09 +01:00
Thomas Adam
bfc1f0ca62 Merge branch 'obsd-master' 2019-04-02 11:02:44 +01:00
nicm
7bcc0d16f2 Add an argument to copy commands to set the prefix for the buffer name,
allows buffers for different sessions to be named separately.
2019-04-02 09:03:39 +00:00
Nicholas Marriott
f03776c262 Don't use a config file in tests. 2019-04-02 09:53:02 +01:00
nicm
ffa4d48967 Store and restore cursor across reflow by working out a position based
on unwrapped lines, rather than a grid offset. Fixes problems reported
by Thomas Sattler and Paul de Weerd.
2019-04-02 08:45:32 +00:00
Nicholas Marriott
79c3fd4f39 Add cursor regression tests. 2019-04-02 09:44:28 +01:00
Thomas Adam
294accea1b Merge branch 'obsd-master' 2019-04-01 21:02:37 +01:00
nicm
792fcb1dbf Restore a check to stop scrolled lines becoming larger than total lines,
fixes a crash reported by Thomas Sattler.
2019-04-01 19:33:38 +00:00
Nicholas Marriott
da359269cb Merge branch '2.9-rc' 2019-03-29 11:20:15 +00:00
Thomas Adam
ecc5cea09b Merge branch 'obsd-master' 2019-03-29 10:02:36 +00:00
nicm
d68a17a1df Free old strings after they have been expanded in format_choose. 2019-03-29 09:33:24 +00:00
Nicholas Marriott
52cb7a6382 Merge branch '2.9-rc' 2019-03-29 07:05:50 +00:00
Thomas Adam
405bb8435c Merge branch 'obsd-master' 2019-03-29 00:02:36 +00:00
nicm
c6c4960b35 Fix offset of list ranges. 2019-03-28 22:18:46 +00:00
Thomas Adam
2c4f2393ec Merge branch 'obsd-master' 2019-03-28 22:02:37 +00:00
nicm
08bc226527 Expand session and window formats for buffer filters. 2019-03-28 21:05:15 +00:00
Thomas Adam
6b38334b28 Merge branch 'obsd-master' 2019-03-27 14:02:36 +00:00
nicm
c1f0918f8a Fix stop-selection, from Avi Halachmi. 2019-03-27 13:25:11 +00:00
Thomas Adam
6861045d38 Merge branch 'obsd-master' 2019-03-26 22:02:36 +00:00
nicm
9f8d193b11 Break copy mode commands into individual functions instead of a big load
of if statements.
2019-03-26 21:01:19 +00:00
Nicholas Marriott
a13e57f701 Update TODO. 2019-03-26 17:04:47 +00:00
Nicholas Marriott
e722ba38e3 There is no examples directory anymore. 2019-03-26 15:10:05 +00:00
Nicholas Marriott
b1dc2b5353 Merge branch '2.9-rc' 2019-03-26 15:05:41 +00:00
Nicholas Marriott
0a913b09cf For 3.0. 2019-03-26 14:38:20 +00:00
147 changed files with 17069 additions and 6564 deletions

78
.github/CONTRIBUTING.md vendored Normal file
View File

@@ -0,0 +1,78 @@
## What should I do before opening an issue?
Before opening an issue, please ensure that:
- Your problem is a specific problem or question or suggestion, not a general
complaint.
- `$TERM` inside tmux is screen, screen-256color, tmux or tmux-256color. Check
by running `echo $TERM` inside tmux.
- You can reproduce the problem with the latest tmux release, or a build from
Git master.
- Your question or issue is not covered [in the
manual](https://man.openbsd.org/tmux.1) (run `man tmux`).
- Your problem is not mentioned in [the CHANGES
file](https://raw.githubusercontent.com/tmux/tmux/master/CHANGES).
- Nobody else has opened the same issue recently.
## What should I include in an issue?
Please include the output of:
~~~bash
uname -sp && tmux -V && echo $TERM
~~~
Also include:
- Your platform (Linux, OS X, or whatever).
- A brief description of the problem with steps to reproduce.
- A minimal tmux config, if you can't reproduce without a config.
- Your terminal, and `$TERM` inside and outside of tmux.
- Logs from tmux (see below).
- At most one or two screenshots, if helpful.
## How do I test without a .tmux.conf?
Run a separate tmux server with `-f/dev/null` to skip loading `.tmux.conf`:
~~~bash
tmux -Ltest kill-server
tmux -Ltest -f/dev/null new
~~~
## How do I get logs from tmux?
Add `-vv` to tmux to create three log files in the current directory. If you can
reproduce without a configuration file:
~~~bash
tmux -Ltest kill-server
tmux -vv -Ltest -f/dev/null new
~~~
Or if you need your configuration:
~~~base
tmux kill-server
tmux -vv new
~~~
The log files are:
- `tmux-server*.log`: server log file.
- `tmux-client*.log`: client log file.
- `tmux-out*.log`: output log file.
Please attach the log files to your issue.

2
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,2 @@
github: nicm
liberapay: tmux

22
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,22 @@
### Issue description
Please read https://github.com/tmux/tmux/blob/master/.github/CONTRIBUTING.md
before opening an issue.
If you have upgraded, make sure your issue is not covered in the CHANGES file
for your version: https://raw.githubusercontent.com/tmux/tmux/master/CHANGES
Describe the problem and the steps to reproduce. Add a minimal tmux config if
necessary. Screenshots can be helpful, but no more than one or two.
Do not report bugs (crashes, incorrect behaviour) without reproducing on a tmux
built from the latest code in Git.
### Required information
Please provide the following information:
* tmux version (`tmux -V`).
* Platform (`uname -sp`).
* $TERM inside and outside of tmux (`echo $TERM`).
* Logs from tmux (`tmux kill-server; tmux -vv new`).

83
.github/README.md vendored Normal file
View File

@@ -0,0 +1,83 @@
# Welcome to tmux!
tmux is a terminal multiplexer: it enables a number of terminals to be created,
accessed, and controlled from a single screen. tmux may be detached from a
screen and continue running in the background, then later reattached.
This release runs on OpenBSD, FreeBSD, NetBSD, Linux, OS X and Solaris.
## Dependencies
tmux depends on [libevent](https://libevent.org) 2.x, available from [this
page](https://github.com/libevent/libevent/releases/latest).
It also depends on [ncurses](https://www.gnu.org/software/ncurses/), available
from [this page](https://invisible-mirror.net/archives/ncurses/).
## Installation
### From release tarball
To build and install tmux from a release tarball, use:
~~~bash
./configure && make
sudo make install
~~~
tmux can use the utempter library to update utmp(5), if it is installed - run
configure with `--enable-utempter` to enable this.
### From version control
To get and build the latest from version control - note that this requires
`autoconf`, `automake` and `pkg-config`:
~~~bash
git clone https://github.com/tmux/tmux.git
cd tmux
sh autogen.sh
./configure && make
~~~
## Contributing
Bug reports, feature suggestions and especially code contributions are most
welcome. Please send by email to:
tmux-users@googlegroups.com
Or open a GitHub issue or pull request. **Please read [this
document](CONTRIBUTING.md) before opening an issue.**
There is [a list of suggestions for contributions](https://github.com/tmux/tmux/wiki/Contributing).
Please feel free to ask on the mailing list if you're thinking of working on something or need
further information.
## Documentation
For documentation on using tmux, see the tmux.1 manpage. View it from the
source tree with:
~~~bash
nroff -mdoc tmux.1|less
~~~
A small example configuration is in `example_tmux.conf`.
And a bash(1) completion file at:
https://github.com/imomaliev/tmux-bash-completion
For debugging, run tmux with `-v` or `-vv` to generate server and client log
files in the current directory.
## Support
The tmux mailing list for general discussion and bug reports is:
https://groups.google.com/forum/#!forum/tmux-users
Subscribe by sending an email to:
tmux-users+subscribe@googlegroups.com

10
.github/lock.yml vendored Normal file
View File

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

1
.gitignore vendored
View File

@@ -18,3 +18,4 @@ Makefile.in
configure
tmux.1.*
*.dSYM
cmd-parse.c

View File

@@ -1,10 +1,16 @@
language: c
matrix:
include:
- compiler: gcc
- compiler: clang
env: CFLAGS="-g -O2"
os:
- linux
- osx
compiler:
- gcc
- clang
before_install:
- sudo apt-get update -qq
- sudo apt-get -y install debhelper autotools-dev dh-autoreconf file libncurses5-dev libevent-dev pkg-config libutempter-dev build-essential
script: (CFLAGS= ./autogen.sh) && ./configure --enable-debug && make
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get update -qq; fi
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get -y install bison autotools-dev libncurses5-dev libevent-dev pkg-config libutempter-dev build-essential; fi
script:
- ./autogen.sh && ./configure && make

293
CHANGES
View File

@@ -1,8 +1,295 @@
CHANGES FROM 2.9 to 2.9a
CHANGED FROM 3.1b TO 3.1c
* Fix a stack overflow on colon-separated CSI parsing.
CHANGES FROM 3.1a TO 3.1b
* Fix build on systems without sys/queue.h.
* Fix crash when allow-rename is on and an empty name is set.
CHANGES FROM 3.1 TO 3.1a
* Do not close stdout prematurely in control mode since it is needed to print
exit messages. Prevents hanging when detaching with iTerm2.
CHANGES FROM 3.0a TO 3.1
* Only search the visible part of the history when marking (highlighting)
search terms. This is much faster than searching the whole history and solves
problems with large histories. The count of matches shown is now the visible
matches rather than all matches.
* Search using regular expressions in copy mode. search-forward and
search-backward use regular expressions by default; the incremental versions
do not.
* Turn off mouse mode 1003 as well as the rest when exiting.
* Add selection_active format for when the selection is present but not moving
with the cursor.
* Fix dragging with modifier keys, so binding keys such as C-MouseDrag1Pane and
C-MouseDragEnd1Pane now work.
* Add -a to list-keys to also list keys without notes with -N.
* Do not jump to next word end if already on a word end when selecting a word;
fixes select-word with single character words and vi(1) keys.
* Fix top and bottom pane calculation with pane border status enabled.
* Add support for adding a note to a key binding (with bind-key -N) and use
this to add descriptions to the default key bindings. A new -N flag to
list-keys shows key bindings with notes. Change the default ? binding to use
this to show a readable summary of keys. Also extend command-prompt to return
the name of the key pressed and add a default binding (/) to show the note
for the next key pressed.
* Add support for the iTerm2 DSR 1337 sequence to get the terminal version.
* Treat plausible but invalid keys (like C-BSpace) as literal like any other
unrecognised string passed to send-keys.
* Detect iTerm2 and enable use of DECSLRM (much faster with horizontally split
windows).
* Add -Z to default switch-client command in tree mode.
* Add ~ to quoted characters for %%%.
* Document client exit messages in the manual page.
* Do not let read-only clients limit the size, unless all clients are
read-only.
* Add a number of new formats to inspect what sessions and clients a window is
present or active in.
* Change file reading and writing to go through the client if necessary. This
fixes commands like "tmux loadb /dev/fd/X". Also modify source-file to
support "-" for standard input, like load-buffer and save-buffer.
* Add ~/.config/tmux/tmux.conf to the default search path for configuration
files.
* Bump the escape sequence timeout to five seconds to allow for longer
legitimate sequences.
* Make a best effort to set xpixel and ypixel for each pane and add formats for
them.
* Add push-default to status-left and status-right in status-format[0].
* Do not clear search marks on cursor movement with vi(1) keys.
* Add p format modifier for padding to width and allow multiple substitutions
in a single format.
* Add -f for full size to join-pane (like split-window).
* Do not use bright when emulating 256 colours on an 8 colour terminal because
it is also bold on some terminals.
* Make select-pane -P set window-active-style also to match previous behaviour.
* Do not truncate list-keys output.
* Turn automatic-rename back on if the \033k rename escape sequence is used
with an empty name.
* Add support for percentage sizes for resize-pane ("-x 10%"). Also change
split-window and join-pane -l to accept similar percentages and deprecate the
-p flag.
* Add -F flag to send-keys to expand formats in search-backward and forward
copy mode commands and copy_cursor_word and copy_cursor_line formats for word
and line at cursor in copy mode. Use for default # and * binding with vi(1)
keys.
* Add formats for word and line at cursor position in copy mode.
* Add formats for cursor and selection position in copy mode.
* Support all the forms of RGB colour strings in OSC sequences rather than
requiring two digits.
* Limit lazy resize to panes in attached sessions only.
* Add an option to set the key sent by backspace for those whose system uses ^H
rather than ^?.
* Change new-session -A without a session name (that is, no -s option also) to
attach to the best existing session like attach-session rather than a new
one.
* Add a "latest" window-size option which tries to size windows based on the
most recently used client. This is now the default.
* Add simple support for OSC 7 (result is available in the pane_path format).
* Add push-default and pop-default for styles which change the colours and
attributes used for #[default]. These are used in status-format to restore
the behaviour of window-status-style being the default for
window-status-format.
* Add window_marked_flag.
* Add cursor-down-and-cancel in copy mode.
* Default to previous search string for search-forward and search-backward.
* Add -Z flag to rotate-window, select-pane, swap-pane, switch-client to
preserve zoomed state.
* Add -N to capture-pane to preserve trailing spaces.
* Add reverse sorting in tree, client and buffer modes.
CHANGES FROM 3.0 TO 3.0a
* Do not require REG_STARTEND.
* Respawn panes or windows correctly if default-command is set.
* Add missing option for after-kill-pane hook.
* Fix for crash with a format variable that doesn't exist.
* Do not truncate list-keys output on some platforms.
* Do not crash when restoring a layout with only one pane.
CHANGES FROM 2.9 TO 3.0
* Workaround invalid layout strings generated by older tmux versions and add
some additional sanity checks
* xterm 348 now disables margins when resized, so send DECLRMM again after
resize.
* Add support for the SD (scroll down) escape sequence.
* Expand arguments to C and s format modifiers to match the m modifier.
* Add support for underscore colours (Setulc capability must be added with
terminal-overrides as described in tmux(1)).
* Add a "fill" style attribute for the fill colour of the drawing area (where
appropriate).
* New -H flag to send-keys to send literal keys.
* Format variables for pane mouse modes (mouse_utf8_flag and mouse_sgr_flag)
and for origin mode (origin_flag).
* Add -F to refresh-client for flags for control mode clients, only one flag
(no-output) supported at the moment.
* Add a few vi(1) keys for menus.
* Add pane options, set with set-option -p and displayed with show-options -p.
Pane options inherit from window options (so every pane option is also
a window option). The pane style is now configured by setting window-style
and window-active-style in the pane options; select-pane -P and -g now change
the option but are no longer documented.
* Do not document set-window-option and show-window-options. set-option -w and
show-options -w should be used instead.
* Add a -A flag to show-options to show parent options as well (they are marked
with a *).
* Resize panes lazily - do not resize unless they are in an attached, active
window.
* Add regular expression support for the format search, match and substitute
modifiers and make them able to ignore case. find-window now accepts -r to
use regular expressions.
* Do not use $TMUX to find the session because for windows in multiple sessions
it is wrong as often as it is right, and for windows in one session it is
pointless. Instead use TMUX_PANE if it is present.
* Do not always resize the window back to its original size after applying a
layout, keep it at the layout size until it must be resized (for example when
attached and window-size is not manual).
* Add new-session -X and attach-session -x to send SIGHUP to parent when
detaching (like detach-client -P).
* Support for octal escapes in strings (such as \007) and improve list-keys
output so it parses correctly if copied into a configuration file.
* INCOMPATIBLE: Add a new {} syntax to the configuration file. This is a string
similar to single quotes but also includes newlines and allows commands that
take other commands as string arguments to be expressed more clearly and
without additional escaping.
A literal { and } or a string containing { or } must now be escaped or
quoted, for example '{' and '}' instead of { or }, or 'X#{foo}' instead of
X#{foo}.
* New <, >, <= and >= comparison operators for formats.
* Improve escaping of special characters in list-keys output.
* INCOMPATIBLE: tmux's configuration parsing has changed to use yacc(1). There
is one incompatible change: a \ on its own must be escaped or quoted as
either \\ or '\' (the latter works on older tmux versions).
Entirely the same parser is now used for parsing the configuration file
and for string commands. This means that constructs previously only
available in .tmux.conf, such as %if, can now be used in string commands
(for example, those given to if-shell - not commands invoked from the
shell, they are still parsed by the shell itself).
* Add support for the overline attribute (SGR 53). The Smol capability is
needed in terminal-overrides.
* Add the ability to create simple menus. Introduces new command
display-menu. Default menus are bound to MouseDown3 on the status line;
MouseDown3 or M-MouseDown3 on panes; MouseDown3 in tree, client and
buffer modes; and C-b < and >.
* Allow panes to be empty (no command). They can be created either by piping to
split-window -I, or by passing an empty command ('') to split-window. Output
can be sent to an existing empty window with display-message -I.
* Add keys to jump between matching brackets (emacs C-M-f and C-M-b, vi %).
* Add a -e flag to new-window, split-window, respawn-window, respawn-pane to
pass environment variables into the newly created process.
* Hooks are now stored in the options tree as array options, allowing them to
have multiple separate commands. set-hook and show-hooks remain but
set-option and show-options can now also be used (show-options will only show
hooks if given the -H flag). Hooks with multiple commands are run in index
order.
* Automatically scroll if dragging to create a selection with the mouse and the
cursor reaches the top or bottom line.
* Add -no-clear variants of copy-selection and copy-pipe which do not clear the
selection after copying. Make copy-pipe clear the selection by default to be
consistent with copy-selection.
* Add an argument to copy commands to set the prefix for the buffer name, this
(for example) allows buffers for different sessions to be named separately.
* Update session activity on focus event.
* Pass target from source-file into the config file parser so formats in %if
and %endif have access to more useful variables.
* Add the ability to infer an option type (server, session, window) from its
name to show-options (it was already present in set-option).
CHANGES FROM 2.9 TO 2.9a
* Fix bugs in select-pane and the main-horizontal and main-vertical layouts.
CHANGES FROM 2.8 to 2.9
CHANGES FROM 2.8 TO 2.9
* Attempt to preserve horizontal cursor position as well as vertical with
reflow.
@@ -127,7 +414,7 @@ CHANGES FROM 2.8 to 2.9
moves up, down, left or right and -c returns to automatic cursor
tracking. The position is reset when the current window is changed.
CHANGES FROM 2.7 to 2.8
CHANGES FROM 2.7 TO 2.8
* Make display-panes block the client until a pane is chosen or it
times out.

View File

@@ -1,33 +0,0 @@
When reporting issues:
YOU MUST INCLUDE THE TMUX VERSION
DO NOT OPEN AN ISSUE THAT DOES NOT MENTION THE TMUX VERSION
Please also include:
- your platform (Linux, OS X, or whatever);
- a brief description of the problem with steps to reproduce;
- a minimal tmux config, if you can't reproduce without a config;
- your terminal, and $TERM inside and outside of tmux;
- logs from tmux (see below);
- at most one or two screenshots, if helpful.
This should include at least the output of:
$ uname -sp && tmux -V && echo $TERM
Please do not report bugs (crashes, incorrect behaviour) without reproducing on
a tmux built from the latest code in Git.
Note that TERM inside tmux must be a variant of screen or tmux (for example:
screen or screen-256color, tmux or tmux-256color). Please ensure this is the
case before opening an issue.
To run tmux without a config and get logs, run:
tmux -Ltest kill-server
tmux -vv -Ltest -f/dev/null new
Then reproduce the problem, exit tmux, and attach the tmux-server-*.log file
from the current directory to the issue.

View File

@@ -1,10 +1,7 @@
THIS IS FOR INFORMATION ONLY, CODE IS UNDER THE LICENCE AT THE TOP OF ITS FILE.
The README, CHANGES, FAQ and TODO files are licensed under the ISC
license. Files under examples/ remain copyright their authors unless otherwise
stated in the file but permission has been received to distribute them with
tmux. All other files have a license and copyright notice at their start,
typically:
The README, CHANGES, FAQ and TODO files are licensed under the ISC license. All
other files have a license and copyright notice at their start, typically:
Copyright (c) <author>

View File

@@ -1 +0,0 @@
Please read https://raw.githubusercontent.com/tmux/tmux/master/CONTRIBUTING

View File

@@ -2,15 +2,18 @@
# Obvious program stuff.
bin_PROGRAMS = tmux
CLEANFILES = tmux.1.mdoc tmux.1.man
CLEANFILES = tmux.1.mdoc tmux.1.man cmd-parse.c
# Distribution tarball options.
EXTRA_DIST = \
CHANGES README README.ja COPYING example_tmux.conf compat/*.[ch] \
CHANGES README README.ja COPYING example_tmux.conf \
osdep-*.c mdoc2man.awk tmux.1
dist_EXTRA_tmux_SOURCES = compat/*.[ch]
# Preprocessor flags.
AM_CPPFLAGS += @XOPEN_DEFINES@ -DTMUX_CONF="\"$(sysconfdir)/tmux.conf\""
AM_CPPFLAGS += @XOPEN_DEFINES@ \
-DTMUX_VERSION="\"@VERSION@\"" \
-DTMUX_CONF="\"$(sysconfdir)/tmux.conf:~/.tmux.conf:~/.config/tmux/tmux.conf\""
# Additional object files.
LDADD = $(LIBOBJS)
@@ -71,6 +74,7 @@ dist_tmux_SOURCES = \
cmd-confirm-before.c \
cmd-copy-mode.c \
cmd-detach-client.c \
cmd-display-menu.c \
cmd-display-message.c \
cmd-display-panes.c \
cmd-find-window.c \
@@ -87,12 +91,12 @@ dist_tmux_SOURCES = \
cmd-list-panes.c \
cmd-list-sessions.c \
cmd-list-windows.c \
cmd-list.c \
cmd-load-buffer.c \
cmd-lock-server.c \
cmd-move-window.c \
cmd-new-session.c \
cmd-new-window.c \
cmd-parse.y \
cmd-paste-buffer.c \
cmd-pipe-pane.c \
cmd-queue.c \
@@ -112,14 +116,12 @@ dist_tmux_SOURCES = \
cmd-send-keys.c \
cmd-set-buffer.c \
cmd-set-environment.c \
cmd-set-hook.c \
cmd-set-option.c \
cmd-show-environment.c \
cmd-show-messages.c \
cmd-show-options.c \
cmd-source-file.c \
cmd-split-window.c \
cmd-string.c \
cmd-swap-pane.c \
cmd-swap-window.c \
cmd-switch-client.c \
@@ -131,11 +133,11 @@ dist_tmux_SOURCES = \
control-notify.c \
control.c \
environ.c \
file.c \
format.c \
format-draw.c \
grid-view.c \
grid.c \
hooks.c \
input-keys.c \
input.c \
job.c \
@@ -145,6 +147,7 @@ dist_tmux_SOURCES = \
layout-set.c \
layout.c \
log.c \
menu.c \
mode-tree.c \
names.c \
notify.c \
@@ -152,6 +155,7 @@ dist_tmux_SOURCES = \
options.c \
paste.c \
proc.c \
regsub.c \
resize.c \
screen-redraw.c \
screen-write.c \
@@ -160,6 +164,7 @@ dist_tmux_SOURCES = \
server-fn.c \
server.c \
session.c \
spawn.c \
status.c \
style.c \
tmux.c \

78
README
View File

@@ -6,13 +6,17 @@ screen and continue running in the background, then later reattached.
This release runs on OpenBSD, FreeBSD, NetBSD, Linux, OS X and Solaris.
tmux depends on libevent 2.x. Download it from:
* Dependencies
http://libevent.org
tmux depends on libevent 2.x, available from:
https://github.com/libevent/libevent/releases/latest
It also depends on ncurses, available from:
http://invisible-island.net/ncurses/
https://invisible-mirror.net/archives/ncurses/
* Installation
To build and install tmux from a release tarball, use:
@@ -22,50 +26,50 @@ To build and install tmux from a release tarball, use:
tmux can use the utempter library to update utmp(5), if it is installed - run
configure with --enable-utempter to enable this.
To get and build the latest from version control:
To get and build the latest from version control - note that this requires
autoconf, automake and pkg-config:
$ git clone https://github.com/tmux/tmux.git
$ cd tmux
$ sh autogen.sh
$ ./configure && make
(Note that this requires at least a working C compiler, make, autoconf,
automake, pkg-config as well as libevent and ncurses libraries and headers.)
For more information see http://git-scm.com. Patches should be sent by email to
the mailing list at tmux-users@googlegroups.com or submitted through GitHub at
https://github.com/tmux/tmux/issues.
For documentation on using tmux, see the tmux.1 manpage. It can be viewed from
the source tree with:
$ nroff -mdoc tmux.1|less
A small example configuration in example_tmux.conf.
And a bash(1) completion file at:
https://github.com/imomaliev/tmux-bash-completion
For debugging, running tmux with -v or -vv will generate server and client log
files in the current directory.
tmux mailing lists are available. For general discussion and bug reports:
https://groups.google.com/forum/#!forum/tmux-users
And for Git commit emails:
https://groups.google.com/forum/#!forum/tmux-git
Subscribe by sending an email to <tmux-users+subscribe@googlegroups.com>.
* Contributing
Bug reports, feature suggestions and especially code contributions are most
welcome. Please send by email to:
tmux-users@googlegroups.com
This file and the CHANGES, FAQ, SYNCING and TODO files are licensed under the
ISC license. All other files have a license and copyright notice at their start.
Or open a GitHub issue or pull request.
-- Nicholas Marriott <nicholas.marriott@gmail.com>
* Documentation
For documentation on using tmux, see the tmux.1 manpage. View it from the
source tree with:
$ nroff -mdoc tmux.1|less
A small example configuration is in example_tmux.conf.
A bash(1) completion file is at:
https://github.com/imomaliev/tmux-bash-completion
For debugging, run tmux with -v and -vv to generate server and client log files
in the current directory.
* Support
The tmux mailing list for general discussion and bug reports is:
https://groups.google.com/forum/#!forum/tmux-users
Subscribe by sending an email to:
tmux-users+subscribe@googlegroups.com
* License
This file and the CHANGES files are licensed under the ISC license. All other
files have a license and copyright notice at their start.

161
TODO
View File

@@ -1,161 +0,0 @@
- command bits and pieces:
* allow multiple targets: fnmatch for -t/-c, for example detach all
clients with -t*
* ' and " should be parsed the same (eg "\e" vs '\e') in config
and command prompt
* last-pane across sessions
* resize-pane -p to match split-window -p
* flag to wait-for to have a timeout and/or to stop waiting when the
client gets a signal
- make command sequences more usable
* don't require space after ;
* options for error handling: && and ||?
- options bits and pieces:
* way to set socket path from config file
- format improvements:
* some way to pad # stuff with spaces
* formats to show if a window is linked into multiple sessions, into
multiple attached sessions, and is the active window in multiple
attached sessions?
* comparison operators like < and > (for #{version}?)
- improve monitor-*:
* straighten out rules for multiple clients
* think about what happens across sessions
* monitor changes within a region
* perhaps monitor /all/ panes in the window not just one
- improve mouse support:
* bind commands to mouse in different areas?
* commands executed when clicking on a pattern (URL)
- warts on current naming:
* display-time but message-fg/bg/attr
* list-* vs show-*
- copy/paste improvements:
* paste w/o trailing whitespace
* command to toggle selection not to move it in copy-mode
* regex searching
* searching in copy mode should unwrap lines, so if you search for "foobar"
then it should be found even if it is now "foo\nbar" (if the WRAP flag
is set on the line)
* capture-pane option to preserve spaces but not join lines
* improve word and line selection in copy mode (for example when
dragging it should select by word. compare how xterm works. GitHub
issue 682)
* key to search for word under cursor (GitHub issue 1240)
* when entering copy mode, should copy grid so that input does not
need to be suspended
* allow the prefix for automatic buffers to be specified as part of the
key binding to allow session buffers or similar (GitHub issue 1501)
* copy-pipe should be synchronous (GitHub issue 1517)
- layout stuff
* way to tag a layout as a number/name
* maybe keep last layout + size around and if size reverts just put it
back
* revamp layouts: they are too complicated, should be more closely
integrated, should support hints, layout sets should just be a
special case of custom layouts, and we should support panes that are
not attached to a cell at all. this could be the time to introduce
panelink to replace layout_cell
* way to set hints/limits about pane size for resizing
* a mode where one application can cross two panes (ie x|y, width =
COLUMNS/2 but height = ROWS * 2)
* separate active panes for different clients
* way to choose where the freed space goes when a pane is killed:
option to kill-pane? GitHub issue 918
- code cleanup
* instead of separate window and session options, just one master
options list with each option having a type (window or session), then
options on window, on session, and global. for window options we look
window->session->global, and for session we look session->global.
problem: what about windows in multiple sessions? there are contexts
where we do not know which session, or where multiple choices makes
no sense... could at least have one global list for all types of
global options and keep separate window,session lists
* the way pane, window, session destroy is handled is too complicated
and the distinction between session.c, window.c and server-fn.c
functions is not clear. could we just have kill_pane(),
kill_window(), unlink_window(), kill_session() that fix up all data
structures (flagging sessions as dead) and return a value to say
whether clients need to be checked for dead sessions? sort of like
session_detach now but more so. or some other scheme to make it
simpler and clearer? also would be nice to remove/rename server-fn.c
* more readable way to work out the various things commands need to
know about the client, notably:
- is this the config file? (cmdq->c == NULL)
- is this a command client? (cmdq->c != NULL &&
cmdq->c->session == NULL)
- is this a control client?
- can i do stdin or stdout to this client?
or even guarantee that cmdq->c != NULL and provide a better way to
tell when in the config file - then we use cmdq->c if we need a
client w/o a session else cmd_current_client
- miscellaneous
* link panes into multiple windows
* live update: server started with -U connects to server, requests
sessions and windows, receives file descriptors
* there are inconsistencies in what we get from old shell and what
comes from config for new sessions and windows. likewise, panes and
jobs and run-shell and lock command all start with slightly different
environments
* multiline status line? separate command prompt and status line?
* automatic pane logging
* marks in history, automatically add (move?) one when pane is changed
* this doesn't work, need pane reference count probably:
bind -n DoubleClick3Status confirm-before -p "kill-window #I? (y/n)" kill-window
* marker lines in history (GitHub issue 1042)
* tree mode stuff: make command prompt (:) common code so all modes get it,
predefined filters, tag-all key, ...
* drag panes and windows around to move/swap them in choose mode
* flag to specify environment to new-window, split-window,
new-session (issue 1498)
* multiple column panes (issue 1503)
* support for ZERO WIDTH JOINER U+200D
* individual pane synchronize-panes (with pane options?); issue 1638
- hooks
* more hooks for various things
* finish after hooks for special commands. these do not have a hook at
the moment:
attach-session detach-client kill-server respawn-window
swap-window break-pane find-window kill-session rotate-window
switch-client choose-tree if-shell kill-window run-shell
wait-for command-prompt join-pane move-window source-file
confirm-before kill-pane respawn-pane swap-pane
at the moment AFTERHOOK uses current only if target is not valid,
but target is ALWAYS valid - it should use current if no -t flag?
then select-* could use AFTERHOOK
* multiple hooks with the same name?
* finish hooks for notifys
* for session_closed, if no sessions at all, perhaps fake up a
temporary one
- pan
* tty_window_offset should try to keep as much off active pane
visible as possible
* rather than centering cursor it might be better if only
moved offset when it gets close to an edge?
----
TODO soonish maybe:
- Store hooks as options, issue 1619.
- Support buffer prefixes, issue 1501.
- copy-pipe should be synchronous, issue 1517.
- -E flag to pass environment to new-*, issue 1498.
- Copy mode searching is slow when there is a big history, issue 1545.
- Grid "block" stuff, issue 1269. Can be used potentially for compression of
history (bit silly really though), reflow performance (can reflow blocks on
demand). It would possibly be good if history-limit could be global and
collected LRU.
- Command aliases should be able to override builtin commands in order to add
default flags (some mechanism needed to avoid recursion). GitHub issue 1630.

View File

@@ -28,9 +28,16 @@
* Manipulate command arguments.
*/
struct args_value {
char *value;
TAILQ_ENTRY(args_value) entry;
};
TAILQ_HEAD(args_values, args_value);
struct args_entry {
u_char flag;
char *value;
struct args_values values;
u_int count;
RB_ENTRY(args_entry) entry;
};
@@ -67,6 +74,7 @@ args_parse(const char *template, int argc, char **argv)
optreset = 1;
optind = 1;
optarg = NULL;
while ((opt = getopt(argc, argv, template)) != -1) {
if (opt < 0)
@@ -76,6 +84,7 @@ args_parse(const char *template, int argc, char **argv)
return (NULL);
}
args_set(args, opt, optarg);
optarg = NULL;
}
argc -= optind;
argv += optind;
@@ -92,12 +101,18 @@ args_free(struct args *args)
{
struct args_entry *entry;
struct args_entry *entry1;
struct args_value *value;
struct args_value *value1;
cmd_free_argv(args->argc, args->argv);
RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) {
RB_REMOVE(args_tree, &args->tree, entry);
free(entry->value);
TAILQ_FOREACH_SAFE(value, &entry->values, entry, value1) {
TAILQ_REMOVE(&entry->values, value, entry);
free(value->value);
free(value);
}
free(entry);
}
@@ -123,94 +138,146 @@ args_print_add(char **buf, size_t *len, const char *fmt, ...)
free(s);
}
/* Add value to string. */
static void
args_print_add_value(char **buf, size_t *len, struct args_entry *entry,
struct args_value *value)
{
char *escaped;
if (**buf != '\0')
args_print_add(buf, len, " -%c ", entry->flag);
else
args_print_add(buf, len, "-%c ", entry->flag);
escaped = args_escape(value->value);
args_print_add(buf, len, "%s", escaped);
free(escaped);
}
/* Add argument to string. */
static void
args_print_add_argument(char **buf, size_t *len, const char *argument)
{
char *escaped;
if (**buf != '\0')
args_print_add(buf, len, " ");
escaped = args_escape(argument);
args_print_add(buf, len, "%s", escaped);
free(escaped);
}
/* Print a set of arguments. */
char *
args_print(struct args *args)
{
size_t len;
char *buf, *escaped;
int i, flags;
char *buf;
int i;
u_int j;
struct args_entry *entry;
static const char quoted[] = " #\"';$";
struct args_value *value;
len = 1;
buf = xcalloc(1, len);
/* Process the flags first. */
RB_FOREACH(entry, args_tree, &args->tree) {
if (entry->value != NULL)
if (!TAILQ_EMPTY(&entry->values))
continue;
if (*buf == '\0')
args_print_add(&buf, &len, "-");
args_print_add(&buf, &len, "%c", entry->flag);
for (j = 0; j < entry->count; j++)
args_print_add(&buf, &len, "%c", entry->flag);
}
/* Then the flags with arguments. */
RB_FOREACH(entry, args_tree, &args->tree) {
if (entry->value == NULL)
continue;
if (*buf != '\0')
args_print_add(&buf, &len, " -%c ", entry->flag);
else
args_print_add(&buf, &len, "-%c ", entry->flag);
flags = VIS_OCTAL|VIS_TAB|VIS_NL;
if (entry->value[strcspn(entry->value, quoted)] != '\0')
flags |= VIS_DQ;
utf8_stravis(&escaped, entry->value, flags);
if (flags & VIS_DQ)
args_print_add(&buf, &len, "\"%s\"", escaped);
else
args_print_add(&buf, &len, "%s", escaped);
free(escaped);
TAILQ_FOREACH(value, &entry->values, entry)
args_print_add_value(&buf, &len, entry, value);
}
/* And finally the argument vector. */
for (i = 0; i < args->argc; i++) {
if (*buf != '\0')
args_print_add(&buf, &len, " ");
flags = VIS_OCTAL|VIS_TAB|VIS_NL;
if (args->argv[i][strcspn(args->argv[i], quoted)] != '\0')
flags |= VIS_DQ;
utf8_stravis(&escaped, args->argv[i], flags);
if (flags & VIS_DQ)
args_print_add(&buf, &len, "\"%s\"", escaped);
else
args_print_add(&buf, &len, "%s", escaped);
free(escaped);
}
for (i = 0; i < args->argc; i++)
args_print_add_argument(&buf, &len, args->argv[i]);
return (buf);
}
/* Escape an argument. */
char *
args_escape(const char *s)
{
static const char quoted[] = " #\"';${}";
char *escaped, *result;
int flags;
if (*s == '\0')
return (xstrdup(s));
if (s[0] != ' ' &&
(strchr(quoted, s[0]) != NULL || s[0] == '~') &&
s[1] == '\0') {
xasprintf(&escaped, "\\%c", s[0]);
return (escaped);
}
flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL;
if (s[strcspn(s, quoted)] != '\0')
flags |= VIS_DQ;
utf8_stravis(&escaped, s, flags);
if (flags & VIS_DQ) {
if (*escaped == '~')
xasprintf(&result, "\"\\%s\"", escaped);
else
xasprintf(&result, "\"%s\"", escaped);
} else {
if (*escaped == '~')
xasprintf(&result, "\\%s", escaped);
else
result = xstrdup(escaped);
}
free(escaped);
return (result);
}
/* Return if an argument is present. */
int
args_has(struct args *args, u_char ch)
{
return (args_find(args, ch) != NULL);
struct args_entry *entry;
entry = args_find(args, ch);
if (entry == NULL)
return (0);
return (entry->count);
}
/* Set argument value in the arguments tree. */
void
args_set(struct args *args, u_char ch, const char *value)
args_set(struct args *args, u_char ch, const char *s)
{
struct args_entry *entry;
struct args_value *value;
/* Replace existing argument. */
if ((entry = args_find(args, ch)) != NULL) {
free(entry->value);
entry->value = NULL;
} else {
entry = args_find(args, ch);
if (entry == NULL) {
entry = xcalloc(1, sizeof *entry);
entry->flag = ch;
entry->count = 1;
TAILQ_INIT(&entry->values);
RB_INSERT(args_tree, &args->tree, entry);
}
} else
entry->count++;
if (value != NULL)
entry->value = xstrdup(value);
if (s != NULL) {
value = xcalloc(1, sizeof *value);
value->value = xstrdup(s);
TAILQ_INSERT_TAIL(&entry->values, value, entry);
}
}
/* Get argument value. Will be NULL if it isn't present. */
@@ -221,7 +288,34 @@ args_get(struct args *args, u_char ch)
if ((entry = args_find(args, ch)) == NULL)
return (NULL);
return (entry->value);
return (TAILQ_LAST(&entry->values, args_values)->value);
}
/* Get first value in argument. */
const char *
args_first_value(struct args *args, u_char ch, struct args_value **value)
{
struct args_entry *entry;
if ((entry = args_find(args, ch)) == NULL)
return (NULL);
*value = TAILQ_FIRST(&entry->values);
if (*value == NULL)
return (NULL);
return ((*value)->value);
}
/* Get next value in argument. */
const char *
args_next_value(struct args_value **value)
{
if (*value == NULL)
return (NULL);
*value = TAILQ_NEXT(*value, entry);
if (*value == NULL)
return (NULL);
return ((*value)->value);
}
/* Convert an argument value to a number. */
@@ -232,13 +326,15 @@ args_strtonum(struct args *args, u_char ch, long long minval, long long maxval,
const char *errstr;
long long ll;
struct args_entry *entry;
struct args_value *value;
if ((entry = args_find(args, ch)) == NULL) {
*cause = xstrdup("missing");
return (0);
}
value = TAILQ_LAST(&entry->values, args_values);
ll = strtonum(entry->value, minval, maxval, &errstr);
ll = strtonum(value->value, minval, maxval, &errstr);
if (errstr != NULL) {
*cause = xstrdup(errstr);
return (0);

View File

@@ -31,7 +31,7 @@ attributes_tostring(int attr)
if (attr == 0)
return ("none");
len = xsnprintf(buf, sizeof buf, "%s%s%s%s%s%s%s%s%s%s%s%s",
len = xsnprintf(buf, sizeof buf, "%s%s%s%s%s%s%s%s%s%s%s%s%s",
(attr & GRID_ATTR_BRIGHT) ? "bright," : "",
(attr & GRID_ATTR_DIM) ? "dim," : "",
(attr & GRID_ATTR_UNDERSCORE) ? "underscore," : "",
@@ -43,7 +43,8 @@ attributes_tostring(int attr)
(attr & GRID_ATTR_UNDERSCORE_2) ? "double-underscore," : "",
(attr & GRID_ATTR_UNDERSCORE_3) ? "curly-underscore," : "",
(attr & GRID_ATTR_UNDERSCORE_4) ? "dotted-underscore," : "",
(attr & GRID_ATTR_UNDERSCORE_5) ? "dashed-underscore," : "");
(attr & GRID_ATTR_UNDERSCORE_5) ? "dashed-underscore," : "",
(attr & GRID_ATTR_OVERLINE) ? "overline," : "");
if (len > 0)
buf[len - 1] = '\0';
@@ -58,8 +59,8 @@ attributes_fromstring(const char *str)
size_t end;
u_int i;
struct {
const char* name;
int attr;
const char *name;
int attr;
} table[] = {
{ "bright", GRID_ATTR_BRIGHT },
{ "bold", GRID_ATTR_BRIGHT },
@@ -73,7 +74,8 @@ attributes_fromstring(const char *str)
{ "double-underscore", GRID_ATTR_UNDERSCORE_2 },
{ "curly-underscore", GRID_ATTR_UNDERSCORE_3 },
{ "dotted-underscore", GRID_ATTR_UNDERSCORE_4 },
{ "dashed-underscore", GRID_ATTR_UNDERSCORE_5 }
{ "dashed-underscore", GRID_ATTR_UNDERSCORE_5 },
{ "overline", GRID_ATTR_OVERLINE }
};
if (*str == '\0' || strcspn(str, delimiters) == 0)

335
cfg.c
View File

@@ -26,17 +26,6 @@
#include "tmux.h"
/* Condition for %if, %elif, %else and %endif. */
struct cfg_cond {
size_t line; /* line number of %if */
int met; /* condition was met */
int skip; /* skip later %elif/%else */
int saw_else; /* saw a %else */
TAILQ_ENTRY(cfg_cond) entry;
};
TAILQ_HEAD(cfg_conds, cfg_cond);
struct client *cfg_client;
static char *cfg_file;
int cfg_finished;
@@ -63,7 +52,7 @@ cfg_done(__unused struct cmdq_item *item, __unused void *data)
cfg_show_causes(RB_MIN(sessions, &sessions));
if (cfg_item != NULL)
cfg_item->flags &= ~CMDQ_WAITING;
cmdq_continue(cfg_item);
status_prompt_load_history();
@@ -77,17 +66,49 @@ set_cfg_file(const char *path)
cfg_file = xstrdup(path);
}
static char *
expand_cfg_file(const char *path, const char *home)
{
char *expanded, *name;
const char *end;
struct environ_entry *value;
if (strncmp(path, "~/", 2) == 0) {
if (home == NULL)
return (NULL);
xasprintf(&expanded, "%s%s", home, path + 1);
return (expanded);
}
if (*path == '$') {
end = strchr(path, '/');
if (end == NULL)
name = xstrdup(path + 1);
else
name = xstrndup(path + 1, end - path - 1);
value = environ_find(global_environ, name);
free(name);
if (value == NULL)
return (NULL);
if (end == NULL)
end = "";
xasprintf(&expanded, "%s%s", value->value, end);
return (expanded);
}
return (xstrdup(path));
}
void
start_cfg(void)
{
const char *home;
int quiet = 0;
const char *home = find_home();
struct client *c;
char *path, *copy, *next, *expanded;
/*
* Configuration files are loaded without a client, so NULL is passed
* into load_cfg() and commands run in the global queue with
* item->client NULL.
* Configuration files are loaded without a client, so commands are run
* in the global queue with item->client NULL.
*
* However, we must block the initial client (but just the initial
* client) so that its command runs after the configuration is loaded.
@@ -101,210 +122,122 @@ start_cfg(void)
cmdq_append(c, cfg_item);
}
load_cfg(TMUX_CONF, NULL, NULL, 1);
if (cfg_file == NULL && (home = find_home()) != NULL) {
xasprintf(&cfg_file, "%s/.tmux.conf", home);
quiet = 1;
}
if (cfg_file != NULL)
load_cfg(cfg_file, NULL, NULL, quiet);
if (cfg_file == NULL) {
path = copy = xstrdup(TMUX_CONF);
while ((next = strsep(&path, ":")) != NULL) {
expanded = expand_cfg_file(next, home);
if (expanded == NULL) {
log_debug("couldn't expand %s", next);
continue;
}
log_debug("expanded %s to %s", next, expanded);
load_cfg(expanded, c, NULL, CMD_PARSE_QUIET, NULL);
free(expanded);
}
free(copy);
} else
load_cfg(cfg_file, c, NULL, 0, NULL);
cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL));
}
static int
cfg_check_condition(const char *path, size_t line, const char *p, int *skip)
{
struct format_tree *ft;
char *s;
int result;
while (isspace((u_char)*p))
p++;
if (p[0] == '\0') {
cfg_add_cause("%s:%zu: invalid condition", path, line);
*skip = 1;
return (0);
}
ft = format_create(NULL, NULL, FORMAT_NONE, FORMAT_NOJOBS);
s = format_expand(ft, p);
result = format_true(s);
free(s);
format_free(ft);
*skip = result;
return (result);
}
static void
cfg_handle_if(const char *path, size_t line, struct cfg_conds *conds,
const char *p)
{
struct cfg_cond *cond;
struct cfg_cond *parent = TAILQ_FIRST(conds);
/*
* Add a new condition. If a previous condition exists and isn't
* currently met, this new one also can't be met.
*/
cond = xcalloc(1, sizeof *cond);
cond->line = line;
if (parent == NULL || parent->met)
cond->met = cfg_check_condition(path, line, p, &cond->skip);
else
cond->skip = 1;
cond->saw_else = 0;
TAILQ_INSERT_HEAD(conds, cond, entry);
}
static void
cfg_handle_elif(const char *path, size_t line, struct cfg_conds *conds,
const char *p)
{
struct cfg_cond *cond = TAILQ_FIRST(conds);
/*
* If a previous condition exists and wasn't met, check this
* one instead and change the state.
*/
if (cond == NULL || cond->saw_else)
cfg_add_cause("%s:%zu: unexpected %%elif", path, line);
else if (!cond->skip)
cond->met = cfg_check_condition(path, line, p, &cond->skip);
else
cond->met = 0;
}
static void
cfg_handle_else(const char *path, size_t line, struct cfg_conds *conds)
{
struct cfg_cond *cond = TAILQ_FIRST(conds);
/*
* If a previous condition exists and wasn't met and wasn't already
* %else, use this one instead.
*/
if (cond == NULL || cond->saw_else) {
cfg_add_cause("%s:%zu: unexpected %%else", path, line);
return;
}
cond->saw_else = 1;
cond->met = !cond->skip;
cond->skip = 1;
}
static void
cfg_handle_endif(const char *path, size_t line, struct cfg_conds *conds)
{
struct cfg_cond *cond = TAILQ_FIRST(conds);
/*
* Remove previous condition if one exists.
*/
if (cond == NULL) {
cfg_add_cause("%s:%zu: unexpected %%endif", path, line);
return;
}
TAILQ_REMOVE(conds, cond, entry);
free(cond);
}
static void
cfg_handle_directive(const char *p, const char *path, size_t line,
struct cfg_conds *conds)
{
int n = 0;
while (p[n] != '\0' && !isspace((u_char)p[n]))
n++;
if (strncmp(p, "%if", n) == 0)
cfg_handle_if(path, line, conds, p + n);
else if (strncmp(p, "%elif", n) == 0)
cfg_handle_elif(path, line, conds, p + n);
else if (strcmp(p, "%else") == 0)
cfg_handle_else(path, line, conds);
else if (strcmp(p, "%endif") == 0)
cfg_handle_endif(path, line, conds);
else
cfg_add_cause("%s:%zu: invalid directive: %s", path, line, p);
}
int
load_cfg(const char *path, struct client *c, struct cmdq_item *item, int quiet)
load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags,
struct cmdq_item **new_item)
{
FILE *f;
const char delim[3] = { '\\', '\\', '\0' };
u_int found = 0;
size_t line = 0;
char *buf, *cause1, *p, *q;
struct cmd_list *cmdlist;
struct cmdq_item *new_item;
struct cfg_cond *cond, *cond1;
struct cfg_conds conds;
struct cmd_parse_input pi;
struct cmd_parse_result *pr;
struct cmdq_item *new_item0;
TAILQ_INIT(&conds);
if (new_item != NULL)
*new_item = NULL;
log_debug("loading %s", path);
if ((f = fopen(path, "rb")) == NULL) {
if (errno == ENOENT && quiet)
if (errno == ENOENT && (flags & CMD_PARSE_QUIET))
return (0);
cfg_add_cause("%s: %s", path, strerror(errno));
return (-1);
}
while ((buf = fparseln(f, NULL, &line, delim, 0)) != NULL) {
log_debug("%s: %s", path, buf);
memset(&pi, 0, sizeof pi);
pi.flags = flags;
pi.file = path;
pi.line = 1;
pi.item = item;
pi.c = c;
p = buf;
while (isspace((u_char)*p))
p++;
if (*p == '\0') {
free(buf);
continue;
}
q = p + strlen(p) - 1;
while (q != p && isspace((u_char)*q))
*q-- = '\0';
if (*p == '%') {
cfg_handle_directive(p, path, line, &conds);
continue;
}
cond = TAILQ_FIRST(&conds);
if (cond != NULL && !cond->met)
continue;
cmdlist = cmd_string_parse(p, path, line, &cause1);
if (cmdlist == NULL) {
free(buf);
if (cause1 == NULL)
continue;
cfg_add_cause("%s:%zu: %s", path, line, cause1);
free(cause1);
continue;
}
free(buf);
new_item = cmdq_get_command(cmdlist, NULL, NULL, 0);
if (item != NULL)
cmdq_insert_after(item, new_item);
else
cmdq_append(c, new_item);
cmd_list_free(cmdlist);
found++;
}
pr = cmd_parse_from_file(f, &pi);
fclose(f);
TAILQ_FOREACH_REVERSE_SAFE(cond, &conds, cfg_conds, entry, cond1) {
cfg_add_cause("%s:%zu: unterminated %%if", path, cond->line);
TAILQ_REMOVE(&conds, cond, entry);
free(cond);
if (pr->status == CMD_PARSE_EMPTY)
return (0);
if (pr->status == CMD_PARSE_ERROR) {
cfg_add_cause("%s", pr->error);
free(pr->error);
return (-1);
}
if (flags & CMD_PARSE_PARSEONLY) {
cmd_list_free(pr->cmdlist);
return (0);
}
return (found);
new_item0 = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
if (item != NULL)
new_item0 = cmdq_insert_after(item, new_item0);
else
new_item0 = cmdq_append(NULL, new_item0);
cmd_list_free(pr->cmdlist);
if (new_item != NULL)
*new_item = new_item0;
return (0);
}
int
load_cfg_from_buffer(const void *buf, size_t len, const char *path,
struct client *c, struct cmdq_item *item, int flags,
struct cmdq_item **new_item)
{
struct cmd_parse_input pi;
struct cmd_parse_result *pr;
struct cmdq_item *new_item0;
if (new_item != NULL)
*new_item = NULL;
log_debug("loading %s", path);
memset(&pi, 0, sizeof pi);
pi.flags = flags;
pi.file = path;
pi.line = 1;
pi.item = item;
pi.c = c;
pr = cmd_parse_from_buffer(buf, len, &pi);
if (pr->status == CMD_PARSE_EMPTY)
return (0);
if (pr->status == CMD_PARSE_ERROR) {
cfg_add_cause("%s", pr->error);
free(pr->error);
return (-1);
}
if (flags & CMD_PARSE_PARSEONLY) {
cmd_list_free(pr->cmdlist);
return (0);
}
new_item0 = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
if (item != NULL)
new_item0 = cmdq_insert_after(item, new_item0);
else
new_item0 = cmdq_append(NULL, new_item0);
cmd_list_free(pr->cmdlist);
if (new_item != NULL)
*new_item = new_item0;
return (0);
}
void

375
client.c
View File

@@ -35,7 +35,6 @@
static struct tmuxproc *client_proc;
static struct tmuxpeer *client_peer;
static int client_flags;
static struct event client_stdin;
static enum {
CLIENT_EXIT_NONE,
CLIENT_EXIT_DETACHED,
@@ -46,19 +45,19 @@ static enum {
CLIENT_EXIT_EXITED,
CLIENT_EXIT_SERVER_EXITED,
} client_exitreason = CLIENT_EXIT_NONE;
static int client_exitflag;
static int client_exitval;
static enum msgtype client_exittype;
static const char *client_exitsession;
static const char *client_execshell;
static const char *client_execcmd;
static int client_attached;
static struct client_files client_files = RB_INITIALIZER(&client_files);
static __dead void client_exec(const char *,const char *);
static int client_get_lock(char *);
static int client_connect(struct event_base *, const char *, int);
static void client_send_identify(const char *, const char *);
static void client_stdin_callback(int, short, void *);
static void client_write(int, const char *, size_t);
static void client_signal(int);
static void client_dispatch(struct imsg *, void *);
static void client_dispatch_attached(struct imsg *);
@@ -202,7 +201,7 @@ client_exit_message(void)
case CLIENT_EXIT_TERMINATED:
return ("terminated");
case CLIENT_EXIT_LOST_SERVER:
return ("lost server");
return ("server exited unexpectedly");
case CLIENT_EXIT_EXITED:
return ("exited");
case CLIENT_EXIT_SERVER_EXITED:
@@ -211,18 +210,38 @@ client_exit_message(void)
return ("unknown reason");
}
/* Exit if all streams flushed. */
static void
client_exit(void)
{
struct client_file *cf;
size_t left;
int waiting = 0;
RB_FOREACH (cf, client_files, &client_files) {
if (cf->event == NULL)
continue;
left = EVBUFFER_LENGTH(cf->event->output);
if (left != 0) {
waiting++;
log_debug("file %u %zu bytes left", cf->stream, left);
}
}
if (waiting == 0)
proc_exit(client_proc);
}
/* Client main loop. */
int
client_main(struct event_base *base, int argc, char **argv, int flags)
{
struct cmd_parse_result *pr;
struct cmd *cmd;
struct cmd_list *cmdlist;
struct msg_command_data *data;
struct msg_command *data;
int cmdflags, fd, i;
const char *ttynam, *cwd;
pid_t ppid;
enum msgtype msg;
char *cause;
struct termios tio, saved_tio;
size_t size;
@@ -248,14 +267,15 @@ client_main(struct event_base *base, int argc, char **argv, int flags)
* later in server) but it is necessary to get the start server
* flag.
*/
cmdlist = cmd_list_parse(argc, argv, NULL, 0, &cause);
if (cmdlist != NULL) {
TAILQ_FOREACH(cmd, &cmdlist->list, qentry) {
pr = cmd_parse_from_arguments(argc, argv, NULL);
if (pr->status == CMD_PARSE_SUCCESS) {
TAILQ_FOREACH(cmd, &pr->cmdlist->list, qentry) {
if (cmd->entry->flags & CMD_STARTSERVER)
cmdflags |= CMD_STARTSERVER;
}
cmd_list_free(cmdlist);
}
cmd_list_free(pr->cmdlist);
} else
free(pr->error);
}
/* Create client process structure (starts logging). */
@@ -291,7 +311,9 @@ client_main(struct event_base *base, int argc, char **argv, int flags)
*
* "sendfd" is dropped later in client_dispatch_wait().
*/
if (pledge("stdio unix sendfd proc exec tty", NULL) != 0)
if (pledge(
"stdio rpath wpath cpath unix sendfd proc exec tty",
NULL) != 0)
fatal("pledge failed");
/* Free stuff that is not used in the client. */
@@ -302,10 +324,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags)
options_free(global_w_options);
environ_free(global_environ);
/* Create stdin handler. */
setblocking(STDIN_FILENO, 0);
event_set(&client_stdin, STDIN_FILENO, EV_READ|EV_PERSIST,
client_stdin_callback, NULL);
/* Set up control mode. */
if (client_flags & CLIENT_CONTROLCONTROL) {
if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) {
fprintf(stderr, "tcgetattr failed: %s\n",
@@ -428,39 +447,255 @@ client_send_identify(const char *ttynam, const char *cwd)
proc_send(client_peer, MSG_IDENTIFY_DONE, -1, NULL, 0);
}
/* Callback for client stdin read events. */
/* File write error callback. */
static void
client_stdin_callback(__unused int fd, __unused short events,
__unused void *arg)
client_write_error_callback(__unused struct bufferevent *bev,
__unused short what, void *arg)
{
struct msg_stdin_data data;
struct client_file *cf = arg;
data.size = read(STDIN_FILENO, data.data, sizeof data.data);
if (data.size < 0 && (errno == EINTR || errno == EAGAIN))
return;
log_debug("write error file %d", cf->stream);
proc_send(client_peer, MSG_STDIN, -1, &data, sizeof data);
if (data.size <= 0)
event_del(&client_stdin);
bufferevent_free(cf->event);
cf->event = NULL;
close(cf->fd);
cf->fd = -1;
if (client_exitflag)
client_exit();
}
/* Force write to file descriptor. */
/* File write callback. */
static void
client_write(int fd, const char *data, size_t size)
client_write_callback(__unused struct bufferevent *bev, void *arg)
{
ssize_t used;
struct client_file *cf = arg;
log_debug("%s: %.*s", __func__, (int)size, data);
while (size != 0) {
used = write(fd, data, size);
if (used == -1) {
if (errno == EINTR || errno == EAGAIN)
continue;
break;
}
data += used;
size -= used;
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. */
@@ -555,12 +790,10 @@ client_dispatch(struct imsg *imsg, __unused void *arg)
static void
client_dispatch_wait(struct imsg *imsg)
{
char *data;
ssize_t datalen;
struct msg_stdout_data stdoutdata;
struct msg_stderr_data stderrdata;
int retval;
static int pledge_applied;
char *data;
ssize_t datalen;
int retval;
static int pledge_applied;
/*
* "sendfd" is no longer required once all of the identify messages
@@ -569,10 +802,12 @@ client_dispatch_wait(struct imsg *imsg)
* get the first message from the server.
*/
if (!pledge_applied) {
if (pledge("stdio unix proc exec tty", NULL) != 0)
if (pledge(
"stdio rpath wpath cpath unix proc exec tty",
NULL) != 0)
fatal("pledge failed");
pledge_applied = 1;
};
}
data = imsg->data;
datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
@@ -586,38 +821,16 @@ client_dispatch_wait(struct imsg *imsg)
memcpy(&retval, data, sizeof retval);
client_exitval = retval;
}
proc_exit(client_proc);
client_exitflag = 1;
client_exit();
break;
case MSG_READY:
if (datalen != 0)
fatalx("bad MSG_READY size");
event_del(&client_stdin);
client_attached = 1;
proc_send(client_peer, MSG_RESIZE, -1, NULL, 0);
break;
case MSG_STDIN:
if (datalen != 0)
fatalx("bad MSG_STDIN size");
event_add(&client_stdin, NULL);
break;
case MSG_STDOUT:
if (datalen != sizeof stdoutdata)
fatalx("bad MSG_STDOUT size");
memcpy(&stdoutdata, data, sizeof stdoutdata);
client_write(STDOUT_FILENO, stdoutdata.data,
stdoutdata.size);
break;
case MSG_STDERR:
if (datalen != sizeof stderrdata)
fatalx("bad MSG_STDERR size");
memcpy(&stderrdata, data, sizeof stderrdata);
client_write(STDERR_FILENO, stderrdata.data,
stderrdata.size);
break;
case MSG_VERSION:
if (datalen != 0)
fatalx("bad MSG_VERSION size");
@@ -641,6 +854,24 @@ client_dispatch_wait(struct imsg *imsg)
case MSG_EXITED:
proc_exit(client_proc);
break;
case MSG_READ_OPEN:
client_read_open(data, datalen);
break;
case MSG_WRITE_OPEN:
client_write_open(data, datalen);
break;
case MSG_WRITE:
client_write_data(data, datalen);
break;
case MSG_WRITE_CLOSE:
client_write_close(data, datalen);
break;
case MSG_OLDSTDERR:
case MSG_OLDSTDIN:
case MSG_OLDSTDOUT:
fprintf(stderr, "server version is too old for client\n");
proc_exit(client_proc);
break;
}
}

View File

@@ -37,8 +37,8 @@ const struct cmd_entry cmd_attach_session_entry = {
.name = "attach-session",
.alias = "attach",
.args = { "c:dErt:", 0, 0 },
.usage = "[-dEr] [-c working-directory] " CMD_TARGET_SESSION_USAGE,
.args = { "c:dErt:x", 0, 0 },
.usage = "[-dErx] [-c working-directory] " CMD_TARGET_SESSION_USAGE,
/* -t is special */
@@ -48,7 +48,7 @@ const struct cmd_entry cmd_attach_session_entry = {
enum cmd_retval
cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
int rflag, const char *cflag, int Eflag)
int xflag, int rflag, const char *cflag, int Eflag)
{
struct cmd_find_state *current = &item->shared->current;
enum cmd_find_type type;
@@ -58,6 +58,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
struct winlink *wl;
struct window_pane *wp;
char *cause;
enum msgtype msgtype;
if (RB_EMPTY(&sessions)) {
cmdq_error(item, "no sessions");
@@ -87,7 +88,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
if (wl != NULL) {
if (wp != NULL)
window_set_active_pane(wp->window, wp);
window_set_active_pane(wp->window, wp, 1);
session_set_current(s, wl);
if (wp != NULL)
cmd_find_from_winlink_pane(current, wl, wp, 0);
@@ -102,11 +103,15 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
c->last_session = c->session;
if (c->session != NULL) {
if (dflag) {
if (dflag || xflag) {
if (xflag)
msgtype = MSG_DETACHKILL;
else
msgtype = MSG_DETACH;
TAILQ_FOREACH(c_loop, &clients, entry) {
if (c_loop->session != s || c == c_loop)
continue;
server_client_detach(c_loop, MSG_DETACH);
server_client_detach(c_loop, msgtype);
}
}
if (!Eflag)
@@ -122,6 +127,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
gettimeofday(&s->last_attached_time, NULL);
server_redraw_client(c);
s->curw->flags &= ~WINLINK_ALERTFLAGS;
s->curw->window->latest = c;
} else {
if (server_client_open(c, &cause) != 0) {
cmdq_error(item, "open terminal failed: %s", cause);
@@ -131,11 +137,15 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
if (rflag)
c->flags |= CLIENT_READONLY;
if (dflag) {
if (dflag || xflag) {
if (xflag)
msgtype = MSG_DETACHKILL;
else
msgtype = MSG_DETACH;
TAILQ_FOREACH(c_loop, &clients, entry) {
if (c_loop->session != s || c == c_loop)
continue;
server_client_detach(c_loop, MSG_DETACH);
server_client_detach(c_loop, msgtype);
}
}
if (!Eflag)
@@ -150,6 +160,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
gettimeofday(&s->last_attached_time, NULL);
server_redraw_client(c);
s->curw->flags &= ~WINLINK_ALERTFLAGS;
s->curw->window->latest = c;
if (~c->flags & CLIENT_CONTROL)
proc_send(c->peer, MSG_READY, -1, NULL, 0);
@@ -169,6 +180,6 @@ cmd_attach_session_exec(struct cmd *self, struct cmdq_item *item)
struct args *args = self->args;
return (cmd_attach_session(item, args_get(args, 't'),
args_has(args, 'd'), args_has(args, 'r'), args_get(args, 'c'),
args_has(args, 'E')));
args_has(args, 'd'), args_has(args, 'x'), args_has(args, 'r'),
args_get(args, 'c'), args_has(args, 'E')));
}

View File

@@ -33,8 +33,8 @@ const struct cmd_entry cmd_bind_key_entry = {
.name = "bind-key",
.alias = "bind",
.args = { "cnrT:", 2, -1 },
.usage = "[-cnr] [-T key-table] key "
.args = { "nrN:T:", 2, -1 },
.usage = "[-nr] [-T key-table] [-N note] key "
"command [arguments]",
.flags = CMD_AFTERHOOK,
@@ -44,15 +44,16 @@ const struct cmd_entry cmd_bind_key_entry = {
static enum cmd_retval
cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
char *cause;
struct cmd_list *cmdlist;
key_code key;
const char *tablename;
struct args *args = self->args;
key_code key;
const char *tablename, *note;
struct cmd_parse_result *pr;
char **argv = args->argv;
int argc = args->argc, repeat;
key = key_string_lookup_string(args->argv[0]);
key = key_string_lookup_string(argv[0]);
if (key == KEYC_NONE || key == KEYC_UNKNOWN) {
cmdq_error(item, "unknown key: %s", args->argv[0]);
cmdq_error(item, "unknown key: %s", argv[0]);
return (CMD_RETURN_ERROR);
}
@@ -62,15 +63,24 @@ cmd_bind_key_exec(struct cmd *self, struct cmdq_item *item)
tablename = "root";
else
tablename = "prefix";
repeat = args_has(args, 'r');
cmdlist = cmd_list_parse(args->argc - 1, args->argv + 1, NULL, 0,
&cause);
if (cmdlist == NULL) {
cmdq_error(item, "%s", cause);
free(cause);
if (argc == 2)
pr = cmd_parse_from_string(argv[1], NULL);
else
pr = cmd_parse_from_arguments(argc - 1, argv + 1, NULL);
switch (pr->status) {
case CMD_PARSE_EMPTY:
cmdq_error(item, "empty command");
return (CMD_RETURN_ERROR);
case CMD_PARSE_ERROR:
cmdq_error(item, "%s", pr->error);
free(pr->error);
return (CMD_RETURN_ERROR);
case CMD_PARSE_SUCCESS:
break;
}
key_bindings_add(tablename, key, args_has(args, 'r'), cmdlist);
note = args_get(args, 'N');
key_bindings_add(tablename, key, note, repeat, pr->cmdlist);
return (CMD_RETURN_NORMAL);
}

View File

@@ -76,9 +76,12 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
window_lost_pane(w, wp);
layout_close_pane(wp);
w = wp->window = window_create(w->sx, w->sy);
w = wp->window = window_create(w->sx, w->sy, w->xpixel, w->ypixel);
options_set_parent(wp->options, w->options);
wp->flags |= PANE_STYLECHANGED;
TAILQ_INSERT_HEAD(&w->panes, wp, entry);
w->active = wp;
w->latest = c;
if (!args_has(args, 'n')) {
name = default_window_name(w);

View File

@@ -39,8 +39,8 @@ const struct cmd_entry cmd_capture_pane_entry = {
.name = "capture-pane",
.alias = "capturep",
.args = { "ab:CeE:JpPqS:t:", 0, 0 },
.usage = "[-aCeJpPq] " CMD_BUFFER_USAGE " [-E end-line] "
.args = { "ab:CeE:JNpPqS:t:", 0, 0 },
.usage = "[-aCeJNpPq] " CMD_BUFFER_USAGE " [-E end-line] "
"[-S start-line] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 },
@@ -110,7 +110,7 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
struct grid *gd;
const struct grid_line *gl;
struct grid_cell *gc = NULL;
int n, with_codes, escape_c0, join_lines;
int n, with_codes, escape_c0, join_lines, no_trim;
u_int i, sx, top, bottom, tmp;
char *cause, *buf, *line;
const char *Sflag, *Eflag;
@@ -170,11 +170,12 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
with_codes = args_has(args, 'e');
escape_c0 = args_has(args, 'C');
join_lines = args_has(args, 'J');
no_trim = args_has(args, 'N');
buf = NULL;
for (i = top; i <= bottom; i++) {
line = grid_string_cells(gd, 0, i, sx, &gc, with_codes,
escape_c0, !join_lines);
escape_c0, !join_lines && !no_trim);
linelen = strlen(line);
buf = cmd_capture_pane_append(buf, len, line, linelen);
@@ -192,7 +193,7 @@ static enum cmd_retval
cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c;
struct client *c = item->client;
struct window_pane *wp = item->target.wp;
char *buf, *cause;
const char *bufname;
@@ -213,18 +214,15 @@ cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR);
if (args_has(args, 'p')) {
c = item->client;
if (c == NULL ||
(c->session != NULL && !(c->flags & CLIENT_CONTROL))) {
cmdq_error(item, "can't write to stdout");
if (!file_can_print(c)) {
cmdq_error(item, "can't write output to client");
free(buf);
return (CMD_RETURN_ERROR);
}
evbuffer_add(c->stdout_data, buf, len);
free(buf);
file_print_buffer(c, buf, len);
if (args_has(args, 'P') && len > 0)
evbuffer_add(c->stdout_data, "\n", 1);
server_client_push_stdout(c);
file_print(c, "\n");
free(buf);
} else {
bufname = NULL;
if (args_has(args, 'b'))

View File

@@ -30,8 +30,8 @@ const struct cmd_entry cmd_choose_tree_entry = {
.name = "choose-tree",
.alias = NULL,
.args = { "F:Gf:NO:st:wZ", 0, 1 },
.usage = "[-GNsw] [-F format] [-f filter] [-O sort-order] "
.args = { "F:Gf:NO:rst:wZ", 0, 1 },
.usage = "[-GNrswZ] [-F format] [-f filter] [-O sort-order] "
CMD_TARGET_PANE_USAGE " [template]",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -44,8 +44,8 @@ const struct cmd_entry cmd_choose_client_entry = {
.name = "choose-client",
.alias = NULL,
.args = { "F:f:NO:t:Z", 0, 1 },
.usage = "[-N] [-F format] [-f filter] [-O sort-order] "
.args = { "F:f:NO:rt:Z", 0, 1 },
.usage = "[-NrZ] [-F format] [-f filter] [-O sort-order] "
CMD_TARGET_PANE_USAGE " [template]",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -58,8 +58,8 @@ const struct cmd_entry cmd_choose_buffer_entry = {
.name = "choose-buffer",
.alias = NULL,
.args = { "F:f:NO:t:Z", 0, 1 },
.usage = "[-N] [-F format] [-f filter] [-O sort-order] "
.args = { "F:f:NO:rt:Z", 0, 1 },
.usage = "[-NrZ] [-F format] [-f filter] [-O sort-order] "
CMD_TARGET_PANE_USAGE " [template]",
.target = { 't', CMD_FIND_PANE, 0 },

View File

@@ -40,8 +40,8 @@ const struct cmd_entry cmd_command_prompt_entry = {
.name = "command-prompt",
.alias = NULL,
.args = { "1iI:Np:t:", 0, 1 },
.usage = "[-1Ni] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " "
.args = { "1kiI:Np:t:", 0, 1 },
.usage = "[-1kiN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " "
"[template]",
.flags = 0,
@@ -122,6 +122,8 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
cdata->flags |= PROMPT_NUMERIC;
else if (args_has(args, 'i'))
cdata->flags |= PROMPT_INCREMENTAL;
else if (args_has(args, 'k'))
cdata->flags |= PROMPT_KEY;
status_prompt_set(c, prompt, input, cmd_command_prompt_callback,
cmd_command_prompt_free, cdata, cdata->flags);
free(prompt);
@@ -129,26 +131,15 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_command_prompt_error(struct cmdq_item *item, void *data)
{
char *error = data;
cmdq_error(item, "%s", error);
free(error);
return (CMD_RETURN_NORMAL);
}
static int
cmd_command_prompt_callback(struct client *c, void *data, const char *s,
int done)
{
struct cmd_command_prompt_cdata *cdata = data;
struct cmd_list *cmdlist;
struct cmdq_item *new_item;
char *cause, *new_template, *prompt, *ptr;
char *new_template, *prompt, *ptr;
char *input = NULL;
struct cmd_parse_result *pr;
if (s == NULL)
return (0);
@@ -175,20 +166,22 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s,
return (1);
}
cmdlist = cmd_string_parse(new_template, NULL, 0, &cause);
if (cmdlist == NULL) {
if (cause != NULL) {
new_item = cmdq_get_callback(cmd_command_prompt_error,
cause);
} else
new_item = NULL;
} else {
new_item = cmdq_get_command(cmdlist, NULL, NULL, 0);
cmd_list_free(cmdlist);
}
if (new_item != NULL)
pr = cmd_parse_from_string(new_template, NULL);
switch (pr->status) {
case CMD_PARSE_EMPTY:
new_item = NULL;
break;
case CMD_PARSE_ERROR:
new_item = cmdq_get_error(pr->error);
free(pr->error);
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)
free(new_template);

View File

@@ -82,48 +82,38 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_confirm_before_error(struct cmdq_item *item, void *data)
{
char *error = data;
cmdq_error(item, "%s", error);
free(error);
return (CMD_RETURN_NORMAL);
}
static int
cmd_confirm_before_callback(struct client *c, void *data, const char *s,
__unused int done)
{
struct cmd_confirm_before_data *cdata = data;
struct cmd_list *cmdlist;
struct cmdq_item *new_item;
char *cause;
struct cmd_parse_result *pr;
if (c->flags & CLIENT_DEAD)
return (0);
if (s == NULL || *s == '\0')
return (0);
if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
if (tolower((u_char)s[0]) != 'y' || s[1] != '\0')
return (0);
cmdlist = cmd_string_parse(cdata->cmd, NULL, 0, &cause);
if (cmdlist == NULL) {
if (cause != NULL) {
new_item = cmdq_get_callback(cmd_confirm_before_error,
cause);
} else
new_item = NULL;
} else {
new_item = cmdq_get_command(cmdlist, NULL, NULL, 0);
cmd_list_free(cmdlist);
}
if (new_item != NULL)
pr = cmd_parse_from_string(cdata->cmd, NULL);
switch (pr->status) {
case CMD_PARSE_EMPTY:
new_item = NULL;
break;
case CMD_PARSE_ERROR:
new_item = cmdq_get_error(pr->error);
free(pr->error);
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);
}

View File

@@ -30,8 +30,8 @@ const struct cmd_entry cmd_copy_mode_entry = {
.name = "copy-mode",
.alias = NULL,
.args = { "Met:u", 0, 0 },
.usage = "[-Mu] " CMD_TARGET_PANE_USAGE,
.args = { "eHMt:uq", 0, 0 },
.usage = "[-eHMuq] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 },
@@ -61,6 +61,11 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
struct session *s;
struct window_pane *wp = item->target.wp;
if (args_has(args, 'q')) {
window_pane_reset_mode_all(wp);
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'M')) {
if ((wp = cmd_mouse_pane(&shared->mouse, &s, NULL)) == NULL)
return (CMD_RETURN_NORMAL);
@@ -73,10 +78,10 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
if (window_pane_set_mode(wp, &window_copy_mode, NULL, args) != 0)
return (CMD_RETURN_NORMAL);
if (args_has(args, 'M'))
window_copy_start_drag(c, &shared->mouse);
if (!window_pane_set_mode(wp, &window_copy_mode, NULL, args)) {
if (args_has(args, 'M'))
window_copy_start_drag(c, &shared->mouse);
}
if (args_has(self->args, 'u'))
window_copy_pageup(wp, 0);

178
cmd-display-menu.c Normal file
View File

@@ -0,0 +1,178 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
/*
* Display a menu on a client.
*/
static enum cmd_retval cmd_display_menu_exec(struct cmd *,
struct cmdq_item *);
const struct cmd_entry cmd_display_menu_entry = {
.name = "display-menu",
.alias = "menu",
.args = { "c:t:T:x:y:", 1, -1 },
.usage = "[-c target-client] " CMD_TARGET_PANE_USAGE " [-T title] "
"[-x position] [-y position] name key command ...",
.target = { 't', CMD_FIND_PANE, 0 },
.flags = CMD_AFTERHOOK,
.exec = cmd_display_menu_exec
};
static enum cmd_retval
cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c;
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct window_pane *wp = item->target.wp;
struct cmd_find_state *fs = &item->target;
struct menu *menu = NULL;
struct style_range *sr;
struct menu_item menu_item;
const char *xp, *yp, *key;
char *title, *name;
int at, flags, i;
u_int px, py, ox, oy, sx, sy;
if ((c = cmd_find_client(item, args_get(args, 'c'), 0)) == NULL)
return (CMD_RETURN_ERROR);
if (c->overlay_draw != NULL)
return (CMD_RETURN_NORMAL);
at = status_at_line(c);
if (args_has(args, 'T'))
title = format_single(NULL, args_get(args, 'T'), c, s, wl, wp);
else
title = xstrdup("");
menu = menu_create(title);
for (i = 0; i != args->argc; /* nothing */) {
name = args->argv[i++];
if (*name == '\0') {
menu_add_item(menu, NULL, item, c, fs);
continue;
}
if (args->argc - i < 2) {
cmdq_error(item, "not enough arguments");
free(title);
menu_free(menu);
return (CMD_RETURN_ERROR);
}
key = args->argv[i++];
menu_item.name = name;
menu_item.key = key_string_lookup_string(key);
menu_item.command = args->argv[i++];
menu_add_item(menu, &menu_item, item, c, fs);
}
free(title);
if (menu == NULL) {
cmdq_error(item, "invalid menu arguments");
return (CMD_RETURN_ERROR);
}
if (menu->count == 0) {
menu_free(menu);
return (CMD_RETURN_NORMAL);
}
xp = args_get(args, 'x');
if (xp == NULL)
px = 0;
else if (strcmp(xp, "R") == 0)
px = c->tty.sx - 1;
else if (strcmp(xp, "P") == 0) {
tty_window_offset(&c->tty, &ox, &oy, &sx, &sy);
if (wp->xoff >= ox)
px = wp->xoff - ox;
else
px = 0;
} else if (strcmp(xp, "M") == 0 && item->shared->mouse.valid) {
if (item->shared->mouse.x > (menu->width + 4) / 2)
px = item->shared->mouse.x - (menu->width + 4) / 2;
else
px = 0;
}
else if (strcmp(xp, "W") == 0) {
if (at == -1)
px = 0;
else {
TAILQ_FOREACH(sr, &c->status.entries[0].ranges, entry) {
if (sr->type != STYLE_RANGE_WINDOW)
continue;
if (sr->argument == (u_int)wl->idx)
break;
}
if (sr != NULL)
px = sr->start;
else
px = 0;
}
} else
px = strtoul(xp, NULL, 10);
if (px + menu->width + 4 >= c->tty.sx)
px = c->tty.sx - menu->width - 4;
yp = args_get(args, 'y');
if (yp == NULL)
py = 0;
else if (strcmp(yp, "P") == 0) {
tty_window_offset(&c->tty, &ox, &oy, &sx, &sy);
if (wp->yoff + wp->sy >= oy)
py = wp->yoff + wp->sy - oy;
else
py = 0;
} else if (strcmp(yp, "M") == 0 && item->shared->mouse.valid)
py = item->shared->mouse.y + menu->count + 2;
else if (strcmp(yp, "S") == 0) {
if (at == -1)
py = c->tty.sy;
else if (at == 0)
py = status_line_size(c) + menu->count + 2;
else
py = at;
} else
py = strtoul(yp, NULL, 10);
if (py < menu->count + 2)
py = 0;
else
py -= menu->count + 2;
if (py + menu->count + 2 >= c->tty.sy)
py = c->tty.sy - menu->count - 2;
flags = 0;
if (!item->shared->mouse.valid)
flags |= MENU_NOMOUSE;
if (menu_display(menu, flags, item, px, py, c, fs, NULL, NULL) != 0)
return (CMD_RETURN_NORMAL);
return (CMD_RETURN_WAIT);
}

View File

@@ -39,8 +39,8 @@ const struct cmd_entry cmd_display_message_entry = {
.name = "display-message",
.alias = "display",
.args = { "ac:pt:F:v", 0, 1 },
.usage = "[-apv] [-c target-client] [-F format] "
.args = { "ac:Ipt:F:v", 0, 1 },
.usage = "[-aIpv] [-c target-client] [-F format] "
CMD_TARGET_PANE_USAGE " [message]",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -66,10 +66,19 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
struct winlink *wl = item->target.wl;
struct window_pane *wp = item->target.wp;
const char *template;
char *msg;
char *msg, *cause;
struct format_tree *ft;
int flags;
if (args_has(args, 'I')) {
if (window_pane_start_input(wp, item, &cause) != 0) {
cmdq_error(item, "%s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
return (CMD_RETURN_WAIT);
}
if (args_has(args, 'F') && args->argc != 0) {
cmdq_error(item, "only one of -F or argument must be given");
return (CMD_RETURN_ERROR);
@@ -100,8 +109,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
format_defaults(ft, target_c, s, wl, wp);
if (args_has(args, 'a')) {
if (item != NULL)
format_each(ft, cmd_display_message_each, item);
format_each(ft, cmd_display_message_each, item);
return (CMD_RETURN_NORMAL);
}

View File

@@ -18,8 +18,8 @@
#include <sys/types.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
@@ -30,9 +30,6 @@
static enum cmd_retval cmd_display_panes_exec(struct cmd *,
struct cmdq_item *);
static void cmd_display_panes_callback(struct client *,
struct window_pane *);
const struct cmd_entry cmd_display_panes_entry = {
.name = "display-panes",
.alias = "displayp",
@@ -44,32 +41,218 @@ const struct cmd_entry cmd_display_panes_entry = {
.exec = cmd_display_panes_exec
};
struct cmd_display_panes_data {
struct cmdq_item *item;
char *command;
};
static void
cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
struct window_pane *wp)
{
struct client *c = ctx->c;
struct tty *tty = &c->tty;
struct session *s = c->session;
struct options *oo = s->options;
struct window *w = wp->window;
struct grid_cell gc;
u_int idx, px, py, i, j, xoff, yoff, sx, sy;
int colour, active_colour;
char buf[16], *ptr;
size_t len;
if (wp->xoff + wp->sx <= ctx->ox ||
wp->xoff >= ctx->ox + ctx->sx ||
wp->yoff + wp->sy <= ctx->oy ||
wp->yoff >= ctx->oy + ctx->sy)
return;
if (wp->xoff >= ctx->ox && wp->xoff + wp->sx <= ctx->ox + ctx->sx) {
/* All visible. */
xoff = wp->xoff - ctx->ox;
sx = wp->sx;
} else if (wp->xoff < ctx->ox &&
wp->xoff + wp->sx > ctx->ox + ctx->sx) {
/* Both left and right not visible. */
xoff = 0;
sx = ctx->sx;
} else if (wp->xoff < ctx->ox) {
/* Left not visible. */
xoff = 0;
sx = wp->sx - (ctx->ox - wp->xoff);
} else {
/* Right not visible. */
xoff = wp->xoff - ctx->ox;
sx = wp->sx - xoff;
}
if (wp->yoff >= ctx->oy && wp->yoff + wp->sy <= ctx->oy + ctx->sy) {
/* All visible. */
yoff = wp->yoff - ctx->oy;
sy = wp->sy;
} else if (wp->yoff < ctx->oy &&
wp->yoff + wp->sy > ctx->oy + ctx->sy) {
/* Both top and bottom not visible. */
yoff = 0;
sy = ctx->sy;
} else if (wp->yoff < ctx->oy) {
/* Top not visible. */
yoff = 0;
sy = wp->sy - (ctx->oy - wp->yoff);
} else {
/* Bottom not visible. */
yoff = wp->yoff - ctx->oy;
sy = wp->sy - yoff;
}
if (ctx->statustop)
yoff += ctx->statuslines;
px = sx / 2;
py = sy / 2;
if (window_pane_index(wp, &idx) != 0)
fatalx("index not found");
len = xsnprintf(buf, sizeof buf, "%u", idx);
if (sx < len)
return;
colour = options_get_number(oo, "display-panes-colour");
active_colour = options_get_number(oo, "display-panes-active-colour");
if (sx < len * 6 || sy < 5) {
tty_cursor(tty, xoff + px - len / 2, yoff + py);
goto draw_text;
}
px -= len * 3;
py -= 2;
memcpy(&gc, &grid_default_cell, sizeof gc);
if (w->active == wp)
gc.bg = active_colour;
else
gc.bg = colour;
gc.flags |= GRID_FLAG_NOPALETTE;
tty_attributes(tty, &gc, wp);
for (ptr = buf; *ptr != '\0'; ptr++) {
if (*ptr < '0' || *ptr > '9')
continue;
idx = *ptr - '0';
for (j = 0; j < 5; j++) {
for (i = px; i < px + 5; i++) {
tty_cursor(tty, xoff + i, yoff + py + j);
if (window_clock_table[idx][j][i - px])
tty_putc(tty, ' ');
}
}
px += 6;
}
len = xsnprintf(buf, sizeof buf, "%ux%u", wp->sx, wp->sy);
if (sx < len || sy < 6)
return;
tty_cursor(tty, xoff + sx - len, yoff);
draw_text:
memcpy(&gc, &grid_default_cell, sizeof gc);
if (w->active == wp)
gc.fg = active_colour;
else
gc.fg = colour;
gc.flags |= GRID_FLAG_NOPALETTE;
tty_attributes(tty, &gc, wp);
tty_puts(tty, buf);
tty_cursor(tty, 0, 0);
}
static void
cmd_display_panes_draw(struct client *c, struct screen_redraw_ctx *ctx)
{
struct window *w = c->session->curw->window;
struct window_pane *wp;
log_debug("%s: %s @%u", __func__, c->name, w->id);
TAILQ_FOREACH(wp, &w->panes, entry) {
if (window_pane_visible(wp))
cmd_display_panes_draw_pane(ctx, wp);
}
}
static void
cmd_display_panes_free(struct client *c)
{
struct cmd_display_panes_data *cdata = c->overlay_data;
if (cdata->item != NULL)
cmdq_continue(cdata->item);
free(cdata->command);
free(cdata);
}
static int
cmd_display_panes_key(struct client *c, struct key_event *event)
{
struct cmd_display_panes_data *cdata = c->overlay_data;
struct cmdq_item *new_item;
char *cmd, *expanded;
struct window *w = c->session->curw->window;
struct window_pane *wp;
struct cmd_parse_result *pr;
if (event->key < '0' || event->key > '9')
return (-1);
wp = window_pane_at_index(w, event->key - '0');
if (wp == NULL)
return (1);
window_unzoom(w);
xasprintf(&expanded, "%%%u", wp->id);
cmd = cmd_template_replace(cdata->command, expanded, 1);
pr = cmd_parse_from_string(cmd, NULL);
switch (pr->status) {
case CMD_PARSE_EMPTY:
new_item = NULL;
break;
case CMD_PARSE_ERROR:
new_item = cmdq_get_error(pr->error);
free(pr->error);
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(expanded);
return (1);
}
static enum cmd_retval
cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c;
struct session *s;
u_int delay;
char *cause;
struct args *args = self->args;
struct client *c;
struct session *s;
u_int delay;
char *cause;
struct cmd_display_panes_data *cdata;
if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL)
return (CMD_RETURN_ERROR);
s = c->session;
if (c->identify_callback != NULL)
if (c->overlay_draw != NULL)
return (CMD_RETURN_NORMAL);
c->identify_callback = cmd_display_panes_callback;
if (args->argc != 0)
c->identify_callback_data = xstrdup(args->argv[0]);
else
c->identify_callback_data = xstrdup("select-pane -t '%%'");
if (args_has(args, 'b'))
c->identify_callback_item = NULL;
else
c->identify_callback_item = item;
if (args_has(args, 'd')) {
delay = args_strtonum(args, 'd', 0, UINT_MAX, &cause);
if (cause != NULL) {
@@ -79,65 +262,21 @@ cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item)
}
} else
delay = options_get_number(s->options, "display-panes-time");
server_client_set_identify(c, delay);
cdata = xmalloc(sizeof *cdata);
if (args->argc != 0)
cdata->command = xstrdup(args->argv[0]);
else
cdata->command = xstrdup("select-pane -t '%%'");
if (args_has(args, 'b'))
cdata->item = NULL;
else
cdata->item = item;
server_client_set_overlay(c, delay, cmd_display_panes_draw,
cmd_display_panes_key, cmd_display_panes_free, cdata);
if (args_has(args, 'b'))
return (CMD_RETURN_NORMAL);
return (CMD_RETURN_WAIT);
}
static enum cmd_retval
cmd_display_panes_error(struct cmdq_item *item, void *data)
{
char *error = data;
cmdq_error(item, "%s", error);
free(error);
return (CMD_RETURN_NORMAL);
}
static void
cmd_display_panes_callback(struct client *c, struct window_pane *wp)
{
struct cmd_list *cmdlist;
struct cmdq_item *new_item;
char *cmd, *expanded, *cause;
if (wp == NULL)
goto out;
xasprintf(&expanded, "%%%u", wp->id);
cmd = cmd_template_replace(c->identify_callback_data, expanded, 1);
cmdlist = cmd_string_parse(cmd, NULL, 0, &cause);
if (cmdlist == NULL && cause != NULL)
new_item = cmdq_get_callback(cmd_display_panes_error, cause);
else if (cmdlist == NULL)
new_item = NULL;
else {
new_item = cmdq_get_command(cmdlist, NULL, NULL, 0);
cmd_list_free(cmdlist);
}
if (new_item != NULL) {
if (c->identify_callback_item != NULL)
cmdq_insert_after(c->identify_callback_item, new_item);
else
cmdq_append(c, new_item);
}
free(cmd);
free(expanded);
out:
if (c->identify_callback_item != NULL) {
c->identify_callback_item->flags &= ~CMDQ_WAITING;
c->identify_callback_item = NULL;
}
free(c->identify_callback_data);
c->identify_callback_data = NULL;
c->identify_callback = NULL;
}

View File

@@ -32,8 +32,8 @@ const struct cmd_entry cmd_find_window_entry = {
.name = "find-window",
.alias = "findw",
.args = { "CNt:TZ", 1, 1 },
.usage = "[-CNTZ] " CMD_TARGET_PANE_USAGE " match-string",
.args = { "CNrt:TZ", 1, 1 },
.usage = "[-CNrTZ] " CMD_TARGET_PANE_USAGE " match-string",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -57,30 +57,59 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item)
if (!C && !N && !T)
C = N = T = 1;
if (C && N && T) {
xasprintf(&filter,
"#{||:"
"#{C:%s},#{||:#{m:*%s*,#{window_name}},"
"#{m:*%s*,#{pane_title}}}}",
s, s, s);
} else if (C && N) {
xasprintf(&filter,
"#{||:#{C:%s},#{m:*%s*,#{window_name}}}",
s, s);
} else if (C && T) {
xasprintf(&filter,
"#{||:#{C:%s},#{m:*%s*,#{pane_title}}}",
s, s);
} else if (N && T) {
xasprintf(&filter,
"#{||:#{m:*%s*,#{window_name}},#{m:*%s*,#{pane_title}}}",
s, s);
} else if (C)
xasprintf(&filter, "#{C:%s}", s);
else if (N)
xasprintf(&filter, "#{m:*%s*,#{window_name}}", s);
else
xasprintf(&filter, "#{m:*%s*,#{pane_title}}", s);
if (!args_has(args, 'r')) {
if (C && N && T) {
xasprintf(&filter,
"#{||:"
"#{C:%s},#{||:#{m:*%s*,#{window_name}},"
"#{m:*%s*,#{pane_title}}}}",
s, s, s);
} else if (C && N) {
xasprintf(&filter,
"#{||:#{C:%s},#{m:*%s*,#{window_name}}}",
s, s);
} else if (C && T) {
xasprintf(&filter,
"#{||:#{C:%s},#{m:*%s*,#{pane_title}}}",
s, s);
} else if (N && T) {
xasprintf(&filter,
"#{||:#{m:*%s*,#{window_name}},"
"#{m:*%s*,#{pane_title}}}",
s, s);
} else if (C)
xasprintf(&filter, "#{C:%s}", s);
else if (N)
xasprintf(&filter, "#{m:*%s*,#{window_name}}", s);
else
xasprintf(&filter, "#{m:*%s*,#{pane_title}}", s);
} 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);
if (args_has(args, 'Z'))

View File

@@ -75,38 +75,12 @@ static const char *cmd_find_pane_table[][2] = {
{ NULL, NULL }
};
/* Get session from TMUX if present. */
static struct session *
cmd_find_try_TMUX(struct client *c)
{
struct environ_entry *envent;
char tmp[256];
long long pid;
u_int session;
struct session *s;
envent = environ_find(c->environ, "TMUX");
if (envent == NULL)
return (NULL);
if (sscanf(envent->value, "%255[^,],%lld,%d", tmp, &pid, &session) != 3)
return (NULL);
if (pid != getpid())
return (NULL);
log_debug("%s: client %p TMUX %s (session $%u)", __func__, c,
envent->value, session);
s = session_find_by_id(session);
if (s != NULL)
log_debug("%s: session $%u still exists", __func__, s->id);
return (s);
}
/* Find pane containing client if any. */
static struct window_pane *
cmd_find_inside_pane(struct client *c)
{
struct window_pane *wp;
struct environ_entry *envent;
if (c == NULL)
return (NULL);
@@ -115,6 +89,11 @@ cmd_find_inside_pane(struct client *c)
if (wp->fd != -1 && strcmp(wp->tty, c->ttyname) == 0)
break;
}
if (wp == NULL) {
envent = environ_find(c->environ, "TMUX_PANE");
if (envent != NULL)
wp = window_pane_find_by_id_str(envent->value);
}
if (wp != NULL)
log_debug("%s: got pane %%%u (%s)", __func__, wp->id, wp->tty);
return (wp);
@@ -879,8 +858,6 @@ cmd_find_from_mouse(struct cmd_find_state *fs, struct mouse_event *m, int flags)
int
cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags)
{
struct session *s;
struct winlink *wl;
struct window_pane *wp;
/* If no client, treat as from nothing. */
@@ -902,30 +879,6 @@ cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags)
if (wp == NULL)
goto unknown_pane;
/* If we have a session in TMUX, see if it has this pane. */
s = cmd_find_try_TMUX(c);
if (s != NULL) {
RB_FOREACH(wl, winlinks, &s->windows) {
if (window_has_pane(wl->window, wp))
break;
}
if (wl != NULL) {
log_debug("%s: session $%u has pane %%%u", __func__,
s->id, wp->id);
fs->s = s;
fs->wl = s->curw; /* use current session */
fs->w = fs->wl->window;
fs->wp = fs->w->active; /* use active pane */
cmd_find_log_state(__func__, fs);
return (0);
} else {
log_debug("%s: session $%u does not have pane %%%u",
__func__, s->id, wp->id);
}
}
/*
* Don't have a session, or it doesn't have this pane. Try all
* sessions.
@@ -947,17 +900,7 @@ cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags)
return (0);
unknown_pane:
/*
* We're not running in a known pane, but maybe this client has TMUX
* in the environment. That'd give us a session.
*/
s = cmd_find_try_TMUX(c);
if (s != NULL) {
cmd_find_from_session(fs, s, flags);
return (0);
}
/* Otherwise we need to guess. */
/* We can't find the pane so need to guess. */
return (cmd_find_from_nothing(fs, flags));
}
@@ -1005,6 +948,8 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
strlcat(tmp, "CANFAIL,", sizeof tmp);
if (*tmp != '\0')
tmp[strlen(tmp) - 1] = '\0';
else
strlcat(tmp, "NONE", sizeof tmp);
log_debug("%s: target %s, type %s, item %p, flags %s", __func__,
target == NULL ? "none" : target, s, item, tmp);
@@ -1039,12 +984,16 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
switch (type) {
case CMD_FIND_PANE:
fs->wp = cmd_mouse_pane(m, &fs->s, &fs->wl);
if (fs->wp != NULL)
if (fs->wp != NULL) {
fs->w = fs->wl->window;
break;
break;
}
/* FALLTHROUGH */
case CMD_FIND_WINDOW:
case CMD_FIND_SESSION:
fs->wl = cmd_mouse_window(m, &fs->s);
if (fs->wl == NULL && fs->s != NULL)
fs->wl = fs->s->curw;
if (fs->wl != NULL) {
fs->w = fs->wl->window;
fs->wp = fs->w->active;

View File

@@ -49,8 +49,7 @@ const struct cmd_entry cmd_if_shell_entry = {
};
struct cmd_if_shell_data {
char *file;
u_int line;
struct cmd_parse_input input;
char *cmd_if;
char *cmd_else;
@@ -64,53 +63,68 @@ static enum cmd_retval
cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct cmdq_shared *shared = item->shared;
struct mouse_event *m = &item->shared->mouse;
struct cmd_if_shell_data *cdata;
char *shellcmd, *cmd, *cause;
struct cmd_list *cmdlist;
char *shellcmd, *cmd;
struct cmdq_item *new_item;
struct cmd_find_state *fs = &item->target;
struct client *c = cmd_find_client(item, NULL, 1);
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct window_pane *wp = item->target.wp;
struct session *s = fs->s;
struct winlink *wl = fs->wl;
struct window_pane *wp = fs->wp;
struct cmd_parse_input pi;
struct cmd_parse_result *pr;
shellcmd = format_single(item, args->argv[0], c, s, wl, wp);
if (args_has(args, 'F')) {
cmd = NULL;
if (*shellcmd != '0' && *shellcmd != '\0')
cmd = args->argv[1];
else if (args->argc == 3)
cmd = args->argv[2];
else
cmd = NULL;
free(shellcmd);
if (cmd == NULL)
return (CMD_RETURN_NORMAL);
cmdlist = cmd_string_parse(cmd, NULL, 0, &cause);
if (cmdlist == NULL) {
if (cause != NULL) {
cmdq_error(item, "%s", cause);
free(cause);
}
memset(&pi, 0, sizeof pi);
if (self->file != NULL)
pi.file = self->file;
pi.line = self->line;
pi.item = item;
pi.c = c;
cmd_find_copy_state(&pi.fs, fs);
pr = cmd_parse_from_string(cmd, &pi);
switch (pr->status) {
case CMD_PARSE_EMPTY:
break;
case CMD_PARSE_ERROR:
cmdq_error(item, "%s", pr->error);
free(pr->error);
return (CMD_RETURN_ERROR);
case CMD_PARSE_SUCCESS:
new_item = cmdq_get_command(pr->cmdlist, fs, m, 0);
cmdq_insert_after(item, new_item);
cmd_list_free(pr->cmdlist);
break;
}
new_item = cmdq_get_command(cmdlist, NULL, &shared->mouse, 0);
cmdq_insert_after(item, new_item);
cmd_list_free(cmdlist);
return (CMD_RETURN_NORMAL);
}
cdata = xcalloc(1, sizeof *cdata);
if (self->file != NULL) {
cdata->file = xstrdup(self->file);
cdata->line = self->line;
}
cdata->cmd_if = xstrdup(args->argv[1]);
if (args->argc == 3)
cdata->cmd_else = xstrdup(args->argv[2]);
else
cdata->cmd_else = NULL;
memcpy(&cdata->mouse, m, sizeof cdata->mouse);
cdata->client = item->client;
if (!args_has(args, 'b'))
cdata->client = item->client;
else
cdata->client = c;
if (cdata->client != NULL)
cdata->client->references++;
@@ -118,7 +132,16 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item)
cdata->item = item;
else
cdata->item = NULL;
memcpy(&cdata->mouse, &shared->mouse, sizeof cdata->mouse);
memset(&cdata->input, 0, sizeof cdata->input);
if (self->file != NULL)
cdata->input.file = xstrdup(self->file);
cdata->input.line = self->line;
cdata->input.item = cdata->item;
cdata->input.c = c;
if (cdata->input.c != NULL)
cdata->input.c->references++;
cmd_find_copy_state(&cdata->input.fs, fs);
if (job_run(shellcmd, s, server_client_get_cwd(item->client, s), NULL,
cmd_if_shell_callback, cmd_if_shell_free, cdata, 0) == NULL) {
@@ -139,11 +162,11 @@ cmd_if_shell_callback(struct job *job)
{
struct cmd_if_shell_data *cdata = job_get_data(job);
struct client *c = cdata->client;
struct cmd_list *cmdlist;
struct cmdq_item *new_item;
char *cause, *cmd, *file = cdata->file;
u_int line = cdata->line;
struct mouse_event *m = &cdata->mouse;
struct cmdq_item *new_item = NULL;
char *cmd;
int status;
struct cmd_parse_result *pr;
status = job_get_status(job);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
@@ -153,17 +176,20 @@ cmd_if_shell_callback(struct job *job)
if (cmd == NULL)
goto out;
cmdlist = cmd_string_parse(cmd, file, line, &cause);
if (cmdlist == NULL) {
if (cause != NULL && cdata->item != NULL)
cmdq_error(cdata->item, "%s", cause);
free(cause);
new_item = NULL;
} else {
new_item = cmdq_get_command(cmdlist, NULL, &cdata->mouse, 0);
cmd_list_free(cmdlist);
pr = cmd_parse_from_string(cmd, &cdata->input);
switch (pr->status) {
case CMD_PARSE_EMPTY:
break;
case CMD_PARSE_ERROR:
if (cdata->item != NULL)
cmdq_error(cdata->item, "%s", pr->error);
free(pr->error);
break;
case CMD_PARSE_SUCCESS:
new_item = cmdq_get_command(pr->cmdlist, NULL, m, 0);
cmd_list_free(pr->cmdlist);
break;
}
if (new_item != NULL) {
if (cdata->item == NULL)
cmdq_append(c, new_item);
@@ -173,7 +199,7 @@ cmd_if_shell_callback(struct job *job)
out:
if (cdata->item != NULL)
cdata->item->flags &= ~CMDQ_WAITING;
cmdq_continue(cdata->item);
}
static void
@@ -187,6 +213,9 @@ cmd_if_shell_free(void *data)
free(cdata->cmd_else);
free(cdata->cmd_if);
free(cdata->file);
if (cdata->input.c != NULL)
server_client_unref(cdata->input.c);
free((void *)cdata->input.file);
free(cdata);
}

View File

@@ -20,6 +20,7 @@
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "tmux.h"
@@ -34,8 +35,8 @@ const struct cmd_entry cmd_join_pane_entry = {
.name = "join-pane",
.alias = "joinp",
.args = { "bdhvp:l:s:t:", 0, 0 },
.usage = "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE,
.args = { "bdfhvp:l:s:t:", 0, 0 },
.usage = "[-bdfhv] [-l size] " CMD_SRCDST_PANE_USAGE,
.source = { 's', CMD_FIND_PANE, CMD_FIND_DEFAULT_MARKED },
.target = { 't', CMD_FIND_PANE, 0 },
@@ -51,7 +52,7 @@ const struct cmd_entry cmd_move_pane_entry = {
.args = { "bdhvp:l:s:t:", 0, 0 },
.usage = "[-bdhv] [-p percentage|-l size] " CMD_SRCDST_PANE_USAGE,
.source = { 's', CMD_FIND_PANE, 0 },
.source = { 's', CMD_FIND_PANE, CMD_FIND_DEFAULT_MARKED },
.target = { 't', CMD_FIND_PANE, 0 },
.flags = 0,
@@ -67,11 +68,13 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
struct winlink *src_wl, *dst_wl;
struct window *src_w, *dst_w;
struct window_pane *src_wp, *dst_wp;
char *cause;
int size, percentage, dst_idx;
char *cause, *copy;
const char *errstr, *p;
size_t plen;
int size, percentage, dst_idx, not_same_window;
int flags;
enum layout_type type;
struct layout_cell *lc;
int not_same_window;
if (self->entry == &cmd_join_pane_entry)
not_same_window = 1;
@@ -104,12 +107,28 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
type = LAYOUT_LEFTRIGHT;
size = -1;
if (args_has(args, 'l')) {
size = args_strtonum(args, 'l', 0, INT_MAX, &cause);
if (cause != NULL) {
cmdq_error(item, "size %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
if ((p = args_get(args, 'l')) != NULL) {
plen = strlen(p);
if (p[plen - 1] == '%') {
copy = xstrdup(p);
copy[plen - 1] = '\0';
percentage = strtonum(copy, 0, INT_MAX, &errstr);
free(copy);
if (errstr != NULL) {
cmdq_error(item, "percentage %s", errstr);
return (CMD_RETURN_ERROR);
}
if (type == LAYOUT_TOPBOTTOM)
size = (dst_wp->sy * percentage) / 100;
else
size = (dst_wp->sx * percentage) / 100;
} else {
size = args_strtonum(args, 'l', 0, INT_MAX, &cause);
if (cause != NULL) {
cmdq_error(item, "size %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
}
} else if (args_has(args, 'p')) {
percentage = args_strtonum(args, 'p', 0, 100, &cause);
@@ -123,7 +142,14 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
else
size = (dst_wp->sx * percentage) / 100;
}
lc = layout_split_pane(dst_wp, type, size, args_has(args, 'b'), 0);
flags = 0;
if (args_has(args, 'b'))
flags |= SPAWN_BEFORE;
if (args_has(args, 'f'))
flags |= SPAWN_FULLSIZE;
lc = layout_split_pane(dst_wp, type, size, flags);
if (lc == NULL) {
cmdq_error(item, "create pane failed: pane too small");
return (CMD_RETURN_ERROR);
@@ -135,6 +161,8 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
TAILQ_REMOVE(&src_w->panes, src_wp, entry);
src_wp->window = dst_w;
options_set_parent(src_wp->options, dst_w->options);
src_wp->flags |= PANE_STYLECHANGED;
TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry);
layout_assign_pane(lc, src_wp);
@@ -144,7 +172,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
server_redraw_window(dst_w);
if (!args_has(args, 'd')) {
window_set_active_pane(dst_w, src_wp);
window_set_active_pane(dst_w, src_wp, 1);
session_select(dst_s, dst_idx);
cmd_find_from_session(current, dst_s, 0);
server_redraw_session(dst_s);

View File

@@ -37,7 +37,7 @@ const struct cmd_entry cmd_kill_pane_entry = {
.target = { 't', CMD_FIND_PANE, 0 },
.flags = 0,
.flags = CMD_AFTERHOOK,
.exec = cmd_kill_pane_exec
};

View File

@@ -61,12 +61,12 @@ cmd_kill_session_exec(struct cmd *self, struct cmdq_item *item)
RB_FOREACH_SAFE(sloop, sessions, &sessions, stmp) {
if (sloop != s) {
server_destroy_session(sloop);
session_destroy(sloop, __func__);
session_destroy(sloop, 1, __func__);
}
}
} else {
server_destroy_session(s);
session_destroy(s, __func__);
session_destroy(s, 1, __func__);
}
return (CMD_RETURN_NORMAL);
}

View File

@@ -36,8 +36,8 @@ const struct cmd_entry cmd_list_keys_entry = {
.name = "list-keys",
.alias = "lsk",
.args = { "T:", 0, 0 },
.usage = "[-T key-table]",
.args = { "1aNP:T:", 0, 1 },
.usage = "[-1aN] [-P prefix-string] [-T key-table] [key]",
.flags = CMD_STARTSERVER|CMD_AFTERHOOK,
.exec = cmd_list_keys_exec
@@ -47,32 +47,164 @@ const struct cmd_entry cmd_list_commands_entry = {
.name = "list-commands",
.alias = "lscm",
.args = { "F:", 0, 0 },
.usage = "[-F format]",
.args = { "F:", 0, 1 },
.usage = "[-F format] [command]",
.flags = CMD_STARTSERVER|CMD_AFTERHOOK,
.exec = cmd_list_keys_exec
};
static u_int
cmd_list_keys_get_width(const char *tablename, key_code only)
{
struct key_table *table;
struct key_binding *bd;
u_int width, keywidth = 0;
table = key_bindings_get_table(tablename, 0);
if (table == NULL)
return (0);
bd = key_bindings_first(table);
while (bd != NULL) {
if ((only != KEYC_UNKNOWN && bd->key != only) ||
KEYC_IS_MOUSE(bd->key) ||
bd->note == NULL) {
bd = key_bindings_next(table, bd);
continue;
}
width = utf8_cstrwidth(key_string_lookup_key(bd->key));
if (width > keywidth)
keywidth = width;
bd = key_bindings_next(table, bd);
}
return (keywidth);
}
static int
cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args,
const char *tablename, u_int keywidth, key_code only, const char *prefix)
{
struct client *c = cmd_find_client(item, NULL, 1);
struct key_table *table;
struct key_binding *bd;
const char *key;
char *tmp, *note;
int found = 0;
table = key_bindings_get_table(tablename, 0);
if (table == NULL)
return (0);
bd = key_bindings_first(table);
while (bd != NULL) {
if ((only != KEYC_UNKNOWN && bd->key != only) ||
KEYC_IS_MOUSE(bd->key) ||
(bd->note == NULL && !args_has(args, 'a'))) {
bd = key_bindings_next(table, bd);
continue;
}
found = 1;
key = key_string_lookup_key(bd->key);
if (bd->note == NULL)
note = cmd_list_print(bd->cmdlist, 1);
else
note = xstrdup(bd->note);
tmp = utf8_padcstr(key, keywidth + 1);
if (args_has(args, '1') && c != NULL)
status_message_set(c, "%s%s%s", prefix, tmp, note);
else
cmdq_print(item, "%s%s%s", prefix, tmp, note);
free(tmp);
free(note);
if (args_has(args, '1'))
break;
bd = key_bindings_next(table, bd);
}
return (found);
}
static char *
cmd_list_keys_get_prefix(struct args *args, key_code *prefix)
{
char *s;
*prefix = options_get_number(global_s_options, "prefix");
if (!args_has(args, 'P')) {
if (*prefix != KEYC_NONE)
xasprintf(&s, "%s ", key_string_lookup_key(*prefix));
else
s = xstrdup("");
} else
s = xstrdup(args_get(args, 'P'));
return (s);
}
static enum cmd_retval
cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct key_table *table;
struct key_binding *bd;
const char *key, *tablename, *r;
char *cp, tmp[BUFSIZ];
int repeat, width, tablewidth, keywidth;
const char *tablename, *r;
char *key, *cp, *tmp, *start, *empty;
key_code prefix, only = KEYC_UNKNOWN;
int repeat, width, tablewidth, keywidth, found = 0;
size_t tmpsize, tmpused, cplen;
if (self->entry == &cmd_list_commands_entry)
return (cmd_list_keys_commands(self, item));
if (args->argc != 0) {
only = key_string_lookup_string(args->argv[0]);
if (only == KEYC_UNKNOWN) {
cmdq_error(item, "invalid key: %s", args->argv[0]);
return (CMD_RETURN_ERROR);
}
}
tablename = args_get(args, 'T');
if (tablename != NULL && key_bindings_get_table(tablename, 0) == NULL) {
cmdq_error(item, "table %s doesn't exist", tablename);
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'N')) {
if (tablename == NULL) {
start = cmd_list_keys_get_prefix(args, &prefix);
keywidth = cmd_list_keys_get_width("root", only);
if (prefix != KEYC_NONE) {
width = cmd_list_keys_get_width("prefix", only);
if (width == 0)
prefix = KEYC_NONE;
else if (width > keywidth)
keywidth = width;
}
empty = utf8_padcstr("", utf8_cstrwidth(start));
found = cmd_list_keys_print_notes(item, args, "root",
keywidth, only, empty);
if (prefix != KEYC_NONE) {
if (cmd_list_keys_print_notes(item, args,
"prefix", keywidth, only, start))
found = 1;
}
free(empty);
} else {
if (args_has(args, 'P'))
start = xstrdup(args_get(args, 'P'));
else
start = xstrdup("");
keywidth = cmd_list_keys_get_width(tablename, only);
found = cmd_list_keys_print_notes(item, args, tablename,
keywidth, only, start);
}
free(start);
goto out;
}
repeat = 0;
tablewidth = keywidth = 0;
table = key_bindings_first_table ();
@@ -83,7 +215,11 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
}
bd = key_bindings_first(table);
while (bd != NULL) {
key = key_string_lookup_key(bd->key);
if (only != KEYC_UNKNOWN && bd->key != only) {
bd = key_bindings_next(table, bd);
continue;
}
key = args_escape(key_string_lookup_key(bd->key));
if (bd->flags & KEY_BINDING_REPEAT)
repeat = 1;
@@ -95,11 +231,15 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
if (width > keywidth)
keywidth = width;
free(key);
bd = key_bindings_next(table, bd);
}
table = key_bindings_next_table(table);
}
tmpsize = 256;
tmp = xmalloc(tmpsize);
table = key_bindings_first_table ();
while (table != NULL) {
if (tablename != NULL && strcmp(table->name, tablename) != 0) {
@@ -108,7 +248,12 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
}
bd = key_bindings_first(table);
while (bd != NULL) {
key = key_string_lookup_key(bd->key);
if (only != KEYC_UNKNOWN && bd->key != only) {
bd = key_bindings_next(table, bd);
continue;
}
found = 1;
key = args_escape(key_string_lookup_key(bd->key));
if (!repeat)
r = "";
@@ -116,41 +261,68 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
r = "-r ";
else
r = " ";
xsnprintf(tmp, sizeof tmp, "%s-T ", r);
tmpused = xsnprintf(tmp, tmpsize, "%s-T ", r);
cp = utf8_padcstr(table->name, tablewidth);
strlcat(tmp, cp, sizeof tmp);
strlcat(tmp, " ", sizeof tmp);
cplen = strlen(cp) + 1;
while (tmpused + cplen + 1 >= tmpsize) {
tmpsize *= 2;
tmp = xrealloc(tmp, tmpsize);
}
tmpused = strlcat(tmp, cp, tmpsize);
tmpused = strlcat(tmp, " ", tmpsize);
free(cp);
cp = utf8_padcstr(key, keywidth);
strlcat(tmp, cp, sizeof tmp);
strlcat(tmp, " ", sizeof tmp);
cplen = strlen(cp) + 1;
while (tmpused + cplen + 1 >= tmpsize) {
tmpsize *= 2;
tmp = xrealloc(tmp, tmpsize);
}
tmpused = strlcat(tmp, cp, tmpsize);
tmpused = strlcat(tmp, " ", tmpsize);
free(cp);
cp = cmd_list_print(bd->cmdlist);
strlcat(tmp, cp, sizeof tmp);
cp = cmd_list_print(bd->cmdlist, 1);
cplen = strlen(cp);
while (tmpused + cplen + 1 >= tmpsize) {
tmpsize *= 2;
tmp = xrealloc(tmp, tmpsize);
}
strlcat(tmp, cp, tmpsize);
free(cp);
cmdq_print(item, "bind-key %s", tmp);
free(key);
bd = key_bindings_next(table, bd);
}
table = key_bindings_next_table(table);
}
free(tmp);
out:
if (only != KEYC_UNKNOWN && !found) {
cmdq_error(item, "unknown key: %s", args->argv[0]);
return (CMD_RETURN_ERROR);
}
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct args *args = self->args;
const struct cmd_entry **entryp;
const struct cmd_entry *entry;
struct format_tree *ft;
const char *template, *s;
const char *template, *s, *command = NULL;
char *line;
if (args->argc != 0)
command = args->argv[0];
if ((template = args_get(args, 'F')) == NULL) {
template = "#{command_list_name}"
"#{?command_list_alias, (#{command_list_alias}),} "
@@ -162,6 +334,11 @@ cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
for (entryp = cmd_table; *entryp != NULL; entryp++) {
entry = *entryp;
if (command != NULL &&
(strcmp(entry->name, command) != 0 &&
(entry->alias == NULL ||
strcmp(entry->alias, command) != 0)))
continue;
format_add(ft, "command_list_name", "%s", entry->name);
if (entry->alias != NULL)

View File

@@ -1,126 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
struct cmd_list *
cmd_list_parse(int argc, char **argv, const char *file, u_int line,
char **cause)
{
struct cmd_list *cmdlist;
struct cmd *cmd;
int i, lastsplit;
size_t arglen, new_argc;
char **copy_argv, **new_argv;
copy_argv = cmd_copy_argv(argc, argv);
cmdlist = xcalloc(1, sizeof *cmdlist);
cmdlist->references = 1;
TAILQ_INIT(&cmdlist->list);
lastsplit = 0;
for (i = 0; i < argc; i++) {
arglen = strlen(copy_argv[i]);
if (arglen == 0 || copy_argv[i][arglen - 1] != ';')
continue;
copy_argv[i][arglen - 1] = '\0';
if (arglen > 1 && copy_argv[i][arglen - 2] == '\\') {
copy_argv[i][arglen - 2] = ';';
continue;
}
new_argc = i - lastsplit;
new_argv = copy_argv + lastsplit;
if (arglen != 1)
new_argc++;
cmd = cmd_parse(new_argc, new_argv, file, line, cause);
if (cmd == NULL)
goto bad;
TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry);
lastsplit = i + 1;
}
if (lastsplit != argc) {
cmd = cmd_parse(argc - lastsplit, copy_argv + lastsplit,
file, line, cause);
if (cmd == NULL)
goto bad;
TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry);
}
cmd_free_argv(argc, copy_argv);
return (cmdlist);
bad:
cmd_list_free(cmdlist);
cmd_free_argv(argc, copy_argv);
return (NULL);
}
void
cmd_list_free(struct cmd_list *cmdlist)
{
struct cmd *cmd, *cmd1;
if (--cmdlist->references != 0)
return;
TAILQ_FOREACH_SAFE(cmd, &cmdlist->list, qentry, cmd1) {
TAILQ_REMOVE(&cmdlist->list, cmd, qentry);
args_free(cmd->args);
free(cmd->file);
free(cmd);
}
free(cmdlist);
}
char *
cmd_list_print(struct cmd_list *cmdlist)
{
struct cmd *cmd;
char *buf, *this;
size_t len;
len = 1;
buf = xcalloc(1, len);
TAILQ_FOREACH(cmd, &cmdlist->list, qentry) {
this = cmd_print(cmd);
len += strlen(this) + 3;
buf = xrealloc(buf, len);
strlcat(buf, this, len);
if (TAILQ_NEXT(cmd, qentry) != NULL)
strlcat(buf, " ; ", len);
free(this);
}
return (buf);
}

View File

@@ -33,8 +33,6 @@
static enum cmd_retval cmd_load_buffer_exec(struct cmd *, struct cmdq_item *);
static void cmd_load_buffer_callback(struct client *, int, void *);
const struct cmd_entry cmd_load_buffer_entry = {
.name = "load-buffer",
.alias = "loadb",
@@ -48,9 +46,40 @@ const struct cmd_entry cmd_load_buffer_entry = {
struct cmd_load_buffer_data {
struct cmdq_item *item;
char *bufname;
char *name;
};
static void
cmd_load_buffer_done(__unused struct client *c, const char *path, int error,
int closed, struct evbuffer *buffer, void *data)
{
struct cmd_load_buffer_data *cdata = data;
struct cmdq_item *item = cdata->item;
void *bdata = EVBUFFER_DATA(buffer);
size_t bsize = EVBUFFER_LENGTH(buffer);
void *copy;
char *cause;
if (!closed)
return;
if (error != 0)
cmdq_error(item, "%s: %s", path, strerror(error));
else if (bsize != 0) {
copy = xmalloc(bsize);
memcpy(copy, bdata, bsize);
if (paste_set(copy, bsize, cdata->name, &cause) != 0) {
cmdq_error(item, "%s", cause);
free(cause);
free(copy);
}
}
cmdq_continue(item);
free(cdata->name);
free(cdata);
}
static enum cmd_retval
cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item)
{
@@ -60,124 +89,19 @@ cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item)
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct window_pane *wp = item->target.wp;
FILE *f;
const char *bufname;
char *pdata = NULL, *new_pdata, *cause;
char *path, *file;
size_t psize;
int ch, error;
const char *bufname = args_get(args, 'b');
char *path;
bufname = NULL;
if (args_has(args, 'b'))
bufname = args_get(args, 'b');
cdata = xmalloc(sizeof *cdata);
cdata->item = item;
if (bufname != NULL)
cdata->name = xstrdup(bufname);
else
cdata->name = NULL;
path = format_single(item, args->argv[0], c, s, wl, wp);
if (strcmp(path, "-") == 0) {
free(path);
c = item->client;
cdata = xcalloc(1, sizeof *cdata);
cdata->item = item;
if (bufname != NULL)
cdata->bufname = xstrdup(bufname);
error = server_set_stdin_callback(c, cmd_load_buffer_callback,
cdata, &cause);
if (error != 0) {
cmdq_error(item, "-: %s", cause);
free(cause);
free(cdata);
return (CMD_RETURN_ERROR);
}
return (CMD_RETURN_WAIT);
}
file = server_client_get_path(c, path);
file_read(item->client, path, cmd_load_buffer_done, cdata);
free(path);
f = fopen(file, "rb");
if (f == NULL) {
cmdq_error(item, "%s: %s", file, strerror(errno));
goto error;
}
pdata = NULL;
psize = 0;
while ((ch = getc(f)) != EOF) {
/* Do not let the server die due to memory exhaustion. */
if ((new_pdata = realloc(pdata, psize + 2)) == NULL) {
cmdq_error(item, "realloc error: %s", strerror(errno));
goto error;
}
pdata = new_pdata;
pdata[psize++] = ch;
}
if (ferror(f)) {
cmdq_error(item, "%s: read error", file);
goto error;
}
if (pdata != NULL)
pdata[psize] = '\0';
fclose(f);
free(file);
if (paste_set(pdata, psize, bufname, &cause) != 0) {
cmdq_error(item, "%s", cause);
free(pdata);
free(cause);
return (CMD_RETURN_ERROR);
}
return (CMD_RETURN_NORMAL);
error:
free(pdata);
if (f != NULL)
fclose(f);
free(file);
return (CMD_RETURN_ERROR);
}
static void
cmd_load_buffer_callback(struct client *c, int closed, void *data)
{
struct cmd_load_buffer_data *cdata = data;
char *pdata, *cause, *saved;
size_t psize;
if (!closed)
return;
c->stdin_callback = NULL;
server_client_unref(c);
if (c->flags & CLIENT_DEAD)
goto out;
psize = EVBUFFER_LENGTH(c->stdin_data);
if (psize == 0 || (pdata = malloc(psize + 1)) == NULL)
goto out;
memcpy(pdata, EVBUFFER_DATA(c->stdin_data), psize);
pdata[psize] = '\0';
evbuffer_drain(c->stdin_data, psize);
if (paste_set(pdata, psize, cdata->bufname, &cause) != 0) {
/* No context so can't use server_client_msg_error. */
if (~c->flags & CLIENT_UTF8) {
saved = cause;
cause = utf8_sanitize(saved);
free(saved);
}
evbuffer_add_printf(c->stderr_data, "%s", cause);
server_client_push_stderr(c);
free(pdata);
free(cause);
}
out:
cdata->item->flags &= ~CMDQ_WAITING;
free(cdata->bufname);
free(cdata);
return (CMD_RETURN_WAIT);
}

View File

@@ -39,8 +39,8 @@ const struct cmd_entry cmd_new_session_entry = {
.name = "new-session",
.alias = "new",
.args = { "Ac:dDEF:n:Ps:t:x:y:", 0, -1 },
.usage = "[-AdDEP] [-c start-directory] [-F format] [-n window-name] "
.args = { "Ac:dDEF:n:Ps:t:x:Xy:", 0, -1 },
.usage = "[-AdDEPX] [-c start-directory] [-F format] [-n window-name] "
"[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] "
"[-y height] [command]",
@@ -69,20 +69,17 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
struct args *args = self->args;
struct client *c = item->client;
struct session *s, *as, *groupwith;
struct window *w;
struct environ *env;
struct options *oo;
struct termios tio, *tiop;
struct session_group *sg;
const char *errstr, *template, *group, *prefix;
const char *path, *cmd, *tmp, *value;
char **argv, *cause, *cp, *newname, *cwd = NULL;
int detached, already_attached, idx, argc;
int is_control = 0;
u_int sx, sy, dsx = 80, dsy = 24;
struct environ_entry *envent;
struct cmd_find_state fs;
const char *errstr, *template, *group, *prefix, *tmp;
char *cause, *cwd = NULL, *cp, *newname = NULL;
int detached, already_attached, is_control = 0;
u_int sx, sy, dsx, dsy;
struct spawn_context sc;
enum cmd_retval retval;
struct cmd_find_state fs;
if (self->entry == &cmd_has_session_entry) {
/*
@@ -97,26 +94,31 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR);
}
newname = NULL;
if (args_has(args, 's')) {
newname = format_single(item, args_get(args, 's'), c, NULL,
NULL, NULL);
tmp = args_get(args, 's');
if (tmp != NULL) {
newname = format_single(item, tmp, c, NULL, NULL, NULL);
if (!session_check_name(newname)) {
cmdq_error(item, "bad session name: %s", newname);
goto error;
goto fail;
}
if ((as = session_find(newname)) != NULL) {
if (args_has(args, 'A')) {
retval = cmd_attach_session(item,
newname, args_has(args, 'D'),
0, NULL, args_has(args, 'E'));
free(newname);
return (retval);
}
cmdq_error(item, "duplicate session: %s", newname);
goto error;
}
if (args_has(args, 'A')) {
if (newname != NULL)
as = session_find(newname);
else
as = item->target.s;
if (as != NULL) {
retval = cmd_attach_session(item, as->name,
args_has(args, 'D'), args_has(args, 'X'), 0, NULL,
args_has(args, 'E'));
free(newname);
return (retval);
}
}
if (newname != NULL && session_find(newname) != NULL) {
cmdq_error(item, "duplicate session: %s", newname);
goto fail;
}
/* Is this going to be part of a session group? */
group = args_get(args, 't');
@@ -125,7 +127,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (groupwith == NULL) {
if (!session_check_name(group)) {
cmdq_error(item, "bad group name: %s", group);
goto error;
goto fail;
}
sg = session_group_find(group);
} else
@@ -173,7 +175,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (server_client_check_nested(item->client)) {
cmdq_error(item, "sessions should be nested with care, "
"unset $TMUX to force");
return (CMD_RETURN_ERROR);
goto fail;
}
if (tcgetattr(c->tty.fd, &tio) != 0)
fatal("tcgetattr failed");
@@ -186,7 +188,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (server_client_open(c, &cause) != 0) {
cmdq_error(item, "open terminal failed: %s", cause);
free(cause);
goto error;
goto fail;
}
}
@@ -196,11 +198,13 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (strcmp(tmp, "-") == 0) {
if (c != NULL)
dsx = c->tty.sx;
else
dsx = 80;
} else {
dsx = strtonum(tmp, 1, USHRT_MAX, &errstr);
if (errstr != NULL) {
cmdq_error(item, "width %s", errstr);
goto error;
goto fail;
}
}
}
@@ -209,11 +213,13 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (strcmp(tmp, "-") == 0) {
if (c != NULL)
dsy = c->tty.sy;
else
dsy = 24;
} else {
dsy = strtonum(tmp, 1, USHRT_MAX, &errstr);
if (errstr != NULL) {
cmdq_error(item, "height %s", errstr);
goto error;
goto fail;
}
}
}
@@ -225,8 +231,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (sy > 0 && options_get_number(global_s_options, "status"))
sy--;
} else {
value = options_get_string(global_s_options, "default-size");
if (sscanf(value, "%ux%u", &sx, &sy) != 2) {
tmp = options_get_string(global_s_options, "default-size");
if (sscanf(tmp, "%ux%u", &sx, &sy) != 2) {
sx = 80;
sy = 24;
}
@@ -240,59 +246,40 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (sy == 0)
sy = 1;
/* Figure out the command for the new window. */
argc = -1;
argv = NULL;
if (!args_has(args, 't') && args->argc != 0) {
argc = args->argc;
argv = args->argv;
} else if (sg == NULL && groupwith == NULL) {
cmd = options_get_string(global_s_options, "default-command");
if (cmd != NULL && *cmd != '\0') {
argc = 1;
argv = (char **)&cmd;
} else {
argc = 0;
argv = NULL;
}
/* Create the new session. */
oo = options_create(global_s_options);
if (args_has(args, 'x') || args_has(args, 'y')) {
if (!args_has(args, 'x'))
dsx = sx;
if (!args_has(args, 'y'))
dsy = sy;
options_set_string(oo, "default-size", 0, "%ux%u", dsx, dsy);
}
path = NULL;
if (c != NULL && c->session == NULL)
envent = environ_find(c->environ, "PATH");
else
envent = environ_find(global_environ, "PATH");
if (envent != NULL)
path = envent->value;
/* Construct the environment. */
env = environ_create();
if (c != NULL && !args_has(args, 'E'))
environ_update(global_s_options, c->environ, env);
s = session_create(prefix, newname, cwd, env, oo, tiop);
/* Set up the options. */
oo = options_create(global_s_options);
if (args_has(args, 'x') || args_has(args, 'y'))
options_set_string(oo, "default-size", 0, "%ux%u", dsx, dsy);
/* Spawn the initial window. */
memset(&sc, 0, sizeof sc);
sc.item = item;
sc.s = s;
sc.c = c;
/* Create the new session. */
idx = -1 - options_get_number(global_s_options, "base-index");
s = session_create(prefix, newname, argc, argv, path, cwd, env, oo,
tiop, idx, &cause);
environ_free(env);
if (s == NULL) {
cmdq_error(item, "create session failed: %s", cause);
sc.name = args_get(args, 'n');
sc.argc = args->argc;
sc.argv = args->argv;
sc.idx = -1;
sc.cwd = args_get(args, 'c');
sc.flags = 0;
if (spawn_window(&sc, &cause) == NULL) {
session_destroy(s, 0, __func__);
cmdq_error(item, "create window failed: %s", cause);
free(cause);
goto error;
}
/* Set the initial window name if one given. */
if (argc >= 0 && (tmp = args_get(args, 'n')) != NULL) {
cp = format_single(item, tmp, c, s, NULL, NULL);
w = s->curw->window;
window_set_name(w, cp);
options_set_number(w->options, "automatic-rename", 0);
free(cp);
goto fail;
}
/*
@@ -347,7 +334,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'P')) {
if ((template = args_get(args, 'F')) == NULL)
template = NEW_SESSION_TEMPLATE;
cp = format_single(item, template, c, s, NULL, NULL);
cp = format_single(item, template, c, s, s->curw, NULL);
cmdq_print(item, "%s", cp);
free(cp);
}
@@ -358,13 +345,13 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
}
cmd_find_from_session(&fs, s, 0);
hooks_insert(s->hooks, item, &fs, "after-new-session");
cmdq_insert_hook(s, item, &fs, "after-new-session");
free(cwd);
free(newname);
return (CMD_RETURN_NORMAL);
error:
fail:
free(cwd);
free(newname);
return (CMD_RETURN_ERROR);

View File

@@ -38,9 +38,9 @@ const struct cmd_entry cmd_new_window_entry = {
.name = "new-window",
.alias = "neww",
.args = { "ac:dF:kn:Pt:", 0, -1 },
.usage = "[-adkP] [-c start-directory] [-F format] [-n window-name] "
CMD_TARGET_WINDOW_USAGE " [command]",
.args = { "ac:de:F:kn:Pt:", 0, -1 },
.usage = "[-adkP] [-c start-directory] [-e environment] [-F format] "
"[-n window-name] " CMD_TARGET_WINDOW_USAGE " [command]",
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX },
@@ -53,87 +53,54 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct cmd_find_state *current = &item->shared->current;
struct spawn_context sc;
struct client *c = cmd_find_client(item, NULL, 1);
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct client *c = cmd_find_client(item, NULL, 1);
int idx = item->target.idx;
const char *cmd, *path, *template, *tmp;
char **argv, *cause, *cp, *cwd, *name;
int argc, detached;
struct environ_entry *envent;
struct winlink *new_wl;
char *cause = NULL, *cp;
const char *template, *add;
struct cmd_find_state fs;
struct args_value *value;
if (args_has(args, 'a') && wl != NULL) {
if ((idx = winlink_shuffle_up(s, wl)) == -1) {
cmdq_error(item, "no free window indexes");
return (CMD_RETURN_ERROR);
}
}
detached = args_has(args, 'd');
if (args->argc == 0) {
cmd = options_get_string(s->options, "default-command");
if (cmd != NULL && *cmd != '\0') {
argc = 1;
argv = (char **)&cmd;
} else {
argc = 0;
argv = NULL;
}
} else {
argc = args->argc;
argv = args->argv;
if (args_has(args, 'a') && (idx = winlink_shuffle_up(s, wl)) == -1) {
cmdq_error(item, "couldn't get a window index");
return (CMD_RETURN_ERROR);
}
path = NULL;
if (item->client != NULL && item->client->session == NULL)
envent = environ_find(item->client->environ, "PATH");
else
envent = environ_find(s->environ, "PATH");
if (envent != NULL)
path = envent->value;
memset(&sc, 0, sizeof sc);
sc.item = item;
sc.s = s;
sc.c = c;
if ((tmp = args_get(args, 'c')) != NULL)
cwd = format_single(item, tmp, c, s, NULL, NULL);
else
cwd = xstrdup(server_client_get_cwd(item->client, s));
sc.name = args_get(args, 'n');
sc.argc = args->argc;
sc.argv = args->argv;
sc.environ = environ_create();
if ((tmp = args_get(args, 'n')) != NULL)
name = format_single(item, tmp, c, s, NULL, NULL);
else
name = NULL;
if (idx != -1)
wl = winlink_find_by_index(&s->windows, idx);
if (wl != NULL && args_has(args, 'k')) {
/*
* Can't use session_detach as it will destroy session if this
* makes it empty.
*/
notify_session_window("window-unlinked", s, wl->window);
wl->flags &= ~WINLINK_ALERTFLAGS;
winlink_stack_remove(&s->lastw, wl);
winlink_remove(&s->windows, wl);
/* Force select/redraw if current. */
if (wl == s->curw) {
detached = 0;
s->curw = NULL;
}
add = args_first_value(args, 'e', &value);
while (add != NULL) {
environ_put(sc.environ, add);
add = args_next_value(&value);
}
if (idx == -1)
idx = -1 - options_get_number(s->options, "base-index");
wl = session_new(s, name, argc, argv, path, cwd, idx,
&cause);
if (wl == NULL) {
sc.idx = idx;
sc.cwd = args_get(args, 'c');
sc.flags = 0;
if (args_has(args, 'd'))
sc.flags |= SPAWN_DETACHED;
if (args_has(args, 'k'))
sc.flags |= SPAWN_KILL;
if ((new_wl = spawn_window(&sc, &cause)) == NULL) {
cmdq_error(item, "create window failed: %s", cause);
free(cause);
goto error;
return (CMD_RETURN_ERROR);
}
if (!detached) {
session_select(s, wl->idx);
cmd_find_from_winlink(current, wl, 0);
if (!args_has(args, 'd') || new_wl == s->curw) {
cmd_find_from_winlink(current, new_wl, 0);
server_redraw_session_group(s);
} else
server_status_session_group(s);
@@ -141,20 +108,15 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'P')) {
if ((template = args_get(args, 'F')) == NULL)
template = NEW_WINDOW_TEMPLATE;
cp = format_single(item, template, c, s, wl, NULL);
cp = format_single(item, template, c, s, new_wl,
new_wl->window->active);
cmdq_print(item, "%s", cp);
free(cp);
}
cmd_find_from_winlink(&fs, wl, 0);
hooks_insert(s->hooks, item, &fs, "after-new-window");
cmd_find_from_winlink(&fs, new_wl, 0);
cmdq_insert_hook(s, item, &fs, "after-new-window");
free(name);
free(cwd);
environ_free(sc.environ);
return (CMD_RETURN_NORMAL);
error:
free(name);
free(cwd);
return (CMD_RETURN_ERROR);
}

1567
cmd-parse.y Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -32,11 +32,14 @@ static struct cmdq_list global_queue = TAILQ_HEAD_INITIALIZER(global_queue);
static const char *
cmdq_name(struct client *c)
{
static char s[32];
static char s[256];
if (c == NULL)
return ("<global>");
xsnprintf(s, sizeof s, "<%p>", c);
if (c->name != NULL)
xsnprintf(s, sizeof s, "<%s>", c->name);
else
xsnprintf(s, sizeof s, "<%p>", c);
return (s);
}
@@ -50,7 +53,7 @@ cmdq_get(struct client *c)
}
/* Append an item. */
void
struct cmdq_item *
cmdq_append(struct client *c, struct cmdq_item *item)
{
struct cmdq_list *queue = cmdq_get(c);
@@ -66,13 +69,15 @@ cmdq_append(struct client *c, struct cmdq_item *item)
item->queue = queue;
TAILQ_INSERT_TAIL(queue, item, entry);
log_debug("%s %s: %s", __func__, cmdq_name(c), item->name);
item = next;
} while (item != NULL);
return (TAILQ_LAST(queue, cmdq_list));
}
/* Insert an item. */
void
struct cmdq_item *
cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item)
{
struct client *c = after->client;
@@ -81,21 +86,81 @@ cmdq_insert_after(struct cmdq_item *after, struct cmdq_item *item)
do {
next = item->next;
item->next = NULL;
item->next = after->next;
after->next = item;
if (c != NULL)
c->references++;
item->client = c;
item->queue = queue;
if (after->next != NULL)
TAILQ_INSERT_AFTER(queue, after->next, item, entry);
else
TAILQ_INSERT_AFTER(queue, after, item, entry);
after->next = item;
TAILQ_INSERT_AFTER(queue, after, item, entry);
log_debug("%s %s: %s after %s", __func__, cmdq_name(c),
item->name, after->name);
after = item;
item = next;
} while (item != NULL);
return (after);
}
/* Insert a hook. */
void
cmdq_insert_hook(struct session *s, struct cmdq_item *item,
struct cmd_find_state *fs, const char *fmt, ...)
{
struct options *oo;
va_list ap;
char *name;
struct cmdq_item *new_item;
struct options_entry *o;
struct options_array_item *a;
struct cmd_list *cmdlist;
if (item->flags & CMDQ_NOHOOKS)
return;
if (s == NULL)
oo = global_s_options;
else
oo = s->options;
va_start(ap, fmt);
xvasprintf(&name, fmt, ap);
va_end(ap);
o = options_get(oo, name);
if (o == NULL) {
free(name);
return;
}
log_debug("running hook %s (parent %p)", name, item);
a = options_array_first(o);
while (a != NULL) {
cmdlist = options_array_item_value(a)->cmdlist;
if (cmdlist == NULL) {
a = options_array_next(a);
continue;
}
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);
}
free(name);
}
/* Continue processing command queue. */
void
cmdq_continue(struct cmdq_item *item)
{
item->flags &= ~CMDQ_WAITING;
}
/* Remove an item. */
@@ -111,30 +176,23 @@ cmdq_remove(struct cmdq_item *item)
if (item->client != NULL)
server_client_unref(item->client);
if (item->type == CMDQ_COMMAND)
if (item->cmdlist != NULL)
cmd_list_free(item->cmdlist);
TAILQ_REMOVE(item->queue, item, entry);
free((void *)item->name);
free(item->name);
free(item);
}
/* Set command group. */
static u_int
cmdq_next_group(void)
{
static u_int group;
return (++group);
}
/* Remove all subsequent items that match this item's group. */
static void
cmdq_remove_group(struct cmdq_item *item)
{
struct cmdq_item *this, *next;
if (item->group == 0)
return;
this = TAILQ_NEXT(item, entry);
while (this != NULL) {
next = TAILQ_NEXT(this, entry);
@@ -151,32 +209,34 @@ cmdq_get_command(struct cmd_list *cmdlist, struct cmd_find_state *current,
{
struct cmdq_item *item, *first = NULL, *last = NULL;
struct cmd *cmd;
u_int group = cmdq_next_group();
char *tmp;
struct cmdq_shared *shared;
shared = xcalloc(1, sizeof *shared);
if (current != NULL)
cmd_find_copy_state(&shared->current, current);
else
cmd_find_clear_state(&shared->current, 0);
if (m != NULL)
memcpy(&shared->mouse, m, sizeof shared->mouse);
struct cmdq_shared *shared = NULL;
u_int group = 0;
TAILQ_FOREACH(cmd, &cmdlist->list, qentry) {
xasprintf(&tmp, "command[%s]", cmd->entry->name);
if (cmd->group != group) {
shared = xcalloc(1, sizeof *shared);
if (current != NULL)
cmd_find_copy_state(&shared->current, current);
else
cmd_find_clear_state(&shared->current, 0);
if (m != NULL)
memcpy(&shared->mouse, m, sizeof shared->mouse);
group = cmd->group;
}
item = xcalloc(1, sizeof *item);
item->name = tmp;
xasprintf(&item->name, "[%s/%p]", cmd->entry->name, item);
item->type = CMDQ_COMMAND;
item->group = group;
item->group = cmd->group;
item->flags = flags;
item->shared = shared;
item->cmdlist = cmdlist;
item->cmd = cmd;
log_debug("%s: %s group %u", __func__, item->name, item->group);
shared->references++;
cmdlist->references++;
@@ -214,13 +274,22 @@ static enum cmd_retval
cmdq_fire_command(struct cmdq_item *item)
{
struct client *c = item->client;
const char *name = cmdq_name(c);
struct cmdq_shared *shared = item->shared;
struct cmd *cmd = item->cmd;
const struct cmd_entry *entry = cmd->entry;
enum cmd_retval retval;
struct cmd_find_state *fsp, fs;
int flags;
char *tmp;
flags = !!(cmd->flags & CMD_CONTROL);
if (log_get_level() > 1) {
tmp = cmd_print(cmd);
log_debug("%s %s: (%u) %s", __func__, name, item->group, tmp);
free(tmp);
}
flags = !!(shared->flags & CMDQ_SHARED_CONTROL);
cmdq_guard(item, "begin", flags);
if (item->client == NULL)
@@ -245,7 +314,7 @@ cmdq_fire_command(struct cmdq_item *item)
fsp = &fs;
else
goto out;
hooks_insert(fsp->s->hooks, item, fsp, "after-%s", entry->name);
cmdq_insert_hook(fsp->s, item, fsp, "after-%s", entry->name);
}
out:
@@ -262,12 +331,9 @@ struct cmdq_item *
cmdq_get_callback1(const char *name, cmdq_cb cb, void *data)
{
struct cmdq_item *item;
char *tmp;
xasprintf(&tmp, "callback[%s]", name);
item = xcalloc(1, sizeof *item);
item->name = tmp;
xasprintf(&item->name, "[%s/%p]", name, item);
item->type = CMDQ_CALLBACK;
item->group = 0;
@@ -279,6 +345,25 @@ cmdq_get_callback1(const char *name, cmdq_cb cb, void *data)
return (item);
}
/* Generic error callback. */
static enum cmd_retval
cmdq_error_callback(struct cmdq_item *item, void *data)
{
char *error = data;
cmdq_error(item, "%s", error);
free(error);
return (CMD_RETURN_NORMAL);
}
/* Get an error callback for the command queue. */
struct cmdq_item *
cmdq_get_error(const char *error)
{
return (cmdq_get_callback(cmdq_error_callback, xstrdup(error)));
}
/* Fire callback on callback queue. */
static enum cmd_retval
cmdq_fire_callback(struct cmdq_item *item)
@@ -391,13 +476,11 @@ void
cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
{
struct client *c = item->client;
long t = item->time;
u_int number = item->number;
if (c == NULL || !(c->flags & CLIENT_CONTROL))
return;
evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard,
(long)item->time, item->number, flags);
server_client_push_stdout(c);
if (c != NULL && (c->flags & CLIENT_CONTROL))
file_print(c, "%%%s %ld %u %d\n", guard, t, number, flags);
}
/* Show message from command. */
@@ -411,29 +494,29 @@ cmdq_print(struct cmdq_item *item, const char *fmt, ...)
char *tmp, *msg;
va_start(ap, fmt);
xvasprintf(&msg, fmt, ap);
va_end(ap);
log_debug("%s: %s", __func__, msg);
if (c == NULL)
/* nothing */;
else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
if (~c->flags & CLIENT_UTF8) {
xvasprintf(&tmp, fmt, ap);
tmp = msg;
msg = utf8_sanitize(tmp);
free(tmp);
evbuffer_add(c->stdout_data, msg, strlen(msg));
free(msg);
} else
evbuffer_add_vprintf(c->stdout_data, fmt, ap);
evbuffer_add(c->stdout_data, "\n", 1);
server_client_push_stdout(c);
}
file_print(c, "%s\n", msg);
} else {
wp = c->session->curw->window->active;
wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode != &window_view_mode)
window_pane_set_mode(wp, &window_view_mode, NULL, NULL);
window_copy_vadd(wp, fmt, ap);
window_copy_add(wp, "%s", msg);
}
va_end(ap);
free(msg);
}
/* Show error from command. */
@@ -444,11 +527,10 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...)
struct cmd *cmd = item->cmd;
va_list ap;
char *msg;
size_t msglen;
char *tmp;
va_start(ap, fmt);
msglen = xvasprintf(&msg, fmt, ap);
xvasprintf(&msg, fmt, ap);
va_end(ap);
log_debug("%s: %s", __func__, msg);
@@ -460,11 +542,11 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...)
tmp = msg;
msg = utf8_sanitize(tmp);
free(tmp);
msglen = strlen(msg);
}
evbuffer_add(c->stderr_data, msg, msglen);
evbuffer_add(c->stderr_data, "\n", 1);
server_client_push_stderr(c);
if (c->flags & CLIENT_CONTROL)
file_print(c, "%s\n", msg);
else
file_error(c, "%s\n", msg);
c->retval = 1;
} else {
*msg = toupper((u_char) *msg);

View File

@@ -19,6 +19,7 @@
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
@@ -33,8 +34,9 @@ const struct cmd_entry cmd_refresh_client_entry = {
.name = "refresh-client",
.alias = "refresh",
.args = { "cC:DlLRSt:U", 0, 1 },
.usage = "[-cDlLRSU] [-C size] " CMD_TARGET_CLIENT_USAGE " [adjustment]",
.args = { "cC:DF:lLRSt:U", 0, 1 },
.usage = "[-cDlLRSU] [-C XxY] [-F flags] " CMD_TARGET_CLIENT_USAGE
" [adjustment]",
.flags = CMD_AFTERHOOK,
.exec = cmd_refresh_client_exec
@@ -48,6 +50,7 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item)
struct tty *tty;
struct window *w;
const char *size, *errstr;
char *copy, *next, *s;
u_int x, y, adjust;
if ((c = cmd_find_client(item, args_get(args, 't'), 0)) == NULL)
@@ -107,28 +110,43 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'l')) {
if (c->session != NULL)
tty_putcode_ptr2(&c->tty, TTYC_MS, "", "?");
} else if (args_has(args, 'C')) {
if ((size = args_get(args, 'C')) == NULL) {
cmdq_error(item, "missing size");
return (CMD_RETURN_ERROR);
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'C') || args_has(args, 'F')) {
if (args_has(args, 'C')) {
if (!(c->flags & CLIENT_CONTROL)) {
cmdq_error(item, "not a control client");
return (CMD_RETURN_ERROR);
}
size = args_get(args, 'C');
if (sscanf(size, "%u,%u", &x, &y) != 2 &&
sscanf(size, "%ux%u", &x, &y) != 2) {
cmdq_error(item, "bad size argument");
return (CMD_RETURN_ERROR);
}
if (x < WINDOW_MINIMUM || x > WINDOW_MAXIMUM ||
y < WINDOW_MINIMUM || y > WINDOW_MAXIMUM) {
cmdq_error(item, "size too small or too big");
return (CMD_RETURN_ERROR);
}
tty_set_size(&c->tty, x, y, 0, 0);
c->flags |= CLIENT_SIZECHANGED;
recalculate_sizes();
}
if (sscanf(size, "%u,%u", &x, &y) != 2 &&
sscanf(size, "%ux%u", &x, &y)) {
cmdq_error(item, "bad size argument");
return (CMD_RETURN_ERROR);
if (args_has(args, 'F')) {
if (!(c->flags & CLIENT_CONTROL)) {
cmdq_error(item, "not a control client");
return (CMD_RETURN_ERROR);
}
s = copy = xstrdup(args_get(args, 'F'));
while ((next = strsep(&s, ",")) != NULL) {
/* Unknown flags are ignored. */
if (strcmp(next, "no-output") == 0)
c->flags |= CLIENT_CONTROL_NOOUTPUT;
}
free(copy);
}
if (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);
}
if (!(c->flags & CLIENT_CONTROL)) {
cmdq_error(item, "not a control client");
return (CMD_RETURN_ERROR);
}
tty_set_size(&c->tty, x, y);
c->flags |= CLIENT_SIZECHANGED;
recalculate_sizes();
return (CMD_RETURN_NORMAL);
}

View File

@@ -19,6 +19,7 @@
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
@@ -55,10 +56,11 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
struct window *w = wl->window;
struct client *c = item->client;
struct session *s = item->target.s;
const char *errstr;
char *cause;
const char *errstr, *p;
char *cause, *copy;
u_int adjust;
int x, y;
int x, y, percentage;
size_t plen;
if (args_has(args, 'M')) {
if (cmd_mouse_window(&shared->mouse, &s) == NULL)
@@ -91,21 +93,58 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
}
}
if (args_has(args, 'x')) {
x = args_strtonum(args, 'x', PANE_MINIMUM, INT_MAX, &cause);
if (cause != NULL) {
cmdq_error(item, "width %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
if ((p = args_get(args, 'x')) != NULL) {
plen = strlen(p);
if (p[plen - 1] == '%') {
copy = xstrdup(p);
copy[plen - 1] = '\0';
percentage = strtonum(copy, 0, INT_MAX, &errstr);
free(copy);
if (errstr != NULL) {
cmdq_error(item, "width %s", errstr);
return (CMD_RETURN_ERROR);
}
x = (w->sx * percentage) / 100;
if (x < PANE_MINIMUM)
x = PANE_MINIMUM;
if (x > INT_MAX)
x = INT_MAX;
} else {
x = args_strtonum(args, 'x', PANE_MINIMUM, INT_MAX,
&cause);
if (cause != NULL) {
cmdq_error(item, "width %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
}
layout_resize_pane_to(wp, LAYOUT_LEFTRIGHT, x);
}
if (args_has(args, 'y')) {
y = args_strtonum(args, 'y', PANE_MINIMUM, INT_MAX, &cause);
if (cause != NULL) {
cmdq_error(item, "height %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
if ((p = args_get(args, 'y')) != NULL) {
plen = strlen(p);
if (p[plen - 1] == '%') {
copy = xstrdup(p);
copy[plen - 1] = '\0';
percentage = strtonum(copy, 0, INT_MAX, &errstr);
free(copy);
if (errstr != NULL) {
cmdq_error(item, "height %s", errstr);
return (CMD_RETURN_ERROR);
}
y = (w->sy * percentage) / 100;
if (y < PANE_MINIMUM)
y = PANE_MINIMUM;
if (y > INT_MAX)
y = INT_MAX;
}
else {
y = args_strtonum(args, 'y', PANE_MINIMUM, INT_MAX,
&cause);
if (cause != NULL) {
cmdq_error(item, "height %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
}
layout_resize_pane_to(wp, LAYOUT_TOPBOTTOM, y);
}
@@ -143,14 +182,14 @@ cmd_resize_pane_mouse_update(struct client *c, struct mouse_event *m)
}
w = wl->window;
y = m->y; x = m->x;
if (m->statusat == 0 && y > 0)
y--;
y = m->y + m->oy; x = m->x + m->ox;
if (m->statusat == 0 && y >= m->statuslines)
y -= m->statuslines;
else if (m->statusat > 0 && y >= (u_int)m->statusat)
y = m->statusat - 1;
ly = m->ly; lx = m->lx;
if (m->statusat == 0 && ly > 0)
ly--;
ly = m->ly + m->oy; lx = m->lx + m->ox;
if (m->statusat == 0 && ly >= m->statuslines)
ly -= m->statuslines;
else if (m->statusat > 0 && ly >= (u_int)m->statusat)
ly = m->statusat - 1;

View File

@@ -53,6 +53,7 @@ cmd_resize_window_exec(struct cmd *self, struct cmdq_item *item)
const char *errstr;
char *cause;
u_int adjust, sx, sy;
int xpixel = -1, ypixel = -1;
if (args->argc == 0)
adjust = 1;
@@ -97,13 +98,16 @@ cmd_resize_window_exec(struct cmd *self, struct cmdq_item *item)
} else if (args_has(args, 'D'))
sy += adjust;
if (args_has(args, 'A'))
default_window_size(s, w, &sx, &sy, WINDOW_SIZE_LARGEST);
else if (args_has(args, 'a'))
default_window_size(s, w, &sx, &sy, WINDOW_SIZE_SMALLEST);
if (args_has(args, 'A')) {
default_window_size(NULL, s, w, &sx, &sy, &xpixel, &ypixel,
WINDOW_SIZE_LARGEST);
} else if (args_has(args, 'a')) {
default_window_size(NULL, s, w, &sx, &sy, &xpixel, &ypixel,
WINDOW_SIZE_SMALLEST);
}
options_set_number(w->options, "window-size", WINDOW_SIZE_MANUAL);
resize_window(w, sx, sy);
resize_window(w, sx, sy, xpixel, ypixel);
return (CMD_RETURN_NORMAL);
}

View File

@@ -20,7 +20,7 @@
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "tmux.h"
@@ -34,9 +34,9 @@ const struct cmd_entry cmd_respawn_pane_entry = {
.name = "respawn-pane",
.alias = "respawnp",
.args = { "c:kt:", 0, -1 },
.usage = "[-c start-directory] [-k] " CMD_TARGET_PANE_USAGE
" [command]",
.args = { "c:e:kt:", 0, -1 },
.usage = "[-k] [-c start-directory] [-e environment] "
CMD_TARGET_PANE_USAGE " [command]",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -48,53 +48,49 @@ static enum cmd_retval
cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct winlink *wl = item->target.wl;
struct window *w = wl->window;
struct window_pane *wp = item->target.wp;
struct client *c = cmd_find_client(item, NULL, 1);
struct spawn_context sc;
struct session *s = item->target.s;
struct environ *env;
const char *path = NULL, *cp;
char *cause, *cwd = NULL;
u_int idx;
struct environ_entry *envent;
struct winlink *wl = item->target.wl;
struct window_pane *wp = item->target.wp;
char *cause = NULL;
const char *add;
struct args_value *value;
if (!args_has(self->args, 'k') && wp->fd != -1) {
if (window_pane_index(wp, &idx) != 0)
fatalx("index not found");
cmdq_error(item, "pane still active: %s:%d.%u",
s->name, wl->idx, idx);
return (CMD_RETURN_ERROR);
memset(&sc, 0, sizeof sc);
sc.item = item;
sc.s = s;
sc.wl = wl;
sc.wp0 = wp;
sc.lc = NULL;
sc.name = NULL;
sc.argc = args->argc;
sc.argv = args->argv;
sc.environ = environ_create();
add = args_first_value(args, 'e', &value);
while (add != NULL) {
environ_put(sc.environ, add);
add = args_next_value(&value);
}
window_pane_reset_mode_all(wp);
screen_reinit(&wp->base);
input_init(wp);
sc.idx = -1;
sc.cwd = args_get(args, 'c');
if (item->client != NULL && item->client->session == NULL)
envent = environ_find(item->client->environ, "PATH");
else
envent = environ_find(s->environ, "PATH");
if (envent != NULL)
path = envent->value;
sc.flags = SPAWN_RESPAWN;
if (args_has(args, 'k'))
sc.flags |= SPAWN_KILL;
if ((cp = args_get(args, 'c')) != NULL)
cwd = format_single(item, cp, c, s, NULL, NULL);
env = environ_for_session(s, 0);
if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, cwd, env,
s->tio, &cause) != 0) {
if (spawn_pane(&sc, &cause) == NULL) {
cmdq_error(item, "respawn pane failed: %s", cause);
free(cause);
environ_free(env);
free(cwd);
return (CMD_RETURN_ERROR);
}
environ_free(env);
free(cwd);
wp->flags |= PANE_REDRAW;
server_status_window(w);
server_status_window(wp->window);
environ_free(sc.environ);
return (CMD_RETURN_NORMAL);
}

View File

@@ -19,7 +19,7 @@
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "tmux.h"
@@ -34,9 +34,9 @@ const struct cmd_entry cmd_respawn_window_entry = {
.name = "respawn-window",
.alias = "respawnw",
.args = { "c:kt:", 0, -1 },
.usage = "[-c start-directory] [-k] " CMD_TARGET_WINDOW_USAGE
" [command]",
.args = { "c:e:kt:", 0, -1 },
.usage = "[-k] [-c start-directory] [-e environment] "
CMD_TARGET_WINDOW_USAGE " [command]",
.target = { 't', CMD_FIND_WINDOW, 0 },
@@ -48,64 +48,45 @@ static enum cmd_retval
cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct spawn_context sc;
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct window *w = wl->window;
struct window_pane *wp;
struct client *c = cmd_find_client(item, NULL, 1);
struct environ *env;
const char *path = NULL, *cp;
char *cause, *cwd = NULL;
struct environ_entry *envent;
char *cause = NULL;
const char *add;
struct args_value *value;
if (!args_has(self->args, 'k')) {
TAILQ_FOREACH(wp, &w->panes, entry) {
if (wp->fd == -1)
continue;
cmdq_error(item, "window still active: %s:%d", s->name,
wl->idx);
return (CMD_RETURN_ERROR);
}
memset(&sc, 0, sizeof sc);
sc.item = item;
sc.s = s;
sc.wl = wl;
sc.c = cmd_find_client(item, NULL, 1);
sc.name = NULL;
sc.argc = args->argc;
sc.argv = args->argv;
sc.environ = environ_create();
add = args_first_value(args, 'e', &value);
while (add != NULL) {
environ_put(sc.environ, add);
add = args_next_value(&value);
}
wp = TAILQ_FIRST(&w->panes);
TAILQ_REMOVE(&w->panes, wp, entry);
layout_free(w);
window_destroy_panes(w);
TAILQ_INSERT_HEAD(&w->panes, wp, entry);
window_pane_resize(wp, w->sx, w->sy);
sc.idx = -1;
sc.cwd = args_get(args, 'c');
if (item->client != NULL && item->client->session == NULL)
envent = environ_find(item->client->environ, "PATH");
else
envent = environ_find(s->environ, "PATH");
if (envent != NULL)
path = envent->value;
sc.flags = SPAWN_RESPAWN;
if (args_has(args, 'k'))
sc.flags |= SPAWN_KILL;
if ((cp = args_get(args, 'c')) != NULL)
cwd = format_single(item, cp, c, s, NULL, NULL);
env = environ_for_session(s, 0);
if (window_pane_spawn(wp, args->argc, args->argv, path, NULL, cwd, env,
s->tio, &cause) != 0) {
if (spawn_window(&sc, &cause) == NULL) {
cmdq_error(item, "respawn window failed: %s", cause);
free(cause);
environ_free(env);
free(cwd);
server_destroy_pane(wp, 0);
return (CMD_RETURN_ERROR);
}
environ_free(env);
free(cwd);
layout_init(w, wp);
window_pane_reset_mode_all(wp);
screen_reinit(&wp->base);
input_init(wp);
window_set_active_pane(w, wp);
recalculate_sizes();
server_redraw_window(w);
server_redraw_window(wl->window);
environ_free(sc.environ);
return (CMD_RETURN_NORMAL);
}

View File

@@ -31,8 +31,8 @@ const struct cmd_entry cmd_rotate_window_entry = {
.name = "rotate-window",
.alias = "rotatew",
.args = { "Dt:U", 0, 0 },
.usage = "[-DU] " CMD_TARGET_WINDOW_USAGE,
.args = { "Dt:UZ", 0, 0 },
.usage = "[-DUZ] " CMD_TARGET_WINDOW_USAGE,
.target = { 't', CMD_FIND_WINDOW, 0 },
@@ -50,7 +50,7 @@ cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item)
struct layout_cell *lc;
u_int sx, sy, xoff, yoff;
server_unzoom_window(w);
window_push_zoom(w, args_has(self->args, 'Z'));
if (args_has(self->args, 'D')) {
wp = TAILQ_LAST(&w->panes, window_panes);
@@ -77,9 +77,6 @@ cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item)
if ((wp = TAILQ_PREV(w->active, window_panes, entry)) == NULL)
wp = TAILQ_LAST(&w->panes, window_panes);
window_set_active_pane(w, wp);
cmd_find_from_winlink_pane(current, wl, wp, 0);
server_redraw_window(w);
} else {
wp = TAILQ_FIRST(&w->panes);
TAILQ_REMOVE(&w->panes, wp, entry);
@@ -105,10 +102,12 @@ cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item)
if ((wp = TAILQ_NEXT(w->active, entry)) == NULL)
wp = TAILQ_FIRST(&w->panes);
window_set_active_pane(w, wp);
cmd_find_from_winlink_pane(current, wl, wp, 0);
server_redraw_window(w);
}
window_set_active_pane(w, wp, 1);
cmd_find_from_winlink_pane(current, wl, wp, 0);
window_pop_zoom(w);
server_redraw_window(w);
return (CMD_RETURN_NORMAL);
}

View File

@@ -155,7 +155,7 @@ cmd_run_shell_callback(struct job *job)
free(msg);
if (cdata->item != NULL)
cdata->item->flags &= ~CMDQ_WAITING;
cmdq_continue(cdata->item);
}
static void

View File

@@ -55,6 +55,20 @@ const struct cmd_entry cmd_show_buffer_entry = {
.exec = cmd_save_buffer_exec
};
static void
cmd_save_buffer_done(__unused struct client *c, const char *path, int error,
__unused int closed, __unused struct evbuffer *buffer, void *data)
{
struct cmdq_item *item = data;
if (!closed)
return;
if (error != 0)
cmdq_error(item, "%s: %s", path, strerror(error));
cmdq_continue(item);
}
static enum cmd_retval
cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
{
@@ -64,18 +78,17 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
struct winlink *wl = item->target.wl;
struct window_pane *wp = item->target.wp;
struct paste_buffer *pb;
const char *bufname, *bufdata, *start, *end, *flags;
char *msg, *path, *file;
size_t size, used, msglen, bufsize;
FILE *f;
int flags;
const char *bufname = args_get(args, 'b'), *bufdata;
size_t bufsize;
char *path;
if (!args_has(args, 'b')) {
if (bufname == NULL) {
if ((pb = paste_get_top(NULL)) == NULL) {
cmdq_error(item, "no buffers");
return (CMD_RETURN_ERROR);
}
} else {
bufname = args_get(args, 'b');
pb = paste_get_name(bufname);
if (pb == NULL) {
cmdq_error(item, "no buffer %s", bufname);
@@ -88,74 +101,13 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
path = xstrdup("-");
else
path = format_single(item, args->argv[0], c, s, wl, wp);
if (strcmp(path, "-") == 0) {
free(path);
c = item->client;
if (c == NULL) {
cmdq_error(item, "can't write to stdout");
return (CMD_RETURN_ERROR);
}
if (c->session == NULL || (c->flags & CLIENT_CONTROL))
goto do_stdout;
goto do_print;
}
flags = "wb";
if (args_has(self->args, 'a'))
flags = "ab";
file = server_client_get_path(c, path);
flags = O_APPEND;
else
flags = 0;
file_write(item->client, path, flags, bufdata, bufsize,
cmd_save_buffer_done, item);
free(path);
f = fopen(file, flags);
if (f == NULL) {
cmdq_error(item, "%s: %s", file, strerror(errno));
free(file);
return (CMD_RETURN_ERROR);
}
if (fwrite(bufdata, 1, bufsize, f) != bufsize) {
cmdq_error(item, "%s: write error", file);
fclose(f);
free(file);
return (CMD_RETURN_ERROR);
}
fclose(f);
free(file);
return (CMD_RETURN_NORMAL);
do_stdout:
evbuffer_add(c->stdout_data, bufdata, bufsize);
server_client_push_stdout(c);
return (CMD_RETURN_NORMAL);
do_print:
if (bufsize > (INT_MAX / 4) - 1) {
cmdq_error(item, "buffer too big");
return (CMD_RETURN_ERROR);
}
msg = NULL;
used = 0;
while (used != bufsize) {
start = bufdata + used;
end = memchr(start, '\n', bufsize - used);
if (end != NULL)
size = end - start;
else
size = bufsize - used;
msglen = size * 4 + 1;
msg = xrealloc(msg, msglen);
strvisx(msg, start, size, VIS_OCTAL|VIS_TAB);
cmdq_print(item, "%s", msg);
used += size + (end != NULL);
}
free(msg);
return (CMD_RETURN_NORMAL);
return (CMD_RETURN_WAIT);
}

View File

@@ -33,8 +33,8 @@ const struct cmd_entry cmd_select_pane_entry = {
.name = "select-pane",
.alias = "selectp",
.args = { "DdegLlMmP:RT:t:U", 0, 0 },
.usage = "[-DdegLlMmRU] [-P style] [-T title] " CMD_TARGET_PANE_USAGE,
.args = { "DdegLlMmP:RT:t:UZ", 0, 0 }, /* -P and -g deprecated */
.usage = "[-DdeLlMmRUZ] [-T title] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 },
@@ -46,8 +46,8 @@ const struct cmd_entry cmd_last_pane_entry = {
.name = "last-pane",
.alias = "lastp",
.args = { "det:", 0, 0 },
.usage = "[-de] " CMD_TARGET_WINDOW_USAGE,
.args = { "det:Z", 0, 0 },
.usage = "[-deZ] " CMD_TARGET_WINDOW_USAGE,
.target = { 't', CMD_FIND_WINDOW, 0 },
@@ -90,9 +90,10 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
struct window *w = wl->window;
struct session *s = item->target.s;
struct window_pane *wp = item->target.wp, *lastwp, *markedwp;
struct style *sy = &wp->style;
char *pane_title;
const char *style;
struct style *sy;
struct options_entry *o;
if (self->entry == &cmd_last_pane_entry || args_has(args, 'l')) {
lastwp = w->last;
@@ -110,12 +111,15 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
else if (args_has(self->args, 'd'))
lastwp->flags |= PANE_INPUTOFF;
else {
server_unzoom_window(w);
if (window_push_zoom(w, args_has(self->args, 'Z')))
server_redraw_window(w);
window_redraw_active_switch(w, lastwp);
if (window_set_active_pane(w, lastwp)) {
if (window_set_active_pane(w, lastwp, 1)) {
cmd_find_from_winlink(current, wl, 0);
cmd_select_pane_redraw(w);
}
if (window_pop_zoom(w))
server_redraw_window(w);
}
return (CMD_RETURN_NORMAL);
}
@@ -144,30 +148,39 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(self->args, 'P') || args_has(self->args, 'g')) {
if ((style = args_get(args, 'P')) != NULL) {
style_set(sy, &grid_default_cell);
if (style_parse(sy, &grid_default_cell, style) == -1) {
o = options_set_style(wp->options, "window-style", 0,
style);
if (o == NULL) {
cmdq_error(item, "bad style: %s", style);
return (CMD_RETURN_ERROR);
}
wp->flags |= PANE_REDRAW;
options_set_style(wp->options, "window-active-style", 0,
style);
wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
}
if (args_has(self->args, 'g'))
if (args_has(self->args, 'g')) {
sy = options_get_style(wp->options, "window-style");
cmdq_print(item, "%s", style_tostring(sy));
}
return (CMD_RETURN_NORMAL);
}
if (args_has(self->args, 'L')) {
server_unzoom_window(wp->window);
window_push_zoom(w, 1);
wp = window_pane_find_left(wp);
window_pop_zoom(w);
} else if (args_has(self->args, 'R')) {
server_unzoom_window(wp->window);
window_push_zoom(w, 1);
wp = window_pane_find_right(wp);
window_pop_zoom(w);
} else if (args_has(self->args, 'U')) {
server_unzoom_window(wp->window);
window_push_zoom(w, 1);
wp = window_pane_find_up(wp);
window_pop_zoom(w);
} else if (args_has(self->args, 'D')) {
server_unzoom_window(wp->window);
window_push_zoom(w, 1);
wp = window_pane_find_down(wp);
window_pop_zoom(w);
}
if (wp == NULL)
return (CMD_RETURN_NORMAL);
@@ -184,21 +197,24 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(self->args, 'T')) {
pane_title = format_single(item, args_get(self->args, 'T'),
c, s, wl, wp);
screen_set_title(&wp->base, pane_title);
server_status_window(wp->window);
if (screen_set_title(&wp->base, pane_title))
server_status_window(wp->window);
free(pane_title);
return (CMD_RETURN_NORMAL);
}
if (wp == w->active)
return (CMD_RETURN_NORMAL);
server_unzoom_window(wp->window);
if (window_push_zoom(w, args_has(self->args, 'Z')))
server_redraw_window(w);
window_redraw_active_switch(w, wp);
if (window_set_active_pane(w, wp)) {
if (window_set_active_pane(w, wp, 1)) {
cmd_find_from_winlink_pane(current, wl, wp, 0);
hooks_insert(s->hooks, item, current, "after-select-pane");
cmdq_insert_hook(s, item, current, "after-select-pane");
cmd_select_pane_redraw(w);
}
if (window_pop_zoom(w))
server_redraw_window(w);
return (CMD_RETURN_NORMAL);
}

View File

@@ -119,7 +119,7 @@ cmd_select_window_exec(struct cmd *self, struct cmdq_item *item)
}
cmd_find_from_session(current, s, 0);
server_redraw_session(s);
hooks_insert(s->hooks, item, current, "after-select-window");
cmdq_insert_hook(s, item, current, "after-select-window");
} else {
/*
* If -T and select-window is invoked on same window as
@@ -137,7 +137,7 @@ cmd_select_window_exec(struct cmd *self, struct cmdq_item *item)
cmd_find_from_session(current, s, 0);
server_redraw_session(s);
}
hooks_insert(s->hooks, item, current, "after-select-window");
cmdq_insert_hook(s, item, current, "after-select-window");
}
recalculate_sizes();

View File

@@ -33,8 +33,9 @@ const struct cmd_entry cmd_send_keys_entry = {
.name = "send-keys",
.alias = "send",
.args = { "lXRMN:t:", 0, -1 },
.usage = "[-lXRM] [-N repeat-count] " CMD_TARGET_PANE_USAGE " key ...",
.args = { "FHlMN:Rt:X", 0, -1 },
.usage = "[-FHlMRX] [-N repeat-count] " CMD_TARGET_PANE_USAGE
" key ...",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -55,31 +56,76 @@ const struct cmd_entry cmd_send_prefix_entry = {
.exec = cmd_send_keys_exec
};
static void
cmd_send_keys_inject(struct client *c, struct cmdq_item *item, key_code key)
static struct cmdq_item *
cmd_send_keys_inject_key(struct client *c, struct cmd_find_state *fs,
struct cmdq_item *item, key_code key)
{
struct window_pane *wp = item->target.wp;
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct session *s = fs->s;
struct winlink *wl = fs->wl;
struct window_pane *wp = fs->wp;
struct window_mode_entry *wme;
struct key_table *table;
struct key_binding *bd;
wme = TAILQ_FIRST(&wp->modes);
wme = TAILQ_FIRST(&fs->wp->modes);
if (wme == NULL || wme->mode->key_table == NULL) {
if (options_get_number(wp->window->options, "xterm-keys"))
if (options_get_number(fs->wp->window->options, "xterm-keys"))
key |= KEYC_XTERM;
window_pane_key(wp, NULL, s, wl, key, NULL);
return;
if (window_pane_key(wp, item->client, s, wl, key, NULL) != 0)
return (NULL);
return (item);
}
table = key_bindings_get_table(wme->mode->key_table(wme), 1);
bd = key_bindings_get(table, key & ~KEYC_XTERM);
if (bd != NULL) {
table->references++;
key_bindings_dispatch(bd, item, c, NULL, &item->target);
item = key_bindings_dispatch(bd, item, c, NULL, &item->target);
key_bindings_unref_table(table);
}
return (item);
}
static struct cmdq_item *
cmd_send_keys_inject_string(struct client *c, struct cmd_find_state *fs,
struct cmdq_item *item, struct args *args, int i)
{
const char *s = args->argv[i];
struct cmdq_item *new_item;
struct utf8_data *ud, *uc;
wchar_t wc;
key_code key;
char *endptr;
long n;
int literal;
if (args_has(args, 'H')) {
n = strtol(s, &endptr, 16);
if (*s =='\0' || n < 0 || n > 0xff || *endptr != '\0')
return (item);
return (cmd_send_keys_inject_key(c, fs, item, KEYC_LITERAL|n));
}
literal = args_has(args, 'l');
if (!literal) {
key = key_string_lookup_string(s);
if (key != KEYC_NONE && key != KEYC_UNKNOWN) {
new_item = cmd_send_keys_inject_key(c, fs, item, key);
if (new_item != NULL)
return (new_item);
}
literal = 1;
}
if (literal) {
ud = utf8_fromcstr(s);
for (uc = ud; uc->size != 0; uc++) {
if (utf8_combine(uc, &wc) != UTF8_DONE)
continue;
item = cmd_send_keys_inject_key(c, fs, item, wc);
}
free(ud);
}
return (item);
}
static enum cmd_retval
@@ -87,14 +133,13 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct client *c = cmd_find_client(item, NULL, 1);
struct cmd_find_state *fs = &item->target;
struct window_pane *wp = item->target.wp;
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct mouse_event *m = &item->shared->mouse;
struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
struct utf8_data *ud, *uc;
wchar_t wc;
int i, literal;
int i;
key_code key;
u_int np = 1;
char *cause = NULL;
@@ -132,7 +177,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
cmdq_error(item, "no mouse target");
return (CMD_RETURN_ERROR);
}
window_pane_key(wp, NULL, s, wl, m->key, m);
window_pane_key(wp, item->client, s, wl, m->key, m);
return (CMD_RETURN_NORMAL);
}
@@ -141,7 +186,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
key = options_get_number(s->options, "prefix2");
else
key = options_get_number(s->options, "prefix");
cmd_send_keys_inject(c, item, key);
cmd_send_keys_inject_key(c, fs, item, key);
return (CMD_RETURN_NORMAL);
}
@@ -151,26 +196,8 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
}
for (; np != 0; np--) {
for (i = 0; i < args->argc; i++) {
literal = args_has(args, 'l');
if (!literal) {
key = key_string_lookup_string(args->argv[i]);
if (key != KEYC_NONE && key != KEYC_UNKNOWN)
cmd_send_keys_inject(c, item, key);
else
literal = 1;
}
if (literal) {
ud = utf8_fromcstr(args->argv[i]);
for (uc = ud; uc->size != 0; uc++) {
if (utf8_combine(uc, &wc) != UTF8_DONE)
continue;
cmd_send_keys_inject(c, item, wc);
}
free(ud);
}
}
for (i = 0; i < args->argc; i++)
item = cmd_send_keys_inject_string(c, fs, item, args, i);
}
return (CMD_RETURN_NORMAL);

View File

@@ -1,133 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2012 Thomas Adam <thomas@xteddy.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
/*
* Set or show global or session hooks.
*/
static enum cmd_retval cmd_set_hook_exec(struct cmd *, struct cmdq_item *);
const struct cmd_entry cmd_set_hook_entry = {
.name = "set-hook",
.alias = NULL,
.args = { "gRt:u", 1, 2 },
.usage = "[-gRu] " CMD_TARGET_SESSION_USAGE " hook-name [command]",
.target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL },
.flags = CMD_AFTERHOOK,
.exec = cmd_set_hook_exec
};
const struct cmd_entry cmd_show_hooks_entry = {
.name = "show-hooks",
.alias = NULL,
.args = { "gt:", 0, 1 },
.usage = "[-g] " CMD_TARGET_SESSION_USAGE,
.target = { 't', CMD_FIND_SESSION, 0 },
.flags = CMD_AFTERHOOK,
.exec = cmd_set_hook_exec
};
static enum cmd_retval
cmd_set_hook_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct cmd_list *cmdlist;
struct hooks *hooks;
struct hook *hook;
char *cause, *tmp;
const char *name, *cmd, *target;
if (args_has(args, 'g'))
hooks = global_hooks;
else {
if (item->target.s == NULL) {
target = args_get(args, 't');
if (target != NULL)
cmdq_error(item, "no such session: %s", target);
else
cmdq_error(item, "no current session");
return (CMD_RETURN_ERROR);
}
hooks = item->target.s->hooks;
}
if (self->entry == &cmd_show_hooks_entry) {
hook = hooks_first(hooks);
while (hook != NULL) {
tmp = cmd_list_print(hook->cmdlist);
cmdq_print(item, "%s -> %s", hook->name, tmp);
free(tmp);
hook = hooks_next(hook);
}
return (CMD_RETURN_NORMAL);
}
name = args->argv[0];
if (*name == '\0') {
cmdq_error(item, "invalid hook name");
return (CMD_RETURN_ERROR);
}
if (args->argc < 2)
cmd = NULL;
else
cmd = args->argv[1];
if (cmd != NULL && (args_has(args, 'R') || args_has(args, 'u'))) {
cmdq_error(item, "no command allowed");
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'R')) {
notify_hook(item, name);
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'u')) {
hooks_remove(hooks, name);
return (CMD_RETURN_NORMAL);
}
if (cmd == NULL) {
cmdq_error(item, "no command given");
return (CMD_RETURN_ERROR);
}
cmdlist = cmd_string_parse(cmd, NULL, 0, &cause);
if (cmdlist == NULL) {
if (cause != NULL) {
cmdq_error(item, "%s", cause);
free(cause);
}
return (CMD_RETURN_ERROR);
}
hooks_add(hooks, name, cmdlist);
cmd_list_free(cmdlist);
return (CMD_RETURN_NORMAL);
}

View File

@@ -43,10 +43,10 @@ const struct cmd_entry cmd_set_option_entry = {
.name = "set-option",
.alias = "set",
.args = { "aFgoqst:uw", 1, 2 },
.usage = "[-aFgosquw] [-t target-window] option [value]",
.args = { "aFgopqst:uw", 1, 2 },
.usage = "[-aFgopqsuw] " CMD_TARGET_PANE_USAGE " option [value]",
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL },
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
.flags = CMD_AFTERHOOK,
.exec = cmd_set_option_exec
@@ -65,6 +65,19 @@ const struct cmd_entry cmd_set_window_option_entry = {
.exec = cmd_set_option_exec
};
const struct cmd_entry cmd_set_hook_entry = {
.name = "set-hook",
.alias = NULL,
.args = { "agRt:u", 1, 2 },
.usage = "[-agRu] " CMD_TARGET_SESSION_USAGE " hook [command]",
.target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL },
.flags = CMD_AFTERHOOK,
.exec = cmd_set_option_exec
};
static enum cmd_retval
cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
{
@@ -75,18 +88,27 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
struct session *s = fs->s;
struct winlink *wl = fs->wl;
struct window *w;
enum options_table_scope scope;
struct window_pane *wp;
struct options *oo;
struct options_entry *parent, *o;
char *name, *argument, *value = NULL, *cause;
const char *target;
int window, idx, already, error, ambiguous;
int scope;
struct style *sy;
window = (self->entry == &cmd_set_window_option_entry);
/* Expand argument. */
c = cmd_find_client(item, NULL, 1);
argument = format_single(item, args->argv[0], c, s, wl, NULL);
/* If set-hook -R, fire the hook straight away. */
if (self->entry == &cmd_set_hook_entry && args_has(args, 'R')) {
notify_hook(item, argument);
free(argument);
return (CMD_RETURN_NORMAL);
}
/* Parse option name and index. */
name = options_match(argument, &idx, &ambiguous);
if (name == NULL) {
@@ -105,25 +127,8 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
else
value = xstrdup(args->argv[1]);
/*
* Figure out the scope: for user options it comes from the arguments,
* otherwise from the option name.
*/
if (*name == '@') {
window = (self->entry == &cmd_set_window_option_entry);
scope = options_scope_from_flags(args, window, fs, &oo, &cause);
} else {
if (options_get_only(global_options, name) != NULL)
scope = OPTIONS_TABLE_SERVER;
else if (options_get_only(global_s_options, name) != NULL)
scope = OPTIONS_TABLE_SESSION;
else if (options_get_only(global_w_options, name) != NULL)
scope = OPTIONS_TABLE_WINDOW;
else {
scope = OPTIONS_TABLE_NONE;
xasprintf(&cause, "unknown option: %s", argument);
}
}
/* Get the scope and table for the option .*/
scope = options_scope_from_name(args, window, name, fs, &oo, &cause);
if (scope == OPTIONS_TABLE_NONE) {
if (args_has(args, 'q'))
goto out;
@@ -131,35 +136,6 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
free(cause);
goto fail;
}
/* Which table should this option go into? */
if (scope == OPTIONS_TABLE_SERVER)
oo = global_options;
else if (scope == OPTIONS_TABLE_SESSION) {
if (args_has(self->args, 'g'))
oo = global_s_options;
else if (s == NULL) {
target = args_get(args, 't');
if (target != NULL)
cmdq_error(item, "no such session: %s", target);
else
cmdq_error(item, "no current session");
goto fail;
} else
oo = s->options;
} else if (scope == OPTIONS_TABLE_WINDOW) {
if (args_has(self->args, 'g'))
oo = global_w_options;
else if (wl == NULL) {
target = args_get(args, 't');
if (target != NULL)
cmdq_error(item, "no such window: %s", target);
else
cmdq_error(item, "no current window");
goto fail;
} else
oo = wl->window->options;
}
o = options_get_only(oo, name);
parent = options_get(oo, name);
@@ -200,8 +176,11 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
options_default(oo, options_table_entry(o));
else
options_remove(o);
} else
options_array_set(o, idx, NULL, 0);
} else if (options_array_set(o, idx, NULL, 0, &cause) != 0) {
cmdq_error(item, "%s", cause);
free(cause);
goto fail;
}
} else if (*name == '@') {
if (value == NULL) {
cmdq_error(item, "empty value");
@@ -222,9 +201,15 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
if (idx == -1) {
if (!append)
options_array_clear(o);
options_array_assign(o, value);
} else if (options_array_set(o, idx, value, append) != 0) {
cmdq_error(item, "invalid index: %s", argument);
if (options_array_assign(o, value, &cause) != 0) {
cmdq_error(item, "%s", cause);
free(cause);
goto fail;
}
} else if (options_array_set(o, idx, value, append,
&cause) != 0) {
cmdq_error(item, "%s", cause);
free(cause);
goto fail;
}
}
@@ -265,8 +250,8 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
alerts_reset_all();
if (strcmp(name, "window-style") == 0 ||
strcmp(name, "window-active-style") == 0) {
RB_FOREACH(w, windows, &windows)
w->flags |= WINDOW_STYLECHANGED;
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)
@@ -366,7 +351,7 @@ cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo,
return (-1);
}
return (0);
case OPTIONS_TABLE_ARRAY:
case OPTIONS_TABLE_COMMAND:
break;
}
return (-1);

View File

@@ -29,19 +29,19 @@
static enum cmd_retval cmd_show_options_exec(struct cmd *, struct cmdq_item *);
static enum cmd_retval cmd_show_options_one(struct cmd *, struct cmdq_item *,
struct options *);
static void cmd_show_options_print(struct cmd *, struct cmdq_item *,
struct options_entry *, int, int);
static enum cmd_retval cmd_show_options_all(struct cmd *, struct cmdq_item *,
struct options *);
int, struct options *);
const struct cmd_entry cmd_show_options_entry = {
.name = "show-options",
.alias = "show",
.args = { "gqst:vw", 0, 1 },
.usage = "[-gqsvw] [-t target-session|target-window] [option]",
.args = { "AgHpqst:vw", 0, 1 },
.usage = "[-AgHpqsvw] " CMD_TARGET_PANE_USAGE " [option]",
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL },
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
.flags = CMD_AFTERHOOK,
.exec = cmd_show_options_exec
@@ -60,129 +60,193 @@ const struct cmd_entry cmd_show_window_options_entry = {
.exec = cmd_show_options_exec
};
const struct cmd_entry cmd_show_hooks_entry = {
.name = "show-hooks",
.alias = NULL,
.args = { "gt:", 0, 1 },
.usage = "[-g] " CMD_TARGET_SESSION_USAGE,
.target = { 't', CMD_FIND_SESSION, 0 },
.flags = CMD_AFTERHOOK,
.exec = cmd_show_options_exec
};
static enum cmd_retval
cmd_show_options_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct cmd_find_state *fs = &item->target;
struct client *c = cmd_find_client(item, NULL, 1);
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct options *oo;
enum options_table_scope scope;
char *cause;
int window;
char *argument, *name = NULL, *cause;
int window, idx, ambiguous, parent, scope;
struct options_entry *o;
window = (self->entry == &cmd_show_window_options_entry);
scope = options_scope_from_flags(args, window, fs, &oo, &cause);
if (args->argc == 0) {
scope = options_scope_from_flags(args, window, fs, &oo, &cause);
if (scope == OPTIONS_TABLE_NONE) {
if (args_has(args, 'q'))
return (CMD_RETURN_NORMAL);
cmdq_error(item, "%s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
return (cmd_show_options_all(self, item, scope, oo));
}
argument = format_single(item, args->argv[0], c, s, wl, NULL);
name = options_match(argument, &idx, &ambiguous);
if (name == NULL) {
if (args_has(args, 'q'))
goto fail;
if (ambiguous)
cmdq_error(item, "ambiguous option: %s", argument);
else
cmdq_error(item, "invalid option: %s", argument);
goto fail;
}
scope = options_scope_from_name(args, window, name, fs, &oo, &cause);
if (scope == OPTIONS_TABLE_NONE) {
if (args_has(args, 'q'))
goto fail;
cmdq_error(item, "%s", cause);
free(cause);
return (CMD_RETURN_ERROR);
goto fail;
}
o = options_get_only(oo, name);
if (args_has(args, 'A') && o == NULL) {
o = options_get(oo, name);
parent = 1;
} else
parent = 0;
if (o != NULL)
cmd_show_options_print(self, item, o, idx, parent);
if (args->argc == 0)
return (cmd_show_options_all(self, item, oo));
else
return (cmd_show_options_one(self, item, oo));
free(name);
free(argument);
return (CMD_RETURN_NORMAL);
fail:
free(name);
free(argument);
return (CMD_RETURN_ERROR);
}
static void
cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
struct options_entry *o, int idx)
struct options_entry *o, int idx, int parent)
{
struct options_array_item *a;
const char *name, *value;
char *tmp, *escaped;
const char *name = options_name(o);
char *value, *tmp = NULL, *escaped;
if (idx != -1) {
xasprintf(&tmp, "%s[%d]", options_name(o), idx);
xasprintf(&tmp, "%s[%d]", name, idx);
name = tmp;
} else {
if (options_isarray(o)) {
a = options_array_first(o);
if (a == NULL) {
if (!args_has(self->args, 'v'))
cmdq_print(item, "%s", name);
return;
}
while (a != NULL) {
idx = options_array_item_index(a);
cmd_show_options_print(self, item, o, idx);
cmd_show_options_print(self, item, o, idx,
parent);
a = options_array_next(a);
}
return;
}
tmp = NULL;
name = options_name(o);
}
value = options_tostring(o, idx, 0);
if (args_has(self->args, 'v'))
cmdq_print(item, "%s", value);
else if (options_isstring(o)) {
utf8_stravis(&escaped, value, VIS_OCTAL|VIS_TAB|VIS_NL|VIS_DQ);
cmdq_print(item, "%s \"%s\"", name, escaped);
escaped = args_escape(value);
if (parent)
cmdq_print(item, "%s* %s", name, escaped);
else
cmdq_print(item, "%s %s", name, escaped);
free(escaped);
} else
cmdq_print(item, "%s %s", name, value);
} else {
if (parent)
cmdq_print(item, "%s* %s", name, value);
else
cmdq_print(item, "%s %s", name, value);
}
free(value);
free(tmp);
}
static enum cmd_retval
cmd_show_options_one(struct cmd *self, struct cmdq_item *item,
cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope,
struct options *oo)
{
struct args *args = self->args;
struct client *c = cmd_find_client(item, NULL, 1);
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct options_entry *o;
int idx, ambiguous;
char *name;
name = format_single(item, args->argv[0], c, s, wl, NULL);
o = options_match_get(oo, name, &idx, 1, &ambiguous);
if (o == NULL) {
if (args_has(args, 'q')) {
free(name);
return (CMD_RETURN_NORMAL);
}
if (ambiguous) {
cmdq_error(item, "ambiguous option: %s", name);
free(name);
return (CMD_RETURN_ERROR);
}
if (*name != '@' &&
options_match_get(oo, name, &idx, 0, &ambiguous) != NULL) {
free(name);
return (CMD_RETURN_NORMAL);
}
cmdq_error(item, "unknown option: %s", name);
free(name);
return (CMD_RETURN_ERROR);
}
cmd_show_options_print(self, item, o, idx);
free(name);
return (CMD_RETURN_NORMAL);
}
static enum cmd_retval
cmd_show_options_all(struct cmd *self, struct cmdq_item *item,
struct options *oo)
{
struct options_entry *o;
const struct options_table_entry *oe;
struct options_entry *o;
struct options_array_item *a;
const char *name;
u_int idx;
int parent;
o = options_first(oo);
while (o != NULL) {
oe = options_table_entry(o);
if (options_table_entry(o) == NULL)
cmd_show_options_print(self, item, o, -1, 0);
o = options_next(o);
}
for (oe = options_table; oe->name != NULL; oe++) {
if (~oe->scope & scope)
continue;
if ((self->entry != &cmd_show_hooks_entry &&
!args_has(self->args, 'H') &&
oe != NULL &&
(oe->flags & OPTIONS_TABLE_IS_HOOK)) ||
(self->entry == &cmd_show_hooks_entry &&
(oe == NULL ||
(~oe->flags & OPTIONS_TABLE_IS_HOOK))))
continue;
o = options_get_only(oo, oe->name);
if (o == NULL) {
if (!args_has(self->args, 'A'))
continue;
o = options_get(oo, oe->name);
if (o == NULL)
continue;
parent = 1;
} else
parent = 0;
if (!options_isarray(o))
cmd_show_options_print(self, item, o, -1);
else {
a = options_array_first(o);
cmd_show_options_print(self, item, o, -1, parent);
else if ((a = options_array_first(o)) == NULL) {
if (!args_has(self->args, 'v')) {
name = options_name(o);
if (parent)
cmdq_print(item, "%s*", name);
else
cmdq_print(item, "%s", name);
}
} else {
while (a != NULL) {
idx = options_array_item_index(a);
cmd_show_options_print(self, item, o, idx);
cmd_show_options_print(self, item, o, idx,
parent);
a = options_array_next(a);
}
}
o = options_next(o);
}
return (CMD_RETURN_NORMAL);
}

View File

@@ -31,68 +31,162 @@
static enum cmd_retval cmd_source_file_exec(struct cmd *, struct cmdq_item *);
static enum cmd_retval cmd_source_file_done(struct cmdq_item *, void *);
const struct cmd_entry cmd_source_file_entry = {
.name = "source-file",
.alias = "source",
.args = { "q", 1, 1 },
.usage = "[-q] path",
.args = { "nqv", 1, -1 },
.usage = "[-nqv] path ...",
.flags = 0,
.exec = cmd_source_file_exec
};
static enum cmd_retval
cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
int quiet = args_has(args, 'q');
struct client *c = item->client;
struct cmdq_item *new_item;
enum cmd_retval retval;
char *pattern, *tmp;
const char *path = args->argv[0];
glob_t g;
u_int i;
struct cmd_source_file_data {
struct cmdq_item *item;
int flags;
if (*path == '/')
pattern = xstrdup(path);
else {
utf8_stravis(&tmp, server_client_get_cwd(c, NULL), VIS_GLOB);
xasprintf(&pattern, "%s/%s", tmp, path);
free(tmp);
}
log_debug("%s: %s", __func__, pattern);
struct cmdq_item *after;
enum cmd_retval retval;
retval = CMD_RETURN_NORMAL;
if (glob(pattern, 0, NULL, &g) != 0) {
if (!quiet || errno != ENOENT) {
cmdq_error(item, "%s: %s", path, strerror(errno));
retval = CMD_RETURN_ERROR;
}
free(pattern);
return (retval);
}
free(pattern);
for (i = 0; i < (u_int)g.gl_pathc; i++) {
if (load_cfg(g.gl_pathv[i], c, item, quiet) < 0)
retval = CMD_RETURN_ERROR;
}
if (cfg_finished) {
new_item = cmdq_get_callback(cmd_source_file_done, NULL);
cmdq_insert_after(item, new_item);
}
globfree(&g);
return (retval);
}
u_int current;
char **files;
u_int nfiles;
};
static enum cmd_retval
cmd_source_file_done(struct cmdq_item *item, __unused void *data)
cmd_source_file_complete_cb(struct cmdq_item *item, __unused void *data)
{
cfg_print_causes(item);
return (CMD_RETURN_NORMAL);
}
static void
cmd_source_file_complete(struct client *c, struct cmd_source_file_data *cdata)
{
struct cmdq_item *new_item;
if (cfg_finished) {
if (cdata->retval == CMD_RETURN_ERROR && c->session == NULL)
c->retval = 1;
new_item = cmdq_get_callback(cmd_source_file_complete_cb, NULL);
cmdq_insert_after(cdata->after, new_item);
}
free(cdata->files);
free(cdata);
}
static void
cmd_source_file_done(struct client *c, const char *path, int error,
int closed, struct evbuffer *buffer, void *data)
{
struct cmd_source_file_data *cdata = data;
struct cmdq_item *item = cdata->item;
void *bdata = EVBUFFER_DATA(buffer);
size_t bsize = EVBUFFER_LENGTH(buffer);
u_int n;
struct cmdq_item *new_item;
if (!closed)
return;
if (error != 0)
cmdq_error(item, "%s: %s", path, strerror(error));
else if (bsize != 0) {
if (load_cfg_from_buffer(bdata, bsize, path, c, cdata->after,
cdata->flags, &new_item) < 0)
cdata->retval = CMD_RETURN_ERROR;
else if (new_item != NULL)
cdata->after = new_item;
}
n = ++cdata->current;
if (n < cdata->nfiles)
file_read(c, cdata->files[n], cmd_source_file_done, cdata);
else {
cmd_source_file_complete(c, cdata);
cmdq_continue(item);
}
}
static void
cmd_source_file_add(struct cmd_source_file_data *cdata, const char *path)
{
log_debug("%s: %s", __func__, path);
cdata->files = xreallocarray(cdata->files, cdata->nfiles + 1,
sizeof *cdata->files);
cdata->files[cdata->nfiles++] = xstrdup(path);
}
static enum cmd_retval
cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct cmd_source_file_data *cdata;
struct client *c = item->client;
enum cmd_retval retval = CMD_RETURN_NORMAL;
char *pattern, *cwd;
const char *path, *error;
glob_t g;
int i, result;
u_int j;
cdata = xcalloc(1, sizeof *cdata);
cdata->item = item;
if (args_has(args, 'q'))
cdata->flags |= CMD_PARSE_QUIET;
if (args_has(args, 'n'))
cdata->flags |= CMD_PARSE_PARSEONLY;
if (args_has(args, 'v'))
cdata->flags |= CMD_PARSE_VERBOSE;
utf8_stravis(&cwd, server_client_get_cwd(c, NULL), VIS_GLOB);
for (i = 0; i < args->argc; i++) {
path = args->argv[i];
if (strcmp(path, "-") == 0) {
cmd_source_file_add(cdata, "-");
continue;
}
if (*path == '/')
pattern = xstrdup(path);
else
xasprintf(&pattern, "%s/%s", cwd, path);
log_debug("%s: %s", __func__, pattern);
if ((result = glob(pattern, 0, NULL, &g)) != 0) {
if (result != GLOB_NOMATCH ||
(~cdata->flags & CMD_PARSE_QUIET)) {
if (result == GLOB_NOMATCH)
error = strerror(ENOENT);
else if (result == GLOB_NOSPACE)
error = strerror(ENOMEM);
else
error = strerror(EINVAL);
cmdq_error(item, "%s: %s", path, error);
retval = CMD_RETURN_ERROR;
}
free(pattern);
continue;
}
free(pattern);
for (j = 0; j < g.gl_pathc; j++)
cmd_source_file_add(cdata, g.gl_pathv[j]);
}
cdata->after = item;
cdata->retval = retval;
if (cdata->nfiles != 0) {
file_read(c, cdata->files[0], cmd_source_file_done, cdata);
retval = CMD_RETURN_WAIT;
} else
cmd_source_file_complete(c, cdata);
free(cwd);
return (retval);
}

View File

@@ -39,9 +39,9 @@ const struct cmd_entry cmd_split_window_entry = {
.name = "split-window",
.alias = "splitw",
.args = { "bc:dfF:l:hp:Pt:v", 0, -1 },
.usage = "[-bdfhvP] [-c start-directory] [-F format] "
"[-p percentage|-l size] " CMD_TARGET_PANE_USAGE " [command]",
.args = { "bc:de:fF:hIl:p:Pt:v", 0, -1 },
.usage = "[-bdefhIPv] [-c start-directory] [-e environment] "
"[-F format] [-l size] " CMD_TARGET_PANE_USAGE " [command]",
.target = { 't', CMD_FIND_PANE, 0 },
@@ -52,111 +52,122 @@ const struct cmd_entry cmd_split_window_entry = {
static enum cmd_retval
cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
{
struct cmd_find_state *current = &item->shared->current;
struct args *args = self->args;
struct cmd_find_state *current = &item->shared->current;
struct spawn_context sc;
struct client *c = cmd_find_client(item, NULL, 1);
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct window *w = wl->window;
struct window_pane *wp = item->target.wp, *new_wp = NULL;
struct environ *env;
const char *cmd, *path, *shell, *template, *tmp;
char **argv, *cause, *new_cause, *cp, *cwd;
u_int hlimit;
int argc, size, percentage, before;
struct window_pane *wp = item->target.wp, *new_wp;
enum layout_type type;
struct layout_cell *lc;
struct environ_entry *envent;
struct cmd_find_state fs;
struct cmd_find_state fs;
int size, percentage, flags, input;
const char *template, *add, *errstr, *p;
char *cause, *cp, *copy;
size_t plen;
struct args_value *value;
server_unzoom_window(w);
if (args->argc == 0) {
cmd = options_get_string(s->options, "default-command");
if (cmd != NULL && *cmd != '\0') {
argc = 1;
argv = (char **)&cmd;
} else {
argc = 0;
argv = NULL;
}
} else {
argc = args->argc;
argv = args->argv;
}
if ((tmp = args_get(args, 'c')) != NULL)
cwd = format_single(item, tmp, c, s, NULL, NULL);
else
cwd = xstrdup(server_client_get_cwd(item->client, s));
type = LAYOUT_TOPBOTTOM;
if (args_has(args, 'h'))
type = LAYOUT_LEFTRIGHT;
before = args_has(args, 'b');
size = -1;
if (args_has(args, 'l')) {
size = args_strtonum(args, 'l', 0, INT_MAX, &cause);
if (cause != NULL) {
xasprintf(&new_cause, "size %s", cause);
free(cause);
cause = new_cause;
goto error;
else
type = LAYOUT_TOPBOTTOM;
if ((p = args_get(args, 'l')) != NULL) {
plen = strlen(p);
if (p[plen - 1] == '%') {
copy = xstrdup(p);
copy[plen - 1] = '\0';
percentage = strtonum(copy, 0, INT_MAX, &errstr);
free(copy);
if (errstr != NULL) {
cmdq_error(item, "percentage %s", errstr);
return (CMD_RETURN_ERROR);
}
if (type == LAYOUT_TOPBOTTOM)
size = (wp->sy * percentage) / 100;
else
size = (wp->sx * percentage) / 100;
} else {
size = args_strtonum(args, 'l', 0, INT_MAX, &cause);
if (cause != NULL) {
cmdq_error(item, "lines %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
}
} else if (args_has(args, 'p')) {
percentage = args_strtonum(args, 'p', 0, INT_MAX, &cause);
if (cause != NULL) {
xasprintf(&new_cause, "percentage %s", cause);
cmdq_error(item, "create pane failed: -p %s", cause);
free(cause);
cause = new_cause;
goto error;
return (CMD_RETURN_ERROR);
}
if (type == LAYOUT_TOPBOTTOM)
size = (wp->sy * percentage) / 100;
else
size = (wp->sx * percentage) / 100;
}
hlimit = options_get_number(s->options, "history-limit");
shell = options_get_string(s->options, "default-shell");
if (*shell == '\0' || areshell(shell))
shell = _PATH_BSHELL;
lc = layout_split_pane(wp, type, size, before, args_has(args, 'f'));
if (lc == NULL) {
cause = xstrdup("pane too small");
goto error;
}
new_wp = window_add_pane(w, wp, before, args_has(args, 'f'), hlimit);
layout_make_leaf(lc, new_wp);
path = NULL;
if (item->client != NULL && item->client->session == NULL)
envent = environ_find(item->client->environ, "PATH");
else
envent = environ_find(s->environ, "PATH");
if (envent != NULL)
path = envent->value;
env = environ_for_session(s, 0);
if (window_pane_spawn(new_wp, argc, argv, path, shell, cwd, env,
s->tio, &cause) != 0) {
environ_free(env);
goto error;
}
environ_free(env);
layout_fix_panes(w);
server_redraw_window(w);
if (!args_has(args, 'd')) {
window_set_active_pane(w, new_wp);
session_select(s, wl->idx);
cmd_find_from_session(current, s, 0);
server_redraw_session(s);
} else
server_status_session(s);
size = -1;
server_unzoom_window(wp->window);
input = (args_has(args, 'I') && args->argc == 0);
flags = 0;
if (args_has(args, 'b'))
flags |= SPAWN_BEFORE;
if (args_has(args, 'f'))
flags |= SPAWN_FULLSIZE;
if (input || (args->argc == 1 && *args->argv[0] == '\0'))
flags |= SPAWN_EMPTY;
lc = layout_split_pane(wp, type, size, flags);
if (lc == NULL) {
cmdq_error(item, "no space for new pane");
return (CMD_RETURN_ERROR);
}
memset(&sc, 0, sizeof sc);
sc.item = item;
sc.s = s;
sc.wl = wl;
sc.wp0 = wp;
sc.lc = lc;
sc.name = NULL;
sc.argc = args->argc;
sc.argv = args->argv;
sc.environ = environ_create();
add = args_first_value(args, 'e', &value);
while (add != NULL) {
environ_put(sc.environ, add);
add = args_next_value(&value);
}
sc.idx = -1;
sc.cwd = args_get(args, 'c');
sc.flags = flags;
if (args_has(args, 'd'))
sc.flags |= SPAWN_DETACHED;
if ((new_wp = spawn_pane(&sc, &cause)) == NULL) {
cmdq_error(item, "create pane failed: %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
if (input && window_pane_start_input(new_wp, item, &cause) != 0) {
layout_close_pane(new_wp);
window_remove_pane(wp->window, new_wp);
cmdq_error(item, "%s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
if (!args_has(args, 'd'))
cmd_find_from_winlink_pane(current, wl, new_wp, 0);
server_redraw_window(wp->window);
server_status_session(s);
if (args_has(args, 'P')) {
if ((template = args_get(args, 'F')) == NULL)
@@ -165,22 +176,12 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
cmdq_print(item, "%s", cp);
free(cp);
}
notify_window("window-layout-changed", w);
cmd_find_from_winlink_pane(&fs, wl, new_wp, 0);
hooks_insert(s->hooks, item, &fs, "after-split-window");
cmdq_insert_hook(s, item, &fs, "after-split-window");
free(cwd);
environ_free(sc.environ);
if (input)
return (CMD_RETURN_WAIT);
return (CMD_RETURN_NORMAL);
error:
if (new_wp != NULL) {
layout_close_pane(new_wp);
window_remove_pane(w, new_wp);
}
cmdq_error(item, "create pane failed: %s", cause);
free(cause);
free(cwd);
return (CMD_RETURN_ERROR);
}

View File

@@ -1,390 +0,0 @@
/* $OpenBSD$ */
/*
* 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 <errno.h>
#include <pwd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "tmux.h"
/*
* Parse a command from a string.
*/
static int cmd_string_getc(const char *, size_t *);
static void cmd_string_ungetc(size_t *);
static void cmd_string_copy(char **, char *, size_t *);
static char *cmd_string_string(const char *, size_t *, char, int);
static char *cmd_string_variable(const char *, size_t *);
static char *cmd_string_expand_tilde(const char *, size_t *);
static int
cmd_string_getc(const char *s, size_t *p)
{
const u_char *ucs = s;
if (ucs[*p] == '\0')
return (EOF);
return (ucs[(*p)++]);
}
static void
cmd_string_ungetc(size_t *p)
{
(*p)--;
}
static int
cmd_string_unicode(wchar_t *wc, const char *s, size_t *p, char ch)
{
int size = (ch == 'u') ? 4 : 8;
u_int tmp;
if (size == 4 && sscanf(s + *p, "%4x", &tmp) != 1)
return (-1);
if (size == 8 && sscanf(s + *p, "%8x", &tmp) != 1)
return (-1);
*p += size;
*wc = (wchar_t)tmp;
return (0);
}
int
cmd_string_split(const char *s, int *rargc, char ***rargv)
{
size_t p = 0;
int ch, argc = 0, append = 0;
char **argv = NULL, *buf = NULL, *t;
const char *whitespace, *equals;
size_t len = 0;
for (;;) {
ch = cmd_string_getc(s, &p);
switch (ch) {
case '\'':
if ((t = cmd_string_string(s, &p, '\'', 0)) == NULL)
goto error;
cmd_string_copy(&buf, t, &len);
break;
case '"':
if ((t = cmd_string_string(s, &p, '"', 1)) == NULL)
goto error;
cmd_string_copy(&buf, t, &len);
break;
case '$':
if ((t = cmd_string_variable(s, &p)) == NULL)
goto error;
cmd_string_copy(&buf, t, &len);
break;
case '#':
/* Comment: discard rest of line. */
while ((ch = cmd_string_getc(s, &p)) != EOF)
;
/* FALLTHROUGH */
case EOF:
case ' ':
case '\t':
if (buf != NULL) {
buf = xrealloc(buf, len + 1);
buf[len] = '\0';
argv = xreallocarray(argv, argc + 1,
sizeof *argv);
argv[argc++] = buf;
buf = NULL;
len = 0;
}
if (ch != EOF)
break;
while (argc != 0) {
equals = strchr(argv[0], '=');
whitespace = argv[0] + strcspn(argv[0], " \t");
if (equals == NULL || equals > whitespace)
break;
environ_put(global_environ, argv[0]);
argc--;
memmove(argv, argv + 1, argc * (sizeof *argv));
}
goto done;
case '~':
if (buf != NULL) {
append = 1;
break;
}
t = cmd_string_expand_tilde(s, &p);
if (t == NULL)
goto error;
cmd_string_copy(&buf, t, &len);
break;
default:
append = 1;
break;
}
if (append) {
if (len >= SIZE_MAX - 2)
goto error;
buf = xrealloc(buf, len + 1);
buf[len++] = ch;
}
append = 0;
}
done:
*rargc = argc;
*rargv = argv;
free(buf);
return (0);
error:
if (argv != NULL)
cmd_free_argv(argc, argv);
free(buf);
return (-1);
}
struct cmd_list *
cmd_string_parse(const char *s, const char *file, u_int line, char **cause)
{
struct cmd_list *cmdlist = NULL;
int argc;
char **argv;
*cause = NULL;
if (cmd_string_split(s, &argc, &argv) != 0) {
xasprintf(cause, "invalid or unknown command: %s", s);
return (NULL);
}
if (argc != 0) {
cmdlist = cmd_list_parse(argc, argv, file, line, cause);
if (cmdlist == NULL) {
cmd_free_argv(argc, argv);
return (NULL);
}
}
cmd_free_argv(argc, argv);
return (cmdlist);
}
static void
cmd_string_copy(char **dst, char *src, size_t *len)
{
size_t srclen;
srclen = strlen(src);
*dst = xrealloc(*dst, *len + srclen + 1);
strlcpy(*dst + *len, src, srclen + 1);
*len += srclen;
free(src);
}
static char *
cmd_string_string(const char *s, size_t *p, char endch, int esc)
{
int ch;
wchar_t wc;
struct utf8_data ud;
char *buf = NULL, *t;
size_t len = 0;
while ((ch = cmd_string_getc(s, p)) != endch) {
switch (ch) {
case EOF:
goto error;
case '\\':
if (!esc)
break;
switch (ch = cmd_string_getc(s, p)) {
case EOF:
goto error;
case 'e':
ch = '\033';
break;
case 'r':
ch = '\r';
break;
case 'n':
ch = '\n';
break;
case 't':
ch = '\t';
break;
case 'u':
case 'U':
if (cmd_string_unicode(&wc, s, p, ch) != 0)
goto error;
if (utf8_split(wc, &ud) != UTF8_DONE)
goto error;
if (len >= SIZE_MAX - ud.size - 1)
goto error;
buf = xrealloc(buf, len + ud.size);
memcpy(buf + len, ud.data, ud.size);
len += ud.size;
continue;
}
break;
case '$':
if (!esc)
break;
if ((t = cmd_string_variable(s, p)) == NULL)
goto error;
cmd_string_copy(&buf, t, &len);
continue;
}
if (len >= SIZE_MAX - 2)
goto error;
buf = xrealloc(buf, len + 1);
buf[len++] = ch;
}
buf = xrealloc(buf, len + 1);
buf[len] = '\0';
return (buf);
error:
free(buf);
return (NULL);
}
static char *
cmd_string_variable(const char *s, size_t *p)
{
int ch, fch;
char *buf, *t;
size_t len;
struct environ_entry *envent;
#define cmd_string_first(ch) ((ch) == '_' || \
((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z'))
#define cmd_string_other(ch) ((ch) == '_' || \
((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z') || \
((ch) >= '0' && (ch) <= '9'))
buf = NULL;
len = 0;
fch = EOF;
switch (ch = cmd_string_getc(s, p)) {
case EOF:
goto error;
case '{':
fch = '{';
ch = cmd_string_getc(s, p);
if (!cmd_string_first(ch))
goto error;
/* FALLTHROUGH */
default:
if (!cmd_string_first(ch)) {
xasprintf(&t, "$%c", ch);
return (t);
}
buf = xrealloc(buf, len + 1);
buf[len++] = ch;
for (;;) {
ch = cmd_string_getc(s, p);
if (ch == EOF || !cmd_string_other(ch))
break;
else {
if (len >= SIZE_MAX - 3)
goto error;
buf = xrealloc(buf, len + 1);
buf[len++] = ch;
}
}
}
if (fch == '{' && ch != '}')
goto error;
if (ch != EOF && fch != '{')
cmd_string_ungetc(p); /* ch */
buf = xrealloc(buf, len + 1);
buf[len] = '\0';
envent = environ_find(global_environ, buf);
free(buf);
if (envent == NULL)
return (xstrdup(""));
return (xstrdup(envent->value));
error:
free(buf);
return (NULL);
}
static char *
cmd_string_expand_tilde(const char *s, size_t *p)
{
struct passwd *pw;
struct environ_entry *envent;
char *home, *path, *user, *cp;
int last;
home = NULL;
last = cmd_string_getc(s, p);
if (last == EOF || last == '/' || last == ' '|| last == '\t') {
envent = environ_find(global_environ, "HOME");
if (envent != NULL && *envent->value != '\0')
home = envent->value;
else if ((pw = getpwuid(getuid())) != NULL)
home = pw->pw_dir;
} else {
cmd_string_ungetc(p);
cp = user = xmalloc(strlen(s));
for (;;) {
last = cmd_string_getc(s, p);
if (last == EOF ||
last == '/' ||
last == ' '||
last == '\t')
break;
*cp++ = last;
}
*cp = '\0';
if ((pw = getpwnam(user)) != NULL)
home = pw->pw_dir;
free(user);
}
if (home == NULL)
return (NULL);
if (last != EOF)
xasprintf(&path, "%s%c", home, last);
else
xasprintf(&path, "%s", home);
return (path);
}

View File

@@ -32,8 +32,8 @@ const struct cmd_entry cmd_swap_pane_entry = {
.name = "swap-pane",
.alias = "swapp",
.args = { "dDs:t:U", 0, 0 },
.usage = "[-dDU] " CMD_SRCDST_PANE_USAGE,
.args = { "dDs:t:UZ", 0, 0 },
.usage = "[-dDUZ] " CMD_SRCDST_PANE_USAGE,
.source = { 's', CMD_FIND_PANE, CMD_FIND_DEFAULT_MARKED },
.target = { 't', CMD_FIND_PANE, 0 },
@@ -45,6 +45,7 @@ const struct cmd_entry cmd_swap_pane_entry = {
static enum cmd_retval
cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = self->args;
struct window *src_w, *dst_w;
struct window_pane *tmp_wp, *src_wp, *dst_wp;
struct layout_cell *src_lc, *dst_lc;
@@ -54,23 +55,27 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
dst_wp = item->target.wp;
src_w = item->source.wl->window;
src_wp = item->source.wp;
server_unzoom_window(dst_w);
if (args_has(self->args, 'D')) {
if (window_push_zoom(dst_w, args_has(args, 'Z')))
server_redraw_window(dst_w);
if (args_has(args, 'D')) {
src_w = dst_w;
src_wp = TAILQ_NEXT(dst_wp, entry);
if (src_wp == NULL)
src_wp = TAILQ_FIRST(&dst_w->panes);
} else if (args_has(self->args, 'U')) {
} else if (args_has(args, 'U')) {
src_w = dst_w;
src_wp = TAILQ_PREV(dst_wp, window_panes, entry);
if (src_wp == NULL)
src_wp = TAILQ_LAST(&dst_w->panes, window_panes);
}
server_unzoom_window(src_w);
if (src_w != dst_w && window_push_zoom(src_w, args_has(args, 'Z')))
server_redraw_window(src_w);
if (src_wp == dst_wp)
return (CMD_RETURN_NORMAL);
goto out;
tmp_wp = TAILQ_PREV(dst_wp, window_panes, entry);
TAILQ_REMOVE(&dst_w->panes, dst_wp, entry);
@@ -90,7 +95,11 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
src_wp->layout_cell = dst_lc;
src_wp->window = dst_w;
options_set_parent(src_wp->options, dst_w->options);
src_wp->flags |= PANE_STYLECHANGED;
dst_wp->window = src_w;
options_set_parent(dst_wp->options, src_w->options);
dst_wp->flags |= PANE_STYLECHANGED;
sx = src_wp->sx; sy = src_wp->sy;
xoff = src_wp->xoff; yoff = src_wp->yoff;
@@ -99,19 +108,19 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
dst_wp->xoff = xoff; dst_wp->yoff = yoff;
window_pane_resize(dst_wp, sx, sy);
if (!args_has(self->args, 'd')) {
if (!args_has(args, 'd')) {
if (src_w != dst_w) {
window_set_active_pane(src_w, dst_wp);
window_set_active_pane(dst_w, src_wp);
window_set_active_pane(src_w, dst_wp, 1);
window_set_active_pane(dst_w, src_wp, 1);
} else {
tmp_wp = dst_wp;
window_set_active_pane(src_w, tmp_wp);
window_set_active_pane(src_w, tmp_wp, 1);
}
} else {
if (src_w->active == src_wp)
window_set_active_pane(src_w, dst_wp);
window_set_active_pane(src_w, dst_wp, 1);
if (dst_w->active == dst_wp)
window_set_active_pane(dst_w, src_wp);
window_set_active_pane(dst_w, src_wp, 1);
}
if (src_w != dst_w) {
if (src_w->last == src_wp)
@@ -122,5 +131,10 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
server_redraw_window(src_w);
server_redraw_window(dst_w);
out:
if (window_pop_zoom(src_w))
server_redraw_window(src_w);
if (src_w != dst_w && window_pop_zoom(dst_w))
server_redraw_window(dst_w);
return (CMD_RETURN_NORMAL);
}

View File

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

View File

@@ -34,8 +34,8 @@ const struct cmd_entry cmd_switch_client_entry = {
.name = "switch-client",
.alias = "switchc",
.args = { "lc:Enpt:rT:", 0, 0 },
.usage = "[-Elnpr] [-c target-client] [-t target-session] "
.args = { "lc:Enpt:rT:Z", 0, 0 },
.usage = "[-ElnprZ] [-c target-client] [-t target-session] "
"[-T key-table]",
/* -t is special */
@@ -54,6 +54,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
struct client *c;
struct session *s;
struct winlink *wl;
struct window *w;
struct window_pane *wp;
const char *tablename;
struct key_table *table;
@@ -61,7 +62,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
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;
flags = 0;
} else {
@@ -72,6 +73,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR);
s = item->target.s;
wl = item->target.wl;
w = wl->window;
wp = item->target.wp;
if (args_has(args, 'r'))
@@ -112,12 +114,15 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
} else {
if (item->client == NULL)
return (CMD_RETURN_NORMAL);
if (wl != NULL && wp != NULL) {
if (window_push_zoom(w, args_has(self->args, 'Z')))
server_redraw_window(w);
window_redraw_active_switch(w, wp);
window_set_active_pane(w, wp, 1);
if (window_pop_zoom(w))
server_redraw_window(w);
}
if (wl != NULL) {
server_unzoom_window(wl->window);
if (wp != NULL) {
window_redraw_active_switch(wp->window, wp);
window_set_active_pane(wp->window, wp);
}
session_set_current(s, wl);
cmd_find_from_session(&item->shared->current, s, 0);
}
@@ -137,10 +142,11 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
session_update_activity(s, NULL);
gettimeofday(&s->last_attached_time, NULL);
recalculate_sizes();
server_check_unattached();
server_redraw_client(c);
s->curw->flags &= ~WINLINK_ALERTFLAGS;
s->curw->window->latest = c;
recalculate_sizes();
alerts_check_session(s);
return (CMD_RETURN_NORMAL);

View File

@@ -153,7 +153,7 @@ cmd_wait_for_signal(__unused struct cmdq_item *item, const char *name,
log_debug("signal wait channel %s, with waiters", wc->name);
TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) {
wi->item->flags &= ~CMDQ_WAITING;
cmdq_continue(wi->item);
TAILQ_REMOVE(&wc->waiters, wi, entry);
free(wi);
@@ -229,7 +229,7 @@ cmd_wait_for_unlock(struct cmdq_item *item, const char *name,
}
if ((wi = TAILQ_FIRST(&wc->lockers)) != NULL) {
wi->item->flags &= ~CMDQ_WAITING;
cmdq_continue(wi->item);
TAILQ_REMOVE(&wc->lockers, wi, entry);
free(wi);
} else {
@@ -248,13 +248,13 @@ cmd_wait_for_flush(void)
RB_FOREACH_SAFE(wc, wait_channels, &wait_channels, wc1) {
TAILQ_FOREACH_SAFE(wi, &wc->waiters, entry, wi1) {
wi->item->flags &= ~CMDQ_WAITING;
cmdq_continue(wi->item);
TAILQ_REMOVE(&wc->waiters, wi, entry);
free(wi);
}
wc->woken = 1;
TAILQ_FOREACH_SAFE(wi, &wc->lockers, entry, wi1) {
wi->item->flags &= ~CMDQ_WAITING;
cmdq_continue(wi->item);
TAILQ_REMOVE(&wc->lockers, wi, entry);
free(wi);
}

327
cmd.c
View File

@@ -41,6 +41,7 @@ extern const struct cmd_entry cmd_confirm_before_entry;
extern const struct cmd_entry cmd_copy_mode_entry;
extern const struct cmd_entry cmd_delete_buffer_entry;
extern const struct cmd_entry cmd_detach_client_entry;
extern const struct cmd_entry cmd_display_menu_entry;
extern const struct cmd_entry cmd_display_message_entry;
extern const struct cmd_entry cmd_display_panes_entry;
extern const struct cmd_entry cmd_down_pane_entry;
@@ -129,6 +130,7 @@ const struct cmd_entry *cmd_table[] = {
&cmd_copy_mode_entry,
&cmd_delete_buffer_entry,
&cmd_detach_client_entry,
&cmd_display_menu_entry,
&cmd_display_message_entry,
&cmd_display_panes_entry,
&cmd_find_window_entry,
@@ -202,13 +204,45 @@ const struct cmd_entry *cmd_table[] = {
NULL
};
void
cmd_log_argv(int argc, char **argv, const char *prefix)
static u_int cmd_list_next_group = 1;
void printflike(3, 4)
cmd_log_argv(int argc, char **argv, const char *fmt, ...)
{
int i;
char *prefix;
va_list ap;
int i;
va_start(ap, fmt);
xvasprintf(&prefix, fmt, ap);
va_end(ap);
for (i = 0; i < argc; i++)
log_debug("%s: argv[%d]=%s", prefix, i, argv[i]);
free(prefix);
}
void
cmd_prepend_argv(int *argc, char ***argv, char *arg)
{
char **new_argv;
int i;
new_argv = xreallocarray(NULL, (*argc) + 1, sizeof *new_argv);
new_argv[0] = xstrdup(arg);
for (i = 0; i < *argc; i++)
new_argv[1 + i] = (*argv)[i];
free(*argv);
*argv = new_argv;
(*argc)++;
}
void
cmd_append_argv(int *argc, char ***argv, char *arg)
{
*argv = xreallocarray(*argv, (*argc) + 1, sizeof **argv);
(*argv)[(*argc)++] = xstrdup(arg);
}
int
@@ -219,7 +253,7 @@ cmd_pack_argv(int argc, char **argv, char *buf, size_t len)
if (argc == 0)
return (0);
cmd_log_argv(argc, argv, __func__);
cmd_log_argv(argc, argv, "%s", __func__);
*buf = '\0';
for (i = 0; i < argc; i++) {
@@ -256,7 +290,7 @@ cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv)
buf += arglen;
len -= arglen;
}
cmd_log_argv(argc, *argv, __func__);
cmd_log_argv(argc, *argv, "%s", __func__);
return (0);
}
@@ -315,107 +349,103 @@ cmd_stringify_argv(int argc, char **argv)
return (buf);
}
static int
cmd_try_alias(int *argc, char ***argv)
char *
cmd_get_alias(const char *name)
{
struct options_entry *o;
struct options_array_item *a;
int old_argc = *argc, new_argc, i;
char **old_argv = *argv, **new_argv;
size_t wanted;
const char *s, *cp = NULL;
struct options_entry *o;
struct options_array_item *a;
union options_value *ov;
size_t wanted, n;
const char *equals;
o = options_get_only(global_options, "command-alias");
if (o == NULL)
return (-1);
wanted = strlen(old_argv[0]);
return (NULL);
wanted = strlen(name);
a = options_array_first(o);
while (a != NULL) {
s = options_array_item_value(a);
if (s != NULL) {
cp = strchr(s, '=');
if (cp != NULL &&
(size_t)(cp - s) == wanted &&
strncmp(old_argv[0], s, wanted) == 0)
break;
ov = options_array_item_value(a);
equals = strchr(ov->string, '=');
if (equals != NULL) {
n = equals - ov->string;
if (n == wanted && strncmp(name, ov->string, n) == 0)
return (xstrdup(equals + 1));
}
a = options_array_next(a);
}
if (a == NULL)
return (-1);
return (NULL);
}
if (cmd_string_split(cp + 1, &new_argc, &new_argv) != 0)
return (-1);
static const struct cmd_entry *
cmd_find(const char *name, char **cause)
{
const struct cmd_entry **loop, *entry, *found = NULL;
int ambiguous;
char s[8192];
*argc = new_argc + old_argc - 1;
*argv = xcalloc((*argc) + 1, sizeof **argv);
ambiguous = 0;
for (loop = cmd_table; *loop != NULL; loop++) {
entry = *loop;
if (entry->alias != NULL && strcmp(entry->alias, name) == 0) {
ambiguous = 0;
found = entry;
break;
}
for (i = 0; i < new_argc; i++)
(*argv)[i] = xstrdup(new_argv[i]);
for (i = 1; i < old_argc; i++)
(*argv)[new_argc + i - 1] = xstrdup(old_argv[i]);
if (strncmp(entry->name, name, strlen(name)) != 0)
continue;
if (found != NULL)
ambiguous = 1;
found = entry;
log_debug("alias: %s=%s", old_argv[0], cp + 1);
for (i = 0; i < *argc; i++)
log_debug("alias: argv[%d] = %s", i, (*argv)[i]);
if (strcmp(entry->name, name) == 0)
break;
}
if (ambiguous)
goto ambiguous;
if (found == NULL) {
xasprintf(cause, "unknown command: %s", name);
return (NULL);
}
return (found);
cmd_free_argv(new_argc, new_argv);
return (0);
ambiguous:
*s = '\0';
for (loop = cmd_table; *loop != NULL; loop++) {
entry = *loop;
if (strncmp(entry->name, name, strlen(name)) != 0)
continue;
if (strlcat(s, entry->name, sizeof s) >= sizeof s)
break;
if (strlcat(s, ", ", sizeof s) >= sizeof s)
break;
}
s[strlen(s) - 2] = '\0';
xasprintf(cause, "ambiguous command: %s, could be: %s", name, s);
return (NULL);
}
struct cmd *
cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause)
{
const struct cmd_entry *entry;
const char *name;
const struct cmd_entry **entryp, *entry;
struct cmd *cmd;
struct args *args;
char s[BUFSIZ];
int ambiguous, allocated = 0;
*cause = NULL;
if (argc == 0) {
xasprintf(cause, "no command");
return (NULL);
}
name = argv[0];
retry:
ambiguous = 0;
entry = NULL;
for (entryp = cmd_table; *entryp != NULL; entryp++) {
if ((*entryp)->alias != NULL &&
strcmp((*entryp)->alias, argv[0]) == 0) {
ambiguous = 0;
entry = *entryp;
break;
}
if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0)
continue;
if (entry != NULL)
ambiguous = 1;
entry = *entryp;
/* Bail now if an exact match. */
if (strcmp(entry->name, argv[0]) == 0)
break;
}
if ((ambiguous || entry == NULL) &&
server_proc != NULL &&
!allocated &&
cmd_try_alias(&argc, &argv) == 0) {
allocated = 1;
goto retry;
}
if (ambiguous)
goto ambiguous;
if (entry == NULL) {
xasprintf(cause, "unknown command: %s", name);
entry = cmd_find(name, cause);
if (entry == NULL)
return (NULL);
}
cmd_log_argv(argc, argv, entry->name);
cmd_log_argv(argc, argv, "%s: %s", __func__, entry->name);
args = args_parse(entry->args.template, argc, argv);
if (args == NULL)
@@ -433,23 +463,11 @@ retry:
cmd->file = xstrdup(file);
cmd->line = line;
if (allocated)
cmd_free_argv(argc, argv);
return (cmd);
cmd->alias = NULL;
cmd->argc = argc;
cmd->argv = cmd_copy_argv(argc, argv);
ambiguous:
*s = '\0';
for (entryp = cmd_table; *entryp != NULL; entryp++) {
if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0)
continue;
if (strlcat(s, (*entryp)->name, sizeof s) >= sizeof s)
break;
if (strlcat(s, ", ", sizeof s) >= sizeof s)
break;
}
s[strlen(s) - 2] = '\0';
xasprintf(cause, "ambiguous command: %s, could be: %s", name, s);
return (NULL);
return (cmd);
usage:
if (args != NULL)
@@ -458,6 +476,18 @@ usage:
return (NULL);
}
void
cmd_free(struct cmd *cmd)
{
free(cmd->alias);
cmd_free_argv(cmd->argc, cmd->argv);
free(cmd->file);
args_free(cmd->args);
free(cmd);
}
char *
cmd_print(struct cmd *cmd)
{
@@ -473,6 +503,83 @@ cmd_print(struct cmd *cmd)
return (out);
}
struct cmd_list *
cmd_list_new(void)
{
struct cmd_list *cmdlist;
cmdlist = xcalloc(1, sizeof *cmdlist);
cmdlist->references = 1;
cmdlist->group = cmd_list_next_group++;
TAILQ_INIT(&cmdlist->list);
return (cmdlist);
}
void
cmd_list_append(struct cmd_list *cmdlist, struct cmd *cmd)
{
cmd->group = cmdlist->group;
TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry);
}
void
cmd_list_move(struct cmd_list *cmdlist, struct cmd_list *from)
{
struct cmd *cmd, *cmd1;
TAILQ_FOREACH_SAFE(cmd, &from->list, qentry, cmd1) {
TAILQ_REMOVE(&from->list, cmd, qentry);
TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry);
}
cmdlist->group = cmd_list_next_group++;
}
void
cmd_list_free(struct cmd_list *cmdlist)
{
struct cmd *cmd, *cmd1;
if (--cmdlist->references != 0)
return;
TAILQ_FOREACH_SAFE(cmd, &cmdlist->list, qentry, cmd1) {
TAILQ_REMOVE(&cmdlist->list, cmd, qentry);
cmd_free(cmd);
}
free(cmdlist);
}
char *
cmd_list_print(struct cmd_list *cmdlist, int escaped)
{
struct cmd *cmd;
char *buf, *this;
size_t len;
len = 1;
buf = xcalloc(1, len);
TAILQ_FOREACH(cmd, &cmdlist->list, qentry) {
this = cmd_print(cmd);
len += strlen(this) + 4;
buf = xrealloc(buf, len);
strlcat(buf, this, len);
if (TAILQ_NEXT(cmd, qentry) != NULL) {
if (escaped)
strlcat(buf, " \\; ", len);
else
strlcat(buf, " ; ", len);
}
free(this);
}
return (buf);
}
/* Adjust current mouse position for a pane. */
int
cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp,
@@ -481,17 +588,16 @@ cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp,
u_int x, y;
if (last) {
x = m->lx;
y = m->ly;
x = m->lx + m->ox;
y = m->ly + m->oy;
} else {
x = m->x;
y = m->y;
x = m->x + m->ox;
y = m->y + m->oy;
}
log_debug("%s: x=%u, y=%u%s", __func__, x, y, last ? " (last)" : "");
if (m->statusat == 0 && y > 0)
y--;
else if (m->statusat > 0 && y >= (u_int)m->statusat)
y = m->statusat - 1;
if (m->statusat == 0 && y >= m->statuslines)
y -= m->statuslines;
if (x < wp->xoff || x >= wp->xoff + wp->sx)
return (-1);
@@ -511,17 +617,22 @@ cmd_mouse_window(struct mouse_event *m, struct session **sp)
{
struct session *s;
struct window *w;
struct winlink *wl;
if (!m->valid || m->s == -1 || m->w == -1)
if (!m->valid)
return (NULL);
if ((s = session_find_by_id(m->s)) == NULL)
if (m->s == -1 || (s = session_find_by_id(m->s)) == NULL)
return (NULL);
if ((w = window_find_by_id(m->w)) == NULL)
return (NULL);
if (m->w == -1)
wl = s->curw;
else {
if ((w = window_find_by_id(m->w)) == NULL)
return (NULL);
wl = winlink_find_by_window(&s->windows, w);
}
if (sp != NULL)
*sp = s;
return (winlink_find_by_window(&s->windows, w));
return (wl);
}
/* Get current mouse pane if any. */
@@ -549,7 +660,7 @@ char *
cmd_template_replace(const char *template, const char *s, int idx)
{
char ch, *buf;
const char *ptr, *cp, quote[] = "\"\\$";
const char *ptr, *cp, quote[] = "\"\\$;~";
int replaced, quoted;
size_t len;
@@ -580,10 +691,6 @@ cmd_template_replace(const char *template, const char *s, int idx)
for (cp = s; *cp != '\0'; cp++) {
if (quoted && strchr(quote, *cp) != NULL)
buf[len++] = '\\';
if (quoted && *cp == ';') {
buf[len++] = '\\';
buf[len++] = '\\';
}
buf[len++] = *cp;
}
buf[len] = '\0';

View File

@@ -230,11 +230,85 @@ colour_fromstring(const char *s)
return (-1);
}
/* Convert 256 colour palette to 16. */
u_char
colour_256to16(u_char c)
/* Convert 256 colour to RGB colour. */
int
colour_256toRGB(int c)
{
static const u_char table[256] = {
static const int table[256] = {
0x000000, 0x800000, 0x008000, 0x808000,
0x000080, 0x800080, 0x008080, 0xc0c0c0,
0x808080, 0xff0000, 0x00ff00, 0xffff00,
0x0000ff, 0xff00ff, 0x00ffff, 0xffffff,
0x000000, 0x00005f, 0x000087, 0x0000af,
0x0000d7, 0x0000ff, 0x005f00, 0x005f5f,
0x005f87, 0x005faf, 0x005fd7, 0x005fff,
0x008700, 0x00875f, 0x008787, 0x0087af,
0x0087d7, 0x0087ff, 0x00af00, 0x00af5f,
0x00af87, 0x00afaf, 0x00afd7, 0x00afff,
0x00d700, 0x00d75f, 0x00d787, 0x00d7af,
0x00d7d7, 0x00d7ff, 0x00ff00, 0x00ff5f,
0x00ff87, 0x00ffaf, 0x00ffd7, 0x00ffff,
0x5f0000, 0x5f005f, 0x5f0087, 0x5f00af,
0x5f00d7, 0x5f00ff, 0x5f5f00, 0x5f5f5f,
0x5f5f87, 0x5f5faf, 0x5f5fd7, 0x5f5fff,
0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af,
0x5f87d7, 0x5f87ff, 0x5faf00, 0x5faf5f,
0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff,
0x5fd700, 0x5fd75f, 0x5fd787, 0x5fd7af,
0x5fd7d7, 0x5fd7ff, 0x5fff00, 0x5fff5f,
0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff,
0x870000, 0x87005f, 0x870087, 0x8700af,
0x8700d7, 0x8700ff, 0x875f00, 0x875f5f,
0x875f87, 0x875faf, 0x875fd7, 0x875fff,
0x878700, 0x87875f, 0x878787, 0x8787af,
0x8787d7, 0x8787ff, 0x87af00, 0x87af5f,
0x87af87, 0x87afaf, 0x87afd7, 0x87afff,
0x87d700, 0x87d75f, 0x87d787, 0x87d7af,
0x87d7d7, 0x87d7ff, 0x87ff00, 0x87ff5f,
0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff,
0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af,
0xaf00d7, 0xaf00ff, 0xaf5f00, 0xaf5f5f,
0xaf5f87, 0xaf5faf, 0xaf5fd7, 0xaf5fff,
0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af,
0xaf87d7, 0xaf87ff, 0xafaf00, 0xafaf5f,
0xafaf87, 0xafafaf, 0xafafd7, 0xafafff,
0xafd700, 0xafd75f, 0xafd787, 0xafd7af,
0xafd7d7, 0xafd7ff, 0xafff00, 0xafff5f,
0xafff87, 0xafffaf, 0xafffd7, 0xafffff,
0xd70000, 0xd7005f, 0xd70087, 0xd700af,
0xd700d7, 0xd700ff, 0xd75f00, 0xd75f5f,
0xd75f87, 0xd75faf, 0xd75fd7, 0xd75fff,
0xd78700, 0xd7875f, 0xd78787, 0xd787af,
0xd787d7, 0xd787ff, 0xd7af00, 0xd7af5f,
0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff,
0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af,
0xd7d7d7, 0xd7d7ff, 0xd7ff00, 0xd7ff5f,
0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff,
0xff0000, 0xff005f, 0xff0087, 0xff00af,
0xff00d7, 0xff00ff, 0xff5f00, 0xff5f5f,
0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff,
0xff8700, 0xff875f, 0xff8787, 0xff87af,
0xff87d7, 0xff87ff, 0xffaf00, 0xffaf5f,
0xffaf87, 0xffafaf, 0xffafd7, 0xffafff,
0xffd700, 0xffd75f, 0xffd787, 0xffd7af,
0xffd7d7, 0xffd7ff, 0xffff00, 0xffff5f,
0xffff87, 0xffffaf, 0xffffd7, 0xffffff,
0x080808, 0x121212, 0x1c1c1c, 0x262626,
0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e,
0x585858, 0x626262, 0x6c6c6c, 0x767676,
0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e,
0xa8a8a8, 0xb2b2b2, 0xbcbcbc, 0xc6c6c6,
0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee
};
return (table[c & 0xff] | COLOUR_FLAG_RGB);
}
/* Convert 256 colour to 16 colour. */
int
colour_256to16(int c)
{
static const char table[256] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 4, 4, 4, 12, 12, 2, 6, 4, 4, 12, 12, 2, 2, 6, 4,
12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10,
@@ -253,5 +327,5 @@ colour_256to16(u_char c)
8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15
};
return (table[c]);
return (table[c & 0xff]);
}

View File

@@ -21,6 +21,7 @@
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <fnmatch.h>
#include <limits.h>
#include <stdio.h>
#include <termios.h>
@@ -61,14 +62,34 @@ void warn(const char *, ...);
void warnx(const char *, ...);
#endif
#ifndef HAVE_PATHS_H
#define _PATH_BSHELL "/bin/sh"
#define _PATH_TMP "/tmp/"
#ifdef HAVE_PATHS_H
#include <paths.h>
#endif
#ifndef _PATH_BSHELL
#define _PATH_BSHELL "/bin/sh"
#endif
#ifndef _PATH_TMP
#define _PATH_TMP "/tmp/"
#endif
#ifndef _PATH_DEVNULL
#define _PATH_DEVNULL "/dev/null"
#endif
#ifndef _PATH_TTY
#define _PATH_TTY "/dev/tty"
#endif
#ifndef _PATH_DEV
#define _PATH_DEV "/dev/"
#endif
#ifndef _PATH_DEFPATH
#define _PATH_DEFPATH "/usr/bin:/bin"
#endif
#ifndef __OpenBSD__
#define pledge(s, p) (0)
#endif
@@ -97,10 +118,6 @@ void warnx(const char *, ...);
#include "compat/bitstring.h"
#endif
#ifdef HAVE_PATHS_H
#include <paths.h>
#endif
#ifdef HAVE_LIBUTIL_H
#include <libutil.h>
#endif
@@ -153,6 +170,14 @@ void warnx(const char *, ...);
#define O_DIRECTORY 0
#endif
#ifndef FNM_CASEFOLD
#ifdef FNM_IGNORECASE
#define FNM_CASEFOLD FNM_IGNORECASE
#else
#define FNM_CASEFOLD 0
#endif
#endif
#ifndef INFTIM
#define INFTIM -1
#endif
@@ -312,10 +337,6 @@ int vasprintf(char **, const char *, va_list);
char *fgetln(FILE *, size_t *);
#endif
#ifndef HAVE_FPARSELN
char *fparseln(FILE *, size_t *, size_t *, const char *, int);
#endif
#ifndef HAVE_SETENV
/* setenv.c */
int setenv(const char *, const char *, int);
@@ -349,7 +370,6 @@ int utf8proc_mbtowc(wchar_t *, const char *, size_t);
int utf8proc_wctomb(char *, wchar_t);
#endif
#ifndef HAVE_GETOPT
/* getopt.c */
extern int BSDopterr;
extern int BSDoptind;
@@ -363,6 +383,5 @@ int BSDgetopt(int, char *const *, const char *);
#define optopt BSDoptopt
#define optreset BSDoptreset
#define optarg BSDoptarg
#endif
#endif /* COMPAT_H */

View File

@@ -19,8 +19,10 @@
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "compat.h"
#include "xmalloc.h"
int
asprintf(char **ret, const char *fmt, ...)

View File

@@ -1,221 +0,0 @@
/* $OpenBSD: fparseln.c,v 1.6 2005/08/02 21:46:23 espie Exp $ */
/* $NetBSD: fparseln.c,v 1.7 1999/07/02 15:49:12 simonb Exp $ */
/*
* Copyright (c) 1997 Christos Zoulas. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Christos Zoulas.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* OPENBSD ORIGINAL: lib/libutil/fparseln.c */
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "compat.h"
/*
* fparseln() specific operation flags.
*/
#define FPARSELN_UNESCESC 0x01
#define FPARSELN_UNESCCONT 0x02
#define FPARSELN_UNESCCOMM 0x04
#define FPARSELN_UNESCREST 0x08
#define FPARSELN_UNESCALL 0x0f
static int isescaped(const char *, const char *, int);
/* isescaped():
* Return true if the character in *p that belongs to a string
* that starts in *sp, is escaped by the escape character esc.
*/
static int
isescaped(const char *sp, const char *p, int esc)
{
const char *cp;
size_t ne;
/* No escape character */
if (esc == '\0')
return 1;
/* Count the number of escape characters that precede ours */
for (ne = 0, cp = p; --cp >= sp && *cp == esc; ne++)
continue;
/* Return true if odd number of escape characters */
return (ne & 1) != 0;
}
/* fparseln():
* Read a line from a file parsing continuations ending in \
* and eliminating trailing newlines, or comments starting with
* the comment char.
*/
char *
fparseln(FILE *fp, size_t *size, size_t *lineno, const char str[3],
int flags)
{
static const char dstr[3] = { '\\', '\\', '#' };
char *buf = NULL, *ptr, *cp, esc, con, nl, com;
size_t s, len = 0;
int cnt = 1;
if (str == NULL)
str = dstr;
esc = str[0];
con = str[1];
com = str[2];
/*
* XXX: it would be cool to be able to specify the newline character,
* but unfortunately, fgetln does not let us
*/
nl = '\n';
while (cnt) {
cnt = 0;
if (lineno)
(*lineno)++;
if ((ptr = fgetln(fp, &s)) == NULL)
break;
if (s && com) { /* Check and eliminate comments */
for (cp = ptr; cp < ptr + s; cp++)
if (*cp == com && !isescaped(ptr, cp, esc)) {
s = cp - ptr;
cnt = s == 0 && buf == NULL;
break;
}
}
if (s && nl) { /* Check and eliminate newlines */
cp = &ptr[s - 1];
if (*cp == nl)
s--; /* forget newline */
}
if (s && con) { /* Check and eliminate continuations */
cp = &ptr[s - 1];
if (*cp == con && !isescaped(ptr, cp, esc)) {
s--; /* forget escape */
cnt = 1;
}
}
if (s == 0 && buf != NULL)
continue;
if ((cp = realloc(buf, len + s + 1)) == NULL) {
free(buf);
return NULL;
}
buf = cp;
(void) memcpy(buf + len, ptr, s);
len += s;
buf[len] = '\0';
}
if ((flags & FPARSELN_UNESCALL) != 0 && esc && buf != NULL &&
strchr(buf, esc) != NULL) {
ptr = cp = buf;
while (cp[0] != '\0') {
int skipesc;
while (cp[0] != '\0' && cp[0] != esc)
*ptr++ = *cp++;
if (cp[0] == '\0' || cp[1] == '\0')
break;
skipesc = 0;
if (cp[1] == com)
skipesc += (flags & FPARSELN_UNESCCOMM);
if (cp[1] == con)
skipesc += (flags & FPARSELN_UNESCCONT);
if (cp[1] == esc)
skipesc += (flags & FPARSELN_UNESCESC);
if (cp[1] != com && cp[1] != con && cp[1] != esc)
skipesc = (flags & FPARSELN_UNESCREST);
if (skipesc)
cp++;
else
*ptr++ = *cp++;
*ptr++ = *cp++;
}
*ptr = '\0';
len = strlen(buf);
}
if (size)
*size = len;
return buf;
}
#ifdef TEST
int main(int, char **);
int
main(argc, argv)
int argc;
char **argv;
{
char *ptr;
size_t size, line;
line = 0;
while ((ptr = fparseln(stdin, &size, &line, NULL,
FPARSELN_UNESCALL)) != NULL)
printf("line %d (%d) |%s|\n", line, size, ptr);
return 0;
}
/*
# This is a test
line 1
line 2 \
line 3 # Comment
line 4 \# Not comment \\\\
# And a comment \
line 5 \\\
line 6
*/
#endif /* TEST */

View File

@@ -1,6 +1,6 @@
# configure.ac
AC_INIT([tmux], 2.9a)
AC_INIT([tmux], 3.1c)
AC_PREREQ([2.60])
AC_CONFIG_AUX_DIR(etc)
@@ -28,6 +28,7 @@ AC_PROG_CC_C99
AC_PROG_CPP
AC_PROG_EGREP
AC_PROG_INSTALL
AC_PROG_YACC
PKG_PROG_PKG_CONFIG
AC_USE_SYSTEM_EXTENSIONS
@@ -124,6 +125,12 @@ AC_FUNC_STRNLEN
# Look for clock_gettime. Must come before event_init.
AC_SEARCH_LIBS(clock_gettime, rt)
# Always use our getopt because 1) glibc's doesn't enforce argument order 2)
# musl does not set optarg to NULL for flags without arguments (although it is
# not required to, but it is helpful) 3) there are probably other weird
# implementations.
AC_LIBOBJ(getopt)
# Look for libevent.
PKG_CHECK_MODULES(
LIBEVENT,
@@ -423,48 +430,6 @@ else
AC_LIBOBJ(unvis)
fi
# Look for getopt. glibc's getopt does not enforce argument order and the ways
# of making it do so are stupid, so just use our own instead.
AC_CHECK_FUNC(getopt, found_getopt=yes, found_getopt=no)
if test "x$found_getopt" != xno; then
AC_MSG_CHECKING(if getopt is suitable)
AC_EGREP_CPP(
yes,
[
#include <features.h>
#ifdef __GLIBC__
yes
#endif
],
[
found_getopt=no
AC_MSG_RESULT(no)
],
AC_MSG_RESULT(yes))
fi
if test "x$found_getopt" != xno; then
AC_CHECK_DECLS(
[optarg, optind, optreset],
,
found_getopt=no,
[
#include <unistd.h>
])
fi
if test "x$found_getopt" != xno; then
AC_DEFINE(HAVE_GETOPT)
else
AC_LIBOBJ(getopt)
fi
# Look for fparseln in libutil.
AC_SEARCH_LIBS(fparseln, util, found_fparseln=yes, found_fparseln=no)
if test "x$found_fparseln" = xyes; then
AC_DEFINE(HAVE_FPARSELN)
else
AC_LIBOBJ(fparseln)
fi
# Look for fdforkpty and forkpty in libutil.
AC_SEARCH_LIBS(fdforkpty, util, found_fdforkpty=yes, found_fdforkpty=no)
if test "x$found_fdforkpty" = xyes; then

View File

@@ -28,18 +28,16 @@
void
control_notify_input(struct client *c, struct window_pane *wp,
struct evbuffer *input)
const u_char *buf, size_t len)
{
u_char *buf;
size_t len;
struct evbuffer *message;
u_int i;
if (c->session == NULL)
return;
buf = EVBUFFER_DATA(input);
len = EVBUFFER_LENGTH(input);
if (c->flags & CLIENT_CONTROL_NOOUTPUT)
return;
/*
* Only write input if the window pane is linked to a window belonging
@@ -56,7 +54,8 @@ control_notify_input(struct client *c, struct window_pane *wp,
else
evbuffer_add_printf(message, "%c", buf[i]);
}
control_write_buffer(c, message);
evbuffer_add(message, "", 1);
control_write(c, "%s", EVBUFFER_DATA(message));
evbuffer_free(message);
}
}

View File

@@ -30,23 +30,12 @@
void
control_write(struct client *c, const char *fmt, ...)
{
va_list ap;
va_list ap;
va_start(ap, fmt);
evbuffer_add_vprintf(c->stdout_data, fmt, ap);
file_vprint(c, fmt, ap);
file_print(c, "\n");
va_end(ap);
evbuffer_add(c->stdout_data, "\n", 1);
server_client_push_stdout(c);
}
/* Write a buffer, adding a terminal newline. Empties buffer. */
void
control_write_buffer(struct client *c, struct evbuffer *buffer)
{
evbuffer_add_buffer(c->stdout_data, buffer);
evbuffer_add(c->stdout_data, "\n", 1);
server_client_push_stdout(c);
}
/* Control error callback. */
@@ -65,38 +54,53 @@ control_error(struct cmdq_item *item, void *data)
}
/* Control input callback. Read lines and fire commands. */
void
control_callback(struct client *c, int closed, __unused void *data)
static void
control_callback(__unused struct client *c, __unused const char *path,
int error, int closed, struct evbuffer *buffer, __unused void *data)
{
char *line, *cause;
struct cmd_list *cmdlist;
struct cmd *cmd;
char *line;
struct cmdq_item *item;
struct cmd_parse_result *pr;
if (closed)
if (closed || error != 0)
c->flags |= CLIENT_EXIT;
for (;;) {
line = evbuffer_readln(c->stdin_data, NULL, EVBUFFER_EOL_LF);
line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_LF);
if (line == NULL)
break;
log_debug("%s: %s", __func__, line);
if (*line == '\0') { /* empty line exit */
free(line);
c->flags |= CLIENT_EXIT;
break;
}
cmdlist = cmd_string_parse(line, NULL, 0, &cause);
if (cmdlist == NULL) {
item = cmdq_get_callback(control_error, cause);
pr = cmd_parse_from_string(line, NULL);
switch (pr->status) {
case CMD_PARSE_EMPTY:
break;
case CMD_PARSE_ERROR:
item = cmdq_get_callback(control_error, pr->error);
cmdq_append(c, item);
} else {
TAILQ_FOREACH(cmd, &cmdlist->list, qentry)
cmd->flags |= CMD_CONTROL;
item = cmdq_get_command(cmdlist, NULL, NULL, 0);
break;
case CMD_PARSE_SUCCESS:
item = cmdq_get_command(pr->cmdlist, NULL, NULL, 0);
item->shared->flags |= CMDQ_SHARED_CONTROL;
cmdq_append(c, item);
cmd_list_free(cmdlist);
cmd_list_free(pr->cmdlist);
break;
}
free(line);
}
}
void
control_start(struct client *c)
{
file_read(c, "-", control_callback, c);
if (c->flags & CLIENT_CONTROLCONTROL)
file_print(c, "\033P1000p");
}

View File

@@ -177,20 +177,16 @@ environ_update(struct options *oo, struct environ *src, struct environ *dst)
struct environ_entry *envent;
struct options_entry *o;
struct options_array_item *a;
const char *value;
union options_value *ov;
o = options_get(oo, "update-environment");
if (o == NULL)
return;
a = options_array_first(o);
while (a != NULL) {
value = options_array_item_value(a);
if (value == NULL) {
a = options_array_next(a);
continue;
}
if ((envent = environ_find(src, value)) == NULL)
environ_clear(dst, value);
ov = options_array_item_value(a);
if ((envent = environ_find(src, ov->string)) == NULL)
environ_clear(dst, ov->string);
else
environ_set(dst, envent->name, "%s", envent->value);
a = options_array_next(a);

View File

@@ -6,7 +6,7 @@
# Some tweaks to the status line
set -g status-right "%H:%M"
set -g window-status-current-attr "underscore"
set -g window-status-current-style "underscore"
# If running inside tmux ($TMUX is set), then change the status line to red
%if #{TMUX}

413
file.c Normal file
View File

@@ -0,0 +1,413 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2019 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 <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "tmux.h"
static int file_next_stream = 3;
RB_GENERATE(client_files, client_file, entry, file_cmp);
static char *
file_get_path(struct client *c, const char *file)
{
char *path;
if (*file == '/')
path = xstrdup(file);
else
xasprintf(&path, "%s/%s", server_client_get_cwd(c, NULL), file);
return (path);
}
int
file_cmp(struct client_file *cf1, struct client_file *cf2)
{
if (cf1->stream < cf2->stream)
return (-1);
if (cf1->stream > cf2->stream)
return (1);
return (0);
}
struct client_file *
file_create(struct client *c, int stream, client_file_cb cb, void *cbdata)
{
struct client_file *cf;
cf = xcalloc(1, sizeof *cf);
cf->c = c;
cf->references = 1;
cf->stream = stream;
cf->buffer = evbuffer_new();
if (cf->buffer == NULL)
fatalx("out of memory");
cf->cb = cb;
cf->data = cbdata;
if (cf->c != NULL) {
RB_INSERT(client_files, &cf->c->files, cf);
cf->c->references++;
}
return (cf);
}
void
file_free(struct client_file *cf)
{
if (--cf->references != 0)
return;
evbuffer_free(cf->buffer);
free(cf->path);
if (cf->c != NULL) {
RB_REMOVE(client_files, &cf->c->files, cf);
server_client_unref(cf->c);
}
free(cf);
}
static void
file_fire_done_cb(__unused int fd, __unused short events, void *arg)
{
struct client_file *cf = arg;
struct client *c = cf->c;
if (cf->cb != NULL && (c == NULL || (~c->flags & CLIENT_DEAD)))
cf->cb(c, cf->path, cf->error, 1, cf->buffer, cf->data);
file_free(cf);
}
void
file_fire_done(struct client_file *cf)
{
event_once(-1, EV_TIMEOUT, file_fire_done_cb, cf, NULL);
}
void
file_fire_read(struct client_file *cf)
{
struct client *c = cf->c;
if (cf->cb != NULL)
cf->cb(c, cf->path, cf->error, 0, cf->buffer, cf->data);
}
int
file_can_print(struct client *c)
{
if (c == NULL)
return (0);
if (c->session != NULL && (~c->flags & CLIENT_CONTROL))
return (0);
return (1);
}
void
file_print(struct client *c, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
file_vprint(c, fmt, ap);
va_end(ap);
}
void
file_vprint(struct client *c, const char *fmt, va_list ap)
{
struct client_file find, *cf;
struct msg_write_open msg;
if (!file_can_print(c))
return;
find.stream = 1;
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
cf = file_create(c, 1, NULL, NULL);
cf->path = xstrdup("-");
evbuffer_add_vprintf(cf->buffer, fmt, ap);
msg.stream = 1;
msg.fd = STDOUT_FILENO;
msg.flags = 0;
proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
} else {
evbuffer_add_vprintf(cf->buffer, fmt, ap);
file_push(cf);
}
}
void
file_print_buffer(struct client *c, void *data, size_t size)
{
struct client_file find, *cf;
struct msg_write_open msg;
if (!file_can_print(c))
return;
find.stream = 1;
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
cf = file_create(c, 1, NULL, NULL);
cf->path = xstrdup("-");
evbuffer_add(cf->buffer, data, size);
msg.stream = 1;
msg.fd = STDOUT_FILENO;
msg.flags = 0;
proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
} else {
evbuffer_add(cf->buffer, data, size);
file_push(cf);
}
}
void
file_error(struct client *c, const char *fmt, ...)
{
struct client_file find, *cf;
struct msg_write_open msg;
va_list ap;
if (!file_can_print(c))
return;
va_start(ap, fmt);
find.stream = 2;
if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
cf = file_create(c, 2, NULL, NULL);
cf->path = xstrdup("-");
evbuffer_add_vprintf(cf->buffer, fmt, ap);
msg.stream = 2;
msg.fd = STDERR_FILENO;
msg.flags = 0;
proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
} else {
evbuffer_add_vprintf(cf->buffer, fmt, ap);
file_push(cf);
}
va_end(ap);
}
void
file_write(struct client *c, const char *path, int flags, const void *bdata,
size_t bsize, client_file_cb cb, void *cbdata)
{
struct client_file *cf;
FILE *f;
struct msg_write_open *msg;
size_t msglen;
int fd = -1;
const char *mode;
if (strcmp(path, "-") == 0) {
cf = file_create(c, file_next_stream++, cb, cbdata);
cf->path = xstrdup("-");
fd = STDOUT_FILENO;
if (c == NULL || c->flags & CLIENT_ATTACHED) {
cf->error = EBADF;
goto done;
}
goto skip;
}
cf = file_create(c, file_next_stream++, cb, cbdata);
cf->path = file_get_path(c, path);
if (c == NULL || c->flags & CLIENT_ATTACHED) {
if (flags & O_APPEND)
mode = "ab";
else
mode = "wb";
f = fopen(cf->path, mode);
if (f == NULL) {
cf->error = errno;
goto done;
}
if (fwrite(bdata, 1, bsize, f) != bsize) {
fclose(f);
cf->error = EIO;
goto done;
}
fclose(f);
goto done;
}
skip:
evbuffer_add(cf->buffer, bdata, bsize);
msglen = strlen(cf->path) + 1 + sizeof *msg;
if (msglen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
cf->error = E2BIG;
goto done;
}
msg = xmalloc(msglen);
msg->stream = cf->stream;
msg->fd = fd;
msg->flags = flags;
memcpy(msg + 1, cf->path, msglen - sizeof *msg);
if (proc_send(c->peer, MSG_WRITE_OPEN, -1, msg, msglen) != 0) {
free(msg);
cf->error = EINVAL;
goto done;
}
free(msg);
return;
done:
file_fire_done(cf);
}
void
file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
{
struct client_file *cf;
FILE *f;
struct msg_read_open *msg;
size_t msglen, size;
int fd = -1;
char buffer[BUFSIZ];
if (strcmp(path, "-") == 0) {
cf = file_create(c, file_next_stream++, cb, cbdata);
cf->path = xstrdup("-");
fd = STDIN_FILENO;
if (c == NULL || c->flags & CLIENT_ATTACHED) {
cf->error = EBADF;
goto done;
}
goto skip;
}
cf = file_create(c, file_next_stream++, cb, cbdata);
cf->path = file_get_path(c, path);
if (c == NULL || c->flags & CLIENT_ATTACHED) {
f = fopen(cf->path, "rb");
if (f == NULL) {
cf->error = errno;
goto done;
}
for (;;) {
size = fread(buffer, 1, sizeof buffer, f);
if (evbuffer_add(cf->buffer, buffer, size) != 0) {
cf->error = ENOMEM;
goto done;
}
if (size != sizeof buffer)
break;
}
if (ferror(f)) {
cf->error = EIO;
goto done;
}
fclose(f);
goto done;
}
skip:
msglen = strlen(cf->path) + 1 + sizeof *msg;
if (msglen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
cf->error = E2BIG;
goto done;
}
msg = xmalloc(msglen);
msg->stream = cf->stream;
msg->fd = fd;
memcpy(msg + 1, cf->path, msglen - sizeof *msg);
if (proc_send(c->peer, MSG_READ_OPEN, -1, msg, msglen) != 0) {
free(msg);
cf->error = EINVAL;
goto done;
}
free(msg);
return;
done:
file_fire_done(cf);
}
static void
file_push_cb(__unused int fd, __unused short events, void *arg)
{
struct client_file *cf = arg;
struct client *c = cf->c;
if (~c->flags & CLIENT_DEAD)
file_push(cf);
file_free(cf);
}
void
file_push(struct client_file *cf)
{
struct client *c = cf->c;
struct msg_write_data *msg;
size_t msglen, sent, left;
struct msg_write_close close;
msg = xmalloc(sizeof *msg);
left = EVBUFFER_LENGTH(cf->buffer);
while (left != 0) {
sent = left;
if (sent > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg)
sent = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg;
msglen = (sizeof *msg) + sent;
msg = xrealloc(msg, msglen);
msg->stream = cf->stream;
memcpy(msg + 1, EVBUFFER_DATA(cf->buffer), sent);
if (proc_send(c->peer, MSG_WRITE, -1, msg, msglen) != 0)
break;
evbuffer_drain(cf->buffer, sent);
left = EVBUFFER_LENGTH(cf->buffer);
log_debug("%s: file %d sent %zu, left %zu", c->name, cf->stream,
sent, left);
}
if (left != 0) {
cf->references++;
event_once(-1, EV_TIMEOUT, file_push_cb, cf, NULL);
} else if (cf->stream > 2) {
close.stream = cf->stream;
proc_send(c->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close);
file_fire_done(cf);
}
free(msg);
}

View File

@@ -142,7 +142,8 @@ format_draw_put_list(struct screen_write_ctx *octx,
width -= list_left->cx;
}
if (start + width < list->cx && width > list_right->cx) {
screen_write_cursormove(octx, ocx + offset + width - 1, ocy, 0);
screen_write_cursormove(octx, ocx + offset + width -
list_right->cx, ocy, 0);
screen_write_fast_copy(octx, list_right, 0, 0, list_right->cx,
1);
width -= list_right->cx;
@@ -345,12 +346,6 @@ format_draw_centre(struct screen_write_ctx *octx, u_int available, u_int ocx,
/* Write left at 0. */
format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
/* Write after at available - width_after. */
format_draw_put(octx, ocx, ocy, after, frs,
available - width_after,
after->cx - width_after,
width_after);
/* Write right at available - width_right. */
format_draw_put(octx, ocx, ocy, right, frs,
available - width_right,
@@ -374,10 +369,10 @@ format_draw_centre(struct screen_write_ctx *octx, u_int available, u_int ocx,
/*
* Write after at
* middle + width_list / 2 - width_centre.
* middle - width_list / 2 + width_list
*/
format_draw_put(octx, ocx, ocy, after, frs,
middle + width_list / 2,
middle - width_list / 2 + width_list,
0,
width_after);
@@ -517,9 +512,10 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
u_int ocx = os->cx, ocy = os->cy, i, width[TOTAL];
u_int map[] = { LEFT, LEFT, CENTRE, RIGHT };
int focus_start = -1, focus_end = -1;
int list_state = -1;
int list_state = -1, fill = -1;
enum style_align list_align = STYLE_ALIGN_DEFAULT;
struct style sy;
struct grid_cell gc, current_default;
struct style sy, saved_sy;
struct utf8_data *ud = &sy.gc.data;
const char *cp, *end;
enum utf8_state more;
@@ -528,7 +524,8 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
struct format_ranges frs;
struct style_range *sr;
style_set(&sy, base);
memcpy(&current_default, base, sizeof current_default);
style_set(&sy, &current_default);
TAILQ_INIT(&frs);
log_debug("%s: %s", __func__, expanded);
@@ -540,7 +537,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
for (i = 0; i < TOTAL; i++) {
screen_init(&s[i], size, 1, 0);
screen_write_start(&ctx[i], NULL, &s[i]);
screen_write_clearendofline(&ctx[i], base->bg);
screen_write_clearendofline(&ctx[i], current_default.bg);
width[i] = 0;
}
@@ -570,7 +567,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
cp++;
}
/* Draw the cell to th current screen. */
/* Draw the cell to the current screen. */
screen_write_cell(&ctx[current], &sy.gc);
width[current] += ud->width;
continue;
@@ -586,7 +583,8 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
goto out;
}
tmp = xstrndup(cp + 2, end - (cp + 2));
if (style_parse(&sy, base, tmp) != 0) {
style_copy(&saved_sy, &sy);
if (style_parse(&sy, &current_default, tmp) != 0) {
log_debug("%s: invalid style '%s'", __func__, tmp);
free(tmp);
cp = end + 1;
@@ -596,6 +594,19 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
style_tostring(&sy));
free(tmp);
/* If this style has a fill colour, store it for later. */
if (sy.fill != 8)
fill = sy.fill;
/* If this style pushed or popped the default, update it. */
if (sy.default_type == STYLE_DEFAULT_PUSH) {
memcpy(&current_default, &saved_sy.gc, sizeof current_default);
sy.default_type = STYLE_DEFAULT_BASE;
} else if (sy.default_type == STYLE_DEFAULT_POP) {
memcpy(&current_default, base, sizeof current_default);
sy.default_type = STYLE_DEFAULT_BASE;
}
/* Check the list state. */
switch (sy.list) {
case STYLE_LIST_ON:
@@ -717,6 +728,14 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
fr->argument, names[fr->index], fr->start, fr->end);
}
/* Clear the available area. */
if (fill != -1) {
memcpy(&gc, &grid_default_cell, sizeof gc);
gc.bg = fill;
for (i = 0; i < available; i++)
screen_write_putc(octx, &gc, ' ');
}
/*
* Draw the screens. How they are arranged depends on where the list
* appearsq.
@@ -797,7 +816,8 @@ format_width(const char *expanded)
} else if (*cp > 0x1f && *cp < 0x7f) {
width++;
cp++;
}
} else
cp++;
}
return (width);
}
@@ -830,8 +850,10 @@ format_trim_left(const char *expanded, u_int limit)
out += ud.size;
}
width += ud.width;
} else
} else {
cp -= ud.have;
cp++;
}
} else if (*cp > 0x1f && *cp < 0x7f) {
if (width + 1 <= limit)
*out++ = *cp;
@@ -877,8 +899,10 @@ format_trim_right(const char *expanded, u_int limit)
out += ud.size;
}
width += ud.width;
} else
} else {
cp -= ud.have;
cp++;
}
} else if (*cp > 0x1f && *cp < 0x7f) {
if (width >= skip)
*out++ = *cp;

748
format.c

File diff suppressed because it is too large Load Diff

75
grid.c
View File

@@ -37,12 +37,12 @@
/* Default grid cell data. */
const struct grid_cell grid_default_cell = {
0, 0, 8, 8, { { ' ' }, 0, 1, 1 }
{ { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0
};
/* Cleared grid cell data. */
const struct grid_cell grid_cleared_cell = {
GRID_FLAG_CLEARED, 0, 8, 8, { { ' ' }, 0, 1, 1 }
{ { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 0
};
static const struct grid_cell_entry grid_cleared_entry = {
GRID_FLAG_CLEARED, { .data = { 0, 8, 8, ' ' } }
@@ -82,6 +82,8 @@ grid_need_extended_cell(const struct grid_cell_entry *gce,
return (1);
if ((gc->fg & COLOUR_FLAG_RGB) || (gc->bg & COLOUR_FLAG_RGB))
return (1);
if (gc->us != 0) /* only supports 256 or RGB */
return (1);
return (0);
}
@@ -184,23 +186,25 @@ grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg)
struct grid_cell *gc;
memcpy(gce, &grid_cleared_entry, sizeof *gce);
if (bg & COLOUR_FLAG_RGB) {
grid_get_extended_cell(gl, gce, gce->flags);
gl->flags |= GRID_LINE_EXTENDED;
if (bg != 8) {
if (bg & COLOUR_FLAG_RGB) {
grid_get_extended_cell(gl, gce, gce->flags);
gl->flags |= GRID_LINE_EXTENDED;
gc = &gl->extddata[gce->offset];
memcpy(gc, &grid_cleared_cell, sizeof *gc);
gc->bg = bg;
} else {
if (bg & COLOUR_FLAG_256)
gce->flags |= GRID_FLAG_BG256;
gce->data.bg = bg;
gc = &gl->extddata[gce->offset];
memcpy(gc, &grid_cleared_cell, sizeof *gc);
gc->bg = bg;
} else {
if (bg & COLOUR_FLAG_256)
gce->flags |= GRID_FLAG_BG256;
gce->data.bg = bg;
}
}
}
/* Check grid y position. */
static int
grid_check_y(struct grid *gd, const char* from, u_int py)
grid_check_y(struct grid *gd, const char *from, u_int py)
{
if (py >= gd->hsize + gd->sy) {
log_debug("%s: y out of range: %u", from, py);
@@ -473,6 +477,7 @@ grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc)
gc->bg = gce->data.bg;
if (gce->flags & GRID_FLAG_BG256)
gc->bg |= COLOUR_FLAG_256;
gc->us = 0;
utf8_set(&gc->data, gce->data.data);
}
@@ -544,7 +549,7 @@ void
grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg)
{
struct grid_line *gl;
u_int xx, yy;
u_int xx, yy, ox, sx;
if (nx == 0 || ny == 0)
return;
@@ -561,16 +566,20 @@ grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg)
for (yy = py; yy < py + ny; yy++) {
gl = &gd->linedata[yy];
if (px + nx >= gd->sx && px < gl->cellused)
gl->cellused = px;
if (px > gl->cellsize && COLOUR_DEFAULT(bg))
continue;
if (px + nx >= gl->cellsize && COLOUR_DEFAULT(bg)) {
gl->cellsize = px;
continue;
sx = gd->sx;
if (sx > gl->cellsize)
sx = gl->cellsize;
ox = nx;
if (COLOUR_DEFAULT(bg)) {
if (px > sx)
continue;
if (px + nx > sx)
ox = sx - px;
}
grid_expand_line(gd, yy, px + nx, 8); /* default bg first */
for (xx = px; xx < px + nx; xx++)
grid_expand_line(gd, yy, px + ox, 8); /* default bg first */
for (xx = px; xx < px + ox; xx++)
grid_clear_cell(gd, xx, yy, bg);
}
}
@@ -790,6 +799,7 @@ grid_string_cells_code(const struct grid_cell *lastgc,
{ GRID_ATTR_UNDERSCORE_3, 43 },
{ GRID_ATTR_UNDERSCORE_4, 44 },
{ GRID_ATTR_UNDERSCORE_5, 45 },
{ GRID_ATTR_OVERLINE, 53 },
};
n = 0;
@@ -1349,3 +1359,22 @@ grid_unwrap_position(struct grid *gd, u_int *px, u_int *py, u_int wx, u_int wy)
*px = wx;
*py = yy;
}
/* Get length of line. */
u_int
grid_line_length(struct grid *gd, u_int py)
{
struct grid_cell gc;
u_int px;
px = grid_get_line(gd, py)->cellsize;
if (px > gd->sx)
px = gd->sx;
while (px > 0) {
grid_get_cell(gd, px - 1, py, &gc);
if (gc.data.size != 1 || *gc.data.data != ' ')
break;
px--;
}
return (px);
}

173
hooks.c
View File

@@ -1,173 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2012 Thomas Adam <thomas@xteddy.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
struct hooks {
RB_HEAD(hooks_tree, hook) tree;
struct hooks *parent;
};
static int hooks_cmp(struct hook *, struct hook *);
RB_GENERATE_STATIC(hooks_tree, hook, entry, hooks_cmp);
static struct hook *hooks_find1(struct hooks *, const char *);
static void hooks_free1(struct hooks *, struct hook *);
static int
hooks_cmp(struct hook *hook1, struct hook *hook2)
{
return (strcmp(hook1->name, hook2->name));
}
struct hooks *
hooks_get(struct session *s)
{
if (s != NULL)
return (s->hooks);
return (global_hooks);
}
struct hooks *
hooks_create(struct hooks *parent)
{
struct hooks *hooks;
hooks = xcalloc(1, sizeof *hooks);
RB_INIT(&hooks->tree);
hooks->parent = parent;
return (hooks);
}
static void
hooks_free1(struct hooks *hooks, struct hook *hook)
{
RB_REMOVE(hooks_tree, &hooks->tree, hook);
cmd_list_free(hook->cmdlist);
free((char *)hook->name);
free(hook);
}
void
hooks_free(struct hooks *hooks)
{
struct hook *hook, *hook1;
RB_FOREACH_SAFE(hook, hooks_tree, &hooks->tree, hook1)
hooks_free1(hooks, hook);
free(hooks);
}
struct hook *
hooks_first(struct hooks *hooks)
{
return (RB_MIN(hooks_tree, &hooks->tree));
}
struct hook *
hooks_next(struct hook *hook)
{
return (RB_NEXT(hooks_tree, &hooks->tree, hook));
}
void
hooks_add(struct hooks *hooks, const char *name, struct cmd_list *cmdlist)
{
struct hook *hook;
if ((hook = hooks_find1(hooks, name)) != NULL)
hooks_free1(hooks, hook);
hook = xcalloc(1, sizeof *hook);
hook->name = xstrdup(name);
hook->cmdlist = cmdlist;
hook->cmdlist->references++;
RB_INSERT(hooks_tree, &hooks->tree, hook);
}
void
hooks_remove(struct hooks *hooks, const char *name)
{
struct hook *hook;
if ((hook = hooks_find1(hooks, name)) != NULL)
hooks_free1(hooks, hook);
}
static struct hook *
hooks_find1(struct hooks *hooks, const char *name)
{
struct hook hook;
hook.name = name;
return (RB_FIND(hooks_tree, &hooks->tree, &hook));
}
struct hook *
hooks_find(struct hooks *hooks, const char *name)
{
struct hook hook0, *hook;
hook0.name = name;
hook = RB_FIND(hooks_tree, &hooks->tree, &hook0);
while (hook == NULL) {
hooks = hooks->parent;
if (hooks == NULL)
break;
hook = RB_FIND(hooks_tree, &hooks->tree, &hook0);
}
return (hook);
}
void
hooks_insert(struct hooks *hooks, struct cmdq_item *item,
struct cmd_find_state *fs, const char *fmt, ...)
{
struct hook *hook;
va_list ap;
char *name;
struct cmdq_item *new_item;
if (item->flags & CMDQ_NOHOOKS)
return;
va_start(ap, fmt);
xvasprintf(&name, fmt, ap);
va_end(ap);
hook = hooks_find(hooks, name);
if (hook == NULL) {
free(name);
return;
}
log_debug("running hook %s (parent %p)", name, item);
new_item = cmdq_get_command(hook->cmdlist, fs, NULL, CMDQ_NOHOOKS);
cmdq_format(new_item, "hook", "%s", name);
if (item != NULL)
cmdq_insert_after(item, new_item);
else
cmdq_append(NULL, new_item);
free(name);
}

View File

@@ -42,9 +42,6 @@ struct input_key_ent {
};
static const struct input_key_ent input_keys[] = {
/* Backspace key. */
{ KEYC_BSPACE, "\177", 0 },
/* Paste keys. */
{ KEYC_PASTE_START, "\033[200~", 0 },
{ KEYC_PASTE_END, "\033[201~", 0 },
@@ -152,14 +149,14 @@ input_split2(u_int c, u_char *dst)
}
/* Translate a key code into an output key sequence. */
void
int
input_key(struct window_pane *wp, key_code key, struct mouse_event *m)
{
const struct input_key_ent *ike;
u_int i;
size_t dlen;
char *out;
key_code justkey;
key_code justkey, newkey;
struct utf8_data ud;
log_debug("writing key 0x%llx (%s) to %%%u", key,
@@ -169,7 +166,22 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m)
if (KEYC_IS_MOUSE(key)) {
if (m != NULL && m->wp != -1 && (u_int)m->wp == wp->id)
input_key_mouse(wp, m);
return;
return (0);
}
/* Literal keys go as themselves (can't be more than eight bits). */
if (key & KEYC_LITERAL) {
ud.data[0] = (u_char)key;
bufferevent_write(wp->event, &ud.data[0], 1);
return (0);
}
/* Is this backspace? */
if ((key & KEYC_MASK_KEY) == KEYC_BSPACE) {
newkey = options_get_number(global_options, "backspace");
if (newkey >= 0x7f)
newkey = '\177';
key = newkey|(key & KEYC_MASK_MOD);
}
/*
@@ -182,15 +194,15 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m)
bufferevent_write(wp->event, "\033", 1);
ud.data[0] = justkey;
bufferevent_write(wp->event, &ud.data[0], 1);
return;
return (0);
}
if (justkey > 0x7f && justkey < KEYC_BASE) {
if (utf8_split(justkey, &ud) != UTF8_DONE)
return;
return (-1);
if (key & KEYC_ESCAPE)
bufferevent_write(wp->event, "\033", 1);
bufferevent_write(wp->event, ud.data, ud.size);
return;
return (0);
}
/*
@@ -201,7 +213,7 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m)
if ((out = xterm_keys_lookup(key)) != NULL) {
bufferevent_write(wp->event, out, strlen(out));
free(out);
return;
return (0);
}
}
key &= ~KEYC_XTERM;
@@ -224,7 +236,7 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m)
}
if (i == nitems(input_keys)) {
log_debug("key 0x%llx missing", key);
return;
return (-1);
}
dlen = strlen(ike->data);
log_debug("found key 0x%llx: \"%s\"", key, ike->data);
@@ -233,6 +245,7 @@ input_key(struct window_pane *wp, key_code key, struct mouse_event *m)
if (key & KEYC_ESCAPE)
bufferevent_write(wp->event, "\033", 1);
bufferevent_write(wp->event, ike->data, dlen);
return (0);
}
/* Translate mouse and output. */

227
input.c
View File

@@ -20,6 +20,7 @@
#include <netinet/in.h>
#include <ctype.h>
#include <resolv.h>
#include <stdlib.h>
#include <string.h>
@@ -244,6 +245,7 @@ enum input_csi_type {
INPUT_CSI_RM,
INPUT_CSI_RM_PRIVATE,
INPUT_CSI_SCP,
INPUT_CSI_SD,
INPUT_CSI_SGR,
INPUT_CSI_SM,
INPUT_CSI_SM_PRIVATE,
@@ -270,6 +272,7 @@ static const struct input_table_entry input_csi_table[] = {
{ 'M', "", INPUT_CSI_DL },
{ 'P', "", INPUT_CSI_DCH },
{ 'S', "", INPUT_CSI_SU },
{ 'T', "", INPUT_CSI_SD },
{ 'X', "", INPUT_CSI_ECH },
{ 'Z', "", INPUT_CSI_CBT },
{ '`', "", INPUT_CSI_HPA },
@@ -738,7 +741,7 @@ input_timer_callback(__unused int fd, __unused short events, void *arg)
static void
input_start_timer(struct input_ctx *ictx)
{
struct timeval tv = { .tv_usec = 100000 };
struct timeval tv = { .tv_sec = 5, .tv_usec = 0 };
event_del(&ictx->timer);
event_add(&ictx->timer, &tv);
@@ -770,6 +773,7 @@ input_save_state(struct input_ctx *ictx)
ictx->old_mode = s->mode;
}
/* Restore screen state. */
static void
input_restore_state(struct input_ctx *ictx)
{
@@ -873,19 +877,29 @@ input_set_state(struct window_pane *wp, const struct input_transition *itr)
/* Parse input. */
void
input_parse(struct window_pane *wp)
{
struct evbuffer *evb = wp->event->input;
input_parse_buffer(wp, EVBUFFER_DATA(evb), EVBUFFER_LENGTH(evb));
evbuffer_drain(evb, EVBUFFER_LENGTH(evb));
}
/* Parse given input. */
void
input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len)
{
struct input_ctx *ictx = wp->ictx;
struct screen_write_ctx *sctx = &ictx->ctx;
const struct input_transition *itr;
struct evbuffer *evb = wp->event->input;
u_char *buf;
size_t len, off;
const struct input_state *state = NULL;
const struct input_transition *itr = NULL;
size_t off = 0;
if (EVBUFFER_LENGTH(evb) == 0)
if (len == 0)
return;
window_update_activity(wp->window);
wp->flags |= PANE_CHANGED;
notify_input(wp, buf, len);
/*
* Open the screen. Use NULL wp if there is a mode set as don't want to
@@ -897,12 +911,6 @@ input_parse(struct window_pane *wp)
screen_write_start(sctx, NULL, &wp->base);
ictx->wp = wp;
buf = EVBUFFER_DATA(evb);
len = EVBUFFER_LENGTH(evb);
off = 0;
notify_input(wp, evb);
log_debug("%s: %%%u %s, %zu bytes: %.*s", __func__, wp->id,
ictx->state->name, len, (int)len, buf);
@@ -911,16 +919,23 @@ input_parse(struct window_pane *wp)
ictx->ch = buf[off++];
/* Find the transition. */
itr = ictx->state->transitions;
while (itr->first != -1 && itr->last != -1) {
if (ictx->ch >= itr->first && ictx->ch <= itr->last)
break;
itr++;
}
if (itr->first == -1 || itr->last == -1) {
/* No transition? Eh? */
fatalx("no transition from state");
if (ictx->state != state ||
itr == NULL ||
ictx->ch < itr->first ||
ictx->ch > itr->last) {
itr = ictx->state->transitions;
while (itr->first != -1 && itr->last != -1) {
if (ictx->ch >= itr->first &&
ictx->ch <= itr->last)
break;
itr++;
}
if (itr->first == -1 || itr->last == -1) {
/* No transition? Eh? */
fatalx("no transition from state");
}
}
state = ictx->state;
/*
* Any state except print stops the current collection. This is
@@ -950,8 +965,6 @@ input_parse(struct window_pane *wp)
/* Close the screen. */
screen_write_stop(sctx);
evbuffer_drain(evb, len);
}
/* Split the parameter list (if any). */
@@ -1185,6 +1198,8 @@ input_c0_dispatch(struct input_ctx *ictx)
case '\013': /* VT */
case '\014': /* FF */
screen_write_linefeed(sctx, 0, ictx->cell.cell.bg);
if (s->mode & MODE_CRLF)
screen_write_carriagereturn(sctx);
break;
case '\015': /* CR */
screen_write_carriagereturn(sctx);
@@ -1288,6 +1303,7 @@ input_csi_dispatch(struct input_ctx *ictx)
struct input_table_entry *entry;
int i, n, m;
u_int cx, bg = ictx->cell.cell.bg;
char *copy, *cp;
if (ictx->flags & INPUT_DISCARD)
return (0);
@@ -1419,6 +1435,13 @@ input_csi_dispatch(struct input_ctx *ictx)
case 6:
input_reply(ictx, "\033[%u;%uR", s->cy + 1, s->cx + 1);
break;
case 1337: /* Terminal version, from iTerm2. */
copy = xstrdup(getversion());
for (cp = copy; *cp != '\0'; cp++)
*cp = toupper((u_char)*cp);
input_reply(ictx, "\033[TMUX %sn", copy);
free(copy);
break;
default:
log_debug("%s: unknown '%c'", __func__, ictx->ch);
break;
@@ -1522,6 +1545,11 @@ input_csi_dispatch(struct input_ctx *ictx)
if (n != -1)
screen_write_scrollup(sctx, n, bg);
break;
case INPUT_CSI_SD:
n = input_get(ictx, 0, 1, 1);
if (n != -1)
screen_write_scrolldown(sctx, n, bg);
break;
case INPUT_CSI_TBC:
switch (input_get(ictx, 0, 0, 0)) {
case -1:
@@ -1826,6 +1854,8 @@ input_csi_dispatch_sgr_256_do(struct input_ctx *ictx, int fgbg, int c)
gc->fg = c | COLOUR_FLAG_256;
else if (fgbg == 48)
gc->bg = c | COLOUR_FLAG_256;
else if (fgbg == 58)
gc->us = c | COLOUR_FLAG_256;
}
return (1);
}
@@ -1859,6 +1889,8 @@ input_csi_dispatch_sgr_rgb_do(struct input_ctx *ictx, int fgbg, int r, int g,
gc->fg = colour_join_rgb(r, g, b);
else if (fgbg == 48)
gc->bg = colour_join_rgb(r, g, b);
else if (fgbg == 58)
gc->us = colour_join_rgb(r, g, b);
return (1);
}
@@ -1897,8 +1929,13 @@ input_csi_dispatch_sgr_colon(struct input_ctx *ictx, u_int i)
free(copy);
return;
}
} else
} else {
n++;
if (n == nitems(p)) {
free(copy);
return;
}
}
log_debug("%s: %u = %d", __func__, n - 1, p[n - 1]);
}
free(copy);
@@ -1935,23 +1972,25 @@ input_csi_dispatch_sgr_colon(struct input_ctx *ictx, u_int i)
}
return;
}
if (p[0] != 38 && p[0] != 48)
if (n < 2 || (p[0] != 38 && p[0] != 48 && p[0] != 58))
return;
if (p[1] == -1)
i = 2;
else
i = 1;
switch (p[i]) {
switch (p[1]) {
case 2:
if (n < i + 4)
if (n < 3)
break;
input_csi_dispatch_sgr_rgb_do(ictx, p[0], p[i + 1], p[i + 2],
p[i + 3]);
if (n == 5)
i = 2;
else
i = 3;
if (n < i + 3)
break;
input_csi_dispatch_sgr_rgb_do(ictx, p[0], p[i], p[i + 1],
p[i + 2]);
break;
case 5:
if (n < i + 2)
if (n < 3)
break;
input_csi_dispatch_sgr_256_do(ictx, p[0], p[i + 1]);
input_csi_dispatch_sgr_256_do(ictx, p[0], p[2]);
break;
}
}
@@ -1978,7 +2017,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx)
if (n == -1)
continue;
if (n == 38 || n == 48) {
if (n == 38 || n == 48 || n == 58) {
i++;
switch (input_get(ictx, i, 0, -1)) {
case 2:
@@ -2067,6 +2106,15 @@ input_csi_dispatch_sgr(struct input_ctx *ictx)
case 49:
gc->bg = 8;
break;
case 53:
gc->attr |= GRID_ATTR_OVERLINE;
break;
case 55:
gc->attr &= ~GRID_ATTR_OVERLINE;
break;
case 59:
gc->us = 0;
break;
case 90:
case 91:
case 92:
@@ -2170,14 +2218,18 @@ input_exit_osc(struct input_ctx *ictx)
switch (option) {
case 0:
case 2:
if (utf8_isvalid(p)) {
screen_set_title(sctx->s, p);
if (screen_set_title(sctx->s, p))
server_status_window(ictx->wp->window);
}
break;
case 4:
input_osc_4(ictx, p);
break;
case 7:
if (utf8_isvalid(p)) {
screen_set_path(sctx->s, p);
server_status_window(ictx->wp->window);
}
break;
case 10:
input_osc_10(ictx, p);
break;
@@ -2225,10 +2277,8 @@ input_exit_apc(struct input_ctx *ictx)
return;
log_debug("%s: \"%s\"", __func__, ictx->input_buf);
if (!utf8_isvalid(ictx->input_buf))
return;
screen_set_title(sctx->s, ictx->input_buf);
server_status_window(ictx->wp->window);
if (screen_set_title(sctx->s, ictx->input_buf))
server_status_window(ictx->wp->window);
}
/* Rename string started. */
@@ -2246,14 +2296,24 @@ input_enter_rename(struct input_ctx *ictx)
static void
input_exit_rename(struct input_ctx *ictx)
{
struct window_pane *wp = ictx->wp;
struct options_entry *oe;
if (ictx->flags & INPUT_DISCARD)
return;
if (!options_get_number(ictx->wp->window->options, "allow-rename"))
if (!options_get_number(ictx->wp->options, "allow-rename"))
return;
log_debug("%s: \"%s\"", __func__, ictx->input_buf);
if (!utf8_isvalid(ictx->input_buf))
return;
if (ictx->input_len == 0) {
oe = options_get_only(wp->window->options, "automatic-rename");
if (oe != NULL)
options_remove(oe);
return;
}
window_set_name(ictx->wp->window, ictx->input_buf);
options_set_number(ictx->wp->window->options, "automatic-rename", 0);
server_status_window(ictx->wp->window);
@@ -2295,6 +2355,54 @@ input_top_bit_set(struct input_ctx *ictx)
return (0);
}
/* Parse colour from OSC. */
static int
input_osc_parse_colour(const char *p, u_int *r, u_int *g, u_int *b)
{
u_int rsize, gsize, bsize;
const char *cp, *s = p;
if (sscanf(p, "rgb:%x/%x/%x", r, g, b) != 3)
return (0);
p += 4;
cp = strchr(p, '/');
rsize = cp - p;
if (rsize == 1)
(*r) = (*r) | ((*r) << 4);
else if (rsize == 3)
(*r) >>= 4;
else if (rsize == 4)
(*r) >>= 8;
else if (rsize != 2)
return (0);
p = cp + 1;
cp = strchr(p, '/');
gsize = cp - p;
if (gsize == 1)
(*g) = (*g) | ((*g) << 4);
else if (gsize == 3)
(*g) >>= 4;
else if (gsize == 4)
(*g) >>= 8;
else if (gsize != 2)
return (0);
bsize = strlen(cp + 1);
if (bsize == 1)
(*b) = (*b) | ((*b) << 4);
else if (bsize == 3)
(*b) >>= 4;
else if (bsize == 4)
(*b) >>= 8;
else if (bsize != 2)
return (0);
log_debug("%s: %s = %02x%02x%02x", __func__, s, *r, *g, *b);
return (1);
}
/* Handle the OSC 4 sequence for setting (multiple) palette entries. */
static void
input_osc_4(struct input_ctx *ictx, const char *p)
@@ -2313,7 +2421,7 @@ input_osc_4(struct input_ctx *ictx, const char *p)
goto bad;
s = strsep(&next, ";");
if (sscanf(s, "rgb:%2x/%2x/%2x", &r, &g, &b) != 3) {
if (!input_osc_parse_colour(s, &r, &g, &b)) {
s = next;
continue;
}
@@ -2336,12 +2444,17 @@ input_osc_10(struct input_ctx *ictx, const char *p)
{
struct window_pane *wp = ictx->wp;
u_int r, g, b;
char tmp[16];
if (sscanf(p, "rgb:%2x/%2x/%2x", &r, &g, &b) != 3)
goto bad;
if (strcmp(p, "?") == 0)
return;
wp->style.gc.fg = colour_join_rgb(r, g, b);
wp->flags |= PANE_REDRAW;
if (!input_osc_parse_colour(p, &r, &g, &b))
goto bad;
xsnprintf(tmp, sizeof tmp, "fg=#%02x%02x%02x", r, g, b);
options_set_style(wp->options, "window-style", 1, tmp);
options_set_style(wp->options, "window-active-style", 1, tmp);
wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
return;
@@ -2355,12 +2468,17 @@ input_osc_11(struct input_ctx *ictx, const char *p)
{
struct window_pane *wp = ictx->wp;
u_int r, g, b;
char tmp[16];
if (sscanf(p, "rgb:%2x/%2x/%2x", &r, &g, &b) != 3)
if (strcmp(p, "?") == 0)
return;
if (!input_osc_parse_colour(p, &r, &g, &b))
goto bad;
wp->style.gc.bg = colour_join_rgb(r, g, b);
wp->flags |= PANE_REDRAW;
xsnprintf(tmp, sizeof tmp, "bg=#%02x%02x%02x", r, g, b);
options_set_style(wp->options, "window-style", 1, tmp);
options_set_style(wp->options, "window-active-style", 1, tmp);
wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
return;
@@ -2398,7 +2516,6 @@ input_osc_52(struct input_ctx *ictx, const char *p)
outlen = 4 * ((len + 2) / 3) + 1;
out = xmalloc(outlen);
if ((outlen = b64_ntop(buf, len, out, outlen)) == -1) {
abort();
free(out);
return;
}
@@ -2432,7 +2549,7 @@ input_osc_52(struct input_ctx *ictx, const char *p)
screen_write_stop(&ctx);
notify_pane("pane-set-clipboard", wp);
paste_add(out, outlen);
paste_add(NULL, out, outlen);
}
/* Handle the OSC 104 sequence for unsetting (multiple) palette entries. */

2
job.c
View File

@@ -117,7 +117,7 @@ job_run(const char *cmd, struct session *s, const char *cwd,
close(out[0]);
nullfd = open(_PATH_DEVNULL, O_RDWR, 0);
if (nullfd < 0)
if (nullfd == -1)
fatal("open failed");
if (dup2(nullfd, STDERR_FILENO) == -1)
fatal("dup2 failed");

View File

@@ -24,6 +24,45 @@
#include "tmux.h"
#define DEFAULT_SESSION_MENU \
" 'Next' 'n' {switch-client -n}" \
" 'Previous' 'p' {switch-client -p}" \
" ''" \
" 'Renumber' 'N' {move-window -r}" \
" 'Rename' 'n' {command-prompt -I \"#S\" \"rename-session -- '%%'\"}" \
" ''" \
" 'New Session' 's' {new-session}" \
" 'New Window' 'w' {new-window}"
#define DEFAULT_WINDOW_MENU \
" 'Swap Left' 'l' {swap-window -t:-1}" \
" 'Swap Right' 'r' {swap-window -t:+1}" \
" '#{?pane_marked_set,,-}Swap Marked' 's' {swap-window}" \
" ''" \
" 'Kill' 'X' {kill-window}" \
" 'Respawn' 'R' {respawn-window -k}" \
" '#{?pane_marked,Unmark,Mark}' 'm' {select-pane -m}" \
" 'Rename' 'n' {command-prompt -I \"#W\" \"rename-window -- '%%'\"}" \
" ''" \
" 'New After' 'w' {new-window -a}" \
" 'New At End' 'W' {new-window}"
#define DEFAULT_PANE_MENU \
" '#{?mouse_word,Search For #[underscore]#{=/9/...:mouse_word},}' 'C-r' {copy-mode -t=; send -Xt= search-backward \"#{q:mouse_word}\"}" \
" '#{?mouse_word,Type #[underscore]#{=/9/...:mouse_word},}' 'C-y' {send-keys -l -- \"#{q:mouse_word}\"}" \
" '#{?mouse_word,Copy #[underscore]#{=/9/...:mouse_word},}' 'c' {set-buffer -- \"#{q:mouse_word}\"}" \
" '#{?mouse_line,Copy Line,}' 'l' {set-buffer -- \"#{q:mouse_line}\"}" \
" ''" \
" 'Horizontal Split' 'h' {split-window -h}" \
" 'Vertical Split' 'v' {split-window -v}" \
" ''" \
" 'Swap Up' 'u' {swap-pane -U}" \
" 'Swap Down' 'd' {swap-pane -D}" \
" '#{?pane_marked_set,,-}Swap Marked' 's' {swap-pane}" \
" ''" \
" 'Kill' 'X' {kill-pane}" \
" 'Respawn' 'R' {respawn-pane -k}" \
" '#{?pane_marked,Unmark,Mark}' 'm' {select-pane -m}" \
" '#{?window_zoomed_flag,Unzoom,Zoom}' 'z' {resize-pane -Z}"
static int key_bindings_cmp(struct key_binding *, struct key_binding *);
RB_GENERATE_STATIC(key_bindings, key_binding, entry, key_bindings_cmp);
static int key_table_cmp(struct key_table *, struct key_table *);
@@ -46,6 +85,15 @@ key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2)
return (0);
}
static void
key_bindings_free(struct key_table *table, struct key_binding *bd)
{
RB_REMOVE(key_bindings, &table->key_bindings, bd);
cmd_list_free(bd->cmdlist);
free((void *)bd->note);
free(bd);
}
struct key_table *
key_bindings_get_table(const char *name, int create)
{
@@ -87,11 +135,8 @@ key_bindings_unref_table(struct key_table *table)
if (--table->references != 0)
return;
RB_FOREACH_SAFE(bd, key_bindings, &table->key_bindings, bd1) {
RB_REMOVE(key_bindings, &table->key_bindings, bd);
cmd_list_free(bd->cmdlist);
free(bd);
}
RB_FOREACH_SAFE(bd, key_bindings, &table->key_bindings, bd1)
key_bindings_free(table, bd);
free((void *)table->name);
free(table);
@@ -119,24 +164,22 @@ key_bindings_next(__unused struct key_table *table, struct key_binding *bd)
}
void
key_bindings_add(const char *name, key_code key, int repeat,
key_bindings_add(const char *name, key_code key, const char *note, int repeat,
struct cmd_list *cmdlist)
{
struct key_table *table;
struct key_binding bd_find, *bd;
struct key_binding *bd;
table = key_bindings_get_table(name, 1);
bd_find.key = (key & ~KEYC_XTERM);
bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find);
if (bd != NULL) {
RB_REMOVE(key_bindings, &table->key_bindings, bd);
cmd_list_free(bd->cmdlist);
free(bd);
}
bd = key_bindings_get(table, key & ~KEYC_XTERM);
if (bd != NULL)
key_bindings_free(table, bd);
bd = xcalloc(1, sizeof *bd);
bd->key = key;
if (note != NULL)
bd->note = xstrdup(note);
RB_INSERT(key_bindings, &table->key_bindings, bd);
if (repeat)
@@ -148,20 +191,16 @@ void
key_bindings_remove(const char *name, key_code key)
{
struct key_table *table;
struct key_binding bd_find, *bd;
struct key_binding *bd;
table = key_bindings_get_table(name, 0);
if (table == NULL)
return;
bd_find.key = (key & ~KEYC_XTERM);
bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find);
bd = key_bindings_get(table, key & ~KEYC_XTERM);
if (bd == NULL)
return;
RB_REMOVE(key_bindings, &table->key_bindings, bd);
cmd_list_free(bd->cmdlist);
free(bd);
key_bindings_free(table, bd);
if (RB_EMPTY(&table->key_bindings)) {
RB_REMOVE(key_tables, &key_tables, table);
@@ -190,95 +229,103 @@ void
key_bindings_init(void)
{
static const char *defaults[] = {
"bind C-b send-prefix",
"bind C-o rotate-window",
"bind C-z suspend-client",
"bind Space next-layout",
"bind ! break-pane",
"bind '\"' split-window",
"bind '#' list-buffers",
"bind '$' command-prompt -I'#S' \"rename-session -- '%%'\"",
"bind % split-window -h",
"bind & confirm-before -p\"kill-window #W? (y/n)\" kill-window",
"bind \"'\" command-prompt -pindex \"select-window -t ':%%'\"",
"bind ( switch-client -p",
"bind ) switch-client -n",
"bind , command-prompt -I'#W' \"rename-window -- '%%'\"",
"bind - delete-buffer",
"bind . command-prompt \"move-window -t '%%'\"",
"bind 0 select-window -t:=0",
"bind 1 select-window -t:=1",
"bind 2 select-window -t:=2",
"bind 3 select-window -t:=3",
"bind 4 select-window -t:=4",
"bind 5 select-window -t:=5",
"bind 6 select-window -t:=6",
"bind 7 select-window -t:=7",
"bind 8 select-window -t:=8",
"bind 9 select-window -t:=9",
"bind : command-prompt",
"bind \\; last-pane",
"bind = choose-buffer -Z",
"bind ? list-keys",
"bind D choose-client -Z",
"bind E select-layout -E",
"bind L switch-client -l",
"bind M select-pane -M",
"bind [ copy-mode",
"bind ] paste-buffer",
"bind c new-window",
"bind d detach-client",
"bind f command-prompt \"find-window -Z -- '%%'\"",
"bind i display-message",
"bind l last-window",
"bind m select-pane -m",
"bind n next-window",
"bind o select-pane -t:.+",
"bind p previous-window",
"bind q display-panes",
"bind r refresh-client",
"bind s choose-tree -Zs",
"bind t clock-mode",
"bind w choose-tree -Zw",
"bind x confirm-before -p\"kill-pane #P? (y/n)\" kill-pane",
"bind z resize-pane -Z",
"bind { swap-pane -U",
"bind } swap-pane -D",
"bind '~' show-messages",
"bind PPage copy-mode -u",
"bind -r Up select-pane -U",
"bind -r Down select-pane -D",
"bind -r Left select-pane -L",
"bind -r Right select-pane -R",
"bind M-1 select-layout even-horizontal",
"bind M-2 select-layout even-vertical",
"bind M-3 select-layout main-horizontal",
"bind M-4 select-layout main-vertical",
"bind M-5 select-layout tiled",
"bind M-n next-window -a",
"bind M-o rotate-window -D",
"bind M-p previous-window -a",
"bind -r S-Up refresh-client -U 10",
"bind -r S-Down refresh-client -D 10",
"bind -r S-Left refresh-client -L 10",
"bind -r S-Right refresh-client -R 10",
"bind -r DC refresh-client -c",
"bind -r M-Up resize-pane -U 5",
"bind -r M-Down resize-pane -D 5",
"bind -r M-Left resize-pane -L 5",
"bind -r M-Right resize-pane -R 5",
"bind -r C-Up resize-pane -U",
"bind -r C-Down resize-pane -D",
"bind -r C-Left resize-pane -L",
"bind -r C-Right resize-pane -R",
"bind -N 'Send the prefix key' C-b send-prefix",
"bind -N 'Rotate through the panes' C-o rotate-window",
"bind -N 'Suspend the current client' C-z suspend-client",
"bind -N 'Select next layout' Space next-layout",
"bind -N 'Break pane to a new window' ! break-pane",
"bind -N 'Split window vertically' '\"' split-window",
"bind -N 'List all paste buffers' '#' list-buffers",
"bind -N 'Rename current session' '$' command-prompt -I'#S' \"rename-session -- '%%'\"",
"bind -N 'Split window horizontally' % split-window -h",
"bind -N 'Kill current window' & confirm-before -p\"kill-window #W? (y/n)\" kill-window",
"bind -N 'Prompt for window index to select' \"'\" command-prompt -pindex \"select-window -t ':%%'\"",
"bind -N 'Switch to previous client' ( switch-client -p",
"bind -N 'Switch to next client' ) switch-client -n",
"bind -N 'Rename current window' , command-prompt -I'#W' \"rename-window -- '%%'\"",
"bind -N 'Delete the most recent paste buffer' - delete-buffer",
"bind -N 'Move the current window' . command-prompt \"move-window -t '%%'\"",
"bind -N 'Describe key binding' '/' command-prompt -kpkey 'list-keys -1N \"%%%\"'",
"bind -N 'Select window 0' 0 select-window -t:=0",
"bind -N 'Select window 1' 1 select-window -t:=1",
"bind -N 'Select window 2' 2 select-window -t:=2",
"bind -N 'Select window 3' 3 select-window -t:=3",
"bind -N 'Select window 4' 4 select-window -t:=4",
"bind -N 'Select window 5' 5 select-window -t:=5",
"bind -N 'Select window 6' 6 select-window -t:=6",
"bind -N 'Select window 7' 7 select-window -t:=7",
"bind -N 'Select window 8' 8 select-window -t:=8",
"bind -N 'Select window 9' 9 select-window -t:=9",
"bind -N 'Prompt for a command' : command-prompt",
"bind -N 'Move to the previously active pane' \\; last-pane",
"bind -N 'Choose a paste buffer from a list' = choose-buffer -Z",
"bind -N 'List key bindings' ? list-keys -N",
"bind -N 'Choose a client from a list' D choose-client -Z",
"bind -N 'Spread panes out evenly' E select-layout -E",
"bind -N 'Switch to the last client' L switch-client -l",
"bind -N 'Clear the marked pane' M select-pane -M",
"bind -N 'Enter copy mode' [ copy-mode",
"bind -N 'Paste the most recent paste buffer' ] paste-buffer",
"bind -N 'Create a new window' c new-window",
"bind -N 'Detach the current client' d detach-client",
"bind -N 'Search for a pane' f command-prompt \"find-window -Z -- '%%'\"",
"bind -N 'Display window information' i display-message",
"bind -N 'Select the previously current window' l last-window",
"bind -N 'Toggle the marked pane' m select-pane -m",
"bind -N 'Select the next window' n next-window",
"bind -N 'Select the next pane' o select-pane -t:.+",
"bind -N 'Select the previous pane' p previous-window",
"bind -N 'Display pane numbers' q display-panes",
"bind -N 'Redraw the current client' r refresh-client",
"bind -N 'Choose a session from a list' s choose-tree -Zs",
"bind -N 'Show a clock' t clock-mode",
"bind -N 'Choose a window from a list' w choose-tree -Zw",
"bind -N 'Kill the active pane' x confirm-before -p\"kill-pane #P? (y/n)\" kill-pane",
"bind -N 'Zoom the active pane' z resize-pane -Z",
"bind -N 'Swap the active pane with the pane above' '{' swap-pane -U",
"bind -N 'Swap the active pane with the pane below' '}' swap-pane -D",
"bind -N 'Show messages' '~' show-messages",
"bind -N 'Enter copy mode and scroll up' PPage copy-mode -u",
"bind -N 'Select the pane above the active pane' -r Up select-pane -U",
"bind -N 'Select the pane below the active pane' -r Down select-pane -D",
"bind -N 'Select the pane to the left of the active pane' -r Left select-pane -L",
"bind -N 'Select the pane to the right of the active pane' -r Right select-pane -R",
"bind -N 'Set the even-horizontal layout' M-1 select-layout even-horizontal",
"bind -N 'Set the even-vertical layout' M-2 select-layout even-vertical",
"bind -N 'Set the main-horizontal layout' M-3 select-layout main-horizontal",
"bind -N 'Set the main-vertical layout' M-4 select-layout main-vertical",
"bind -N 'Select the tiled layout' M-5 select-layout tiled",
"bind -N 'Select the next window with an alert' M-n next-window -a",
"bind -N 'Rotate through the panes in reverse' M-o rotate-window -D",
"bind -N 'Select the previous window with an alert' M-p previous-window -a",
"bind -N 'Move the visible part of the window up' -r S-Up refresh-client -U 10",
"bind -N 'Move the visible part of the window down' -r S-Down refresh-client -D 10",
"bind -N 'Move the visible part of the window left' -r S-Left refresh-client -L 10",
"bind -N 'Move the visible part of the window right' -r S-Right refresh-client -R 10",
"bind -N 'Reset so the visible part of the window follows the cursor' -r DC refresh-client -c",
"bind -N 'Resize the pane up by 5' -r M-Up resize-pane -U 5",
"bind -N 'Resize the pane down by 5' -r M-Down resize-pane -D 5",
"bind -N 'Resize the pane left by 5' -r M-Left resize-pane -L 5",
"bind -N 'Resize the pane right by 5' -r M-Right resize-pane -R 5",
"bind -N 'Resize the pane up' -r C-Up resize-pane -U",
"bind -N 'Resize the pane down' -r C-Down resize-pane -D",
"bind -N 'Resize the pane left' -r C-Left resize-pane -L",
"bind -N 'Resize the pane right' -r C-Right resize-pane -R",
"bind -n MouseDown1Pane select-pane -t=\\; send-keys -M",
"bind -n MouseDrag1Border resize-pane -M",
"bind -n MouseDown1Status select-window -t=",
"bind -n WheelDownStatus next-window",
"bind -n WheelUpStatus previous-window",
"bind -n MouseDrag1Pane if -Ft= '#{mouse_any_flag}' 'if -Ft= \"#{pane_in_mode}\" \"copy-mode -M\" \"send-keys -M\"' 'copy-mode -M'",
"bind -n MouseDown3Pane if-shell -Ft= '#{mouse_any_flag}' 'select-pane -t=; send-keys -M' 'select-pane -mt='",
"bind -n WheelUpPane if-shell -Ft= '#{mouse_any_flag}' 'send-keys -M' 'if -Ft= \"#{pane_in_mode}\" \"send-keys -M\" \"copy-mode -et=\"'",
"bind -n WheelUpPane if -Ft= '#{mouse_any_flag}' 'send-keys -M' 'if -Ft= \"#{pane_in_mode}\" \"send-keys -M\" \"copy-mode -et=\"'",
"bind -n MouseDown3StatusLeft display-menu -t= -xM -yS -T \"#[align=centre]#{session_name}\" " DEFAULT_SESSION_MENU,
"bind -n MouseDown3Status display-menu -t= -xW -yS -T \"#[align=centre]#{window_index}:#{window_name}\" " DEFAULT_WINDOW_MENU,
"bind < display-menu -xW -yS -T \"#[align=centre]#{window_index}:#{window_name}\" " DEFAULT_WINDOW_MENU,
"bind -n MouseDown3Pane if -Ft= '#{||:#{mouse_any_flag},#{pane_in_mode}}' 'select-pane -t=; send-keys -M' {display-menu -t= -xM -yM -T \"#[align=centre]#{pane_index} (#{pane_id})\" " DEFAULT_PANE_MENU "}",
"bind -n M-MouseDown3Pane display-menu -t= -xM -yM -T \"#[align=centre]#{pane_index} (#{pane_id})\" " DEFAULT_PANE_MENU,
"bind > display-menu -xP -yP -T \"#[align=centre]#{pane_index} (#{pane_id})\" " DEFAULT_PANE_MENU,
"bind -Tcopy-mode C-Space send -X begin-selection",
"bind -Tcopy-mode C-a send -X start-of-line",
@@ -335,18 +382,22 @@ key_bindings_init(void)
"bind -Tcopy-mode M-> send -X history-bottom",
"bind -Tcopy-mode M-R send -X top-line",
"bind -Tcopy-mode M-b send -X previous-word",
"bind -Tcopy-mode C-M-b send -X previous-matching-bracket",
"bind -Tcopy-mode M-f send -X next-word-end",
"bind -Tcopy-mode C-M-f send -X next-matching-bracket",
"bind -Tcopy-mode M-m send -X back-to-indentation",
"bind -Tcopy-mode M-r send -X middle-line",
"bind -Tcopy-mode M-v send -X page-up",
"bind -Tcopy-mode M-w send -X copy-selection-and-cancel",
"bind -Tcopy-mode M-{ send -X previous-paragraph",
"bind -Tcopy-mode M-} send -X next-paragraph",
"bind -Tcopy-mode 'M-{' send -X previous-paragraph",
"bind -Tcopy-mode 'M-}' send -X next-paragraph",
"bind -Tcopy-mode M-Up send -X halfpage-up",
"bind -Tcopy-mode M-Down send -X halfpage-down",
"bind -Tcopy-mode C-Up send -X scroll-up",
"bind -Tcopy-mode C-Down send -X scroll-down",
"bind -Tcopy-mode-vi '#' send -FX search-backward '#{copy_cursor_word}'",
"bind -Tcopy-mode-vi * send -FX search-forward '#{copy_cursor_word}'",
"bind -Tcopy-mode-vi C-c send -X cancel",
"bind -Tcopy-mode-vi C-d send -X halfpage-down",
"bind -Tcopy-mode-vi C-e send -X scroll-down",
@@ -406,8 +457,9 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi t command-prompt -1p'(jump to forward)' 'send -X jump-to-forward \"%%%\"'",
"bind -Tcopy-mode-vi v send -X rectangle-toggle",
"bind -Tcopy-mode-vi w send -X next-word",
"bind -Tcopy-mode-vi { send -X previous-paragraph",
"bind -Tcopy-mode-vi } send -X next-paragraph",
"bind -Tcopy-mode-vi '{' send -X previous-paragraph",
"bind -Tcopy-mode-vi '}' send -X next-paragraph",
"bind -Tcopy-mode-vi % send -X next-matching-bracket",
"bind -Tcopy-mode-vi MouseDown1Pane select-pane",
"bind -Tcopy-mode-vi MouseDrag1Pane select-pane\\; send -X begin-selection",
"bind -Tcopy-mode-vi MouseDragEnd1Pane send -X copy-selection-and-cancel",
@@ -425,16 +477,15 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi C-Up send -X scroll-up",
"bind -Tcopy-mode-vi C-Down send -X scroll-down",
};
u_int i;
struct cmd_list *cmdlist;
char *cause;
u_int i;
struct cmd_parse_result *pr;
for (i = 0; i < nitems(defaults); i++) {
cmdlist = cmd_string_parse(defaults[i], "<default>", i, &cause);
if (cmdlist == NULL)
pr = cmd_parse_from_string(defaults[i], NULL);
if (pr->status != CMD_PARSE_SUCCESS)
fatalx("bad default key: %s", defaults[i]);
cmdq_append(NULL, cmdq_get_command(cmdlist, NULL, NULL, 0));
cmd_list_free(cmdlist);
cmdq_append(NULL, cmdq_get_command(pr->cmdlist, NULL, NULL, 0));
cmd_list_free(pr->cmdlist);
}
}
@@ -445,7 +496,7 @@ key_bindings_read_only(struct cmdq_item *item, __unused void *data)
return (CMD_RETURN_ERROR);
}
void
struct cmdq_item *
key_bindings_dispatch(struct key_binding *bd, struct cmdq_item *item,
struct client *c, struct mouse_event *m, struct cmd_find_state *fs)
{
@@ -453,12 +504,16 @@ key_bindings_dispatch(struct key_binding *bd, struct cmdq_item *item,
struct cmdq_item *new_item;
int readonly;
readonly = 1;
TAILQ_FOREACH(cmd, &bd->cmdlist->list, qentry) {
if (!(cmd->entry->flags & CMD_READONLY))
readonly = 0;
if (c == NULL || (~c->flags & CLIENT_READONLY))
readonly = 1;
else {
readonly = 1;
TAILQ_FOREACH(cmd, &bd->cmdlist->list, qentry) {
if (~cmd->entry->flags & CMD_READONLY)
readonly = 0;
}
}
if (!readonly && (c->flags & CLIENT_READONLY))
if (!readonly)
new_item = cmdq_get_callback(key_bindings_read_only, NULL);
else {
new_item = cmdq_get_command(bd->cmdlist, fs, m, 0);
@@ -466,7 +521,8 @@ key_bindings_dispatch(struct key_binding *bd, struct cmdq_item *item,
new_item->shared->flags |= CMDQ_SHARED_REPEAT;
}
if (item != NULL)
cmdq_insert_after(item, new_item);
new_item = cmdq_insert_after(item, new_item);
else
cmdq_append(c, new_item);
new_item = cmdq_append(c, new_item);
return (new_item);
}

View File

@@ -159,7 +159,7 @@ key_string_get_modifiers(const char **string)
key_code
key_string_lookup_string(const char *string)
{
static const char *other = "!#()+,-.0123456789:;<=>?'\r\t";
static const char *other = "!#()+,-.0123456789:;<=>'\r\t";
key_code key;
u_int u;
key_code modifiers;
@@ -196,7 +196,7 @@ key_string_lookup_string(const char *string)
/* Is this a standard ASCII key? */
if (string[1] == '\0' && (u_char)string[0] <= 127) {
key = (u_char)string[0];
if (key < 32 || key == 127)
if (key < 32)
return (KEYC_UNKNOWN);
} else {
/* Try as a UTF-8 key. */
@@ -226,6 +226,8 @@ key_string_lookup_string(const char *string)
key -= 64;
else if (key == 32)
key = 0;
else if (key == '?')
key = 127;
else if (key == 63)
key = KEYC_BSPACE;
else
@@ -240,58 +242,24 @@ key_string_lookup_string(const char *string)
const char *
key_string_lookup_key(key_code key)
{
static char out[32];
char tmp[8];
u_int i;
struct utf8_data ud;
size_t off;
static char out[32];
char tmp[8];
const char *s;
u_int i;
struct utf8_data ud;
size_t off;
*out = '\0';
/* Handle no key. */
if (key == KEYC_NONE)
return ("None");
/* Handle special keys. */
if (key == KEYC_UNKNOWN)
return ("Unknown");
if (key == KEYC_ANY)
return ("Any");
if (key == KEYC_FOCUS_IN)
return ("FocusIn");
if (key == KEYC_FOCUS_OUT)
return ("FocusOut");
if (key == KEYC_PASTE_START)
return ("PasteStart");
if (key == KEYC_PASTE_END)
return ("PasteEnd");
if (key == KEYC_MOUSE)
return ("Mouse");
if (key == KEYC_DRAGGING)
return ("Dragging");
if (key == KEYC_MOUSEMOVE_PANE)
return ("MouseMovePane");
if (key == KEYC_MOUSEMOVE_STATUS)
return ("MouseMoveStatus");
if (key == KEYC_MOUSEMOVE_STATUS_LEFT)
return ("MouseMoveStatusLeft");
if (key == KEYC_MOUSEMOVE_STATUS_RIGHT)
return ("MouseMoveStatusRight");
if (key == KEYC_MOUSEMOVE_BORDER)
return ("MouseMoveBorder");
if (key >= KEYC_USER && key < KEYC_USER + KEYC_NUSER) {
snprintf(out, sizeof out, "User%u", (u_int)(key - KEYC_USER));
/* Literal keys are themselves. */
if (key & KEYC_LITERAL) {
snprintf(out, sizeof out, "%c", (int)(key & 0xff));
return (out);
}
/*
* Special case: display C-@ as C-Space. Could do this below in
* the (key >= 0 && key <= 32), but this way we let it be found
* in key_string_table, for the unlikely chance that we might
* change its name.
*/
/* Display C-@ as C-Space. */
if ((key & KEYC_MASK_KEY) == 0)
key = ' ' | KEYC_CTRL | (key & KEYC_MASK_MOD);
key = ' ' | KEYC_CTRL | (key & KEYC_MASK_MOD);
/* Fill in the modifiers. */
if (key & KEYC_CTRL)
@@ -302,6 +270,69 @@ key_string_lookup_key(key_code key)
strlcat(out, "S-", sizeof out);
key &= KEYC_MASK_KEY;
/* Handle no key. */
if (key == KEYC_NONE)
return ("None");
/* Handle special keys. */
if (key == KEYC_UNKNOWN) {
s = "Unknown";
goto append;
}
if (key == KEYC_ANY) {
s = "Any";
goto append;
}
if (key == KEYC_FOCUS_IN) {
s = "FocusIn";
goto append;
}
if (key == KEYC_FOCUS_OUT) {
s = "FocusOut";
goto append;
}
if (key == KEYC_PASTE_START) {
s = "PasteStart";
goto append;
}
if (key == KEYC_PASTE_END) {
s = "PasteEnd";
goto append;
}
if (key == KEYC_MOUSE) {
s = "Mouse";
goto append;
}
if (key == KEYC_DRAGGING) {
s = "Dragging";
goto append;
}
if (key == KEYC_MOUSEMOVE_PANE) {
s = "MouseMovePane";
goto append;
}
if (key == KEYC_MOUSEMOVE_STATUS) {
s = "MouseMoveStatus";
goto append;
}
if (key == KEYC_MOUSEMOVE_STATUS_LEFT) {
s = "MouseMoveStatusLeft";
goto append;
}
if (key == KEYC_MOUSEMOVE_STATUS_RIGHT) {
s = "MouseMoveStatusRight";
goto append;
}
if (key == KEYC_MOUSEMOVE_BORDER) {
s = "MouseMoveBorder";
goto append;
}
if (key >= KEYC_USER && key < KEYC_USER + KEYC_NUSER) {
snprintf(tmp, sizeof tmp, "User%u", (u_int)(key - KEYC_USER));
strlcat(out, tmp, sizeof out);
return (out);
}
/* Try the key against the string table. */
for (i = 0; i < nitems(key_string_table); i++) {
if (key == key_string_table[i].key)
@@ -323,7 +354,7 @@ key_string_lookup_key(key_code key)
}
/* Invalid keys are errors. */
if (key == 127 || key > 255) {
if (key > 255) {
snprintf(out, sizeof out, "Invalid#%llx", key);
return (out);
}
@@ -337,9 +368,15 @@ key_string_lookup_key(key_code key)
} else if (key >= 32 && key <= 126) {
tmp[0] = key;
tmp[1] = '\0';
} else if (key >= 128)
} else if (key == 127)
xsnprintf(tmp, sizeof tmp, "C-?");
else if (key >= 128)
xsnprintf(tmp, sizeof tmp, "\\%llo", key);
strlcat(out, tmp, sizeof out);
return (out);
append:
strlcat(out, s, sizeof out);
return (out);
}

View File

@@ -60,7 +60,7 @@ layout_checksum(const char *layout)
char *
layout_dump(struct layout_cell *root)
{
char layout[BUFSIZ], *out;
char layout[8192], *out;
*layout = '\0';
if (layout_append(root, layout, sizeof layout) != 0)
@@ -116,13 +116,49 @@ layout_append(struct layout_cell *lc, char *buf, size_t len)
return (0);
}
/* Check layout sizes fit. */
static int
layout_check(struct layout_cell *lc)
{
struct layout_cell *lcchild;
u_int n = 0;
switch (lc->type) {
case LAYOUT_WINDOWPANE:
break;
case LAYOUT_LEFTRIGHT:
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (lcchild->sy != lc->sy)
return (0);
if (!layout_check(lcchild))
return (0);
n += lcchild->sx + 1;
}
if (n - 1 != lc->sx)
return (0);
break;
case LAYOUT_TOPBOTTOM:
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (lcchild->sx != lc->sx)
return (0);
if (!layout_check(lcchild))
return (0);
n += lcchild->sy + 1;
}
if (n - 1 != lc->sy)
return (0);
break;
}
return (1);
}
/* Parse a layout string and arrange window as layout. */
int
layout_parse(struct window *w, const char *layout)
{
struct layout_cell *lc, *lcchild;
struct window_pane *wp;
u_int npanes, ncells, sx, sy;
u_int npanes, ncells, sx = 0, sy = 0;
u_short csum;
/* Check validity. */
@@ -153,9 +189,39 @@ layout_parse(struct window *w, const char *layout)
layout_destroy_cell(w, lcchild, &lc);
}
/* Save the old window size and resize to the layout size. */
sx = w->sx; sy = w->sy;
window_resize(w, lc->sx, lc->sy);
/*
* It appears older versions of tmux were able to generate layouts with
* an incorrect top cell size - if it is larger than the top child then
* correct that (if this is still wrong the check code will catch it).
*/
switch (lc->type) {
case LAYOUT_WINDOWPANE:
break;
case LAYOUT_LEFTRIGHT:
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
sy = lcchild->sy + 1;
sx += lcchild->sx + 1;
}
break;
case LAYOUT_TOPBOTTOM:
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
sx = lcchild->sx + 1;
sy += lcchild->sy + 1;
}
break;
}
if (lc->type != LAYOUT_WINDOWPANE && (lc->sx != sx || lc->sy != sy)) {
log_debug("fix layout %u,%u to %u,%u", lc->sx, lc->sy, sx,sy);
layout_print_cell(lc, __func__, 0);
lc->sx = sx - 1; lc->sy = sy - 1;
}
/* Check the new layout. */
if (!layout_check(lc))
return (-1);
/* Resize to the layout size. */
window_resize(w, lc->sx, lc->sy, -1, -1);
/* Destroy the old layout and swap to the new. */
layout_free_cell(w->layout_root);
@@ -166,12 +232,9 @@ layout_parse(struct window *w, const char *layout)
layout_assign(&wp, lc);
/* Update pane offsets and sizes. */
layout_fix_offsets(lc);
layout_fix_offsets(w);
layout_fix_panes(w);
/* Then resize the layout back to the original window size. */
layout_resize(w, sx, sy);
window_resize(w, sx, sy);
recalculate_sizes();
layout_print_cell(lc, __func__, 0);

View File

@@ -158,12 +158,12 @@ layout_set_even(struct window *w, enum layout_type type)
layout_spread_cell(w, lc);
/* Fix cell offsets. */
layout_fix_offsets(lc);
layout_fix_offsets(w);
layout_fix_panes(w);
layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lc->sx, lc->sy);
window_resize(w, lc->sx, lc->sy, -1, -1);
notify_window("window-layout-changed", w);
server_redraw_window(w);
}
@@ -185,7 +185,7 @@ layout_set_main_h(struct window *w)
{
struct window_pane *wp;
struct layout_cell *lc, *lcmain, *lcother, *lcchild;
u_int n, mainh, otherh, sx;
u_int n, mainh, otherh, sx, sy;
layout_print_cell(w->layout_root, __func__, 1);
@@ -195,22 +195,25 @@ layout_set_main_h(struct window *w)
return;
n--; /* take off main pane */
/* Find available height - take off one line for the border. */
sy = w->sy - 1;
/* Get the main pane height and work out the other pane height. */
mainh = options_get_number(w->options, "main-pane-height");
if (mainh + PANE_MINIMUM + 1 >= w->sy) {
if (w->sy <= PANE_MINIMUM + 1 + PANE_MINIMUM)
if (mainh + PANE_MINIMUM >= sy) {
if (sy <= PANE_MINIMUM + PANE_MINIMUM)
mainh = PANE_MINIMUM;
else
mainh = w->sy - (PANE_MINIMUM + 1);
mainh = sy - PANE_MINIMUM;
otherh = PANE_MINIMUM;
} else {
otherh = options_get_number(w->options, "other-pane-height");
if (otherh == 0)
otherh = w->sy - mainh;
else if (otherh > w->sy || w->sy - otherh < mainh)
otherh = w->sy - mainh;
otherh = sy - mainh;
else if (otherh > sy || sy - otherh < mainh)
otherh = sy - mainh;
else
mainh = w->sy - otherh;
mainh = sy - otherh;
}
/* Work out what width is needed. */
@@ -221,7 +224,7 @@ layout_set_main_h(struct window *w)
/* Free old tree and create a new root. */
layout_free(w);
lc = w->layout_root = layout_create_cell(NULL);
layout_set_size(lc, sx, mainh + otherh, 0, 0);
layout_set_size(lc, sx, mainh + otherh + 1, 0, 0);
layout_make_node(lc, LAYOUT_TOPBOTTOM);
/* Create the main pane. */
@@ -254,12 +257,12 @@ layout_set_main_h(struct window *w)
}
/* Fix cell offsets. */
layout_fix_offsets(lc);
layout_fix_offsets(w);
layout_fix_panes(w);
layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lc->sx, lc->sy);
window_resize(w, lc->sx, lc->sy, -1, -1);
notify_window("window-layout-changed", w);
server_redraw_window(w);
}
@@ -269,7 +272,7 @@ layout_set_main_v(struct window *w)
{
struct window_pane *wp;
struct layout_cell *lc, *lcmain, *lcother, *lcchild;
u_int n, mainw, otherw, sy;
u_int n, mainw, otherw, sx, sy;
layout_print_cell(w->layout_root, __func__, 1);
@@ -279,22 +282,25 @@ layout_set_main_v(struct window *w)
return;
n--; /* take off main pane */
/* Find available width - take off one line for the border. */
sx = w->sx - 1;
/* Get the main pane width and work out the other pane width. */
mainw = options_get_number(w->options, "main-pane-width");
if (mainw + PANE_MINIMUM + 1 >= w->sx) {
if (w->sx <= PANE_MINIMUM + 1 + PANE_MINIMUM)
if (mainw + PANE_MINIMUM >= sx) {
if (sx <= PANE_MINIMUM + PANE_MINIMUM)
mainw = PANE_MINIMUM;
else
mainw = w->sx - (PANE_MINIMUM + 1);
mainw = sx - PANE_MINIMUM;
otherw = PANE_MINIMUM;
} else {
otherw = options_get_number(w->options, "other-pane-width");
if (otherw == 0)
otherw = w->sx - mainw;
else if (otherw > w->sx || w->sx - otherw < mainw)
otherw = w->sx - mainw;
otherw = sx - mainw;
else if (otherw > sx || sx - otherw < mainw)
otherw = sx - mainw;
else
mainw = w->sx - otherw;
mainw = sx - otherw;
}
/* Work out what height is needed. */
@@ -305,7 +311,7 @@ layout_set_main_v(struct window *w)
/* Free old tree and create a new root. */
layout_free(w);
lc = w->layout_root = layout_create_cell(NULL);
layout_set_size(lc, mainw + otherw, sy, 0, 0);
layout_set_size(lc, mainw + otherw + 1, sy, 0, 0);
layout_make_node(lc, LAYOUT_LEFTRIGHT);
/* Create the main pane. */
@@ -338,12 +344,12 @@ layout_set_main_v(struct window *w)
}
/* Fix cell offsets. */
layout_fix_offsets(lc);
layout_fix_offsets(w);
layout_fix_panes(w);
layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lc->sx, lc->sy);
window_resize(w, lc->sx, lc->sy, -1, -1);
notify_window("window-layout-changed", w);
server_redraw_window(w);
}
@@ -447,12 +453,12 @@ layout_set_tiled(struct window *w)
}
/* Fix cell offsets. */
layout_fix_offsets(lc);
layout_fix_offsets(w);
layout_fix_panes(w);
layout_print_cell(w->layout_root, __func__, 1);
window_resize(w, lc->sx, lc->sy);
window_resize(w, lc->sx, lc->sy, -1, -1);
notify_window("window-layout-changed", w);
server_redraw_window(w);
}

161
layout.c
View File

@@ -39,7 +39,6 @@ static int layout_resize_pane_grow(struct window *, struct layout_cell *,
enum layout_type, int, int);
static int layout_resize_pane_shrink(struct window *, struct layout_cell *,
enum layout_type, int);
static int layout_need_status(struct layout_cell *, int);
static u_int layout_new_pane_size(struct window *, u_int,
struct layout_cell *, enum layout_type, u_int, u_int,
u_int);
@@ -199,9 +198,9 @@ layout_make_node(struct layout_cell *lc, enum layout_type type)
lc->wp = NULL;
}
/* Fix cell offsets based on their sizes. */
void
layout_fix_offsets(struct layout_cell *lc)
/* Fix cell offsets for a child cell. */
static void
layout_fix_offsets1(struct layout_cell *lc)
{
struct layout_cell *lcchild;
u_int xoff, yoff;
@@ -212,7 +211,7 @@ layout_fix_offsets(struct layout_cell *lc)
lcchild->xoff = xoff;
lcchild->yoff = lc->yoff;
if (lcchild->type != LAYOUT_WINDOWPANE)
layout_fix_offsets(lcchild);
layout_fix_offsets1(lcchild);
xoff += lcchild->sx + 1;
}
} else {
@@ -221,61 +220,92 @@ layout_fix_offsets(struct layout_cell *lc)
lcchild->xoff = lc->xoff;
lcchild->yoff = yoff;
if (lcchild->type != LAYOUT_WINDOWPANE)
layout_fix_offsets(lcchild);
layout_fix_offsets1(lcchild);
yoff += lcchild->sy + 1;
}
}
}
/*
* Returns 1 if we need to reserve space for the pane status line. This is the
* case for the most upper panes only.
*/
static int
layout_need_status(struct layout_cell *lc, int at_top)
/* Update cell offsets based on their sizes. */
void
layout_fix_offsets(struct window *w)
{
struct layout_cell *first_lc;
struct layout_cell *lc = w->layout_root;
if (lc->parent != NULL) {
if (lc->parent->type == LAYOUT_LEFTRIGHT)
return (layout_need_status(lc->parent, at_top));
lc->xoff = 0;
lc->yoff = 0;
if (at_top)
first_lc = TAILQ_FIRST(&lc->parent->cells);
else
first_lc = TAILQ_LAST(&lc->parent->cells,layout_cells);
if (lc == first_lc)
return (layout_need_status(lc->parent, at_top));
return (0);
layout_fix_offsets1(lc);
}
/* Is this a top cell? */
static int
layout_cell_is_top(struct window *w, struct layout_cell *lc)
{
struct layout_cell *next;
while (lc != w->layout_root) {
next = lc->parent;
if (next->type == LAYOUT_TOPBOTTOM &&
lc != TAILQ_FIRST(&next->cells))
return (0);
lc = next;
}
return (1);
}
/* Is this a bottom cell? */
static int
layout_cell_is_bottom(struct window *w, struct layout_cell *lc)
{
struct layout_cell *next;
while (lc != w->layout_root) {
next = lc->parent;
if (next->type == LAYOUT_TOPBOTTOM &&
lc != TAILQ_LAST(&next->cells, layout_cells))
return (0);
lc = next;
}
return (1);
}
/*
* Returns 1 if we need to add an extra line for the pane status line. This is
* the case for the most upper or lower panes only.
*/
static int
layout_add_border(struct window *w, struct layout_cell *lc, int status)
{
if (status == PANE_STATUS_TOP)
return (layout_cell_is_top(w, lc));
if (status == PANE_STATUS_BOTTOM)
return (layout_cell_is_bottom(w, lc));
return (0);
}
/* Update pane offsets and sizes based on their cells. */
void
layout_fix_panes(struct window *w)
{
struct window_pane *wp;
struct layout_cell *lc;
int shift, status;
int status;
status = options_get_number(w->options, "pane-border-status");
TAILQ_FOREACH(wp, &w->panes, entry) {
if ((lc = wp->layout_cell) == NULL)
continue;
if (status != 0)
shift = layout_need_status(lc, status == 1);
else
shift = 0;
wp->xoff = lc->xoff;
wp->yoff = lc->yoff;
if (shift && status == 1)
wp->yoff += 1;
window_pane_resize(wp, lc->sx, lc->sy - shift);
if (layout_add_border(w, lc, status)) {
if (status == PANE_STATUS_TOP)
wp->yoff++;
window_pane_resize(wp, lc->sx, lc->sy - 1);
} else
window_pane_resize(wp, lc->sx, lc->sy);
}
}
@@ -312,13 +342,15 @@ layout_resize_check(struct window *w, struct layout_cell *lc,
status = options_get_number(w->options, "pane-border-status");
if (lc->type == LAYOUT_WINDOWPANE) {
/* Space available in this cell only. */
minimum = PANE_MINIMUM;
if (type == LAYOUT_LEFTRIGHT)
if (type == LAYOUT_LEFTRIGHT) {
available = lc->sx;
else {
minimum = PANE_MINIMUM;
} else {
available = lc->sy;
if (status != 0)
minimum += layout_need_status(lc, status == 1);
if (layout_add_border(w, lc, status))
minimum = PANE_MINIMUM + 1;
else
minimum = PANE_MINIMUM;
}
if (available > minimum)
available -= minimum;
@@ -414,9 +446,9 @@ layout_destroy_cell(struct window *w, struct layout_cell *lc,
lcother = TAILQ_NEXT(lc, entry);
else
lcother = TAILQ_PREV(lc, layout_cells, entry);
if (lcparent->type == LAYOUT_LEFTRIGHT)
if (lcother != NULL && lcparent->type == LAYOUT_LEFTRIGHT)
layout_resize_adjust(w, lcother, lcparent->type, lc->sx + 1);
else
else if (lcother != NULL)
layout_resize_adjust(w, lcother, lcparent->type, lc->sy + 1);
/* Remove this from the parent's list. */
@@ -507,7 +539,7 @@ layout_resize(struct window *w, u_int sx, u_int sy)
layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, ychange);
/* Fix cell offsets. */
layout_fix_offsets(lc);
layout_fix_offsets(w);
layout_fix_panes(w);
}
@@ -567,7 +599,7 @@ layout_resize_layout(struct window *w, struct layout_cell *lc,
}
/* Fix cell offsets. */
layout_fix_offsets(w->layout_root);
layout_fix_offsets(w);
layout_fix_panes(w);
notify_window("window-layout-changed", w);
}
@@ -831,12 +863,12 @@ layout_resize_child_cells(struct window *w, struct layout_cell *lc)
*/
struct layout_cell *
layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
int insert_before, int full_size)
int flags)
{
struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2;
u_int sx, sy, xoff, yoff, size1, size2, minimum;
u_int new_size, saved_size, resize_first = 0;
int status;
int full_size = (flags & SPAWN_FULLSIZE), status;
/*
* If full_size is specified, add a new cell at the top of the window
@@ -861,9 +893,10 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
return (NULL);
break;
case LAYOUT_TOPBOTTOM:
minimum = PANE_MINIMUM * 2 + 1;
if (status != 0)
minimum += layout_need_status(lc, status == 1);
if (layout_add_border(wp->window, lc, status))
minimum = PANE_MINIMUM * 2 + 2;
else
minimum = PANE_MINIMUM * 2 + 1;
if (sy < minimum)
return (NULL);
break;
@@ -881,7 +914,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
saved_size = sy;
if (size < 0)
size2 = ((saved_size + 1) / 2) - 1;
else if (insert_before)
else if (flags & SPAWN_BEFORE)
size2 = saved_size - size - 1;
else
size2 = size;
@@ -892,7 +925,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
size1 = saved_size - 1 - size2;
/* Which size are we using? */
if (insert_before)
if (flags & SPAWN_BEFORE)
new_size = size2;
else
new_size = size1;
@@ -908,7 +941,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
*/
lcparent = lc->parent;
lcnew = layout_create_cell(lcparent);
if (insert_before)
if (flags & SPAWN_BEFORE)
TAILQ_INSERT_BEFORE(lc, lcnew, entry);
else
TAILQ_INSERT_AFTER(&lcparent->cells, lc, lcnew, entry);
@@ -937,7 +970,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
layout_set_size(lcnew, size, sy, 0, 0);
else if (lc->type == LAYOUT_TOPBOTTOM)
layout_set_size(lcnew, sx, size, 0, 0);
if (insert_before)
if (flags & SPAWN_BEFORE)
TAILQ_INSERT_HEAD(&lc->cells, lcnew, entry);
else
TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
@@ -961,12 +994,12 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
/* Create the new child cell. */
lcnew = layout_create_cell(lcparent);
if (insert_before)
if (flags & SPAWN_BEFORE)
TAILQ_INSERT_HEAD(&lcparent->cells, lcnew, entry);
else
TAILQ_INSERT_TAIL(&lcparent->cells, lcnew, entry);
}
if (insert_before) {
if (flags & SPAWN_BEFORE) {
lc1 = lcnew;
lc2 = lc;
} else {
@@ -988,7 +1021,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
if (full_size) {
if (!resize_first)
layout_resize_child_cells(wp->window, lc);
layout_fix_offsets(wp->window->layout_root);
layout_fix_offsets(wp->window);
} else
layout_make_leaf(lc, wp);
@@ -1006,7 +1039,7 @@ layout_close_pane(struct window_pane *wp)
/* Fix pane offsets and sizes. */
if (w->layout_root != NULL) {
layout_fix_offsets(w->layout_root);
layout_fix_offsets(w);
layout_fix_panes(w);
}
notify_window("window-layout-changed", w);
@@ -1021,7 +1054,7 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
number = 0;
TAILQ_FOREACH (lc, &parent->cells, entry)
number++;
number++;
if (number <= 1)
return (0);
status = options_get_number(w->options, "pane-border-status");
@@ -1029,9 +1062,10 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
if (parent->type == LAYOUT_LEFTRIGHT)
size = parent->sx;
else if (parent->type == LAYOUT_TOPBOTTOM) {
size = parent->sy;
if (status != 0)
size -= layout_need_status(parent, status == 1);
if (layout_add_border(w, parent, status))
size = parent->sy - 1;
else
size = parent->sy;
} else
return (0);
if (size < number - 1)
@@ -1049,9 +1083,10 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
change = each - (int)lc->sx;
layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, change);
} else if (parent->type == LAYOUT_TOPBOTTOM) {
this = each;
if (status != 0)
this += layout_need_status(lc, status == 1);
if (layout_add_border(w, lc, status))
this = each + 1;
else
this = each;
change = this - (int)lc->sy;
layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, change);
}
@@ -1073,7 +1108,7 @@ layout_spread_out(struct window_pane *wp)
do {
if (layout_spread_cell(w, parent)) {
layout_fix_offsets(parent);
layout_fix_offsets(w);
layout_fix_panes(w);
break;
}

3
log.c
View File

@@ -130,6 +130,9 @@ log_debug(const char *msg, ...)
{
va_list ap;
if (log_file == NULL)
return;
va_start(ap, msg);
log_vwrite(msg, ap);
va_end(ap);

323
menu.c Normal file
View File

@@ -0,0 +1,323 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
struct menu_data {
struct cmdq_item *item;
int flags;
struct cmd_find_state fs;
struct screen s;
u_int px;
u_int py;
struct menu *menu;
int choice;
menu_choice_cb cb;
void *data;
};
void
menu_add_items(struct menu *menu, const struct menu_item *items,
struct cmdq_item *qitem, struct client *c, struct cmd_find_state *fs)
{
const struct menu_item *loop;
for (loop = items; loop->name != NULL; loop++)
menu_add_item(menu, loop, qitem, c, fs);
}
void
menu_add_item(struct menu *menu, const struct menu_item *item,
struct cmdq_item *qitem, struct client *c, struct cmd_find_state *fs)
{
struct menu_item *new_item;
const char *key, *cmd;
char *s, *name;
u_int width;
int line;
line = (item == NULL || item->name == NULL || *item->name == '\0');
if (line && menu->count == 0)
return;
menu->items = xreallocarray(menu->items, menu->count + 1,
sizeof *menu->items);
new_item = &menu->items[menu->count++];
memset(new_item, 0, sizeof *new_item);
if (line)
return;
if (fs != NULL)
s = format_single(qitem, item->name, c, fs->s, fs->wl, fs->wp);
else
s = format_single(qitem, item->name, c, NULL, NULL, NULL);
if (*s == '\0') { /* no item if empty after format expanded */
menu->count--;
return;
}
if (*s != '-' && item->key != KEYC_UNKNOWN && item->key != KEYC_NONE) {
key = key_string_lookup_key(item->key);
xasprintf(&name, "%s#[default] #[align=right](%s)", s, key);
} else
xasprintf(&name, "%s", s);
new_item->name = name;
free(s);
cmd = item->command;
if (cmd != NULL) {
if (fs != NULL)
s = format_single(qitem, cmd, c, fs->s, fs->wl, fs->wp);
else
s = format_single(qitem, cmd, c, NULL, NULL, NULL);
} else
s = NULL;
new_item->command = s;
new_item->key = item->key;
width = format_width(new_item->name);
if (width > menu->width)
menu->width = width;
}
struct menu *
menu_create(const char *title)
{
struct menu *menu;
menu = xcalloc(1, sizeof *menu);
menu->title = xstrdup(title);
return (menu);
}
void
menu_free(struct menu *menu)
{
u_int i;
for (i = 0; i < menu->count; i++) {
free((void *)menu->items[i].name);
free((void *)menu->items[i].command);
}
free(menu->items);
free((void *)menu->title);
free(menu);
}
static void
menu_draw_cb(struct client *c, __unused struct screen_redraw_ctx *ctx0)
{
struct menu_data *md = c->overlay_data;
struct tty *tty = &c->tty;
struct screen *s = &md->s;
struct menu *menu = md->menu;
struct screen_write_ctx ctx;
u_int i, px, py;
screen_write_start(&ctx, NULL, s);
screen_write_clearscreen(&ctx, 8);
screen_write_menu(&ctx, menu, md->choice);
screen_write_stop(&ctx);
px = md->px;
py = md->py;
for (i = 0; i < screen_size_y(&md->s); i++)
tty_draw_line(tty, NULL, s, 0, i, menu->width + 4, px, py + i);
if (~md->flags & MENU_NOMOUSE)
tty_update_mode(tty, MODE_MOUSE_ALL, NULL);
}
static void
menu_free_cb(struct client *c)
{
struct menu_data *md = c->overlay_data;
if (md->item != NULL)
cmdq_continue(md->item);
if (md->cb != NULL)
md->cb(md->menu, UINT_MAX, KEYC_NONE, md->data);
screen_free(&md->s);
menu_free(md->menu);
free(md);
}
static int
menu_key_cb(struct client *c, struct key_event *event)
{
struct menu_data *md = c->overlay_data;
struct menu *menu = md->menu;
struct mouse_event *m = &event->m;
u_int i;
int count = menu->count, old = md->choice;
const struct menu_item *item;
struct cmdq_item *new_item;
struct cmd_parse_result *pr;
const char *name;
if (KEYC_IS_MOUSE(event->key)) {
if (md->flags & MENU_NOMOUSE) {
if (MOUSE_BUTTONS(m->b) != 0)
return (1);
return (0);
}
if (m->x < md->px ||
m->x > md->px + 4 + menu->width ||
m->y < md->py + 1 ||
m->y > md->py + 1 + count - 1) {
if (MOUSE_RELEASE(m->b))
return (1);
if (md->choice != -1) {
md->choice = -1;
c->flags |= CLIENT_REDRAWOVERLAY;
}
return (0);
}
if (MOUSE_RELEASE(m->b))
goto chosen;
md->choice = m->y - (md->py + 1);
if (md->choice != old)
c->flags |= CLIENT_REDRAWOVERLAY;
return (0);
}
for (i = 0; i < (u_int)count; i++) {
name = menu->items[i].name;
if (name == NULL || *name == '-')
continue;
if (event->key == menu->items[i].key) {
md->choice = i;
goto chosen;
}
}
switch (event->key) {
case KEYC_UP:
case 'k':
if (old == -1)
old = 0;
do {
if (md->choice == -1 || md->choice == 0)
md->choice = count - 1;
else
md->choice--;
name = menu->items[md->choice].name;
} while ((name == NULL || *name == '-') && md->choice != old);
c->flags |= CLIENT_REDRAWOVERLAY;
return (0);
case KEYC_DOWN:
case 'j':
if (old == -1)
old = 0;
do {
if (md->choice == -1 || md->choice == count - 1)
md->choice = 0;
else
md->choice++;
name = menu->items[md->choice].name;
} while ((name == NULL || *name == '-') && md->choice != old);
c->flags |= CLIENT_REDRAWOVERLAY;
return (0);
case '\r':
goto chosen;
case '\033': /* Escape */
case '\003': /* C-c */
case '\007': /* C-g */
case 'q':
return (1);
}
return (0);
chosen:
if (md->choice == -1)
return (1);
item = &menu->items[md->choice];
if (item->name == NULL || *item->name == '-')
return (1);
if (md->cb != NULL) {
md->cb(md->menu, md->choice, item->key, md->data);
md->cb = NULL;
return (1);
}
pr = cmd_parse_from_string(item->command, NULL);
switch (pr->status) {
case CMD_PARSE_EMPTY:
new_item = NULL;
break;
case CMD_PARSE_ERROR:
new_item = cmdq_get_error(pr->error);
free(pr->error);
cmdq_append(c, new_item);
break;
case CMD_PARSE_SUCCESS:
if (md->item != NULL)
m = &md->item->shared->mouse;
else
m = NULL;
new_item = cmdq_get_command(pr->cmdlist, &md->fs, m, 0);
cmd_list_free(pr->cmdlist);
cmdq_append(c, new_item);
break;
}
return (1);
}
int
menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px,
u_int py, struct client *c, struct cmd_find_state *fs, menu_choice_cb cb,
void *data)
{
struct menu_data *md;
if (c->tty.sx < menu->width + 4 || c->tty.sy < menu->count + 2)
return (-1);
md = xcalloc(1, sizeof *md);
md->item = item;
md->flags = flags;
if (fs != NULL)
cmd_find_copy_state(&md->fs, fs);
screen_init(&md->s, menu->width + 4, menu->count + 2, 0);
md->px = px;
md->py = py;
md->menu = menu;
md->choice = -1;
md->cb = cb;
md->data = data;
server_client_set_overlay(c, 0, menu_draw_cb, menu_key_cb, menu_free_cb,
md);
return (0);
}

View File

@@ -35,14 +35,16 @@ struct mode_tree_data {
struct window_pane *wp;
void *modedata;
const struct menu_item *menu;
const char **sort_list;
u_int sort_size;
u_int sort_type;
struct mode_tree_sort_criteria sort_crit;
mode_tree_build_cb buildcb;
mode_tree_draw_cb drawcb;
mode_tree_search_cb searchcb;
mode_tree_menu_cb menucb;
struct mode_tree_list children;
struct mode_tree_list saved;
@@ -89,8 +91,24 @@ struct mode_tree_line {
int flat;
};
struct mode_tree_menu {
struct mode_tree_data *data;
struct client *c;
u_int line;
void *itemdata;
};
static void mode_tree_free_items(struct mode_tree_list *);
static const struct menu_item mode_tree_menu_items[] = {
{ "Scroll Left", '<', NULL },
{ "Scroll Right", '>', NULL },
{ "", KEYC_NONE, NULL },
{ "Cancel", 'q', NULL },
{ NULL, KEYC_NONE, NULL }
};
static struct mode_tree_item *
mode_tree_find_item(struct mode_tree_list *mtl, uint64_t tag)
{
@@ -299,8 +317,9 @@ mode_tree_each_tagged(struct mode_tree_data *mtd, mode_tree_each_cb cb,
struct mode_tree_data *
mode_tree_start(struct window_pane *wp, struct args *args,
mode_tree_build_cb buildcb, mode_tree_draw_cb drawcb,
mode_tree_search_cb searchcb, void *modedata, const char **sort_list,
u_int sort_size, struct screen **s)
mode_tree_search_cb searchcb, mode_tree_menu_cb menucb, void *modedata,
const struct menu_item *menu, const char **sort_list, u_int sort_size,
struct screen **s)
{
struct mode_tree_data *mtd;
const char *sort;
@@ -311,10 +330,10 @@ mode_tree_start(struct window_pane *wp, struct args *args,
mtd->wp = wp;
mtd->modedata = modedata;
mtd->menu = menu;
mtd->sort_list = sort_list;
mtd->sort_size = sort_size;
mtd->sort_type = 0;
mtd->preview = !args_has(args, 'N');
@@ -322,9 +341,10 @@ mode_tree_start(struct window_pane *wp, struct args *args,
if (sort != NULL) {
for (i = 0; i < sort_size; i++) {
if (strcasecmp(sort, sort_list[i]) == 0)
mtd->sort_type = i;
mtd->sort_crit.field = i;
}
}
mtd->sort_crit.reversed = args_has(args, 'r');
if (args_has(args, 'f'))
mtd->filter = xstrdup(args_get(args, 'f'));
@@ -334,6 +354,7 @@ mode_tree_start(struct window_pane *wp, struct args *args,
mtd->buildcb = buildcb;
mtd->drawcb = drawcb;
mtd->searchcb = searchcb;
mtd->menucb = menucb;
TAILQ_INIT(&mtd->children);
@@ -366,15 +387,15 @@ mode_tree_build(struct mode_tree_data *mtd)
if (mtd->line_list != NULL)
tag = mtd->line_list[mtd->current].item->tag;
else
tag = 0;
tag = UINT64_MAX;
TAILQ_CONCAT(&mtd->saved, &mtd->children, entry);
TAILQ_INIT(&mtd->children);
mtd->buildcb(mtd->modedata, mtd->sort_type, &tag, mtd->filter);
mtd->buildcb(mtd->modedata, &mtd->sort_crit, &tag, mtd->filter);
mtd->no_matches = TAILQ_EMPTY(&mtd->children);
if (mtd->no_matches)
mtd->buildcb(mtd->modedata, mtd->sort_type, &tag, NULL);
mtd->buildcb(mtd->modedata, &mtd->sort_crit, &tag, NULL);
mode_tree_free_items(&mtd->saved);
TAILQ_INIT(&mtd->saved);
@@ -382,6 +403,8 @@ mode_tree_build(struct mode_tree_data *mtd)
mode_tree_clear_lines(mtd);
mode_tree_build_lines(mtd, &mtd->children, 0);
if (tag == UINT64_MAX)
tag = mtd->line_list[mtd->current].item->tag;
mode_tree_set_current(mtd, tag);
mtd->width = screen_size_x(s);
@@ -457,7 +480,7 @@ mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent,
saved = mode_tree_find_item(&mtd->saved, tag);
if (saved != NULL) {
if (parent == NULL || (parent != NULL && parent->expanded))
if (parent == NULL || parent->expanded)
mti->tagged = saved->tagged;
mti->expanded = saved->expanded;
} else if (expanded == -1)
@@ -575,6 +598,8 @@ mode_tree_draw(struct mode_tree_data *mtd)
xasprintf(&text, "%-*s%s%s%s: ", keylen, key, start, mti->name,
tag);
width = utf8_cstrwidth(text);
if (width > w)
width = w;
free(start);
if (mti->tagged) {
@@ -584,11 +609,11 @@ mode_tree_draw(struct mode_tree_data *mtd)
if (i != mtd->current) {
screen_write_clearendofline(&ctx, 8);
screen_write_puts(&ctx, &gc0, "%s", text);
screen_write_nputs(&ctx, w, &gc0, "%s", text);
format_draw(&ctx, &gc0, w - width, mti->text, NULL);
} else {
screen_write_clearendofline(&ctx, gc.bg);
screen_write_puts(&ctx, &gc, "%s", text);
screen_write_nputs(&ctx, w, &gc, "%s", text);
format_draw(&ctx, &gc, w - width, mti->text, NULL);
}
free(text);
@@ -611,8 +636,9 @@ mode_tree_draw(struct mode_tree_data *mtd)
screen_write_cursormove(&ctx, 0, h, 0);
screen_write_box(&ctx, w, sy - h);
xasprintf(&text, " %s (sort: %s)", mti->name,
mtd->sort_list[mtd->sort_type]);
xasprintf(&text, " %s (sort: %s%s)", mti->name,
mtd->sort_list[mtd->sort_crit.field],
mtd->sort_crit.reversed ? ", reversed" : "");
if (w - 2 >= strlen(text)) {
screen_write_cursormove(&ctx, 1, h, 0);
screen_write_puts(&ctx, &gc0, "%s", text);
@@ -762,6 +788,70 @@ mode_tree_filter_free(void *data)
mode_tree_remove_ref(data);
}
static void
mode_tree_menu_callback(__unused struct menu *menu, __unused u_int idx,
key_code key, void *data)
{
struct mode_tree_menu *mtm = data;
struct mode_tree_data *mtd = mtm->data;
struct mode_tree_item *mti;
if (mtd->dead || key == KEYC_NONE)
goto out;
if (mtm->line >= mtd->line_size)
goto out;
mti = mtd->line_list[mtm->line].item;
if (mti->itemdata != mtm->itemdata)
goto out;
mtd->current = mtm->line;
mtd->menucb (mtd->modedata, mtm->c, key);
out:
mode_tree_remove_ref(mtd);
free(mtm);
}
static void
mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x,
u_int y, int outside)
{
struct mode_tree_item *mti;
struct menu *menu;
const struct menu_item *items;
struct mode_tree_menu *mtm;
char *title;
u_int line;
if (mtd->offset + y > mtd->line_size - 1)
line = mtd->current;
else
line = mtd->offset + y;
mti = mtd->line_list[line].item;
if (!outside) {
items = mtd->menu;
xasprintf(&title, "#[align=centre]%s", mti->name);
} else {
items = mode_tree_menu_items;
title = xstrdup("");
}
menu = menu_create(title);
menu_add_items(menu, items, NULL, NULL, NULL);
free(title);
mtm = xmalloc(sizeof *mtm);
mtm->data = mtd;
mtm->c = c;
mtm->line = line;
mtm->itemdata = mti->itemdata;
mtd->references++;
if (menu_display(menu, 0, NULL, x, y, c, NULL, mode_tree_menu_callback,
mtm) != 0)
menu_free(menu);
}
int
mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
struct mouse_event *m, u_int *xp, u_int *yp)
@@ -772,7 +862,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
int choice;
key_code tmp;
if (KEYC_IS_MOUSE(*key)) {
if (KEYC_IS_MOUSE(*key) && m != NULL) {
if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) {
*key = KEYC_NONE;
return (0);
@@ -782,20 +872,29 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
if (yp != NULL)
*yp = y;
if (x > mtd->width || y > mtd->height) {
if (*key == KEYC_MOUSEDOWN3_PANE)
mode_tree_display_menu(mtd, c, x, y, 1);
if (!mtd->preview)
*key = KEYC_NONE;
return (0);
}
if (mtd->offset + y < mtd->line_size) {
if (*key == KEYC_MOUSEDOWN1_PANE ||
*key == KEYC_MOUSEDOWN3_PANE ||
*key == KEYC_DOUBLECLICK1_PANE)
mtd->current = mtd->offset + y;
if (*key == KEYC_DOUBLECLICK1_PANE)
*key = '\r';
else
else {
if (*key == KEYC_MOUSEDOWN3_PANE)
mode_tree_display_menu(mtd, c, x, y, 0);
*key = KEYC_NONE;
} else
}
} else {
if (*key == KEYC_MOUSEDOWN3_PANE)
mode_tree_display_menu(mtd, c, x, y, 0);
*key = KEYC_NONE;
}
return (0);
}
@@ -837,6 +936,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
case '\016': /* C-n */
mode_tree_down(mtd, 1);
break;
case 'g':
case KEYC_PPAGE:
case '\002': /* C-b */
for (i = 0; i < mtd->height; i++) {
@@ -845,6 +945,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
mode_tree_up(mtd, 1);
}
break;
case 'G':
case KEYC_NPAGE:
case '\006': /* C-f */
for (i = 0; i < mtd->height; i++) {
@@ -879,7 +980,8 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
current->tagged = 1;
} else
current->tagged = 0;
mode_tree_down(mtd, 0);
if (m != NULL)
mode_tree_down(mtd, 0);
break;
case 'T':
for (i = 0; i < mtd->line_size; i++)
@@ -894,9 +996,13 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
}
break;
case 'O':
mtd->sort_type++;
if (mtd->sort_type == mtd->sort_size)
mtd->sort_type = 0;
mtd->sort_crit.field++;
if (mtd->sort_crit.field == mtd->sort_size)
mtd->sort_crit.field = 0;
mode_tree_build(mtd);
break;
case 'r':
mtd->sort_crit.reversed = !mtd->sort_crit.reversed;
mode_tree_build(mtd);
break;
case KEYC_LEFT:
@@ -922,6 +1028,8 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
mode_tree_build(mtd);
}
break;
case '?':
case '/':
case '\023': /* C-s */
mtd->references++;
status_prompt_set(c, "(search) ", "",
@@ -952,8 +1060,8 @@ mode_tree_run_command(struct client *c, struct cmd_find_state *fs,
const char *template, const char *name)
{
struct cmdq_item *new_item;
struct cmd_list *cmdlist;
char *command, *cause;
char *command;
struct cmd_parse_result *pr;
command = cmd_template_replace(template, name, 1);
if (command == NULL || *command == '\0') {
@@ -961,17 +1069,22 @@ mode_tree_run_command(struct client *c, struct cmd_find_state *fs,
return;
}
cmdlist = cmd_string_parse(command, NULL, 0, &cause);
if (cmdlist == NULL) {
if (cause != NULL && c != NULL) {
*cause = toupper((u_char)*cause);
status_message_set(c, "%s", cause);
pr = cmd_parse_from_string(command, NULL);
switch (pr->status) {
case CMD_PARSE_EMPTY:
break;
case CMD_PARSE_ERROR:
if (c != NULL) {
*pr->error = toupper((u_char)*pr->error);
status_message_set(c, "%s", pr->error);
}
free(cause);
} else {
new_item = cmdq_get_command(cmdlist, fs, NULL, 0);
free(pr->error);
break;
case CMD_PARSE_SUCCESS:
new_item = cmdq_get_command(pr->cmdlist, fs, NULL, 0);
cmdq_append(c, new_item);
cmd_list_free(cmdlist);
cmd_list_free(pr->cmdlist);
break;
}
free(command);

View File

@@ -35,13 +35,34 @@ struct notify_entry {
};
static void
notify_hook1(struct cmdq_item *item, struct notify_entry *ne)
notify_hook_formats(struct cmdq_item *item, struct session *s, struct window *w,
int pane)
{
struct cmd_find_state fs;
struct hook *hook;
struct cmdq_item *new_item;
struct session *s = ne->session;
struct window *w = ne->window;
if (s != NULL) {
cmdq_format(item, "hook_session", "$%u", s->id);
cmdq_format(item, "hook_session_name", "%s", s->name);
}
if (w != NULL) {
cmdq_format(item, "hook_window", "@%u", w->id);
cmdq_format(item, "hook_window_name", "%s", w->name);
}
if (pane != -1)
cmdq_format(item, "hook_pane", "%%%d", pane);
}
static void
notify_insert_hook(struct cmdq_item *item, struct notify_entry *ne)
{
struct cmd_find_state fs;
struct options *oo;
struct cmdq_item *new_item;
struct session *s = ne->session;
struct window *w = ne->window;
struct options_entry *o;
struct options_array_item *a;
struct cmd_list *cmdlist;
log_debug("%s: %s", __func__, ne->name);
cmd_find_clear_state(&fs, 0);
if (cmd_find_empty_state(&ne->fs) || !cmd_find_valid_state(&ne->fs))
@@ -49,26 +70,29 @@ notify_hook1(struct cmdq_item *item, struct notify_entry *ne)
else
cmd_find_copy_state(&fs, &ne->fs);
hook = hooks_find(hooks_get(fs.s), ne->name);
if (hook == NULL)
if (fs.s == NULL)
oo = global_s_options;
else
oo = fs.s->options;
o = options_get(oo, ne->name);
if (o == NULL)
return;
log_debug("notify hook %s", ne->name);
new_item = cmdq_get_command(hook->cmdlist, &fs, NULL, CMDQ_NOHOOKS);
cmdq_format(new_item, "hook", "%s", ne->name);
a = options_array_first(o);
while (a != NULL) {
cmdlist = options_array_item_value(a)->cmdlist;
if (cmdlist == NULL) {
a = options_array_next(a);
continue;
}
if (s != NULL) {
cmdq_format(new_item, "hook_session", "$%u", s->id);
cmdq_format(new_item, "hook_session_name", "%s", s->name);
new_item = cmdq_get_command(cmdlist, &fs, NULL, CMDQ_NOHOOKS);
cmdq_format(new_item, "hook", "%s", ne->name);
notify_hook_formats(new_item, s, w, ne->pane);
item = cmdq_insert_after(item, new_item);
a = options_array_next(a);
}
if (w != NULL) {
cmdq_format(new_item, "hook_window", "@%u", w->id);
cmdq_format(new_item, "hook_window_name", "%s", w->name);
}
if (ne->pane != -1)
cmdq_format(new_item, "hook_pane", "%%%d", ne->pane);
cmdq_insert_after(item, new_item);
}
static enum cmd_retval
@@ -101,7 +125,7 @@ notify_callback(struct cmdq_item *item, void *data)
if (strcmp(ne->name, "session-window-changed") == 0)
control_notify_session_window_changed(ne->session);
notify_hook1(item, ne);
notify_insert_hook(item, ne);
if (ne->client != NULL)
server_client_unref(ne->client);
@@ -168,17 +192,17 @@ notify_hook(struct cmdq_item *item, const char *name)
ne.window = item->target.w;
ne.pane = item->target.wp->id;
notify_hook1(item, &ne);
notify_insert_hook(item, &ne);
}
void
notify_input(struct window_pane *wp, struct evbuffer *input)
notify_input(struct window_pane *wp, const u_char *buf, size_t len)
{
struct client *c;
TAILQ_FOREACH(c, &clients, entry) {
if (c->flags & CLIENT_CONTROL)
control_notify_input(c, wp, input);
control_notify_input(c, wp, buf, len);
}
}

View File

@@ -63,13 +63,16 @@ static const char *options_table_set_clipboard_list[] = {
"off", "external", "on", NULL
};
static const char *options_table_window_size_list[] = {
"largest", "smallest", "manual", NULL
"largest", "smallest", "manual", "latest", NULL
};
/* Status line format. */
#define OPTIONS_TABLE_STATUS_FORMAT1 \
"#[align=left range=left #{status-left-style}]" \
"#{T;=/#{status-left-length}:status-left}#[norange default]" \
"#[push-default]" \
"#{T;=/#{status-left-length}:status-left}" \
"#[pop-default]" \
"#[norange default]" \
"#[list=on align=#{status-justify}]" \
"#[list=left-marker]<#[list=right-marker]>#[list=on]" \
"#{W:" \
@@ -91,7 +94,9 @@ static const char *options_table_window_size_list[] = {
"}" \
"}" \
"]" \
"#[push-default]" \
"#{T:window-status-format}" \
"#[pop-default]" \
"#[norange default]" \
"#{?window_end_flag,,#{window-status-separator}}" \
"," \
@@ -116,12 +121,17 @@ static const char *options_table_window_size_list[] = {
"}" \
"}" \
"]" \
"#[push-default]" \
"#{T:window-status-current-format}" \
"#[pop-default]" \
"#[norange list=on default]" \
"#{?window_end_flag,,#{window-status-separator}}" \
"}" \
"#[nolist align=right range=right #{status-right-style}]" \
"#{T;=/#{status-right-length}:status-right}#[norange default]"
"#[push-default]" \
"#{T;=/#{status-right-length}:status-right}" \
"#[pop-default]" \
"#[norange default]"
#define OPTIONS_TABLE_STATUS_FORMAT2 \
"#[align=centre]#{P:#{?pane_active,#[reverse],}" \
"#{pane_index}[#{pane_width}x#{pane_height}]#[default] }"
@@ -129,8 +139,25 @@ static const char *options_table_status_format_default[] = {
OPTIONS_TABLE_STATUS_FORMAT1, OPTIONS_TABLE_STATUS_FORMAT2, NULL
};
/* Helper for hook options. */
#define OPTIONS_TABLE_HOOK(hook_name, default_value) \
{ .name = hook_name, \
.type = OPTIONS_TABLE_COMMAND, \
.scope = OPTIONS_TABLE_SESSION, \
.flags = OPTIONS_TABLE_IS_ARRAY|OPTIONS_TABLE_IS_HOOK, \
.default_str = default_value, \
.separator = "" \
}
/* Top-level options. */
const struct options_table_entry options_table[] = {
/* Server options. */
{ .name = "backspace",
.type = OPTIONS_TABLE_KEY,
.scope = OPTIONS_TABLE_SERVER,
.default_num = '\177',
},
{ .name = "buffer-limit",
.type = OPTIONS_TABLE_NUMBER,
.scope = OPTIONS_TABLE_SERVER,
@@ -140,8 +167,9 @@ const struct options_table_entry options_table[] = {
},
{ .name = "command-alias",
.type = OPTIONS_TABLE_ARRAY,
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_ARRAY,
.default_str = "split-pane=split-window,"
"splitp=split-window,"
"server-info=show-messages -JT,"
@@ -205,8 +233,9 @@ const struct options_table_entry options_table[] = {
},
{ .name = "terminal-overrides",
.type = OPTIONS_TABLE_ARRAY,
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_ARRAY,
.default_str = "xterm*:XT:Ms=\\E]52;%p1%s;%p2%s\\007"
":Cs=\\E]12;%p1%s\\007:Cr=\\E]112\\007"
":Ss=\\E[%p1%d q:Se=\\E[2 q,screen*:XT",
@@ -214,12 +243,14 @@ const struct options_table_entry options_table[] = {
},
{ .name = "user-keys",
.type = OPTIONS_TABLE_ARRAY,
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_ARRAY,
.default_str = "",
.separator = ","
},
/* Session options. */
{ .name = "activity-action",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION,
@@ -420,8 +451,9 @@ const struct options_table_entry options_table[] = {
},
{ .name = "status-format",
.type = OPTIONS_TABLE_ARRAY,
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.flags = OPTIONS_TABLE_IS_ARRAY,
.default_arr = options_table_status_format_default,
},
@@ -503,8 +535,9 @@ const struct options_table_entry options_table[] = {
},
{ .name = "update-environment",
.type = OPTIONS_TABLE_ARRAY,
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.flags = OPTIONS_TABLE_IS_ARRAY,
.default_str = "DISPLAY KRB5CCNAME SSH_ASKPASS SSH_AUTH_SOCK "
"SSH_AGENT_PID SSH_CONNECTION WINDOWID XAUTHORITY"
},
@@ -533,9 +566,10 @@ const struct options_table_entry options_table[] = {
{ .name = "word-separators",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.default_str = " -_@"
.default_str = " "
},
/* Window options. */
{ .name = "aggressive-resize",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_WINDOW,
@@ -544,13 +578,13 @@ const struct options_table_entry options_table[] = {
{ .name = "allow-rename",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_WINDOW,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_num = 0
},
{ .name = "alternate-screen",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_WINDOW,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_num = 1
},
@@ -670,7 +704,7 @@ const struct options_table_entry options_table[] = {
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_WINDOW,
.choices = options_table_pane_status_list,
.default_num = 0
.default_num = PANE_STATUS_OFF
},
{ .name = "pane-border-style",
@@ -681,7 +715,7 @@ const struct options_table_entry options_table[] = {
{ .name = "remain-on-exit",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_WINDOW,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_num = 0
},
@@ -693,7 +727,7 @@ const struct options_table_entry options_table[] = {
{ .name = "window-active-style",
.type = OPTIONS_TABLE_STYLE,
.scope = OPTIONS_TABLE_WINDOW,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_str = "default"
},
@@ -701,12 +735,12 @@ const struct options_table_entry options_table[] = {
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_WINDOW,
.choices = options_table_window_size_list,
.default_num = WINDOW_SIZE_SMALLEST
.default_num = WINDOW_SIZE_LATEST
},
{ .name = "window-style",
.type = OPTIONS_TABLE_STYLE,
.scope = OPTIONS_TABLE_WINDOW,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_str = "default"
},
@@ -770,5 +804,67 @@ const struct options_table_entry options_table[] = {
.default_num = 1
},
/* Hook options. */
OPTIONS_TABLE_HOOK("after-bind-key", ""),
OPTIONS_TABLE_HOOK("after-capture-pane", ""),
OPTIONS_TABLE_HOOK("after-copy-mode", ""),
OPTIONS_TABLE_HOOK("after-display-message", ""),
OPTIONS_TABLE_HOOK("after-display-panes", ""),
OPTIONS_TABLE_HOOK("after-kill-pane", ""),
OPTIONS_TABLE_HOOK("after-list-buffers", ""),
OPTIONS_TABLE_HOOK("after-list-clients", ""),
OPTIONS_TABLE_HOOK("after-list-keys", ""),
OPTIONS_TABLE_HOOK("after-list-panes", ""),
OPTIONS_TABLE_HOOK("after-list-sessions", ""),
OPTIONS_TABLE_HOOK("after-list-windows", ""),
OPTIONS_TABLE_HOOK("after-load-buffer", ""),
OPTIONS_TABLE_HOOK("after-lock-server", ""),
OPTIONS_TABLE_HOOK("after-new-session", ""),
OPTIONS_TABLE_HOOK("after-new-window", ""),
OPTIONS_TABLE_HOOK("after-paste-buffer", ""),
OPTIONS_TABLE_HOOK("after-pipe-pane", ""),
OPTIONS_TABLE_HOOK("after-queue", ""),
OPTIONS_TABLE_HOOK("after-refresh-client", ""),
OPTIONS_TABLE_HOOK("after-rename-session", ""),
OPTIONS_TABLE_HOOK("after-rename-window", ""),
OPTIONS_TABLE_HOOK("after-resize-pane", ""),
OPTIONS_TABLE_HOOK("after-resize-window", ""),
OPTIONS_TABLE_HOOK("after-save-buffer", ""),
OPTIONS_TABLE_HOOK("after-select-layout", ""),
OPTIONS_TABLE_HOOK("after-select-pane", ""),
OPTIONS_TABLE_HOOK("after-select-window", ""),
OPTIONS_TABLE_HOOK("after-send-keys", ""),
OPTIONS_TABLE_HOOK("after-set-buffer", ""),
OPTIONS_TABLE_HOOK("after-set-environment", ""),
OPTIONS_TABLE_HOOK("after-set-hook", ""),
OPTIONS_TABLE_HOOK("after-set-option", ""),
OPTIONS_TABLE_HOOK("after-show-environment", ""),
OPTIONS_TABLE_HOOK("after-show-messages", ""),
OPTIONS_TABLE_HOOK("after-show-options", ""),
OPTIONS_TABLE_HOOK("after-split-window", ""),
OPTIONS_TABLE_HOOK("after-unbind-key", ""),
OPTIONS_TABLE_HOOK("alert-activity", ""),
OPTIONS_TABLE_HOOK("alert-bell", ""),
OPTIONS_TABLE_HOOK("alert-silence", ""),
OPTIONS_TABLE_HOOK("client-attached", ""),
OPTIONS_TABLE_HOOK("client-detached", ""),
OPTIONS_TABLE_HOOK("client-resized", ""),
OPTIONS_TABLE_HOOK("client-session-changed", ""),
OPTIONS_TABLE_HOOK("pane-died", ""),
OPTIONS_TABLE_HOOK("pane-exited", ""),
OPTIONS_TABLE_HOOK("pane-focus-in", ""),
OPTIONS_TABLE_HOOK("pane-focus-out", ""),
OPTIONS_TABLE_HOOK("pane-mode-changed", ""),
OPTIONS_TABLE_HOOK("pane-set-clipboard", ""),
OPTIONS_TABLE_HOOK("session-closed", ""),
OPTIONS_TABLE_HOOK("session-created", ""),
OPTIONS_TABLE_HOOK("session-renamed", ""),
OPTIONS_TABLE_HOOK("session-window-changed", ""),
OPTIONS_TABLE_HOOK("window-layout-changed", ""),
OPTIONS_TABLE_HOOK("window-linked", ""),
OPTIONS_TABLE_HOOK("window-pane-changed", ""),
OPTIONS_TABLE_HOOK("window-renamed", ""),
OPTIONS_TABLE_HOOK("window-unlinked", ""),
{ .name = NULL }
};

416
options.c
View File

@@ -32,10 +32,9 @@
struct options_array_item {
u_int index;
char *value;
union options_value value;
RB_ENTRY(options_array_item) entry;
};
RB_HEAD(options_array, options_array_item);
static int
options_array_cmp(struct options_array_item *a1, struct options_array_item *a2)
{
@@ -48,19 +47,13 @@ options_array_cmp(struct options_array_item *a1, struct options_array_item *a2)
RB_GENERATE_STATIC(options_array, options_array_item, entry, options_array_cmp);
struct options_entry {
struct options *owner;
struct options *owner;
const char *name;
const struct options_table_entry *tableentry;
const char *name;
const struct options_table_entry *tableentry;
union options_value value;
union {
char *string;
long long number;
struct style style;
struct options_array array;
};
RB_ENTRY(options_entry) entry;
RB_ENTRY(options_entry) entry;
};
struct options {
@@ -83,9 +76,13 @@ static struct options_entry *options_add(struct options *, const char *);
#define OPTIONS_IS_STYLE(o) \
((o)->tableentry != NULL && \
(o)->tableentry->type == OPTIONS_TABLE_STYLE)
#define OPTIONS_IS_ARRAY(o) \
#define OPTIONS_IS_COMMAND(o) \
((o)->tableentry != NULL && \
(o)->tableentry->type == OPTIONS_TABLE_ARRAY)
(o)->tableentry->type == OPTIONS_TABLE_COMMAND)
#define OPTIONS_IS_ARRAY(o) \
((o)->tableentry != NULL && \
((o)->tableentry->flags & OPTIONS_TABLE_IS_ARRAY))
static int options_cmp(struct options_entry *, struct options_entry *);
RB_GENERATE_STATIC(options_tree, options_entry, entry, options_cmp);
@@ -103,12 +100,63 @@ options_parent_table_entry(struct options *oo, const char *s)
if (oo->parent == NULL)
fatalx("no parent options for %s", s);
o = options_get_only(oo->parent, s);
o = options_get(oo->parent, s);
if (o == NULL)
fatalx("%s not in parent options", s);
return (o->tableentry);
}
static void
options_value_free(struct options_entry *o, union options_value *ov)
{
if (OPTIONS_IS_STRING(o))
free(ov->string);
if (OPTIONS_IS_COMMAND(o) && ov->cmdlist != NULL)
cmd_list_free(ov->cmdlist);
}
static char *
options_value_tostring(struct options_entry *o, union options_value *ov,
int numeric)
{
char *s;
if (OPTIONS_IS_COMMAND(o))
return (cmd_list_print(ov->cmdlist, 0));
if (OPTIONS_IS_STYLE(o))
return (xstrdup(style_tostring(&ov->style)));
if (OPTIONS_IS_NUMBER(o)) {
switch (o->tableentry->type) {
case OPTIONS_TABLE_NUMBER:
xasprintf(&s, "%lld", ov->number);
break;
case OPTIONS_TABLE_KEY:
s = xstrdup(key_string_lookup_key(ov->number));
break;
case OPTIONS_TABLE_COLOUR:
s = xstrdup(colour_tostring(ov->number));
break;
case OPTIONS_TABLE_FLAG:
if (numeric)
xasprintf(&s, "%lld", ov->number);
else
s = xstrdup(ov->number ? "on" : "off");
break;
case OPTIONS_TABLE_CHOICE:
s = xstrdup(o->tableentry->choices[ov->number]);
break;
case OPTIONS_TABLE_STRING:
case OPTIONS_TABLE_STYLE:
case OPTIONS_TABLE_COMMAND:
fatalx("not a number option type");
}
return (s);
}
if (OPTIONS_IS_STRING(o))
return (xstrdup(ov->string));
return (xstrdup(""));
}
struct options *
options_create(struct options *parent)
{
@@ -130,6 +178,12 @@ options_free(struct options *oo)
free(oo);
}
void
options_set_parent(struct options *oo, struct options *parent)
{
oo->parent = parent;
}
struct options_entry *
options_first(struct options *oo)
{
@@ -174,8 +228,8 @@ options_empty(struct options *oo, const struct options_table_entry *oe)
o = options_add(oo, oe->name);
o->tableentry = oe;
if (oe->type == OPTIONS_TABLE_ARRAY)
RB_INIT(&o->array);
if (oe->flags & OPTIONS_TABLE_IS_ARRAY)
RB_INIT(&o->value.array);
return (o);
}
@@ -183,23 +237,35 @@ options_empty(struct options *oo, const struct options_table_entry *oe)
struct options_entry *
options_default(struct options *oo, const struct options_table_entry *oe)
{
struct options_entry *o;
u_int i;
struct options_entry *o;
union options_value *ov;
u_int i;
o = options_empty(oo, oe);
if (oe->type == OPTIONS_TABLE_ARRAY) {
if (oe->default_arr != NULL) {
for (i = 0; oe->default_arr[i] != NULL; i++)
options_array_set(o, i, oe->default_arr[i], 0);
} else
options_array_assign(o, oe->default_str);
} else if (oe->type == OPTIONS_TABLE_STRING)
o->string = xstrdup(oe->default_str);
else if (oe->type == OPTIONS_TABLE_STYLE) {
style_set(&o->style, &grid_default_cell);
style_parse(&o->style, &grid_default_cell, oe->default_str);
} else
o->number = oe->default_num;
ov = &o->value;
if (oe->flags & OPTIONS_TABLE_IS_ARRAY) {
if (oe->default_arr == NULL) {
options_array_assign(o, oe->default_str, NULL);
return (o);
}
for (i = 0; oe->default_arr[i] != NULL; i++)
options_array_set(o, i, oe->default_arr[i], 0, NULL);
return (o);
}
switch (oe->type) {
case OPTIONS_TABLE_STRING:
ov->string = xstrdup(oe->default_str);
break;
case OPTIONS_TABLE_STYLE:
style_set(&ov->style, &grid_default_cell);
style_parse(&ov->style, &grid_default_cell, oe->default_str);
break;
default:
ov->number = oe->default_num;
break;
}
return (o);
}
@@ -225,12 +291,12 @@ options_remove(struct options_entry *o)
{
struct options *oo = o->owner;
if (OPTIONS_IS_STRING(o))
free(o->string);
else if (OPTIONS_IS_ARRAY(o))
if (OPTIONS_IS_ARRAY(o))
options_array_clear(o);
else
options_value_free(o, &o->value);
RB_REMOVE(options_tree, &oo->tree, o);
free((void *)o->name);
free(o);
}
@@ -252,14 +318,25 @@ options_array_item(struct options_entry *o, u_int idx)
struct options_array_item a;
a.index = idx;
return (RB_FIND(options_array, &o->array, &a));
return (RB_FIND(options_array, &o->value.array, &a));
}
static struct options_array_item *
options_array_new(struct options_entry *o, u_int idx)
{
struct options_array_item *a;
a = xcalloc(1, sizeof *a);
a->index = idx;
RB_INSERT(options_array, &o->value.array, a);
return (a);
}
static void
options_array_free(struct options_entry *o, struct options_array_item *a)
{
free(a->value);
RB_REMOVE(options_array, &o->array, a);
options_value_free(o, &a->value);
RB_REMOVE(options_array, &o->value.array, a);
free(a);
}
@@ -271,11 +348,11 @@ options_array_clear(struct options_entry *o)
if (!OPTIONS_IS_ARRAY(o))
return;
RB_FOREACH_SAFE(a, options_array, &o->array, a1)
RB_FOREACH_SAFE(a, options_array, &o->value.array, a1)
options_array_free(o, a);
}
const char *
union options_value *
options_array_get(struct options_entry *o, u_int idx)
{
struct options_array_item *a;
@@ -285,45 +362,77 @@ options_array_get(struct options_entry *o, u_int idx)
a = options_array_item(o, idx);
if (a == NULL)
return (NULL);
return (a->value);
return (&a->value);
}
int
options_array_set(struct options_entry *o, u_int idx, const char *value,
int append)
int append, char **cause)
{
struct options_array_item *a;
char *new;
struct cmd_parse_result *pr;
if (!OPTIONS_IS_ARRAY(o))
if (!OPTIONS_IS_ARRAY(o)) {
if (cause != NULL)
*cause = xstrdup("not an array");
return (-1);
}
a = options_array_item(o, idx);
if (value == NULL) {
a = options_array_item(o, idx);
if (a != NULL)
options_array_free(o, a);
return (0);
}
if (a == NULL) {
a = xcalloc(1, sizeof *a);
a->index = idx;
a->value = xstrdup(value);
RB_INSERT(options_array, &o->array, a);
} else {
free(a->value);
if (a != NULL && append)
xasprintf(&new, "%s%s", a->value, value);
if (OPTIONS_IS_COMMAND(o)) {
pr = cmd_parse_from_string(value, NULL);
switch (pr->status) {
case CMD_PARSE_EMPTY:
if (cause != NULL)
*cause = xstrdup("empty command");
return (-1);
case CMD_PARSE_ERROR:
if (cause != NULL)
*cause = pr->error;
else
free(pr->error);
return (-1);
case CMD_PARSE_SUCCESS:
break;
}
a = options_array_item(o, idx);
if (a == NULL)
a = options_array_new(o, idx);
else
new = xstrdup(value);
a->value = new;
options_value_free(o, &a->value);
a->value.cmdlist = pr->cmdlist;
return (0);
}
return (0);
if (OPTIONS_IS_STRING(o)) {
a = options_array_item(o, idx);
if (a != NULL && append)
xasprintf(&new, "%s%s", a->value.string, value);
else
new = xstrdup(value);
if (a == NULL)
a = options_array_new(o, idx);
else
options_value_free(o, &a->value);
a->value.string = new;
return (0);
}
if (cause != NULL)
*cause = xstrdup("wrong array type");
return (-1);
}
void
options_array_assign(struct options_entry *o, const char *s)
int
options_array_assign(struct options_entry *o, const char *s, char **cause)
{
const char *separator;
char *copy, *next, *string;
@@ -332,7 +441,18 @@ options_array_assign(struct options_entry *o, const char *s)
separator = o->tableentry->separator;
if (separator == NULL)
separator = " ,";
if (*separator == '\0') {
if (*s == '\0')
return (0);
for (i = 0; i < UINT_MAX; i++) {
if (options_array_item(o, i) == NULL)
break;
}
return (options_array_set(o, i, s, 0, cause));
}
if (*s == '\0')
return (0);
copy = string = xstrdup(s);
while ((next = strsep(&string, separator)) != NULL) {
if (*next == '\0')
@@ -343,9 +463,13 @@ options_array_assign(struct options_entry *o, const char *s)
}
if (i == UINT_MAX)
break;
options_array_set(o, i, next, 0);
if (options_array_set(o, i, next, 0, cause) != 0) {
free(copy);
return (-1);
}
}
free(copy);
return (0);
}
struct options_array_item *
@@ -353,13 +477,13 @@ options_array_first(struct options_entry *o)
{
if (!OPTIONS_IS_ARRAY(o))
return (NULL);
return (RB_MIN(options_array, &o->array));
return (RB_MIN(options_array, &o->value.array));
}
struct options_array_item *
options_array_next(struct options_array_item *a)
{
return (RB_NEXT(options_array, &o->array, a));
return (RB_NEXT(options_array, &o->value.array, a));
}
u_int
@@ -368,10 +492,10 @@ options_array_item_index(struct options_array_item *a)
return (a->index);
}
const char *
union options_value *
options_array_item_value(struct options_array_item *a)
{
return (a->value);
return (&a->value);
}
int
@@ -383,59 +507,23 @@ options_isarray(struct options_entry *o)
int
options_isstring(struct options_entry *o)
{
return (OPTIONS_IS_STRING(o) || OPTIONS_IS_ARRAY(o));
return (OPTIONS_IS_STRING(o));
}
const char *
char *
options_tostring(struct options_entry *o, int idx, int numeric)
{
static char s[1024];
const char *tmp;
struct options_array_item *a;
if (OPTIONS_IS_ARRAY(o)) {
if (idx == -1)
return (NULL);
return (xstrdup(""));
a = options_array_item(o, idx);
if (a == NULL)
return ("");
return (a->value);
return (xstrdup(""));
return (options_value_tostring(o, &a->value, numeric));
}
if (OPTIONS_IS_STYLE(o))
return (style_tostring(&o->style));
if (OPTIONS_IS_NUMBER(o)) {
tmp = NULL;
switch (o->tableentry->type) {
case OPTIONS_TABLE_NUMBER:
xsnprintf(s, sizeof s, "%lld", o->number);
break;
case OPTIONS_TABLE_KEY:
tmp = key_string_lookup_key(o->number);
break;
case OPTIONS_TABLE_COLOUR:
tmp = colour_tostring(o->number);
break;
case OPTIONS_TABLE_FLAG:
if (numeric)
xsnprintf(s, sizeof s, "%lld", o->number);
else
tmp = (o->number ? "on" : "off");
break;
case OPTIONS_TABLE_CHOICE:
tmp = o->tableentry->choices[o->number];
break;
case OPTIONS_TABLE_STRING:
case OPTIONS_TABLE_STYLE:
case OPTIONS_TABLE_ARRAY:
break;
}
if (tmp != NULL)
xsnprintf(s, sizeof s, "%s", tmp);
return (s);
}
if (OPTIONS_IS_STRING(o))
return (o->string);
return (NULL);
return (options_value_tostring(o, &o->value, numeric));
}
char *
@@ -481,7 +569,7 @@ options_parse_get(struct options *oo, const char *s, int *idx, int only)
}
char *
options_match(const char *s, int *idx, int* ambiguous)
options_match(const char *s, int *idx, int *ambiguous)
{
const struct options_table_entry *oe, *found;
char *name;
@@ -522,7 +610,7 @@ options_match(const char *s, int *idx, int* ambiguous)
struct options_entry *
options_match_get(struct options *oo, const char *s, int *idx, int only,
int* ambiguous)
int *ambiguous)
{
char *name;
struct options_entry *o;
@@ -549,7 +637,7 @@ options_get_string(struct options *oo, const char *name)
fatalx("missing option %s", name);
if (!OPTIONS_IS_STRING(o))
fatalx("option %s is not a string", name);
return (o->string);
return (o->value.string);
}
long long
@@ -562,7 +650,7 @@ options_get_number(struct options *oo, const char *name)
fatalx("missing option %s", name);
if (!OPTIONS_IS_NUMBER(o))
fatalx("option %s is not a number", name);
return (o->number);
return (o->value.number);
}
struct style *
@@ -575,7 +663,7 @@ options_get_style(struct options *oo, const char *name)
fatalx("missing option %s", name);
if (!OPTIONS_IS_STYLE(o))
fatalx("option %s is not a style", name);
return (&o->style);
return (&o->value.style);
}
struct options_entry *
@@ -592,7 +680,7 @@ options_set_string(struct options *oo, const char *name, int append,
o = options_get_only(oo, name);
if (o != NULL && append && OPTIONS_IS_STRING(o)) {
xasprintf(&value, "%s%s", o->string, s);
xasprintf(&value, "%s%s", o->value.string, s);
free(s);
} else
value = s;
@@ -606,8 +694,8 @@ options_set_string(struct options *oo, const char *name, int append,
if (!OPTIONS_IS_STRING(o))
fatalx("option %s is not a string", name);
free(o->string);
o->string = value;
free(o->value.string);
o->value.string = value;
return (o);
}
@@ -628,7 +716,7 @@ options_set_number(struct options *oo, const char *name, long long value)
if (!OPTIONS_IS_NUMBER(o))
fatalx("option %s is not a number", name);
o->number = value;
o->value.number = value;
return (o);
}
@@ -644,7 +732,7 @@ options_set_style(struct options *oo, const char *name, int append,
o = options_get_only(oo, name);
if (o != NULL && append && OPTIONS_IS_STYLE(o))
style_copy(&sy, &o->style);
style_copy(&sy, &o->value.style);
else
style_set(&sy, &grid_default_cell);
if (style_parse(&sy, &grid_default_cell, value) == -1)
@@ -657,24 +745,106 @@ options_set_style(struct options *oo, const char *name, int append,
if (!OPTIONS_IS_STYLE(o))
fatalx("option %s is not a style", name);
style_copy(&o->style, &sy);
style_copy(&o->value.style, &sy);
return (o);
}
enum options_table_scope
int
options_scope_from_name(struct args *args, int window,
const char *name, struct cmd_find_state *fs, struct options **oo,
char **cause)
{
struct session *s = fs->s;
struct winlink *wl = fs->wl;
struct window_pane *wp = fs->wp;
const char *target = args_get(args, 't');
const struct options_table_entry *oe;
int scope = OPTIONS_TABLE_NONE;
if (*name == '@')
return (options_scope_from_flags(args, window, fs, oo, cause));
for (oe = options_table; oe->name != NULL; oe++) {
if (strcmp(oe->name, name) == 0)
break;
}
if (oe->name == NULL) {
xasprintf(cause, "unknown option: %s", name);
return (OPTIONS_TABLE_NONE);
}
switch (oe->scope) {
case OPTIONS_TABLE_SERVER:
*oo = global_options;
scope = OPTIONS_TABLE_SERVER;
break;
case OPTIONS_TABLE_SESSION:
if (args_has(args, 'g')) {
*oo = global_s_options;
scope = OPTIONS_TABLE_SESSION;
} else if (s == NULL && target != NULL)
xasprintf(cause, "no such session: %s", target);
else if (s == NULL)
xasprintf(cause, "no current session");
else {
*oo = s->options;
scope = OPTIONS_TABLE_SESSION;
}
break;
case OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE:
if (args_has(args, 'p')) {
if (wp == NULL && target != NULL)
xasprintf(cause, "no such pane: %s", target);
else if (wp == NULL)
xasprintf(cause, "no current pane");
else {
*oo = wp->options;
scope = OPTIONS_TABLE_PANE;
}
break;
}
/* FALLTHROUGH */
case OPTIONS_TABLE_WINDOW:
if (args_has(args, 'g')) {
*oo = global_w_options;
scope = OPTIONS_TABLE_WINDOW;
} else if (wl == NULL && target != NULL)
xasprintf(cause, "no such window: %s", target);
else if (wl == NULL)
xasprintf(cause, "no current window");
else {
*oo = wl->window->options;
scope = OPTIONS_TABLE_WINDOW;
}
break;
}
return (scope);
}
int
options_scope_from_flags(struct args *args, int window,
struct cmd_find_state *fs, struct options **oo, char **cause)
{
struct session *s = fs->s;
struct winlink *wl = fs->wl;
const char *target= args_get(args, 't');
struct session *s = fs->s;
struct winlink *wl = fs->wl;
struct window_pane *wp = fs->wp;
const char *target = args_get(args, 't');
if (args_has(args, 's')) {
*oo = global_options;
return (OPTIONS_TABLE_SERVER);
}
if (window || args_has(args, 'w')) {
if (args_has(args, 'p')) {
if (wp == NULL) {
if (target != NULL)
xasprintf(cause, "no such pane: %s", target);
else
xasprintf(cause, "no current pane");
return (OPTIONS_TABLE_NONE);
}
*oo = wp->options;
return (OPTIONS_TABLE_PANE);
} else if (window || args_has(args, 'w')) {
if (args_has(args, 'g')) {
*oo = global_w_options;
return (OPTIONS_TABLE_WINDOW);

View File

@@ -30,7 +30,9 @@ char *osdep_get_name(int, char *);
char *osdep_get_cwd(int);
struct event_base *osdep_event_init(void);
#ifndef __unused
#define __unused __attribute__ ((__unused__))
#endif
char *
osdep_get_name(int fd, __unused char *tty)
@@ -47,6 +49,7 @@ osdep_get_name(int fd, __unused char *tty)
&bsdinfo, sizeof bsdinfo);
if (ret == sizeof bsdinfo && *bsdinfo.pbsi_comm != '\0')
return (strdup(bsdinfo.pbsi_comm));
return (NULL);
#else
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, 0 };
size_t size;

View File

@@ -133,13 +133,27 @@ char *
osdep_get_cwd(int fd)
{
static char target[PATH_MAX + 1];
char *path;
pid_t pgrp;
#ifdef KERN_PROC_CWD
int mib[4];
size_t len;
#else
char *path;
ssize_t n;
#endif
if ((pgrp = tcgetpgrp(fd)) == -1)
return (NULL);
#ifdef KERN_PROC_CWD
mib[0] = CTL_KERN;
mib[1] = KERN_PROC_ARGS;
mib[2] = pgrp;
mib[3] = KERN_PROC_CWD;
len = sizeof(target);
if (sysctl(mib, __arraycount(mib), target, &len, NULL, 0) == 0)
return (target);
#else
xasprintf(&path, "/proc/%lld/cwd", (long long) pgrp);
n = readlink(path, target, sizeof(target) - 1);
free(path);
@@ -147,6 +161,7 @@ osdep_get_cwd(int fd)
target[n] = '\0';
return (target);
}
#endif
return (NULL);
}

View File

@@ -157,11 +157,14 @@ paste_free(struct paste_buffer *pb)
* that the caller is responsible for allocating data.
*/
void
paste_add(char *data, size_t size)
paste_add(const char *prefix, char *data, size_t size)
{
struct paste_buffer *pb, *pb1;
u_int limit;
if (prefix == NULL)
prefix = "buffer";
if (size == 0) {
free(data);
return;
@@ -180,7 +183,7 @@ paste_add(char *data, size_t size)
pb->name = NULL;
do {
free(pb->name);
xasprintf(&pb->name, "buffer%04u", paste_next_index);
xasprintf(&pb->name, "%s%u", prefix, paste_next_index);
paste_next_index++;
} while (paste_get_name(pb->name) != NULL);
@@ -262,7 +265,7 @@ paste_set(char *data, size_t size, const char *name, char **cause)
return (0);
}
if (name == NULL) {
paste_add(data, size);
paste_add(NULL, data, size);
return (0);
}

2
proc.c
View File

@@ -181,7 +181,7 @@ proc_start(const char *name)
memset(&u, 0, sizeof u);
log_debug("%s started (%ld): version %s, socket %s, protocol %d", name,
(long)getpid(), VERSION, socket_path, PROTOCOL_VERSION);
(long)getpid(), getversion(), socket_path, PROTOCOL_VERSION);
log_debug("on %s %s %s; libevent %s (%s)", u.sysname, u.release,
u.version, event_get_version(), event_get_method());

51
regress/command-order.sh Normal file
View File

@@ -0,0 +1,51 @@
#!/bin/sh
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)
trap "rm -f $TMP" 0 1 15
cat <<EOF >$TMP
new -sfoo -nfoo0; neww -nfoo1; neww -nfoo2
new -sbar -nbar0; neww -nbar1; neww -nbar2
EOF
$TMUX -f$TMP start </dev/null || exit 1
sleep 1
$TMUX lsw -aF '#{session_name},#{window_name}'|sort >$TMP || exit 1
$TMUX kill-server 2>/dev/null
cat <<EOF|cmp -s $TMP - || exit 1
bar,bar0
bar,bar1
bar,bar2
foo,foo0
foo,foo1
foo,foo2
EOF
cat <<EOF >$TMP
new -sfoo -nfoo0
neww -nfoo1
neww -nfoo2
new -sbar -nbar0
neww -nbar1
neww -nbar2
EOF
$TMUX -f$TMP start </dev/null || exit 1
sleep 1
$TMUX lsw -aF '#{session_name},#{window_name}'|sort >$TMP || exit 1
$TMUX kill-server 2>/dev/null
cat <<EOF|cmp -s $TMP - || exit 1
bar,bar0
bar,bar1
bar,bar2
foo,foo0
foo,foo1
foo,foo2
EOF
exit 0

14
regress/conf-syntax.sh Normal file
View File

@@ -0,0 +1,14 @@
#!/bin/sh
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
for i in conf/*.conf; do
$TMUX -f/dev/null start \; source -n $i || exit 1
done
exit 0

View File

@@ -0,0 +1,8 @@
%if #{l:1}
set -g status-style fg=cyan,bg='#001040'
%elif #{l:1}
set -g status-style fg=white,bg='#400040'
%else
set -g status-style fg=white,bg='#800000'
%endif
bind ^X last-window

View File

@@ -0,0 +1,58 @@
set -g prefix C-g
# needed for e.g. mutt
bind C-g send-prefix
set -g set-titles on
set -g status-position top
set -g status-keys vi
set -g mode-keys vi
set -g base-index 1
set -g pane-base-index 1
set -g focus-events on
set history-file ~/.tmux_SSH_history
set focus-events on
set -g history-limit 100000
set -s set-clipboard on
set -g display-time 3000
set -g display-panes-time 3000
set -g pane-border-status top
setw -g window-status-current-style bg=colour240,fg=colour250
setw -g window-status-separator "|"
set -g status-bg colour235
set -g status-fg colour245
set -g window-status-format " #I #{=+15:pane_title} #{=-2:?window_flags, #{window_flags}, }"
set -g window-status-current-format " #I #{=+15:pane_title} #{=-2:?window_flags, #{window_flags}, }"
set -g pane-border-format " #P: #{s/ //:pane_title} "
set -g renumber-windows on
set -g status-right-length 0
##############################################################
# I prefer not to have a status for my tabbed term
set -g status-right ""
set -g status-right-length 0
set -g status-left-length 0
set -g status-left ""
# some settings for "navigation"
bind -n C-PageUp copy-mode -u
unbind -n C-Left
unbind -n C-Right
bind -n C-Left select-window -t :-
bind -n C-Right select-window -t :+
# I prefer a tiled layout and easy joining of current active pane via windows'
# index
bind F1 join-pane -s 1.\; select-layout tiled
bind F2 join-pane -s 2.\; select-layout tiled
bind F3 join-pane -s 3.\; select-layout tiled
bind F4 join-pane -s 4.\; select-layout tiled
bind F5 join-pane -s 5.\; select-layout tiled
bind F6 join-pane -s 6.\; select-layout tiled
bind F7 join-pane -s 7.\; select-layout tiled
bind F8 join-pane -s 8.\; select-layout tiled
bind F9 join-pane -s 9.\; select-layout tiled

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