119 Commits
1.3 ... 1.4

Author SHA1 Message Date
no_author
2365b09d6a This commit was manufactured by cvs2svn to create tag 'TMUX_1_4'. 2010-12-27 21:37:43 +00:00
Tiago Cunha
dcef4f8084 Major changes since the previous version. 2010-12-27 21:37:42 +00:00
Tiago Cunha
030d284006 No point in talking about this here when it's already covered by other files. 2010-12-27 21:36:37 +00:00
Tiago Cunha
1b037f74f4 Bump VERSION and comment FDEBUG. 2010-12-27 21:32:16 +00:00
Tiago Cunha
750e6ad542 usage output should fit on a 80 column display. 2010-12-27 21:22:24 +00:00
Tiago Cunha
5f72510edf Sync OpenBSD patchset 810:
tweak previous;
2010-12-27 21:17:25 +00:00
Tiago Cunha
5d86284a1a Sync OpenBSD patchset 809:
Add a missing .Pp and sort options alphabetically, from Tiago Cunha.
2010-12-27 21:13:59 +00:00
Tiago Cunha
3cd9ea1789 Sync OpenBSD patchset 808:
server_kill_window can modify the RB tree so don't use RB_FOREACH, fixes
crash seen by Dan Harnett.
2010-12-25 23:44:37 +00:00
Tiago Cunha
3585feca54 Sync OpenBSD patchset 807:
Style tweaks.
2010-12-25 23:43:53 +00:00
Tiago Cunha
a373235106 Sync OpenBSD patchset 806:
Store sessions in an RB tree by name rather than a list, this is tidier
and allows them to easily be shown sorted in various lists
(list-sessions/choose-sessions).

Keep a session index which is used in a couple of places internally but
make it an ever-increasing number rather than filling in gaps with new
sessions.
2010-12-22 15:36:44 +00:00
Tiago Cunha
9f3399da00 Sync OpenBSD patchset 805:
Fix another stray addition that was too early. Oops.
2010-12-22 15:33:14 +00:00
Tiago Cunha
c05a47ad0d Sync OpenBSD patchset 804:
Undo a change to next/previous session that got mixed in prematurely.
2010-12-22 15:32:40 +00:00
Tiago Cunha
a932742a8a Sync OpenBSD patchset 803:
Dead sessions are never on the active sessions list, so the SESSION_DEAD
flag is effectively unused. Remove it.
2010-12-22 15:31:56 +00:00
Tiago Cunha
bb728b89a7 Sync OpenBSD patchset 802:
Use pointer rather than index for the client's last session.
2010-12-22 15:31:00 +00:00
Tiago Cunha
64d16cf2d6 Sync OpenBSD patchset 801:
Unify the way sessions are used by callbacks - store the address and use
the reference count, then check it is still on the global sessions list
in the callback.
2010-12-22 15:28:51 +00:00
Tiago Cunha
b8eae3902b Sync OpenBSD patchset 800:
Don't nuke the index counter when a session group comes up.
2010-12-22 15:25:07 +00:00
Tiago Cunha
d1bdc9a161 Sync OpenBSD patchset 799:
Add other-pane-height and other-pane-width options, allowing the width
or height of the smaller panes in the main-horizontal and main-vertical
layouts to be set. Mostly from David Goodlad.
2010-12-22 15:23:59 +00:00
Nicholas Marriott
cd92f44686 "So you have screwed up your /dev/null?" From Mathias Gumz. 2010-12-15 23:31:30 +00:00
Nicholas Marriott
206ae727f9 Read ${X} environment variables in strings and $HOME from the global
environment rather than getenv, this allows them to be updated during
the configuration file.
2010-12-13 22:53:56 +00:00
Nicholas Marriott
5fcd6711e4 Track the last session for a client and add a flag to switch-client and
a key binding (L) to move a client back to its last session.
2010-12-11 18:42:20 +00:00
Nicholas Marriott
d227a2e661 Remove a bunch of stuff that is done a few that aren't going to happen
and remove the priority list for now.
2010-12-11 18:06:42 +00:00
Nicholas Marriott
0b8ce56d73 Fix rectangle copy to behave like emacs - the cursor is not part of the
selection on the right edge but on the left it is.
2010-12-11 17:57:28 +00:00
Nicholas Marriott
095ffe9cd1 Make the prompt history global for all clients which is much more useful
than per-client history.
2010-12-11 16:15:02 +00:00
Nicholas Marriott
1a4d78c7af -V to report version, SF issue 3038862. 2010-12-10 21:19:13 +00:00
Nicholas Marriott
286fef9b4d Rephrase a confusing sentence. 2010-12-10 21:05:22 +00:00
Nicholas Marriott
1891f1ce99 Some do still need -lrt. 2010-12-08 19:55:31 +00:00
Micah Cowan
3ab25ac5b5 In the built-in layouts, distribute the panes more evenly.
Set the default value of main-pane-width to 80, rather than 81.
2010-12-07 20:23:21 +00:00
Nicholas Marriott
3a4f765a51 Add an option to alert (monitor) for silence (lack of activity) in a
window. From Thomas Adam.
2010-12-06 22:52:21 +00:00
Nicholas Marriott
d0adcbc98a PatchSet 790
Date: 2010/11/29 19:45:58
Author: nicm
Branch: HEAD
Tag: (none)
Log:
If VISUAL or EDITOR contains "vi", configure mode-keys and status-keys
to vi.

Based on a diff from martynas@, previously requested by a couple of
other people.

Members:
        tmux.1:1.190->1.191
        tmux.c:1.93->1.94
2010-12-06 21:59:42 +00:00
Nicholas Marriott
e75187310d PatchSet 789
Date: 2010/11/22 21:13:13
Author: nicm
Branch: HEAD
Tag: (none)
Log:
There is somewhere that WINDOW_HIDDEN is getting set when it shouldn't
be and I can't find it, but the flag itself is a useless optimisation
that only applies to automatic-resize windows, so just dispose of it
entirely.

Fixes problems reported by Nicholas Riley.

Members:
        resize.c:1.5->1.6
        tmux.h:1.246->1.247
        tty.c:1.92->1.93
2010-12-06 21:57:56 +00:00
Nicholas Marriott
7a0468c95c PatchSet 788
Date: 2010/11/15 06:52:11
Author: nicm
Branch: HEAD
Tag: (none)
Log:
Show more window and pane flags in list-* output, and put layout on the
same line.

Members:
        cmd-list-panes.c:1.5->1.6
        cmd-list-windows.c:1.9->1.10
2010-12-06 21:56:32 +00:00
Nicholas Marriott
1b0d235e3a PatchSet 787
Date: 2010/11/14 09:04:22
Author: nicm
Branch: HEAD
Tag: (none)
Log:
Update man page for update-environment.

Members:
        tmux.1:1.189->1.190
2010-12-06 21:55:42 +00:00
Nicholas Marriott
36e537bcef PatchSet 786
Date: 2010/11/14 08:58:25
Author: nicm
Branch: HEAD
Tag: (none)
Log:
Don't allow last and active window to become the same - a very bad move
when the active window is closed and freed. Reported by sthen@.

Members:
        window.c:1.58->1.59
2010-12-06 21:53:50 +00:00
Nicholas Marriott
fd51bf61c9 Add XAUTHORITY to update-environment, requested by Andreas Kloeckner. 2010-12-06 21:53:00 +00:00
Nicholas Marriott
fe4f760eba PatchSet 784
Date: 2010/11/11 20:54:06
Author: nicm
Branch: HEAD
Tag: (none)
Log:
Flag to flush all key bindings from Rob Paisley.

Members:
        cmd-unbind-key.c:1.7->1.8
        tmux.1:1.188->1.189
2010-12-06 21:51:02 +00:00
Nicholas Marriott
3a4b82d27a PatchSet 783
Date: 2010/11/11 20:51:30
Author: nicm
Branch: HEAD
Tag: (none)
Log:
Declaration in header should be extern.

Members:
        tmux.h:1.245->1.246
2010-12-06 21:50:24 +00:00
Nicholas Marriott
ccdafdabca PatchSet 782
Date: 2010/11/01 20:59:45
Author: nicm
Branch: HEAD
Tag: (none)
Log:
Typo, from Rob Paisley.

Members:
        tmux.1:1.187->1.188
2010-12-06 21:49:57 +00:00
Nicholas Marriott
39e277be3c |---------------------
|PatchSet 781
|Date: 2010/10/29 21:11:57
|Author: nicm
|Branch: HEAD
|Tag: (none)
|Log:
|We now send argv to the server after parsing it in the client to get the
|command, so the client should not modify it. Instead, take a copy. Fixes
|parsing command lists, reported by mcbride@.
|
|Members:
|       cmd-list.c:1.5->1.6
|       cmd.c:1.45->1.46
|       tmux.h:1.244->1.245
2010-12-06 21:48:56 +00:00
Nicholas Marriott
1650155589 Another. 2010-12-06 17:10:42 +00:00
Nicholas Marriott
42327f06df And moar. 2010-12-06 17:09:00 +00:00
Nicholas Marriott
1585b1e928 Moar. 2010-12-06 17:05:31 +00:00
Nicholas Marriott
9ab191b053 + SF requests. 2010-12-06 17:03:22 +00:00
Nicholas Marriott
13032d1d88 Using working directory always, from Vivien Mallet. 2010-12-06 17:00:34 +00:00
Nicholas Marriott
64387d18dd +. 2010-12-04 15:28:14 +00:00
Nicholas Marriott
25bf0faf21 +. 2010-11-22 22:29:30 +00:00
Nicholas Marriott
112fc58e6e +. 2010-11-15 20:34:38 +00:00
Nicholas Marriott
ba68bed10e +. 2010-11-13 16:55:30 +00:00
Nicholas Marriott
30e8ed393e Whoops, get the logic the right way round. 2010-11-13 16:29:05 +00:00
Nicholas Marriott
a6fc49ae08 Fix AIX warnings. 2010-11-11 20:45:49 +00:00
Nicholas Marriott
94c2adf499 malloc(0) may return NULL, so only assume that is an error if allocating
more than zero.
2010-11-11 20:41:08 +00:00
Nicholas Marriott
9c541e42f0 AIX doesn't have daemon(). 2010-11-11 20:37:53 +00:00
Nicholas Marriott
96601ce9fe Bring back fuzz.c. 2010-11-02 20:55:13 +00:00
Nicholas Marriott
1cec111af3 >4 now. 2010-11-01 21:42:20 +00:00
Nicholas Marriott
ae7a7be819 I am almost certain we don't need crypt or rt now, and it builds fine on
Linux without.
2010-10-27 21:40:03 +00:00
Nicholas Marriott
58684ea998 And more. 2010-10-27 21:34:59 +00:00
Nicholas Marriott
914f8584ef Style tweaks. 2010-10-27 21:33:15 +00:00
Nicholas Marriott
2287ec7b3e Compat for closefrom(). 2010-10-27 20:21:01 +00:00
Nicholas Marriott
8f84217023 Put setproctitle back under HAVE_SETPROCTITLE. 2010-10-24 19:54:41 +00:00
Tiago Cunha
83447580b1 Remove redundant preprocessor check (it's already taken care of by tmux.h). 2010-10-24 01:55:21 +00:00
Tiago Cunha
1cd9ff4460 Ugh. Don't know how this happened, but make it compile by removing duplicate
code.
2010-10-24 01:51:34 +00:00
Tiago Cunha
cd079e8fbf Sync OpenBSD patchset 780:
Add a last-pane command (bound to ; by default). Requested ages ago by
somebody whose name I have forgotten.
2010-10-24 01:34:30 +00:00
Tiago Cunha
8703e9f2f9 Sync OpenBSD patchset 779:
When removing a pane, don't change the active pane unless the active
pane is actually the one being removed.
2010-10-24 01:32:35 +00:00
Tiago Cunha
2da0730f78 Sync OpenBSD patchset 778:
Mark repeating keys with "(repeat)" in the key list.
2010-10-24 01:31:57 +00:00
Tiago Cunha
bdbd4e28c2 Sync OpenBSD patchset 777:
Merge the before and after attach client code into one in client.c
(instead of two in tmux.c and client.c).
2010-10-24 01:31:08 +00:00
Tiago Cunha
1276ea3653 Zap paths.h compat include. 2010-10-24 00:50:11 +00:00
Tiago Cunha
f79b467dce Sync OpenBSD patchset 776:
Make stdio blocking again before calling shell command with -c.
2010-10-24 00:47:46 +00:00
Tiago Cunha
d4b58c71a2 Sync OpenBSD patchset 775:
Trying to set FD_CLOEXEC on every fd is a lost cause, just use
closefrom() before exec.
2010-10-24 00:45:57 +00:00
Tiago Cunha
5fb4f8c1fa Helper script which greps for compat includes that shouldn't be present in all files but compat.h. 2010-10-24 00:42:04 +00:00
Tiago Cunha
5a0ecc5931 Sync OpenBSD patchset 774:
Fall back on normal session choice method if $TMUX exists but is invalid
rather than rejecting.
2010-10-24 00:32:35 +00:00
Tiago Cunha
e7a4b68f73 Sync OpenBSD patchset 773:
Use an explicit event rather than event_once for the main event so it
can be removed when the client becomes ready.
2010-10-24 00:31:57 +00:00
Tiago Cunha
a7e5092bd4 Sync OpenBSD patchset 772:
Treat the meta bit in the xterm extended modifier key set as the same as
escape (tmux's meta). From Emanuele Giaquinta.
2010-10-24 00:30:51 +00:00
Tiago Cunha
b73ac822fb Sync OpenBSD patchset 771:
Put "or" on new line from command with .Ic.
2010-10-24 00:29:57 +00:00
Nicholas Marriott
0ad532d9c2 Rewrite the screen vs tmux bit to be more accurate and complete and less
subjective.
2010-10-23 14:09:29 +00:00
Nicholas Marriott
b0ad6e94bb +ttyfast. 2010-10-18 19:01:07 +00:00
Tiago Cunha
b2ae7c6261 Sync OpenBSD patchset 770:
Set cause when failing due to linking a window to itself, from Martin
Pieuchot.
2010-10-09 14:31:50 +00:00
Tiago Cunha
6c9269baa7 Sync OpenBSD patchset 769:
Skip NULL entries in the sessions list when choosing the next session,
from Simon Olofsson.
2010-10-09 14:31:14 +00:00
Tiago Cunha
45784bd5d6 Sync OpenBSD patchset 767:
Nuke a leftover RB tree declaration spotted by blambert.
2010-10-09 14:30:26 +00:00
Tiago Cunha
b5349ab5d9 Sync OpenBSD patchset 766:
Two new options:

- server option "exit-unattached" makes the server exit when no clients
  are attached, even if sessions are present;

- session option "destroy-unattached" destroys a session once no clients
  are attached to it.

These are useful for preventing tmux remaining in the background where
it is undesirable and when using tmux as a login shell to keep a limit
on new sessions.
2010-10-09 14:29:32 +00:00
Tiago Cunha
7874b00d4c Sync OpenBSD patchset 765:
Modify the permissions on the socket when adding or removing +x to show
attached sessions, rather than replacing them.
2010-10-09 14:26:29 +00:00
Tiago Cunha
6139fac10d Sync OpenBSD patchset 764:
detach-on-destroy is a session option, not server.
2010-10-09 14:25:40 +00:00
Nicholas Marriott
d0d98d4ec0 done 2010-09-25 20:21:51 +00:00
Tiago Cunha
29f04400b5 Sync OpenBSD patchset 763:
Ugh. Pass the right type into tty_term_has. Teaches me to make last
minute changes :-/.
2010-09-18 15:45:03 +00:00
Tiago Cunha
5126037ea0 Sync OpenBSD patchset 762:
Use UTF-8 line drawing characters on UTF-8 terminals. Fixes some stupid
terminals (I'm looking at you, putty) which disable the vt100 ACS mode
switching sequences in UTF-8 mode.

Also on terminals without ACS at all, use ASCII equivalents where
obvious.
2010-09-18 15:43:53 +00:00
Tiago Cunha
d7a3fc3df4 Sync OpenBSD patchset 761:
Ignore terminal overrides settings without a value.
2010-09-18 15:41:50 +00:00
Tiago Cunha
59dc08a7a0 Sync OpenBSD patchset 760:
When resizing the copy mode screen, don't allow it to end up with the
viewable position beyond the size of the history.
2010-09-18 15:41:18 +00:00
Nicholas Marriott
fe3621cbc5 Update, from Daniel Thau. 2010-09-18 09:36:15 +00:00
Tiago Cunha
89c07dedd9 Sync OpenBSD patchset 759:
Add -n and -p flags to switch-client to move to the next and previous
session (yes, it doesn't match window/pane, but so what, nor does
switch-client).

Based on a diff long ago from "edsouza".
2010-09-10 13:36:17 +00:00
Tiago Cunha
28a0b6fd20 Sync OpenBSD patchset 758:
Do not crash if the screen size is too small for the indicator in copy mode.
2010-09-10 13:34:12 +00:00
Nicholas Marriott
f54482a461 Solaris 9 has no stdint.h, ugh. Reported by a couple of people most
recently Timothy Larson.
2010-09-07 19:32:58 +00:00
Tiago Cunha
afcc29a51d Sync OpenBSD patchset 757:
Simplify xterm modifier detection by treating as a bitmask + 1. Spotted
by and diff from Emanuele Giaquinta.
2010-09-07 13:21:18 +00:00
Tiago Cunha
33df467d40 Sync OpenBSD patchset 756:
Reset running jobs when the status line is enabled or disabled as well,
some people have it bound to a key.
2010-09-07 13:20:28 +00:00
Tiago Cunha
510b43569f Sync OpenBSD patchset 755:
Add missing prototype.
2010-09-07 13:19:53 +00:00
Nicholas Marriott
a8b22d3673 +. 2010-09-01 20:39:32 +00:00
Nicholas Marriott
19923625d4 +. 2010-09-01 20:37:20 +00:00
Nicholas Marriott
3b4d26d16b +. 2010-08-31 22:44:01 +00:00
Tiago Cunha
01052ca38e Sync OpenBSD patchset 754:
When destroying a pane, reset any mode (which reenables pane
bufferevent) before freeing the bufferevent.
2010-08-29 14:46:13 +00:00
Tiago Cunha
ee44a8dca9 Sync OpenBSD patchset 753:
Can't call event_del() without event_set() first - so call event_set()
when setting up the client.
2010-08-29 14:44:55 +00:00
Tiago Cunha
89acd757d0 Sync OpenBSD patchset 752:
MSG_EXIT can now have a return code in the message, so check for that
size as well. Stops the client fatal()ing on exit.
2010-08-29 14:43:45 +00:00
Tiago Cunha
56040be346 Sync OpenBSD patchset 751:
Do not call event_del() for signals after fork(), just use sigaction()
directly instead - calling libevent functions after fork() w/o
event_reinit() is a bad idea, even if in this case it was harmless.
2010-08-29 14:42:11 +00:00
Tiago Cunha
e6bb3d6942 Sync OpenBSD patchset 750:
Do not need to dup() the tty fd sent from the client because it is
already dup()d again later. Fixes a leak seen by espie@.
2010-08-29 14:39:45 +00:00
Nicholas Marriott
aba555509e +-. 2010-08-19 18:04:56 +00:00
Tiago Cunha
c50c4ec834 Sync OpenBSD patchset 749:
Do not allow duplicate session names to be created, reported by Dominik
Honnef, patch from Thomas Adam.
2010-08-11 22:19:03 +00:00
Tiago Cunha
9858071dd0 Sync OpenBSD patchset 748:
Handle failure to change mode, to avoid dying when switching into copy
mode when already in a different mode. Reported by "Florian".
2010-08-11 22:18:28 +00:00
Tiago Cunha
b0169d9b84 Sync OpenBSD patchset 747:
Usage string fixes from Ben Boeckel.
2010-08-11 22:17:32 +00:00
Tiago Cunha
e34c6e2305 Sync OpenBSD patchset 746:
Treat trying to link or move to the same window as an error to avoid
removing it accidentally.
2010-08-11 22:16:43 +00:00
Tiago Cunha
761bd3c9e3 Sync OpenBSD patchset 745:
Change the way backoff works. Instead of stopping reading from the pty
when the client tty backs up too much, just stop updating the tty and
only update the internal screen. Then when the tty recovers, force a
redraw.

This prevents a dodgy client from causing other clients to go into
backoff while still allowing tmux to be responsive (locally) when seeing
lots of output.
2010-08-11 22:16:04 +00:00
Tiago Cunha
5e9429e2d6 Sync OpenBSD patchset 744:
Show which pane is active in the list-panes output, suggested by Dominik
Honnef.
2010-08-11 22:14:23 +00:00
Tiago Cunha
4387db506f Sync OpenBSD patchset 742:
dup() the stdin fd so it isn't closed twice (once for stdin, once for tty).
2010-08-09 21:45:37 +00:00
Tiago Cunha
d7bae0edce Sync OpenBSD patchset 741:
When changing so that the client passes its stdout and stderr as well as
stdin up to the server, I forgot one essential point - the tmux server
could now be both the producer and consumer. This happens when tmux is
run inside tmux, as well as when piping tmux commands together.

So, using stdio(3) was a bad idea - if sufficient data was written, this
could block in write(2). When that happened and the server was both
producer and consumer, it deadlocks.

Change to use libevent bufferevents for the client stdin, stdout and
stderr instead. This is trivial enough for output but requires a
callback mechanism to trigger when stdin is finished.

This relies on the underlying polling mechanism for libevent to work
with whatever devices to which the user could redirect stdin, stdout or
stderr, hence the change to use poll(2) over kqueue(2) for tmux.
2010-08-09 21:44:25 +00:00
Nicholas Marriott
482bd7b65e Basic GNU bash completion from Frank Barknecht. 2010-08-09 18:22:33 +00:00
Nicholas Marriott
f42364b4b5 How to blank on lock, from Thomas Adam, slightly tweaked by me. 2010-07-31 11:46:28 +00:00
Nicholas Marriott
5be8175b0b No more up-pane/down-pane. 2010-07-31 11:39:13 +00:00
Tiago Cunha
5576fe42b2 Sync with reality, per SF bug #3035214. 2010-07-27 18:29:07 +00:00
Micah Cowan
1944747759 Don't enable/disable invalid event pointers. Fixes SF #3033119. 2010-07-22 19:51:48 +00:00
Tiago Cunha
28c4c86589 Sync OpenBSD patchset 738:
Don't return if in the current window since we may want to report a bell
(if bell-action any/current), just clear the flag so the status line
doesn't show the bell.
2010-07-20 17:36:41 +00:00
Tiago Cunha
7ed9b0f8fb Sync OpenBSD patchset 737:
Send the \n to stdout with the message, not stderr... doh.
2010-07-20 17:36:03 +00:00
Nicholas Marriott
11497af4dd Solaris has MAXHOSTNAMELEN in netdb.h. 2010-07-19 18:31:42 +00:00
Tiago Cunha
b0878774e0 Don't hard-code the username. 2010-07-19 13:57:22 +00:00
Tiago Cunha
e71f0842ce Working on 1.4. 2010-07-18 14:53:27 +00:00
71 changed files with 2335 additions and 1197 deletions

43
CHANGES
View File

@@ -1,3 +1,44 @@
CHANGES FROM 1.3 TO 1.4, 27 December 2010
* Window bell reporting fixed.
* Show which pane is active in the list-panes output.
* Backoff reworked.
* Prevent the server from dying when switching into copy mode when already
in a different mode.
* Reset running jobs when the status line is enabled or disabled.
* Simplify xterm modifier detection.
* Avoid crashing in copy mode if the screen size is too small for the
indicator.
* Flags -n and -p added to switch-client.
* Use UTF-8 line drawing characters on UTF-8 terminals, thus fixing some
terminals (eg putty) which disable the vt100 ACS mode switching sequences
in UTF-8 mode. On terminals without ACS, use ASCII equivalents.
* New server option exit-unattached added.
* New session option destroy-unattached added.
* Fall back on normal session choice method if $TMUX exists but is invalid
rather than rejecting.
* Mark repeating keys with "(repeat)" in the key list.
* When removing a pane, don't change the active pane unless the active pane
is actually the one being removed.
* New command last-pane added.
* AIX fixes.
* Flag -a added to unbind-key.
* Add XAUTHORITY to update-environment.
* More info regarding window and pane flags is now shown in list-*.
* If VISUAL or EDITOR contains "vi" configure mode-keys and status-key to vi.
* New window option monitor-silence and session option visual-silence added.
* In the built-in layouts distribute the panes more evenly.
* Set the default value of main-pane-width to 80 instead of 81.
* Command-line flag -V added.
* Instead of keeping a per-client prompt history make it global.
* Fix rectangle copy to behave like emacs (the cursor is not part of the
selection on the right edge but on the left it is).
* Flag -l added to switch-client.
* Retrieve environment variables from the global environment rather than
getenv(3), thus allowing them to be updated during the configuration file.
* New window options other-pane-{height,width} added.
* More minor bugs fixed and manpage improvements.
CHANGES FROM 1.2 TO 1.3, 18 July 2010 CHANGES FROM 1.2 TO 1.3, 18 July 2010
* New input parser. * New input parser.
@@ -1513,7 +1554,7 @@ The list of older changes is below.
(including mutt, emacs). No status bar yet and no key remapping or other (including mutt, emacs). No status bar yet and no key remapping or other
customisation. customisation.
$Id: CHANGES,v 1.303 2010-07-18 13:40:59 tcunha Exp $ $Id: CHANGES,v 1.304 2010-12-27 21:37:42 tcunha Exp $
LocalWords: showw utf UTF fulvio ciriaco joshe OSC APC gettime abc DEF OA clr LocalWords: showw utf UTF fulvio ciriaco joshe OSC APC gettime abc DEF OA clr
LocalWords: rivo nurges lscm Erdely eol smysession mysession ek dstname RB ms LocalWords: rivo nurges lscm Erdely eol smysession mysession ek dstname RB ms

173
FAQ
View File

@@ -12,29 +12,99 @@ tmux frequently asked questions
* and derivatives. * * and derivatives. *
****************************************************************************** ******************************************************************************
* How is tmux different from GNU screen? What else does it offer? * How is tmux different from GNU screen?
tmux offers several advantages over screen: tmux and GNU screen have many similarities. Some of the main differences I am
aware of are (bearing in mind I haven't used screen for a few years now):
- a clearly-defined client-server model: windows are independent entities which - tmux uses a client-server model. Each server has single Unix domain socket in
may be attached simultaneously to multiple sessions and viewed from multiple /tmp and within one server there are multiple sessions which may be attached
clients (terminals), as well as moved freely between sessions within the same to multiple clients (terminals).
tmux server;
- a consistent, well-documented command interface, with the same syntax
whether used interactively, as a key binding, or from the shell;
- easily scriptable from the shell;
- multiple paste buffers;
- choice of vi or emacs key layouts;
- an option to limit the window size;
- a more usable status line syntax, with the ability to display the first line
of output of a specific command;
- a cleaner, modern, easily extended, BSD-licensed codebase.
There are still a few features screen includes that tmux omits: This has advantages, notably: windows may be linked simultaneously to
multiple sessions; windows may be moved freely between sessions; and a client
may be switched between sessions easily (C-b D). There is one major
disadvantage: if the server crashes, game over, all sessions die. In
practice, however, tmux is quite stable and gets more so as people report any
bugs they hit :-).
- builtin serial and telnet support; this is bloat and is unlikely to be added This model is different from screen, where typically each new screen instance
to tmux; is independent. tmux supports the same behaviour by using multiple servers
- wider platform support, for example IRIX and HP-UX, and for odd terminals. with the -L option but it is not typically recommended.
- Different command interfaces. One of the goals of tmux is that the shell
should be easily usable as a scripting language - almost all tmux commands
can be used from the shell and behave identically whether used from the
shell, from a key binding or from the command prompt. Personally I also find
tmux's command interface much more consistent and clearer, but this is
subjective.
- tmux calls window names (what you see in the status line) "names", screen
calls them "titles".
- tmux has a multiple paste buffers. Not a major one but comes in handy quite a
lot.
- tmux supports automatically renaming windows to the running application
without gross hacks using escape sequences. Its even on by default.
- tmux has a choice of vi or emacs key layouts. Again, not major, but I use
emacs so if tmux did support only one key set it would be emacs and then all
the vi users would get humpy. Key bindings may be completely reconfigured in
any case.
- tmux has an option to limit the window size.
- tmux has search in windows (C-b f).
- The window split (pane) model is different. tmux has two objects, windows and
panes; screen has just windows. This difference has several implications:
* In screen you can have a window appear in several layouts, in tmux a pane
can only be in one window (fixing this is a big todo item but quite
invasive).
* tmux layouts are immutable and do not get changed unless you modify them.
* In tmux, all panes are closed when you kill a window.
* tmux panes do not have individual names, titles and so on.
I think tmux's model is much easier to manage and navigate within a window,
but breaking panes off from and joining them to windows is more clumsy.
tmux also has support for preset pane layouts.
- tmux's status line syntax is more readable and easier to use. I think it'd be
hard for anyone to argue with this. tmux doesn't support running a command
constantly and always using the last line of its output, commands must be run
again each time.
- tmux has modern, easily extended code. Again hard to argue screen is better
if you have looked at the code.
- tmux depends on libevent. I don't see this as a disadvantage: libevent is
small and portable, and on modern systems with current package management
systems dependencies are not an issue. libevent brings advantages in code
simplicity and performance.
- screen allows the window to be bigger than the terminal and can pan around
it. tmux limits the size to the largest attached client. This is a big todo
item for tmux but it is not trivial.
- screen has builtin serial and telnet support; this is bloat and is unlikely
to be added to tmux.
- screen has support for updating utmp. Nobody has really come up with a clean,
portable way to do this without making tmux setuid or setgid yet.
- Environment handling is different.
- tmux tends to be more demanding on the terminal so tends to show up terminal
and application bugs which screen does not.
- screen has wider platform support, for example IRIX and HP-UX, and for odd
terminals.
* I found a bug! What do I do? * I found a bug! What do I do?
@@ -167,6 +237,8 @@ the ctrl (bit 5 set, for example ^[[5~ to ^[[5^) modifier in non-xterm(1) mode;
it may be possible to configure vim to accept these, an example of how to do so it may be possible to configure vim to accept these, an example of how to do so
would be welcome. would be welcome.
vim users may also want to set the "ttyfast" option inside tmux.
* Why doesn't elinks set the window title inside tmux? * Why doesn't elinks set the window title inside tmux?
There isn't a way to detect if a terminal supports setting the window title, so There isn't a way to detect if a terminal supports setting the window title, so
@@ -239,4 +311,65 @@ set -g terminal-overrides "xterm*:kLFT5=\eOD:kRIT5=\eOC:kUP5=\eOA:kDN5=\eOB:smkx
Note that this will only work in tmux 1.2 and above. Note that this will only work in tmux 1.2 and above.
$Id: FAQ,v 1.36 2010-02-04 21:01:59 nicm Exp $ * How can I blank the tmux window?
GNU screen has a feature whereby it will blank the screen after a period of
inactivity. To do the same thing in tmux, use the lock-command setting, for
example (with GNU bash):
set -g lock-command 'tput civis && read -s -n1'
This will remove the cursor and tell the shell to quit once a key has been
pressed. For zsh, use "read -s -k1".
In addition, it's possible to have both blanking and locking (for instance via
lock(1) or vlock(1)) by using the following:
bind x set lock-command '/usr/bin/vlock' \; lock-client \; set lock-command 'tput civis && read -s -n1'
* How can I open a new window in the same directory as the current window?
One option is to just run "TMUX= tmux" in the window. However, this only works if no
command is running, so that you can input the command.
A workaround is to let tmux know about the current path through an environment
variable. To do so, use the following command:
[ -n "$TMUX" ] && tmux setenv TMUXPWD_$(tmux display -p "#I") $PWD
Which sets TMUXPWD_i (where i is the number of the current window) to the path
of the current directory. This command can be added to PS1, for example:
PS1='$([ -n "$TMUX" ] && tmux setenv TMUXPWD_$(tmux display -p "#I") $PWD)\h$ '
When a new window is created, the shell should be asked to change
directory. You can define a new binding (for example, if using GNU bash):
bind-key C-c run-shell 'tmux neww "cd $(tmux display -p "\$TMUXPWD_#I"); exec bash"'
This solution will work even if a command is currently running in the terminal,
but it will not work from a window that has just been swapped with another
because TMUXPWD_i will not be updated after a swap. However, once a new prompt
is displayed, TMUXPWD_i is updated properly.
* tmux doesn't start with "daemon failed"
tmux shows something similar to this when started:
fatal: server_start: daemon failed: No such file or directory
fatal: main_dispatch: imsg_read failed
A possible reason is that /dev/null is not a character device or is otherwise
inaccessible.
Check with:
file /dev/null
ls -l /dev/null
If it is not a character device or has incorrect permissions, it can typically
be recreated with:
cd /dev && rm null && ./MAKEDEV null
$Id: FAQ,v 1.41 2010-12-15 23:31:30 nicm Exp $

View File

@@ -1,4 +1,4 @@
# $Id: GNUmakefile,v 1.128 2010-07-18 13:36:52 tcunha Exp $ # $Id: GNUmakefile,v 1.130 2010-12-27 21:32:16 tcunha Exp $
# #
# Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net> # Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
# #
@@ -17,7 +17,7 @@
.PHONY: clean .PHONY: clean
VERSION= 1.3 VERSION= 1.4
#FDEBUG= 1 #FDEBUG= 1

View File

@@ -1,4 +1,4 @@
# $Id: Makefile,v 1.160 2010-07-18 13:36:52 tcunha Exp $ # $Id: Makefile,v 1.162 2010-12-27 21:32:16 tcunha Exp $
# #
# Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net> # Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
# #
@@ -18,7 +18,7 @@
.SUFFIXES: .c .o .SUFFIXES: .c .o
.PHONY: clean .PHONY: clean
VERSION= 1.3 VERSION= 1.4
#FDEBUG= 1 #FDEBUG= 1

25
NOTES
View File

@@ -25,34 +25,11 @@ windows into several simultaneously displayed panes; and to bind and unbind
command keys (invoked preceded by a prefix key, by default ctrl-b). Please see command keys (invoked preceded by a prefix key, by default ctrl-b). Please see
the tmux(1) man page for further information. the tmux(1) man page for further information.
The following is a summary of major features implemented in this version:
- Basic multiplexing, window switching, attaching and detaching.
- Window listing and renaming.
- Key binding.
- Handling of client terminal resize.
- Terminal emulation sufficient to handle most curses applications.
- A optional status line (enabled by default).
- Window history and copy and paste.
- Support for VT100 line drawing characters.
- A large command set.
- Vertical window splitting and layout.
- Automatic server locking on inactivity by running an external command.
- A configuration file.
- UTF-8 support.
A more extensive, but rough, todo list is included in the TODO file. A more extensive, but rough, todo list is included in the TODO file.
tmux also depends on several features of the client terminal (TERM), if these tmux also depends on several features of the client terminal (TERM), if these
are missing it may refuse to run, or not behave correctly. are missing it may refuse to run, or not behave correctly.
tmux supports UTF-8. To use it, the utf8 option must be set on each window;
this may be turned on for all windows by setting it as a global option, see
tmux(1) and the FAQ file. As of 0.9, tmux attempts to autodetect a
UTF-8 capable terminal by checking the LC_ALL, LC_CTYPE and LANG environment
variables. list-clients may be used to check if this is detected correctly; if
not, the -u command-line flag may be specified.
A Vim syntax file is available in the examples directory. To install it: A Vim syntax file is available in the examples directory. To install it:
- Drop the file in the syntax directory in your runtimepath (such as - Drop the file in the syntax directory in your runtimepath (such as
@@ -86,4 +63,4 @@ start. Please contact me with any queries.
-- Nicholas Marriott <nicm@users.sf.net> -- Nicholas Marriott <nicm@users.sf.net>
$Id: NOTES,v 1.53 2010-03-10 15:18:11 tcunha Exp $ $Id: NOTES,v 1.54 2010-12-27 21:36:37 tcunha Exp $

88
TODO
View File

@@ -1,4 +1,3 @@
- window creation/idle time
- better errors when creating new windows/sessions (how?) - better errors when creating new windows/sessions (how?)
- implicitly add exec to the commands for new windows (switch to disable it)? - implicitly add exec to the commands for new windows (switch to disable it)?
- it would be nice to have multichar commands eg C-b K K - it would be nice to have multichar commands eg C-b K K
@@ -7,26 +6,18 @@
bring back detach-session to detach all clients on a session? bring back detach-session to detach all clients on a session?
- allow fnmatch for -c, so that you can, eg, detach all clients - allow fnmatch for -c, so that you can, eg, detach all clients
- garbage collect window history (100 lines at a time?) if it hasn't been used - garbage collect window history (100 lines at a time?) if it hasn't been used
in $x time (need window creation/use times) in $x time
- lift SHRT_MAX limits for history? - lift SHRT_MAX limits for history?
- flags to centre screen in window - flags to centre screen in window
- better terminal emulation
- activity/bell should be per-window not per-link? what if it is cur win in - activity/bell should be per-window not per-link? what if it is cur win in
session not being watched? session not being watched?
- next prev word etc in command prompt - next prev word etc in command prompt
- many more info() displays for various things
- use a better termcap internally instead of screen, perhaps xterm - use a better termcap internally instead of screen, perhaps xterm
- fix rxvt cursor fg issue (text under cursor can have non-white fg)
- should be able to move to a hidden pane and it would be moved into view. pane - should be able to move to a hidden pane and it would be moved into view. pane
number in status line/top-right would be cool for this number in status line/top-right would be cool for this
- support other mouse modes (highlight etc) and use it in copy mode - support other mouse modes (highlight etc) and use it in copy mode
- set-remain-on-exit is a bit of a hack, some way to do it generically? - set-remain-on-exit is a bit of a hack, some way to do it generically?
- set-option should be set-session-option and should be overall global options
also quiet, utf8 and maybe other flags?
-g is a bit unexpected in conf file
- clear window title on exit - clear window title on exit
- the output code (tty.c) could do with optimisation depending on term
capabilities
- would be nice to be able to use "--" to mark start of command w/ neww etc - would be nice to be able to use "--" to mark start of command w/ neww etc
to avoid quoting to avoid quoting
- make command sequences more usable: don't require space after ;, handle - make command sequences more usable: don't require space after ;, handle
@@ -37,11 +28,7 @@
"new-session" if-shell "[ -e $HOME/.tmux-session.conf ]" source-file "new-session" if-shell "[ -e $HOME/.tmux-session.conf ]" source-file
$HOME/.tmux-session.conf $HOME/.tmux-session.conf
- get it passing all the vttest tests that don't require resizing the terminal - get it passing all the vttest tests that don't require resizing the terminal
- esc seq to set window name and title should be documented, and name variant
should clear automatic-rename
- way to set socket path from config file - way to set socket path from config file
- XXX once env stuff is in, default-path and VISUAL/EDITOR should be picked up
when session is started
- what about utmp etc? can tmux update it like screen? setgid? - what about utmp etc? can tmux update it like screen? setgid?
- warts on current naming: - warts on current naming:
- display-time but message-fg/bg/attr - display-time but message-fg/bg/attr
@@ -49,46 +36,30 @@
- server-info - server-info
- up-pane/down-pane/swap-pane -U/swap-pane -D vs next-*/previous-* - up-pane/down-pane/swap-pane -U/swap-pane -D vs next-*/previous-*
- split-window -> split-pane?? - split-window -> split-pane??
- tidy up and prioritise todo list ;-)
- neww and attach can create a session if none exists?
would work fine with config file since
- a way for force-width/height to apply to only one pane (how?) - a way for force-width/height to apply to only one pane (how?)
- command to list what is actually running in each window with command line, - command to list what is actually running in each window with command line,
pid (need some adaption of the osdep code) pid (need some adaption of the osdep code)
- support for bce - support for bce
- it would be nice if the start/end line keys in copy mode were aware of
wrapped lines
- some way to force a screen to use the entire terminal even if it is forced - some way to force a screen to use the entire terminal even if it is forced
to be smaller by other clients. pan smaller terminal? (like screen F) to be smaller by other clients. pan smaller terminal? (like screen F)
-- idea of a "view" onto a window, need base x/y offsets -- idea of a "view" onto a window, need base x/y offsets for redraw
for redraw
- handle resize better in copy mode - handle resize better in copy mode
- way to copy stuff that is off screen due to resize - way to copy stuff that is off screen due to resize
- commands should be able to succeed or fail and have || or && for command - commands should be able to succeed or fail and have || or && for command
lists lists
- support the mouse wheel to scroll through history
- some way to KEEP a command running continually and just use its LAST line of - some way to KEEP a command running continually and just use its LAST line of
output output
- bind commands to mouse buttons - bind commands to mouse buttons
- UTF-8 to a non-UTF-8 terminal should not be able to balls up - UTF-8 to a non-UTF-8 terminal should not be able to balls up
the terminal - www/ruby-addressable; make regress the terminal - www/ruby-addressable; make regress
- copy mode needs a tidy/cleanup
- ability to save history (to buffer?)
- multiple keys could be done with tables, ie have prefixes go and instead - multiple keys could be done with tables, ie have prefixes go and instead
bind -n ^A prefix-table "default" bind -n ^A prefix-table "default"
where prefix-table sets command lookup table and sets prefix flag, then next where prefix-table sets command lookup table and sets prefix flag, then next
key is looked up in that table key is looked up in that table
- UTF-8 should be a pointer into a combined string buffer
- check irssi (term_charset) works with UTF-8
- support esc-esc to quit in modes - support esc-esc to quit in modes
- fix ctrl+F1-F4 output. to what? - fix ctrl+F1-F4 output. to what?
- look into xterm clearing its selection when scrolling etc - look into xterm clearing its selection when scrolling etc
- better utf8 support: - better utf8 support: window names, prompt input, message display
window names
prompt input
message display
copy and paste cursor and wide characters
...?
- session history for client and last-session command - session history for client and last-session command
- option to change status line colour when current window is in a mode? - option to change status line colour when current window is in a mode?
- option to move copy mode indicator into status line - option to move copy mode indicator into status line
@@ -96,7 +67,6 @@
- selection behaviour closer to vi in vi mode - selection behaviour closer to vi in vi mode
- live update: server started with -U connects to server, requests sessions and - live update: server started with -U connects to server, requests sessions and
windows, receives fds windows, receives fds
- convert status line history to be server global (anything else?)
- command to show a tree of sessions-windows-panes (active marked with *) - command to show a tree of sessions-windows-panes (active marked with *)
- sort out inheriting config from shell on new sessions/windows: - sort out inheriting config from shell on new sessions/windows:
should pick up default-path/termios/etc from client if possible, should pick up default-path/termios/etc from client if possible,
@@ -110,7 +80,7 @@
- better session sharing: create-socket command to create socket somewhere (-r - better session sharing: create-socket command to create socket somewhere (-r
flag for readonly) flag for readonly)
- allow buffer to be specified when copying in copy mode - allow buffer to be specified when copying in copy mode
- multiline status line - multiline status line (no?)
- flag for absolute pane size to resize-pane - flag for absolute pane size to resize-pane
- sanity check input to socket - sanity check input to socket
- select-buffer command - select-buffer command
@@ -128,25 +98,33 @@
- ' and " should be parsed the same (eg "\e" vs '\e') in config and command - ' and " should be parsed the same (eg "\e" vs '\e') in config and command
prompt? prompt?
- command to toggle selection not to move it in copy-mode - command to toggle selection not to move it in copy-mode
- why are alerts per-winlink? try per window?
For 1.3 (not in order): - audit of escape sequence support vs xterm
- support binding keys to mouse (mouse-select-pane -> mouse-keys or something,
3 why are alerts per-winlink? try per window? mouse click == select-pane -t %%, mouse scroll up == copy-mode)
4 audit of escape sequence support vs xterm - something for -t "last window in session" so a session can be used as a stack
6 rectangle copy: when selecting leftward, cursor should be inside block per - synchronous commands - client sends cmd and blocks, neww/splitw saves client
emacs key to rotate corner at which cursor is ptr then when program inside died, sends MSG_SOMETHING with wait status to
9 something for -t "last window in session" so a session can be used as a stack client
10 synchronous commands - client sends cmd and blocks, neww/splitw saves client - documentation improvements - rlpowell's tutorial - build instructions
ptr then when program inside died, sends MSG_SOMETHING with wait status to - better configure? with-libevent
client - bind commands to key sequences?
11 documentation improvements - rlpowell's tutorial - monitor, bell etc should monitor /all/ panes in the window not just one
- build instructions - a history of commands that can be reversed (reverse member of each command,
12 better configure? with-libevent and a buffer) info() when changing to same window
14 bind commands to key sequences? - don't pass UTF-8 through vis for titles
16 monitor, bell etc should monitor /all/ panes in the window not just one - clearing screen should push lines into history
17 wd should be updated on attach etc. maybe lose default-path and use PWD - add a unique ever-increasing pane id to each pane, export it in $TMUX_PANE
18 a history of commands that can be reversed (reverse member of each command, and a buffer) (as %1, %2 etc) and allow it to be used as a target
19 info() when changing to same window - way to add dest for break-pane; maybe some easier way to unbreak-pane
20 don't pass UTF-8 through vis for titles - case insensitive searching
... - dynamically generated jobs (eg "date ...") do not work well because
their entries are never collected, should either store status jobs in
a different tree or flush all unused persist jobs every update rather
than just updating them
- pane-index option like base-index
- option to move status line to top
- support "xterm2" mouse mode
- respawn-pane command
- configurable borders and empty space filler for when panes < window?

349
client.c
View File

@@ -1,4 +1,4 @@
/* $Id: client.c,v 1.95 2010-07-02 02:52:13 tcunha Exp $ */ /* $Id: client.c,v 1.100 2010-10-24 19:54:41 nicm Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -28,7 +28,6 @@
#include <pwd.h> #include <pwd.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <syslog.h>
#include <unistd.h> #include <unistd.h>
#include "tmux.h" #include "tmux.h"
@@ -37,82 +36,173 @@ struct imsgbuf client_ibuf;
struct event client_event; struct event client_event;
const char *client_exitmsg; const char *client_exitmsg;
int client_exitval; int client_exitval;
int client_attached;
int client_connect(char *, int);
void client_send_identify(int); void client_send_identify(int);
void client_send_environ(void); void client_send_environ(void);
void client_write_server(enum msgtype, void *, size_t); void client_write_server(enum msgtype, void *, size_t);
void client_update_event(void); void client_update_event(void);
void client_signal(int, short, void *); void client_signal(int, short, void *);
void client_callback(int, short, void *); void client_callback(int, short, void *);
int client_dispatch(void); int client_dispatch_attached(void);
int client_dispatch_wait(void *);
struct imsgbuf * /* Connect client to server. */
client_init(char *path, int cmdflags, int flags) int
client_connect(char *path, int start_server)
{ {
struct sockaddr_un sa; struct sockaddr_un sa;
size_t size; size_t size;
int fd, mode; int fd, mode;
#ifdef HAVE_SETPROCTITLE
char rpathbuf[MAXPATHLEN];
#endif
#ifdef HAVE_SETPROCTITLE
if (realpath(path, rpathbuf) == NULL)
strlcpy(rpathbuf, path, sizeof rpathbuf);
setproctitle("client (%s)", rpathbuf);
#endif
memset(&sa, 0, sizeof sa); memset(&sa, 0, sizeof sa);
sa.sun_family = AF_UNIX; sa.sun_family = AF_UNIX;
size = strlcpy(sa.sun_path, path, sizeof sa.sun_path); size = strlcpy(sa.sun_path, path, sizeof sa.sun_path);
if (size >= sizeof sa.sun_path) { if (size >= sizeof sa.sun_path) {
errno = ENAMETOOLONG; errno = ENAMETOOLONG;
goto not_found; return (-1);
} }
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
fatal("socket failed"); fatal("socket failed");
if (connect(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) { if (connect(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) {
if (!(cmdflags & CMD_STARTSERVER)) if (!start_server)
goto not_found; goto failed;
switch (errno) { switch (errno) {
case ECONNREFUSED: case ECONNREFUSED:
if (unlink(path) != 0) if (unlink(path) != 0)
goto not_found; goto failed;
/* FALLTHROUGH */ /* FALLTHROUGH */
case ENOENT: case ENOENT:
if ((fd = server_start(path)) == -1) if ((fd = server_start()) == -1)
goto start_failed; goto failed;
goto server_started; break;
default:
goto failed;
} }
goto not_found;
} }
server_started:
if ((mode = fcntl(fd, F_GETFL)) == -1) if ((mode = fcntl(fd, F_GETFL)) == -1)
fatal("fcntl failed"); fatal("fcntl failed");
if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1) if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
fatal("fcntl failed"); fatal("fcntl failed");
if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) return (fd);
fatal("fcntl failed");
imsg_init(&client_ibuf, fd);
failed:
close(fd);
return (-1);
}
/* Client main loop. */
int
client_main(int argc, char **argv, int flags)
{
struct cmd *cmd;
struct cmd_list *cmdlist;
struct msg_command_data cmddata;
int cmdflags, fd;
enum msgtype msg;
char *cause;
/* Set up the initial command. */
cmdflags = 0;
if (shell_cmd != NULL) {
msg = MSG_SHELL;
cmdflags = CMD_STARTSERVER;
} else if (argc == 0) {
msg = MSG_COMMAND;
cmdflags = CMD_STARTSERVER|CMD_SENDENVIRON|CMD_CANTNEST;
} else {
msg = MSG_COMMAND;
/*
* It sucks parsing the command string twice (in client and
* later in server) but it is necessary to get the start server
* flag.
*/
if ((cmdlist = cmd_list_parse(argc, argv, &cause)) == NULL) {
log_warnx("%s", cause);
return (1);
}
cmdflags &= ~CMD_STARTSERVER;
TAILQ_FOREACH(cmd, &cmdlist->list, qentry) {
if (cmd->entry->flags & CMD_STARTSERVER)
cmdflags |= CMD_STARTSERVER;
if (cmd->entry->flags & CMD_SENDENVIRON)
cmdflags |= CMD_SENDENVIRON;
if (cmd->entry->flags & CMD_CANTNEST)
cmdflags |= CMD_CANTNEST;
}
cmd_list_free(cmdlist);
}
/*
* Check if this could be a nested session, if the command can't nest:
* if the socket path matches $TMUX, this is probably the same server.
*/
if (shell_cmd == NULL && environ_path != NULL &&
cmdflags & CMD_CANTNEST && strcmp(socket_path, environ_path) == 0) {
log_warnx("sessions should be nested with care. "
"unset $TMUX to force.");
return (1);
}
/* Initialise the client socket and start the server. */
fd = client_connect(socket_path, cmdflags & CMD_STARTSERVER);
if (fd == -1) {
log_warn("failed to connect to server");
return (1);
}
/* Set process title, log and signals now this is the client. */
#ifdef HAVE_SETPROCTITLE
setproctitle("client (%s)", socket_path);
#endif
logfile("client");
/* Create imsg. */
imsg_init(&client_ibuf, fd);
event_set(&client_event, fd, EV_READ, client_callback, shell_cmd);
/* Establish signal handlers. */
set_signals(client_signal);
/* Send initial environment. */
if (cmdflags & CMD_SENDENVIRON) if (cmdflags & CMD_SENDENVIRON)
client_send_environ(); client_send_environ();
client_send_identify(flags); client_send_identify(flags);
return (&client_ibuf); /* Send first command. */
if (msg == MSG_COMMAND) {
/* Fill in command line arguments. */
cmddata.pid = environ_pid;
cmddata.idx = environ_idx;
start_failed: /* Prepare command for server. */
log_warnx("server failed to start"); cmddata.argc = argc;
return (NULL); if (cmd_pack_argv(
argc, argv, cmddata.argv, sizeof cmddata.argv) != 0) {
log_warnx("command too long");
return (1);
}
not_found: client_write_server(msg, &cmddata, sizeof cmddata);
log_warn("server not found"); } else if (msg == MSG_SHELL)
return (NULL); client_write_server(msg, NULL, 0);
/* Set the event and dispatch. */
client_update_event();
event_dispatch();
/* Print the exit message, if any, and exit. */
if (client_attached && client_exitmsg != NULL && !login_shell)
printf("[%s]\n", client_exitmsg);
return (client_exitval);
} }
/* Send identify message to server with the file descriptors. */
void void
client_send_identify(int flags) client_send_identify(int flags)
{ {
@@ -137,13 +227,16 @@ client_send_identify(int flags)
if ((fd = dup(STDOUT_FILENO)) == -1) if ((fd = dup(STDOUT_FILENO)) == -1)
fatal("dup failed"); fatal("dup failed");
imsg_compose(&client_ibuf, MSG_STDOUT, PROTOCOL_VERSION, -1, fd, NULL, 0); imsg_compose(&client_ibuf,
MSG_STDOUT, PROTOCOL_VERSION, -1, fd, NULL, 0);
if ((fd = dup(STDERR_FILENO)) == -1) if ((fd = dup(STDERR_FILENO)) == -1)
fatal("dup failed"); fatal("dup failed");
imsg_compose(&client_ibuf, MSG_STDERR, PROTOCOL_VERSION, -1, fd, NULL, 0); imsg_compose(&client_ibuf,
MSG_STDERR, PROTOCOL_VERSION, -1, fd, NULL, 0);
} }
/* Forward entire environment to server. */
void void
client_send_environ(void) client_send_environ(void)
{ {
@@ -157,12 +250,14 @@ client_send_environ(void)
} }
} }
/* Write a message to the server without a file descriptor. */
void void
client_write_server(enum msgtype type, void *buf, size_t len) client_write_server(enum msgtype type, void *buf, size_t len)
{ {
imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, -1, buf, len); imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, -1, buf, len);
} }
/* Update client event based on whether it needs to read or read and write. */
void void
client_update_event(void) client_update_event(void)
{ {
@@ -172,91 +267,74 @@ client_update_event(void)
events = EV_READ; events = EV_READ;
if (client_ibuf.w.queued > 0) if (client_ibuf.w.queued > 0)
events |= EV_WRITE; events |= EV_WRITE;
event_set(&client_event, client_ibuf.fd, events, client_callback, NULL); event_set(
&client_event, client_ibuf.fd, events, client_callback, shell_cmd);
event_add(&client_event, NULL); event_add(&client_event, NULL);
} }
__dead void /* Callback to handle signals in the client. */
client_main(void)
{
logfile("client");
/* Note: event_init() has already been called. */
/* Set up signals. */
set_signals(client_signal);
/*
* Send a resize message immediately in case the terminal size has
* changed between the identify message to the server and the MSG_READY
* telling us to move into the client code.
*/
client_write_server(MSG_RESIZE, NULL, 0);
/*
* imsg_read in the first client poll loop (before the terminal has
* been initialised) may have read messages into the buffer after the
* MSG_READY switched to here. Process anything outstanding now to
* avoid hanging waiting for messages that have already arrived.
*/
if (client_dispatch() != 0)
goto out;
/* Set the event and dispatch. */
client_update_event();
event_dispatch();
out:
/* Print the exit message, if any, and exit. */
if (client_exitmsg != NULL && !login_shell)
printf("[%s]\n", client_exitmsg);
exit(client_exitval);
}
/* ARGSUSED */ /* ARGSUSED */
void void
client_signal(int sig, unused short events, unused void *data) client_signal(int sig, unused short events, unused void *data)
{ {
struct sigaction sigact; struct sigaction sigact;
int status;
switch (sig) { if (!client_attached) {
case SIGHUP: switch (sig) {
client_exitmsg = "lost tty"; case SIGCHLD:
client_exitval = 1; waitpid(WAIT_ANY, &status, WNOHANG);
client_write_server(MSG_EXITING, NULL, 0); break;
break; case SIGTERM:
case SIGTERM: event_loopexit(NULL);
client_exitmsg = "terminated"; break;
client_exitval = 1; }
client_write_server(MSG_EXITING, NULL, 0); } else {
break; switch (sig) {
case SIGWINCH: case SIGHUP:
client_write_server(MSG_RESIZE, NULL, 0); client_exitmsg = "lost tty";
break; client_exitval = 1;
case SIGCONT: client_write_server(MSG_EXITING, NULL, 0);
memset(&sigact, 0, sizeof sigact); break;
sigemptyset(&sigact.sa_mask); case SIGTERM:
sigact.sa_flags = SA_RESTART; client_exitmsg = "terminated";
sigact.sa_handler = SIG_IGN; client_exitval = 1;
if (sigaction(SIGTSTP, &sigact, NULL) != 0) client_write_server(MSG_EXITING, NULL, 0);
fatal("sigaction failed"); break;
client_write_server(MSG_WAKEUP, NULL, 0); case SIGWINCH:
break; client_write_server(MSG_RESIZE, NULL, 0);
break;
case SIGCONT:
memset(&sigact, 0, sizeof sigact);
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = SA_RESTART;
sigact.sa_handler = SIG_IGN;
if (sigaction(SIGTSTP, &sigact, NULL) != 0)
fatal("sigaction failed");
client_write_server(MSG_WAKEUP, NULL, 0);
break;
}
} }
client_update_event(); client_update_event();
} }
/* Callback for client imsg read events. */
/* ARGSUSED */ /* ARGSUSED */
void void
client_callback(unused int fd, short events, unused void *data) client_callback(unused int fd, short events, void *data)
{ {
ssize_t n; ssize_t n;
int retval;
if (events & EV_READ) { if (events & EV_READ) {
if ((n = imsg_read(&client_ibuf)) == -1 || n == 0) if ((n = imsg_read(&client_ibuf)) == -1 || n == 0)
goto lost_server; goto lost_server;
if (client_dispatch() != 0) { if (client_attached)
retval = client_dispatch_attached();
else
retval = client_dispatch_wait(data);
if (retval != 0) {
event_loopexit(NULL); event_loopexit(NULL);
return; return;
} }
@@ -276,8 +354,76 @@ lost_server:
event_loopexit(NULL); event_loopexit(NULL);
} }
/* Dispatch imsgs when in wait state (before MSG_READY). */
int int
client_dispatch(void) client_dispatch_wait(void *data)
{
struct imsg imsg;
ssize_t n, datalen;
struct msg_shell_data shelldata;
struct msg_exit_data exitdata;
const char *shellcmd = data;
if ((n = imsg_read(&client_ibuf)) == -1 || n == 0)
fatalx("imsg_read failed");
for (;;) {
if ((n = imsg_get(&client_ibuf, &imsg)) == -1)
fatalx("imsg_get failed");
if (n == 0)
return (0);
datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
switch (imsg.hdr.type) {
case MSG_EXIT:
case MSG_SHUTDOWN:
if (datalen != sizeof exitdata) {
if (datalen != 0)
fatalx("bad MSG_EXIT size");
} else {
memcpy(&exitdata, imsg.data, sizeof exitdata);
client_exitval = exitdata.retcode;
}
imsg_free(&imsg);
return (-1);
case MSG_READY:
if (datalen != 0)
fatalx("bad MSG_READY size");
client_attached = 1;
break;
case MSG_VERSION:
if (datalen != 0)
fatalx("bad MSG_VERSION size");
log_warnx("protocol version mismatch (client %u, "
"server %u)", PROTOCOL_VERSION, imsg.hdr.peerid);
client_exitval = 1;
imsg_free(&imsg);
return (-1);
case MSG_SHELL:
if (datalen != sizeof shelldata)
fatalx("bad MSG_SHELL size");
memcpy(&shelldata, imsg.data, sizeof shelldata);
shelldata.shell[(sizeof shelldata.shell) - 1] = '\0';
clear_signals(0);
shell_exec(shelldata.shell, shellcmd);
/* NOTREACHED */
default:
fatalx("unexpected message");
}
imsg_free(&imsg);
}
}
/* Dispatch imsgs in attached state (after MSG_READY). */
/* ARGSUSED */
int
client_dispatch_attached(void)
{ {
struct imsg imsg; struct imsg imsg;
struct msg_lock_data lockdata; struct msg_lock_data lockdata;
@@ -301,7 +447,8 @@ client_dispatch(void)
client_exitmsg = "detached"; client_exitmsg = "detached";
break; break;
case MSG_EXIT: case MSG_EXIT:
if (datalen != 0) if (datalen != 0 &&
datalen != sizeof (struct msg_exit_data))
fatalx("bad MSG_EXIT size"); fatalx("bad MSG_EXIT size");
client_write_server(MSG_EXITING, NULL, 0); client_write_server(MSG_EXITING, NULL, 0);

View File

@@ -1,4 +1,4 @@
/* $Id: cmd-attach-session.c,v 1.36 2010-02-08 18:27:34 tcunha Exp $ */ /* $Id: cmd-attach-session.c,v 1.37 2010-12-22 15:36:44 tcunha Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -47,7 +47,7 @@ cmd_attach_session_exec(struct cmd *self, struct cmd_ctx *ctx)
char *overrides, *cause; char *overrides, *cause;
u_int i; u_int i;
if (ARRAY_LENGTH(&sessions) == 0) { if (RB_EMPTY(&sessions)) {
ctx->error(ctx, "no sessions"); ctx->error(ctx, "no sessions");
return (-1); return (-1);
} }

View File

@@ -1,4 +1,4 @@
/* $Id: cmd-choose-session.c,v 1.15 2009-11-14 17:56:39 tcunha Exp $ */ /* $Id: cmd-choose-session.c,v 1.17 2010-12-22 15:36:44 tcunha Exp $ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -55,7 +55,7 @@ cmd_choose_session_exec(struct cmd *self, struct cmd_ctx *ctx)
struct winlink *wl; struct winlink *wl;
struct session *s; struct session *s;
struct session_group *sg; struct session_group *sg;
u_int i, idx, cur; u_int idx, sgidx, cur;
char tmp[64]; char tmp[64];
if (ctx->curclient == NULL) { if (ctx->curclient == NULL) {
@@ -70,10 +70,7 @@ cmd_choose_session_exec(struct cmd *self, struct cmd_ctx *ctx)
return (0); return (0);
cur = idx = 0; cur = idx = 0;
for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { RB_FOREACH(s, sessions, &sessions) {
s = ARRAY_ITEM(&sessions, i);
if (s == NULL)
continue;
if (s == ctx->curclient->session) if (s == ctx->curclient->session)
cur = idx; cur = idx;
idx++; idx++;
@@ -82,11 +79,11 @@ cmd_choose_session_exec(struct cmd *self, struct cmd_ctx *ctx)
if (sg == NULL) if (sg == NULL)
*tmp = '\0'; *tmp = '\0';
else { else {
idx = session_group_index(sg); sgidx = session_group_index(sg);
xsnprintf(tmp, sizeof tmp, " (group %u)", idx); xsnprintf(tmp, sizeof tmp, " (group %u)", sgidx);
} }
window_choose_add(wl->window->active, i, window_choose_add(wl->window->active, s->idx,
"%s: %u windows [%ux%u]%s%s", s->name, "%s: %u windows [%ux%u]%s%s", s->name,
winlink_count(&s->windows), s->sx, s->sy, winlink_count(&s->windows), s->sx, s->sy,
tmp, s->flags & SESSION_UNATTACHED ? "" : " (attached)"); tmp, s->flags & SESSION_UNATTACHED ? "" : " (attached)");
@@ -120,9 +117,7 @@ cmd_choose_session_callback(void *data, int idx)
if (cdata->client->flags & CLIENT_DEAD) if (cdata->client->flags & CLIENT_DEAD)
return; return;
if ((u_int) idx > ARRAY_LENGTH(&sessions) - 1) s = session_find_by_index(idx);
return;
s = ARRAY_ITEM(&sessions, idx);
if (s == NULL) if (s == NULL)
return; return;
template = cmd_template_replace(cdata->template, s->name, 1); template = cmd_template_replace(cdata->template, s->name, 1);

View File

@@ -1,4 +1,4 @@
/* $Id: cmd-choose-window.c,v 1.22 2010-06-22 23:26:18 tcunha Exp $ */ /* $Id: cmd-choose-window.c,v 1.24 2010-12-22 15:28:50 tcunha Exp $ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -87,6 +87,8 @@ cmd_choose_window_exec(struct cmd *self, struct cmd_ctx *ctx)
flag = '!'; flag = '!';
else if (wm->flags & WINLINK_CONTENT) else if (wm->flags & WINLINK_CONTENT)
flag = '+'; flag = '+';
else if (wm->flags & WINLINK_SILENCE)
flag = '~';
else if (wm == s->curw) else if (wm == s->curw)
flag = '*'; flag = '*';
else if (wm == TAILQ_FIRST(&s->lastw)) else if (wm == TAILQ_FIRST(&s->lastw))
@@ -127,20 +129,19 @@ void
cmd_choose_window_callback(void *data, int idx) cmd_choose_window_callback(void *data, int idx)
{ {
struct cmd_choose_window_data *cdata = data; struct cmd_choose_window_data *cdata = data;
struct session *s = cdata->session;
struct cmd_list *cmdlist; struct cmd_list *cmdlist;
struct cmd_ctx ctx; struct cmd_ctx ctx;
char *target, *template, *cause; char *target, *template, *cause;
if (idx == -1) if (idx == -1)
return; return;
if (!session_alive(s))
return;
if (cdata->client->flags & CLIENT_DEAD) if (cdata->client->flags & CLIENT_DEAD)
return; return;
if (cdata->session->flags & SESSION_DEAD)
return;
if (cdata->client->session != cdata->session)
return;
xasprintf(&target, "%s:%d", cdata->session->name, idx); xasprintf(&target, "%s:%d", s->name, idx);
template = cmd_template_replace(cdata->template, target, 1); template = cmd_template_replace(cdata->template, target, 1);
xfree(target); xfree(target);

View File

@@ -1,4 +1,4 @@
/* $Id: cmd-copy-mode.c,v 1.27 2010-04-05 05:11:42 micahcowan Exp $ */ /* $Id: cmd-copy-mode.c,v 1.28 2010-08-11 22:18:28 tcunha Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -62,7 +62,8 @@ cmd_copy_mode_exec(struct cmd *self, struct cmd_ctx *ctx)
if (cmd_find_pane(ctx, data->target, NULL, &wp) == NULL) if (cmd_find_pane(ctx, data->target, NULL, &wp) == NULL)
return (-1); return (-1);
window_pane_set_mode(wp, &window_copy_mode); if (window_pane_set_mode(wp, &window_copy_mode) != 0)
return (0);
window_copy_init_from_pane(wp); window_copy_init_from_pane(wp);
if (wp->mode == &window_copy_mode && cmd_check_flag(data->chflags, 'u')) if (wp->mode == &window_copy_mode && cmd_check_flag(data->chflags, 'u'))
window_copy_pageup(wp); window_copy_pageup(wp);

View File

@@ -1,4 +1,4 @@
/* $Id: cmd-find-window.c,v 1.14 2009-11-14 17:56:39 tcunha Exp $ */ /* $Id: cmd-find-window.c,v 1.15 2010-12-22 15:28:50 tcunha Exp $ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -30,6 +30,7 @@
int cmd_find_window_exec(struct cmd *, struct cmd_ctx *); int cmd_find_window_exec(struct cmd *, struct cmd_ctx *);
void cmd_find_window_callback(void *, int); void cmd_find_window_callback(void *, int);
void cmd_find_window_free(void *);
const struct cmd_entry cmd_find_window_entry = { const struct cmd_entry cmd_find_window_entry = {
"find-window", "findw", "find-window", "findw",
@@ -43,7 +44,7 @@ const struct cmd_entry cmd_find_window_entry = {
}; };
struct cmd_find_window_data { struct cmd_find_window_data {
u_int session; struct session *session;
}; };
int int
@@ -134,11 +135,11 @@ cmd_find_window_exec(struct cmd *self, struct cmd_ctx *ctx)
} }
cdata = xmalloc(sizeof *cdata); cdata = xmalloc(sizeof *cdata);
if (session_index(s, &cdata->session) != 0) cdata->session = s;
fatalx("session not found"); cdata->session->references++;
window_choose_ready( window_choose_ready(wl->window->active,
wl->window->active, 0, cmd_find_window_callback, xfree, cdata); 0, cmd_find_window_callback, cmd_find_window_free, cdata);
out: out:
ARRAY_FREE(&list_idx); ARRAY_FREE(&list_idx);
@@ -151,12 +152,24 @@ void
cmd_find_window_callback(void *data, int idx) cmd_find_window_callback(void *data, int idx)
{ {
struct cmd_find_window_data *cdata = data; struct cmd_find_window_data *cdata = data;
struct session *s; struct session *s = cdata->session;
if (idx != -1 && cdata->session <= ARRAY_LENGTH(&sessions) - 1) { if (idx == -1)
s = ARRAY_ITEM(&sessions, cdata->session); return;
if (s != NULL && session_select(s, idx) == 0) if (!session_alive(s))
server_redraw_session(s); return;
if (session_select(s, idx) == 0) {
server_redraw_session(s);
recalculate_sizes(); recalculate_sizes();
} }
} }
void
cmd_find_window_free(void *data)
{
struct cmd_find_window_data *cdata = data;
cdata->session->references--;
xfree(cdata);
}

View File

@@ -1,4 +1,4 @@
/* $Id: cmd-if-shell.c,v 1.9 2010-07-17 14:36:40 tcunha Exp $ */ /* $Id: cmd-if-shell.c,v 1.10 2010-08-09 21:44:25 tcunha Exp $ */
/* /*
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org> * Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
@@ -109,8 +109,7 @@ cmd_if_shell_free(void *data)
if (ctx->cmdclient != NULL) { if (ctx->cmdclient != NULL) {
ctx->cmdclient->references--; ctx->cmdclient->references--;
exitdata.retcode = ctx->cmdclient->retcode; exitdata.retcode = ctx->cmdclient->retcode;
server_write_client( ctx->cmdclient->flags |= CLIENT_EXIT;
ctx->cmdclient, MSG_EXIT, &exitdata, sizeof exitdata);
} }
if (ctx->curclient != NULL) if (ctx->curclient != NULL)
ctx->curclient->references--; ctx->curclient->references--;

View File

@@ -1,4 +1,4 @@
/* $Id: cmd-join-pane.c,v 1.4 2010-04-09 07:09:37 micahcowan Exp $ */ /* $Id: cmd-join-pane.c,v 1.5 2010-08-11 22:17:32 tcunha Exp $ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -44,7 +44,7 @@ struct cmd_join_pane_data {
const struct cmd_entry cmd_join_pane_entry = { const struct cmd_entry cmd_join_pane_entry = {
"join-pane", "joinp", "join-pane", "joinp",
"[-dhv] [-p percentage|-l size] [-s src-pane] [-t dst-pane] [command]", "[-dhv] [-p percentage|-l size] [-s src-pane] [-t dst-pane]",
0, "", 0, "",
cmd_join_pane_init, cmd_join_pane_init,
cmd_join_pane_parse, cmd_join_pane_parse,

58
cmd-last-pane.c Normal file
View File

@@ -0,0 +1,58 @@
/* $Id: cmd-last-pane.c,v 1.1 2010-10-24 01:34:30 tcunha Exp $ */
/*
* Copyright (c) 2010 Nicholas Marriott <nicm@users.sourceforge.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF 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 "tmux.h"
/*
* Move to last pane.
*/
int cmd_last_pane_exec(struct cmd *, struct cmd_ctx *);
const struct cmd_entry cmd_last_pane_entry = {
"last-pane", "lastp",
CMD_TARGET_WINDOW_USAGE,
0, "",
cmd_target_init,
cmd_target_parse,
cmd_last_pane_exec,
cmd_target_free,
cmd_target_print
};
int
cmd_last_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
{
struct cmd_target_data *data = self->data;
struct winlink *wl;
struct window *w;
if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL)
return (-1);
w = wl->window;
if (w->last == NULL) {
ctx->error(ctx, "no last pane");
return (-1);
}
window_set_active_pane(w, w->last);
return (0);
}

View File

@@ -1,4 +1,4 @@
/* $Id: cmd-list-keys.c,v 1.24 2009-12-04 22:14:47 tcunha Exp $ */ /* $Id: cmd-list-keys.c,v 1.25 2010-10-24 01:31:57 tcunha Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -80,6 +80,11 @@ cmd_list_keys_exec(struct cmd *self, struct cmd_ctx *ctx)
if (used >= sizeof tmp) if (used >= sizeof tmp)
continue; continue;
} }
if (bd->can_repeat) {
used = strlcat(tmp, "(repeat) ", sizeof tmp);
if (used >= sizeof tmp)
continue;
}
cmd_list_print(bd->cmdlist, tmp + used, (sizeof tmp) - used); cmd_list_print(bd->cmdlist, tmp + used, (sizeof tmp) - used);
ctx->print(ctx, "%s", tmp); ctx->print(ctx, "%s", tmp);
} }

View File

@@ -1,4 +1,4 @@
/* $Id: cmd-list-panes.c,v 1.4 2009-12-04 22:14:47 tcunha Exp $ */ /* $Id: cmd-list-panes.c,v 1.6 2010-12-06 21:56:32 nicm Exp $ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -65,8 +65,10 @@ cmd_list_panes_exec(struct cmd *self, struct cmd_ctx *ctx)
} }
size += gd->hsize * sizeof *gd->linedata; size += gd->hsize * sizeof *gd->linedata;
ctx->print(ctx, "%u: [%ux%u] [history %u/%u, %llu bytes]", ctx->print(ctx, "%u: [%ux%u] [history %u/%u, %llu bytes]%s%s",
n, wp->sx, wp->sy, gd->hsize, gd->hlimit, size); n, wp->sx, wp->sy, gd->hsize, gd->hlimit, size,
wp == wp->window->active ? " (active)" : "",
wp->fd == -1 ? " (dead)" : "");
n++; n++;
} }

View File

@@ -1,4 +1,4 @@
/* $Id: cmd-list-sessions.c,v 1.25 2009-11-28 14:50:36 tcunha Exp $ */ /* $Id: cmd-list-sessions.c,v 1.26 2010-12-22 15:36:44 tcunha Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -46,14 +46,10 @@ cmd_list_sessions_exec(unused struct cmd *self, struct cmd_ctx *ctx)
struct session *s; struct session *s;
struct session_group *sg; struct session_group *sg;
char *tim, tmp[64]; char *tim, tmp[64];
u_int i, idx; u_int idx;
time_t t; time_t t;
for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { RB_FOREACH(s, sessions, &sessions) {
s = ARRAY_ITEM(&sessions, i);
if (s == NULL)
continue;
sg = session_group_find(s); sg = session_group_find(s);
if (sg == NULL) if (sg == NULL)
*tmp = '\0'; *tmp = '\0';

View File

@@ -1,4 +1,4 @@
/* $Id: cmd-list-windows.c,v 1.43 2010-07-02 02:54:52 tcunha Exp $ */ /* $Id: cmd-list-windows.c,v 1.44 2010-12-06 21:56:32 nicm Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -51,10 +51,10 @@ cmd_list_windows_exec(struct cmd *self, struct cmd_ctx *ctx)
return (-1); return (-1);
RB_FOREACH(wl, winlinks, &s->windows) { RB_FOREACH(wl, winlinks, &s->windows) {
ctx->print(ctx, "%d: %s [%ux%u]",
wl->idx, wl->window->name, wl->window->sx, wl->window->sy);
layout = layout_dump(wl->window); layout = layout_dump(wl->window);
ctx->print(ctx, " layout: %s", layout); ctx->print(ctx, "%d: %s [%ux%u] [layout %s]%s",
wl->idx, wl->window->name, wl->window->sx, wl->window->sy,
layout, wl == s->curw ? " (active)" : "");
xfree(layout); xfree(layout);
} }

View File

@@ -1,4 +1,4 @@
/* $Id: cmd-list.c,v 1.9 2010-07-02 02:43:01 tcunha Exp $ */ /* $Id: cmd-list.c,v 1.10 2010-12-06 21:48:56 nicm Exp $ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -29,7 +29,9 @@ cmd_list_parse(int argc, char **argv, char **cause)
struct cmd *cmd; struct cmd *cmd;
int i, lastsplit; int i, lastsplit;
size_t arglen, new_argc; size_t arglen, new_argc;
char **new_argv; char **copy_argv, **new_argv;
copy_argv = cmd_copy_argv(argc, argv);
cmdlist = xmalloc(sizeof *cmdlist); cmdlist = xmalloc(sizeof *cmdlist);
cmdlist->references = 1; cmdlist->references = 1;
@@ -37,18 +39,18 @@ cmd_list_parse(int argc, char **argv, char **cause)
lastsplit = 0; lastsplit = 0;
for (i = 0; i < argc; i++) { for (i = 0; i < argc; i++) {
arglen = strlen(argv[i]); arglen = strlen(copy_argv[i]);
if (arglen == 0 || argv[i][arglen - 1] != ';') if (arglen == 0 || copy_argv[i][arglen - 1] != ';')
continue; continue;
argv[i][arglen - 1] = '\0'; copy_argv[i][arglen - 1] = '\0';
if (arglen > 1 && argv[i][arglen - 2] == '\\') { if (arglen > 1 && copy_argv[i][arglen - 2] == '\\') {
argv[i][arglen - 2] = ';'; copy_argv[i][arglen - 2] = ';';
continue; continue;
} }
new_argc = i - lastsplit; new_argc = i - lastsplit;
new_argv = argv + lastsplit; new_argv = copy_argv + lastsplit;
if (arglen != 1) if (arglen != 1)
new_argc++; new_argc++;
@@ -61,16 +63,18 @@ cmd_list_parse(int argc, char **argv, char **cause)
} }
if (lastsplit != argc) { if (lastsplit != argc) {
cmd = cmd_parse(argc - lastsplit, argv + lastsplit, cause); cmd = cmd_parse(argc - lastsplit, copy_argv + lastsplit, cause);
if (cmd == NULL) if (cmd == NULL)
goto bad; goto bad;
TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry); TAILQ_INSERT_TAIL(&cmdlist->list, cmd, qentry);
} }
cmd_free_argv(argc, copy_argv);
return (cmdlist); return (cmdlist);
bad: bad:
cmd_list_free(cmdlist); cmd_list_free(cmdlist);
cmd_free_argv(argc, copy_argv);
return (NULL); return (NULL);
} }

View File

@@ -1,4 +1,4 @@
/* $Id: cmd-load-buffer.c,v 1.16 2010-07-02 02:52:13 tcunha Exp $ */ /* $Id: cmd-load-buffer.c,v 1.18 2010-12-22 15:28:50 tcunha Exp $ */
/* /*
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org> * Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
@@ -31,6 +31,7 @@
*/ */
int cmd_load_buffer_exec(struct cmd *, struct cmd_ctx *); int cmd_load_buffer_exec(struct cmd *, struct cmd_ctx *);
void cmd_load_buffer_callback(struct client *, void *);
const struct cmd_entry cmd_load_buffer_entry = { const struct cmd_entry cmd_load_buffer_entry = {
"load-buffer", "loadb", "load-buffer", "loadb",
@@ -43,37 +44,56 @@ const struct cmd_entry cmd_load_buffer_entry = {
cmd_buffer_print cmd_buffer_print
}; };
struct cmd_load_buffer_cdata {
struct session *session;
int buffer;
};
int int
cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx) cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
{ {
struct cmd_buffer_data *data = self->data; struct cmd_buffer_data *data = self->data;
struct session *s; struct cmd_load_buffer_cdata *cdata;
FILE *f, *close_f; struct session *s;
char *pdata, *new_pdata; struct client *c = ctx->cmdclient;
size_t psize; FILE *f;
u_int limit; char *pdata, *new_pdata;
int ch; size_t psize;
u_int limit;
int ch;
if ((s = cmd_find_session(ctx, data->target)) == NULL) if ((s = cmd_find_session(ctx, data->target)) == NULL)
return (-1); return (-1);
if (strcmp(data->arg, "-") == 0 ) { if (strcmp(data->arg, "-") == 0) {
if (ctx->cmdclient == NULL) { if (c == NULL) {
ctx->error(ctx, "%s: can't read from stdin", data->arg); ctx->error(ctx, "%s: can't read from stdin", data->arg);
return (-1); return (-1);
} }
f = ctx->cmdclient->stdin_file; if (c->flags & CLIENT_TERMINAL) {
if (isatty(fileno(ctx->cmdclient->stdin_file))) {
ctx->error(ctx, "%s: stdin is a tty", data->arg); ctx->error(ctx, "%s: stdin is a tty", data->arg);
return (-1); return (-1);
} }
close_f = NULL; if (c->stdin_fd == -1) {
} else { ctx->error(ctx, "%s: can't read from stdin", data->arg);
if ((f = fopen(data->arg, "rb")) == NULL) {
ctx->error(ctx, "%s: %s", data->arg, strerror(errno));
return (-1); return (-1);
} }
close_f = f;
cdata = xmalloc(sizeof *cdata);
cdata->session = s;
cdata->session->references++;
cdata->buffer = data->buffer;
c->stdin_data = cdata;
c->stdin_callback = cmd_load_buffer_callback;
c->references++;
bufferevent_enable(c->stdin_event, EV_READ);
return (1);
}
if ((f = fopen(data->arg, "rb")) == NULL) {
ctx->error(ctx, "%s: %s", data->arg, strerror(errno));
return (-1);
} }
pdata = NULL; pdata = NULL;
@@ -94,8 +114,8 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
if (pdata != NULL) if (pdata != NULL)
pdata[psize] = '\0'; pdata[psize] = '\0';
if (close_f != NULL) fclose(f);
fclose(close_f); f = NULL;
limit = options_get_number(&s->options, "buffer-limit"); limit = options_get_number(&s->options, "buffer-limit");
if (data->buffer == -1) { if (data->buffer == -1) {
@@ -104,7 +124,7 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
} }
if (paste_replace(&s->buffers, data->buffer, pdata, psize) != 0) { if (paste_replace(&s->buffers, data->buffer, pdata, psize) != 0) {
ctx->error(ctx, "no buffer %d", data->buffer); ctx->error(ctx, "no buffer %d", data->buffer);
goto error; return (-1);
} }
return (0); return (0);
@@ -112,7 +132,54 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
error: error:
if (pdata != NULL) if (pdata != NULL)
xfree(pdata); xfree(pdata);
if (close_f != NULL) if (f != NULL)
fclose(close_f); fclose(f);
return (-1); return (-1);
} }
void
cmd_load_buffer_callback(struct client *c, void *data)
{
struct cmd_load_buffer_cdata *cdata = data;
struct session *s = cdata->session;
char *pdata;
size_t psize;
u_int limit;
/*
* Event callback has already checked client is not dead and reduced
* its reference count. But tell it to exit.
*/
c->flags |= CLIENT_EXIT;
/* Does the target session still exist? */
if (!session_alive(s))
goto out;
psize = EVBUFFER_LENGTH(c->stdin_event->input);
if (psize == 0)
goto out;
pdata = malloc(psize + 1);
if (pdata == NULL)
goto out;
bufferevent_read(c->stdin_event, pdata, psize);
pdata[psize] = '\0';
limit = options_get_number(&s->options, "buffer-limit");
if (cdata->buffer == -1) {
paste_add(&s->buffers, pdata, psize, limit);
goto out;
}
if (paste_replace(&s->buffers, cdata->buffer, pdata, psize) != 0) {
/* No context so can't use server_client_msg_error. */
evbuffer_add_printf(
c->stderr_event->output, "no buffer %d\n", cdata->buffer);
bufferevent_enable(c->stderr_event, EV_WRITE);
goto out;
}
out:
cdata->session->references--;
xfree(cdata);
}

View File

@@ -1,4 +1,4 @@
/* $Id: cmd-new-session.c,v 1.78 2010-07-02 02:49:19 tcunha Exp $ */ /* $Id: cmd-new-session.c,v 1.80 2010-12-22 15:31:00 tcunha Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -122,7 +122,7 @@ int
cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx) cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx)
{ {
struct cmd_new_session_data *data = self->data; struct cmd_new_session_data *data = self->data;
struct session *s, *groupwith; struct session *s, *old_s, *groupwith;
struct window *w; struct window *w;
struct window_pane *wp; struct window_pane *wp;
struct environ env; struct environ env;
@@ -279,9 +279,16 @@ cmd_new_session_exec(struct cmd *self, struct cmd_ctx *ctx)
if (!detached) { if (!detached) {
if (ctx->cmdclient != NULL) { if (ctx->cmdclient != NULL) {
server_write_client(ctx->cmdclient, MSG_READY, NULL, 0); server_write_client(ctx->cmdclient, MSG_READY, NULL, 0);
old_s = ctx->cmdclient->session;
if (old_s != NULL)
ctx->cmdclient->last_session = old_s;
ctx->cmdclient->session = s; ctx->cmdclient->session = s;
server_redraw_client(ctx->cmdclient); server_redraw_client(ctx->cmdclient);
} else { } else {
old_s = ctx->curclient->session;
if (old_s != NULL)
ctx->curclient->last_session = old_s;
ctx->curclient->session = s; ctx->curclient->session = s;
server_redraw_client(ctx->curclient); server_redraw_client(ctx->curclient);
} }

View File

@@ -1,4 +1,4 @@
/* $Id: cmd-paste-buffer.c,v 1.28 2010-06-06 00:03:02 tcunha Exp $ */ /* $Id: cmd-paste-buffer.c,v 1.29 2010-08-11 22:17:32 tcunha Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -45,7 +45,7 @@ size_t cmd_paste_buffer_print(struct cmd *, char *, size_t);
const struct cmd_entry cmd_paste_buffer_entry = { const struct cmd_entry cmd_paste_buffer_entry = {
"paste-buffer", "pasteb", "paste-buffer", "pasteb",
"[-dr] [-s separator] [-b buffer-index] [-t target-window]", "[-dr] [-s separator] [-b buffer-index] [-t target-pane]",
0, "", 0, "",
cmd_paste_buffer_init, cmd_paste_buffer_init,
cmd_paste_buffer_parse, cmd_paste_buffer_parse,

View File

@@ -1,4 +1,4 @@
/* $Id: cmd-pipe-pane.c,v 1.13 2010-06-15 20:25:40 tcunha Exp $ */ /* $Id: cmd-pipe-pane.c,v 1.15 2010-10-24 00:45:57 tcunha Exp $ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -96,7 +96,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
case 0: case 0:
/* Child process. */ /* Child process. */
close(pipe_fd[0]); close(pipe_fd[0]);
clear_signals(); clear_signals(1);
if (dup2(pipe_fd[1], STDIN_FILENO) == -1) if (dup2(pipe_fd[1], STDIN_FILENO) == -1)
_exit(1); _exit(1);
@@ -111,6 +111,8 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
if (null_fd != STDOUT_FILENO && null_fd != STDERR_FILENO) if (null_fd != STDOUT_FILENO && null_fd != STDERR_FILENO)
close(null_fd); close(null_fd);
closefrom(STDERR_FILENO + 1);
command = status_replace(c, NULL, data->arg, time(NULL), 0); command = status_replace(c, NULL, data->arg, time(NULL), 0);
execl(_PATH_BSHELL, "sh", "-c", command, (char *) NULL); execl(_PATH_BSHELL, "sh", "-c", command, (char *) NULL);
_exit(1); _exit(1);
@@ -129,8 +131,6 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
fatal("fcntl failed"); fatal("fcntl failed");
if (fcntl(wp->pipe_fd, F_SETFL, mode|O_NONBLOCK) == -1) if (fcntl(wp->pipe_fd, F_SETFL, mode|O_NONBLOCK) == -1)
fatal("fcntl failed"); fatal("fcntl failed");
if (fcntl(wp->pipe_fd, F_SETFD, FD_CLOEXEC) == -1)
fatal("fcntl failed");
return (0); return (0);
} }
} }

View File

@@ -1,4 +1,4 @@
/* $Id: cmd-rename-session.c,v 1.19 2009-11-14 17:56:39 tcunha Exp $ */ /* $Id: cmd-rename-session.c,v 1.21 2010-12-22 15:36:44 tcunha Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -45,11 +45,18 @@ cmd_rename_session_exec(struct cmd *self, struct cmd_ctx *ctx)
struct cmd_target_data *data = self->data; struct cmd_target_data *data = self->data;
struct session *s; struct session *s;
if (data->arg != NULL && session_find(data->arg) != NULL) {
ctx->error(ctx, "duplicate session: %s", data->arg);
return (-1);
}
if ((s = cmd_find_session(ctx, data->target)) == NULL) if ((s = cmd_find_session(ctx, data->target)) == NULL)
return (-1); return (-1);
RB_REMOVE(sessions, &sessions, s);
xfree(s->name); xfree(s->name);
s->name = xstrdup(data->arg); s->name = xstrdup(data->arg);
RB_INSERT(sessions, &sessions, s);
server_status_session(s); server_status_session(s);

View File

@@ -1,4 +1,4 @@
/* $Id: cmd-run-shell.c,v 1.8 2010-07-17 14:36:40 tcunha Exp $ */ /* $Id: cmd-run-shell.c,v 1.9 2010-08-09 21:44:25 tcunha Exp $ */
/* /*
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org> * Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
@@ -131,13 +131,10 @@ cmd_run_shell_free(void *data)
{ {
struct cmd_run_shell_data *cdata = data; struct cmd_run_shell_data *cdata = data;
struct cmd_ctx *ctx = &cdata->ctx; struct cmd_ctx *ctx = &cdata->ctx;
struct msg_exit_data exitdata;
if (ctx->cmdclient != NULL) { if (ctx->cmdclient != NULL) {
ctx->cmdclient->references--; ctx->cmdclient->references--;
exitdata.retcode = ctx->cmdclient->retcode; ctx->cmdclient->flags |= CLIENT_EXIT;
server_write_client(
ctx->cmdclient, MSG_EXIT, &exitdata, sizeof exitdata);
} }
if (ctx->curclient != NULL) if (ctx->curclient != NULL)
ctx->curclient->references--; ctx->curclient->references--;

View File

@@ -1,4 +1,4 @@
/* $Id: cmd-save-buffer.c,v 1.11 2010-07-02 02:52:13 tcunha Exp $ */ /* $Id: cmd-save-buffer.c,v 1.12 2010-08-09 21:44:25 tcunha Exp $ */
/* /*
* Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org> * Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
@@ -47,8 +47,8 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
struct cmd_buffer_data *data = self->data; struct cmd_buffer_data *data = self->data;
struct session *s; struct session *s;
struct paste_buffer *pb; struct paste_buffer *pb;
mode_t mask; mode_t mask;
FILE *f, *close_f; FILE *f;
if ((s = cmd_find_session(ctx, data->target)) == NULL) if ((s = cmd_find_session(ctx, data->target)) == NULL)
return (-1); return (-1);
@@ -70,8 +70,8 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
ctx->error(ctx, "%s: can't write to stdout", data->arg); ctx->error(ctx, "%s: can't write to stdout", data->arg);
return (-1); return (-1);
} }
f = ctx->cmdclient->stdout_file; bufferevent_write(
close_f = NULL; ctx->cmdclient->stdout_event, pb->data, pb->size);
} else { } else {
mask = umask(S_IRWXG | S_IRWXO); mask = umask(S_IRWXG | S_IRWXO);
if (cmd_check_flag(data->chflags, 'a')) if (cmd_check_flag(data->chflags, 'a'))
@@ -83,17 +83,13 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
ctx->error(ctx, "%s: %s", data->arg, strerror(errno)); ctx->error(ctx, "%s: %s", data->arg, strerror(errno));
return (-1); return (-1);
} }
close_f = f; if (fwrite(pb->data, 1, pb->size, f) != pb->size) {
ctx->error(ctx, "%s: fwrite error", data->arg);
fclose(f);
return (-1);
}
fclose(f);
} }
if (fwrite(pb->data, 1, pb->size, f) != pb->size) {
ctx->error(ctx, "%s: fwrite error", data->arg);
fclose(f);
return (-1);
}
if (close_f != NULL)
fclose(close_f);
return (0); return (0);
} }

View File

@@ -1,4 +1,4 @@
/* $Id: cmd-server-info.c,v 1.37 2009-12-10 16:59:02 tcunha Exp $ */ /* $Id: cmd-server-info.c,v 1.38 2010-12-22 15:36:44 tcunha Exp $ */
/* /*
* Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -81,8 +81,6 @@ cmd_server_info_exec(unused struct cmd *self, struct cmd_ctx *ctx)
else else
ctx->print(ctx, "configuration file not specified"); ctx->print(ctx, "configuration file not specified");
ctx->print(ctx, "protocol version is %d", PROTOCOL_VERSION); ctx->print(ctx, "protocol version is %d", PROTOCOL_VERSION);
ctx->print(ctx, "%u clients, %u sessions",
ARRAY_LENGTH(&clients), ARRAY_LENGTH(&sessions));
ctx->print(ctx, "%s", ""); ctx->print(ctx, "%s", "");
ctx->print(ctx, "Clients:"); ctx->print(ctx, "Clients:");
@@ -101,19 +99,14 @@ cmd_server_info_exec(unused struct cmd *self, struct cmd_ctx *ctx)
ctx->print(ctx, "Sessions: [%zu/%zu]", ctx->print(ctx, "Sessions: [%zu/%zu]",
sizeof (struct grid_cell), sizeof (struct grid_utf8)); sizeof (struct grid_cell), sizeof (struct grid_utf8));
for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { RB_FOREACH(s, sessions, &sessions) {
s = ARRAY_ITEM(&sessions, i);
if (s == NULL)
continue;
t = s->creation_time.tv_sec; t = s->creation_time.tv_sec;
tim = ctime(&t); tim = ctime(&t);
*strchr(tim, '\n') = '\0'; *strchr(tim, '\n') = '\0';
ctx->print(ctx, "%2u: %s: %u windows (created %s) [%ux%u] " ctx->print(ctx, "%2u: %s: %u windows (created %s) [%ux%u] "
"[flags=0x%x, references=%u]", i, s->name, "[flags=0x%x]", s->idx, s->name,
winlink_count(&s->windows), tim, s->sx, s->sy, s->flags, winlink_count(&s->windows), tim, s->sx, s->sy, s->flags);
s->references);
RB_FOREACH(wl, winlinks, &s->windows) { RB_FOREACH(wl, winlinks, &s->windows) {
w = wl->window; w = wl->window;
ctx->print(ctx, "%4u: %s [%ux%u] [flags=0x%x, " ctx->print(ctx, "%4u: %s [%ux%u] [flags=0x%x, "

View File

@@ -1,4 +1,4 @@
/* $Id: cmd-set-option.c,v 1.98 2010-07-02 02:45:52 tcunha Exp $ */ /* $Id: cmd-set-option.c,v 1.102 2010-12-22 15:23:59 tcunha Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -75,6 +75,7 @@ const char *set_option_bell_action_list[] = {
const struct set_option_entry set_option_table[] = { const struct set_option_entry set_option_table[] = {
{ "escape-time", SET_OPTION_NUMBER, 0, INT_MAX, NULL }, { "escape-time", SET_OPTION_NUMBER, 0, INT_MAX, NULL },
{ "exit-unattached", SET_OPTION_FLAG, 0, 0, NULL },
{ "quiet", SET_OPTION_FLAG, 0, 0, NULL }, { "quiet", SET_OPTION_FLAG, 0, 0, NULL },
{ NULL, 0, 0, 0, NULL } { NULL, 0, 0, 0, NULL }
}; };
@@ -87,6 +88,7 @@ const struct set_option_entry set_session_option_table[] = {
{ "default-path", SET_OPTION_STRING, 0, 0, NULL }, { "default-path", SET_OPTION_STRING, 0, 0, NULL },
{ "default-shell", SET_OPTION_STRING, 0, 0, NULL }, { "default-shell", SET_OPTION_STRING, 0, 0, NULL },
{ "default-terminal", SET_OPTION_STRING, 0, 0, NULL }, { "default-terminal", SET_OPTION_STRING, 0, 0, NULL },
{ "destroy-unattached", SET_OPTION_FLAG, 0, 0, NULL },
{ "detach-on-destroy", SET_OPTION_FLAG, 0, 0, NULL }, { "detach-on-destroy", SET_OPTION_FLAG, 0, 0, NULL },
{ "display-panes-colour", SET_OPTION_COLOUR, 0, 0, NULL }, { "display-panes-colour", SET_OPTION_COLOUR, 0, 0, NULL },
{ "display-panes-active-colour", SET_OPTION_COLOUR, 0, 0, NULL }, { "display-panes-active-colour", SET_OPTION_COLOUR, 0, 0, NULL },
@@ -134,6 +136,7 @@ const struct set_option_entry set_session_option_table[] = {
{ "visual-activity", SET_OPTION_FLAG, 0, 0, NULL }, { "visual-activity", SET_OPTION_FLAG, 0, 0, NULL },
{ "visual-bell", SET_OPTION_FLAG, 0, 0, NULL }, { "visual-bell", SET_OPTION_FLAG, 0, 0, NULL },
{ "visual-content", SET_OPTION_FLAG, 0, 0, NULL }, { "visual-content", SET_OPTION_FLAG, 0, 0, NULL },
{ "visual-silence", SET_OPTION_FLAG, 0, 0, NULL },
{ NULL, 0, 0, 0, NULL } { NULL, 0, 0, 0, NULL }
}; };
@@ -155,6 +158,9 @@ const struct set_option_entry set_window_option_table[] = {
{ "mode-mouse", SET_OPTION_FLAG, 0, 0, NULL }, { "mode-mouse", SET_OPTION_FLAG, 0, 0, NULL },
{ "monitor-activity", SET_OPTION_FLAG, 0, 0, NULL }, { "monitor-activity", SET_OPTION_FLAG, 0, 0, NULL },
{ "monitor-content", SET_OPTION_STRING, 0, 0, NULL }, { "monitor-content", SET_OPTION_STRING, 0, 0, NULL },
{ "monitor-silence",SET_OPTION_NUMBER, 0, INT_MAX, NULL},
{ "other-pane-height", SET_OPTION_NUMBER, 0, INT_MAX, NULL },
{ "other-pane-width", SET_OPTION_NUMBER, 0, INT_MAX, NULL },
{ "remain-on-exit", SET_OPTION_FLAG, 0, 0, NULL }, { "remain-on-exit", SET_OPTION_FLAG, 0, 0, NULL },
{ "synchronize-panes", SET_OPTION_FLAG, 0, 0, NULL }, { "synchronize-panes", SET_OPTION_FLAG, 0, 0, NULL },
{ "utf8", SET_OPTION_FLAG, 0, 0, NULL }, { "utf8", SET_OPTION_FLAG, 0, 0, NULL },
@@ -293,6 +299,7 @@ cmd_set_option_exec(struct cmd *self, struct cmd_ctx *ctx)
*/ */
if (strcmp(entry->name, "status-left") == 0 || if (strcmp(entry->name, "status-left") == 0 ||
strcmp(entry->name, "status-right") == 0 || strcmp(entry->name, "status-right") == 0 ||
strcmp(entry->name, "status") == 0 ||
strcmp(entry->name, "set-titles-string") == 0 || strcmp(entry->name, "set-titles-string") == 0 ||
strcmp(entry->name, "window-status-format") == 0) { strcmp(entry->name, "window-status-format") == 0) {
for (i = 0; i < ARRAY_LENGTH(&clients); i++) { for (i = 0; i < ARRAY_LENGTH(&clients); i++) {

View File

@@ -1,4 +1,4 @@
/* $Id: cmd-string.c,v 1.31 2010-02-26 13:27:38 tcunha Exp $ */ /* $Id: cmd-string.c,v 1.32 2010-12-13 22:53:56 nicm Exp $ */
/* /*
* Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -247,9 +247,10 @@ error:
char * char *
cmd_string_variable(const char *s, size_t *p) cmd_string_variable(const char *s, size_t *p)
{ {
int ch, fch; int ch, fch;
char *buf, *t; char *buf, *t;
size_t len; size_t len;
struct environ_entry *envent;
#define cmd_string_first(ch) ((ch) == '_' || \ #define cmd_string_first(ch) ((ch) == '_' || \
((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z')) ((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z'))
@@ -301,12 +302,11 @@ cmd_string_variable(const char *s, size_t *p)
buf = xrealloc(buf, 1, len + 1); buf = xrealloc(buf, 1, len + 1);
buf[len] = '\0'; buf[len] = '\0';
if ((t = getenv(buf)) == NULL) { envent = environ_find(&global_environ, buf);
xfree(buf);
return (xstrdup(""));
}
xfree(buf); xfree(buf);
return (xstrdup(t)); if (envent == NULL)
return (xstrdup(""));
return (xstrdup(envent->value));
error: error:
if (buf != NULL) if (buf != NULL)
@@ -317,15 +317,17 @@ error:
char * char *
cmd_string_expand_tilde(const char *s, size_t *p) cmd_string_expand_tilde(const char *s, size_t *p)
{ {
struct passwd *pw; struct passwd *pw;
char *home, *path, *username; struct environ_entry *envent;
char *home, *path, *username;
home = NULL; home = NULL;
if (cmd_string_getc(s, p) == '/') { if (cmd_string_getc(s, p) == '/') {
if ((home = getenv("HOME")) == NULL || *home == '\0') { envent = environ_find(&global_environ, "HOME");
if ((pw = getpwuid(getuid())) != NULL) if (envent != NULL && *envent->value != '\0')
home = pw->pw_dir; home = envent->value;
} else if ((pw = getpwuid(getuid())) != NULL)
home = pw->pw_dir;
} else { } else {
cmd_string_ungetc(p); cmd_string_ungetc(p);
if ((username = cmd_string_string(s, p, '/', 0)) == NULL) if ((username = cmd_string_string(s, p, '/', 0)) == NULL)

View File

@@ -1,4 +1,4 @@
/* $Id: cmd-switch-client.c,v 1.19 2010-01-25 17:12:44 tcunha Exp $ */ /* $Id: cmd-switch-client.c,v 1.23 2010-12-22 15:31:00 tcunha Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -27,6 +27,7 @@
* Switch client to a different session. * Switch client to a different session.
*/ */
void cmd_switch_client_init(struct cmd *, int);
int cmd_switch_client_parse(struct cmd *, int, char **, char **); int cmd_switch_client_parse(struct cmd *, int, char **, char **);
int cmd_switch_client_exec(struct cmd *, struct cmd_ctx *); int cmd_switch_client_exec(struct cmd *, struct cmd_ctx *);
void cmd_switch_client_free(struct cmd *); void cmd_switch_client_free(struct cmd *);
@@ -35,36 +36,83 @@ size_t cmd_switch_client_print(struct cmd *, char *, size_t);
struct cmd_switch_client_data { struct cmd_switch_client_data {
char *name; char *name;
char *target; char *target;
int flag_last;
int flag_next;
int flag_previous;
}; };
const struct cmd_entry cmd_switch_client_entry = { const struct cmd_entry cmd_switch_client_entry = {
"switch-client", "switchc", "switch-client", "switchc",
"[-c target-client] [-t target-session]", "[-lnp] [-c target-client] [-t target-session]",
0, "", 0, "",
NULL, cmd_switch_client_init,
cmd_switch_client_parse, cmd_switch_client_parse,
cmd_switch_client_exec, cmd_switch_client_exec,
cmd_switch_client_free, cmd_switch_client_free,
cmd_switch_client_print cmd_switch_client_print
}; };
void
cmd_switch_client_init(struct cmd *self, int key)
{
struct cmd_switch_client_data *data;
self->data = data = xmalloc(sizeof *data);
data->name = NULL;
data->target = NULL;
data->flag_last = 0;
data->flag_next = 0;
data->flag_previous = 0;
switch (key) {
case '(':
data->flag_previous = 1;
break;
case ')':
data->flag_next = 1;
break;
case 'L':
data->flag_last = 1;
break;
}
}
int int
cmd_switch_client_parse(struct cmd *self, int argc, char **argv, char **cause) cmd_switch_client_parse(struct cmd *self, int argc, char **argv, char **cause)
{ {
struct cmd_switch_client_data *data; struct cmd_switch_client_data *data;
int opt; int opt;
self->data = data = xmalloc(sizeof *data); self->entry->init(self, KEYC_NONE);
data->name = NULL; data = self->data;
data->target = NULL;
while ((opt = getopt(argc, argv, "c:t:")) != -1) { while ((opt = getopt(argc, argv, "c:lnpt:")) != -1) {
switch (opt) { switch (opt) {
case 'c': case 'c':
if (data->name == NULL) if (data->name == NULL)
data->name = xstrdup(optarg); data->name = xstrdup(optarg);
break; break;
case 'l':
if (data->flag_next || data->flag_previous ||
data->target != NULL)
goto usage;
data->flag_last = 1;
break;
case 'n':
if (data->flag_previous || data->flag_last ||
data->target != NULL)
goto usage;
data->flag_next = 1;
break;
case 'p':
if (data->flag_next || data->flag_last ||
data->target != NULL)
goto usage;
data->flag_next = 1;
break;
case 't': case 't':
if (data->flag_next || data->flag_previous)
goto usage;
if (data->target == NULL) if (data->target == NULL)
data->target = xstrdup(optarg); data->target = xstrdup(optarg);
break; break;
@@ -98,12 +146,36 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_ctx *ctx)
if ((c = cmd_find_client(ctx, data->name)) == NULL) if ((c = cmd_find_client(ctx, data->name)) == NULL)
return (-1); return (-1);
if ((s = cmd_find_session(ctx, data->target)) == NULL)
s = NULL;
if (data->flag_next) {
if ((s = session_next_session(c->session)) == NULL) {
ctx->error(ctx, "can't find next session");
return (-1);
}
} else if (data->flag_previous) {
if ((s = session_previous_session(c->session)) == NULL) {
ctx->error(ctx, "can't find previous session");
return (-1);
}
} else if (data->flag_last) {
if (c->last_session != NULL && session_alive(c->last_session))
s = c->last_session;
if (s == NULL) {
ctx->error(ctx, "can't find last session");
return (-1);
}
} else
s = cmd_find_session(ctx, data->target);
if (s == NULL)
return (-1); return (-1);
if (c->session != NULL)
c->last_session = c->session;
c->session = s; c->session = s;
recalculate_sizes(); recalculate_sizes();
server_check_unattached();
server_redraw_client(c); server_redraw_client(c);
return (0); return (0);
@@ -130,6 +202,12 @@ cmd_switch_client_print(struct cmd *self, char *buf, size_t len)
off += xsnprintf(buf, len, "%s", self->entry->name); off += xsnprintf(buf, len, "%s", self->entry->name);
if (data == NULL) if (data == NULL)
return (off); return (off);
if (off < len && data->flag_last)
off += xsnprintf(buf + off, len - off, "%s", " -l");
if (off < len && data->flag_next)
off += xsnprintf(buf + off, len - off, "%s", " -n");
if (off < len && data->flag_previous)
off += xsnprintf(buf + off, len - off, "%s", " -p");
if (off < len && data->name != NULL) if (off < len && data->name != NULL)
off += cmd_prarg(buf + off, len - off, " -c ", data->name); off += cmd_prarg(buf + off, len - off, " -c ", data->name);
if (off < len && data->target != NULL) if (off < len && data->target != NULL)

View File

@@ -1,4 +1,4 @@
/* $Id: cmd-unbind-key.c,v 1.22 2010-01-25 17:12:44 tcunha Exp $ */ /* $Id: cmd-unbind-key.c,v 1.23 2010-12-06 21:51:02 nicm Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -33,13 +33,14 @@ int cmd_unbind_key_table(struct cmd *, struct cmd_ctx *);
struct cmd_unbind_key_data { struct cmd_unbind_key_data {
int key; int key;
int flag_all;
int command_key; int command_key;
char *tablename; char *tablename;
}; };
const struct cmd_entry cmd_unbind_key_entry = { const struct cmd_entry cmd_unbind_key_entry = {
"unbind-key", "unbind", "unbind-key", "unbind",
"[-cn] [-t key-table] key", "[-acn] [-t key-table] key",
0, "", 0, "",
NULL, NULL,
cmd_unbind_key_parse, cmd_unbind_key_parse,
@@ -55,11 +56,15 @@ cmd_unbind_key_parse(struct cmd *self, int argc, char **argv, char **cause)
int opt, no_prefix = 0; int opt, no_prefix = 0;
self->data = data = xmalloc(sizeof *data); self->data = data = xmalloc(sizeof *data);
data->flag_all = 0;
data->command_key = 0; data->command_key = 0;
data->tablename = NULL; data->tablename = NULL;
while ((opt = getopt(argc, argv, "cnt:")) != -1) { while ((opt = getopt(argc, argv, "acnt:")) != -1) {
switch (opt) { switch (opt) {
case 'a':
data->flag_all = 1;
break;
case 'c': case 'c':
data->command_key = 1; data->command_key = 1;
break; break;
@@ -76,15 +81,20 @@ cmd_unbind_key_parse(struct cmd *self, int argc, char **argv, char **cause)
} }
argc -= optind; argc -= optind;
argv += optind; argv += optind;
if (argc != 1) if (data->flag_all && (argc != 0 || data->tablename))
goto usage;
if (!data->flag_all && argc != 1)
goto usage; goto usage;
if ((data->key = key_string_lookup_string(argv[0])) == KEYC_NONE) { if (!data->flag_all) {
xasprintf(cause, "unknown key: %s", argv[0]); data->key = key_string_lookup_string(argv[0]);
goto error; if (data->key == KEYC_NONE) {
xasprintf(cause, "unknown key: %s", argv[0]);
goto error;
}
if (!no_prefix)
data->key |= KEYC_PREFIX;
} }
if (!no_prefix)
data->key |= KEYC_PREFIX;
return (0); return (0);
@@ -100,13 +110,23 @@ int
cmd_unbind_key_exec(struct cmd *self, unused struct cmd_ctx *ctx) cmd_unbind_key_exec(struct cmd *self, unused struct cmd_ctx *ctx)
{ {
struct cmd_unbind_key_data *data = self->data; struct cmd_unbind_key_data *data = self->data;
struct key_binding *bd;
if (data == NULL) if (data == NULL)
return (0); return (0);
if (data->tablename != NULL) if (data->flag_all) {
return (cmd_unbind_key_table(self, ctx)); while (!SPLAY_EMPTY(&key_bindings)) {
bd = SPLAY_ROOT(&key_bindings);
SPLAY_REMOVE(key_bindings, &key_bindings, bd);
cmd_list_free(bd->cmdlist);
xfree(bd);
}
} else {
if (data->tablename != NULL)
return (cmd_unbind_key_table(self, ctx));
key_bindings_remove(data->key); key_bindings_remove(data->key);
}
return (0); return (0);
} }

78
cmd.c
View File

@@ -1,4 +1,4 @@
/* $Id: cmd.c,v 1.142 2010-07-17 14:38:13 tcunha Exp $ */ /* $Id: cmd.c,v 1.146 2010-12-22 15:36:44 tcunha Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -53,6 +53,7 @@ const struct cmd_entry *cmd_table[] = {
&cmd_kill_server_entry, &cmd_kill_server_entry,
&cmd_kill_session_entry, &cmd_kill_session_entry,
&cmd_kill_window_entry, &cmd_kill_window_entry,
&cmd_last_pane_entry,
&cmd_last_window_entry, &cmd_last_window_entry,
&cmd_link_window_entry, &cmd_link_window_entry,
&cmd_list_buffers_entry, &cmd_list_buffers_entry,
@@ -110,7 +111,8 @@ const struct cmd_entry *cmd_table[] = {
NULL NULL
}; };
struct session *cmd_choose_session(struct sessions *); struct session *cmd_choose_session_list(struct sessionslist *);
struct session *cmd_choose_session(void);
struct client *cmd_choose_client(struct clients *); struct client *cmd_choose_client(struct clients *);
struct client *cmd_lookup_client(const char *); struct client *cmd_lookup_client(const char *);
struct session *cmd_lookup_session(const char *, int *); struct session *cmd_lookup_session(const char *, int *);
@@ -164,6 +166,22 @@ cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv)
return (0); return (0);
} }
char **
cmd_copy_argv(int argc, char **argv)
{
char **new_argv;
int i;
if (argc == 0)
return (NULL);
new_argv = xcalloc(argc, sizeof *new_argv);
for (i = 0; i < argc; i++) {
if (argv[i] != NULL)
new_argv[i] = xstrdup(argv[i]);
}
return (new_argv);
}
void void
cmd_free_argv(int argc, char **argv) cmd_free_argv(int argc, char **argv)
{ {
@@ -298,10 +316,9 @@ cmd_current_session(struct cmd_ctx *ctx)
struct msg_command_data *data = ctx->msgdata; struct msg_command_data *data = ctx->msgdata;
struct client *c = ctx->cmdclient; struct client *c = ctx->cmdclient;
struct session *s; struct session *s;
struct sessions ss; struct sessionslist ss;
struct winlink *wl; struct winlink *wl;
struct window_pane *wp; struct window_pane *wp;
u_int i;
int found; int found;
if (ctx->curclient != NULL && ctx->curclient->session != NULL) if (ctx->curclient != NULL && ctx->curclient->session != NULL)
@@ -314,9 +331,7 @@ cmd_current_session(struct cmd_ctx *ctx)
*/ */
if (c != NULL && c->tty.path != NULL) { if (c != NULL && c->tty.path != NULL) {
ARRAY_INIT(&ss); ARRAY_INIT(&ss);
for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { RB_FOREACH(s, sessions, &sessions) {
if ((s = ARRAY_ITEM(&sessions, i)) == NULL)
continue;
found = 0; found = 0;
RB_FOREACH(wl, winlinks, &s->windows) { RB_FOREACH(wl, winlinks, &s->windows) {
TAILQ_FOREACH(wp, &wl->window->panes, entry) { TAILQ_FOREACH(wp, &wl->window->panes, entry) {
@@ -332,29 +347,43 @@ cmd_current_session(struct cmd_ctx *ctx)
ARRAY_ADD(&ss, s); ARRAY_ADD(&ss, s);
} }
s = cmd_choose_session(&ss); s = cmd_choose_session_list(&ss);
ARRAY_FREE(&ss); ARRAY_FREE(&ss);
if (s != NULL) if (s != NULL)
return (s); return (s);
} }
/* Use the session from the TMUX environment variable. */ /* Use the session from the TMUX environment variable. */
if (data != NULL && data->pid != -1) { if (data != NULL && data->pid == getpid()) {
if (data->pid != getpid()) s = session_find_by_index(data->idx);
return (NULL); if (s != NULL)
if (data->idx > ARRAY_LENGTH(&sessions)) return (s);
return (NULL);
if ((s = ARRAY_ITEM(&sessions, data->idx)) == NULL)
return (NULL);
return (s);
} }
return (cmd_choose_session(&sessions)); return (cmd_choose_session());
}
/* Find the most recently used session. */
struct session *
cmd_choose_session(void)
{
struct session *s, *sbest;
struct timeval *tv = NULL;
sbest = NULL;
RB_FOREACH(s, sessions, &sessions) {
if (tv == NULL || timercmp(&s->activity_time, tv, >)) {
sbest = s;
tv = &s->activity_time;
}
}
return (sbest);
} }
/* Find the most recently used session from a list. */ /* Find the most recently used session from a list. */
struct session * struct session *
cmd_choose_session(struct sessions *ss) cmd_choose_session_list(struct sessionslist *ss)
{ {
struct session *s, *sbest; struct session *s, *sbest;
struct timeval *tv = NULL; struct timeval *tv = NULL;
@@ -502,7 +531,6 @@ struct session *
cmd_lookup_session(const char *name, int *ambiguous) cmd_lookup_session(const char *name, int *ambiguous)
{ {
struct session *s, *sfound; struct session *s, *sfound;
u_int i;
*ambiguous = 0; *ambiguous = 0;
@@ -511,21 +539,15 @@ cmd_lookup_session(const char *name, int *ambiguous)
* be unique so an exact match can't be ambigious and can just be * be unique so an exact match can't be ambigious and can just be
* returned. * returned.
*/ */
for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { if ((s = session_find(name)) != NULL)
if ((s = ARRAY_ITEM(&sessions, i)) == NULL) return (s);
continue;
if (strcmp(name, s->name) == 0)
return (s);
}
/* /*
* Otherwise look for partial matches, returning early if it is found to * Otherwise look for partial matches, returning early if it is found to
* be ambiguous. * be ambiguous.
*/ */
sfound = NULL; sfound = NULL;
for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { RB_FOREACH(s, sessions, &sessions) {
if ((s = ARRAY_ITEM(&sessions, i)) == NULL)
continue;
if (strncmp(name, s->name, strlen(name)) == 0 || if (strncmp(name, s->name, strlen(name)) == 0 ||
fnmatch(name, s->name, 0) == 0) { fnmatch(name, s->name, 0) == 0) {
if (sfound != NULL) { if (sfound != NULL) {

View File

@@ -1,4 +1,4 @@
/* $Id: compat.h,v 1.25 2010-06-06 13:00:46 tcunha Exp $ */ /* $Id: compat.h,v 1.31 2010-11-11 20:45:49 nicm Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -16,6 +16,20 @@
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */
#ifndef COMPAT_H
#define COMPAT_H
#ifndef __GNUC__
#define __attribute__(a)
#endif
#ifndef __dead
#define __dead __attribute__ ((__noreturn__))
#endif
#ifndef __packed
#define __packed __attribute__ ((__packed__))
#endif
#ifndef HAVE_U_INT #ifndef HAVE_U_INT
typedef uint8_t u_int8_t; typedef uint8_t u_int8_t;
typedef uint16_t u_int16_t; typedef uint16_t u_int16_t;
@@ -49,14 +63,6 @@ typedef uint64_t u_int64_t;
#include "compat/bitstring.h" #include "compat/bitstring.h"
#endif #endif
#ifdef HAVE_GETOPT
#include <getopt.h>
#endif
#ifdef HAVE_CRYPT_H
#include <crypt.h>
#endif
#ifdef HAVE_PATHS_H #ifdef HAVE_PATHS_H
#include <paths.h> #include <paths.h>
#endif #endif
@@ -79,10 +85,16 @@ typedef uint64_t u_int64_t;
#include "compat/vis.h" #include "compat/vis.h"
#endif #endif
#ifndef HAVE_IMSG #ifdef HAVE_IMSG
#include "compat/imsg.h"
#else
#include <imsg.h> #include <imsg.h>
#else
#include "compat/imsg.h"
#endif
#ifdef HAVE_STDINT_H
#include <stdint.h>
#else
#include <inttypes.h>
#endif #endif
#ifdef HAVE_BROKEN_CMSG_FIRSTHDR #ifdef HAVE_BROKEN_CMSG_FIRSTHDR
@@ -123,13 +135,6 @@ typedef uint64_t u_int64_t;
#define SUN_LEN(sun) (sizeof (sun)->sun_path) #define SUN_LEN(sun) (sizeof (sun)->sun_path)
#endif #endif
#ifndef __dead
#define __dead __attribute__ ((__noreturn__))
#endif
#ifndef __packed
#define __packed __attribute__ ((__packed__))
#endif
#ifndef timercmp #ifndef timercmp
#define timercmp(tvp, uvp, cmp) \ #define timercmp(tvp, uvp, cmp) \
(((tvp)->tv_sec == (uvp)->tv_sec) ? \ (((tvp)->tv_sec == (uvp)->tv_sec) ? \
@@ -154,9 +159,18 @@ typedef uint64_t u_int64_t;
#endif #endif
#ifndef HAVE_BZERO #ifndef HAVE_BZERO
#undef bzero
#define bzero(buf, len) memset(buf, 0, len); #define bzero(buf, len) memset(buf, 0, len);
#endif #endif
#ifndef HAVE_CLOSEFROM
/* closefrom.c */
#define HAVE_FCNTL_H
#define HAVE_DIRENT_H
#define HAVE_SYSCONF
void closefrom(int);
#endif
#ifndef HAVE_STRCASESTR #ifndef HAVE_STRCASESTR
/* strcasestr.c */ /* strcasestr.c */
char *strcasestr(const char *, const char *); char *strcasestr(const char *, const char *);
@@ -189,6 +203,7 @@ int daemon(int, int);
#ifndef HAVE_FORKPTY #ifndef HAVE_FORKPTY
/* forkpty.c */ /* forkpty.c */
#include <sys/ioctl.h>
pid_t forkpty(int *, char *, struct termios *, struct winsize *); pid_t forkpty(int *, char *, struct termios *, struct winsize *);
#endif #endif
@@ -209,7 +224,9 @@ int setenv(const char *, const char *, int);
int unsetenv(const char *); int unsetenv(const char *);
#endif #endif
#ifndef HAVE_GETOPT #ifdef HAVE_GETOPT
#include <getopt.h>
#else
/* getopt.c */ /* getopt.c */
extern int BSDopterr; extern int BSDopterr;
extern int BSDoptind; extern int BSDoptind;
@@ -224,3 +241,5 @@ int BSDgetopt(int, char *const *, const char *);
#define optreset BSDoptreset #define optreset BSDoptreset
#define optarg BSDoptarg #define optarg BSDoptarg
#endif #endif
#endif /* COMPAT_H */

111
compat/closefrom.c Normal file
View File

@@ -0,0 +1,111 @@
/* $Id: closefrom.c,v 1.1 2010-10-27 20:21:01 nicm Exp $ */
/*
* Copyright (c) 2004-2005 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "tmux.h"
#ifndef HAVE_CLOSEFROM
#include <sys/types.h>
#include <sys/param.h>
#include <unistd.h>
#include <stdio.h>
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif
#include <limits.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#ifdef HAVE_DIRENT_H
# include <dirent.h>
# define NAMLEN(dirent) strlen((dirent)->d_name)
#else
# define dirent direct
# define NAMLEN(dirent) (dirent)->d_namlen
# ifdef HAVE_SYS_NDIR_H
# include <sys/ndir.h>
# endif
# ifdef HAVE_SYS_DIR_H
# include <sys/dir.h>
# endif
# ifdef HAVE_NDIR_H
# include <ndir.h>
# endif
#endif
#ifndef OPEN_MAX
# define OPEN_MAX 256
#endif
#if 0
__unused static const char rcsid[] = "$Sudo: closefrom.c,v 1.11 2006/08/17 15:26:54 millert Exp $";
#endif /* lint */
/*
* Close all file descriptors greater than or equal to lowfd.
*/
#ifdef HAVE_FCNTL_CLOSEM
void
closefrom(int lowfd)
{
(void) fcntl(lowfd, F_CLOSEM, 0);
}
#else
void
closefrom(int lowfd)
{
long fd, maxfd;
#if defined(HAVE_DIRFD) && defined(HAVE_PROC_PID)
char fdpath[PATH_MAX], *endp;
struct dirent *dent;
DIR *dirp;
int len;
/* Check for a /proc/$$/fd directory. */
len = snprintf(fdpath, sizeof(fdpath), "/proc/%ld/fd", (long)getpid());
if (len > 0 && (size_t)len <= sizeof(fdpath) && (dirp = opendir(fdpath))) {
while ((dent = readdir(dirp)) != NULL) {
fd = strtol(dent->d_name, &endp, 10);
if (dent->d_name != endp && *endp == '\0' &&
fd >= 0 && fd < INT_MAX && fd >= lowfd && fd != dirfd(dirp))
(void) close((int) fd);
}
(void) closedir(dirp);
} else
#endif
{
/*
* Fall back on sysconf() or getdtablesize(). We avoid checking
* resource limits since it is possible to open a file descriptor
* and then drop the rlimit such that it is below the open fd.
*/
#ifdef HAVE_SYSCONF
maxfd = sysconf(_SC_OPEN_MAX);
#else
maxfd = getdtablesize();
#endif /* HAVE_SYSCONF */
if (maxfd < 0)
maxfd = OPEN_MAX;
for (fd = lowfd; fd < maxfd; fd++)
(void) close((int) fd);
}
}
#endif /* !HAVE_FCNTL_CLOSEM */
#endif /* HAVE_CLOSEFROM */

View File

@@ -32,10 +32,6 @@
#include <unistd.h> #include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
#ifdef HAVE_PATHS_H
#include <paths.h>
#endif
#include "tmux.h" #include "tmux.h"
int int

View File

@@ -1,4 +1,4 @@
/* $Id: imsg.c,v 1.5 2010-06-06 00:08:28 tcunha Exp $ */ /* $Id: imsg.c,v 1.7 2010-11-13 16:29:05 nicm Exp $ */
/* $OpenBSD: imsg.c,v 1.3 2010/05/26 13:56:07 nicm Exp $ */ /* $OpenBSD: imsg.c,v 1.3 2010/05/26 13:56:07 nicm Exp $ */
/* /*
@@ -111,7 +111,7 @@ imsg_get(struct imsgbuf *ibuf, struct imsg *imsg)
return (0); return (0);
datalen = imsg->hdr.len - IMSG_HEADER_SIZE; datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE; ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE;
if ((imsg->data = malloc(datalen)) == NULL) if ((imsg->data = malloc(datalen)) == NULL && datalen != 0)
return (-1); return (-1);
if (imsg->hdr.flags & IMSGF_HASFD) if (imsg->hdr.flags & IMSGF_HASFD)

40
configure vendored
View File

@@ -1,5 +1,5 @@
#!/bin/sh #!/bin/sh
# $Id: configure,v 1.53 2010-06-06 13:00:47 tcunha Exp $ # $Id: configure,v 1.59 2010-12-08 19:55:31 nicm Exp $
# #
# Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net> # Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
# #
@@ -33,18 +33,22 @@ cat <<EOF >>$CONFIG_H
#undef HAVE_BROKEN_KQUEUE #undef HAVE_BROKEN_KQUEUE
#undef HAVE_BROKEN_POLL #undef HAVE_BROKEN_POLL
#undef HAVE_BZERO #undef HAVE_BZERO
#undef HAVE_CRYPT_H #undef HAVE_CLOSEFROM
#undef HAVE_DAEMON #undef HAVE_DAEMON
#undef HAVE_DIRFD
#undef HAVE_FCNTL_CLOSEM
#undef HAVE_FGETLN #undef HAVE_FGETLN
#undef HAVE_FORKPTY #undef HAVE_FORKPTY
#undef HAVE_GETOPT #undef HAVE_GETOPT
#undef HAVE_IMSG #undef HAVE_IMSG
#undef HAVE_LIBUTIL_H #undef HAVE_LIBUTIL_H
#undef HAVE_PATHS_H #undef HAVE_PATHS_H
#undef HAVE_PROC_PID
#undef HAVE_PROGNAME #undef HAVE_PROGNAME
#undef HAVE_PTY_H #undef HAVE_PTY_H
#undef HAVE_QUEUE_H #undef HAVE_QUEUE_H
#undef HAVE_SETPROCTITLE #undef HAVE_SETPROCTITLE
#undef HAVE_STDINT_H
#undef HAVE_STRCASESTR #undef HAVE_STRCASESTR
#undef HAVE_STRLCAT #undef HAVE_STRLCAT
#undef HAVE_STRLCPY #undef HAVE_STRLCPY
@@ -63,6 +67,7 @@ case $TMUX_PLATFORM in
#define HAVE_ASPRINTF #define HAVE_ASPRINTF
#define HAVE_BITSTRING_H #define HAVE_BITSTRING_H
#define HAVE_BZERO #define HAVE_BZERO
#define HAVE_CLOSEFROM
#define HAVE_DAEMON #define HAVE_DAEMON
#define HAVE_FGETLN #define HAVE_FGETLN
#define HAVE_FORKPTY #define HAVE_FORKPTY
@@ -73,6 +78,7 @@ case $TMUX_PLATFORM in
#define HAVE_QUEUE_H #define HAVE_QUEUE_H
#define HAVE_SETENV #define HAVE_SETENV
#define HAVE_SETPROCTITLE #define HAVE_SETPROCTITLE
#define HAVE_STDINT_H
#define HAVE_STRCASESTR #define HAVE_STRCASESTR
#define HAVE_STRLCAT #define HAVE_STRLCAT
#define HAVE_STRLCPY #define HAVE_STRLCPY
@@ -94,24 +100,28 @@ EOF
#define HAVE_ASPRINTF #define HAVE_ASPRINTF
#define HAVE_BZERO #define HAVE_BZERO
#define HAVE_DAEMON #define HAVE_DAEMON
#define HAVE_DIRFD
#define HAVE_FORKPTY #define HAVE_FORKPTY
#define HAVE_PATHS_H #define HAVE_PATHS_H
#define HAVE_PROC_PID
#define HAVE_PROGNAME #define HAVE_PROGNAME
#define HAVE_PTY_H #define HAVE_PTY_H
#define HAVE_SETENV #define HAVE_SETENV
#define HAVE_STDINT_H
#define HAVE_STRCASESTR #define HAVE_STRCASESTR
#define HAVE_STRSEP #define HAVE_STRSEP
#define HAVE_U_INT #define HAVE_U_INT
EOF EOF
cat <<EOF >>$CONFIG_MK cat <<EOF >>$CONFIG_MK
CFLAGS+= -std=c99 -D_GNU_SOURCE -D_POSIX_SOURCE CFLAGS+= -std=c99 -D_GNU_SOURCE -D_POSIX_SOURCE
LIBS+= -lncurses -lcrypt -lutil -levent -lrt LIBS+= -lncurses -lutil -levent -lrt
SRCS+= osdep-linux.c \ SRCS+= osdep-linux.c \
compat/closefrom.c \
compat/fgetln.c \ compat/fgetln.c \
compat/strlcat.c \ compat/strlcat.c \
compat/strlcpy.c \ compat/strlcpy.c \
compat/strtonum.c \ compat/strtonum.c \
compat/getopt.c \ compat/getopt.c \
compat/vis.c \ compat/vis.c \
compat/unvis.c \ compat/unvis.c \
compat/imsg-buffer.c \ compat/imsg-buffer.c \
@@ -121,14 +131,14 @@ EOF
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
AIX) AIX)
cat <<EOF >>$CONFIG_H cat <<EOF >>$CONFIG_H
#define HAVE_BZERO
#define HAVE_DAEMON
#define HAVE_SETENV #define HAVE_SETENV
#define HAVE_STDINT_H
EOF EOF
cat <<EOF >>$CONFIG_MK cat <<EOF >>$CONFIG_MK
LIBS+= -lcurses -levent LIBS+= -lcurses -levent
SRCS+= osdep-unknown.c \ SRCS+= osdep-unknown.c \
compat/asprintf.c \ compat/asprintf.c \
compat/closefrom.c \
compat/daemon.c \ compat/daemon.c \
compat/forkpty-aix.c \ compat/forkpty-aix.c \
compat/strcasestr.c \ compat/strcasestr.c \
@@ -147,7 +157,7 @@ EOF
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
SunOS) SunOS)
cat <<EOF >>$CONFIG_H cat <<EOF >>$CONFIG_H
#define HAVE_CRYPT_H #define HAVE_CLOSEFROM
#define HAVE_STRLCAT #define HAVE_STRLCAT
#define HAVE_STRLCPY #define HAVE_STRLCPY
EOF EOF
@@ -179,12 +189,15 @@ EOF
#define HAVE_BROKEN_POLL #define HAVE_BROKEN_POLL
#define HAVE_BZERO #define HAVE_BZERO
#define HAVE_DAEMON #define HAVE_DAEMON
#define HAVE_DIRFD
#define HAVE_FGETLN #define HAVE_FGETLN
#define HAVE_FORKPTY #define HAVE_FORKPTY
#define HAVE_GETOPT #define HAVE_GETOPT
#define HAVE_PATHS_H #define HAVE_PATHS_H
#define HAVE_PROC_PID
#define HAVE_PROGNAME #define HAVE_PROGNAME
#define HAVE_SETENV #define HAVE_SETENV
#define HAVE_STDINT_H
#define HAVE_STRCASESTR #define HAVE_STRCASESTR
#define HAVE_STRLCAT #define HAVE_STRLCAT
#define HAVE_STRLCPY #define HAVE_STRLCPY
@@ -197,6 +210,7 @@ CPPFLAGS+= -I/opt/local/include
LDFLAGS+= -L/opt/local/lib LDFLAGS+= -L/opt/local/lib
LIBS+= -lcurses -levent LIBS+= -lcurses -levent
SRCS+= osdep-darwin.c \ SRCS+= osdep-darwin.c \
compat/closefrom.c \
compat/strtonum.c \ compat/strtonum.c \
compat/vis.c \ compat/vis.c \
compat/unvis.c \ compat/unvis.c \
@@ -210,6 +224,7 @@ EOF
#define HAVE_ASPRINTF #define HAVE_ASPRINTF
#define HAVE_BROKEN_KQUEUE #define HAVE_BROKEN_KQUEUE
#define HAVE_BZERO #define HAVE_BZERO
#define HAVE_CLOSEFROM
#define HAVE_DAEMON #define HAVE_DAEMON
#define HAVE_FGETLN #define HAVE_FGETLN
#define HAVE_FORKPTY #define HAVE_FORKPTY
@@ -219,15 +234,16 @@ EOF
#define HAVE_PROGNAME #define HAVE_PROGNAME
#define HAVE_SETENV #define HAVE_SETENV
#define HAVE_SETPROCTITLE #define HAVE_SETPROCTITLE
#define HAVE_STDINT_H
#define HAVE_STRCASESTR #define HAVE_STRCASESTR
#define HAVE_STRLCAT #define HAVE_STRLCAT
#define HAVE_STRLCPY #define HAVE_STRLCPY
#define HAVE_STRTONUM
#define HAVE_STRSEP #define HAVE_STRSEP
#define HAVE_STRTONUM
#define HAVE_U_INT #define HAVE_U_INT
EOF EOF
cat <<EOF >>$CONFIG_MK cat <<EOF >>$CONFIG_MK
LIBS+= -lcurses -lcrypt -lutil -levent LIBS+= -lcurses -lutil -levent
SRCS+= osdep-freebsd.c \ SRCS+= osdep-freebsd.c \
compat/vis.c \ compat/vis.c \
compat/unvis.c \ compat/unvis.c \
@@ -240,14 +256,16 @@ EOF
cat <<EOF >>$CONFIG_H cat <<EOF >>$CONFIG_H
#define HAVE_ASPRINTF #define HAVE_ASPRINTF
#define HAVE_BZERO #define HAVE_BZERO
#define HAVE_CLOSEFROM
#define HAVE_DAEMON #define HAVE_DAEMON
#define HAVE_FGETLN #define HAVE_FGETLN
#define HAVE_FORKPTY #define HAVE_FORKPTY
#define HAVE_GETOPT #define HAVE_GETOPT
#define HAVE_PATHS_H #define HAVE_PATHS_H
#define HAVE_PROGNAME #define HAVE_PROGNAME
#define HAVE_SETPROCTITLE
#define HAVE_SETENV #define HAVE_SETENV
#define HAVE_SETPROCTITLE
#define HAVE_STDINT_H
#define HAVE_STRCASESTR #define HAVE_STRCASESTR
#define HAVE_STRLCAT #define HAVE_STRLCAT
#define HAVE_STRLCPY #define HAVE_STRLCPY
@@ -271,7 +289,7 @@ EOF
EOF EOF
fi fi
cat <<EOF >>$CONFIG_MK cat <<EOF >>$CONFIG_MK
LIBS+= -lcrypt -lutil -levent LIBS+= -lutil -levent
SRCS+= osdep-netbsd.c \ SRCS+= osdep-netbsd.c \
compat/strtonum.c \ compat/strtonum.c \
compat/vis.c \ compat/vis.c \

View File

@@ -0,0 +1,105 @@
# START tmux completion
# This file is in the public domain
# See: http://www.debian-administration.org/articles/317 for how to write more.
# Usage: Put "source bash_completion_tmux.sh" into your .bashrc
_tmux()
{
local cur prev opts
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
opts=" \
attach-session \
bind-key \
break-pane \
capture-pane \
choose-client \
choose-session \
choose-window \
clear-history \
clock-mode \
command-prompt \
confirm-before \
copy-buffer \
copy-mode \
delete-buffer \
detach-client \
display-message \
display-panes \
down-pane \
find-window \
has-session \
if-shell \
join-pane \
kill-pane \
kill-server \
kill-session \
kill-window \
last-window \
link-window \
list-buffers \
list-clients \
list-commands \
list-keys \
list-panes \
list-sessions \
list-windows \
load-buffer \
lock-client \
lock-server \
lock-session \
move-window \
new-session \
new-window \
next-layout \
next-window \
paste-buffer \
pipe-pane \
previous-layout \
previous-window \
refresh-client \
rename-session \
rename-window \
resize-pane \
respawn-window \
rotate-window \
run-shell \
save-buffer \
select-layout \
select-pane \
select-prompt \
select-window \
send-keys \
send-prefix \
server-info \
set-buffer \
set-environment \
set-option \
set-window-option \
show-buffer \
show-environment \
show-messages \
show-options \
show-window-options \
source-file \
split-window \
start-server \
suspend-client \
swap-pane \
swap-window \
switch-client \
unbind-key \
unlink-window \
up-pane"
COMPREPLY=($(compgen -W "${opts}" -- ${cur}))
return 0
}
complete -F _tmux tmux
# END tmux completion

View File

@@ -1,4 +1,4 @@
# $Id: screen-keys.conf,v 1.6 2010-02-02 21:34:16 nicm Exp $ # $Id: screen-keys.conf,v 1.7 2010-07-31 11:39:13 nicm Exp $
# #
# By Nicholas Marriott. Public domain. # By Nicholas Marriott. Public domain.
# #
@@ -93,9 +93,9 @@ bind | split-window
# :kB: focus up # :kB: focus up
unbind Tab unbind Tab
bind Tab down-pane bind Tab select-pane -t:.+
unbind BTab unbind BTab
bind BTab up-pane bind BTab select-pane -t:.-
# " windowlist -b # " windowlist -b
unbind '"' unbind '"'

View File

@@ -1,7 +1,7 @@
" Vim syntax file " Vim syntax file
" Language: tmux(1) configuration file " Language: tmux(1) configuration file
" Maintainer: Tiago Cunha <me@tiagocunha.org> " Maintainer: Tiago Cunha <me@tiagocunha.org>
" Last Change: $Date: 2010-07-02 02:46:39 $ " Last Change: $Date: 2010-07-27 18:29:07 $
" License: This file is placed in the public domain. " License: This file is placed in the public domain.
if version < 600 if version < 600
@@ -31,9 +31,9 @@ syn keyword tmuxCmds command-prompt setb set-buffer showb show-buffer lsb
syn keyword tmuxCmds list-buffers deleteb delete-buffer lscm list-commands syn keyword tmuxCmds list-buffers deleteb delete-buffer lscm list-commands
syn keyword tmuxCmds movew move-window respawnw respawn-window syn keyword tmuxCmds movew move-window respawnw respawn-window
syn keyword tmuxCmds source[-file] info server-info clock-mode lock[-server] syn keyword tmuxCmds source[-file] info server-info clock-mode lock[-server]
syn keyword tmuxCmds saveb save-buffer downp down-pane killp syn keyword tmuxCmds saveb save-buffer killp
syn keyword tmuxCmds kill-pane resizep resize-pane selectp select-pane swapp syn keyword tmuxCmds kill-pane resizep resize-pane selectp select-pane swapp
syn keyword tmuxCmds swap-pane splitw split-window upp up-pane choose-session syn keyword tmuxCmds swap-pane splitw split-window choose-session
syn keyword tmuxCmds choose-window loadb load-buffer copyb copy-buffer suspendc syn keyword tmuxCmds choose-window loadb load-buffer copyb copy-buffer suspendc
syn keyword tmuxCmds suspend-client findw find-window breakp break-pane nextl syn keyword tmuxCmds suspend-client findw find-window breakp break-pane nextl
syn keyword tmuxCmds next-layout rotatew rotate-window confirm[-before] syn keyword tmuxCmds next-layout rotatew rotate-window confirm[-before]
@@ -72,7 +72,8 @@ syn keyword tmuxOptsSetw main-pane-width main-pane-height monitor-content
syn keyword tmuxOptsSetw window-status-current-attr window-status-current-bg syn keyword tmuxOptsSetw window-status-current-attr window-status-current-bg
syn keyword tmuxOptsSetw window-status-current-fg mode-mouse synchronize-panes syn keyword tmuxOptsSetw window-status-current-fg mode-mouse synchronize-panes
syn keyword tmuxOptsSetw window-status-format window-status-current-format syn keyword tmuxOptsSetw window-status-format window-status-current-format
syn keyword tmuxOptsSetw word-separators syn keyword tmuxOptsSetw word-separators window-status-alert-alert
syn keyword tmuxOptsSetw window-status-alert-bg window-status-alert-fg
syn keyword tmuxTodo FIXME NOTE TODO XXX contained syn keyword tmuxTodo FIXME NOTE TODO XXX contained

View File

@@ -1,23 +1,24 @@
# $Id: vim-keys.conf,v 1.1 2010-01-17 16:24:09 nicm Exp $ # $Id: vim-keys.conf,v 1.2 2010-09-18 09:36:15 nicm Exp $
# #
# vim-keys.conf, v1.0 2010/01/15 # vim-keys.conf, v1.2 2010/09/12
# #
# By Daniel Thau. Public domain. # By Daniel Thau. Public domain.
# #
# This configuration file binds many vi- and vim-like bindings to the # This configuration file binds many vi- and vim-like bindings to the
# appropriate tmux key bindings. Note that for many key bindings there is no # appropriate tmux key bindings. Note that for many key bindings there is no
# tmux analogue. # tmux analogue. This is intended for tmux 1.3, which handles pane selection
# differently from the previous versions
# split windows like vim # split windows like vim
# vim's definition of a horizontal/vertical split is reversed from tmux's # vim's definition of a horizontal/vertical split is reversed from tmux's
bind s split-window -v bind s split-window -v
bind v split-window -h bind v split-window -h
# move around panes with j and k, a bit like vim # move around panes with hjkl, as one would in vim after pressing ctrl-w
# as of tmux 1.1, there is no way to move based on pane position (ie, no way to bind h select-pane -L
# move the pane to the right) bind j select-pane -D
bind j down-pane bind k select-pane -U
bind k up-pane bind l select-pane -R
# resize panes like vim # resize panes like vim
# feel free to change the "1" to however many lines you want to resize by, only # feel free to change the "1" to however many lines you want to resize by, only

View File

@@ -1,4 +1,4 @@
/* $Id: input-keys.c,v 1.44 2009-12-04 22:14:47 tcunha Exp $ */ /* $Id: input-keys.c,v 1.45 2010-09-07 19:32:58 nicm Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -18,7 +18,6 @@
#include <sys/types.h> #include <sys/types.h>
#include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>

14
input.c
View File

@@ -1,4 +1,4 @@
/* $Id: input.c,v 1.109 2010-04-18 15:11:47 tcunha Exp $ */ /* $Id: input.c,v 1.111 2010-12-25 23:43:53 tcunha Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -681,7 +681,9 @@ input_parse(struct window_pane *wp)
if (EVBUFFER_LENGTH(evb) == 0) if (EVBUFFER_LENGTH(evb) == 0)
return; return;
wp->window->flags |= WINDOW_ACTIVITY; wp->window->flags |= WINDOW_ACTIVITY;
wp->window->flags &= ~WINDOW_SILENCE;
/* /*
* Open the screen. Use NULL wp if there is a mode set as don't want to * Open the screen. Use NULL wp if there is a mode set as don't want to
@@ -718,11 +720,11 @@ input_parse(struct window_pane *wp)
* Execute the handler, if any. Don't switch state if it * Execute the handler, if any. Don't switch state if it
* returns non-zero. * returns non-zero.
*/ */
if (itr->handler && itr->handler(ictx) != 0) if (itr->handler != NULL && itr->handler(ictx) != 0)
continue; continue;
/* And switch state, if necessary. */ /* And switch state, if necessary. */
if (itr->state) { if (itr->state != NULL) {
if (ictx->state->exit != NULL) if (ictx->state->exit != NULL)
ictx->state->exit(ictx); ictx->state->exit(ictx);
ictx->state = itr->state; ictx->state = itr->state;
@@ -922,9 +924,9 @@ input_c0_dispatch(struct input_ctx *ictx)
int int
input_esc_dispatch(struct input_ctx *ictx) input_esc_dispatch(struct input_ctx *ictx)
{ {
struct screen_write_ctx *sctx = &ictx->ctx; struct screen_write_ctx *sctx = &ictx->ctx;
struct screen *s = sctx->s; struct screen *s = sctx->s;
struct input_table_entry *entry; struct input_table_entry *entry;
if (ictx->flags & INPUT_DISCARD) if (ictx->flags & INPUT_DISCARD)
return (0); return (0);

8
job.c
View File

@@ -1,4 +1,4 @@
/* $Id: job.c,v 1.17 2010-05-14 14:30:01 tcunha Exp $ */ /* $Id: job.c,v 1.19 2010-10-24 00:45:57 tcunha Exp $ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -148,7 +148,7 @@ job_run(struct job *job)
case -1: case -1:
return (-1); return (-1);
case 0: /* child */ case 0: /* child */
clear_signals(); clear_signals(1);
environ_push(&global_environ); environ_push(&global_environ);
@@ -168,6 +168,8 @@ job_run(struct job *job)
if (nullfd != STDIN_FILENO && nullfd != STDERR_FILENO) if (nullfd != STDIN_FILENO && nullfd != STDERR_FILENO)
close(nullfd); close(nullfd);
closefrom(STDERR_FILENO + 1);
execl(_PATH_BSHELL, "sh", "-c", job->cmd, (char *) NULL); execl(_PATH_BSHELL, "sh", "-c", job->cmd, (char *) NULL);
fatal("execl failed"); fatal("execl failed");
default: /* parent */ default: /* parent */
@@ -178,8 +180,6 @@ job_run(struct job *job)
fatal("fcntl failed"); fatal("fcntl failed");
if (fcntl(job->fd, F_SETFL, mode|O_NONBLOCK) == -1) if (fcntl(job->fd, F_SETFL, mode|O_NONBLOCK) == -1)
fatal("fcntl failed"); fatal("fcntl failed");
if (fcntl(job->fd, F_SETFD, FD_CLOEXEC) == -1)
fatal("fcntl failed");
if (job->event != NULL) if (job->event != NULL)
bufferevent_free(job->event); bufferevent_free(job->event);

View File

@@ -1,4 +1,4 @@
/* $Id: key-bindings.c,v 1.94 2010-07-02 02:43:01 tcunha Exp $ */ /* $Id: key-bindings.c,v 1.97 2010-12-11 18:42:20 nicm Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -108,6 +108,8 @@ key_bindings_init(void)
{ '#', 0, &cmd_list_buffers_entry }, { '#', 0, &cmd_list_buffers_entry },
{ '%', 0, &cmd_split_window_entry }, { '%', 0, &cmd_split_window_entry },
{ '&', 0, &cmd_confirm_before_entry }, { '&', 0, &cmd_confirm_before_entry },
{ '(', 0, &cmd_switch_client_entry },
{ ')', 0, &cmd_switch_client_entry },
{ ',', 0, &cmd_command_prompt_entry }, { ',', 0, &cmd_command_prompt_entry },
{ '-', 0, &cmd_delete_buffer_entry }, { '-', 0, &cmd_delete_buffer_entry },
{ '.', 0, &cmd_command_prompt_entry }, { '.', 0, &cmd_command_prompt_entry },
@@ -122,9 +124,11 @@ key_bindings_init(void)
{ '8', 0, &cmd_select_window_entry }, { '8', 0, &cmd_select_window_entry },
{ '9', 0, &cmd_select_window_entry }, { '9', 0, &cmd_select_window_entry },
{ ':', 0, &cmd_command_prompt_entry }, { ':', 0, &cmd_command_prompt_entry },
{ ';', 0, &cmd_last_pane_entry },
{ '=', 0, &cmd_choose_buffer_entry }, { '=', 0, &cmd_choose_buffer_entry },
{ '?', 0, &cmd_list_keys_entry }, { '?', 0, &cmd_list_keys_entry },
{ 'D', 0, &cmd_choose_client_entry }, { 'D', 0, &cmd_choose_client_entry },
{ 'L', 0, &cmd_switch_client_entry },
{ '[', 0, &cmd_copy_mode_entry }, { '[', 0, &cmd_copy_mode_entry },
{ '\'', 0, &cmd_command_prompt_entry }, { '\'', 0, &cmd_command_prompt_entry },
{ '\002', /* C-b */ 0, &cmd_send_prefix_entry }, { '\002', /* C-b */ 0, &cmd_send_prefix_entry },

View File

@@ -1,4 +1,4 @@
/* $Id: layout-set.c,v 1.6 2010-05-14 14:16:37 tcunha Exp $ */ /* $Id: layout-set.c,v 1.8 2010-12-22 15:23:59 tcunha Exp $ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -135,10 +135,9 @@ layout_set_even_h(struct window *w)
return; return;
/* How many can we fit? */ /* How many can we fit? */
if (w->sx / n < PANE_MINIMUM + 1) width = (w->sx - (n - 1)) / n;
width = PANE_MINIMUM + 1; if (width < PANE_MINIMUM)
else width = PANE_MINIMUM;
width = w->sx / n;
/* Free the old root and construct a new. */ /* Free the old root and construct a new. */
layout_free(w); layout_free(w);
@@ -151,12 +150,12 @@ layout_set_even_h(struct window *w)
TAILQ_FOREACH(wp, &w->panes, entry) { TAILQ_FOREACH(wp, &w->panes, entry) {
/* Create child cell. */ /* Create child cell. */
lcnew = layout_create_cell(lc); lcnew = layout_create_cell(lc);
layout_set_size(lcnew, width - 1, w->sy, xoff, 0); layout_set_size(lcnew, width, w->sy, xoff, 0);
layout_make_leaf(lcnew, wp); layout_make_leaf(lcnew, wp);
TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry); TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
i++; i++;
xoff += width; xoff += width + 1;
} }
/* Allocate any remaining space. */ /* Allocate any remaining space. */
@@ -189,10 +188,9 @@ layout_set_even_v(struct window *w)
return; return;
/* How many can we fit? */ /* How many can we fit? */
if (w->sy / n < PANE_MINIMUM + 1) height = (w->sy - (n - 1)) / n;
height = PANE_MINIMUM + 1; if (height < PANE_MINIMUM)
else height = PANE_MINIMUM;
height = w->sy / n;
/* Free the old root and construct a new. */ /* Free the old root and construct a new. */
layout_free(w); layout_free(w);
@@ -205,12 +203,12 @@ layout_set_even_v(struct window *w)
TAILQ_FOREACH(wp, &w->panes, entry) { TAILQ_FOREACH(wp, &w->panes, entry) {
/* Create child cell. */ /* Create child cell. */
lcnew = layout_create_cell(lc); lcnew = layout_create_cell(lc);
layout_set_size(lcnew, w->sx, height - 1, 0, yoff); layout_set_size(lcnew, w->sx, height, 0, yoff);
layout_make_leaf(lcnew, wp); layout_make_leaf(lcnew, wp);
TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry); TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
i++; i++;
yoff += height; yoff += height + 1;
} }
/* Allocate any remaining space. */ /* Allocate any remaining space. */
@@ -233,8 +231,8 @@ layout_set_main_h(struct window *w)
{ {
struct window_pane *wp; struct window_pane *wp;
struct layout_cell *lc, *lcmain, *lcrow, *lcchild; struct layout_cell *lc, *lcmain, *lcrow, *lcchild;
u_int n, mainheight, width, height, used; u_int n, mainheight, otherheight, width, height;
u_int i, j, columns, rows, totalrows; u_int used, i, j, columns, rows, totalrows;
layout_print_cell(w->layout_root, __func__, 1); layout_print_cell(w->layout_root, __func__, 1);
@@ -244,16 +242,26 @@ layout_set_main_h(struct window *w)
return; return;
n--; /* take off main pane */ n--; /* take off main pane */
/* How many rows and columns will be needed? */ /* How many rows and columns will be needed, not counting main? */
columns = w->sx / (PANE_MINIMUM + 1); /* maximum columns */ columns = (w->sx + 1) / (PANE_MINIMUM + 1); /* maximum columns */
if (columns == 0) if (columns == 0)
columns = 1; columns = 1;
rows = 1 + (n - 1) / columns; rows = 1 + (n - 1) / columns;
columns = 1 + (n - 1) / rows; columns = 1 + (n - 1) / rows;
width = w->sx / columns; width = (w->sx - (n - 1)) / columns;
/* Get the main pane height and add one for separator line. */ /* Get the main pane height and add one for separator line. */
mainheight = options_get_number(&w->options, "main-pane-height") + 1; mainheight = options_get_number(&w->options, "main-pane-height") + 1;
/* Get the optional other pane height and add one for separator line. */
otherheight = options_get_number(&w->options, "other-pane-height") + 1;
/*
* If an other pane height was specified, honour it so long as it
* doesn't shrink the main height to less than the main-pane-height
*/
if (otherheight > 1 && w->sx - otherheight > mainheight)
mainheight = w->sx - otherheight;
if (mainheight < PANE_MINIMUM + 1) if (mainheight < PANE_MINIMUM + 1)
mainheight = PANE_MINIMUM + 1; mainheight = PANE_MINIMUM + 1;
@@ -264,14 +272,14 @@ layout_set_main_h(struct window *w)
mainheight = PANE_MINIMUM + 2; mainheight = PANE_MINIMUM + 2;
else else
mainheight = w->sy - totalrows; mainheight = w->sy - totalrows;
height = PANE_MINIMUM + 1; height = PANE_MINIMUM;
} else } else
height = (w->sy - mainheight) / rows; height = (w->sy - mainheight - (rows - 1)) / rows;
/* Free old tree and create a new root. */ /* Free old tree and create a new root. */
layout_free(w); layout_free(w);
lc = w->layout_root = layout_create_cell(NULL); lc = w->layout_root = layout_create_cell(NULL);
layout_set_size(lc, w->sx, mainheight + rows * height, 0, 0); layout_set_size(lc, w->sx, mainheight + rows * (height + 1) - 1, 0, 0);
layout_make_node(lc, LAYOUT_TOPBOTTOM); layout_make_node(lc, LAYOUT_TOPBOTTOM);
/* Create the main pane. */ /* Create the main pane. */
@@ -289,7 +297,7 @@ layout_set_main_h(struct window *w)
/* Create the new row. */ /* Create the new row. */
lcrow = layout_create_cell(lc); lcrow = layout_create_cell(lc);
layout_set_size(lcrow, w->sx, height - 1, 0, 0); layout_set_size(lcrow, w->sx, height, 0, 0);
TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry); TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry);
/* If only one column, just use the row directly. */ /* If only one column, just use the row directly. */
@@ -304,7 +312,7 @@ layout_set_main_h(struct window *w)
for (i = 0; i < columns; i++) { for (i = 0; i < columns; i++) {
/* Create and add a pane cell. */ /* Create and add a pane cell. */
lcchild = layout_create_cell(lcrow); lcchild = layout_create_cell(lcrow);
layout_set_size(lcchild, width - 1, height - 1, 0, 0); layout_set_size(lcchild, width, height, 0, 0);
layout_make_leaf(lcchild, wp); layout_make_leaf(lcchild, wp);
TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry); TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry);
@@ -316,7 +324,7 @@ layout_set_main_h(struct window *w)
/* Adjust the row to fit the full width if necessary. */ /* Adjust the row to fit the full width if necessary. */
if (i == columns) if (i == columns)
i--; i--;
used = ((i + 1) * width) - 1; used = ((i + 1) * (width + 1)) - 1;
if (w->sx <= used) if (w->sx <= used)
continue; continue;
lcchild = TAILQ_LAST(&lcrow->cells, layout_cells); lcchild = TAILQ_LAST(&lcrow->cells, layout_cells);
@@ -324,7 +332,7 @@ layout_set_main_h(struct window *w)
} }
/* Adjust the last row height to fit if necessary. */ /* Adjust the last row height to fit if necessary. */
used = mainheight + (rows * height) - 1; used = mainheight + (rows * height) + rows - 1;
if (w->sy > used) { if (w->sy > used) {
lcrow = TAILQ_LAST(&lc->cells, layout_cells); lcrow = TAILQ_LAST(&lc->cells, layout_cells);
layout_resize_adjust(lcrow, LAYOUT_TOPBOTTOM, w->sy - used); layout_resize_adjust(lcrow, LAYOUT_TOPBOTTOM, w->sy - used);
@@ -344,8 +352,8 @@ layout_set_main_v(struct window *w)
{ {
struct window_pane *wp; struct window_pane *wp;
struct layout_cell *lc, *lcmain, *lccolumn, *lcchild; struct layout_cell *lc, *lcmain, *lccolumn, *lcchild;
u_int n, mainwidth, width, height, used; u_int n, mainwidth, otherwidth, width, height;
u_int i, j, columns, rows, totalcolumns; u_int used, i, j, columns, rows, totalcolumns;
layout_print_cell(w->layout_root, __func__, 1); layout_print_cell(w->layout_root, __func__, 1);
@@ -355,16 +363,26 @@ layout_set_main_v(struct window *w)
return; return;
n--; /* take off main pane */ n--; /* take off main pane */
/* How many rows and columns will be needed? */ /* How many rows and columns will be needed, not counting main? */
rows = w->sy / (PANE_MINIMUM + 1); /* maximum rows */ rows = (w->sy + 1) / (PANE_MINIMUM + 1); /* maximum rows */
if (rows == 0) if (rows == 0)
rows = 1; rows = 1;
columns = 1 + (n - 1) / rows; columns = 1 + (n - 1) / rows;
rows = 1 + (n - 1) / columns; rows = 1 + (n - 1) / columns;
height = w->sy / rows; height = (w->sy - (n - 1)) / rows;
/* Get the main pane width and add one for separator line. */ /* Get the main pane width and add one for separator line. */
mainwidth = options_get_number(&w->options, "main-pane-width") + 1; mainwidth = options_get_number(&w->options, "main-pane-width") + 1;
/* Get the optional other pane width and add one for separator line. */
otherwidth = options_get_number(&w->options, "other-pane-width") + 1;
/*
* If an other pane width was specified, honour it so long as it
* doesn't shrink the main width to less than the main-pane-width
*/
if (otherwidth > 1 && w->sx - otherwidth > mainwidth)
mainwidth = w->sx - otherwidth;
if (mainwidth < PANE_MINIMUM + 1) if (mainwidth < PANE_MINIMUM + 1)
mainwidth = PANE_MINIMUM + 1; mainwidth = PANE_MINIMUM + 1;
@@ -375,14 +393,14 @@ layout_set_main_v(struct window *w)
mainwidth = PANE_MINIMUM + 2; mainwidth = PANE_MINIMUM + 2;
else else
mainwidth = w->sx - totalcolumns; mainwidth = w->sx - totalcolumns;
width = PANE_MINIMUM + 1; width = PANE_MINIMUM;
} else } else
width = (w->sx - mainwidth) / columns; width = (w->sx - mainwidth - (columns - 1)) / columns;
/* Free old tree and create a new root. */ /* Free old tree and create a new root. */
layout_free(w); layout_free(w);
lc = w->layout_root = layout_create_cell(NULL); lc = w->layout_root = layout_create_cell(NULL);
layout_set_size(lc, mainwidth + columns * width, w->sy, 0, 0); layout_set_size(lc, mainwidth + columns * (width + 1) - 1, w->sy, 0, 0);
layout_make_node(lc, LAYOUT_LEFTRIGHT); layout_make_node(lc, LAYOUT_LEFTRIGHT);
/* Create the main pane. */ /* Create the main pane. */
@@ -400,7 +418,7 @@ layout_set_main_v(struct window *w)
/* Create the new column. */ /* Create the new column. */
lccolumn = layout_create_cell(lc); lccolumn = layout_create_cell(lc);
layout_set_size(lccolumn, width - 1, w->sy, 0, 0); layout_set_size(lccolumn, width, w->sy, 0, 0);
TAILQ_INSERT_TAIL(&lc->cells, lccolumn, entry); TAILQ_INSERT_TAIL(&lc->cells, lccolumn, entry);
/* If only one row, just use the row directly. */ /* If only one row, just use the row directly. */
@@ -415,7 +433,7 @@ layout_set_main_v(struct window *w)
for (i = 0; i < rows; i++) { for (i = 0; i < rows; i++) {
/* Create and add a pane cell. */ /* Create and add a pane cell. */
lcchild = layout_create_cell(lccolumn); lcchild = layout_create_cell(lccolumn);
layout_set_size(lcchild, width - 1, height - 1, 0, 0); layout_set_size(lcchild, width, height, 0, 0);
layout_make_leaf(lcchild, wp); layout_make_leaf(lcchild, wp);
TAILQ_INSERT_TAIL(&lccolumn->cells, lcchild, entry); TAILQ_INSERT_TAIL(&lccolumn->cells, lcchild, entry);
@@ -427,7 +445,7 @@ layout_set_main_v(struct window *w)
/* Adjust the column to fit the full height if necessary. */ /* Adjust the column to fit the full height if necessary. */
if (i == rows) if (i == rows)
i--; i--;
used = ((i + 1) * height) - 1; used = ((i + 1) * (height + 1)) - 1;
if (w->sy <= used) if (w->sy <= used)
continue; continue;
lcchild = TAILQ_LAST(&lccolumn->cells, layout_cells); lcchild = TAILQ_LAST(&lccolumn->cells, layout_cells);
@@ -435,7 +453,7 @@ layout_set_main_v(struct window *w)
} }
/* Adjust the last column width to fit if necessary. */ /* Adjust the last column width to fit if necessary. */
used = mainwidth + (columns * width) - 1; used = mainwidth + (columns * width) + columns - 1;
if (w->sx > used) { if (w->sx > used) {
lccolumn = TAILQ_LAST(&lc->cells, layout_cells); lccolumn = TAILQ_LAST(&lc->cells, layout_cells);
layout_resize_adjust(lccolumn, LAYOUT_LEFTRIGHT, w->sx - used); layout_resize_adjust(lccolumn, LAYOUT_LEFTRIGHT, w->sx - used);
@@ -474,17 +492,18 @@ layout_set_tiled(struct window *w)
} }
/* What width and height should they be? */ /* What width and height should they be? */
width = w->sx / columns; width = (w->sx - (columns - 1)) / columns;
if (width < PANE_MINIMUM + 1) if (width < PANE_MINIMUM)
width = PANE_MINIMUM + 1; width = PANE_MINIMUM;
height = w->sy / rows; height = (w->sy - (rows - 1)) / rows;
if (width < PANE_MINIMUM + 1) if (height < PANE_MINIMUM)
width = PANE_MINIMUM + 1; height = PANE_MINIMUM;
/* Free old tree and create a new root. */ /* Free old tree and create a new root. */
layout_free(w); layout_free(w);
lc = w->layout_root = layout_create_cell(NULL); lc = w->layout_root = layout_create_cell(NULL);
layout_set_size(lc, width * columns, height * rows, 0, 0); layout_set_size(lc, (width + 1) * columns - 1,
(height + 1) * rows - 1, 0, 0);
layout_make_node(lc, LAYOUT_TOPBOTTOM); layout_make_node(lc, LAYOUT_TOPBOTTOM);
/* Create a grid of the cells. */ /* Create a grid of the cells. */
@@ -496,7 +515,7 @@ layout_set_tiled(struct window *w)
/* Create the new row. */ /* Create the new row. */
lcrow = layout_create_cell(lc); lcrow = layout_create_cell(lc);
layout_set_size(lcrow, w->sx, height - 1, 0, 0); layout_set_size(lcrow, w->sx, height, 0, 0);
TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry); TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry);
/* If only one column, just use the row directly. */ /* If only one column, just use the row directly. */
@@ -511,7 +530,7 @@ layout_set_tiled(struct window *w)
for (i = 0; i < columns; i++) { for (i = 0; i < columns; i++) {
/* Create and add a pane cell. */ /* Create and add a pane cell. */
lcchild = layout_create_cell(lcrow); lcchild = layout_create_cell(lcrow);
layout_set_size(lcchild, width - 1, height - 1, 0, 0); layout_set_size(lcchild, width, height, 0, 0);
layout_make_leaf(lcchild, wp); layout_make_leaf(lcchild, wp);
TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry); TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry);
@@ -526,7 +545,7 @@ layout_set_tiled(struct window *w)
*/ */
if (i == columns) if (i == columns)
i--; i--;
used = ((i + 1) * width) - 1; used = ((i + 1) * (width + 1)) - 1;
if (w->sx <= used) if (w->sx <= used)
continue; continue;
lcchild = TAILQ_LAST(&lcrow->cells, layout_cells); lcchild = TAILQ_LAST(&lcrow->cells, layout_cells);
@@ -534,7 +553,7 @@ layout_set_tiled(struct window *w)
} }
/* Adjust the last row height to fit if necessary. */ /* Adjust the last row height to fit if necessary. */
used = (rows * height) - 1; used = (rows * height) + rows - 1;
if (w->sy > used) { if (w->sy > used) {
lcrow = TAILQ_LAST(&lc->cells, layout_cells); lcrow = TAILQ_LAST(&lc->cells, layout_cells);
layout_resize_adjust(lcrow, LAYOUT_TOPBOTTOM, w->sy - used); layout_resize_adjust(lcrow, LAYOUT_TOPBOTTOM, w->sy - used);

View File

@@ -1,4 +1,4 @@
/* $Id: resize.c,v 1.25 2010-06-22 23:26:18 tcunha Exp $ */ /* $Id: resize.c,v 1.27 2010-12-22 15:36:44 tcunha Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -52,11 +52,7 @@ recalculate_sizes(void)
u_int i, j, ssx, ssy, has, limit; u_int i, j, ssx, ssy, has, limit;
int flag; int flag;
for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { RB_FOREACH(s, sessions, &sessions) {
s = ARRAY_ITEM(&sessions, i);
if (s == NULL)
continue;
ssx = ssy = UINT_MAX; ssx = ssy = UINT_MAX;
for (j = 0; j < ARRAY_LENGTH(&clients); j++) { for (j = 0; j < ARRAY_LENGTH(&clients); j++) {
c = ARRAY_ITEM(&clients, j); c = ARRAY_ITEM(&clients, j);
@@ -98,9 +94,8 @@ recalculate_sizes(void)
flag = options_get_number(&w->options, "aggressive-resize"); flag = options_get_number(&w->options, "aggressive-resize");
ssx = ssy = UINT_MAX; ssx = ssy = UINT_MAX;
for (j = 0; j < ARRAY_LENGTH(&sessions); j++) { RB_FOREACH(s, sessions, &sessions) {
s = ARRAY_ITEM(&sessions, j); if (s->flags & SESSION_UNATTACHED)
if (s == NULL || s->flags & SESSION_UNATTACHED)
continue; continue;
if (flag) if (flag)
has = s->curw->window == w; has = s->curw->window == w;
@@ -113,11 +108,8 @@ recalculate_sizes(void)
ssy = s->sy; ssy = s->sy;
} }
} }
if (ssx == UINT_MAX || ssy == UINT_MAX) { if (ssx == UINT_MAX || ssy == UINT_MAX)
w->flags |= WINDOW_HIDDEN;
continue; continue;
}
w->flags &= ~WINDOW_HIDDEN;
limit = options_get_number(&w->options, "force-width"); limit = options_get_number(&w->options, "force-width");
if (limit != 0 && ssx > limit) if (limit != 0 && ssx > limit)

View File

@@ -1,4 +1,4 @@
/* $Id: screen-redraw.c,v 1.52 2010-02-05 01:31:06 tcunha Exp $ */ /* $Id: screen-redraw.c,v 1.53 2010-09-18 15:43:53 tcunha Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -41,6 +41,8 @@ void screen_redraw_draw_number(struct client *, struct window_pane *);
#define CELL_JOIN 11 #define CELL_JOIN 11
#define CELL_OUTSIDE 12 #define CELL_OUTSIDE 12
#define CELL_BORDERS " xqlkmjwvtun~"
/* Check if cell is on the border of a particular pane. */ /* Check if cell is on the border of a particular pane. */
int int
screen_redraw_cell_border1(struct window_pane *wp, u_int px, u_int py) screen_redraw_cell_border1(struct window_pane *wp, u_int px, u_int py)
@@ -173,8 +175,6 @@ screen_redraw_screen(struct client *c, int status_only, int borders_only)
struct grid_cell active_gc, other_gc; struct grid_cell active_gc, other_gc;
u_int i, j, type; u_int i, j, type;
int status, fg, bg; int status, fg, bg;
const u_char *base, *ptr;
u_char ch, border[20];
/* Get status line, er, status. */ /* Get status line, er, status. */
if (c->message_string != NULL || c->prompt_string != NULL) if (c->message_string != NULL || c->prompt_string != NULL)
@@ -193,6 +193,7 @@ screen_redraw_screen(struct client *c, int status_only, int borders_only)
memcpy(&other_gc, &grid_default_cell, sizeof other_gc); memcpy(&other_gc, &grid_default_cell, sizeof other_gc);
memcpy(&active_gc, &grid_default_cell, sizeof active_gc); memcpy(&active_gc, &grid_default_cell, sizeof active_gc);
active_gc.data = other_gc.data = 'x'; /* not space */ active_gc.data = other_gc.data = 'x'; /* not space */
active_gc.attr = other_gc.attr = GRID_ATTR_CHARSET;
fg = options_get_number(&c->session->options, "pane-border-fg"); fg = options_get_number(&c->session->options, "pane-border-fg");
colour_set_fg(&other_gc, fg); colour_set_fg(&other_gc, fg);
bg = options_get_number(&c->session->options, "pane-border-bg"); bg = options_get_number(&c->session->options, "pane-border-bg");
@@ -203,16 +204,6 @@ screen_redraw_screen(struct client *c, int status_only, int borders_only)
colour_set_bg(&active_gc, bg); colour_set_bg(&active_gc, bg);
/* Draw background and borders. */ /* Draw background and borders. */
strlcpy(border, " |-....--||+.", sizeof border);
if (tty_term_has(tty->term, TTYC_ACSC)) {
base = " xqlkmjwvtun~";
for (ptr = base; *ptr != '\0'; ptr++) {
if ((ch = tty_get_acs(tty, *ptr)) != '\0')
border[ptr - base] = ch;
}
other_gc.attr |= GRID_ATTR_CHARSET;
active_gc.attr |= GRID_ATTR_CHARSET;
}
for (j = 0; j < tty->sy - status; j++) { for (j = 0; j < tty->sy - status; j++) {
if (status_only && j != tty->sy - 1) if (status_only && j != tty->sy - 1)
continue; continue;
@@ -225,7 +216,7 @@ screen_redraw_screen(struct client *c, int status_only, int borders_only)
else else
tty_attributes(tty, &other_gc); tty_attributes(tty, &other_gc);
tty_cursor(tty, i, j); tty_cursor(tty, i, j);
tty_putc(tty, border[type]); tty_putc(tty, CELL_BORDERS[type]);
} }
} }

View File

@@ -1,4 +1,4 @@
/* $Id: screen.c,v 1.101 2010-04-06 22:01:32 nicm Exp $ */ /* $Id: screen.c,v 1.103 2010-12-11 17:57:28 nicm Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -18,6 +18,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <netdb.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
@@ -286,7 +287,7 @@ screen_check_selection(struct screen *s, u_int px, u_int py)
*/ */
if (sel->ex < sel->sx) { if (sel->ex < sel->sx) {
/* Cursor (ex) is on the left. */ /* Cursor (ex) is on the left. */
if (px <= sel->ex) if (px < sel->ex)
return (0); return (0);
if (px > sel->sx) if (px > sel->sx)
@@ -296,7 +297,7 @@ screen_check_selection(struct screen *s, u_int px, u_int py)
if (px < sel->sx) if (px < sel->sx)
return (0); return (0);
if (px >= sel->ex) if (px > sel->ex)
return (0); return (0);
} }
} else { } else {

View File

@@ -1,4 +1,4 @@
/* $Id: server-client.c,v 1.35 2010-07-17 14:36:40 tcunha Exp $ */ /* $Id: server-client.c,v 1.48 2010-12-22 15:31:00 tcunha Exp $ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -28,9 +28,14 @@
void server_client_handle_key(int, struct mouse_event *, void *); void server_client_handle_key(int, struct mouse_event *, void *);
void server_client_repeat_timer(int, short, void *); void server_client_repeat_timer(int, short, void *);
void server_client_check_exit(struct client *);
void server_client_check_backoff(struct client *);
void server_client_check_redraw(struct client *); void server_client_check_redraw(struct client *);
void server_client_set_title(struct client *); void server_client_set_title(struct client *);
void server_client_reset_state(struct client *); void server_client_reset_state(struct client *);
void server_client_in_callback(struct bufferevent *, short, void *);
void server_client_out_callback(struct bufferevent *, short, void *);
void server_client_err_callback(struct bufferevent *, short, void *);
int server_client_msg_dispatch(struct client *); int server_client_msg_dispatch(struct client *);
void server_client_msg_command(struct client *, struct msg_command_data *); void server_client_msg_command(struct client *, struct msg_command_data *);
@@ -54,8 +59,6 @@ server_client_create(int fd)
fatal("fcntl failed"); fatal("fcntl failed");
if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1) if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
fatal("fcntl failed"); fatal("fcntl failed");
if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
fatal("fcntl failed");
c = xcalloc(1, sizeof *c); c = xcalloc(1, sizeof *c);
c->references = 0; c->references = 0;
@@ -66,16 +69,15 @@ server_client_create(int fd)
fatal("gettimeofday failed"); fatal("gettimeofday failed");
memcpy(&c->activity_time, &c->creation_time, sizeof c->activity_time); memcpy(&c->activity_time, &c->creation_time, sizeof c->activity_time);
ARRAY_INIT(&c->prompt_hdata); c->stdin_event = NULL;
c->stdout_event = NULL;
c->stdin_file = NULL; c->stderr_event = NULL;
c->stdout_file = NULL;
c->stderr_file = NULL;
c->tty.fd = -1; c->tty.fd = -1;
c->title = NULL; c->title = NULL;
c->session = NULL; c->session = NULL;
c->last_session = NULL;
c->tty.sx = 80; c->tty.sx = 80;
c->tty.sy = 24; c->tty.sy = 24;
@@ -121,12 +123,18 @@ server_client_lost(struct client *c)
if (c->flags & CLIENT_TERMINAL) if (c->flags & CLIENT_TERMINAL)
tty_free(&c->tty); tty_free(&c->tty);
if (c->stdin_file != NULL) if (c->stdin_fd != -1)
fclose(c->stdin_file); close(c->stdin_fd);
if (c->stdout_file != NULL) if (c->stdin_event != NULL)
fclose(c->stdout_file); bufferevent_free(c->stdin_event);
if (c->stderr_file != NULL) if (c->stdout_fd != -1)
fclose(c->stderr_file); close(c->stdout_fd);
if (c->stdout_event != NULL)
bufferevent_free(c->stdout_event);
if (c->stderr_fd != -1)
close(c->stderr_fd);
if (c->stderr_event != NULL)
bufferevent_free(c->stderr_event);
screen_free(&c->status); screen_free(&c->status);
job_tree_free(&c->status_jobs); job_tree_free(&c->status_jobs);
@@ -151,9 +159,6 @@ server_client_lost(struct client *c)
xfree(c->prompt_string); xfree(c->prompt_string);
if (c->prompt_buffer != NULL) if (c->prompt_buffer != NULL)
xfree(c->prompt_buffer); xfree(c->prompt_buffer);
for (i = 0; i < ARRAY_LENGTH(&c->prompt_hdata); i++)
xfree(ARRAY_ITEM(&c->prompt_hdata, i));
ARRAY_FREE(&c->prompt_hdata);
if (c->cwd != NULL) if (c->cwd != NULL)
xfree(c->cwd); xfree(c->cwd);
@@ -173,6 +178,7 @@ server_client_lost(struct client *c)
c->flags |= CLIENT_DEAD; c->flags |= CLIENT_DEAD;
recalculate_sizes(); recalculate_sizes();
server_check_unattached();
server_update_socket(); server_update_socket();
} }
@@ -389,11 +395,14 @@ server_client_loop(void)
for (i = 0; i < ARRAY_LENGTH(&clients); i++) { for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
c = ARRAY_ITEM(&clients, i); c = ARRAY_ITEM(&clients, i);
if (c == NULL || c->session == NULL) if (c == NULL)
continue; continue;
server_client_check_redraw(c); server_client_check_exit(c);
server_client_reset_state(c); if (c->session != NULL) {
server_client_check_redraw(c);
server_client_reset_state(c);
}
} }
/* /*
@@ -456,6 +465,72 @@ server_client_repeat_timer(unused int fd, unused short events, void *data)
c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT); c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT);
} }
/* Check if client should be exited. */
void
server_client_check_exit(struct client *c)
{
struct msg_exit_data exitdata;
if (!(c->flags & CLIENT_EXIT))
return;
if (c->stdout_fd != -1 && c->stdout_event != NULL &&
EVBUFFER_LENGTH(c->stdout_event->output) != 0)
return;
if (c->stderr_fd != -1 && c->stderr_event != NULL &&
EVBUFFER_LENGTH(c->stderr_event->output) != 0)
return;
exitdata.retcode = c->retcode;
server_write_client(c, MSG_EXIT, &exitdata, sizeof exitdata);
c->flags &= ~CLIENT_EXIT;
}
/*
* Check if the client should backoff. During backoff, data from external
* programs is not written to the terminal. When the existing data drains, the
* client is redrawn.
*
* There are two backoff phases - both the tty and client have backoff flags -
* the first to allow existing data to drain and the latter to ensure backoff
* is disabled until the redraw has finished and prevent the redraw triggering
* another backoff.
*/
void
server_client_check_backoff(struct client *c)
{
struct tty *tty = &c->tty;
size_t used;
used = EVBUFFER_LENGTH(tty->event->output);
/*
* If in the second backoff phase (redrawing), don't check backoff
* until the redraw has completed (or enough of it to drop below the
* backoff threshold).
*/
if (c->flags & CLIENT_BACKOFF) {
if (used > BACKOFF_THRESHOLD)
return;
c->flags &= ~CLIENT_BACKOFF;
return;
}
/* Once drained, allow data through again and schedule redraw. */
if (tty->flags & TTY_BACKOFF) {
if (used != 0)
return;
tty->flags &= ~TTY_BACKOFF;
c->flags |= (CLIENT_BACKOFF|CLIENT_REDRAWWINDOW|CLIENT_STATUS);
return;
}
/* If too much data, start backoff. */
if (used > BACKOFF_THRESHOLD)
tty->flags |= TTY_BACKOFF;
}
/* Check for client redraws. */ /* Check for client redraws. */
void void
server_client_check_redraw(struct client *c) server_client_check_redraw(struct client *c)
@@ -484,6 +559,10 @@ server_client_check_redraw(struct client *c)
if (c->flags & CLIENT_REDRAW) { if (c->flags & CLIENT_REDRAW) {
screen_redraw_screen(c, 0, 0); screen_redraw_screen(c, 0, 0);
c->flags &= ~(CLIENT_STATUS|CLIENT_BORDERS); c->flags &= ~(CLIENT_STATUS|CLIENT_BORDERS);
} else if (c->flags & CLIENT_REDRAWWINDOW) {
TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry)
screen_redraw_pane(c, wp);
c->flags &= ~CLIENT_REDRAWWINDOW;
} else { } else {
TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) { TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) {
if (wp->flags & PANE_REDRAW) if (wp->flags & PANE_REDRAW)
@@ -522,6 +601,52 @@ server_client_set_title(struct client *c)
xfree(title); xfree(title);
} }
/*
* Error callback for client stdin. Caller must increase reference count when
* enabling event!
*/
void
server_client_in_callback(
unused struct bufferevent *bufev, unused short what, void *data)
{
struct client *c = data;
c->references--;
if (c->flags & CLIENT_DEAD)
return;
bufferevent_disable(c->stdin_event, EV_READ|EV_WRITE);
close(c->stdin_fd);
c->stdin_fd = -1;
if (c->stdin_callback != NULL)
c->stdin_callback(c, c->stdin_data);
}
/* Error callback for client stdout. */
void
server_client_out_callback(
unused struct bufferevent *bufev, unused short what, unused void *data)
{
struct client *c = data;
bufferevent_disable(c->stdout_event, EV_READ|EV_WRITE);
close(c->stdout_fd);
c->stdout_fd = -1;
}
/* Error callback for client stderr. */
void
server_client_err_callback(
unused struct bufferevent *bufev, unused short what, unused void *data)
{
struct client *c = data;
bufferevent_disable(c->stderr_event, EV_READ|EV_WRITE);
close(c->stderr_fd);
c->stderr_fd = -1;
}
/* Dispatch message from client. */ /* Dispatch message from client. */
int int
server_client_msg_dispatch(struct client *c) server_client_msg_dispatch(struct client *c)
@@ -531,6 +656,7 @@ server_client_msg_dispatch(struct client *c)
struct msg_identify_data identifydata; struct msg_identify_data identifydata;
struct msg_environ_data environdata; struct msg_environ_data environdata;
ssize_t n, datalen; ssize_t n, datalen;
int mode;
if ((n = imsg_read(&c->ibuf)) == -1 || n == 0) if ((n = imsg_read(&c->ibuf)) == -1 || n == 0)
return (-1); return (-1);
@@ -565,9 +691,15 @@ server_client_msg_dispatch(struct client *c)
fatalx("MSG_IDENTIFY missing fd"); fatalx("MSG_IDENTIFY missing fd");
memcpy(&identifydata, imsg.data, sizeof identifydata); memcpy(&identifydata, imsg.data, sizeof identifydata);
c->stdin_file = fdopen(imsg.fd, "r"); c->stdin_fd = imsg.fd;
if (c->stdin_file == NULL) c->stdin_event = bufferevent_new(c->stdin_fd,
fatal("fdopen(stdin) failed"); NULL, NULL, server_client_in_callback, c);
if (c->stdin_event == NULL)
fatalx("failed to create stdin event");
if ((mode = fcntl(c->stdin_fd, F_GETFL)) != -1)
fcntl(c->stdin_fd, F_SETFL, mode|O_NONBLOCK);
server_client_msg_identify(c, &identifydata, imsg.fd); server_client_msg_identify(c, &identifydata, imsg.fd);
break; break;
case MSG_STDOUT: case MSG_STDOUT:
@@ -576,9 +708,14 @@ server_client_msg_dispatch(struct client *c)
if (imsg.fd == -1) if (imsg.fd == -1)
fatalx("MSG_STDOUT missing fd"); fatalx("MSG_STDOUT missing fd");
c->stdout_file = fdopen(imsg.fd, "w"); c->stdout_fd = imsg.fd;
if (c->stdout_file == NULL) c->stdout_event = bufferevent_new(c->stdout_fd,
fatal("fdopen(stdout) failed"); NULL, NULL, server_client_out_callback, c);
if (c->stdout_event == NULL)
fatalx("failed to create stdout event");
if ((mode = fcntl(c->stdout_fd, F_GETFL)) != -1)
fcntl(c->stdout_fd, F_SETFL, mode|O_NONBLOCK);
break; break;
case MSG_STDERR: case MSG_STDERR:
if (datalen != 0) if (datalen != 0)
@@ -586,9 +723,14 @@ server_client_msg_dispatch(struct client *c)
if (imsg.fd == -1) if (imsg.fd == -1)
fatalx("MSG_STDERR missing fd"); fatalx("MSG_STDERR missing fd");
c->stderr_file = fdopen(imsg.fd, "w"); c->stderr_fd = imsg.fd;
if (c->stderr_file == NULL) c->stderr_event = bufferevent_new(c->stderr_fd,
fatal("fdopen(stderr) failed"); NULL, NULL, server_client_err_callback, c);
if (c->stderr_event == NULL)
fatalx("failed to create stderr event");
if ((mode = fcntl(c->stderr_fd, F_GETFL)) != -1)
fcntl(c->stderr_fd, F_SETFL, mode|O_NONBLOCK);
break; break;
case MSG_RESIZE: case MSG_RESIZE:
if (datalen != 0) if (datalen != 0)
@@ -658,12 +800,10 @@ server_client_msg_error(struct cmd_ctx *ctx, const char *fmt, ...)
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
vfprintf(ctx->cmdclient->stderr_file, fmt, ap); evbuffer_add_vprintf(ctx->cmdclient->stderr_event->output, fmt, ap);
va_end(ap); va_end(ap);
fputc('\n', ctx->cmdclient->stderr_file); bufferevent_write(ctx->cmdclient->stderr_event, "\n", 1);
fflush(ctx->cmdclient->stderr_file);
ctx->cmdclient->retcode = 1; ctx->cmdclient->retcode = 1;
} }
@@ -674,11 +814,10 @@ server_client_msg_print(struct cmd_ctx *ctx, const char *fmt, ...)
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
vfprintf(ctx->cmdclient->stdout_file, fmt, ap); evbuffer_add_vprintf(ctx->cmdclient->stdout_event->output, fmt, ap);
va_end(ap); va_end(ap);
fputc('\n', ctx->cmdclient->stdout_file); bufferevent_write(ctx->cmdclient->stdout_event, "\n", 1);
fflush(ctx->cmdclient->stdout_file);
} }
/* Callback to send print message to client, if not quiet. */ /* Callback to send print message to client, if not quiet. */
@@ -691,22 +830,20 @@ server_client_msg_info(struct cmd_ctx *ctx, const char *fmt, ...)
return; return;
va_start(ap, fmt); va_start(ap, fmt);
vfprintf(ctx->cmdclient->stdout_file, fmt, ap); evbuffer_add_vprintf(ctx->cmdclient->stdout_event->output, fmt, ap);
va_end(ap); va_end(ap);
fputc('\n', ctx->cmdclient->stderr_file); bufferevent_write(ctx->cmdclient->stdout_event, "\n", 1);
fflush(ctx->cmdclient->stdout_file);
} }
/* Handle command message. */ /* Handle command message. */
void void
server_client_msg_command(struct client *c, struct msg_command_data *data) server_client_msg_command(struct client *c, struct msg_command_data *data)
{ {
struct cmd_ctx ctx; struct cmd_ctx ctx;
struct cmd_list *cmdlist = NULL; struct cmd_list *cmdlist = NULL;
struct msg_exit_data exitdata; int argc;
int argc; char **argv, *cause;
char **argv, *cause;
ctx.error = server_client_msg_error; ctx.error = server_client_msg_error;
ctx.print = server_client_msg_print; ctx.print = server_client_msg_print;
@@ -737,18 +874,15 @@ server_client_msg_command(struct client *c, struct msg_command_data *data)
} }
cmd_free_argv(argc, argv); cmd_free_argv(argc, argv);
if (cmd_list_exec(cmdlist, &ctx) != 1) { if (cmd_list_exec(cmdlist, &ctx) != 1)
exitdata.retcode = c->retcode; c->flags |= CLIENT_EXIT;
server_write_client(c, MSG_EXIT, &exitdata, sizeof exitdata);
}
cmd_list_free(cmdlist); cmd_list_free(cmdlist);
return; return;
error: error:
if (cmdlist != NULL) if (cmdlist != NULL)
cmd_list_free(cmdlist); cmd_list_free(cmdlist);
exitdata.retcode = c->retcode; c->flags |= CLIENT_EXIT;
server_write_client(c, MSG_EXIT, &exitdata, sizeof exitdata);
} }
/* Handle identify message. */ /* Handle identify message. */

View File

@@ -1,4 +1,4 @@
/* $Id: server-fn.c,v 1.108 2010-07-02 02:45:52 tcunha Exp $ */ /* $Id: server-fn.c,v 1.117 2010-12-25 23:44:37 tcunha Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -30,13 +30,10 @@ void server_callback_identify(int, short, void *);
void void
server_fill_environ(struct session *s, struct environ *env) server_fill_environ(struct session *s, struct environ *env)
{ {
char tmuxvar[MAXPATHLEN], *term; char tmuxvar[MAXPATHLEN], *term;
u_int idx;
if (session_index(s, &idx) != 0)
fatalx("session not found");
xsnprintf(tmuxvar, sizeof tmuxvar, xsnprintf(tmuxvar, sizeof tmuxvar,
"%s,%ld,%u", socket_path, (long) getpid(), idx); "%s,%ld,%u", socket_path, (long) getpid(), s->idx);
environ_set(env, "TMUX", tmuxvar); environ_set(env, "TMUX", tmuxvar);
term = options_get_string(&s->options, "default-terminal"); term = options_get_string(&s->options, "default-terminal");
@@ -175,7 +172,6 @@ void
server_status_window(struct window *w) server_status_window(struct window *w)
{ {
struct session *s; struct session *s;
u_int i;
/* /*
* This is slightly different. We want to redraw the status line of any * This is slightly different. We want to redraw the status line of any
@@ -183,9 +179,8 @@ server_status_window(struct window *w)
* current window. * current window.
*/ */
for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { RB_FOREACH(s, sessions, &sessions) {
s = ARRAY_ITEM(&sessions, i); if (session_has(s, w) != NULL)
if (s != NULL && session_has(s, w) != NULL)
server_status_session(s); server_status_session(s);
} }
} }
@@ -244,13 +239,15 @@ server_lock_client(struct client *c)
void void
server_kill_window(struct window *w) server_kill_window(struct window *w)
{ {
struct session *s; struct session *s, *next_s;
struct winlink *wl; struct winlink *wl;
u_int i;
for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { next_s = RB_MIN(sessions, &sessions);
s = ARRAY_ITEM(&sessions, i); while (next_s != NULL) {
if (s == NULL || session_has(s, w) == NULL) s = next_s;
next_s = RB_NEXT(sessions, &sessions, s);
if (session_has(s, w) == NULL)
continue; continue;
while ((wl = winlink_find_by_window(&s->windows, w)) != NULL) { while ((wl = winlink_find_by_window(&s->windows, w)) != NULL) {
if (session_detach(s, wl)) { if (session_detach(s, wl)) {
@@ -280,8 +277,10 @@ server_link_window(struct session *src, struct winlink *srcwl,
if (dstidx != -1) if (dstidx != -1)
dstwl = winlink_find_by_index(&dst->windows, dstidx); dstwl = winlink_find_by_index(&dst->windows, dstidx);
if (dstwl != NULL) { if (dstwl != NULL) {
if (dstwl->window == srcwl->window) if (dstwl->window == srcwl->window) {
return (0); xasprintf(cause, "same index: %d", dstidx);
return (-1);
}
if (killflag) { if (killflag) {
/* /*
* Can't use session_detach as it will destroy session * Can't use session_detach as it will destroy session
@@ -363,11 +362,9 @@ struct session *
server_next_session(struct session *s) server_next_session(struct session *s)
{ {
struct session *s_loop, *s_out; struct session *s_loop, *s_out;
u_int i;
s_out = NULL; s_out = NULL;
for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { RB_FOREACH(s_loop, sessions, &sessions) {
s_loop = ARRAY_ITEM(&sessions, i);
if (s_loop == s) if (s_loop == s)
continue; continue;
if (s_out == NULL || if (s_out == NULL ||
@@ -395,8 +392,9 @@ server_destroy_session(struct session *s)
continue; continue;
if (s_new == NULL) { if (s_new == NULL) {
c->session = NULL; c->session = NULL;
server_write_client(c, MSG_EXIT, NULL, 0); c->flags |= CLIENT_EXIT;
} else { } else {
c->last_session = NULL;
c->session = s_new; c->session = s_new;
server_redraw_client(c); server_redraw_client(c);
} }
@@ -404,6 +402,23 @@ server_destroy_session(struct session *s)
recalculate_sizes(); recalculate_sizes();
} }
void
server_check_unattached (void)
{
struct session *s;
/*
* If any sessions are no longer attached and have destroy-unattached
* set, collect them.
*/
RB_FOREACH(s, sessions, &sessions) {
if (!(s->flags & SESSION_UNATTACHED))
continue;
if (options_get_number (&s->options, "destroy-unattached"))
session_destroy(s);
}
}
void void
server_set_identify(struct client *c) server_set_identify(struct client *c)
{ {

View File

@@ -1,4 +1,4 @@
/* $Id: server-window.c,v 1.15 2010-06-22 23:26:18 tcunha Exp $ */ /* $Id: server-window.c,v 1.19 2010-12-22 15:36:44 tcunha Exp $ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -26,34 +26,10 @@
int server_window_backoff(struct window_pane *); int server_window_backoff(struct window_pane *);
int server_window_check_bell(struct session *, struct winlink *); int server_window_check_bell(struct session *, struct winlink *);
int server_window_check_activity(struct session *, struct winlink *); int server_window_check_activity(struct session *, struct winlink *);
int server_window_check_silence(struct session *, struct winlink *);
int server_window_check_content( int server_window_check_content(
struct session *, struct winlink *, struct window_pane *); struct session *, struct winlink *, struct window_pane *);
/* Check if this window should suspend reading. */
int
server_window_backoff(struct window_pane *wp)
{
struct client *c;
u_int i;
if (!window_pane_visible(wp))
return (0);
for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
c = ARRAY_ITEM(&clients, i);
if (c == NULL || c->session == NULL)
continue;
if ((c->flags & (CLIENT_SUSPENDED|CLIENT_DEAD)) != 0)
continue;
if (c->session->curw->window != wp->window)
continue;
if (EVBUFFER_LENGTH(c->tty.event->output) > BACKOFF_THRESHOLD)
return (1);
}
return (0);
}
/* Window functions that need to happen every loop. */ /* Window functions that need to happen every loop. */
void void
server_window_loop(void) server_window_loop(void)
@@ -62,34 +38,21 @@ server_window_loop(void)
struct winlink *wl; struct winlink *wl;
struct window_pane *wp; struct window_pane *wp;
struct session *s; struct session *s;
u_int i, j; u_int i;
for (i = 0; i < ARRAY_LENGTH(&windows); i++) { for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
w = ARRAY_ITEM(&windows, i); w = ARRAY_ITEM(&windows, i);
if (w == NULL) if (w == NULL)
continue; continue;
TAILQ_FOREACH(wp, &w->panes, entry) { RB_FOREACH(s, sessions, &sessions) {
if (wp->fd == -1)
continue;
if (!(wp->flags & PANE_FREEZE)) {
if (server_window_backoff(wp))
bufferevent_disable(wp->event, EV_READ);
else
bufferevent_enable(wp->event, EV_READ);
}
}
for (j = 0; j < ARRAY_LENGTH(&sessions); j++) {
s = ARRAY_ITEM(&sessions, j);
if (s == NULL)
continue;
wl = session_has(s, w); wl = session_has(s, w);
if (wl == NULL) if (wl == NULL)
continue; continue;
if (server_window_check_bell(s, wl) || if (server_window_check_bell(s, wl) ||
server_window_check_activity(s, wl)) server_window_check_activity(s, wl) ||
server_window_check_silence(s, wl))
server_status_session(s); server_status_session(s);
TAILQ_FOREACH(wp, &w->panes, entry) TAILQ_FOREACH(wp, &w->panes, entry)
server_window_check_content(s, wl, wp); server_window_check_content(s, wl, wp);
@@ -109,10 +72,8 @@ server_window_check_bell(struct session *s, struct winlink *wl)
if (!(w->flags & WINDOW_BELL) || wl->flags & WINLINK_BELL) if (!(w->flags & WINDOW_BELL) || wl->flags & WINLINK_BELL)
return (0); return (0);
if (s->curw == wl) if (s->curw != wl)
return (0); wl->flags |= WINLINK_BELL;
wl->flags |= WINLINK_BELL;
action = options_get_number(&s->options, "bell-action"); action = options_get_number(&s->options, "bell-action");
switch (action) { switch (action) {
@@ -189,6 +150,55 @@ server_window_check_activity(struct session *s, struct winlink *wl)
return (1); return (1);
} }
/* Check for silence in window. */
int
server_window_check_silence(struct session *s, struct winlink *wl)
{
struct client *c;
struct window *w = wl->window;
struct timeval timer;
u_int i;
int silence_interval, timer_difference;
if (!(w->flags & WINDOW_SILENCE) || wl->flags & WINLINK_SILENCE)
return (0);
if (s->curw == wl) {
/*
* Reset the timer for this window if we've focused it. We
* don't want the timer tripping as soon as we've switched away
* from this window.
*/
if (gettimeofday(&w->silence_timer, NULL) != 0)
fatal("gettimeofday failed.");
return (0);
}
silence_interval = options_get_number(&w->options, "monitor-silence");
if (silence_interval == 0)
return (0);
if (gettimeofday(&timer, NULL) != 0)
fatal("gettimeofday");
timer_difference = timer.tv_sec - w->silence_timer.tv_sec;
if (timer_difference <= silence_interval)
return (0);
wl->flags |= WINLINK_SILENCE;
if (options_get_number(&s->options, "visual-silence")) {
for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
c = ARRAY_ITEM(&clients, i);
if (c == NULL || c->session != s)
continue;
status_message_set(c, "Silence in window %u",
winlink_find_by_window(&s->windows, w)->idx);
}
}
return (1);
}
/* Check for content change in window. */ /* Check for content change in window. */
int int
server_window_check_content( server_window_check_content(

View File

@@ -1,4 +1,4 @@
/* $Id: server.c,v 1.242 2010-06-22 23:21:39 tcunha Exp $ */ /* $Id: server.c,v 1.249 2010-12-22 15:36:44 tcunha Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -97,8 +97,6 @@ server_create_socket(void)
fatal("fcntl failed"); fatal("fcntl failed");
if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1) if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
fatal("fcntl failed"); fatal("fcntl failed");
if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
fatal("fcntl failed");
server_update_socket(); server_update_socket();
@@ -107,16 +105,13 @@ server_create_socket(void)
/* Fork new server. */ /* Fork new server. */
int int
server_start(char *path) server_start(void)
{ {
struct window_pane *wp; struct window_pane *wp;
int pair[2]; int pair[2];
char *cause; char *cause;
struct timeval tv; struct timeval tv;
u_int i; u_int i;
#ifdef HAVE_SETPROCTITLE
char rpathbuf[MAXPATHLEN];
#endif
/* The first client is special and gets a socketpair; create it. */ /* The first client is special and gets a socketpair; create it. */
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
@@ -143,7 +138,7 @@ server_start(char *path)
/* event_init() was called in our parent, need to reinit. */ /* event_init() was called in our parent, need to reinit. */
if (event_reinit(ev_base) != 0) if (event_reinit(ev_base) != 0)
fatal("event_reinit failed"); fatal("event_reinit failed");
clear_signals(); clear_signals(0);
logfile("server"); logfile("server");
log_debug("server started, pid %ld", (long) getpid()); log_debug("server started, pid %ld", (long) getpid());
@@ -151,21 +146,17 @@ server_start(char *path)
ARRAY_INIT(&windows); ARRAY_INIT(&windows);
ARRAY_INIT(&clients); ARRAY_INIT(&clients);
ARRAY_INIT(&dead_clients); ARRAY_INIT(&dead_clients);
ARRAY_INIT(&sessions); RB_INIT(&sessions);
ARRAY_INIT(&dead_sessions); RB_INIT(&dead_sessions);
TAILQ_INIT(&session_groups); TAILQ_INIT(&session_groups);
mode_key_init_trees(); mode_key_init_trees();
key_bindings_init(); key_bindings_init();
utf8_build(); utf8_build();
start_time = time(NULL); start_time = time(NULL);
socket_path = path;
#ifdef HAVE_SETPROCTITLE
if (realpath(socket_path, rpathbuf) == NULL)
strlcpy(rpathbuf, socket_path, sizeof rpathbuf);
log_debug("socket path %s", socket_path); log_debug("socket path %s", socket_path);
setproctitle("server (%s)", rpathbuf); #ifdef HAVE_SETPROCTITLE
setproctitle("server (%s)", socket_path);
#endif #endif
server_fd = server_create_socket(); server_fd = server_create_socket();
@@ -184,8 +175,8 @@ server_start(char *path)
* If there is a session already, put the current window and pane into * If there is a session already, put the current window and pane into
* more mode. * more mode.
*/ */
if (!ARRAY_EMPTY(&sessions) && !ARRAY_EMPTY(&cfg_causes)) { if (!RB_EMPTY(&sessions) && !ARRAY_EMPTY(&cfg_causes)) {
wp = ARRAY_FIRST(&sessions)->curw->window->active; wp = RB_MIN(sessions, &sessions)->curw->window->active;
window_pane_set_mode(wp, &window_copy_mode); window_pane_set_mode(wp, &window_copy_mode);
window_copy_init_for_output(wp); window_copy_init_for_output(wp);
for (i = 0; i < ARRAY_LENGTH(&cfg_causes); i++) { for (i = 0; i < ARRAY_LENGTH(&cfg_causes); i++) {
@@ -226,14 +217,14 @@ server_loop(void)
} }
} }
/* Check if the server should be shutting down (no more clients or windows). */ /* Check if the server should be shutting down (no more clients or sessions). */
int int
server_should_shutdown(void) server_should_shutdown(void)
{ {
u_int i; u_int i;
for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { if (!options_get_number(&global_options, "exit-unattached")) {
if (ARRAY_ITEM(&sessions, i) != NULL) if (!RB_EMPTY(&sessions))
return (0); return (0);
} }
for (i = 0; i < ARRAY_LENGTH(&clients); i++) { for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
@@ -248,7 +239,7 @@ void
server_send_shutdown(void) server_send_shutdown(void)
{ {
struct client *c; struct client *c;
struct session *s; struct session *s, *next_s;
u_int i; u_int i;
for (i = 0; i < ARRAY_LENGTH(&clients); i++) { for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
@@ -262,9 +253,11 @@ server_send_shutdown(void)
} }
} }
for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { s = RB_MIN(sessions, &sessions);
if ((s = ARRAY_ITEM(&sessions, i)) != NULL) while (s != NULL) {
session_destroy(s); next_s = RB_NEXT(sessions, &sessions, s);
session_destroy(s);
s = next_s;
} }
} }
@@ -272,16 +265,19 @@ server_send_shutdown(void)
void void
server_clean_dead(void) server_clean_dead(void)
{ {
struct session *s; struct session *s, *next_s;
struct client *c; struct client *c;
u_int i; u_int i;
for (i = 0; i < ARRAY_LENGTH(&dead_sessions); i++) { s = RB_MIN(sessions, &dead_sessions);
s = ARRAY_ITEM(&dead_sessions, i); while (s != NULL) {
if (s == NULL || s->references != 0) next_s = RB_NEXT(sessions, &dead_sessions, s);
continue; if (s->references == 0) {
ARRAY_SET(&dead_sessions, i, NULL); RB_REMOVE(sessions, &dead_sessions, s);
xfree(s); xfree(s->name);
xfree(s);
}
s = next_s;
} }
for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) { for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) {
@@ -298,14 +294,13 @@ void
server_update_socket(void) server_update_socket(void)
{ {
struct session *s; struct session *s;
u_int i;
static int last = -1; static int last = -1;
int n; int n, mode;
struct stat sb;
n = 0; n = 0;
for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { RB_FOREACH(s, sessions, &sessions) {
s = ARRAY_ITEM(&sessions, i); if (!(s->flags & SESSION_UNATTACHED)) {
if (s != NULL && !(s->flags & SESSION_UNATTACHED)) {
n++; n++;
break; break;
} }
@@ -313,10 +308,20 @@ server_update_socket(void)
if (n != last) { if (n != last) {
last = n; last = n;
if (n != 0)
chmod(socket_path, S_IRWXU|S_IRWXG); if (stat(socket_path, &sb) != 0)
else return;
chmod(socket_path, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); mode = sb.st_mode;
if (n != 0) {
if (mode & S_IRUSR)
mode |= S_IXUSR;
if (mode & S_IRGRP)
mode |= S_IXGRP;
if (mode & S_IROTH)
mode |= S_IXOTH;
} else
mode &= ~(S_IXUSR|S_IXGRP|S_IXOTH);
chmod(socket_path, mode);
} }
} }
@@ -482,15 +487,11 @@ void
server_lock_server(void) server_lock_server(void)
{ {
struct session *s; struct session *s;
u_int i;
int timeout; int timeout;
time_t t; time_t t;
t = time(NULL); t = time(NULL);
for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { RB_FOREACH(s, sessions, &sessions) {
if ((s = ARRAY_ITEM(&sessions, i)) == NULL)
continue;
if (s->flags & SESSION_UNATTACHED) { if (s->flags & SESSION_UNATTACHED) {
if (gettimeofday(&s->activity_time, NULL) != 0) if (gettimeofday(&s->activity_time, NULL) != 0)
fatal("gettimeofday failed"); fatal("gettimeofday failed");
@@ -511,15 +512,11 @@ void
server_lock_sessions(void) server_lock_sessions(void)
{ {
struct session *s; struct session *s;
u_int i;
int timeout; int timeout;
time_t t; time_t t;
t = time(NULL); t = time(NULL);
for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { RB_FOREACH(s, sessions, &sessions) {
if ((s = ARRAY_ITEM(&sessions, i)) == NULL)
continue;
if (s->flags & SESSION_UNATTACHED) { if (s->flags & SESSION_UNATTACHED) {
if (gettimeofday(&s->activity_time, NULL) != 0) if (gettimeofday(&s->activity_time, NULL) != 0)
fatal("gettimeofday failed"); fatal("gettimeofday failed");

124
session.c
View File

@@ -1,4 +1,4 @@
/* $Id: session.c,v 1.77 2010-07-02 02:49:19 tcunha Exp $ */ /* $Id: session.c,v 1.83 2010-12-22 15:36:44 tcunha Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -29,24 +29,56 @@
/* Global session list. */ /* Global session list. */
struct sessions sessions; struct sessions sessions;
struct sessions dead_sessions; struct sessions dead_sessions;
u_int next_session;
struct session_groups session_groups; struct session_groups session_groups;
struct winlink *session_next_alert(struct winlink *); struct winlink *session_next_alert(struct winlink *);
struct winlink *session_previous_alert(struct winlink *); struct winlink *session_previous_alert(struct winlink *);
RB_GENERATE(sessions, session, entry, session_cmp);
int
session_cmp(struct session *s1, struct session *s2)
{
return (strcmp(s1->name, s2->name));
}
/*
* Find if session is still alive. This is true if it is still on the global
* sessions list.
*/
int
session_alive(struct session *s)
{
struct session *s_loop;
RB_FOREACH(s_loop, sessions, &sessions) {
if (s_loop == s)
return (1);
}
return (0);
}
/* Find session by name. */ /* Find session by name. */
struct session * struct session *
session_find(const char *name) session_find(const char *name)
{ {
struct session *s; struct session s;
u_int i;
for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { s.name = (char *) name;
s = ARRAY_ITEM(&sessions, i); return (RB_FIND(sessions, &sessions, &s));
if (s != NULL && strcmp(s->name, name) == 0) }
/* Find session by index. */
struct session *
session_find_by_index(u_int idx)
{
struct session *s;
RB_FOREACH(s, sessions, &sessions) {
if (s->idx == idx)
return (s); return (s);
} }
return (NULL); return (NULL);
} }
@@ -57,7 +89,6 @@ session_create(const char *name, const char *cmd, const char *cwd,
char **cause) char **cause)
{ {
struct session *s; struct session *s;
u_int i;
s = xmalloc(sizeof *s); s = xmalloc(sizeof *s);
s->references = 0; s->references = 0;
@@ -89,19 +120,12 @@ session_create(const char *name, const char *cmd, const char *cwd,
s->sx = sx; s->sx = sx;
s->sy = sy; s->sy = sy;
for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { s->idx = next_session++;
if (ARRAY_ITEM(&sessions, i) == NULL) {
ARRAY_SET(&sessions, i, s);
break;
}
}
if (i == ARRAY_LENGTH(&sessions))
ARRAY_ADD(&sessions, s);
if (name != NULL) if (name != NULL)
s->name = xstrdup(name); s->name = xstrdup(name);
else else
xasprintf(&s->name, "%u", i); xasprintf(&s->name, "%u", s->idx);
RB_INSERT(sessions, &sessions, s);
if (cmd != NULL) { if (cmd != NULL) {
if (session_new(s, NULL, cmd, cwd, idx, cause) == NULL) { if (session_new(s, NULL, cmd, cwd, idx, cause) == NULL) {
@@ -120,15 +144,9 @@ session_create(const char *name, const char *cmd, const char *cwd,
void void
session_destroy(struct session *s) session_destroy(struct session *s)
{ {
u_int i;
log_debug("session %s destroyed", s->name); log_debug("session %s destroyed", s->name);
if (session_index(s, &i) != 0) RB_REMOVE(sessions, &sessions, s);
fatalx("session not found");
ARRAY_SET(&sessions, i, NULL);
while (!ARRAY_EMPTY(&sessions) && ARRAY_LAST(&sessions) == NULL)
ARRAY_TRUNC(&sessions, 1);
if (s->tio != NULL) if (s->tio != NULL)
xfree(s->tio); xfree(s->tio);
@@ -144,28 +162,48 @@ session_destroy(struct session *s)
winlink_remove(&s->windows, RB_ROOT(&s->windows)); winlink_remove(&s->windows, RB_ROOT(&s->windows));
xfree(s->cwd); xfree(s->cwd);
xfree(s->name);
for (i = 0; i < ARRAY_LENGTH(&dead_sessions); i++) { RB_INSERT(sessions, &dead_sessions, s);
if (ARRAY_ITEM(&dead_sessions, i) == NULL) {
ARRAY_SET(&dead_sessions, i, s);
break;
}
}
if (i == ARRAY_LENGTH(&dead_sessions))
ARRAY_ADD(&dead_sessions, s);
s->flags |= SESSION_DEAD;
} }
/* Find session index. */ /* Find the next usable session. */
int struct session *
session_index(struct session *s, u_int *i) session_next_session(struct session *s)
{ {
for (*i = 0; *i < ARRAY_LENGTH(&sessions); (*i)++) { struct session *s2;
if (s == ARRAY_ITEM(&sessions, *i))
return (0); if (RB_EMPTY(&sessions) || !session_alive(s))
} return (NULL);
return (-1);
s2 = s;
do {
s2 = RB_NEXT(sessions, &sessions, s2);
if (s2 == NULL)
s2 = RB_MIN(sessions, &sessions);
} while (s2 != s);
if (s2 == s)
return (NULL);
return (s2);
}
/* Find the previous usable session. */
struct session *
session_previous_session(struct session *s)
{
struct session *s2;
if (RB_EMPTY(&sessions) || !session_alive(s))
return (NULL);
s2 = s;
do {
s2 = RB_PREV(sessions, &sessions, s2);
if (s2 == NULL)
s2 = RB_MAX(sessions, &sessions);
} while (s2 != s);
if (s2 == s)
return (NULL);
return (s2);
} }
/* Create a new window on a session. */ /* Create a new window on a session. */

View File

@@ -1,4 +1,4 @@
/* $Id: signal.c,v 1.2 2010-05-14 14:35:26 tcunha Exp $ */ /* $Id: signal.c,v 1.3 2010-08-29 14:42:11 tcunha Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -62,7 +62,7 @@ set_signals(void(*handler)(int, short, unused void *))
} }
void void
clear_signals(void) clear_signals(int after_fork)
{ {
struct sigaction sigact; struct sigaction sigact;
@@ -79,10 +79,25 @@ clear_signals(void)
if (sigaction(SIGTSTP, &sigact, NULL) != 0) if (sigaction(SIGTSTP, &sigact, NULL) != 0)
fatal("sigaction failed"); fatal("sigaction failed");
event_del(&ev_sighup); if (after_fork) {
event_del(&ev_sigchld); if (sigaction(SIGHUP, &sigact, NULL) != 0)
event_del(&ev_sigcont); fatal("sigaction failed");
event_del(&ev_sigterm); if (sigaction(SIGCHLD, &sigact, NULL) != 0)
event_del(&ev_sigusr1); fatal("sigaction failed");
event_del(&ev_sigwinch); if (sigaction(SIGCONT, &sigact, NULL) != 0)
fatal("sigaction failed");
if (sigaction(SIGTERM, &sigact, NULL) != 0)
fatal("sigaction failed");
if (sigaction(SIGUSR1, &sigact, NULL) != 0)
fatal("sigaction failed");
if (sigaction(SIGWINCH, &sigact, NULL) != 0)
fatal("sigaction failed");
} else {
event_del(&ev_sighup);
event_del(&ev_sigchld);
event_del(&ev_sigcont);
event_del(&ev_sigterm);
event_del(&ev_sigusr1);
event_del(&ev_sigwinch);
}
} }

View File

@@ -1,4 +1,4 @@
/* $Id: status.c,v 1.149 2010-06-22 23:26:18 tcunha Exp $ */ /* $Id: status.c,v 1.151 2010-12-11 16:15:02 nicm Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -41,9 +41,14 @@ void status_replace1(struct client *,
struct winlink *, char **, char **, char *, size_t, int); struct winlink *, char **, char **, char *, size_t, int);
void status_message_callback(int, short, void *); void status_message_callback(int, short, void *);
void status_prompt_add_history(struct client *); const char *status_prompt_up_history(u_int *);
const char *status_prompt_down_history(u_int *);
void status_prompt_add_history(const char *);
char *status_prompt_complete(const char *); char *status_prompt_complete(const char *);
/* Status prompt history. */
ARRAY_DECL(, char *) status_prompt_history = ARRAY_INITIALIZER;
/* Retrieve options for left string. */ /* Retrieve options for left string. */
char * char *
status_redraw_get_left(struct client *c, status_redraw_get_left(struct client *c,
@@ -395,6 +400,8 @@ status_replace1(struct client *c,struct winlink *wl,
tmp[0] = '!'; tmp[0] = '!';
else if (wl->flags & WINLINK_ACTIVITY) else if (wl->flags & WINLINK_ACTIVITY)
tmp[0] = '#'; tmp[0] = '#';
else if (wl->flags & WINLINK_SILENCE)
tmp[0] = '~';
else if (wl == s->curw) else if (wl == s->curw)
tmp[0] = '*'; tmp[0] = '*';
else if (wl == TAILQ_FIRST(&s->lastw)) else if (wl == TAILQ_FIRST(&s->lastw))
@@ -858,6 +865,7 @@ status_prompt_key(struct client *c, int key)
{ {
struct paste_buffer *pb; struct paste_buffer *pb;
char *s, *first, *last, word[64], swapc; char *s, *first, *last, word[64], swapc;
const char *histstr;
u_char ch; u_char ch;
size_t size, n, off, idx; size_t size, n, off, idx;
@@ -970,29 +978,20 @@ status_prompt_key(struct client *c, int key)
} }
break; break;
case MODEKEYEDIT_HISTORYUP: case MODEKEYEDIT_HISTORYUP:
if (ARRAY_LENGTH(&c->prompt_hdata) == 0) histstr = status_prompt_up_history(&c->prompt_hindex);
if (histstr == NULL)
break; break;
xfree(c->prompt_buffer); xfree(c->prompt_buffer);
c->prompt_buffer = xstrdup(histstr);
c->prompt_buffer = xstrdup(ARRAY_ITEM(&c->prompt_hdata,
ARRAY_LENGTH(&c->prompt_hdata) - 1 - c->prompt_hindex));
if (c->prompt_hindex != ARRAY_LENGTH(&c->prompt_hdata) - 1)
c->prompt_hindex++;
c->prompt_index = strlen(c->prompt_buffer); c->prompt_index = strlen(c->prompt_buffer);
c->flags |= CLIENT_STATUS; c->flags |= CLIENT_STATUS;
break; break;
case MODEKEYEDIT_HISTORYDOWN: case MODEKEYEDIT_HISTORYDOWN:
histstr = status_prompt_down_history(&c->prompt_hindex);
if (histstr == NULL)
break;
xfree(c->prompt_buffer); xfree(c->prompt_buffer);
c->prompt_buffer = xstrdup(histstr);
if (c->prompt_hindex != 0) {
c->prompt_hindex--;
c->prompt_buffer = xstrdup(ARRAY_ITEM(
&c->prompt_hdata, ARRAY_LENGTH(
&c->prompt_hdata) - 1 - c->prompt_hindex));
} else
c->prompt_buffer = xstrdup("");
c->prompt_index = strlen(c->prompt_buffer); c->prompt_index = strlen(c->prompt_buffer);
c->flags |= CLIENT_STATUS; c->flags |= CLIENT_STATUS;
break; break;
@@ -1034,7 +1033,7 @@ status_prompt_key(struct client *c, int key)
break; break;
case MODEKEYEDIT_ENTER: case MODEKEYEDIT_ENTER:
if (*c->prompt_buffer != '\0') if (*c->prompt_buffer != '\0')
status_prompt_add_history(c); status_prompt_add_history(c->prompt_buffer);
if (c->prompt_callbackfn(c->prompt_data, c->prompt_buffer) == 0) if (c->prompt_callbackfn(c->prompt_data, c->prompt_buffer) == 0)
status_prompt_clear(c); status_prompt_clear(c);
break; break;
@@ -1070,20 +1069,56 @@ status_prompt_key(struct client *c, int key)
} }
} }
/* Get previous line from the history. */
const char *
status_prompt_up_history(u_int *idx)
{
u_int size;
/*
* History runs from 0 to size - 1.
*
* Index is from 0 to size. Zero is empty.
*/
size = ARRAY_LENGTH(&status_prompt_history);
if (size == 0 || *idx == size)
return (NULL);
(*idx)++;
return (ARRAY_ITEM(&status_prompt_history, size - *idx));
}
/* Get next line from the history. */
const char *
status_prompt_down_history(u_int *idx)
{
u_int size;
size = ARRAY_LENGTH(&status_prompt_history);
if (size == 0 || *idx == 0)
return ("");
(*idx)--;
if (*idx == 0)
return ("");
return (ARRAY_ITEM(&status_prompt_history, size - *idx));
}
/* Add line to the history. */ /* Add line to the history. */
void void
status_prompt_add_history(struct client *c) status_prompt_add_history(const char *line)
{ {
if (ARRAY_LENGTH(&c->prompt_hdata) > 0 && u_int size;
strcmp(ARRAY_LAST(&c->prompt_hdata), c->prompt_buffer) == 0)
size = ARRAY_LENGTH(&status_prompt_history);
if (size > 0 && strcmp(ARRAY_LAST(&status_prompt_history), line) == 0)
return; return;
if (ARRAY_LENGTH(&c->prompt_hdata) == PROMPT_HISTORY) { if (size == PROMPT_HISTORY) {
xfree(ARRAY_FIRST(&c->prompt_hdata)); xfree(ARRAY_FIRST(&status_prompt_history));
ARRAY_REMOVE(&c->prompt_hdata, 0); ARRAY_REMOVE(&status_prompt_history, 0);
} }
ARRAY_ADD(&c->prompt_hdata, xstrdup(c->prompt_buffer)); ARRAY_ADD(&status_prompt_history, xstrdup(line));
} }
/* Complete word. */ /* Complete word. */

169
tmux.1
View File

@@ -1,4 +1,4 @@
.\" $Id: tmux.1,v 1.264 2010-07-17 14:39:01 tcunha Exp $ .\" $Id: tmux.1,v 1.280 2010-12-27 21:17:25 tcunha Exp $
.\" .\"
.\" Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> .\" Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
.\" .\"
@@ -14,7 +14,7 @@
.\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING .\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
.\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\" .\"
.Dd $Mdocdate: July 15 2010 $ .Dd $Mdocdate: December 10 2010 $
.Dt TMUX 1 .Dt TMUX 1
.Os .Os
.Sh NAME .Sh NAME
@@ -23,7 +23,7 @@
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm tmux .Nm tmux
.Bk -words .Bk -words
.Op Fl 28lquv .Op Fl 28lquvV
.Op Fl c Ar shell-command .Op Fl c Ar shell-command
.Op Fl f Ar file .Op Fl f Ar file
.Op Fl L Ar socket-name .Op Fl L Ar socket-name
@@ -198,6 +198,10 @@ and
files in the current directory, where files in the current directory, where
.Em PID .Em PID
is the PID of the server or client process. is the PID of the server or client process.
.It Fl V
Report the
.Nm
version.
.It Ar command Op Ar flags .It Ar command Op Ar flags
This specifies one of a set of commands used to control This specifies one of a set of commands used to control
.Nm , .Nm ,
@@ -248,6 +252,8 @@ Select windows 0 to 9.
Enter the Enter the
.Nm .Nm
command prompt. command prompt.
.It ;
Move to the previously active pane.
.It = .It =
Choose which buffer to paste interactively from a list. Choose which buffer to paste interactively from a list.
.It \&? .It \&?
@@ -280,6 +286,8 @@ Briefly display pane indexes.
Force redraw of the attached client. Force redraw of the attached client.
.It s .It s
Select a new session for the attached client interactively. Select a new session for the attached client interactively.
.It L
Switch the attached client back to the last session.
.It t .It t
Show the time. Show the time.
.It w .It w
@@ -507,7 +515,7 @@ when they are created with the
command, or later with the command, or later with the
.Ic attach-session .Ic attach-session
command. command.
Each session has one of more windows Each session has one or more windows
.Em linked .Em linked
into it. into it.
Windows may be linked to multiple sessions and are made up of one or Windows may be linked to multiple sessions and are made up of one or
@@ -660,6 +668,7 @@ Suspend a client by sending
.Dv SIGTSTP .Dv SIGTSTP
(tty stop). (tty stop).
.It Xo Ic switch-client .It Xo Ic switch-client
.Op Fl lnp
.Op Fl c Ar target-client .Op Fl c Ar target-client
.Op Fl t Ar target-session .Op Fl t Ar target-session
.Xc .Xc
@@ -668,6 +677,13 @@ Switch the current session for client
.Ar target-client .Ar target-client
to to
.Ar target-session . .Ar target-session .
If
.Fl l ,
.Fl n
or
.Fl p
is used, the client is moved to the last, next or previous session
respectively.
.El .El
.Sh WINDOWS AND PANES .Sh WINDOWS AND PANES
A A
@@ -890,6 +906,7 @@ $ tmux list-windows
layout: bb62,159x48,0,0{79x48,0,0,79x48,80,0} layout: bb62,159x48,0,0{79x48,0,0,79x48,80,0}
$ tmux select-layout bb62,159x48,0,0{79x48,0,0,79x48,80,0} $ tmux select-layout bb62,159x48,0,0{79x48,0,0,79x48,80,0}
.Ed .Ed
.Pp
.Nm .Nm
automatically adjusts the size of the layout for the current window size. automatically adjusts the size of the layout for the current window size.
Note that a layout cannot be applied to a window with more panes than that Note that a layout cannot be applied to a window with more panes than that
@@ -1031,6 +1048,9 @@ option kills all but the pane given with
Kill the current window or the window at Kill the current window or the window at
.Ar target-window , .Ar target-window ,
removing it from any sessions to which it is linked. removing it from any sessions to which it is linked.
.It Ic last-pane Op Fl t Ar target-window
.D1 (alias: Ic lastp )
Select the last (previously selected) pane.
.It Ic last-window Op Fl t Ar target-session .It Ic last-window Op Fl t Ar target-session
.D1 (alias: Ic last ) .D1 (alias: Ic last )
Select the last (previously selected) window. Select the last (previously selected) window.
@@ -1468,7 +1488,7 @@ All arguments are sent sequentially from first to last.
Send the prefix key to a window as if it was pressed. Send the prefix key to a window as if it was pressed.
If multiple prefix keys are configured, only the first is sent. If multiple prefix keys are configured, only the first is sent.
.It Xo Ic unbind-key .It Xo Ic unbind-key
.Op Fl cn .Op Fl acn
.Op Fl t Ar key-table .Op Fl t Ar key-table
.Ar key .Ar key
.Xc .Xc
@@ -1482,6 +1502,9 @@ the primary key bindings are modified; in this case, if
is specified, the command bound to is specified, the command bound to
.Ar key .Ar key
without a prefix (if any) is removed. without a prefix (if any) is removed.
If
.Fl a
is present, all key bindings are removed.
.Pp .Pp
If If
.Fl t .Fl t
@@ -1575,17 +1598,15 @@ Available window options are listed under
.Pp .Pp
Available server options are: Available server options are:
.Bl -tag -width Ds .Bl -tag -width Ds
.It Ic detach-on-destroy
If on (the default), the client is detached when the session it is attached to
is destroyed.
If off, the client is switched to the most recently active of the remaining
sessions.
.It Ic escape-time .It Ic escape-time
Set the time in milliseconds for which Set the time in milliseconds for which
.Nm .Nm
waits after an escape is input to determine if it is part of a function or meta waits after an escape is input to determine if it is part of a function or meta
key sequences. key sequences.
The default is 500 milliseconds. The default is 500 milliseconds.
.It Ic exit-unattached
If enabled, the server will exit when there are no attached clients, rather
than when there are no attached sessions.
.It Ic quiet .It Ic quiet
Enable or disable the display of various informational messages (see also the Enable or disable the display of various informational messages (see also the
.Fl q .Fl q
@@ -1625,6 +1646,11 @@ The default is an empty string, which instructs
to create a login shell using the value of the to create a login shell using the value of the
.Ic default-shell .Ic default-shell
option. option.
.It Ic default-path Ar path
Set the default working directory for processes created from keys, or
interactively from the prompt.
The default is empty, which means to use the working directory of the shell
from which the server was started if it is available or the user's home if not.
.It Ic default-shell Ar path .It Ic default-shell Ar path
Specify the default shell. Specify the default shell.
This is used as the login shell for new windows when the This is used as the login shell for new windows when the
@@ -1641,11 +1667,6 @@ or
This option should be configured when This option should be configured when
.Nm .Nm
is used as a login shell. is used as a login shell.
.It Ic default-path Ar path
Set the default working directory for processes created from keys, or
interactively from the prompt.
The default is empty, which means to use the working directory of the shell
from which the server was started if it is available or the user's home if not.
.It Ic default-terminal Ar terminal .It Ic default-terminal Ar terminal
Set the default terminal for new windows created in this session - the Set the default terminal for new windows created in this session - the
default value of the default value of the
@@ -1658,6 +1679,14 @@ to work correctly, this
be set to be set to
.Ql screen .Ql screen
or a derivative of it. or a derivative of it.
.It Ic destroy-unattached
If enabled and the session is no longer attached to any clients, it is
destroyed.
.It Ic detach-on-destroy
If on (the default), the client is detached when the session it is attached to
is destroyed.
If off, the client is switched to the most recently active of the remaining
sessions.
.It Ic display-panes-active-colour Ar colour .It Ic display-panes-active-colour Ar colour
Set the colour used by the Set the colour used by the
.Ic display-panes .Ic display-panes
@@ -1754,12 +1783,12 @@ If on,
captures the mouse and when a window is split into multiple panes the mouse may captures the mouse and when a window is split into multiple panes the mouse may
be used to select the current pane. be used to select the current pane.
The mouse click is also passed through to the application as normal. The mouse click is also passed through to the application as normal.
.It Ic pane-border-fg Ar colour
.It Ic pane-border-bg Ar colour
Set the pane border colour for panes aside from the active pane.
.It Ic pane-active-border-fg Ar colour
.It Ic pane-active-border-bg Ar colour .It Ic pane-active-border-bg Ar colour
.It Ic pane-active-border-fg Ar colour
Set the pane border colour for the currently active pane. Set the pane border colour for the currently active pane.
.It Ic pane-border-bg Ar colour
.It Ic pane-border-fg Ar colour
Set the pane border colour for panes aside from the active pane.
.It Ic prefix Ar keys .It Ic prefix Ar keys
Set the keys accepted as a prefix key. Set the keys accepted as a prefix key.
.Ar keys .Ar keys
@@ -1832,7 +1861,12 @@ or right justified.
.Xc .Xc
Use vi or emacs-style Use vi or emacs-style
key bindings in the status line, for example at the command prompt. key bindings in the status line, for example at the command prompt.
Defaults to emacs. The default is emacs, unless the
.Ev VISUAL
or
.Ev EDITOR
environment variables are set and contain the string
.Ql vi .
.It Ic status-left Ar string .It Ic status-left Ar string
Display Display
.Ar string .Ar string
@@ -1909,10 +1943,10 @@ is not interpreted, to enable UTF-8, use the
option. option.
.It Ic status-left-attr Ar attributes .It Ic status-left-attr Ar attributes
Set the attribute of the left part of the status line. Set the attribute of the left part of the status line.
.It Ic status-left-fg Ar colour
Set the foreground colour of the left part of the status line.
.It Ic status-left-bg Ar colour .It Ic status-left-bg Ar colour
Set the background colour of the left part of the status line. Set the background colour of the left part of the status line.
.It Ic status-left-fg Ar colour
Set the foreground colour of the left part of the status line.
.It Ic status-left-length Ar length .It Ic status-left-length Ar length
Set the maximum Set the maximum
.Ar length .Ar length
@@ -1934,16 +1968,15 @@ character pairs are replaced, and UTF-8 is dependent on the
option. option.
.It Ic status-right-attr Ar attributes .It Ic status-right-attr Ar attributes
Set the attribute of the right part of the status line. Set the attribute of the right part of the status line.
.It Ic status-right-fg Ar colour
Set the foreground colour of the right part of the status line.
.It Ic status-right-bg Ar colour .It Ic status-right-bg Ar colour
Set the background colour of the right part of the status line. Set the background colour of the right part of the status line.
.It Ic status-right-fg Ar colour
Set the foreground colour of the right part of the status line.
.It Ic status-right-length Ar length .It Ic status-right-length Ar length
Set the maximum Set the maximum
.Ar length .Ar length
of the right component of the status bar. of the right component of the status bar.
The default is 40. The default is 40.
.Pp
.It Xo Ic status-utf8 .It Xo Ic status-utf8
.Op Ic on | off .Op Ic on | off
.Xc .Xc
@@ -2002,7 +2035,8 @@ was given to the
.Ic set-environment .Ic set-environment
command). command).
The default is The default is
"DISPLAY WINDOWID SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID SSH_CONNECTION". "DISPLAY SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID SSH_CONNECTION WINDOWID
XAUTHORITY".
.It Xo Ic visual-activity .It Xo Ic visual-activity
.Op Ic on | off .Op Ic on | off
.Xc .Xc
@@ -2027,6 +2061,12 @@ display a message when content is present in a window
for which the for which the
.Ic monitor-content .Ic monitor-content
window option is enabled. window option is enabled.
.It Xo Ic visual-silence
.Op Ic on | off
.Xc
If
.Ic monitor-silence
is enabled, prints a message after the interval has expired on a given window.
.El .El
.It Xo Ic set-window-option .It Xo Ic set-window-option
.Op Fl agu .Op Fl agu
@@ -2060,6 +2100,19 @@ this option is good for full-screen programs which support
.Dv SIGWINCH .Dv SIGWINCH
and poor for interactive programs such as shells. and poor for interactive programs such as shells.
.Pp .Pp
.It Xo Ic alternate-screen
.Op Ic on | off
.Xc
This option configures whether programs running inside
.Nm
may use the terminal alternate screen feature, which allows the
.Em smcup
and
.Em rmcup
.Xr terminfo 5
capabilities to be issued to preserve the existing window content on start and
restore it on exit.
.Pp
.It Xo Ic automatic-rename .It Xo Ic automatic-rename
.Op Ic on | off .Op Ic on | off
.Xc .Xc
@@ -2070,7 +2123,8 @@ will attempt - on supported platforms - to rename the window to reflect the
command currently running in it. command currently running in it.
This flag is automatically disabled for an individual window when a name This flag is automatically disabled for an individual window when a name
is specified at creation with is specified at creation with
.Ic new-window or .Ic new-window
or
.Ic new-session , .Ic new-session ,
or later with or later with
.Ic rename-window . .Ic rename-window .
@@ -2097,8 +2151,8 @@ or
.Ar height . .Ar height .
A value of zero restores the default unlimited setting. A value of zero restores the default unlimited setting.
.Pp .Pp
.It Ic main-pane-width Ar width
.It Ic main-pane-height Ar height .It Ic main-pane-height Ar height
.It Ic main-pane-width Ar width
Set the width or height of the main (left or top) pane in the Set the width or height of the main (left or top) pane in the
.Ic main-horizontal .Ic main-horizontal
or or
@@ -2118,7 +2172,14 @@ Set window modes foreground colour.
.Op Ic vi | emacs .Op Ic vi | emacs
.Xc .Xc
Use vi or emacs-style key bindings in copy and choice modes. Use vi or emacs-style key bindings in copy and choice modes.
Key bindings default to emacs. As with the
.Ic status-keys
option, the default is emacs, unless
.Ev VISUAL
or
.Ev EDITOR
contains
.Ql vi .
.Pp .Pp
.It Xo Ic mode-mouse .It Xo Ic mode-mouse
.Op Ic on | off .Op Ic on | off
@@ -2141,6 +2202,35 @@ pattern
.Ar match-string .Ar match-string
appears in the window, it is highlighted in the status line. appears in the window, it is highlighted in the status line.
.Pp .Pp
.It Xo Ic monitor-silence
.Op Ic interval
.Xc
Monitor for silence (no activity) in the window within
.Ic interval
seconds.
Windows that have been silent for the interval are highlighted in the
status line.
An interval of zero disables the monitoring.
.Pp
.It Ic other-pane-height Ar height
Set the height of the other panes (not the main pane) in the
.Ic main-horizontal
layout.
If this option is set to 0 (the default), it will have no effect.
If both the
.Ic main-pane-height
and
.Ic other-pane-height
options are set, the main pane will grow taller to make the other panes the
specified height, but will never shrink to do so.
.Pp
.It Ic other-pane-width Ar width
Like
.Ic other-pane-height ,
but set the width of other panes in the
.Ic main-vertical
layout.
.Pp
.It Xo Ic remain-on-exit .It Xo Ic remain-on-exit
.Op Ic on | off .Op Ic on | off
.Xc .Xc
@@ -2156,19 +2246,6 @@ command.
Duplicate input to any pane to all other panes in the same window (only Duplicate input to any pane to all other panes in the same window (only
for panes that are not in any special mode). for panes that are not in any special mode).
.Pp .Pp
.It Xo Ic alternate-screen
.Op Ic on | off
.Xc
This option configures whether programs running inside
.Nm
may use the terminal alternate screen feature, which allows the
.Em smcup
and
.Em rmcup
.Xr terminfo 5
capabilities to be issued to preserve the existing window content on start and
restore it on exit.
.Pp
.It Xo Ic utf8 .It Xo Ic utf8
.Op Ic on | off .Op Ic on | off
.Xc .Xc
@@ -2269,9 +2346,9 @@ copies the environment into the
.Em global environment ; .Em global environment ;
in addition, each session has a in addition, each session has a
.Em session environment . .Em session environment .
When a window is created, the session and global environments are merged with When a window is created, the session and global environments are merged.
the session environment overriding any variable present in both. If a variable exists in both, the value from the session environment is used.
This is the initial environment passed to the new process. The result is the initial environment passed to the new process.
.Pp .Pp
The The
.Ic update-environment .Ic update-environment
@@ -2352,6 +2429,7 @@ The flag is one of the following symbols appended to the window name:
.It Li "#" Ta "Window is monitored and activity has been detected." .It Li "#" Ta "Window is monitored and activity has been detected."
.It Li "!" Ta "A bell has occurred in the window." .It Li "!" Ta "A bell has occurred in the window."
.It Li "+" Ta "Window is monitored for content and it has appeared." .It Li "+" Ta "Window is monitored for content and it has appeared."
.It Li "~" Ta "The window has been silent for the monitor-silence interval."
.El .El
.Pp .Pp
The # symbol relates to the The # symbol relates to the
@@ -2569,7 +2647,6 @@ Set the contents of the specified buffer to
Display the contents of the specified buffer. Display the contents of the specified buffer.
.El .El
.Sh MISCELLANEOUS .Sh MISCELLANEOUS
.Pp
Miscellaneous commands are as follows: Miscellaneous commands are as follows:
.Bl -tag -width Ds .Bl -tag -width Ds
.It Ic clock-mode Op Fl t Ar target-pane .It Ic clock-mode Op Fl t Ar target-pane

319
tmux.c
View File

@@ -1,4 +1,4 @@
/* $Id: tmux.c,v 1.214 2010-07-17 14:36:41 tcunha Exp $ */ /* $Id: tmux.c,v 1.228 2010-12-27 21:22:24 tcunha Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -18,15 +18,13 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/wait.h>
#include <errno.h> #include <errno.h>
#include <event.h> #include <event.h>
#include <fcntl.h>
#include <pwd.h> #include <pwd.h>
#include <signal.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <syslog.h>
#include <unistd.h> #include <unistd.h>
#include "tmux.h" #include "tmux.h"
@@ -35,7 +33,6 @@
extern char *malloc_options; extern char *malloc_options;
#endif #endif
char *cfg_file;
struct options global_options; /* server options */ struct options global_options; /* server options */
struct options global_s_options; /* session options */ struct options global_s_options; /* session options */
struct options global_w_options; /* window options */ struct options global_w_options; /* window options */
@@ -43,27 +40,19 @@ struct environ global_environ;
struct event_base *ev_base; struct event_base *ev_base;
char *cfg_file;
char *shell_cmd;
int debug_level; int debug_level;
time_t start_time; time_t start_time;
char *socket_path; char socket_path[MAXPATHLEN];
int login_shell; int login_shell;
char *environ_path;
struct env_data { pid_t environ_pid;
char *path; u_int environ_idx;
pid_t pid;
u_int idx;
};
__dead void usage(void); __dead void usage(void);
void parse_env(struct env_data *); void parseenvironment(void);
char *makesockpath(const char *); char *makesocketpath(const char *);
__dead void shell_exec(const char *, const char *);
struct imsgbuf *main_ibuf;
void main_signal(int, short, unused void *);
void main_callback(int, short, void *);
void main_dispatch(const char *);
#ifndef HAVE_PROGNAME #ifndef HAVE_PROGNAME
char *__progname = (char *) "tmux"; char *__progname = (char *) "tmux";
@@ -73,7 +62,7 @@ __dead void
usage(void) usage(void)
{ {
fprintf(stderr, fprintf(stderr,
"usage: %s [-28lquv] [-c shell-command] [-f file] [-L socket-name]\n" "usage: %s [-28lquvV] [-c shell-command] [-f file] [-L socket-name]\n"
" [-S socket-path] [command [flags]]\n", " [-S socket-path] [command [flags]]\n",
__progname); __progname);
exit(1); exit(1);
@@ -137,14 +126,14 @@ areshell(const char *shell)
} }
void void
parse_env(struct env_data *data) parseenvironment(void)
{ {
char *env, *path_pid, *pid_idx, buf[256]; char *env, *path_pid, *pid_idx, buf[256];
size_t len; size_t len;
const char *errstr; const char *errstr;
long long ll; long long ll;
data->pid = -1; environ_pid = -1;
if ((env = getenv("TMUX")) == NULL) if ((env = getenv("TMUX")) == NULL)
return; return;
@@ -157,9 +146,9 @@ parse_env(struct env_data *data)
/* path */ /* path */
len = path_pid - env; len = path_pid - env;
data->path = xmalloc (len + 1); environ_path = xmalloc(len + 1);
memcpy(data->path, env, len); memcpy(environ_path, env, len);
data->path[len] = '\0'; environ_path[len] = '\0';
/* pid */ /* pid */
len = pid_idx - path_pid - 1; len = pid_idx - path_pid - 1;
@@ -171,17 +160,17 @@ parse_env(struct env_data *data)
ll = strtonum(buf, 0, LONG_MAX, &errstr); ll = strtonum(buf, 0, LONG_MAX, &errstr);
if (errstr != NULL) if (errstr != NULL)
return; return;
data->pid = ll; environ_pid = ll;
/* idx */ /* idx */
ll = strtonum(pid_idx+1, 0, UINT_MAX, &errstr); ll = strtonum(pid_idx + 1, 0, UINT_MAX, &errstr);
if (errstr != NULL) if (errstr != NULL)
return; return;
data->idx = ll; environ_idx = ll;
} }
char * char *
makesockpath(const char *label) makesocketpath(const char *label)
{ {
char base[MAXPATHLEN], *path; char base[MAXPATHLEN], *path;
struct stat sb; struct stat sb;
@@ -213,6 +202,7 @@ shell_exec(const char *shell, const char *shellcmd)
{ {
const char *shellname, *ptr; const char *shellname, *ptr;
char *argv0; char *argv0;
int mode;
ptr = strrchr(shell, '/'); ptr = strrchr(shell, '/');
if (ptr != NULL && *(ptr + 1) != '\0') if (ptr != NULL && *(ptr + 1) != '\0')
@@ -225,6 +215,14 @@ shell_exec(const char *shell, const char *shellcmd)
xasprintf(&argv0, "%s", shellname); xasprintf(&argv0, "%s", shellname);
setenv("SHELL", shell, 1); setenv("SHELL", shell, 1);
if ((mode = fcntl(STDIN_FILENO, F_GETFL)) != -1)
fcntl(STDIN_FILENO, F_SETFL, mode & ~O_NONBLOCK);
if ((mode = fcntl(STDOUT_FILENO, F_GETFL)) != -1)
fcntl(STDOUT_FILENO, F_SETFL, mode & ~O_NONBLOCK);
if ((mode = fcntl(STDERR_FILENO, F_GETFL)) != -1)
fcntl(STDERR_FILENO, F_SETFL, mode & ~O_NONBLOCK);
closefrom(STDERR_FILENO + 1);
execl(shell, argv0, "-c", shellcmd, (char *) NULL); execl(shell, argv0, "-c", shellcmd, (char *) NULL);
fatal("execl failed"); fatal("execl failed");
} }
@@ -232,30 +230,20 @@ shell_exec(const char *shell, const char *shellcmd)
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
struct cmd_list *cmdlist; struct passwd *pw;
struct cmd *cmd; struct options *oo, *so, *wo;
enum msgtype msg; struct keylist *keylist;
struct passwd *pw; char *s, *path, *label, *home, **var;
struct options *oo, *so, *wo; int opt, flags, quiet, keys;
struct keylist *keylist;
struct env_data envdata;
struct msg_command_data cmddata;
char *s, *shellcmd, *path, *label, *home, *cause;
char **var;
void *buf;
size_t len;
int opt, flags, quiet = 0, cmdflags = 0;
short events;
#if defined(DEBUG) && defined(__OpenBSD__) #if defined(DEBUG) && defined(__OpenBSD__)
malloc_options = (char *) "AFGJPX"; malloc_options = (char *) "AFGJPX";
#endif #endif
flags = 0; quiet = flags = 0;
shellcmd = label = path = NULL; label = path = NULL;
envdata.path = NULL;
login_shell = (**argv == '-'); login_shell = (**argv == '-');
while ((opt = getopt(argc, argv, "28c:df:lL:qS:uUv")) != -1) { while ((opt = getopt(argc, argv, "28c:df:lL:qS:uUvV")) != -1) {
switch (opt) { switch (opt) {
case '2': case '2':
flags |= IDENTIFY_256COLOURS; flags |= IDENTIFY_256COLOURS;
@@ -266,10 +254,13 @@ main(int argc, char **argv)
flags &= ~IDENTIFY_256COLOURS; flags &= ~IDENTIFY_256COLOURS;
break; break;
case 'c': case 'c':
if (shellcmd != NULL) if (shell_cmd != NULL)
xfree(shellcmd); xfree(shell_cmd);
shellcmd = xstrdup(optarg); shell_cmd = xstrdup(optarg);
break; break;
case 'V':
printf("%s %s\n", __progname, BUILD);
exit(0);
case 'f': case 'f':
if (cfg_file != NULL) if (cfg_file != NULL)
xfree(cfg_file); xfree(cfg_file);
@@ -304,7 +295,7 @@ main(int argc, char **argv)
argc -= optind; argc -= optind;
argv += optind; argv += optind;
if (shellcmd != NULL && argc != 0) if (shell_cmd != NULL && argc != 0)
usage(); usage();
log_open_tty(debug_level); log_open_tty(debug_level);
@@ -334,6 +325,7 @@ main(int argc, char **argv)
oo = &global_options; oo = &global_options;
options_set_number(oo, "quiet", quiet); options_set_number(oo, "quiet", quiet);
options_set_number(oo, "escape-time", 500); options_set_number(oo, "escape-time", 500);
options_set_number(oo, "exit-unattached", 0);
options_init(&global_s_options, NULL); options_init(&global_s_options, NULL);
so = &global_s_options; so = &global_s_options;
@@ -344,6 +336,7 @@ main(int argc, char **argv)
options_set_string(so, "default-path", "%s", ""); options_set_string(so, "default-path", "%s", "");
options_set_string(so, "default-shell", "%s", getshell()); options_set_string(so, "default-shell", "%s", getshell());
options_set_string(so, "default-terminal", "screen"); options_set_string(so, "default-terminal", "screen");
options_set_number(so, "destroy-unattached", 0);
options_set_number(so, "detach-on-destroy", 1); options_set_number(so, "detach-on-destroy", 1);
options_set_number(so, "display-panes-active-colour", 1); options_set_number(so, "display-panes-active-colour", 1);
options_set_number(so, "display-panes-colour", 4); options_set_number(so, "display-panes-colour", 4);
@@ -372,7 +365,6 @@ main(int argc, char **argv)
options_set_number(so, "status-fg", 0); options_set_number(so, "status-fg", 0);
options_set_number(so, "status-interval", 15); options_set_number(so, "status-interval", 15);
options_set_number(so, "status-justify", 0); options_set_number(so, "status-justify", 0);
options_set_number(so, "status-keys", MODEKEY_EMACS);
options_set_string(so, "status-left", "[#S]"); options_set_string(so, "status-left", "[#S]");
options_set_number(so, "status-left-attr", 0); options_set_number(so, "status-left-attr", 0);
options_set_number(so, "status-left-bg", 8); options_set_number(so, "status-left-bg", 8);
@@ -385,11 +377,15 @@ main(int argc, char **argv)
options_set_number(so, "status-right-length", 40); options_set_number(so, "status-right-length", 40);
options_set_string(so, "terminal-overrides", options_set_string(so, "terminal-overrides",
"*88col*:colors=88,*256col*:colors=256"); "*88col*:colors=88,*256col*:colors=256");
options_set_string(so, "update-environment", "DISPLAY " options_set_string(so, "update-environment",
"WINDOWID SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID SSH_CONNECTION"); "DISPLAY "
"SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID SSH_CONNECTION "
"WINDOWID "
"XAUTHORITY");
options_set_number(so, "visual-activity", 0); options_set_number(so, "visual-activity", 0);
options_set_number(so, "visual-bell", 0); options_set_number(so, "visual-bell", 0);
options_set_number(so, "visual-content", 0); options_set_number(so, "visual-content", 0);
options_set_number(so, "visual-silence", 0);
keylist = xmalloc(sizeof *keylist); keylist = xmalloc(sizeof *keylist);
ARRAY_INIT(keylist); ARRAY_INIT(keylist);
@@ -406,14 +402,16 @@ main(int argc, char **argv)
options_set_number(wo, "force-height", 0); options_set_number(wo, "force-height", 0);
options_set_number(wo, "force-width", 0); options_set_number(wo, "force-width", 0);
options_set_number(wo, "main-pane-height", 24); options_set_number(wo, "main-pane-height", 24);
options_set_number(wo, "main-pane-width", 81); options_set_number(wo, "main-pane-width", 80);
options_set_number(wo, "mode-attr", 0); options_set_number(wo, "mode-attr", 0);
options_set_number(wo, "mode-bg", 3); options_set_number(wo, "mode-bg", 3);
options_set_number(wo, "mode-fg", 0); options_set_number(wo, "mode-fg", 0);
options_set_number(wo, "mode-keys", MODEKEY_EMACS);
options_set_number(wo, "mode-mouse", 0); options_set_number(wo, "mode-mouse", 0);
options_set_number(wo, "monitor-activity", 0); options_set_number(wo, "monitor-activity", 0);
options_set_string(wo, "monitor-content", "%s", ""); options_set_string(wo, "monitor-content", "%s", "");
options_set_number(wo, "monitor-silence", 0);
options_set_number(wo, "other-pane-height", 0);
options_set_number(wo, "other-pane-width", 0);
options_set_number(wo, "window-status-attr", 0); options_set_number(wo, "window-status-attr", 0);
options_set_number(wo, "window-status-bg", 8); options_set_number(wo, "window-status-bg", 8);
options_set_number(wo, "window-status-current-attr", 0); options_set_number(wo, "window-status-current-attr", 0);
@@ -438,6 +436,17 @@ main(int argc, char **argv)
options_set_number(wo, "utf8", 0); options_set_number(wo, "utf8", 0);
} }
keys = MODEKEY_EMACS;
if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) {
if (strrchr(s, '/') != NULL)
s = strrchr(s, '/') + 1;
if (strstr(s, "vi") != NULL)
keys = MODEKEY_VI;
}
options_set_number(so, "status-keys", keys);
options_set_number(wo, "mode-keys", keys);
/* Locate the configuration file. */
if (cfg_file == NULL) { if (cfg_file == NULL) {
home = getenv("HOME"); home = getenv("HOME");
if (home == NULL || *home == '\0') { if (home == NULL || *home == '\0') {
@@ -453,21 +462,22 @@ main(int argc, char **argv)
} }
/* /*
* Figure out the socket path. If specified on the command-line with * Figure out the socket path. If specified on the command-line with -S
* -S or -L, use it, otherwise try $TMUX or assume -L default. * or -L, use it, otherwise try $TMUX or assume -L default.
*/ */
parse_env(&envdata); parseenvironment();
if (path == NULL) { if (path == NULL) {
/* No -L. Try $TMUX, or default. */ /* If no -L, use the environment. */
if (label == NULL) { if (label == NULL) {
path = envdata.path; if (environ_path != NULL)
if (path == NULL) path = xstrdup(environ_path);
else
label = xstrdup("default"); label = xstrdup("default");
} }
/* -L or default set. */ /* -L or default set. */
if (label != NULL) { if (label != NULL) {
if ((path = makesockpath(label)) == NULL) { if ((path = makesocketpath(label)) == NULL) {
log_warn("can't create socket"); log_warn("can't create socket");
exit(1); exit(1);
} }
@@ -475,66 +485,16 @@ main(int argc, char **argv)
} }
if (label != NULL) if (label != NULL)
xfree(label); xfree(label);
if (realpath(path, socket_path) == NULL)
strlcpy(socket_path, path, sizeof socket_path);
xfree(path);
if (shellcmd != NULL) { #ifdef HAVE_SETPROCTITLE
msg = MSG_SHELL; /* Set process title. */
buf = NULL; setproctitle("%s (%s)", __progname, socket_path);
len = 0; #endif
} else {
cmddata.pid = envdata.pid;
cmddata.idx = envdata.idx;
/* Prepare command for server. */
cmddata.argc = argc;
if (cmd_pack_argv(
argc, argv, cmddata.argv, sizeof cmddata.argv) != 0) {
log_warnx("command too long");
exit(1);
}
msg = MSG_COMMAND;
buf = &cmddata;
len = sizeof cmddata;
}
if (shellcmd != NULL)
cmdflags |= CMD_STARTSERVER;
else if (argc == 0) /* new-session is the default */
cmdflags |= CMD_STARTSERVER|CMD_SENDENVIRON|CMD_CANTNEST;
else {
/*
* It sucks parsing the command string twice (in client and
* later in server) but it is necessary to get the start server
* flag.
*/
if ((cmdlist = cmd_list_parse(argc, argv, &cause)) == NULL) {
log_warnx("%s", cause);
exit(1);
}
cmdflags &= ~CMD_STARTSERVER;
TAILQ_FOREACH(cmd, &cmdlist->list, qentry) {
if (cmd->entry->flags & CMD_STARTSERVER)
cmdflags |= CMD_STARTSERVER;
if (cmd->entry->flags & CMD_SENDENVIRON)
cmdflags |= CMD_SENDENVIRON;
if (cmd->entry->flags & CMD_CANTNEST)
cmdflags |= CMD_CANTNEST;
}
cmd_list_free(cmdlist);
}
/*
* Check if this could be a nested session, if the command can't nest:
* if the socket path matches $TMUX, this is probably the same server.
*/
if (shellcmd == NULL && envdata.path != NULL &&
cmdflags & CMD_CANTNEST &&
(path == envdata.path || strcmp(path, envdata.path) == 0)) {
log_warnx("sessions should be nested with care. "
"unset $TMUX to force.");
exit(1);
}
/* Pass control to the client. */
#ifdef HAVE_BROKEN_KQUEUE #ifdef HAVE_BROKEN_KQUEUE
if (setenv("EVENT_NOKQUEUE", "1", 1) != 0) if (setenv("EVENT_NOKQUEUE", "1", 1) != 0)
fatal("setenv failed"); fatal("setenv failed");
@@ -550,116 +510,5 @@ main(int argc, char **argv)
#ifdef HAVE_BROKEN_POLL #ifdef HAVE_BROKEN_POLL
unsetenv("EVENT_NOPOLL"); unsetenv("EVENT_NOPOLL");
#endif #endif
set_signals(main_signal); exit(client_main(argc, argv, flags));
/* Initialise the client socket/start the server. */
if ((main_ibuf = client_init(path, cmdflags, flags)) == NULL)
exit(1);
xfree(path);
imsg_compose(main_ibuf, msg, PROTOCOL_VERSION, -1, -1, buf, len);
events = EV_READ;
if (main_ibuf->w.queued > 0)
events |= EV_WRITE;
event_once(main_ibuf->fd, events, main_callback, shellcmd, NULL);
event_dispatch();
clear_signals();
client_main(); /* doesn't return */
}
/* ARGSUSED */
void
main_signal(int sig, unused short events, unused void *data)
{
int status;
switch (sig) {
case SIGTERM:
exit(1);
case SIGCHLD:
waitpid(WAIT_ANY, &status, WNOHANG);
break;
}
}
/* ARGSUSED */
void
main_callback(unused int fd, short events, void *data)
{
char *shellcmd = data;
if (events & EV_READ)
main_dispatch(shellcmd);
if (events & EV_WRITE) {
if (msgbuf_write(&main_ibuf->w) < 0)
fatalx("msgbuf_write failed");
}
events = EV_READ;
if (main_ibuf->w.queued > 0)
events |= EV_WRITE;
event_once(main_ibuf->fd, events, main_callback, shellcmd, NULL);
}
void
main_dispatch(const char *shellcmd)
{
struct imsg imsg;
ssize_t n, datalen;
struct msg_shell_data shelldata;
struct msg_exit_data exitdata;
if ((n = imsg_read(main_ibuf)) == -1 || n == 0)
fatalx("imsg_read failed");
for (;;) {
if ((n = imsg_get(main_ibuf, &imsg)) == -1)
fatalx("imsg_get failed");
if (n == 0)
return;
datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
switch (imsg.hdr.type) {
case MSG_EXIT:
case MSG_SHUTDOWN:
if (datalen != sizeof exitdata) {
if (datalen != 0)
fatalx("bad MSG_EXIT size");
exit(0);
}
memcpy(&exitdata, imsg.data, sizeof exitdata);
exit(exitdata.retcode);
case MSG_READY:
if (datalen != 0)
fatalx("bad MSG_READY size");
event_loopexit(NULL); /* move to client_main() */
break;
case MSG_VERSION:
if (datalen != 0)
fatalx("bad MSG_VERSION size");
log_warnx("protocol version mismatch (client %u, "
"server %u)", PROTOCOL_VERSION, imsg.hdr.peerid);
exit(1);
case MSG_SHELL:
if (datalen != sizeof shelldata)
fatalx("bad MSG_SHELL size");
memcpy(&shelldata, imsg.data, sizeof shelldata);
shelldata.shell[(sizeof shelldata.shell) - 1] = '\0';
clear_signals();
shell_exec(shelldata.shell, shellcmd);
default:
fatalx("unexpected message");
}
imsg_free(&imsg);
}
} }

89
tmux.h
View File

@@ -1,4 +1,4 @@
/* $Id: tmux.h,v 1.571 2010-07-17 14:38:13 tcunha Exp $ */ /* $Id: tmux.h,v 1.591 2010-12-22 15:36:44 tcunha Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -31,7 +31,6 @@
#include <limits.h> #include <limits.h>
#include <signal.h> #include <signal.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <termios.h> #include <termios.h>
@@ -58,8 +57,8 @@ extern char **environ;
/* Automatic name refresh interval, in milliseconds. */ /* Automatic name refresh interval, in milliseconds. */
#define NAME_INTERVAL 500 #define NAME_INTERVAL 500
/* Maximum data to buffer for output before suspending reading from panes. */ /* Maximum data to buffer for output before suspending writing to a tty. */
#define BACKOFF_THRESHOLD 1024 #define BACKOFF_THRESHOLD 16384
/* /*
* Maximum sizes of strings in message data. Don't forget to bump * Maximum sizes of strings in message data. Don't forget to bump
@@ -829,8 +828,10 @@ TAILQ_HEAD(window_panes, window_pane);
struct window { struct window {
char *name; char *name;
struct event name_timer; struct event name_timer;
struct timeval silence_timer;
struct window_pane *active; struct window_pane *active;
struct window_pane *last;
struct window_panes panes; struct window_panes panes;
int lastlayout; int lastlayout;
@@ -841,9 +842,9 @@ struct window {
int flags; int flags;
#define WINDOW_BELL 0x1 #define WINDOW_BELL 0x1
#define WINDOW_HIDDEN 0x2 #define WINDOW_ACTIVITY 0x2
#define WINDOW_ACTIVITY 0x4 #define WINDOW_REDRAW 0x4
#define WINDOW_REDRAW 0x8 #define WINDOW_SILENCE 0x8
struct options options; struct options options;
@@ -864,7 +865,9 @@ struct winlink {
#define WINLINK_BELL 0x1 #define WINLINK_BELL 0x1
#define WINLINK_ACTIVITY 0x2 #define WINLINK_ACTIVITY 0x2
#define WINLINK_CONTENT 0x4 #define WINLINK_CONTENT 0x4
#define WINLINK_ALERTFLAGS (WINLINK_BELL|WINLINK_ACTIVITY|WINLINK_CONTENT) #define WINLINK_SILENCE 0x8
#define WINLINK_ALERTFLAGS \
(WINLINK_BELL|WINLINK_ACTIVITY|WINLINK_CONTENT|WINLINK_SILENCE)
RB_ENTRY(winlink) entry; RB_ENTRY(winlink) entry;
TAILQ_ENTRY(winlink) sentry; TAILQ_ENTRY(winlink) sentry;
@@ -925,6 +928,8 @@ struct session_group {
TAILQ_HEAD(session_groups, session_group); TAILQ_HEAD(session_groups, session_group);
struct session { struct session {
u_int idx;
char *name; char *name;
char *cwd; char *cwd;
@@ -943,7 +948,6 @@ struct session {
struct paste_stack buffers; struct paste_stack buffers;
#define SESSION_UNATTACHED 0x1 /* not attached to any clients */ #define SESSION_UNATTACHED 0x1 /* not attached to any clients */
#define SESSION_DEAD 0x2
int flags; int flags;
struct termios *tio; struct termios *tio;
@@ -953,8 +957,10 @@ struct session {
int references; int references;
TAILQ_ENTRY(session) gentry; TAILQ_ENTRY(session) gentry;
RB_ENTRY(session) entry;
}; };
ARRAY_DECL(sessions, struct session *); RB_HEAD(sessions, session);
ARRAY_DECL(sessionslist, struct session *);
/* TTY information. */ /* TTY information. */
struct tty_key { struct tty_key {
@@ -971,6 +977,8 @@ struct tty_term {
char *name; char *name;
u_int references; u_int references;
char acs[UCHAR_MAX + 1][2];
struct tty_code codes[NTTYCODE]; struct tty_code codes[NTTYCODE];
#define TERM_256COLOURS 0x1 #define TERM_256COLOURS 0x1
@@ -1008,14 +1016,13 @@ struct tty {
struct grid_cell cell; struct grid_cell cell;
u_char acs[UCHAR_MAX + 1];
#define TTY_NOCURSOR 0x1 #define TTY_NOCURSOR 0x1
#define TTY_FREEZE 0x2 #define TTY_FREEZE 0x2
#define TTY_ESCAPE 0x4 #define TTY_ESCAPE 0x4
#define TTY_UTF8 0x8 #define TTY_UTF8 0x8
#define TTY_STARTED 0x10 #define TTY_STARTED 0x10
#define TTY_OPENED 0x20 #define TTY_OPENED 0x20
#define TTY_BACKOFF 0x40
int flags; int flags;
int term_flags; int term_flags;
@@ -1095,9 +1102,17 @@ struct client {
char *cwd; char *cwd;
struct tty tty; struct tty tty;
FILE *stdin_file;
FILE *stdout_file; int stdin_fd;
FILE *stderr_file; void *stdin_data;
void (*stdin_callback)(struct client *, void *);
struct bufferevent *stdin_event;
int stdout_fd;
struct bufferevent *stdout_event;
int stderr_fd;
struct bufferevent *stderr_event;
struct event repeat_timer; struct event repeat_timer;
@@ -1107,7 +1122,7 @@ struct client {
#define CLIENT_TERMINAL 0x1 #define CLIENT_TERMINAL 0x1
#define CLIENT_PREFIX 0x2 #define CLIENT_PREFIX 0x2
/* 0x4 unused */ #define CLIENT_EXIT 0x4
#define CLIENT_REDRAW 0x8 #define CLIENT_REDRAW 0x8
#define CLIENT_STATUS 0x10 #define CLIENT_STATUS 0x10
#define CLIENT_REPEAT 0x20 /* allow command to repeat within repeat time */ #define CLIENT_REPEAT 0x20 /* allow command to repeat within repeat time */
@@ -1117,6 +1132,8 @@ struct client {
#define CLIENT_DEAD 0x200 #define CLIENT_DEAD 0x200
#define CLIENT_BORDERS 0x400 #define CLIENT_BORDERS 0x400
#define CLIENT_READONLY 0x800 #define CLIENT_READONLY 0x800
#define CLIENT_BACKOFF 0x1000
#define CLIENT_REDRAWWINDOW 0x2000
int flags; int flags;
struct event identify_timer; struct event identify_timer;
@@ -1131,16 +1148,15 @@ struct client {
int (*prompt_callbackfn)(void *, const char *); int (*prompt_callbackfn)(void *, const char *);
void (*prompt_freefn)(void *); void (*prompt_freefn)(void *);
void *prompt_data; void *prompt_data;
u_int prompt_hindex;
#define PROMPT_SINGLE 0x1 #define PROMPT_SINGLE 0x1
int prompt_flags; int prompt_flags;
u_int prompt_hindex;
ARRAY_DECL(, char *) prompt_hdata;
struct mode_key_data prompt_mdata; struct mode_key_data prompt_mdata;
struct session *session; struct session *session;
struct session *last_session;
int references; int references;
}; };
@@ -1279,19 +1295,23 @@ extern struct options global_w_options;
extern struct environ global_environ; extern struct environ global_environ;
extern struct event_base *ev_base; extern struct event_base *ev_base;
extern char *cfg_file; extern char *cfg_file;
extern char *shell_cmd;
extern int debug_level; extern int debug_level;
extern int be_quiet;
extern time_t start_time; extern time_t start_time;
extern char *socket_path; extern char socket_path[MAXPATHLEN];
extern int login_shell; extern int login_shell;
extern char *environ_path;
extern pid_t environ_pid;
extern u_int environ_idx;
void logfile(const char *); void logfile(const char *);
const char *getshell(void); const char *getshell(void);
int checkshell(const char *); int checkshell(const char *);
int areshell(const char *); int areshell(const char *);
__dead void shell_exec(const char *, const char *);
/* cfg.c */ /* cfg.c */
extern int cfg_finished; extern int cfg_finished;
struct causelist cfg_causes; extern struct causelist cfg_causes;
void printflike2 cfg_add_cause(struct causelist *, const char *, ...); void printflike2 cfg_add_cause(struct causelist *, const char *, ...);
int load_cfg(const char *, struct cmd_ctx *, struct causelist *); int load_cfg(const char *, struct cmd_ctx *, struct causelist *);
@@ -1360,7 +1380,6 @@ void environ_push(struct environ *);
/* tty.c */ /* tty.c */
void tty_raw(struct tty *, const char *); void tty_raw(struct tty *, const char *);
u_char tty_get_acs(struct tty *, u_char);
void tty_attributes(struct tty *, const struct grid_cell *); void tty_attributes(struct tty *, const struct grid_cell *);
void tty_reset(struct tty *); void tty_reset(struct tty *);
void tty_region_pane(struct tty *, const struct tty_ctx *, u_int, u_int); void tty_region_pane(struct tty *, const struct tty_ctx *, u_int, u_int);
@@ -1414,6 +1433,9 @@ const char *tty_term_string2(
int tty_term_number(struct tty_term *, enum tty_code_code); int tty_term_number(struct tty_term *, enum tty_code_code);
int tty_term_flag(struct tty_term *, enum tty_code_code); int tty_term_flag(struct tty_term *, enum tty_code_code);
/* tty-acs.c */
const char *tty_acs_get(struct tty *, u_char);
/* tty-keys.c */ /* tty-keys.c */
void tty_keys_init(struct tty *); void tty_keys_init(struct tty *);
void tty_keys_free(struct tty *); void tty_keys_free(struct tty *);
@@ -1445,6 +1467,7 @@ const char *cmd_set_option_print(
/* cmd.c */ /* cmd.c */
int cmd_pack_argv(int, char **, char *, size_t); int cmd_pack_argv(int, char **, char *, size_t);
int cmd_unpack_argv(char *, size_t, int, char ***); int cmd_unpack_argv(char *, size_t, int, char ***);
char **cmd_copy_argv(int, char **);
void cmd_free_argv(int, char **); void cmd_free_argv(int, char **);
struct cmd *cmd_parse(int, char **, char **); struct cmd *cmd_parse(int, char **, char **);
int cmd_exec(struct cmd *, struct cmd_ctx *); int cmd_exec(struct cmd *, struct cmd_ctx *);
@@ -1489,6 +1512,7 @@ extern const struct cmd_entry cmd_kill_pane_entry;
extern const struct cmd_entry cmd_kill_server_entry; extern const struct cmd_entry cmd_kill_server_entry;
extern const struct cmd_entry cmd_kill_session_entry; extern const struct cmd_entry cmd_kill_session_entry;
extern const struct cmd_entry cmd_kill_window_entry; extern const struct cmd_entry cmd_kill_window_entry;
extern const struct cmd_entry cmd_last_pane_entry;
extern const struct cmd_entry cmd_last_window_entry; extern const struct cmd_entry cmd_last_window_entry;
extern const struct cmd_entry cmd_link_window_entry; extern const struct cmd_entry cmd_link_window_entry;
extern const struct cmd_entry cmd_list_buffers_entry; extern const struct cmd_entry cmd_list_buffers_entry;
@@ -1584,8 +1608,7 @@ void cmd_buffer_free(struct cmd *);
size_t cmd_buffer_print(struct cmd *, char *, size_t); size_t cmd_buffer_print(struct cmd *, char *, size_t);
/* client.c */ /* client.c */
struct imsgbuf *client_init(char *, int, int); int client_main(int, char **, int);
__dead void client_main(void);
/* key-bindings.c */ /* key-bindings.c */
extern struct key_bindings key_bindings; extern struct key_bindings key_bindings;
@@ -1608,7 +1631,7 @@ const char *key_string_lookup_key(int);
/* server.c */ /* server.c */
extern struct clients clients; extern struct clients clients;
extern struct clients dead_clients; extern struct clients dead_clients;
int server_start(char *); int server_start(void);
void server_update_socket(void); void server_update_socket(void);
/* server-client.c */ /* server-client.c */
@@ -1647,6 +1670,7 @@ void server_unlink_window(struct session *, struct winlink *);
void server_destroy_pane(struct window_pane *); void server_destroy_pane(struct window_pane *);
void server_destroy_session_group(struct session *); void server_destroy_session_group(struct session *);
void server_destroy_session(struct session *); void server_destroy_session(struct session *);
void server_check_unattached (void);
void server_set_identify(struct client *); void server_set_identify(struct client *);
void server_clear_identify(struct client *); void server_clear_identify(struct client *);
void server_update_event(struct client *); void server_update_event(struct client *);
@@ -1812,9 +1836,7 @@ int screen_check_selection(struct screen *, u_int, u_int);
/* window.c */ /* window.c */
extern struct windows windows; extern struct windows windows;
int window_cmp(struct window *, struct window *);
int winlink_cmp(struct winlink *, struct winlink *); int winlink_cmp(struct winlink *, struct winlink *);
RB_PROTOTYPE(windows, window, entry, window_cmp);
RB_PROTOTYPE(winlinks, winlink, entry, winlink_cmp); RB_PROTOTYPE(winlinks, winlink, entry, winlink_cmp);
struct winlink *winlink_find_by_index(struct winlinks *, int); struct winlink *winlink_find_by_index(struct winlinks *, int);
struct winlink *winlink_find_by_window(struct winlinks *, struct window *); struct winlink *winlink_find_by_window(struct winlinks *, struct window *);
@@ -1940,19 +1962,24 @@ void queue_window_name(struct window *);
char *default_window_name(struct window *); char *default_window_name(struct window *);
/* signal.c */ /* signal.c */
void set_signals(void(*handler)(int, short, unused void *)); void set_signals(void(*)(int, short, void *));
void clear_signals(void); void clear_signals(int);
/* session.c */ /* session.c */
extern struct sessions sessions; extern struct sessions sessions;
extern struct sessions dead_sessions; extern struct sessions dead_sessions;
extern struct session_groups session_groups; extern struct session_groups session_groups;
int session_cmp(struct session *, struct session *);
RB_PROTOTYPE(sessions, session, entry, session_cmp);
int session_alive(struct session *);
struct session *session_find(const char *); struct session *session_find(const char *);
struct session *session_find_by_index(u_int);
struct session *session_create(const char *, const char *, const char *, struct session *session_create(const char *, const char *, const char *,
struct environ *, struct termios *, int, u_int, u_int, struct environ *, struct termios *, int, u_int, u_int,
char **); char **);
void session_destroy(struct session *); void session_destroy(struct session *);
int session_index(struct session *, u_int *); struct session *session_next_session(struct session *);
struct session *session_previous_session(struct session *);
struct winlink *session_new(struct session *, struct winlink *session_new(struct session *,
const char *, const char *, const char *, int, char **); const char *, const char *, const char *, int, char **);
struct winlink *session_attach( struct winlink *session_attach(

5
tools/check-compat.sh Normal file
View File

@@ -0,0 +1,5 @@
# $Id: check-compat.sh,v 1.1 2010-10-24 00:42:04 tcunha Exp $
grep "#include" compat.h|while read line; do
grep "$line" *.[ch] compat/*.[ch]
done

View File

@@ -1,6 +1,6 @@
# $Id: dist.mk,v 1.9 2010-07-18 13:36:52 tcunha Exp $ # $Id: dist.mk,v 1.11 2010-12-27 21:32:16 tcunha Exp $
VERSION= 1.3 VERSION= 1.4
DISTDIR= tmux-${VERSION} DISTDIR= tmux-${VERSION}
DISTFILES= *.[ch] Makefile GNUmakefile configure tmux.1 \ DISTFILES= *.[ch] Makefile GNUmakefile configure tmux.1 \
@@ -20,7 +20,7 @@ dist:
upload-index.html: update-index.html upload-index.html: update-index.html
scp www/index.html www/main.css www/images/*.png \ scp www/index.html www/main.css www/images/*.png \
nicm,tmux@web.sf.net:/home/groups/t/tm/tmux/htdocs ${USER},tmux@web.sf.net:/home/groups/t/tm/tmux/htdocs
rm -f www/index.html www/images/small-* rm -f www/index.html www/images/small-*
update-index.html: update-index.html:

31
tools/fuzz.c Normal file
View File

@@ -0,0 +1,31 @@
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
int
main(void)
{
time_t t;
int i;
setvbuf(stdout, NULL, _IONBF, 0);
t = time(NULL);
srandom((u_int) t);
for (;;) {
putchar('\033');
for (i = 0; i < random() % 25; i++) {
if (i > 22)
putchar(';');
else
putchar(random() % 256);
}
/* usleep(100); */
}
}

97
tty-acs.c Normal file
View File

@@ -0,0 +1,97 @@
/* $Id: tty-acs.c,v 1.1 2010-09-18 15:43:53 tcunha Exp $ */
/*
* Copyright (c) 2010 Nicholas Marriott <nicm@users.sourceforge.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF 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 "tmux.h"
int tty_acs_cmp(const void *, const void *);
/* Table mapping ACS entries to UTF-8. */
struct tty_acs_entry {
u_char key;
const char *string;
};
const struct tty_acs_entry tty_acs_table[] = {
{ '+', "\342\206\222" },
{ ',', "\342\206\220" },
{ '-', "\342\206\221" },
{ '.', "\342\206\223" },
{ '0', "\342\226\256" },
{ '`', "\342\227\206" },
{ 'a', "\342\226\222" },
{ 'f', "\302\260" },
{ 'g', "\302\261" },
{ 'h', "\342\226\222" },
{ 'i', "\342\230\203" },
{ 'j', "\342\224\230" },
{ 'k', "\342\224\220" },
{ 'l', "\342\224\214" },
{ 'm', "\342\224\224" },
{ 'n', "\342\224\274" },
{ 'o', "\342\216\272" },
{ 'p', "\342\216\273" },
{ 'q', "\342\224\200" },
{ 'r', "\342\216\274" },
{ 's', "\342\216\275" },
{ 't', "\342\224\234" },
{ 'u', "\342\224\244" },
{ 'v', "\342\224\264" },
{ 'w', "\342\224\254" },
{ 'x', "\342\224\202" },
{ 'y', "\342\211\244" },
{ 'z', "\342\211\245" },
{ '{', "\317\200" },
{ '|', "\342\211\240" },
{ '}', "\302\243" },
{ '~', "\302\267" }
};
int
tty_acs_cmp(const void *key, const void *value)
{
const struct tty_acs_entry *entry = value;
u_char ch;
ch = *(u_char *) key;
return (ch - entry->key);
}
/* Retrieve ACS to output as a string. */
const char *
tty_acs_get(struct tty *tty, u_char ch)
{
struct tty_acs_entry *entry;
/* If not a UTF-8 terminal, use the ACS set. */
if (!(tty->flags & TTY_UTF8)) {
if (tty->term->acs[ch][0] == '\0')
return (NULL);
return (&tty->term->acs[ch][0]);
}
/* Otherwise look up the UTF-8 translation. */
entry = bsearch(&ch,
tty_acs_table, nitems(tty_acs_table), sizeof tty_acs_table[0],
tty_acs_cmp);
if (entry == NULL)
return (NULL);
return (entry->string);
}

View File

@@ -1,4 +1,4 @@
/* $Id: tty-term.c,v 1.41 2009-12-18 07:42:30 nicm Exp $ */ /* $Id: tty-term.c,v 1.43 2010-09-18 15:43:53 tcunha Exp $ */
/* /*
* Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -252,7 +252,8 @@ tty_term_override(struct tty_term *term, const char *overrides)
} else if (entstr[strlen(entstr) - 1] == '@') { } else if (entstr[strlen(entstr) - 1] == '@') {
entstr[strlen(entstr) - 1] = '\0'; entstr[strlen(entstr) - 1] = '\0';
removeflag = 1; removeflag = 1;
} } else
continue;
for (i = 0; i < NTTYCODE; i++) { for (i = 0; i < NTTYCODE; i++) {
ent = &tty_term_codes[i]; ent = &tty_term_codes[i];
@@ -304,6 +305,7 @@ tty_term_find(char *name, int fd, const char *overrides, char **cause)
u_int i; u_int i;
int n, error; int n, error;
char *s; char *s;
const char *acs;
SLIST_FOREACH(term, &tty_terms, entry) { SLIST_FOREACH(term, &tty_terms, entry) {
if (strcmp(term->name, name) == 0) { if (strcmp(term->name, name) == 0) {
@@ -317,7 +319,7 @@ tty_term_find(char *name, int fd, const char *overrides, char **cause)
term->name = xstrdup(name); term->name = xstrdup(name);
term->references = 1; term->references = 1;
term->flags = 0; term->flags = 0;
memset(&term->codes, 0, sizeof term->codes); memset(term->codes, 0, sizeof term->codes);
SLIST_INSERT_HEAD(&tty_terms, term, entry); SLIST_INSERT_HEAD(&tty_terms, term, entry);
/* Set up curses terminal. */ /* Set up curses terminal. */
@@ -415,6 +417,15 @@ tty_term_find(char *name, int fd, const char *overrides, char **cause)
if (!tty_term_flag(term, TTYC_XENL)) if (!tty_term_flag(term, TTYC_XENL))
term->flags |= TERM_EARLYWRAP; term->flags |= TERM_EARLYWRAP;
/* Generate ACS table. If none is present, use nearest ASCII. */
memset(term->acs, 0, sizeof term->acs);
if (tty_term_has(term, TTYC_ACSC))
acs = tty_term_string(term, TTYC_ACSC);
else
acs = "a#j+k+l+m+n+o-p-q-r-s-t+u+v+w+x|y<z>~.";
for (; acs[0] != '\0' && acs[1] != '\0'; acs += 2)
term->acs[(u_char) acs[0]][0] = acs[1];
return (term); return (term);
error: error:

66
tty.c
View File

@@ -1,4 +1,4 @@
/* $Id: tty.c,v 1.192 2010-06-06 00:30:34 tcunha Exp $ */ /* $Id: tty.c,v 1.197 2010-12-06 21:57:56 nicm Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -31,8 +31,6 @@
void tty_read_callback(struct bufferevent *, void *); void tty_read_callback(struct bufferevent *, void *);
void tty_error_callback(struct bufferevent *, short, void *); void tty_error_callback(struct bufferevent *, short, void *);
void tty_fill_acs(struct tty *);
int tty_try_256(struct tty *, u_char, const char *); int tty_try_256(struct tty *, u_char, const char *);
int tty_try_88(struct tty *, u_char, const char *); int tty_try_88(struct tty *, u_char, const char *);
@@ -48,6 +46,9 @@ void tty_emulate_repeat(
void tty_cell(struct tty *, void tty_cell(struct tty *,
const struct grid_cell *, const struct grid_utf8 *); const struct grid_cell *, const struct grid_utf8 *);
#define tty_use_acs(tty) \
(tty_term_has(tty->term, TTYC_ACSC) && !((tty)->flags & TTY_UTF8))
void void
tty_init(struct tty *tty, int fd, char *term) tty_init(struct tty *tty, int fd, char *term)
{ {
@@ -60,9 +61,6 @@ tty_init(struct tty *tty, int fd, char *term)
tty->termname = xstrdup("unknown"); tty->termname = xstrdup("unknown");
else else
tty->termname = xstrdup(term); tty->termname = xstrdup(term);
if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
fatal("fcntl failed");
tty->fd = fd; tty->fd = fd;
if ((path = ttyname(fd)) == NULL) if ((path = ttyname(fd)) == NULL)
@@ -143,8 +141,6 @@ tty_open(struct tty *tty, const char *overrides, char **cause)
tty_keys_init(tty); tty_keys_init(tty);
tty_fill_acs(tty);
return (0); return (0);
} }
@@ -201,7 +197,8 @@ tty_start_tty(struct tty *tty)
memcpy(&tty->cell, &grid_default_cell, sizeof tty->cell); memcpy(&tty->cell, &grid_default_cell, sizeof tty->cell);
tty_putcode(tty, TTYC_RMKX); tty_putcode(tty, TTYC_RMKX);
tty_putcode(tty, TTYC_ENACS); if (tty_use_acs(tty))
tty_putcode(tty, TTYC_ENACS);
tty_putcode(tty, TTYC_CLEAR); tty_putcode(tty, TTYC_CLEAR);
tty_putcode(tty, TTYC_CNORM); tty_putcode(tty, TTYC_CNORM);
@@ -242,7 +239,8 @@ tty_stop_tty(struct tty *tty)
return; return;
tty_raw(tty, tty_term_string2(tty->term, TTYC_CSR, 0, ws.ws_row - 1)); tty_raw(tty, tty_term_string2(tty->term, TTYC_CSR, 0, ws.ws_row - 1));
tty_raw(tty, tty_term_string(tty->term, TTYC_RMACS)); if (tty_use_acs(tty))
tty_raw(tty, tty_term_string(tty->term, TTYC_RMACS));
tty_raw(tty, tty_term_string(tty->term, TTYC_SGR0)); tty_raw(tty, tty_term_string(tty->term, TTYC_SGR0));
tty_raw(tty, tty_term_string(tty->term, TTYC_RMKX)); tty_raw(tty, tty_term_string(tty->term, TTYC_RMKX));
tty_raw(tty, tty_term_string(tty->term, TTYC_CLEAR)); tty_raw(tty, tty_term_string(tty->term, TTYC_CLEAR));
@@ -257,30 +255,6 @@ tty_stop_tty(struct tty *tty)
fcntl(tty->fd, F_SETFL, mode & ~O_NONBLOCK); fcntl(tty->fd, F_SETFL, mode & ~O_NONBLOCK);
} }
void
tty_fill_acs(struct tty *tty)
{
const char *ptr;
memset(tty->acs, 0, sizeof tty->acs);
if (!tty_term_has(tty->term, TTYC_ACSC))
return;
ptr = tty_term_string(tty->term, TTYC_ACSC);
if (strlen(ptr) % 2 != 0)
return;
for (; *ptr != '\0'; ptr += 2)
tty->acs[(u_char) ptr[0]] = ptr[1];
}
u_char
tty_get_acs(struct tty *tty, u_char ch)
{
if (tty->acs[ch] != '\0')
return (tty->acs[ch]);
return (ch);
}
void void
tty_close(struct tty *tty) tty_close(struct tty *tty)
{ {
@@ -360,11 +334,17 @@ tty_puts(struct tty *tty, const char *s)
void void
tty_putc(struct tty *tty, u_char ch) tty_putc(struct tty *tty, u_char ch)
{ {
u_int sx; const char *acs;
u_int sx;
if (tty->cell.attr & GRID_ATTR_CHARSET) if (tty->cell.attr & GRID_ATTR_CHARSET) {
ch = tty_get_acs(tty, ch); acs = tty_acs_get(tty, ch);
bufferevent_write(tty->event, &ch, 1); if (acs != NULL)
bufferevent_write(tty->event, acs, strlen(acs));
else
bufferevent_write(tty->event, &ch, 1);
} else
bufferevent_write(tty->event, &ch, 1);
if (ch >= 0x20 && ch != 0x7f) { if (ch >= 0x20 && ch != 0x7f) {
sx = tty->sx; sx = tty->sx;
@@ -567,7 +547,7 @@ tty_write(void (*cmdfn)(
if (wp->window->flags & WINDOW_REDRAW || wp->flags & PANE_REDRAW) if (wp->window->flags & WINDOW_REDRAW || wp->flags & PANE_REDRAW)
return; return;
if (wp->window->flags & WINDOW_HIDDEN || !window_pane_visible(wp)) if (!window_pane_visible(wp))
return; return;
for (i = 0; i < ARRAY_LENGTH(&clients); i++) { for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
@@ -578,7 +558,9 @@ tty_write(void (*cmdfn)(
continue; continue;
if (c->session->curw->window == wp->window) { if (c->session->curw->window == wp->window) {
if (c->tty.flags & TTY_FREEZE || c->tty.term == NULL) if (c->tty.term == NULL)
continue;
if (c->tty.flags & (TTY_FREEZE|TTY_BACKOFF))
continue; continue;
cmdfn(&c->tty, ctx); cmdfn(&c->tty, ctx);
} }
@@ -995,7 +977,7 @@ tty_reset(struct tty *tty)
if (memcmp(gc, &grid_default_cell, sizeof *gc) == 0) if (memcmp(gc, &grid_default_cell, sizeof *gc) == 0)
return; return;
if (tty_term_has(tty->term, TTYC_RMACS) && gc->attr & GRID_ATTR_CHARSET) if ((gc->attr & GRID_ATTR_CHARSET) && tty_use_acs(tty))
tty_putcode(tty, TTYC_RMACS); tty_putcode(tty, TTYC_RMACS);
tty_putcode(tty, TTYC_SGR0); tty_putcode(tty, TTYC_SGR0);
memcpy(gc, &grid_default_cell, sizeof *gc); memcpy(gc, &grid_default_cell, sizeof *gc);
@@ -1232,7 +1214,7 @@ tty_attributes(struct tty *tty, const struct grid_cell *gc)
} }
if (changed & GRID_ATTR_HIDDEN) if (changed & GRID_ATTR_HIDDEN)
tty_putcode(tty, TTYC_INVIS); tty_putcode(tty, TTYC_INVIS);
if (changed & GRID_ATTR_CHARSET) if ((changed & GRID_ATTR_CHARSET) && tty_use_acs(tty))
tty_putcode(tty, TTYC_SMACS); tty_putcode(tty, TTYC_SMACS);
} }

View File

@@ -1,4 +1,4 @@
/* $Id: window-copy.c,v 1.121 2010-07-02 02:56:07 tcunha Exp $ */ /* $Id: window-copy.c,v 1.125 2010-12-11 17:57:28 nicm Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -171,7 +171,8 @@ window_copy_init(struct window_pane *wp)
data->searchstr = NULL; data->searchstr = NULL;
wp->flags |= PANE_FREEZE; wp->flags |= PANE_FREEZE;
bufferevent_disable(wp->event, EV_READ|EV_WRITE); if (wp->fd != -1)
bufferevent_disable(wp->event, EV_READ|EV_WRITE);
data->jumptype = WINDOW_COPY_OFF; data->jumptype = WINDOW_COPY_OFF;
data->jumpchar = '\0'; data->jumpchar = '\0';
@@ -234,7 +235,8 @@ window_copy_free(struct window_pane *wp)
struct window_copy_mode_data *data = wp->modedata; struct window_copy_mode_data *data = wp->modedata;
wp->flags &= ~PANE_FREEZE; wp->flags &= ~PANE_FREEZE;
bufferevent_enable(wp->event, EV_READ|EV_WRITE); if (wp->fd != -1)
bufferevent_enable(wp->event, EV_READ|EV_WRITE);
if (data->searchstr != NULL) if (data->searchstr != NULL)
xfree(data->searchstr); xfree(data->searchstr);
@@ -340,6 +342,8 @@ window_copy_resize(struct window_pane *wp, u_int sx, u_int sy)
data->cy = sy - 1; data->cy = sy - 1;
if (data->cx > sx) if (data->cx > sx)
data->cx = sx; data->cx = sx;
if (data->oy > screen_hsize(data->backing))
data->oy = screen_hsize(data->backing);
window_copy_clear_selection(wp); window_copy_clear_selection(wp);
@@ -1053,6 +1057,8 @@ window_copy_write_line(
if (py == 0) { if (py == 0) {
size = xsnprintf(hdr, sizeof hdr, size = xsnprintf(hdr, sizeof hdr,
"[%u/%u]", data->oy, screen_hsize(data->backing)); "[%u/%u]", data->oy, screen_hsize(data->backing));
if (size > screen_size_x(s))
size = screen_size_x(s);
screen_write_cursormove(ctx, screen_size_x(s) - size, 0); screen_write_cursormove(ctx, screen_size_x(s) - size, 0);
screen_write_puts(ctx, &gc, "%s", hdr); screen_write_puts(ctx, &gc, "%s", hdr);
} else if (py == last && data->inputtype != WINDOW_COPY_OFF) { } else if (py == last && data->inputtype != WINDOW_COPY_OFF) {
@@ -1261,8 +1267,8 @@ window_copy_copy_selection(struct window_pane *wp, struct session *sess)
/* Cursor is on the left. */ /* Cursor is on the left. */
lastex = data->selx + 1; lastex = data->selx + 1;
restex = data->selx + 1; restex = data->selx + 1;
firstsx = data->cx + 1; firstsx = data->cx;
restsx = data->cx + 1; restsx = data->cx;
} }
} else { } else {
/* /*

View File

@@ -1,4 +1,4 @@
/* $Id: window.c,v 1.134 2010-07-17 14:38:13 tcunha Exp $ */ /* $Id: window.c,v 1.142 2010-12-06 22:52:21 nicm Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -24,7 +24,6 @@
#include <fnmatch.h> #include <fnmatch.h>
#include <pwd.h> #include <pwd.h>
#include <signal.h> #include <signal.h>
#include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <termios.h> #include <termios.h>
@@ -323,6 +322,9 @@ window_resize(struct window *w, u_int sx, u_int sy)
void void
window_set_active_pane(struct window *w, struct window_pane *wp) window_set_active_pane(struct window *w, struct window_pane *wp)
{ {
if (wp == w->active)
return;
w->last = w->active;
w->active = wp; w->active = wp;
while (!window_pane_visible(w->active)) { while (!window_pane_visible(w->active)) {
w->active = TAILQ_PREV(w->active, window_panes, entry); w->active = TAILQ_PREV(w->active, window_panes, entry);
@@ -339,7 +341,7 @@ window_set_active_at(struct window *w, u_int x, u_int y)
struct window_pane *wp; struct window_pane *wp;
TAILQ_FOREACH(wp, &w->panes, entry) { TAILQ_FOREACH(wp, &w->panes, entry) {
if (!window_pane_visible(wp)) if (wp == w->active || !window_pane_visible(wp))
continue; continue;
if (x < wp->xoff || x >= wp->xoff + wp->sx) if (x < wp->xoff || x >= wp->xoff + wp->sx)
continue; continue;
@@ -366,9 +368,16 @@ window_add_pane(struct window *w, u_int hlimit)
void void
window_remove_pane(struct window *w, struct window_pane *wp) window_remove_pane(struct window *w, struct window_pane *wp)
{ {
w->active = TAILQ_PREV(wp, window_panes, entry); if (wp == w->active) {
if (w->active == NULL) w->active = w->last;
w->active = TAILQ_NEXT(wp, entry); w->last = NULL;
if (w->active == NULL) {
w->active = TAILQ_PREV(wp, window_panes, entry);
if (w->active == NULL)
w->active = TAILQ_NEXT(wp, entry);
}
} else if (wp == w->last)
w->last = NULL;
TAILQ_REMOVE(&w->panes, wp, entry); TAILQ_REMOVE(&w->panes, wp, entry);
window_pane_destroy(wp); window_pane_destroy(wp);
@@ -493,6 +502,8 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
void void
window_pane_destroy(struct window_pane *wp) window_pane_destroy(struct window_pane *wp)
{ {
window_pane_reset_mode(wp);
if (wp->fd != -1) { if (wp->fd != -1) {
close(wp->fd); close(wp->fd);
bufferevent_free(wp->event); bufferevent_free(wp->event);
@@ -500,7 +511,6 @@ window_pane_destroy(struct window_pane *wp)
input_free(wp); input_free(wp);
window_pane_reset_mode(wp);
screen_free(&wp->base); screen_free(&wp->base);
if (wp->saved_grid != NULL) if (wp->saved_grid != NULL)
grid_destroy(wp->saved_grid); grid_destroy(wp->saved_grid);
@@ -570,9 +580,11 @@ window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell,
if (tcsetattr(STDIN_FILENO, TCSANOW, &tio2) != 0) if (tcsetattr(STDIN_FILENO, TCSANOW, &tio2) != 0)
fatal("tcgetattr failed"); fatal("tcgetattr failed");
closefrom(STDERR_FILENO + 1);
environ_push(env); environ_push(env);
clear_signals(); clear_signals(1);
log_close(); log_close();
if (*wp->cmd != '\0') { if (*wp->cmd != '\0') {
@@ -600,8 +612,6 @@ window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell,
fatal("fcntl failed"); fatal("fcntl failed");
if (fcntl(wp->fd, F_SETFL, mode|O_NONBLOCK) == -1) if (fcntl(wp->fd, F_SETFL, mode|O_NONBLOCK) == -1)
fatal("fcntl failed"); fatal("fcntl failed");
if (fcntl(wp->fd, F_SETFD, FD_CLOEXEC) == -1)
fatal("fcntl failed");
wp->event = bufferevent_new(wp->fd, wp->event = bufferevent_new(wp->fd,
window_pane_read_callback, NULL, window_pane_error_callback, wp); window_pane_read_callback, NULL, window_pane_error_callback, wp);
bufferevent_enable(wp->event, EV_READ|EV_WRITE); bufferevent_enable(wp->event, EV_READ|EV_WRITE);
@@ -626,6 +636,14 @@ window_pane_read_callback(unused struct bufferevent *bufev, void *data)
input_parse(wp); input_parse(wp);
wp->pipe_off = EVBUFFER_LENGTH(wp->event->input); wp->pipe_off = EVBUFFER_LENGTH(wp->event->input);
/*
* If we get here, we're not outputting anymore, so set the silence
* flag on the window.
*/
wp->window->flags |= WINDOW_SILENCE;
if (gettimeofday(&wp->window->silence_timer, NULL) != 0)
fatal("gettimeofday failed.");
} }
/* ARGSUSED */ /* ARGSUSED */

View File

@@ -48,7 +48,7 @@ as GNU screen. Major features include:</p>
<li>A powerful, consistent, well-documented and easily scriptable command <li>A powerful, consistent, well-documented and easily scriptable command
interface.</li> interface.</li>
<li>A window may be split horizontally and vertically into panes.</li> <li>A window may be split horizontally and vertically into panes.</li>
<li>Panes can be freely moved and resized, or arranged into one of four preset <li>Panes can be freely moved and resized, or arranged into preset
layouts. </li> layouts. </li>
<li>Support for UTF-8 and 256-colour terminals.</li> <li>Support for UTF-8 and 256-colour terminals.</li>
<li>Copy and paste with multiple buffers.</li> <li>Copy and paste with multiple buffers.</li>

View File

@@ -1,4 +1,4 @@
/* $Id: xmalloc.c,v 1.12 2009-10-28 23:12:38 tcunha Exp $ */ /* $Id: xmalloc.c,v 1.13 2010-09-07 19:32:58 nicm Exp $ */
/* /*
* Copyright (c) 2004 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2004 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -20,7 +20,6 @@
#include <errno.h> #include <errno.h>
#include <libgen.h> #include <libgen.h>
#include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>

View File

@@ -1,4 +1,4 @@
/* $Id: xterm-keys.c,v 1.5 2009-12-04 22:14:47 tcunha Exp $ */ /* $Id: xterm-keys.c,v 1.7 2010-10-24 00:30:51 tcunha Exp $ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -114,27 +114,23 @@ int
xterm_keys_modifiers(const char *template, const char *buf, size_t len) xterm_keys_modifiers(const char *template, const char *buf, size_t len)
{ {
size_t idx; size_t idx;
int param, modifiers;
idx = strcspn(template, "_"); idx = strcspn(template, "_");
if (idx >= len) if (idx >= len)
return (0); return (0);
switch (buf[idx]) { param = buf[idx] - '1';
case '2':
return (KEYC_SHIFT); modifiers = 0;
case '3': if (param & 1)
return (KEYC_ESCAPE); modifiers |= KEYC_SHIFT;
case '4': if (param & 2)
return (KEYC_SHIFT|KEYC_ESCAPE); modifiers |= KEYC_ESCAPE;
case '5': if (param & 4)
return (KEYC_CTRL); modifiers |= KEYC_CTRL;
case '6': if (param & 8)
return (KEYC_SHIFT|KEYC_CTRL); modifiers |= KEYC_ESCAPE;
case '7': return (modifiers);
return (KEYC_ESCAPE|KEYC_CTRL);
case '8':
return (KEYC_SHIFT|KEYC_ESCAPE|KEYC_CTRL);
}
return (0);
} }
/* /*
@@ -171,30 +167,21 @@ xterm_keys_lookup(int key)
int modifiers; int modifiers;
char *out; char *out;
#define KEY_MODIFIERS(key, modifiers) \ modifiers = 1;
(((key) & (KEYC_SHIFT|KEYC_ESCAPE|KEYC_CTRL)) == (modifiers)) if (key & KEYC_SHIFT)
modifiers = 0; modifiers += 1;
if (KEY_MODIFIERS(key, KEYC_SHIFT)) if (key & KEYC_ESCAPE)
modifiers = 2; modifiers += 2;
else if (KEY_MODIFIERS(key, KEYC_ESCAPE)) if (key & KEYC_CTRL)
modifiers = 3; modifiers += 4;
else if (KEY_MODIFIERS(key, KEYC_SHIFT|KEYC_ESCAPE)) if (key & KEYC_ESCAPE)
modifiers = 4; modifiers += 8;
else if (KEY_MODIFIERS(key, KEYC_CTRL))
modifiers = 5;
else if (KEY_MODIFIERS(key, KEYC_SHIFT|KEYC_CTRL))
modifiers = 6;
else if (KEY_MODIFIERS(key, KEYC_ESCAPE|KEYC_CTRL))
modifiers = 7;
else if (KEY_MODIFIERS(key, KEYC_SHIFT|KEYC_ESCAPE|KEYC_CTRL))
modifiers = 8;
#undef KEY_MODIFIERS
/* /*
* If the key has no modifiers, return NULL and let it fall through to * If the key has no modifiers, return NULL and let it fall through to
* the normal lookup. * the normal lookup.
*/ */
if (modifiers == 0) if (modifiers == 1)
return (NULL); return (NULL);
/* Otherwise, find the key in the table. */ /* Otherwise, find the key in the table. */